UNPKG

emvco-qr-sdk

Version:

A robust TypeScript SDK for decoding, validating, and processing EMVCo-compliant QR codes used in digital payment systems.

67 lines (66 loc) 3.01 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.TlvParserService = void 0; const qr_errors_constant_1 = require("../../application/constants/qr-errors.constant"); const tlv_entity_1 = require("../entities/tlv.entity"); /** * Service responsible for parsing a raw TLV-encoded string into structured `TlvEntity` objects. * * Supports recursive parsing of nested TLVs (e.g., value fields that themselves contain TLV structures), * commonly used in QR codes that follow the EMVCo standard. */ class TlvParserService { /** * Parses a TLV-encoded string into a list of `TlvEntity` instances. * * The TLV (Tag-Length-Value) format assumes that: * - Each tag is 2 characters long. * - Each length is 2 characters, representing the length of the value in characters. * - The value follows the length field, and may itself be recursively parsed as a TLV string. * * @param tlvString - The raw TLV-encoded string. * @param recursive - Whether to recursively parse value fields that are TLV-encoded. Defaults to `true`. * * @returns An array of parsed `TlvEntity` objects representing the structure of the input string. * * @throws {Error} If the input string is invalid (e.g., incomplete, malformed length, or overflows). */ static parse(tlvString, recursive = true) { const result = []; let index = 0; while (index < tlvString.length) { if (index + 4 > tlvString.length) { throw new Error(qr_errors_constant_1.PARSE_ERRORS.EMPTY_QR); } const tag = tlvString.substring(index, index + 2); index += 2; const lengthStr = tlvString.substring(index, index + 2); index += 2; const length = parseInt(lengthStr, 10); if (isNaN(length)) { throw new Error(qr_errors_constant_1.PARSE_ERRORS.INVALID_LENGTH(lengthStr, tag)); } if (index + length > tlvString.length) { throw new Error(qr_errors_constant_1.PARSE_ERRORS.EXCEEDS_AVAILABLE(tag)); } const value = tlvString.substring(index, index + length); index += length; let subTags = undefined; if (recursive && value.length >= 4) { try { const parsed = TlvParserService.parse(value, recursive); const reconstructed = parsed.map((child) => child.toString()).join(''); if (reconstructed === value && parsed.length > 0) { subTags = parsed; } } catch (error) { // Nested value is not a valid TLV string — assumed to be raw data (e.g., merchant name). } } result.push(new tlv_entity_1.TlvEntity(tag, value, subTags)); } return result; } } exports.TlvParserService = TlvParserService;