@p2pdotme/sdk 1.2.21 → 1.2.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/qr-parsers/index.ts","../src/qr-parsers/errors.ts","../src/country/currency.ts","../src/country/currencies/ars.ts","../src/country/types.ts","../src/country/qr-validator.ts","../src/country/currencies/bob.ts","../src/country/currencies/brl.ts","../src/country/currencies/cop.ts","../src/country/currencies/cup.ts","../src/country/currencies/ecu.ts","../src/country/currencies/eur.ts","../src/country/currencies/idr.ts","../src/country/currencies/inr.ts","../src/country/currencies/mex.ts","../src/country/currencies/ngn.ts","../src/country/currencies/pen.ts","../src/country/currencies/php.ts","../src/country/currencies/usd.ts","../src/country/currencies/ven.ts","../src/country/countries.ts","../src/qr-parsers/types.ts","../src/qr-parsers/utils/crc16.ts","../src/qr-parsers/utils/tlv.ts","../src/qr-parsers/parsers/ars.ts","../src/qr-parsers/utils/amount.ts","../src/qr-parsers/parsers/bob.ts","../src/qr-parsers/parsers/brl.ts","../src/qr-parsers/parsers/cop.ts","../src/qr-parsers/parsers/cup.ts","../src/qr-parsers/parsers/ecu.ts","../src/qr-parsers/parsers/idr.ts","../src/qr-parsers/parsers/inr.ts","../src/qr-parsers/parsers/ngn.ts","../src/qr-parsers/parsers/pen.ts","../src/qr-parsers/parsers/php.ts","../src/qr-parsers/parsers/ven.ts","../src/qr-parsers/parse-qr.ts"],"sourcesContent":["export { QRParserError, type QRParserErrorCode } from \"./errors\";\nexport { parseQR } from \"./parse-qr\";\nexport { isPagoMovilQr } from \"./parsers/ven\";\nexport type {\n\tParsedQR,\n\tParseQRParams,\n\tParseResult,\n\tSupportedCurrency,\n} from \"./types\";\n","export type QRParserErrorCode =\n\t| \"INVALID_QR\"\n\t| \"INVALID_CURRENCY\"\n\t| \"INVALID_AMOUNT\"\n\t| \"FETCH_FAILED\";\n\nexport class QRParserError extends Error {\n\treadonly code: QRParserErrorCode;\n\treadonly cause?: unknown;\n\treadonly context?: Record<string, unknown>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: QRParserErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"QRParserError\";\n\t\tthis.code = options.code;\n\t\tthis.cause = options.cause;\n\t\tthis.context = options.context;\n\t}\n}\n","/**\n * All supported currency symbols. Single source of truth for the SDK.\n *\n * Lives alongside the country metadata so that adding a currency is a\n * single-folder operation: drop a new file in `currencies/<code>.ts`, add it\n * to this map, and both `COUNTRY_OPTIONS` and `ZodCurrencySchema` pick it up.\n */\nexport const CURRENCY = {\n\tIDR: \"IDR\",\n\tINR: \"INR\",\n\tBRL: \"BRL\",\n\tARS: \"ARS\",\n\tMEX: \"MEX\",\n\tVEN: \"VEN\",\n\tBOB: \"BOB\",\n\tEUR: \"EUR\",\n\tNGN: \"NGN\",\n\tUSD: \"USD\",\n\tCOP: \"COP\",\n\tCUP: \"CUP\",\n\tECU: \"ECU\",\n\tPEN: \"PEN\",\n\tPHP: \"PHP\",\n} as const;\n\n/** Union of supported currency codes. */\nexport type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];\n\n/**\n * Tuple form of the currency codes — used by `z.enum(...)` in the shared\n * validation layer. Narrow tuple type required by Zod.\n */\nexport const CURRENCY_CODES = Object.values(CURRENCY) as [CurrencyCode, ...CurrencyCode[]];\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const ARS_PLACEHOLDER = \"juan.perez\";\nexport const ARS_VALIDATION_ERROR =\n\t\"Please enter a valid CBU/CVU (22 digits) or Alias (6-20 characters)\";\n\n/** Validates CBU (Clave Bancaria Uniforme) — Argentine banking key, 22 digits with checksums. */\nfunction validateCBU(cbu: string): boolean {\n\tif (cbu.length !== 22) return false;\n\tif (/^(\\d)\\1{21}$/.test(cbu)) return false;\n\n\tconst bankCode = cbu.substring(0, 7);\n\tconst bankCheckDigit = parseInt(cbu[7], 10);\n\tlet sum = 0;\n\tconst weights = [7, 1, 3, 9, 7, 1, 3];\n\tfor (let i = 0; i < 7; i++) {\n\t\tsum += parseInt(bankCode[i], 10) * weights[i];\n\t}\n\tconst calculatedBankCheck = (10 - (sum % 10)) % 10;\n\tif (bankCheckDigit !== calculatedBankCheck) return false;\n\n\tconst accountNumber = cbu.substring(8, 21);\n\tconst accountCheckDigit = parseInt(cbu[21], 10);\n\tsum = 0;\n\tconst accountWeights = [3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3];\n\tfor (let i = 0; i < 13; i++) {\n\t\tsum += parseInt(accountNumber[i], 10) * accountWeights[i];\n\t}\n\tconst calculatedAccountCheck = (10 - (sum % 10)) % 10;\n\tif (accountCheckDigit !== calculatedAccountCheck) return false;\n\n\treturn true;\n}\n\n/**\n * Validates Argentine payment IDs (CBU, CVU, or Alias).\n * CBU/CVU: 22 digits with checksum. Alias: 6-20 alphanumeric characters.\n */\nexport function validateArgentinePaymentId(paymentId: string): boolean {\n\tif (!paymentId || paymentId.trim().length === 0) return false;\n\n\tconst trimmedPaymentId = paymentId.trim();\n\n\tif (/^\\d{22}$/.test(trimmedPaymentId)) return validateCBU(trimmedPaymentId);\n\tif (/^[a-zA-Z0-9.\\-_]{6,20}$/.test(trimmedPaymentId)) return true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for ARS (Argentina, ALIAS). */\nexport const ARS_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"alias\",\n\t\tlabel: \"ALIAS_ID\",\n\t\tplaceholder: ARS_PLACEHOLDER,\n\t\tdisplayLabel: \"Alias\",\n\t\tvalidate: validateArgentinePaymentId,\n\t\tvalidationErrorMessage: ARS_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Argentina (ARS). */\nexport const ARS_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Argentina\",\n\tcurrency: CURRENCY.ARS,\n\tsymbolNative: \"$\",\n\tlocale: \"es-AR\",\n\tpaymentMethod: \"ALIAS\",\n\tpaymentAddressName: \"ALIAS_ID\",\n\ttimezone: \"America/Argentina/Buenos_Aires\",\n\ttimezone_name: \"ART\",\n\tflag: \"🇦🇷\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e6-1f1f7.png\",\n\tphoneCode: \"+54\",\n\ttelegramSupportChannel: \"https://t.me/p2pmeargentina\",\n\ttwitterUsername: \"p2pmeargentina\",\n\tsmsCountryCodes: [\"AR\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import type { CurrencyCode } from \"../types\";\n\n/** Joins an optional QR payload with typed fields (`qr||phone|cci`). */\nexport const PACKED_PAYMENT_ID_SEP = \"||\";\n\nexport interface PaymentIdFieldConfig {\n\treadonly key: string;\n\treadonly label: string;\n\treadonly placeholder: string;\n\treadonly displayLabel: string | null;\n\treadonly validate: (value: string) => boolean;\n\treadonly validationErrorMessage: string;\n\t/**\n\t * When true, an empty value is allowed. If every field is optional, at least\n\t * one field must still be filled (see `validatePaymentIdFields`).\n\t */\n\treadonly optional?: boolean;\n}\n\nexport interface CountryOption {\n\treadonly country: string;\n\treadonly currency: CurrencyCode;\n\treadonly internationalFormat?: string;\n\treadonly symbolNative: string;\n\treadonly locale: string;\n\treadonly paymentMethod: string;\n\treadonly paymentAddressName: string;\n\treadonly timezone: string;\n\treadonly timezone_name: string;\n\treadonly flag: string;\n\treadonly phoneCode?: string;\n\treadonly flagUrl: string;\n\treadonly telegramSupportChannel: string;\n\treadonly twitterUsername: string;\n\treadonly smsCountryCodes: readonly string[];\n\treadonly precision: number;\n\treadonly isAlpha: boolean;\n\treadonly disabled: boolean;\n\treadonly disabledPaymentTypes: readonly string[];\n\t/**\n\t * Merchant/seller provides payment details by uploading a QR image.\n\t * Distinct from PAY, where the buyer scans a QR. Default: false.\n\t */\n\treadonly uploadPaymentQR?: boolean;\n\t/** Structural check for a standalone QR payload (no `||` pack). */\n\treadonly validateQr?: (payload: string) => boolean;\n\t/**\n\t * PAY display: payload to re-draw a stored QR. May be looser than\n\t * `validateQr` so old orders still render (e.g. Yape/Plin without CRC,\n\t * Pago Móvil without `merchantId`). Scan & Pay uses `validateQr` /\n\t * parsers, not this hook. Apps call `getPayQrPayload(currency, id)` —\n\t * do not branch on currency.\n\t */\n\treadonly getPayQrPayload?: (paymentId: string) => string | null;\n\t/**\n\t * Fill catalog fields from a validated QR (e.g. Yape/Plin phone in EMVCo).\n\t * Only used when the typed fallback did not already set the key.\n\t */\n\treadonly hydrateFieldsFromQr?: (qr: string) => Partial<Record<string, string>>;\n\t/**\n\t * i18n key for a warning shown before the payer sends fiat (e.g. a bank\n\t * outage). Apps call `getTransferWarning(currency)` — do not branch on\n\t * currency. Omit when there is no warning.\n\t */\n\treadonly transferWarning?: string;\n}\n","/**\n * QR payload checks for **SELL collection** (seller/merchant uploads a QR).\n *\n * Why this file exists: user-app and merchant-app share the same “is this\n * blob a payment QR we can store?” rules. Keeping them here means the apps\n * call `CountryOption.validateQr` instead of copying peru/ven parsers.\n *\n * This is not `@p2pdotme/sdk/qr-parsers`. Parsers are Scan & Pay: they\n * pull amount/address out of a live scan. Peru Scan & Pay (`parsePeru`)\n * reuses `validatePeruvianQr` so a bad CRC cannot become the stored ID.\n *\n * Image → string stays in the apps (`qr-scanner` needs a File / canvas).\n * This module only sees the decoded payload string.\n */\nimport { PACKED_PAYMENT_ID_SEP } from \"./types\";\n\nfunction crc16ccitt(s: string): string {\n\tlet crc = 0xffff;\n\tfor (let i = 0; i < s.length; i++) {\n\t\tcrc ^= s.charCodeAt(i) << 8;\n\t\tfor (let j = 0; j < 8; j++) {\n\t\t\tcrc = crc & 0x8000 ? (crc << 1) ^ 0x1021 : crc << 1;\n\t\t\tcrc &= 0xffff;\n\t\t}\n\t}\n\treturn crc.toString(16).toUpperCase().padStart(4, \"0\");\n}\n\n/**\n * Structural EMVCo upload check: parseable TLV, country tag 58, currency tag 53,\n * and a matching CRC-16/CCITT-FALSE (tag 63). CRC is required on upload so we\n * never persist a truncated screenshot. `getPayQrPayload` may still re-draw a\n * stored blob whose CRC later fails.\n */\nfunction validateEmvcoQr(payload: string, country: string, currency: string): boolean {\n\tif (!payload || payload.trim().length === 0) return false;\n\tconst data = payload.trim();\n\n\tconst tags: Record<string, string> = {};\n\tlet pos = 0;\n\twhile (pos + 4 <= data.length) {\n\t\tconst tag = data.substring(pos, pos + 2);\n\t\tconst lengthStr = data.substring(pos + 2, pos + 4);\n\t\tif (!/^[0-9]{2}$/.test(tag) || !/^[0-9]{2}$/.test(lengthStr)) break;\n\t\tconst length = Number.parseInt(lengthStr, 10);\n\t\tif (pos + 4 + length > data.length) break;\n\t\ttags[tag] = data.substring(pos + 4, pos + 4 + length);\n\t\tpos += 4 + length;\n\t}\n\n\tif (tags[\"58\"] !== country) return false;\n\tif (tags[\"53\"] !== currency) return false;\n\n\tconst crcTag = tags[\"63\"];\n\tif (!crcTag) return false;\n\n\tconst marker = data.lastIndexOf(\"6304\");\n\tif (marker === -1) return false;\n\n\tconst expected = crc16ccitt(data.substring(0, marker + 4));\n\treturn expected.toUpperCase() === crcTag.toUpperCase();\n}\n\n/**\n * Yape/Plin EMVCo: country PE, currency 604, CRC-16/CCITT-FALSE.\n * Same rule for SELL upload and Scan & Pay (`parsePeru`).\n * `getPayQrPayload` may still re-draw a stored blob whose CRC later fails.\n */\nexport function validatePeruvianQr(payload: string): boolean {\n\treturn validateEmvcoQr(payload, \"PE\", \"604\");\n}\n\n/**\n * Bolivia QR Simple encrypted envelope (Yape Bs, BancoSol, Banco Fie dynamic QR,\n * etc.): `<base64 ciphertext>|<hex checksum>`. The payload is bank-encrypted so we\n * cannot read account/amount — we only validate the envelope shape and store it\n * verbatim to re-render. The checksum is a hex digest whose length varies by bank\n * (24 hex for Banco Fie, 32 hex for Yape/BancoSol). Reject packed `||` IDs so a\n * stored compound string is never mistaken for a QR.\n */\nfunction isBolivianEncryptedQr(payload: string): boolean {\n\tconst trimmed = payload.trim();\n\tif (trimmed.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tconst sep = trimmed.lastIndexOf(\"|\");\n\tif (sep < 40) return false;\n\tconst blob = trimmed.slice(0, sep);\n\tconst tag = trimmed.slice(sep + 1);\n\tif (blob.length % 4 !== 0) return false;\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/.test(blob)) return false;\n\treturn /^(?:[0-9A-Fa-f]{24}|[0-9A-Fa-f]{32})$/.test(tag);\n}\n\n/**\n * Bolivia QR Simple. Accepts either the encrypted envelope (Yape Bs, BancoSol\n * dynamic QR — `<base64>|<32-hex>`) or an EMVCo static QR (country BO, currency\n * 068, CRC-16/CCITT-FALSE). CRC / envelope shape is required on upload so we\n * never persist a truncated screenshot.\n */\nexport function validateBolivianQr(payload: string): boolean {\n\tif (!payload || payload.trim().length === 0) return false;\n\treturn isBolivianEncryptedQr(payload) || validateEmvcoQr(payload, \"BO\", \"068\");\n}\n\n/**\n * Suiche 7B / Pago Móvil collection QR: `base64?merchantId=NNNN&…`.\n * The base64 is bank AES — we cannot decode phone/RIF from it, only the\n * envelope. Reject packed `||` IDs so a stored compound string is never\n * treated as a QR. Same rule for SELL upload and Scan & Pay (`parsePagoMovil`).\n * `getPayQrPayload` may still re-draw a stored blob without `merchantId`.\n */\nexport function validateVenezuelanQr(payload: string): boolean {\n\tif (!payload || typeof payload !== \"string\") return false;\n\tconst trimmed = payload.trim();\n\tif (trimmed.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tconst qIdx = trimmed.indexOf(\"?\");\n\tif (qIdx < 40) return false;\n\tconst blob = trimmed.substring(0, qIdx);\n\tif (!/^[A-Za-z0-9+/=]+$/.test(blob)) return false;\n\treturn /(?:^|[?&])merchantId=\\d{3,4}(?:&|$)/.test(trimmed.substring(qIdx));\n}\n\n/** Left side of `qr||fields`, or the whole string when not packed. */\nexport function payQrCandidate(paymentId: string): string {\n\tconst trimmed = paymentId.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\treturn (sep >= 0 ? trimmed.slice(0, sep) : trimmed).trim();\n}\n\n/**\n * PAY display: Yape/Plin EMVCo (PE / 604) even when CRC fails.\n * SELL upload still uses `validatePeruvianQr`.\n */\nexport function isPeruvianPayQr(payload: string): boolean {\n\tif (!payload.startsWith(\"0002\")) return false;\n\treturn payload.includes(\"5802PE\") && payload.includes(\"5303604\");\n}\n\n/**\n * PAY display: Pago Móvil envelope `base64?…` without requiring merchantId.\n * SELL upload still uses `validateVenezuelanQr`.\n */\nexport function isVenezuelanPayQr(payload: string): boolean {\n\tif (!payload || payload.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tconst qIdx = payload.indexOf(\"?\");\n\tif (qIdx === -1) return false;\n\tconst blob = payload.slice(0, qIdx);\n\treturn blob.length > 0 && /^[A-Za-z0-9+/=]+$/.test(blob);\n}\n\n/**\n * PAY display: QR Ph EMVCo (PH / 608). SELL PHP is `phone|bank-name`, not a QR.\n * CRC is not required here — Scan & Pay already gated it on create.\n */\nexport function isPhilippinePayQr(payload: string): boolean {\n\tif (!payload.startsWith(\"0002\")) return false;\n\treturn payload.includes(\"5802PH\") && payload.includes(\"5303608\");\n}\n\n/**\n * PAY display: Bolivia QR Simple. EMVCo (BO / 068) even when CRC fails, or an\n * encrypted envelope `<base64>|<hex>` with a looser checksum than SELL upload\n * (16–64 hex vs 24/32). SELL still uses `validateBolivianQr`.\n */\nexport function isBolivianPayQr(payload: string): boolean {\n\tif (!payload || payload.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tif (payload.startsWith(\"0002\") && payload.includes(\"5802BO\") && payload.includes(\"5303068\")) {\n\t\treturn true;\n\t}\n\tconst sep = payload.lastIndexOf(\"|\");\n\tif (sep < 40) return false;\n\tconst blob = payload.slice(0, sep);\n\tconst tag = payload.slice(sep + 1);\n\tif (blob.length % 4 !== 0) return false;\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/.test(blob)) return false;\n\treturn /^[0-9A-Fa-f]{16,64}$/.test(tag);\n}\n","import { CURRENCY } from \"../currency\";\nimport { isBolivianPayQr, payQrCandidate, validateBolivianQr } from \"../qr-validator\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport { validateBolivianQr };\n\nexport const BOB_PLACEHOLDER = \"Bank account number (8–20 digits)\";\nexport const BOB_VALIDATION_ERROR = \"Please enter a valid Bolivian bank account number\";\n\n/**\n * Validates a Bolivian bank account number for QR Simple transfers.\n * Accepts 8–20 digits; spaces and dashes are ignored.\n */\nexport function validateBolivianAccount(account: string): boolean {\n\tif (!account || account.trim().length === 0) return false;\n\tconst cleaned = account.trim().replace(/[\\s-]/g, \"\");\n\treturn /^\\d{8,20}$/.test(cleaned);\n}\n\n/** Payment ID field configuration for BOB (Bolivia, QR Simple). */\nexport const BOB_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"account\",\n\t\tlabel: \"ACCOUNT_NUMBER\",\n\t\tplaceholder: BOB_PLACEHOLDER,\n\t\tdisplayLabel: \"Cuenta\",\n\t\tvalidate: validateBolivianAccount,\n\t\tvalidationErrorMessage: BOB_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Bolivia (BOB). */\nexport const BOB_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Bolivia\",\n\tcurrency: CURRENCY.BOB,\n\tinternationalFormat: \"BOB\",\n\tsymbolNative: \"Bs.\",\n\tlocale: \"es-BO\",\n\tpaymentMethod: \"QR_SIMPLE\",\n\tpaymentAddressName: \"QR_SIMPLE_DETAILS\",\n\ttimezone: \"America/La_Paz\",\n\ttimezone_name: \"BOT\",\n\tflag: \"🇧🇴\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e7-1f1f4.png\",\n\tphoneCode: \"+591\",\n\ttelegramSupportChannel: \"https://t.me/p2pme_bolivia_merchants\",\n\ttwitterUsername: \"p2pmebolivia\",\n\tsmsCountryCodes: [\"BO\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tuploadPaymentQR: true,\n\tvalidateQr: validateBolivianQr,\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isBolivianPayQr(candidate) ? candidate : null;\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const BRL_PLACEHOLDER = \"Chave pix ou pix copia e cola\";\nexport const BRL_VALIDATION_ERROR = \"Please enter a valid PIX ID\";\n\n/** Validates CPF (Brazilian tax ID for individuals). */\nfunction validateCPF(cpf: string): boolean {\n\tif (cpf.length !== 11) return false;\n\tif (/^(\\d)\\1{10}$/.test(cpf)) return false;\n\n\tlet sum = 0;\n\tfor (let i = 0; i < 9; i++) {\n\t\tsum += parseInt(cpf[i], 10) * (10 - i);\n\t}\n\tlet remainder = sum % 11;\n\tconst firstDigit = remainder < 2 ? 0 : 11 - remainder;\n\tif (parseInt(cpf[9], 10) !== firstDigit) return false;\n\n\tsum = 0;\n\tfor (let i = 0; i < 10; i++) {\n\t\tsum += parseInt(cpf[i], 10) * (11 - i);\n\t}\n\tremainder = sum % 11;\n\tconst secondDigit = remainder < 2 ? 0 : 11 - remainder;\n\treturn parseInt(cpf[10], 10) === secondDigit;\n}\n\n/** Validates CNPJ (Brazilian tax ID for companies). */\nfunction validateCNPJ(cnpj: string): boolean {\n\tif (cnpj.length !== 14) return false;\n\tif (/^(\\d)\\1{13}$/.test(cnpj)) return false;\n\n\tlet sum = 0;\n\tconst weights1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];\n\tfor (let i = 0; i < 12; i++) {\n\t\tsum += parseInt(cnpj[i], 10) * weights1[i];\n\t}\n\tlet remainder = sum % 11;\n\tconst firstDigit = remainder < 2 ? 0 : 11 - remainder;\n\tif (parseInt(cnpj[12], 10) !== firstDigit) return false;\n\n\tsum = 0;\n\tconst weights2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];\n\tfor (let i = 0; i < 13; i++) {\n\t\tsum += parseInt(cnpj[i], 10) * weights2[i];\n\t}\n\tremainder = sum % 11;\n\tconst secondDigit = remainder < 2 ? 0 : 11 - remainder;\n\treturn parseInt(cnpj[13], 10) === secondDigit;\n}\n\n/**\n * Validates PIX ID format.\n * PIX can be: CPF (11 digits), CNPJ (14 digits), email, phone (10–11 digits or E.164),\n * random key (UUID), or a PIX \"copia e cola\" EMV QR payload (starts with 000201).\n */\nexport function validatePIXId(pixId: string): boolean {\n\tif (!pixId || pixId.trim().length === 0) return false;\n\n\tconst trimmedPixId = pixId.trim();\n\n\t// PIX \"copia e cola\" — EMV QR code payload\n\tif (/^000201/.test(trimmedPixId)) return true;\n\n\t// 11 digits: valid CPF or Brazilian mobile phone key (DDD 11–99 + digit 9 + 8 digits)\n\tif (/^\\d{11}$/.test(trimmedPixId))\n\t\treturn validateCPF(trimmedPixId) || /^[1-9][1-9]9\\d{8}$/.test(trimmedPixId);\n\tif (/^\\d{14}$/.test(trimmedPixId)) return validateCNPJ(trimmedPixId);\n\tif (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmedPixId)) return true;\n\t// Phone key: 10–11 digits (with or without country code prefix)\n\tif (/^\\d{10,11}$/.test(trimmedPixId)) return true;\n\tif (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmedPixId))\n\t\treturn true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for BRL (Brazil, PIX). */\nexport const BRL_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"pix\",\n\t\tlabel: \"PIX_ID\",\n\t\tplaceholder: BRL_PLACEHOLDER,\n\t\tdisplayLabel: \"PIX ID\",\n\t\tvalidate: validatePIXId,\n\t\tvalidationErrorMessage: BRL_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Brazil (BRL). */\nexport const BRL_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Brazil\",\n\tcurrency: CURRENCY.BRL,\n\tsymbolNative: \"R$\",\n\tlocale: \"pt-BR\",\n\tpaymentMethod: \"PIX\",\n\tpaymentAddressName: \"PIX_ID\",\n\ttimezone: \"America/Sao_Paulo\",\n\ttimezone_name: \"BRT\",\n\tflag: \"🇧🇷\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e7-1f1f7.png\",\n\tphoneCode: \"+55\",\n\ttelegramSupportChannel: \"https://t.me/p2pmebrasil\",\n\ttwitterUsername: \"p2pmebrasil\",\n\tsmsCountryCodes: [\"BR\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const COP_PLACEHOLDER = \"3001234567 / juan.perez@nequi.com.co / @alias\";\nexport const COP_VALIDATION_ERROR =\n\t\"Please enter a valid Colombian payment ID (e.g., 3001234567, email, or @alias)\";\n\n/**\n * Validates Colombian payment ID for Nequi, Daviplata, or Bre-B.\n * Accepts a 10-digit phone number starting with 3, a valid email address,\n * or a Bre-B alias starting with @ (e.g. @juanperez).\n */\nexport function validateColombianPaymentId(paymentId: string): boolean {\n\tif (!paymentId || paymentId.trim().length === 0) return false;\n\tconst trimmed = paymentId.trim();\n\tif (/^3\\d{9}$/.test(trimmed)) return true;\n\tif (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmed)) return true;\n\tif (/^@\\S+$/.test(trimmed)) return true;\n\treturn false;\n}\n\n/** Payment ID field configuration for COP (Colombia, Transferencia). */\nexport const COP_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"alias\",\n\t\tlabel: \"ALIAS_TRANSFERENCIA\",\n\t\tplaceholder: COP_PLACEHOLDER,\n\t\tdisplayLabel: \"Nequi / Daviplata / Bre-B\",\n\t\tvalidate: validateColombianPaymentId,\n\t\tvalidationErrorMessage: COP_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Colombia (COP). */\nexport const COP_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Colombia\",\n\tcurrency: CURRENCY.COP,\n\tsymbolNative: \"$\",\n\tlocale: \"es-CO\",\n\tpaymentMethod: \"TRANSFERENCIA\",\n\tpaymentAddressName: \"ALIAS_TRANSFERENCIA\",\n\ttimezone: \"America/Bogota\",\n\ttimezone_name: \"COT\",\n\tflag: \"🇨🇴\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e8-1f1f4.png\",\n\tphoneCode: \"+57\",\n\ttelegramSupportChannel: \"https://t.me/p2pmecolombiaclientes\",\n\ttwitterUsername: \"p2pmeColombia\",\n\tsmsCountryCodes: [\"CO\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const CUP_PLACEHOLDER_PHONE = \"54004417\";\nexport const CUP_PLACEHOLDER_CARD = \"9227 9598 7238 3620\";\n\nexport const CUP_VALIDATION_ERROR_PHONE =\n\t\"Please enter a valid 8-digit phone number (e.g., 54004417)\";\nexport const CUP_VALIDATION_ERROR_CARD =\n\t\"Please enter a valid 16-digit card number (e.g., 9227 9598 7238 3620)\";\n\n/**\n * Validates Cuban phone number for Transfermóvil.\n * Format: 8 digits, optionally prefixed with the +53 country code.\n */\nexport function validateCubanPhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\n\tconst cleaned = phoneNumber.trim().replace(/\\D/g, \"\");\n\n\tif (/^\\d{8}$/.test(cleaned)) return true;\n\tif (/^53\\d{8}$/.test(cleaned)) return true;\n\n\treturn false;\n}\n\n/**\n * Validates Cuban bank card number for Transfermóvil.\n * Format: 16 digits, spaces allowed (e.g., \"9227 9598 7238 3620\").\n */\nexport function validateCubanCardNumber(cardNumber: string): boolean {\n\tif (!cardNumber || cardNumber.trim().length === 0) return false;\n\tconst cleaned = cardNumber.trim().replace(/[\\s-]/g, \"\");\n\treturn /^\\d{16}$/.test(cleaned);\n}\n\n/** Payment ID field configuration for CUP (Cuba, Transfermóvil). */\nexport const CUP_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: CUP_PLACEHOLDER_PHONE,\n\t\tdisplayLabel: \"Phone\",\n\t\tvalidate: validateCubanPhoneNumber,\n\t\tvalidationErrorMessage: CUP_VALIDATION_ERROR_PHONE,\n\t},\n\t{\n\t\tkey: \"card\",\n\t\tlabel: \"CARD_NUMBER\",\n\t\tplaceholder: CUP_PLACEHOLDER_CARD,\n\t\tdisplayLabel: \"Card\",\n\t\tvalidate: validateCubanCardNumber,\n\t\tvalidationErrorMessage: CUP_VALIDATION_ERROR_CARD,\n\t},\n];\n\n/** Country option for Cuba (CUP). Transfermóvil transfers, including QR payments. */\nexport const CUP_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Cuba\",\n\tcurrency: CURRENCY.CUP,\n\tsymbolNative: \"$\",\n\tlocale: \"es-CU\",\n\tpaymentMethod: \"TRANSFERMOVIL\",\n\tpaymentAddressName: \"TRANSFERMOVIL_DETAILS\",\n\ttimezone: \"America/Havana\",\n\ttimezone_name: \"CST\",\n\tflag: \"🇨🇺\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e8-1f1fa.png\",\n\tphoneCode: \"+53\",\n\ttelegramSupportChannel: \"https://t.me/p2pdotmecuba\",\n\ttwitterUsername: \"P2Pdotmecuba\",\n\tsmsCountryCodes: [\"CU\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\ttransferWarning: \"CUP_BANDEC_WARNING\",\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\n// ── Placeholders & error messages ────────────────────────────────────────────\n\nexport const ECU_PLACEHOLDER_BANK = \"Banco Pichincha\";\nexport const ECU_PLACEHOLDER_ACCOUNT_TYPE = \"Savings / Checking\";\nexport const ECU_PLACEHOLDER_ACCOUNT_NUMBER = \"2100123456\";\nexport const ECU_PLACEHOLDER_NAME = \"Juan Pérez\";\nexport const ECU_PLACEHOLDER_CEDULA = \"1710034065\";\n\nexport const ECU_BANK_VALIDATION_ERROR = \"Please enter the bank name\";\nexport const ECU_ACCOUNT_TYPE_VALIDATION_ERROR = \"Please enter the account type\";\nexport const ECU_ACCOUNT_NUMBER_VALIDATION_ERROR =\n\t\"Please enter a valid account number (4-20 digits)\";\nexport const ECU_NAME_VALIDATION_ERROR = \"Please enter the account holder name\";\nexport const ECU_CEDULA_VALIDATION_ERROR = \"Please enter a valid Ecuadorian cédula or RUC\";\n\n// ── Validators ───────────────────────────────────────────────────────────────\n\nfunction isValidCedulaCore(cedula: string): boolean {\n\tif (!/^\\d{10}$/.test(cedula)) return false;\n\tconst province = Number(cedula.slice(0, 2));\n\tif (province < 1 || province > 24) return false;\n\tif (Number(cedula[2]) >= 6) return false;\n\n\tconst coefficients = [2, 1, 2, 1, 2, 1, 2, 1, 2];\n\tlet sum = 0;\n\tfor (let i = 0; i < 9; i++) {\n\t\tlet product = Number(cedula[i]) * coefficients[i];\n\t\tif (product > 9) product -= 9;\n\t\tsum += product;\n\t}\n\tconst checkDigit = sum % 10 === 0 ? 0 : 10 - (sum % 10);\n\treturn checkDigit === Number(cedula[9]);\n}\n\n/**\n * Validates an Ecuadorian cédula (10-digit, módulo-10 checksum) or a 13-digit RUC\n * whose first 10 digits form a valid cédula. Province must be 01–24 and the third\n * digit < 6 (natural person).\n */\nexport function validateEcuadorianCedula(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\tconst cleaned = value.trim().replace(/\\D/g, \"\");\n\tif (cleaned.length === 13) return isValidCedulaCore(cleaned.slice(0, 10));\n\tif (cleaned.length !== 10) return false;\n\treturn isValidCedulaCore(cleaned);\n}\n\n/** Validates an Ecuadorian bank account number — 4–20 digits after stripping separators. */\nexport function validateEcuadorianAccountNumber(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\tconst cleaned = value.trim().replace(/\\D/g, \"\");\n\treturn /^\\d{4,20}$/.test(cleaned);\n}\n\n/** Validates an account holder name — non-empty, letters/spaces/apostrophe/period/hyphen only. */\nexport function validateEcuadorianAccountName(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\treturn /^[A-Za-zÀ-ÿ][A-Za-zÀ-ÿ .'-]{1,}$/.test(value.trim());\n}\n\n// ── Payment fields ───────────────────────────────────────────────────────────\n\n/** Payment ID field configuration for ECU (Ecuador, bank transfer — all free text). */\nexport const ECU_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"bank-name\",\n\t\tlabel: \"BANK_NAME\",\n\t\tplaceholder: ECU_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Bank\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: ECU_BANK_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-type\",\n\t\tlabel: \"ACCOUNT_TYPE\",\n\t\tplaceholder: ECU_PLACEHOLDER_ACCOUNT_TYPE,\n\t\tdisplayLabel: \"Account Type\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: ECU_ACCOUNT_TYPE_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-number\",\n\t\tlabel: \"ACCOUNT_NUMBER\",\n\t\tplaceholder: ECU_PLACEHOLDER_ACCOUNT_NUMBER,\n\t\tdisplayLabel: \"Account Number\",\n\t\tvalidate: validateEcuadorianAccountNumber,\n\t\tvalidationErrorMessage: ECU_ACCOUNT_NUMBER_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-name\",\n\t\tlabel: \"NAME\",\n\t\tplaceholder: ECU_PLACEHOLDER_NAME,\n\t\tdisplayLabel: \"Name\",\n\t\tvalidate: validateEcuadorianAccountName,\n\t\tvalidationErrorMessage: ECU_NAME_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"cedula\",\n\t\tlabel: \"CEDULA\",\n\t\tplaceholder: ECU_PLACEHOLDER_CEDULA,\n\t\tdisplayLabel: \"Cédula\",\n\t\tvalidate: validateEcuadorianCedula,\n\t\tvalidationErrorMessage: ECU_CEDULA_VALIDATION_ERROR,\n\t},\n];\n\n// ── Country option ───────────────────────────────────────────────────────────\n\n/** Country option for Ecuador (ECU — USD-denominated, bank transfer + DeUna QR). */\nexport const ECU_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Ecuador\",\n\tcurrency: CURRENCY.ECU,\n\tinternationalFormat: \"USD\",\n\tsymbolNative: \"$\",\n\tlocale: \"es-EC\",\n\tpaymentMethod: \"TRANSFERENCIA\",\n\tpaymentAddressName: \"ECU_BANK_DETAILS\",\n\ttimezone: \"America/Guayaquil\",\n\ttimezone_name: \"ECT\",\n\tflag: \"🇪🇨\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1ea-1f1e8.png\",\n\tphoneCode: \"+593\",\n\ttelegramSupportChannel: \"https://t.me/Ecuador_P2P\",\n\ttwitterUsername: \"P2Pdotme_Ecu\",\n\tsmsCountryCodes: [],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const EUR_PLACEHOLDER = \"@username or email\";\nexport const EUR_VALIDATION_ERROR = \"Please enter a valid Revolut ID (username, email, or phone)\";\n\n/**\n * Validates Revolut ID (username, email, or phone number).\n */\nexport function validateRevolutId(revolutId: string): boolean {\n\tif (!revolutId || revolutId.trim().length === 0) return false;\n\n\tconst trimmed = revolutId.trim();\n\n\tif (/^@?[a-zA-Z0-9._-]{3,30}$/.test(trimmed)) return true;\n\tif (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmed)) return true;\n\tif (/^\\+?\\d{7,15}$/.test(trimmed)) return true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for EUR (Revolut EUR). */\nexport const EUR_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"revolut\",\n\t\tlabel: \"REVOLUT_ID\",\n\t\tplaceholder: EUR_PLACEHOLDER,\n\t\tdisplayLabel: \"Revolut ID\",\n\t\tvalidate: validateRevolutId,\n\t\tvalidationErrorMessage: EUR_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Revolut EUR. */\nexport const EUR_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Revolut EUR\",\n\tcurrency: CURRENCY.EUR,\n\tsymbolNative: \"€\",\n\tlocale: \"de-DE\",\n\tpaymentMethod: \"REVOLUT\",\n\tpaymentAddressName: \"REVOLUT_ID\",\n\ttimezone: \"Europe/Berlin\",\n\ttimezone_name: \"CET\",\n\tflag: \"\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f310.png\",\n\tphoneCode: \"\",\n\ttelegramSupportChannel: \"https://t.me/P2Pdotme\",\n\ttwitterUsername: \"P2Pdotme\",\n\tsmsCountryCodes: [],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: true,\n\tdisabledPaymentTypes: [\"PAY\"],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const IDR_PLACEHOLDER = \"8123456789\";\nexport const IDR_VALIDATION_ERROR =\n\t\"Please enter a valid Indonesian phone number (e.g., 8123456789)\";\n\n/**\n * Validates Indonesian phone number.\n * Validates just the number part (9-12 digits).\n */\nexport function validateIndonesianPhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\tif (/[a-zA-Z]/.test(phoneNumber)) return false;\n\tconst cleaned = phoneNumber.replace(/\\D/g, \"\");\n\treturn /^\\d{9,12}$/.test(cleaned);\n}\n\n/** Payment ID field configuration for IDR (Indonesia, QRIS). */\nexport const IDR_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: IDR_PLACEHOLDER,\n\t\tdisplayLabel: \"Phone Number\",\n\t\tvalidate: validateIndonesianPhoneNumber,\n\t\tvalidationErrorMessage: IDR_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Indonesia (IDR). */\nexport const IDR_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Indonesia\",\n\tcurrency: CURRENCY.IDR,\n\tsymbolNative: \"Rp\",\n\tlocale: \"id-ID\",\n\tpaymentMethod: \"QRIS\",\n\tpaymentAddressName: \"PHONE_NUMBER\",\n\ttimezone: \"Asia/Jakarta\",\n\ttimezone_name: \"WIB\",\n\tflag: \"🇮🇩\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1ee-1f1e9.png\",\n\tphoneCode: \"+62\",\n\ttelegramSupportChannel: \"https://t.me/p2pmeindonesia\",\n\ttwitterUsername: \"p2pdotmeID\",\n\tsmsCountryCodes: [],\n\tprecision: 0,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const INR_PLACEHOLDER = \"merchant@upi\";\nexport const INR_VALIDATION_ERROR = \"Please enter a valid UPI ID (e.g., username@bankname)\";\n\n/**\n * Validates UPI ID format.\n * UPI ID format: username@bankname (e.g., john@paytm, user@ybl, 8658404239@kotak811)\n */\nexport function validateUPIId(upiId: string): boolean {\n\tif (!upiId || upiId.trim().length === 0) return false;\n\tconst upiRegex = /^[a-zA-Z0-9.\\-_]{2,256}@[a-zA-Z0-9]{2,64}$/;\n\treturn upiRegex.test(upiId.trim());\n}\n\n/** Payment ID field configuration for INR (India, UPI). */\nexport const INR_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"upi\",\n\t\tlabel: \"UPI_ID\",\n\t\tplaceholder: INR_PLACEHOLDER,\n\t\tdisplayLabel: \"UPI ID\",\n\t\tvalidate: validateUPIId,\n\t\tvalidationErrorMessage: INR_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for India (INR). */\nexport const INR_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"India\",\n\tcurrency: CURRENCY.INR,\n\tsymbolNative: \"₹\",\n\tlocale: \"en-IN\",\n\tpaymentMethod: \"UPI\",\n\tpaymentAddressName: \"UPI_ID\",\n\ttimezone: \"Asia/Kolkata\",\n\ttimezone_name: \"IST\",\n\tflag: \"🇮🇳\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1ee-1f1f3.png\",\n\tphoneCode: \"+91\",\n\ttelegramSupportChannel: \"https://t.me/P2Pdotme\",\n\ttwitterUsername: \"P2Pdotme\",\n\tsmsCountryCodes: [\"IN\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const MEX_PLACEHOLDER = \"012345678901234567\";\nexport const MEX_VALIDATION_ERROR =\n\t\"Please enter a valid CLABE (18 digits), card number, or phone number\";\n\n/**\n * Validates Mexican payment IDs (CLABE, card number, or phone number).\n * CLABE: 18 digits. Card: 16 digits. Phone: 10 digits.\n */\nexport function validateMexicanPaymentId(paymentId: string): boolean {\n\tif (!paymentId || paymentId.trim().length === 0) return false;\n\n\tconst trimmed = paymentId.trim().replace(/\\D/g, \"\");\n\n\tif (/^\\d{18}$/.test(trimmed)) return true;\n\tif (/^\\d{16}$/.test(trimmed)) return true;\n\tif (/^\\d{10}$/.test(trimmed)) return true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for MEX (Mexico, SPEI). */\nexport const MEX_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"clabe\",\n\t\tlabel: \"CLABE_ID\",\n\t\tplaceholder: MEX_PLACEHOLDER,\n\t\tdisplayLabel: \"CLABE\",\n\t\tvalidate: validateMexicanPaymentId,\n\t\tvalidationErrorMessage: MEX_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Mexico (MEX). */\nexport const MEX_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Mexico\",\n\tcurrency: CURRENCY.MEX,\n\tinternationalFormat: \"MXN\",\n\tsymbolNative: \"Mx\",\n\tlocale: \"es-MX\",\n\tpaymentMethod: \"SPEI\",\n\tpaymentAddressName: \"CLABE_ID\",\n\ttimezone: \"America/Mexico_City\",\n\ttimezone_name: \"CST\",\n\tflag: \"🇲🇽\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f2-1f1fd.png\",\n\tphoneCode: \"+52\",\n\ttelegramSupportChannel: \"https://t.me/p2pdotmex\",\n\ttwitterUsername: \"p2pmemexico\",\n\tsmsCountryCodes: [\"MX\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: true,\n\tdisabledPaymentTypes: [\"PAY\"],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const NGN_PLACEHOLDER = \"0123456789\";\nexport const NGN_PLACEHOLDER_BANK = \"Bank Name\";\nexport const NGN_PLACEHOLDER_ACCOUNT_NAME = \"Account Name\";\nexport const NGN_VALIDATION_ERROR = \"Please enter a valid 10-digit account number\";\nexport const NGN_BANK_NAME_VALIDATION_ERROR = \"Please enter a valid bank name\";\nexport const NGN_ACCOUNT_NAME_VALIDATION_ERROR = \"Please enter a valid account name\";\n\n/**\n * Validates Nigerian bank account number (NUBAN format, 10 digits).\n */\nexport function validateNigerianAccountNumber(accountNumber: string): boolean {\n\tif (!accountNumber || accountNumber.trim().length === 0) return false;\n\tconst cleaned = accountNumber.trim().replace(/\\D/g, \"\");\n\treturn /^\\d{10}$/.test(cleaned);\n}\n\n/**\n * Validates a Nigerian bank account holder name (non-empty, letters/spaces/.'- only).\n */\nexport function validateNigerianAccountName(accountName: string): boolean {\n\tif (!accountName || accountName.trim().length === 0) return false;\n\treturn /^[A-Za-z][A-Za-z .'-]{1,}$/.test(accountName.trim());\n}\n\n/** Payment ID field configuration for NGN (Nigeria, NIP). */\nexport const NGN_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"account\",\n\t\tlabel: \"ACCOUNT_NUMBER\",\n\t\tplaceholder: NGN_PLACEHOLDER,\n\t\tdisplayLabel: \"Account Number\",\n\t\tvalidate: validateNigerianAccountNumber,\n\t\tvalidationErrorMessage: NGN_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"bank-name\",\n\t\tlabel: \"BANK_NAME\",\n\t\tplaceholder: NGN_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Bank Name\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: NGN_BANK_NAME_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-name\",\n\t\tlabel: \"ACCOUNT_NAME\",\n\t\tplaceholder: NGN_PLACEHOLDER_ACCOUNT_NAME,\n\t\tdisplayLabel: \"Account Name\",\n\t\tvalidate: validateNigerianAccountName,\n\t\tvalidationErrorMessage: NGN_ACCOUNT_NAME_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Nigeria (NGN). */\nexport const NGN_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Nigeria\",\n\tcurrency: CURRENCY.NGN,\n\tsymbolNative: \"₦\",\n\tlocale: \"en-NG\",\n\tpaymentMethod: \"NIP\",\n\tpaymentAddressName: \"ACCOUNT_NUMBER\",\n\ttimezone: \"Africa/Lagos\",\n\ttimezone_name: \"WAT\",\n\tflag: \"🇳🇬\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f3-1f1ec.png\",\n\tphoneCode: \"+234\",\n\ttelegramSupportChannel: \"https://t.me/p2pmengn\",\n\ttwitterUsername: \"p2pmeNigeria\",\n\tsmsCountryCodes: [],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport { isPeruvianPayQr, payQrCandidate, validatePeruvianQr } from \"../qr-validator\";\nimport { type CountryOption, PACKED_PAYMENT_ID_SEP, type PaymentIdFieldConfig } from \"../types\";\n\nexport { validatePeruvianQr };\n\nexport const PEN_PLACEHOLDER = \"CCI (20 digits) or Yape/Plin phone (9XXXXXXXX)\";\nexport const PEN_PHONE_PLACEHOLDER = \"PERU_PHONE_PLACEHOLDER\";\nexport const PEN_CCI_PLACEHOLDER = \"PERU_CCI_PLACEHOLDER\";\nexport const PEN_VALIDATION_ERROR = \"Please enter a valid 20-digit CCI or Yape/Plin phone number\";\nexport const PEN_PHONE_VALIDATION_ERROR = \"Please enter a valid Yape/Plin phone (9XXXXXXXX)\";\nexport const PEN_CCI_VALIDATION_ERROR = \"Please enter a valid 20-digit CCI\";\n\n/** Validates a 20-digit Peruvian CCI (spaces ignored). */\nexport function validatePeruvianCci(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\treturn /^\\d{20}$/.test(value.trim().replace(/\\s+/g, \"\"));\n}\n\n/**\n * Validates a Yape/Plin phone: `9` + 8 digits, optional `+51` / `51` prefix.\n */\nexport function validatePeruvianPhone(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\tconst phone = value\n\t\t.trim()\n\t\t.replace(/\\s+/g, \"\")\n\t\t.replace(/^\\+?51/, \"\");\n\treturn /^9\\d{8}$/.test(phone);\n}\n\n/**\n * Validates a Peruvian payment key for the legacy single-field path.\n * Accepts either a 20-digit CCI or a Yape/Plin phone number.\n */\nexport function validatePeruvianPaymentKey(value: string): boolean {\n\treturn validatePeruvianCci(value) || validatePeruvianPhone(value);\n}\n\n/** Normalizes a CCI or Yape/Plin phone to the stored digit form. */\nexport function normalizePeruvianPaymentKey(value: string): string {\n\tconst trimmed = value.trim();\n\tconst cci = trimmed.replace(/\\s+/g, \"\");\n\tif (/^\\d{20}$/.test(cci)) return cci;\n\tconst phone = trimmed.replace(/\\s+/g, \"\").replace(/^\\+?51/, \"\");\n\tif (/^9\\d{8}$/.test(phone)) return phone;\n\treturn cci;\n}\n\nexport type PeruvianPaymentIdParts = {\n\tqr: string | null;\n\tphone: string | null;\n\tcci: string | null;\n};\n\nfunction looksLikeTlv(value: string): boolean {\n\tif (value.length < 5 || !/^\\d{4}/.test(value)) return false;\n\tconst len = Number.parseInt(value.slice(2, 4), 10);\n\treturn !Number.isNaN(len) && len >= 0 && 4 + len <= value.length;\n}\n\n/**\n * Phone from a TLV value. Skips hex hashes (Yape tag 39) so `998765432`\n * inside a checksum never counts. Allows `+51` / `51` glued to a 9XXXXXXXX.\n */\nfunction phoneFromTlvValue(value: string): string | null {\n\tconst trimmed = value.trim();\n\tif (!trimmed) return null;\n\tif (validatePeruvianPhone(trimmed)) return normalizePeruvianPaymentKey(trimmed);\n\tif (/^[0-9a-f]+$/i.test(trimmed) && /[a-f]/i.test(trimmed)) return null;\n\tconst match = trimmed.match(/(^|[^0-9])(\\+?51)?(9\\d{8})(?![0-9])/);\n\tif (!match) return null;\n\tconst candidate = `${match[2] ?? \"\"}${match[3]}`;\n\treturn validatePeruvianPhone(candidate) ? normalizePeruvianPaymentKey(candidate) : null;\n}\n\n/**\n * Walks EMVCo TLV (merchant-account templates, additional data, and any nested\n * TLV) and returns the first Yape/Plin phone found.\n */\nfunction extractPeruvianPhoneFromQr(payload: string): string | null {\n\tlet pos = 0;\n\twhile (pos + 4 <= payload.length) {\n\t\tconst tag = payload.slice(pos, pos + 2);\n\t\tconst len = Number.parseInt(payload.slice(pos + 2, pos + 4), 10);\n\t\tif (Number.isNaN(len) || len < 0 || pos + 4 + len > payload.length) break;\n\t\tconst value = payload.slice(pos + 4, pos + 4 + len);\n\t\tconst direct = phoneFromTlvValue(value);\n\t\tif (direct) return direct;\n\t\tif ((tag >= \"02\" && tag <= \"51\") || tag === \"62\" || tag === \"64\" || looksLikeTlv(value)) {\n\t\t\tconst nested = extractPeruvianPhoneFromQr(value);\n\t\t\tif (nested) return nested;\n\t\t}\n\t\tpos += 4 + len;\n\t}\n\tconst glued = payload.match(/\\+51\\s*9\\d{8}/);\n\tif (glued && validatePeruvianPhone(glued[0])) {\n\t\treturn normalizePeruvianPaymentKey(glued[0]);\n\t}\n\treturn null;\n}\n\nfunction parsePeruvianFallback(value: string): Pick<PeruvianPaymentIdParts, \"phone\" | \"cci\"> {\n\tconst trimmed = value.trim();\n\tif (!trimmed) return { phone: null, cci: null };\n\tconst tokens = trimmed\n\t\t.split(\"|\")\n\t\t.map((token) => token.trim())\n\t\t.filter(Boolean);\n\tlet phone: string | null = null;\n\tlet cci: string | null = null;\n\tfor (const token of tokens) {\n\t\tif (!phone && validatePeruvianPhone(token)) {\n\t\t\tphone = normalizePeruvianPaymentKey(token);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!cci && validatePeruvianCci(token)) {\n\t\t\tcci = normalizePeruvianPaymentKey(token);\n\t\t}\n\t}\n\treturn { phone, cci };\n}\n\n/**\n * Splits a stored PEN payment ID into QR payload and/or typed fallback.\n * Formats: EMVCo blob, CCI, phone, `phone|cci`, or `blob||fallback`.\n */\nexport function parsePeruvianPaymentId(value: string | null | undefined): PeruvianPaymentIdParts {\n\tif (!value || typeof value !== \"string\") {\n\t\treturn { qr: null, phone: null, cci: null };\n\t}\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\tconst fallback = parsePeruvianFallback(right);\n\t\tconst qr = validatePeruvianQr(left) ? left : null;\n\t\treturn {\n\t\t\tqr,\n\t\t\tphone: fallback.phone ?? (qr ? extractPeruvianPhoneFromQr(qr) : null),\n\t\t\tcci: fallback.cci,\n\t\t};\n\t}\n\tif (validatePeruvianQr(trimmed)) {\n\t\treturn {\n\t\t\tqr: trimmed,\n\t\t\tphone: extractPeruvianPhoneFromQr(trimmed),\n\t\t\tcci: null,\n\t\t};\n\t}\n\tconst fallback = parsePeruvianFallback(trimmed);\n\treturn { qr: null, phone: fallback.phone, cci: fallback.cci };\n}\n\nexport function serializePeruvianPaymentId(\n\tqr: string | null | undefined,\n\tphone: string | null | undefined,\n\tcci: string | null | undefined,\n): string {\n\tconst q = qr?.trim() && validatePeruvianQr(qr.trim()) ? qr.trim() : \"\";\n\tconst p = phone?.trim() && validatePeruvianPhone(phone) ? normalizePeruvianPaymentKey(phone) : \"\";\n\tconst c = cci?.trim() && validatePeruvianCci(cci) ? normalizePeruvianPaymentKey(cci) : \"\";\n\tconst fallback = p && c ? `${p}|${c}` : p || c;\n\tif (q && fallback) return `${q}${PACKED_PAYMENT_ID_SEP}${fallback}`;\n\treturn q || fallback;\n}\n\nfunction isValidPeruvianTypedRest(value: string): boolean {\n\tconst nonempty = value\n\t\t.split(\"|\")\n\t\t.map((token) => token.trim())\n\t\t.filter((token) => token.length > 0);\n\tif (nonempty.length === 0) return false;\n\treturn nonempty.every((token) => validatePeruvianPhone(token) || validatePeruvianCci(token));\n}\n\n/** QR payload and/or CCI and/or Yape/Plin phone. A packed rest must be valid typed fields. */\nexport function validatePeruvianPaymentId(value: string): boolean {\n\tif (!value || typeof value !== \"string\") return false;\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\tconst qrOk = validatePeruvianQr(left);\n\t\tif (right.trim()) return qrOk && isValidPeruvianTypedRest(right);\n\t\treturn qrOk;\n\t}\n\tif (validatePeruvianQr(trimmed)) return true;\n\treturn isValidPeruvianTypedRest(trimmed);\n}\n\n/** Payment ID field configuration for PEN (Peru). At least one of phone / CCI. */\nexport const PEN_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PERU_PHONE_LABEL\",\n\t\tplaceholder: PEN_PHONE_PLACEHOLDER,\n\t\tdisplayLabel: \"Yape / Plin phone\",\n\t\tvalidate: validatePeruvianPhone,\n\t\tvalidationErrorMessage: PEN_PHONE_VALIDATION_ERROR,\n\t\toptional: true,\n\t},\n\t{\n\t\tkey: \"cci\",\n\t\tlabel: \"PERU_CCI_LABEL\",\n\t\tplaceholder: PEN_CCI_PLACEHOLDER,\n\t\tdisplayLabel: \"CCI\",\n\t\tvalidate: validatePeruvianCci,\n\t\tvalidationErrorMessage: PEN_CCI_VALIDATION_ERROR,\n\t\toptional: true,\n\t},\n];\n\n/** Country option for Peru (PEN). */\nexport const PEN_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Peru\",\n\tcurrency: CURRENCY.PEN,\n\tsymbolNative: \"S/\",\n\tlocale: \"es-PE\",\n\tpaymentMethod: \"YAPE_PLIN_CCI\",\n\tpaymentAddressName: \"YAPE_PLIN_CCI_DETAILS\",\n\ttimezone: \"America/Lima\",\n\ttimezone_name: \"PET\",\n\tflag: \"🇵🇪\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f5-1f1ea.png\",\n\tphoneCode: \"+51\",\n\ttelegramSupportChannel: \"https://t.me/p2pmeperu\",\n\ttwitterUsername: \"p2pmePeru\",\n\tsmsCountryCodes: [\"PE\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tuploadPaymentQR: true,\n\tvalidateQr: validatePeruvianQr,\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isPeruvianPayQr(candidate) ? candidate : null;\n\t},\n\thydrateFieldsFromQr: (qr) => {\n\t\tconst phone = extractPeruvianPhoneFromQr(qr);\n\t\treturn phone ? { phone } : {};\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport { isPhilippinePayQr, payQrCandidate } from \"../qr-validator\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const PHP_PLACEHOLDER_PHONE = \"9171234567\";\nexport const PHP_PLACEHOLDER_BANK = \"GCash\";\n\nexport const PHP_VALIDATION_ERROR_PHONE =\n\t\"Please enter a valid Philippine mobile number (e.g., 9171234567)\";\nexport const PHP_BANK_NAME_VALIDATION_ERROR = \"Please enter the receiving bank or e-wallet\";\n\n/**\n * Validates a Philippine mobile number for InstaPay (GCash / Maya).\n * Mobile numbers are 10 digits starting with 9, and are commonly written\n * locally as `09XXXXXXXXX` or internationally as `+63 9XXXXXXXXX`.\n */\nexport function validatePhilippinePhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\tif (/[a-zA-Z]/.test(phoneNumber)) return false;\n\n\tconst cleaned = phoneNumber.trim().replace(/\\D/g, \"\");\n\n\tif (/^9\\d{9}$/.test(cleaned)) return true;\n\tif (/^09\\d{9}$/.test(cleaned)) return true;\n\tif (/^639\\d{9}$/.test(cleaned)) return true;\n\n\treturn false;\n}\n\n/**\n * Payment ID field configuration for PHP (Philippines, InstaPay).\n *\n * InstaPay is an interbank rail, so the receiving institution has to be named\n * alongside the number — it may be an e-wallet (GCash, Maya), a digital bank\n * (GoTyme) or a local bank. For e-wallets the account number is the mobile\n * number the wallet is registered to.\n */\nexport const PHP_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: PHP_PLACEHOLDER_PHONE,\n\t\tdisplayLabel: \"Phone Number\",\n\t\tvalidate: validatePhilippinePhoneNumber,\n\t\tvalidationErrorMessage: PHP_VALIDATION_ERROR_PHONE,\n\t},\n\t{\n\t\tkey: \"bank-name\",\n\t\tlabel: \"BANK_NAME\",\n\t\tplaceholder: PHP_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Bank Name\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: PHP_BANK_NAME_VALIDATION_ERROR,\n\t},\n];\n\n/**\n * Country option for the Philippines (PHP).\n *\n * Payment channel is InstaPay — merchants are free to receive through GCash or\n * Maya, both of which settle over InstaPay. Scan & Pay supports the QR Ph\n * standard only (wallet-proprietary GCash-only / Maya-only QRs are ignored),\n * mirroring how QRIS is handled for Indonesia, so PAY stays enabled.\n */\nexport const PHP_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Philippines\",\n\tcurrency: CURRENCY.PHP,\n\tsymbolNative: \"₱\",\n\tlocale: \"en-PH\",\n\tpaymentMethod: \"INSTAPAY\",\n\tpaymentAddressName: \"PHONE_NUMBER\",\n\ttimezone: \"Asia/Manila\",\n\ttimezone_name: \"PST\",\n\tflag: \"🇵🇭\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f5-1f1ed.png\",\n\tphoneCode: \"+63\",\n\ttelegramSupportChannel: \"https://t.me/p2pdotmeph\",\n\ttwitterUsername: \"p2pdotmeph\",\n\tsmsCountryCodes: [\"PH\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isPhilippinePayQr(candidate) ? candidate : null;\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\nimport { EUR_PLACEHOLDER, EUR_VALIDATION_ERROR, validateRevolutId } from \"./eur\";\n\nexport const USD_PLACEHOLDER = EUR_PLACEHOLDER;\nexport const USD_VALIDATION_ERROR = EUR_VALIDATION_ERROR;\n\n/** Payment ID field configuration for USD (Revolut USD). */\nexport const USD_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"revolut\",\n\t\tlabel: \"REVOLUT_ID\",\n\t\tplaceholder: USD_PLACEHOLDER,\n\t\tdisplayLabel: \"Revolut ID\",\n\t\tvalidate: validateRevolutId,\n\t\tvalidationErrorMessage: USD_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Revolut USD. */\nexport const USD_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Revolut USD\",\n\tcurrency: CURRENCY.USD,\n\tsymbolNative: \"$\",\n\tlocale: \"en-US\",\n\tpaymentMethod: \"REVOLUT\",\n\tpaymentAddressName: \"REVOLUT_ID\",\n\ttimezone: \"America/New_York\",\n\ttimezone_name: \"EST\",\n\tflag: \"\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f310.png\",\n\tphoneCode: \"+1\",\n\ttelegramSupportChannel: \"https://t.me/P2Pdotme\",\n\ttwitterUsername: \"P2Pdotme\",\n\tsmsCountryCodes: [\"US\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: true,\n\tdisabledPaymentTypes: [\"PAY\"],\n};\n","import { CURRENCY } from \"../currency\";\nimport { isVenezuelanPayQr, payQrCandidate, validateVenezuelanQr } from \"../qr-validator\";\nimport { type CountryOption, PACKED_PAYMENT_ID_SEP, type PaymentIdFieldConfig } from \"../types\";\n\nexport { validateVenezuelanQr };\n\nexport const VEN_PLACEHOLDER = \"04121234567\";\nexport const VEN_PLACEHOLDER_RIF = \"V12345678\";\nexport const VEN_PLACEHOLDER_BANK = \"Banesco\";\n\nexport const VEN_VALIDATION_ERROR = \"Please enter a valid phone number (e.g., 04121234567)\";\nexport const VEN_VALIDATION_ERROR_RIF =\n\t\"Please enter a valid Cédula/RIF (e.g., V12345678, P12345678)\";\nexport const VEN_VALIDATION_ERROR_BANK = \"Please enter a bank name\";\n\n/** Allowed Venezuelan document prefixes for Pago Móvil (SENIAT / banking). */\nexport const VEN_RIF_PREFIXES = [\"V\", \"E\", \"J\", \"G\", \"R\", \"P\"] as const;\n\n/**\n * Validates Venezuelan phone number for Pago Movil.\n * Format: 04XX-XXXXXXX (11 digits starting with 04).\n */\nexport function validateVenezuelanPhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\n\tconst cleaned = phoneNumber.trim().replace(/\\D/g, \"\");\n\n\tif (/^04\\d{9}$/.test(cleaned)) return true;\n\tif (/^4\\d{9}$/.test(cleaned)) return true;\n\n\treturn false;\n}\n\n/**\n * Validates Venezuelan Cédula/RIF for Pago Móvil.\n * Format: prefix (V|E|J|G|R|P) followed by digits.\n * Includes natural persons (V), foreigners (E), companies (J), government (G),\n * residual (R), and passport-linked accounts (P).\n */\nexport function validateVenezuelanRif(rif: string): boolean {\n\tif (!rif || rif.trim().length === 0) return false;\n\tconst trimmed = rif.trim().toUpperCase();\n\treturn /^[VEJGRP]\\d+$/.test(trimmed);\n}\n\nfunction isValidVenezuelanCompound(value: string): boolean {\n\tconst parts = value.split(\"|\");\n\tif (parts.length !== 3) return false;\n\treturn (\n\t\tvalidateVenezuelanPhoneNumber(parts[0]) &&\n\t\tvalidateVenezuelanRif(parts[1]) &&\n\t\tparts[2].trim().length > 0\n\t);\n}\n\nexport type VenezuelanPaymentIdParts = {\n\tqr: string | null;\n\tcompound: string | null;\n};\n\n/**\n * Splits a stored VEN payment ID into its QR payload and/or typed fallback.\n * Formats: S7B blob, `phone|rif|bank`, or `blob||phone|rif|bank`.\n */\nexport function parseVenezuelanPaymentId(\n\tvalue: string | null | undefined,\n): VenezuelanPaymentIdParts {\n\tif (!value || typeof value !== \"string\") return { qr: null, compound: null };\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\treturn {\n\t\t\tqr: validateVenezuelanQr(left) ? left : null,\n\t\t\tcompound: isValidVenezuelanCompound(right) ? right : null,\n\t\t};\n\t}\n\tif (validateVenezuelanQr(trimmed)) return { qr: trimmed, compound: null };\n\tif (isValidVenezuelanCompound(trimmed)) return { qr: null, compound: trimmed };\n\treturn { qr: null, compound: null };\n}\n\nexport function serializeVenezuelanPaymentId(\n\tqr: string | null | undefined,\n\tcompound: string | null | undefined,\n): string {\n\tconst q = qr?.trim() && validateVenezuelanQr(qr.trim()) ? qr.trim() : \"\";\n\tconst c = compound?.trim() || \"\";\n\tif (q && c) return `${q}${PACKED_PAYMENT_ID_SEP}${c}`;\n\treturn q || c;\n}\n\n/**\n * Accepts a Suiche 7B QR payload, the typed `phone|rif|bank` fallback,\n * or both packed as `qr||phone|rif|bank`. A non-empty packed rest must be a\n * valid compound — QR does not mask an incomplete trio.\n */\nexport function validateVenezuelanPaymentId(value: string): boolean {\n\tif (!value || typeof value !== \"string\") return false;\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\tconst qrOk = validateVenezuelanQr(left);\n\t\tif (right.trim()) return qrOk && isValidVenezuelanCompound(right);\n\t\treturn qrOk;\n\t}\n\tconst { qr, compound } = parseVenezuelanPaymentId(trimmed);\n\treturn qr !== null || compound !== null;\n}\n\n/** Payment ID field configuration for VEN (Venezuela, Pago Móvil). */\nexport const VEN_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: VEN_PLACEHOLDER,\n\t\tdisplayLabel: \"Phone\",\n\t\tvalidate: validateVenezuelanPhoneNumber,\n\t\tvalidationErrorMessage: VEN_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"rif\",\n\t\tlabel: \"RIF_LABEL\",\n\t\tplaceholder: VEN_PLACEHOLDER_RIF,\n\t\tdisplayLabel: \"Cédula/RIF\",\n\t\tvalidate: validateVenezuelanRif,\n\t\tvalidationErrorMessage: VEN_VALIDATION_ERROR_RIF,\n\t},\n\t{\n\t\tkey: \"bank\",\n\t\tlabel: \"BANK_LABEL\",\n\t\tplaceholder: VEN_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Banco\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: VEN_VALIDATION_ERROR_BANK,\n\t},\n];\n\n/** Country option for Venezuela (VEN). */\nexport const VEN_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Venezuela\",\n\tcurrency: CURRENCY.VEN,\n\tinternationalFormat: \"VES\",\n\tsymbolNative: \"Bs\",\n\tlocale: \"es-VE\",\n\tpaymentMethod: \"PAGO_MOVIL\",\n\tpaymentAddressName: \"PAGO_MOVIL_DETAILS\",\n\ttimezone: \"America/Caracas\",\n\ttimezone_name: \"VET\",\n\tflag: \"🇻🇪\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1fb-1f1ea.png\",\n\tphoneCode: \"+58\",\n\ttelegramSupportChannel: \"https://t.me/p2pmevenezuela\",\n\ttwitterUsername: \"p2pmevenezuela\",\n\tsmsCountryCodes: [\"VE\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tuploadPaymentQR: true,\n\tvalidateQr: validateVenezuelanQr,\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isVenezuelanPayQr(candidate) ? candidate : null;\n\t},\n};\n","import {\n\tARS_COUNTRY_OPTION,\n\tBOB_COUNTRY_OPTION,\n\tBRL_COUNTRY_OPTION,\n\tCOP_COUNTRY_OPTION,\n\tCUP_COUNTRY_OPTION,\n\tECU_COUNTRY_OPTION,\n\tEUR_COUNTRY_OPTION,\n\tIDR_COUNTRY_OPTION,\n\tINR_COUNTRY_OPTION,\n\tMEX_COUNTRY_OPTION,\n\tNGN_COUNTRY_OPTION,\n\tPEN_COUNTRY_OPTION,\n\tPHP_COUNTRY_OPTION,\n\tUSD_COUNTRY_OPTION,\n\tVEN_COUNTRY_OPTION,\n} from \"./currencies\";\nimport type { CurrencyCode } from \"./currency\";\nimport { PAYMENT_ID_FIELDS } from \"./payment-fields\";\nimport type { CountryOption } from \"./types\";\n\n/** All supported countries with their currency metadata, payment methods, and display config. */\nexport const COUNTRY_OPTIONS: readonly CountryOption[] = [\n\tINR_COUNTRY_OPTION,\n\tIDR_COUNTRY_OPTION,\n\tBRL_COUNTRY_OPTION,\n\tARS_COUNTRY_OPTION,\n\tMEX_COUNTRY_OPTION,\n\tVEN_COUNTRY_OPTION,\n\tBOB_COUNTRY_OPTION,\n\tNGN_COUNTRY_OPTION,\n\tCOP_COUNTRY_OPTION,\n\tCUP_COUNTRY_OPTION,\n\tECU_COUNTRY_OPTION,\n\tPEN_COUNTRY_OPTION,\n\tPHP_COUNTRY_OPTION,\n\tEUR_COUNTRY_OPTION,\n\tUSD_COUNTRY_OPTION,\n];\n\nexport function getCountryOption(\n\tcurrency: CurrencyCode | null | undefined,\n): CountryOption | undefined {\n\tif (!currency) return undefined;\n\treturn COUNTRY_OPTIONS.find((c) => c.currency === currency);\n}\n\n/**\n * Whether the merchant/seller provides payment details by uploading a QR image.\n * Distinct from PAY (`disabledPaymentTypes`), where the buyer scans a QR.\n */\nexport function uploadsPaymentQR(currency: CurrencyCode | null | undefined): boolean {\n\treturn getCountryOption(currency)?.uploadPaymentQR === true;\n}\n\n/**\n * Whether a stored payment ID may include a QR payload (`qr||fields` or\n * standalone QR). Derived from `validateQr` — no extra flag.\n */\nexport function usesPackedPaymentId(currency: CurrencyCode | null | undefined): boolean {\n\treturn getCountryOption(currency)?.validateQr != null;\n}\n\n/**\n * Apps mount the catalog form (PackedPaymentInput): QR upload and typed\n * fields may both show — they are not exclusive.\n */\nexport function usesCatalogPaymentForm(currency: CurrencyCode | null | undefined): boolean {\n\tif (!currency) return false;\n\treturn uploadsPaymentQR(currency) || (PAYMENT_ID_FIELDS[currency]?.length ?? 0) > 1;\n}\n\n/**\n * i18n key for a warning shown before the payer sends fiat, or `null` when the\n * currency has none. Apps translate with `t(key)` — do not branch on currency.\n */\nexport function getTransferWarning(currency: CurrencyCode | null | undefined): string | null {\n\treturn getCountryOption(currency)?.transferWarning ?? null;\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { COUNTRY_OPTIONS } from \"../country\";\nimport type { CurrencyCode } from \"../types\";\nimport { QRParserError, type QRParserErrorCode } from \"./errors\";\n\n/** Currencies that support QR parsing — derived from countries where PAY is not disabled. */\nexport const SUPPORTED_QR_CURRENCIES: readonly CurrencyCode[] = COUNTRY_OPTIONS.filter(\n\t(c) => !c.disabledPaymentTypes.includes(\"PAY\"),\n).map((c) => c.currency);\n\nexport type SupportedCurrency = (typeof SUPPORTED_QR_CURRENCIES)[number];\n\n/** Internal — options forwarded to per-currency parsers (today, only PIX uses them). */\nexport interface ParseQRConfig {\n\tproxyUrl?: string;\n\torderId?: string;\n}\n\n/** Single-object params for `parseQR`. */\nexport interface ParseQRParams {\n\treadonly qrData: string;\n\treadonly currency: SupportedCurrency;\n\t/** Exchange rate: 1 USDC = `sellPrice` fiat. */\n\treadonly sellPrice: number;\n\t/** Required for dynamic PIX (BRL) QRs to bypass CORS on the bank endpoint. */\n\treadonly proxyUrl?: string;\n\t/** Forwarded to `proxyUrl` for tracing / correlation. */\n\treadonly orderId?: string;\n}\n\nexport interface ParsedQR {\n\tpaymentAddress: string;\n\tamount?: {\n\t\tusdc: number;\n\t\tfiat: number;\n\t};\n}\n\nexport type ParseResult = Result<ParsedQR, QRParserError>;\n\nexport function success(data: ParsedQR): ParseResult {\n\treturn ok(data);\n}\n\nexport function failure(\n\tcode: QRParserErrorCode,\n\tmessage: string,\n\tcontext?: Record<string, unknown>,\n): ParseResult {\n\treturn err(new QRParserError(message, { code, context }));\n}\n","/**\n * Calculate CRC-16-CCITT-FALSE checksum.\n * Polynomial 0x1021, initial value 0xFFFF.\n * Used by EMVCo-compliant QR codes (PIX, MercadoPago).\n */\nexport function calculateCRC16(data: string): string {\n\tconst payload = `${data}6304`;\n\tconst polynomial = 0x1021;\n\tlet result = 0xffff;\n\n\tconst encoder = new TextEncoder();\n\tconst bytes = encoder.encode(payload);\n\n\tfor (let offset = 0; offset < bytes.length; offset++) {\n\t\tresult ^= bytes[offset] << 8;\n\t\tfor (let bitwise = 0; bitwise < 8; bitwise++) {\n\t\t\tresult <<= 1;\n\t\t\tif (result & 0x10000) {\n\t\t\t\tresult ^= polynomial;\n\t\t\t}\n\t\t\tresult &= 0xffff;\n\t\t}\n\t}\n\n\treturn result.toString(16).toUpperCase().padStart(4, \"0\");\n}\n\n/**\n * Verify CRC-16 checksum of an EMVCo QR string.\n * Expects the string to end with \"6304\" + 4 hex CRC digits.\n */\nexport function verifyCRC16(qrData: string): { valid: boolean; error?: string } {\n\tif (!qrData || qrData.length < 8) {\n\t\treturn { valid: false, error: \"QR data too short for CRC verification\" };\n\t}\n\n\t// The CRC tag is always the final data object: \"63\" (tag) + \"04\" (length) +\n\t// 4 hex CRC digits. It must sit at a fixed position (length - 8). Locating it\n\t// positionally — rather than with lastIndexOf(\"6304\") — avoids a false match\n\t// when the CRC value itself is \"6304\" (payload ends \"...63046304\").\n\tconst crcTagIndex = qrData.length - 8;\n\tif (qrData.substring(crcTagIndex, crcTagIndex + 4) !== \"6304\") {\n\t\treturn { valid: false, error: \"Missing or misplaced CRC tag (6304)\" };\n\t}\n\n\tconst providedCrc = qrData.substring(crcTagIndex + 4, crcTagIndex + 8);\n\tif (!/^[0-9A-Fa-f]{4}$/.test(providedCrc)) {\n\t\treturn { valid: false, error: \"Invalid CRC hex format\" };\n\t}\n\n\tconst dataBeforeCrc = qrData.substring(0, crcTagIndex);\n\tconst calculatedCrc = calculateCRC16(dataBeforeCrc);\n\n\tif (calculatedCrc !== providedCrc.toUpperCase()) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `CRC mismatch: expected ${calculatedCrc}, got ${providedCrc.toUpperCase()}`,\n\t\t};\n\t}\n\n\treturn { valid: true };\n}\n","/**\n * Parse a TLV (Tag-Length-Value) encoded string.\n * Tags are 2 digits, lengths are 2 digits (EMVCo standard).\n * Returns all parsed tag-value pairs. Stops gracefully on truncated data.\n */\nexport function parseTLV(data: string): { tag: string; value: string }[] {\n\tconst entries: { tag: string; value: string }[] = [];\n\tlet pos = 0;\n\n\twhile (pos + 4 <= data.length) {\n\t\tconst tag = data.substring(pos, pos + 2);\n\t\tconst lengthStr = data.substring(pos + 2, pos + 4);\n\n\t\tif (!/^[0-9]{2}$/.test(tag) || !/^[0-9]{2}$/.test(lengthStr)) {\n\t\t\tbreak;\n\t\t}\n\n\t\tconst length = parseInt(lengthStr, 10);\n\t\tif (pos + 4 + length > data.length) {\n\t\t\tbreak;\n\t\t}\n\n\t\tconst value = data.substring(pos + 4, pos + 4 + length);\n\t\tentries.push({ tag, value });\n\t\tpos += 4 + length;\n\t}\n\n\treturn entries;\n}\n\n/**\n * Extract specific tags from a TLV string.\n * Returns a record mapping tag IDs to their values.\n * Only includes tags present in the `tags` array.\n */\nexport function extractTags(data: string, tags: string[]): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tconst tagSet = new Set(tags);\n\n\tfor (const entry of parseTLV(data)) {\n\t\tif (tagSet.has(entry.tag)) {\n\t\t\tresult[entry.tag] = entry.value;\n\t\t}\n\t}\n\n\treturn result;\n}\n","import type { ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags } from \"../utils/tlv\";\n\nexport function parseMercadoPago(qrData: string, _sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tconst isARS = trimmed.includes(\"5303032\");\n\tconst isAR = trimmed.includes(\"5802AR\");\n\tif (!isARS && !isAR) {\n\t\treturn failure(\"INVALID_QR\", \"Not an ARS/Argentina QR code\");\n\t}\n\n\tconst crc = verifyCRC16(trimmed);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid QR checksum\");\n\t}\n\n\tconst tags = extractTags(trimmed, [\"59\"]);\n\tconst merchantName = tags[\"59\"] || \"Unknown\";\n\n\treturn success({ paymentAddress: merchantName });\n}\n","/** Plain decimal, EMVCo-style: digits with an optional single fractional part. */\nconst DECIMAL_AMOUNT = /^\\d+(\\.\\d+)?$/;\n\n/**\n * Parse a fiat amount string and convert to usdc using sellPrice.\n * Returns null unless the whole string is a finite, positive decimal amount and\n * sellPrice is a finite positive number. Rejects trailing garbage (\"12abc\"),\n * exponent/Infinity/NaN forms that parseFloat would otherwise accept, and a\n * zero/negative sellPrice that would produce an Infinite or negative usdc value.\n */\nexport function parseAmount(\n\tamountStr: string,\n\tsellPrice: number,\n): { usdc: number; fiat: number } | null {\n\tif (!amountStr) return null;\n\tconst trimmed = amountStr.trim();\n\tif (!DECIMAL_AMOUNT.test(trimmed)) return null;\n\n\tconst fiat = Number(trimmed);\n\tif (!Number.isFinite(fiat) || fiat <= 0) return null;\n\tif (!Number.isFinite(sellPrice) || sellPrice <= 0) return null;\n\n\treturn { usdc: fiat / sellPrice, fiat };\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst BOB_TAGS = { AMOUNT: \"54\", CURRENCY: \"53\", COUNTRY: \"58\" } as const;\n\n/**\n * Bolivia QR Simple encrypted envelope: `<base64 ciphertext>|<hex checksum>`\n * (Yape Bs, BancoSol, Banco Fie dynamic QR). The payload is bank-encrypted, so\n * amount/account cannot be read — the buyer re-renders the exact blob for their\n * bank app to scan. The checksum is a hex digest whose length varies by bank\n * (24 hex for Banco Fie, 32 hex for Yape/BancoSol).\n */\nfunction isBolivianEncryptedQr(payload: string): boolean {\n\tif (payload.includes(\"||\")) return false;\n\tconst sep = payload.lastIndexOf(\"|\");\n\tif (sep < 40) return false;\n\tconst blob = payload.slice(0, sep);\n\tconst tag = payload.slice(sep + 1);\n\tif (blob.length % 4 !== 0) return false;\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/.test(blob)) return false;\n\treturn /^(?:[0-9A-Fa-f]{24}|[0-9A-Fa-f]{32})$/.test(tag);\n}\n\n/**\n * Parses a Bolivian QR Simple QR. Accepts either the encrypted envelope\n * (`<base64>|<hex>`) or an EMVCo static QR (country tag 58 `BO`, currency tag\n * 53 `068`). Returns the raw payload verbatim as `paymentAddress` so the buyer app\n * can re-render the exact QR. Static EMVCo QRs carry no amount unless tag 54 is\n * present, which is converted to usdc via `sellPrice`; encrypted QRs carry none.\n */\nexport function parseBolivia(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tif (isBolivianEncryptedQr(trimmed)) {\n\t\treturn success({ paymentAddress: trimmed });\n\t}\n\n\tconst tags = extractTags(trimmed, [BOB_TAGS.AMOUNT, BOB_TAGS.CURRENCY, BOB_TAGS.COUNTRY]);\n\n\tif (tags[BOB_TAGS.COUNTRY] !== \"BO\" || tags[BOB_TAGS.CURRENCY] !== \"068\") {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Bolivia QR Simple QR\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: trimmed };\n\n\tconst amountStr = tags[BOB_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { QRParserError, type QRParserErrorCode } from \"../errors\";\nimport type { ParsedQR, ParseQRConfig, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { parseTLV } from \"../utils/tlv\";\n\nconst PIX_TAGS = {\n\tPAYLOAD_FORMAT: \"00\",\n\tPIX_KEY_INFO: \"26\",\n\tAMOUNT: \"54\",\n\tMERCHANT_NAME: \"59\",\n\tCRC: \"63\",\n} as const;\n\nfunction parsePIXKeyInfo(data: string): { pixKey: string | null; location: string | null } {\n\tconst result = { pixKey: null as string | null, location: null as string | null };\n\n\tfor (const entry of parseTLV(data)) {\n\t\tif (entry.tag === \"01\") {\n\t\t\tif (entry.value.includes(\"http\") || entry.value.includes(\"://\")) {\n\t\t\t\tresult.location = entry.value.startsWith(\"http\") ? entry.value : `https://${entry.value}`;\n\t\t\t} else {\n\t\t\t\tresult.pixKey = entry.value;\n\t\t\t}\n\t\t} else if (entry.tag === \"25\") {\n\t\t\tresult.location = entry.value.startsWith(\"http\") ? entry.value : `https://${entry.value}`;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nfunction safeBase64Decode(base64String: string): string {\n\tif (typeof globalThis.atob === \"function\") {\n\t\treturn globalThis.atob(base64String);\n\t}\n\tthrow new Error(\"No base64 decoder available\");\n}\n\ninterface PIXJWTPayload {\n\tstatus?: string;\n\ttxid?: string;\n\tvalor?: { original?: string };\n\tcalendario?: { expiracao?: number };\n}\n\nfunction parseJWT(token: string): PIXJWTPayload | null {\n\ttry {\n\t\tconst parts = token.trim().split(\".\");\n\t\tif (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) return null;\n\t\tconst json = safeBase64Decode(parts[1]);\n\t\treturn JSON.parse(json) as PIXJWTPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction dynamicErr(\n\tcode: QRParserErrorCode,\n\tmessage: string,\n): Result<{ dynamicAmount?: string }, QRParserError> {\n\treturn err(new QRParserError(message, { code }));\n}\n\nasync function fetchDynamicData(\n\tlocation: string,\n\tproxyUrl: string,\n\torderId?: string,\n): Promise<Result<{ dynamicAmount?: string }, QRParserError>> {\n\ttry {\n\t\tconst url = new URL(`${proxyUrl}/pix`);\n\t\turl.searchParams.set(\"locationUrl\", location);\n\t\tif (orderId) {\n\t\t\turl.searchParams.set(\"orderId\", orderId);\n\t\t}\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: { Accept: \"*/*\" },\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\treturn dynamicErr(\"FETCH_FAILED\", `Proxy error: ${response.status} ${response.statusText}`);\n\t\t}\n\n\t\tconst jwtToken = await response.text();\n\t\tconst payload = parseJWT(jwtToken);\n\n\t\tif (!payload) {\n\t\t\treturn dynamicErr(\"FETCH_FAILED\", \"Failed to parse dynamic PIX response\");\n\t\t}\n\n\t\treturn ok({ dynamicAmount: payload.valor?.original });\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : \"Unknown error\";\n\t\treturn dynamicErr(\"FETCH_FAILED\", `Failed to fetch dynamic PIX data: ${msg}`);\n\t}\n}\n\nexport async function parsePIX(\n\tqrData: string,\n\tsellPrice: number,\n\tconfig: ParseQRConfig,\n): Promise<ParseResult> {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tconst crc = verifyCRC16(trimmed);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid QR checksum\");\n\t}\n\n\tconst allTags: Record<string, string> = {};\n\tfor (const entry of parseTLV(trimmed)) {\n\t\tallTags[entry.tag] = entry.value;\n\t}\n\n\tif (!allTags[PIX_TAGS.PAYLOAD_FORMAT]) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid PIX QR format\");\n\t}\n\n\tconst merchantName = allTags[PIX_TAGS.MERCHANT_NAME] || \"MERCHANT_NOT_FOUND\";\n\n\tlet dynamicAmount: string | undefined;\n\tconst pixKeyData = allTags[PIX_TAGS.PIX_KEY_INFO];\n\tif (pixKeyData) {\n\t\tconst { location } = parsePIXKeyInfo(pixKeyData);\n\t\tif (location) {\n\t\t\tif (!config.proxyUrl) {\n\t\t\t\treturn failure(\"FETCH_FAILED\", \"proxyUrl is required for dynamic PIX QR codes\");\n\t\t\t}\n\t\t\tconst dynamicResult = await fetchDynamicData(location, config.proxyUrl, config.orderId);\n\t\t\tif (dynamicResult.isErr()) {\n\t\t\t\treturn err(dynamicResult.error);\n\t\t\t}\n\t\t\tdynamicAmount = dynamicResult.value.dynamicAmount;\n\t\t}\n\t}\n\n\tconst fiatAmountStr = dynamicAmount || allTags[PIX_TAGS.AMOUNT];\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tif (fiatAmountStr) {\n\t\tconst amount = parseAmount(fiatAmountStr, sellPrice);\n\t\tif (amount) {\n\t\t\tresult.amount = amount;\n\t\t}\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst COP_CURRENCY_CODE = \"5303170\";\nconst CO_COUNTRY_CODE = \"5802CO\";\n\nconst DIAN_KEYS = [\"NumFac\", \"CUFE\", \"Cufe\", \"NitFac\", \"ValFac\", \"ValTolFac\"] as const;\n\nfunction parseDianInvoice(qrData: string): ParseResult | null {\n\tconst hasDianMarker = DIAN_KEYS.some(\n\t\t(k) => qrData.includes(`${k}:`) || qrData.includes(`${k} :`),\n\t);\n\tif (!hasDianMarker) return null;\n\n\tconst fields: Record<string, string> = {};\n\tconst segments = qrData.split(/[\\n,;]+/);\n\tfor (const segment of segments) {\n\t\tconst idx = segment.indexOf(\":\");\n\t\tif (idx === -1) continue;\n\t\tconst key = segment.substring(0, idx).trim();\n\t\tconst value = segment.substring(idx + 1).trim();\n\t\tif (key && value && !fields[key]) fields[key] = value;\n\t}\n\n\tconst cufe = fields.CUFE || fields.Cufe || fields.cufe;\n\tconst numFac = fields.NumFac;\n\tconst paymentAddress = cufe || numFac;\n\n\tif (!paymentAddress) {\n\t\treturn failure(\"INVALID_QR\", \"Missing CUFE / NumFac in DIAN invoice QR\");\n\t}\n\n\treturn success({ paymentAddress });\n}\n\nfunction parseColombianEMV(qrData: string, sellPrice: number): ParseResult | null {\n\tconst isCO = qrData.includes(CO_COUNTRY_CODE);\n\tconst isCOP = qrData.includes(COP_CURRENCY_CODE);\n\tif (!isCO || !isCOP) return null;\n\n\tconst tags = extractTags(qrData, [\"54\", \"59\"]);\n\tconst merchantName = tags[\"59\"] || \"MERCHANT_NOT_FOUND\";\n\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tconst amountStr = tags[\"54\"];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (amount) result.amount = amount;\n\t}\n\n\treturn success(result);\n}\n\n/**\n * Parses a Colombian QR. Supports DIAN electronic invoice QRs\n * (key:value text with `CUFE`/`NumFac`) and EMVCo-encoded QRs from\n * Colombian payment networks (Nequi, Bre-B/RBM) identified by country\n * `5802CO` and currency `5303170`.\n */\nexport function parseCOP(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tconst dian = parseDianInvoice(trimmed);\n\tif (dian) return dian;\n\n\tconst emv = parseColombianEMV(trimmed, sellPrice);\n\tif (emv) return emv;\n\n\treturn failure(\"INVALID_QR\", \"Not a recognized Colombian QR code\");\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\n\nconst TRANSFERMOVIL_PREFIX = \"TRANSFERMOVIL_ETECSA\";\n\nconst CARD_INDEX = 2;\nconst PHONE_INDEX = 3;\nconst AMOUNT_INDEX = 4;\n\nfunction normalizePhone(phone: string): string | null {\n\tconst cleaned = phone.replace(/\\D/g, \"\");\n\tif (/^\\d{8}$/.test(cleaned)) return cleaned;\n\tif (/^53\\d{8}$/.test(cleaned)) return cleaned.slice(2);\n\treturn null;\n}\n\nfunction normalizeCard(card: string): string | null {\n\tconst cleaned = card.replace(/[\\s-]/g, \"\");\n\treturn /^\\d{16}$/.test(cleaned) ? cleaned : null;\n}\n\n/**\n * Parses a Cuban Transfermóvil QR code\n * (`TRANSFERMOVIL_ETECSA,<operation>,<card>,<phone>,<amount>`) and returns the\n * payment address as the compound `phone|card` pair used for CUP. The amount\n * field is optional — when present it is converted to usdc via `sellPrice`.\n */\nexport function parseTransfermovil(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tconst parts = trimmed.split(\",\").map((p) => p.trim());\n\n\tif (parts[0]?.toUpperCase() !== TRANSFERMOVIL_PREFIX) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Cuban (Transfermóvil) QR code\");\n\t}\n\n\tif (parts.length <= PHONE_INDEX) {\n\t\treturn failure(\"INVALID_QR\", \"Transfermóvil QR is missing card or phone fields\");\n\t}\n\n\tconst card = normalizeCard(parts[CARD_INDEX] ?? \"\");\n\tif (!card) {\n\t\treturn failure(\"INVALID_QR\", \"Transfermóvil QR has an invalid card number\");\n\t}\n\n\tconst phone = normalizePhone(parts[PHONE_INDEX] ?? \"\");\n\tif (!phone) {\n\t\treturn failure(\"INVALID_QR\", \"Transfermóvil QR has an invalid phone number\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: `${phone}|${card}` };\n\n\tconst amountStr = parts[AMOUNT_INDEX];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\n\nconst DEUNA_HOST = \"pagar.deuna.app\";\n\n/**\n * Parses a DeUna (Banco Pichincha, Ecuador) payment QR. DeUna QRs are plain URLs of\n * the form `https://pagar.deuna.app/<slug>/merchant?id=<hash>`; the `id` query param\n * is the merchant payment address. DeUna QRs carry no amount, so `_sellPrice` is unused.\n */\nexport function parseDeUna(qrData: string, _sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(qrData.trim());\n\t} catch {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid DeUna URL\");\n\t}\n\n\tif (url.hostname !== DEUNA_HOST) {\n\t\treturn failure(\"INVALID_QR\", `Unexpected DeUna host: ${url.hostname}`);\n\t}\n\n\tconst id = url.searchParams.get(\"id\");\n\tif (!id) {\n\t\treturn failure(\"INVALID_QR\", \"Missing DeUna merchant id\");\n\t}\n\n\treturn success({ paymentAddress: id });\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst QRIS_TAGS = { AMOUNT: \"54\", MERCHANT_NAME: \"59\" } as const;\n\nexport function parseQRIS(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tconst tags = extractTags(trimmed, [QRIS_TAGS.AMOUNT, QRIS_TAGS.MERCHANT_NAME]);\n\n\tconst merchantName = tags[QRIS_TAGS.MERCHANT_NAME];\n\tif (!merchantName) {\n\t\treturn failure(\"INVALID_QR\", \"Missing merchant name\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tconst amountStr = tags[QRIS_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\n\nconst UPI_ID_REGEX = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+$/;\n\nexport function parseUPI(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tlet paramString: string;\n\tif (trimmed.startsWith(\"upi://pay?\")) {\n\t\tparamString = trimmed.substring(10);\n\t} else if (trimmed.includes(\"?\")) {\n\t\tparamString = trimmed.split(\"?\")[1];\n\t} else {\n\t\tparamString = trimmed;\n\t}\n\n\tconst params = new URLSearchParams(paramString);\n\tconst pa = params.get(\"pa\");\n\n\tif (!pa) {\n\t\treturn failure(\"INVALID_QR\", \"Missing UPI payment address\");\n\t}\n\n\tif (!UPI_ID_REGEX.test(pa)) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid UPI ID format\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: pa };\n\n\tconst amountStr = params.get(\"am\");\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags, parseTLV } from \"../utils/tlv\";\n\nconst NGN_TAGS = {\n\tAMOUNT: \"54\",\n\tCURRENCY: \"53\",\n\tCOUNTRY: \"58\",\n\tMERCHANT_NAME: \"59\",\n} as const;\n\nconst NGN_CURRENCY_CODE = \"566\";\nconst NIBSS_AID = \"NG.COM.NIBSSPLC.QR\";\n\nfunction isNQR(qrData: string): boolean {\n\tif (qrData.includes(NIBSS_AID)) return true;\n\tif (qrData.includes(`5303${NGN_CURRENCY_CODE}`)) return true;\n\tif (qrData.includes(\"5802NG\")) return true;\n\treturn false;\n}\n\nfunction parseEMVCoNQR(qrData: string, sellPrice: number): ParseResult {\n\tconst crc = verifyCRC16(qrData);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid QR checksum\");\n\t}\n\n\tif (!isNQR(qrData)) {\n\t\treturn failure(\"INVALID_QR\", \"Not a Nigerian (NQR) QR code\");\n\t}\n\n\tconst tags = extractTags(qrData, [NGN_TAGS.AMOUNT, NGN_TAGS.MERCHANT_NAME]);\n\n\tconst merchantName = tags[NGN_TAGS.MERCHANT_NAME];\n\tif (!merchantName) {\n\t\treturn failure(\"INVALID_QR\", \"Missing merchant name\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tconst amountStr = tags[NGN_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n\nfunction parseSPD(qrData: string, sellPrice: number): ParseResult {\n\t// Short Payment Descriptor (Czech spec). Seen in the wild for some\n\t// Nigerian account-based QRs: `SPD*1.0*ACC:<nuban>*AM:<amount>*MSG:<text>*`.\n\tconst parts = qrData.split(\"*\").filter((p) => p.length > 0);\n\tif (parts.length < 2 || parts[0] !== \"SPD\") {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid SPD QR code\");\n\t}\n\n\tconst fields: Record<string, string> = {};\n\tfor (const part of parts.slice(2)) {\n\t\tconst colon = part.indexOf(\":\");\n\t\tif (colon === -1) continue;\n\t\tfields[part.substring(0, colon).toUpperCase()] = part.substring(colon + 1);\n\t}\n\n\tconst account = fields.ACC;\n\tif (!account) {\n\t\treturn failure(\"INVALID_QR\", \"Missing ACC field in SPD QR\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: account };\n\n\tconst amountStr = fields.AM;\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr.replace(/,/g, \"\"), sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n\n/**\n * Parses a Nigerian QR code. Supports NIBSS NQR (EMVCo MPM with AID\n * `NG.COM.NIBSSPLC.QR`) and the SPD `SPD*1.0*ACC:...*AM:...*` format.\n */\nexport function parseNGN(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tif (trimmed.startsWith(\"SPD*\")) {\n\t\treturn parseSPD(trimmed, sellPrice);\n\t}\n\n\t// Sanity-check that this looks like EMVCo TLV before CRC-ing.\n\tif (parseTLV(trimmed).length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Nigerian QR code\");\n\t}\n\n\treturn parseEMVCoNQR(trimmed, sellPrice);\n}\n","import { validatePeruvianQr } from \"../../country/qr-validator\";\nimport type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst PEN_TAGS = { AMOUNT: \"54\" } as const;\n\n/**\n * Parses a Peruvian Yape/Plin EMVCo QR. Uses the same structural + CRC check\n * as SELL upload (`validatePeruvianQr`: country PE, currency 604, CRC-16).\n * Returns the raw payload verbatim as `paymentAddress` so the merchant can\n * re-render the exact QR. Static QRs carry no amount; tag 54 converts via\n * `sellPrice`.\n */\nexport function parsePeru(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tif (!validatePeruvianQr(trimmed)) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Peru Yape/Plin QR\");\n\t}\n\n\tconst tags = extractTags(trimmed, [PEN_TAGS.AMOUNT]);\n\tconst result: ParsedQR = { paymentAddress: trimmed };\n\n\tconst amountStr = tags[PEN_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst PHP_TAGS = { AMOUNT: \"54\", CURRENCY: \"53\", COUNTRY: \"58\" } as const;\n\n/**\n * Parses a Philippine QR Ph EMVCo QR. Validates it is a genuine QR Ph\n * (country tag 58 `PH`, currency tag 53 `608`, valid CRC) and returns the raw\n * payload verbatim as `paymentAddress` so the buyer app can re-render the exact\n * QR. Wallet-proprietary GCash-only / Maya-only QRs are not QR Ph and are\n * rejected by the country/currency check. Static QRs carry no amount; when tag\n * 54 is present it is converted to usdc via `sellPrice`.\n */\nexport function parseQRPh(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tconst tags = extractTags(trimmed, [PHP_TAGS.AMOUNT, PHP_TAGS.CURRENCY, PHP_TAGS.COUNTRY]);\n\n\tif (tags[PHP_TAGS.COUNTRY] !== \"PH\" || tags[PHP_TAGS.CURRENCY] !== \"608\") {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid QR Ph code\");\n\t}\n\n\tconst crc = verifyCRC16(trimmed);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", crc.error ?? \"Invalid QR Ph checksum\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: trimmed };\n\n\tconst amountStr = tags[PHP_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import { validateVenezuelanQr } from \"../../country/qr-validator\";\nimport type { ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\n\n/**\n * Same envelope as SELL upload: opaque `base64?merchantId=NNNN` (blob ≥ 40).\n * Prefer `validateVenezuelanQr` from `@p2pdotme/sdk/country`; this alias stays\n * for `@p2pdotme/sdk/qr-parsers` callers.\n */\nexport function isPagoMovilQr(qrData: string): boolean {\n\treturn validateVenezuelanQr(qrData);\n}\n\n/**\n * Parses a Venezuelan Pago Móvil / Suiche 7B QR. Uses the same envelope as\n * SELL upload (`validateVenezuelanQr`). Returns the raw payload verbatim as\n * `paymentAddress` so the merchant can re-render the exact QR.\n */\nexport function parsePagoMovil(qrData: string, _sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tif (!validateVenezuelanQr(trimmed)) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Venezuelan QR code\");\n\t}\n\n\treturn success({ paymentAddress: trimmed });\n}\n","import { CURRENCY, getCountryOption } from \"../country\";\nimport { parseMercadoPago } from \"./parsers/ars\";\nimport { parseBolivia } from \"./parsers/bob\";\nimport { parsePIX } from \"./parsers/brl\";\nimport { parseCOP } from \"./parsers/cop\";\nimport { parseTransfermovil } from \"./parsers/cup\";\nimport { parseDeUna } from \"./parsers/ecu\";\nimport { parseQRIS } from \"./parsers/idr\";\nimport { parseUPI } from \"./parsers/inr\";\nimport { parseNGN } from \"./parsers/ngn\";\nimport { parsePeru } from \"./parsers/pen\";\nimport { parseQRPh } from \"./parsers/php\";\nimport { parsePagoMovil } from \"./parsers/ven\";\nimport type { ParseQRParams, ParseResult } from \"./types\";\nimport { failure } from \"./types\";\n\n/**\n * Parses a QR string for the given currency and returns the extracted payment data.\n *\n * This dispatcher is `async` because dynamic PIX (BRL) QRs store the amount\n * behind a location URL pointing at the issuing bank's PIX endpoint. The SDK\n * resolves that URL via a CORS-bypassing proxy (see `proxyUrl`), which returns\n * a signed JWT whose `valor.original` field holds the amount. All other parsers\n * (UPI, QRIS, QR Ph, MercadoPago, PagoMovil, Transfermóvil) and static PIX are synchronous and\n * resolve immediately.\n *\n * When the country has `validateQr` (PEN, VEN, BOB), Scan & Pay must pass the\n * same check as SELL upload — not a looser envelope.\n */\nexport async function parseQR(params: ParseQRParams): Promise<ParseResult> {\n\tconst { qrData, currency, sellPrice, proxyUrl, orderId } = params;\n\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tlet result: ParseResult;\n\tswitch (currency) {\n\t\tcase CURRENCY.INR:\n\t\t\tresult = parseUPI(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.IDR:\n\t\t\tresult = parseQRIS(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.BRL:\n\t\t\tresult = await parsePIX(qrData, sellPrice, { proxyUrl, orderId });\n\t\t\tbreak;\n\t\tcase CURRENCY.ARS:\n\t\t\tresult = parseMercadoPago(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.VEN:\n\t\t\tresult = parsePagoMovil(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.BOB:\n\t\t\tresult = parseBolivia(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.NGN:\n\t\t\tresult = parseNGN(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.COP:\n\t\t\tresult = parseCOP(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.CUP:\n\t\t\tresult = parseTransfermovil(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.ECU:\n\t\t\tresult = parseDeUna(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.PEN:\n\t\t\tresult = parsePeru(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.PHP:\n\t\t\tresult = parseQRPh(qrData, sellPrice);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn failure(\"INVALID_CURRENCY\", `Currency \"${currency}\" is not supported`);\n\t}\n\n\tconst validateQr = getCountryOption(currency)?.validateQr;\n\tif (result.isOk() && validateQr && !validateQr(qrData.trim())) {\n\t\treturn failure(\"INVALID_QR\", \"QR does not match catalog validateQr\");\n\t}\n\n\treturn result;\n}\n"],"mappings":"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","names":["isBolivianEncryptedQr","import_neverthrow"]}
1
+ {"version":3,"sources":["../src/qr-parsers/index.ts","../src/qr-parsers/errors.ts","../src/country/currency.ts","../src/country/currencies/ars.ts","../src/country/types.ts","../src/country/qr-validator.ts","../src/country/currencies/bob.ts","../src/country/currencies/brl.ts","../src/country/currencies/cop.ts","../src/country/currencies/cup.ts","../src/country/currencies/ecu.ts","../src/country/currencies/eur.ts","../src/country/currencies/idr.ts","../src/country/currencies/inr.ts","../src/country/currencies/mex.ts","../src/country/currencies/ngn.ts","../src/country/currencies/pen.ts","../src/country/currencies/php.ts","../src/country/currencies/usd.ts","../src/country/currencies/ven.ts","../src/country/countries.ts","../src/qr-parsers/types.ts","../src/qr-parsers/utils/crc16.ts","../src/qr-parsers/utils/tlv.ts","../src/qr-parsers/parsers/ars.ts","../src/qr-parsers/utils/amount.ts","../src/qr-parsers/parsers/bob.ts","../src/qr-parsers/parsers/brl.ts","../src/qr-parsers/parsers/cop.ts","../src/qr-parsers/parsers/cup.ts","../src/qr-parsers/parsers/ecu.ts","../src/qr-parsers/parsers/idr.ts","../src/qr-parsers/parsers/inr.ts","../src/qr-parsers/parsers/ngn.ts","../src/qr-parsers/parsers/pen.ts","../src/qr-parsers/parsers/php.ts","../src/qr-parsers/parsers/ven.ts","../src/qr-parsers/parse-qr.ts"],"sourcesContent":["export { QRParserError, type QRParserErrorCode } from \"./errors\";\nexport { parseQR } from \"./parse-qr\";\nexport { isPagoMovilQr } from \"./parsers/ven\";\nexport type {\n\tParsedQR,\n\tParseQRParams,\n\tParseResult,\n\tSupportedCurrency,\n} from \"./types\";\n","export type QRParserErrorCode =\n\t| \"INVALID_QR\"\n\t| \"INVALID_CURRENCY\"\n\t| \"INVALID_AMOUNT\"\n\t| \"FETCH_FAILED\";\n\nexport class QRParserError extends Error {\n\treadonly code: QRParserErrorCode;\n\treadonly cause?: unknown;\n\treadonly context?: Record<string, unknown>;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions: {\n\t\t\tcode: QRParserErrorCode;\n\t\t\tcause?: unknown;\n\t\t\tcontext?: Record<string, unknown>;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"QRParserError\";\n\t\tthis.code = options.code;\n\t\tthis.cause = options.cause;\n\t\tthis.context = options.context;\n\t}\n}\n","/**\n * All supported currency symbols. Single source of truth for the SDK.\n *\n * Lives alongside the country metadata so that adding a currency is a\n * single-folder operation: drop a new file in `currencies/<code>.ts`, add it\n * to this map, and both `COUNTRY_OPTIONS` and `ZodCurrencySchema` pick it up.\n */\nexport const CURRENCY = {\n\tIDR: \"IDR\",\n\tINR: \"INR\",\n\tBRL: \"BRL\",\n\tARS: \"ARS\",\n\tMEX: \"MEX\",\n\tVEN: \"VEN\",\n\tBOB: \"BOB\",\n\tEUR: \"EUR\",\n\tNGN: \"NGN\",\n\tUSD: \"USD\",\n\tCOP: \"COP\",\n\tCUP: \"CUP\",\n\tECU: \"ECU\",\n\tPEN: \"PEN\",\n\tPHP: \"PHP\",\n} as const;\n\n/** Union of supported currency codes. */\nexport type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];\n\n/**\n * Tuple form of the currency codes — used by `z.enum(...)` in the shared\n * validation layer. Narrow tuple type required by Zod.\n */\nexport const CURRENCY_CODES = Object.values(CURRENCY) as [CurrencyCode, ...CurrencyCode[]];\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const ARS_PLACEHOLDER = \"juan.perez\";\nexport const ARS_VALIDATION_ERROR =\n\t\"Please enter a valid CBU/CVU (22 digits) or Alias (6-20 characters)\";\n\n/** Validates CBU (Clave Bancaria Uniforme) — Argentine banking key, 22 digits with checksums. */\nfunction validateCBU(cbu: string): boolean {\n\tif (cbu.length !== 22) return false;\n\tif (/^(\\d)\\1{21}$/.test(cbu)) return false;\n\n\tconst bankCode = cbu.substring(0, 7);\n\tconst bankCheckDigit = parseInt(cbu[7], 10);\n\tlet sum = 0;\n\tconst weights = [7, 1, 3, 9, 7, 1, 3];\n\tfor (let i = 0; i < 7; i++) {\n\t\tsum += parseInt(bankCode[i], 10) * weights[i];\n\t}\n\tconst calculatedBankCheck = (10 - (sum % 10)) % 10;\n\tif (bankCheckDigit !== calculatedBankCheck) return false;\n\n\tconst accountNumber = cbu.substring(8, 21);\n\tconst accountCheckDigit = parseInt(cbu[21], 10);\n\tsum = 0;\n\tconst accountWeights = [3, 9, 7, 1, 3, 9, 7, 1, 3, 9, 7, 1, 3];\n\tfor (let i = 0; i < 13; i++) {\n\t\tsum += parseInt(accountNumber[i], 10) * accountWeights[i];\n\t}\n\tconst calculatedAccountCheck = (10 - (sum % 10)) % 10;\n\tif (accountCheckDigit !== calculatedAccountCheck) return false;\n\n\treturn true;\n}\n\n/**\n * Validates Argentine payment IDs (CBU, CVU, or Alias).\n * CBU/CVU: 22 digits with checksum. Alias: 6-20 alphanumeric characters.\n */\nexport function validateArgentinePaymentId(paymentId: string): boolean {\n\tif (!paymentId || paymentId.trim().length === 0) return false;\n\n\tconst trimmedPaymentId = paymentId.trim();\n\n\tif (/^\\d{22}$/.test(trimmedPaymentId)) return validateCBU(trimmedPaymentId);\n\tif (/^[a-zA-Z0-9.\\-_]{6,20}$/.test(trimmedPaymentId)) return true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for ARS (Argentina, ALIAS). */\nexport const ARS_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"alias\",\n\t\tlabel: \"ALIAS_ID\",\n\t\tplaceholder: ARS_PLACEHOLDER,\n\t\tdisplayLabel: \"Alias\",\n\t\tvalidate: validateArgentinePaymentId,\n\t\tvalidationErrorMessage: ARS_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Argentina (ARS). */\nexport const ARS_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Argentina\",\n\tcurrency: CURRENCY.ARS,\n\tsymbolNative: \"$\",\n\tlocale: \"es-AR\",\n\tpaymentMethod: \"ALIAS\",\n\tpaymentAddressName: \"ALIAS_ID\",\n\ttimezone: \"America/Argentina/Buenos_Aires\",\n\ttimezone_name: \"ART\",\n\tflag: \"🇦🇷\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e6-1f1f7.png\",\n\tphoneCode: \"+54\",\n\ttelegramSupportChannel: \"https://t.me/p2pmeargentina\",\n\ttwitterUsername: \"p2pmeargentina\",\n\tsmsCountryCodes: [\"AR\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import type { CurrencyCode } from \"../types\";\n\n/** Joins an optional QR payload with typed fields (`qr||phone|cci`). */\nexport const PACKED_PAYMENT_ID_SEP = \"||\";\n\nexport interface PaymentIdFieldConfig {\n\treadonly key: string;\n\treadonly label: string;\n\treadonly placeholder: string;\n\treadonly displayLabel: string | null;\n\treadonly validate: (value: string) => boolean;\n\treadonly validationErrorMessage: string;\n\t/**\n\t * When true, an empty value is allowed. If every field is optional, at least\n\t * one field must still be filled (see `validatePaymentIdFields`).\n\t */\n\treadonly optional?: boolean;\n}\n\nexport interface CountryOption {\n\treadonly country: string;\n\treadonly currency: CurrencyCode;\n\treadonly internationalFormat?: string;\n\treadonly symbolNative: string;\n\treadonly locale: string;\n\treadonly paymentMethod: string;\n\treadonly paymentAddressName: string;\n\treadonly timezone: string;\n\treadonly timezone_name: string;\n\treadonly flag: string;\n\treadonly phoneCode?: string;\n\treadonly flagUrl: string;\n\treadonly telegramSupportChannel: string;\n\treadonly twitterUsername: string;\n\treadonly smsCountryCodes: readonly string[];\n\treadonly precision: number;\n\treadonly isAlpha: boolean;\n\treadonly disabled: boolean;\n\treadonly disabledPaymentTypes: readonly string[];\n\t/**\n\t * Merchant/seller provides payment details by uploading a QR image.\n\t * Distinct from PAY, where the buyer scans a QR. Default: false.\n\t */\n\treadonly uploadPaymentQR?: boolean;\n\t/** Structural check for a standalone QR payload (no `||` pack). */\n\treadonly validateQr?: (payload: string) => boolean;\n\t/**\n\t * PAY display: payload to re-draw a stored QR. May be looser than\n\t * `validateQr` so old orders still render (e.g. Yape/Plin without CRC,\n\t * Pago Móvil without `merchantId`). Scan & Pay uses `validateQr` /\n\t * parsers, not this hook. Apps call `getPayQrPayload(currency, id)` —\n\t * do not branch on currency.\n\t */\n\treadonly getPayQrPayload?: (paymentId: string) => string | null;\n\t/**\n\t * Fill catalog fields from a validated QR (e.g. Yape/Plin phone in EMVCo).\n\t * Only used when the typed fallback did not already set the key.\n\t */\n\treadonly hydrateFieldsFromQr?: (qr: string) => Partial<Record<string, string>>;\n\t/**\n\t * i18n key for a warning shown before the payer sends fiat (e.g. a bank\n\t * outage). Apps call `getTransferWarning(currency)` — do not branch on\n\t * currency. Omit when there is no warning.\n\t */\n\treadonly transferWarning?: string;\n}\n","/**\n * QR payload checks for **SELL collection** (seller/merchant uploads a QR).\n *\n * Why this file exists: user-app and merchant-app share the same “is this\n * blob a payment QR we can store?” rules. Keeping them here means the apps\n * call `CountryOption.validateQr` instead of copying peru/ven parsers.\n *\n * This is not `@p2pdotme/sdk/qr-parsers`. Parsers are Scan & Pay: they\n * pull amount/address out of a live scan. Peru Scan & Pay (`parsePeru`)\n * reuses `validatePeruvianQr` so a bad CRC cannot become the stored ID.\n *\n * Image → string stays in the apps (`qr-scanner` needs a File / canvas).\n * This module only sees the decoded payload string.\n */\nimport { PACKED_PAYMENT_ID_SEP } from \"./types\";\n\nfunction crc16ccitt(s: string): string {\n\tlet crc = 0xffff;\n\tfor (let i = 0; i < s.length; i++) {\n\t\tcrc ^= s.charCodeAt(i) << 8;\n\t\tfor (let j = 0; j < 8; j++) {\n\t\t\tcrc = crc & 0x8000 ? (crc << 1) ^ 0x1021 : crc << 1;\n\t\t\tcrc &= 0xffff;\n\t\t}\n\t}\n\treturn crc.toString(16).toUpperCase().padStart(4, \"0\");\n}\n\n/**\n * Structural EMVCo upload check: parseable TLV, country tag 58, currency tag 53,\n * and a matching CRC-16/CCITT-FALSE (tag 63). CRC is required on upload so we\n * never persist a truncated screenshot. `getPayQrPayload` may still re-draw a\n * stored blob whose CRC later fails.\n */\nfunction validateEmvcoQr(payload: string, country: string, currency: string): boolean {\n\tif (!payload || payload.trim().length === 0) return false;\n\tconst data = payload.trim();\n\n\tconst tags: Record<string, string> = {};\n\tlet pos = 0;\n\twhile (pos + 4 <= data.length) {\n\t\tconst tag = data.substring(pos, pos + 2);\n\t\tconst lengthStr = data.substring(pos + 2, pos + 4);\n\t\tif (!/^[0-9]{2}$/.test(tag) || !/^[0-9]{2}$/.test(lengthStr)) break;\n\t\tconst length = Number.parseInt(lengthStr, 10);\n\t\tif (pos + 4 + length > data.length) break;\n\t\ttags[tag] = data.substring(pos + 4, pos + 4 + length);\n\t\tpos += 4 + length;\n\t}\n\n\tif (tags[\"58\"] !== country) return false;\n\tif (tags[\"53\"] !== currency) return false;\n\n\tconst crcTag = tags[\"63\"];\n\tif (!crcTag) return false;\n\n\tconst marker = data.lastIndexOf(\"6304\");\n\tif (marker === -1) return false;\n\n\tconst expected = crc16ccitt(data.substring(0, marker + 4));\n\treturn expected.toUpperCase() === crcTag.toUpperCase();\n}\n\n/**\n * Yape/Plin EMVCo: country PE, currency 604, CRC-16/CCITT-FALSE.\n * Same rule for SELL upload and Scan & Pay (`parsePeru`).\n * `getPayQrPayload` may still re-draw a stored blob whose CRC later fails.\n */\nexport function validatePeruvianQr(payload: string): boolean {\n\treturn validateEmvcoQr(payload, \"PE\", \"604\");\n}\n\n/**\n * Bolivia QR Simple encrypted envelope (Yape Bs, BancoSol, Banco Fie dynamic QR,\n * etc.): `<base64 ciphertext>|<hex checksum>`. The payload is bank-encrypted so we\n * cannot read account/amount — we only validate the envelope shape and store it\n * verbatim to re-render. The checksum is a hex digest whose length varies by bank\n * (24 hex for Banco Fie, 32 hex for Yape/BancoSol). Reject packed `||` IDs so a\n * stored compound string is never mistaken for a QR.\n */\nfunction isBolivianEncryptedQr(payload: string): boolean {\n\tconst trimmed = payload.trim();\n\tif (trimmed.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tconst sep = trimmed.lastIndexOf(\"|\");\n\tif (sep < 40) return false;\n\tconst blob = trimmed.slice(0, sep);\n\tconst tag = trimmed.slice(sep + 1);\n\tif (blob.length % 4 !== 0) return false;\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/.test(blob)) return false;\n\treturn /^(?:[0-9A-Fa-f]{24}|[0-9A-Fa-f]{32})$/.test(tag);\n}\n\n/**\n * Bolivia QR Simple. Accepts either the encrypted envelope (Yape Bs, BancoSol\n * dynamic QR — `<base64>|<32-hex>`) or an EMVCo static QR (country BO, currency\n * 068, CRC-16/CCITT-FALSE). CRC / envelope shape is required on upload so we\n * never persist a truncated screenshot.\n */\nexport function validateBolivianQr(payload: string): boolean {\n\tif (!payload || payload.trim().length === 0) return false;\n\treturn isBolivianEncryptedQr(payload) || validateEmvcoQr(payload, \"BO\", \"068\");\n}\n\n/**\n * Suiche 7B / Pago Móvil collection QR: `base64?merchantId=NNNN&…`.\n * The base64 is bank AES — we cannot decode phone/RIF from it, only the\n * envelope. Reject packed `||` IDs so a stored compound string is never\n * treated as a QR. Same rule for SELL upload and Scan & Pay (`parsePagoMovil`).\n * `getPayQrPayload` may still re-draw a stored blob without `merchantId`.\n */\nexport function validateVenezuelanQr(payload: string): boolean {\n\tif (!payload || typeof payload !== \"string\") return false;\n\tconst trimmed = payload.trim();\n\tif (trimmed.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tconst qIdx = trimmed.indexOf(\"?\");\n\tif (qIdx < 40) return false;\n\tconst blob = trimmed.substring(0, qIdx);\n\tif (!/^[A-Za-z0-9+/=]+$/.test(blob)) return false;\n\treturn /(?:^|[?&])merchantId=\\d{3,4}(?:&|$)/.test(trimmed.substring(qIdx));\n}\n\n/** Left side of `qr||fields`, or the whole string when not packed. */\nexport function payQrCandidate(paymentId: string): string {\n\tconst trimmed = paymentId.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\treturn (sep >= 0 ? trimmed.slice(0, sep) : trimmed).trim();\n}\n\n/**\n * PAY display: Yape/Plin EMVCo (PE / 604) even when CRC fails.\n * SELL upload still uses `validatePeruvianQr`.\n */\nexport function isPeruvianPayQr(payload: string): boolean {\n\tif (!payload.startsWith(\"0002\")) return false;\n\treturn payload.includes(\"5802PE\") && payload.includes(\"5303604\");\n}\n\n/**\n * PAY display: Pago Móvil envelope `base64?…` without requiring merchantId.\n * SELL upload still uses `validateVenezuelanQr`.\n */\nexport function isVenezuelanPayQr(payload: string): boolean {\n\tif (!payload || payload.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tconst qIdx = payload.indexOf(\"?\");\n\tif (qIdx === -1) return false;\n\tconst blob = payload.slice(0, qIdx);\n\treturn blob.length > 0 && /^[A-Za-z0-9+/=]+$/.test(blob);\n}\n\n/**\n * PAY display: QR Ph EMVCo (PH / 608). SELL PHP is `phone|bank-name`, not a QR.\n * CRC is not required here — Scan & Pay already gated it on create.\n */\nexport function isPhilippinePayQr(payload: string): boolean {\n\tif (!payload.startsWith(\"0002\")) return false;\n\treturn payload.includes(\"5802PH\") && payload.includes(\"5303608\");\n}\n\n/**\n * PAY display: Fonepay (Nepal NPQR) EMVCo QR. INR PAY orders accept these\n * through the NPCI–Fonepay cross-border link, so the payer's UPI app can scan\n * the exact merchant QR. Detected by the `fonepay.com` reverse-domain marker\n * and country tag 58 = NP. SELL INR is a plain UPI ID, not a QR.\n * CRC is not required here — Scan & Pay already gated it on create.\n */\nexport function isNepalFonepayQr(payload: string): boolean {\n\tif (!payload.startsWith(\"0002\")) return false;\n\treturn payload.includes(\"fonepay.com\") && payload.includes(\"5802NP\");\n}\n\n/**\n * PAY display: Bolivia QR Simple. EMVCo (BO / 068) even when CRC fails, or an\n * encrypted envelope `<base64>|<hex>` with a looser checksum than SELL upload\n * (16–64 hex vs 24/32). SELL still uses `validateBolivianQr`.\n */\nexport function isBolivianPayQr(payload: string): boolean {\n\tif (!payload || payload.includes(PACKED_PAYMENT_ID_SEP)) return false;\n\tif (payload.startsWith(\"0002\") && payload.includes(\"5802BO\") && payload.includes(\"5303068\")) {\n\t\treturn true;\n\t}\n\tconst sep = payload.lastIndexOf(\"|\");\n\tif (sep < 40) return false;\n\tconst blob = payload.slice(0, sep);\n\tconst tag = payload.slice(sep + 1);\n\tif (blob.length % 4 !== 0) return false;\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/.test(blob)) return false;\n\treturn /^[0-9A-Fa-f]{16,64}$/.test(tag);\n}\n","import { CURRENCY } from \"../currency\";\nimport { isBolivianPayQr, payQrCandidate, validateBolivianQr } from \"../qr-validator\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport { validateBolivianQr };\n\nexport const BOB_PLACEHOLDER = \"Bank account number (8–20 digits)\";\nexport const BOB_VALIDATION_ERROR = \"Please enter a valid Bolivian bank account number\";\n\n/**\n * Validates a Bolivian bank account number for QR Simple transfers.\n * Accepts 8–20 digits; spaces and dashes are ignored.\n */\nexport function validateBolivianAccount(account: string): boolean {\n\tif (!account || account.trim().length === 0) return false;\n\tconst cleaned = account.trim().replace(/[\\s-]/g, \"\");\n\treturn /^\\d{8,20}$/.test(cleaned);\n}\n\n/** Payment ID field configuration for BOB (Bolivia, QR Simple). */\nexport const BOB_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"account\",\n\t\tlabel: \"ACCOUNT_NUMBER\",\n\t\tplaceholder: BOB_PLACEHOLDER,\n\t\tdisplayLabel: \"Cuenta\",\n\t\tvalidate: validateBolivianAccount,\n\t\tvalidationErrorMessage: BOB_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Bolivia (BOB). */\nexport const BOB_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Bolivia\",\n\tcurrency: CURRENCY.BOB,\n\tinternationalFormat: \"BOB\",\n\tsymbolNative: \"Bs.\",\n\tlocale: \"es-BO\",\n\tpaymentMethod: \"QR_SIMPLE\",\n\tpaymentAddressName: \"QR_SIMPLE_DETAILS\",\n\ttimezone: \"America/La_Paz\",\n\ttimezone_name: \"BOT\",\n\tflag: \"🇧🇴\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e7-1f1f4.png\",\n\tphoneCode: \"+591\",\n\ttelegramSupportChannel: \"https://t.me/p2pme_bolivia_merchants\",\n\ttwitterUsername: \"p2pmebolivia\",\n\tsmsCountryCodes: [\"BO\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tuploadPaymentQR: true,\n\tvalidateQr: validateBolivianQr,\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isBolivianPayQr(candidate) ? candidate : null;\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const BRL_PLACEHOLDER = \"Chave pix ou pix copia e cola\";\nexport const BRL_VALIDATION_ERROR = \"Please enter a valid PIX ID\";\n\n/** Validates CPF (Brazilian tax ID for individuals). */\nfunction validateCPF(cpf: string): boolean {\n\tif (cpf.length !== 11) return false;\n\tif (/^(\\d)\\1{10}$/.test(cpf)) return false;\n\n\tlet sum = 0;\n\tfor (let i = 0; i < 9; i++) {\n\t\tsum += parseInt(cpf[i], 10) * (10 - i);\n\t}\n\tlet remainder = sum % 11;\n\tconst firstDigit = remainder < 2 ? 0 : 11 - remainder;\n\tif (parseInt(cpf[9], 10) !== firstDigit) return false;\n\n\tsum = 0;\n\tfor (let i = 0; i < 10; i++) {\n\t\tsum += parseInt(cpf[i], 10) * (11 - i);\n\t}\n\tremainder = sum % 11;\n\tconst secondDigit = remainder < 2 ? 0 : 11 - remainder;\n\treturn parseInt(cpf[10], 10) === secondDigit;\n}\n\n/** Validates CNPJ (Brazilian tax ID for companies). */\nfunction validateCNPJ(cnpj: string): boolean {\n\tif (cnpj.length !== 14) return false;\n\tif (/^(\\d)\\1{13}$/.test(cnpj)) return false;\n\n\tlet sum = 0;\n\tconst weights1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];\n\tfor (let i = 0; i < 12; i++) {\n\t\tsum += parseInt(cnpj[i], 10) * weights1[i];\n\t}\n\tlet remainder = sum % 11;\n\tconst firstDigit = remainder < 2 ? 0 : 11 - remainder;\n\tif (parseInt(cnpj[12], 10) !== firstDigit) return false;\n\n\tsum = 0;\n\tconst weights2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];\n\tfor (let i = 0; i < 13; i++) {\n\t\tsum += parseInt(cnpj[i], 10) * weights2[i];\n\t}\n\tremainder = sum % 11;\n\tconst secondDigit = remainder < 2 ? 0 : 11 - remainder;\n\treturn parseInt(cnpj[13], 10) === secondDigit;\n}\n\n/**\n * Validates PIX ID format.\n * PIX can be: CPF (11 digits), CNPJ (14 digits), email, phone (10–11 digits or E.164),\n * random key (UUID), or a PIX \"copia e cola\" EMV QR payload (starts with 000201).\n */\nexport function validatePIXId(pixId: string): boolean {\n\tif (!pixId || pixId.trim().length === 0) return false;\n\n\tconst trimmedPixId = pixId.trim();\n\n\t// PIX \"copia e cola\" — EMV QR code payload\n\tif (/^000201/.test(trimmedPixId)) return true;\n\n\t// 11 digits: valid CPF or Brazilian mobile phone key (DDD 11–99 + digit 9 + 8 digits)\n\tif (/^\\d{11}$/.test(trimmedPixId))\n\t\treturn validateCPF(trimmedPixId) || /^[1-9][1-9]9\\d{8}$/.test(trimmedPixId);\n\tif (/^\\d{14}$/.test(trimmedPixId)) return validateCNPJ(trimmedPixId);\n\tif (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmedPixId)) return true;\n\t// Phone key: 10–11 digits (with or without country code prefix)\n\tif (/^\\d{10,11}$/.test(trimmedPixId)) return true;\n\tif (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(trimmedPixId))\n\t\treturn true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for BRL (Brazil, PIX). */\nexport const BRL_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"pix\",\n\t\tlabel: \"PIX_ID\",\n\t\tplaceholder: BRL_PLACEHOLDER,\n\t\tdisplayLabel: \"PIX ID\",\n\t\tvalidate: validatePIXId,\n\t\tvalidationErrorMessage: BRL_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Brazil (BRL). */\nexport const BRL_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Brazil\",\n\tcurrency: CURRENCY.BRL,\n\tsymbolNative: \"R$\",\n\tlocale: \"pt-BR\",\n\tpaymentMethod: \"PIX\",\n\tpaymentAddressName: \"PIX_ID\",\n\ttimezone: \"America/Sao_Paulo\",\n\ttimezone_name: \"BRT\",\n\tflag: \"🇧🇷\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e7-1f1f7.png\",\n\tphoneCode: \"+55\",\n\ttelegramSupportChannel: \"https://t.me/p2pmebrasil\",\n\ttwitterUsername: \"p2pmebrasil\",\n\tsmsCountryCodes: [\"BR\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const COP_PLACEHOLDER = \"3001234567 / juan.perez@nequi.com.co / @alias\";\nexport const COP_VALIDATION_ERROR =\n\t\"Please enter a valid Colombian payment ID (e.g., 3001234567, email, or @alias)\";\n\n/**\n * Validates Colombian payment ID for Nequi, Daviplata, or Bre-B.\n * Accepts a 10-digit phone number starting with 3, a valid email address,\n * or a Bre-B alias starting with @ (e.g. @juanperez).\n */\nexport function validateColombianPaymentId(paymentId: string): boolean {\n\tif (!paymentId || paymentId.trim().length === 0) return false;\n\tconst trimmed = paymentId.trim();\n\tif (/^3\\d{9}$/.test(trimmed)) return true;\n\tif (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmed)) return true;\n\tif (/^@\\S+$/.test(trimmed)) return true;\n\treturn false;\n}\n\n/** Payment ID field configuration for COP (Colombia, Transferencia). */\nexport const COP_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"alias\",\n\t\tlabel: \"ALIAS_TRANSFERENCIA\",\n\t\tplaceholder: COP_PLACEHOLDER,\n\t\tdisplayLabel: \"Nequi / Daviplata / Bre-B\",\n\t\tvalidate: validateColombianPaymentId,\n\t\tvalidationErrorMessage: COP_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Colombia (COP). */\nexport const COP_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Colombia\",\n\tcurrency: CURRENCY.COP,\n\tsymbolNative: \"$\",\n\tlocale: \"es-CO\",\n\tpaymentMethod: \"TRANSFERENCIA\",\n\tpaymentAddressName: \"ALIAS_TRANSFERENCIA\",\n\ttimezone: \"America/Bogota\",\n\ttimezone_name: \"COT\",\n\tflag: \"🇨🇴\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e8-1f1f4.png\",\n\tphoneCode: \"+57\",\n\ttelegramSupportChannel: \"https://t.me/p2pmecolombiaclientes\",\n\ttwitterUsername: \"p2pmeColombia\",\n\tsmsCountryCodes: [\"CO\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const CUP_PLACEHOLDER_PHONE = \"54004417\";\nexport const CUP_PLACEHOLDER_CARD = \"9227 9598 7238 3620\";\n\nexport const CUP_VALIDATION_ERROR_PHONE =\n\t\"Please enter a valid 8-digit phone number (e.g., 54004417)\";\nexport const CUP_VALIDATION_ERROR_CARD =\n\t\"Please enter a valid 16-digit card number (e.g., 9227 9598 7238 3620)\";\n\n/**\n * Validates Cuban phone number for Transfermóvil.\n * Format: 8 digits, optionally prefixed with the +53 country code.\n */\nexport function validateCubanPhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\n\tconst cleaned = phoneNumber.trim().replace(/\\D/g, \"\");\n\n\tif (/^\\d{8}$/.test(cleaned)) return true;\n\tif (/^53\\d{8}$/.test(cleaned)) return true;\n\n\treturn false;\n}\n\n/**\n * Validates Cuban bank card number for Transfermóvil.\n * Format: 16 digits, spaces allowed (e.g., \"9227 9598 7238 3620\").\n */\nexport function validateCubanCardNumber(cardNumber: string): boolean {\n\tif (!cardNumber || cardNumber.trim().length === 0) return false;\n\tconst cleaned = cardNumber.trim().replace(/[\\s-]/g, \"\");\n\treturn /^\\d{16}$/.test(cleaned);\n}\n\n/** Payment ID field configuration for CUP (Cuba, Transfermóvil). */\nexport const CUP_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: CUP_PLACEHOLDER_PHONE,\n\t\tdisplayLabel: \"Phone\",\n\t\tvalidate: validateCubanPhoneNumber,\n\t\tvalidationErrorMessage: CUP_VALIDATION_ERROR_PHONE,\n\t},\n\t{\n\t\tkey: \"card\",\n\t\tlabel: \"CARD_NUMBER\",\n\t\tplaceholder: CUP_PLACEHOLDER_CARD,\n\t\tdisplayLabel: \"Card\",\n\t\tvalidate: validateCubanCardNumber,\n\t\tvalidationErrorMessage: CUP_VALIDATION_ERROR_CARD,\n\t},\n];\n\n/** Country option for Cuba (CUP). Transfermóvil transfers, including QR payments. */\nexport const CUP_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Cuba\",\n\tcurrency: CURRENCY.CUP,\n\tsymbolNative: \"$\",\n\tlocale: \"es-CU\",\n\tpaymentMethod: \"TRANSFERMOVIL\",\n\tpaymentAddressName: \"TRANSFERMOVIL_DETAILS\",\n\ttimezone: \"America/Havana\",\n\ttimezone_name: \"CST\",\n\tflag: \"🇨🇺\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1e8-1f1fa.png\",\n\tphoneCode: \"+53\",\n\ttelegramSupportChannel: \"https://t.me/p2pdotmecuba\",\n\ttwitterUsername: \"P2Pdotmecuba\",\n\tsmsCountryCodes: [\"CU\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\ttransferWarning: \"CUP_BANDEC_WARNING\",\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\n// ── Placeholders & error messages ────────────────────────────────────────────\n\nexport const ECU_PLACEHOLDER_BANK = \"Banco Pichincha\";\nexport const ECU_PLACEHOLDER_ACCOUNT_TYPE = \"Savings / Checking\";\nexport const ECU_PLACEHOLDER_ACCOUNT_NUMBER = \"2100123456\";\nexport const ECU_PLACEHOLDER_NAME = \"Juan Pérez\";\nexport const ECU_PLACEHOLDER_CEDULA = \"1710034065\";\n\nexport const ECU_BANK_VALIDATION_ERROR = \"Please enter the bank name\";\nexport const ECU_ACCOUNT_TYPE_VALIDATION_ERROR = \"Please enter the account type\";\nexport const ECU_ACCOUNT_NUMBER_VALIDATION_ERROR =\n\t\"Please enter a valid account number (4-20 digits)\";\nexport const ECU_NAME_VALIDATION_ERROR = \"Please enter the account holder name\";\nexport const ECU_CEDULA_VALIDATION_ERROR = \"Please enter a valid Ecuadorian cédula or RUC\";\n\n// ── Validators ───────────────────────────────────────────────────────────────\n\nfunction isValidCedulaCore(cedula: string): boolean {\n\tif (!/^\\d{10}$/.test(cedula)) return false;\n\tconst province = Number(cedula.slice(0, 2));\n\tif (province < 1 || province > 24) return false;\n\tif (Number(cedula[2]) >= 6) return false;\n\n\tconst coefficients = [2, 1, 2, 1, 2, 1, 2, 1, 2];\n\tlet sum = 0;\n\tfor (let i = 0; i < 9; i++) {\n\t\tlet product = Number(cedula[i]) * coefficients[i];\n\t\tif (product > 9) product -= 9;\n\t\tsum += product;\n\t}\n\tconst checkDigit = sum % 10 === 0 ? 0 : 10 - (sum % 10);\n\treturn checkDigit === Number(cedula[9]);\n}\n\n/**\n * Validates an Ecuadorian cédula (10-digit, módulo-10 checksum) or a 13-digit RUC\n * whose first 10 digits form a valid cédula. Province must be 01–24 and the third\n * digit < 6 (natural person).\n */\nexport function validateEcuadorianCedula(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\tconst cleaned = value.trim().replace(/\\D/g, \"\");\n\tif (cleaned.length === 13) return isValidCedulaCore(cleaned.slice(0, 10));\n\tif (cleaned.length !== 10) return false;\n\treturn isValidCedulaCore(cleaned);\n}\n\n/** Validates an Ecuadorian bank account number — 4–20 digits after stripping separators. */\nexport function validateEcuadorianAccountNumber(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\tconst cleaned = value.trim().replace(/\\D/g, \"\");\n\treturn /^\\d{4,20}$/.test(cleaned);\n}\n\n/** Validates an account holder name — non-empty, letters/spaces/apostrophe/period/hyphen only. */\nexport function validateEcuadorianAccountName(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\treturn /^[A-Za-zÀ-ÿ][A-Za-zÀ-ÿ .'-]{1,}$/.test(value.trim());\n}\n\n// ── Payment fields ───────────────────────────────────────────────────────────\n\n/** Payment ID field configuration for ECU (Ecuador, bank transfer — all free text). */\nexport const ECU_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"bank-name\",\n\t\tlabel: \"BANK_NAME\",\n\t\tplaceholder: ECU_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Bank\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: ECU_BANK_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-type\",\n\t\tlabel: \"ACCOUNT_TYPE\",\n\t\tplaceholder: ECU_PLACEHOLDER_ACCOUNT_TYPE,\n\t\tdisplayLabel: \"Account Type\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: ECU_ACCOUNT_TYPE_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-number\",\n\t\tlabel: \"ACCOUNT_NUMBER\",\n\t\tplaceholder: ECU_PLACEHOLDER_ACCOUNT_NUMBER,\n\t\tdisplayLabel: \"Account Number\",\n\t\tvalidate: validateEcuadorianAccountNumber,\n\t\tvalidationErrorMessage: ECU_ACCOUNT_NUMBER_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-name\",\n\t\tlabel: \"NAME\",\n\t\tplaceholder: ECU_PLACEHOLDER_NAME,\n\t\tdisplayLabel: \"Name\",\n\t\tvalidate: validateEcuadorianAccountName,\n\t\tvalidationErrorMessage: ECU_NAME_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"cedula\",\n\t\tlabel: \"CEDULA\",\n\t\tplaceholder: ECU_PLACEHOLDER_CEDULA,\n\t\tdisplayLabel: \"Cédula\",\n\t\tvalidate: validateEcuadorianCedula,\n\t\tvalidationErrorMessage: ECU_CEDULA_VALIDATION_ERROR,\n\t},\n];\n\n// ── Country option ───────────────────────────────────────────────────────────\n\n/** Country option for Ecuador (ECU — USD-denominated, bank transfer + DeUna QR). */\nexport const ECU_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Ecuador\",\n\tcurrency: CURRENCY.ECU,\n\tinternationalFormat: \"USD\",\n\tsymbolNative: \"$\",\n\tlocale: \"es-EC\",\n\tpaymentMethod: \"TRANSFERENCIA\",\n\tpaymentAddressName: \"ECU_BANK_DETAILS\",\n\ttimezone: \"America/Guayaquil\",\n\ttimezone_name: \"ECT\",\n\tflag: \"🇪🇨\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1ea-1f1e8.png\",\n\tphoneCode: \"+593\",\n\ttelegramSupportChannel: \"https://t.me/Ecuador_P2P\",\n\ttwitterUsername: \"P2Pdotme_Ecu\",\n\tsmsCountryCodes: [],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const EUR_PLACEHOLDER = \"@username or email\";\nexport const EUR_VALIDATION_ERROR = \"Please enter a valid Revolut ID (username, email, or phone)\";\n\n/**\n * Validates Revolut ID (username, email, or phone number).\n */\nexport function validateRevolutId(revolutId: string): boolean {\n\tif (!revolutId || revolutId.trim().length === 0) return false;\n\n\tconst trimmed = revolutId.trim();\n\n\tif (/^@?[a-zA-Z0-9._-]{3,30}$/.test(trimmed)) return true;\n\tif (/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(trimmed)) return true;\n\tif (/^\\+?\\d{7,15}$/.test(trimmed)) return true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for EUR (Revolut EUR). */\nexport const EUR_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"revolut\",\n\t\tlabel: \"REVOLUT_ID\",\n\t\tplaceholder: EUR_PLACEHOLDER,\n\t\tdisplayLabel: \"Revolut ID\",\n\t\tvalidate: validateRevolutId,\n\t\tvalidationErrorMessage: EUR_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Revolut EUR. */\nexport const EUR_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Revolut EUR\",\n\tcurrency: CURRENCY.EUR,\n\tsymbolNative: \"€\",\n\tlocale: \"de-DE\",\n\tpaymentMethod: \"REVOLUT\",\n\tpaymentAddressName: \"REVOLUT_ID\",\n\ttimezone: \"Europe/Berlin\",\n\ttimezone_name: \"CET\",\n\tflag: \"\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f310.png\",\n\tphoneCode: \"\",\n\ttelegramSupportChannel: \"https://t.me/P2Pdotme\",\n\ttwitterUsername: \"P2Pdotme\",\n\tsmsCountryCodes: [],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: true,\n\tdisabledPaymentTypes: [\"PAY\"],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const IDR_PLACEHOLDER = \"8123456789\";\nexport const IDR_VALIDATION_ERROR =\n\t\"Please enter a valid Indonesian phone number (e.g., 8123456789)\";\n\n/**\n * Validates Indonesian phone number.\n * Validates just the number part (9-12 digits).\n */\nexport function validateIndonesianPhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\tif (/[a-zA-Z]/.test(phoneNumber)) return false;\n\tconst cleaned = phoneNumber.replace(/\\D/g, \"\");\n\treturn /^\\d{9,12}$/.test(cleaned);\n}\n\n/** Payment ID field configuration for IDR (Indonesia, QRIS). */\nexport const IDR_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: IDR_PLACEHOLDER,\n\t\tdisplayLabel: \"Phone Number\",\n\t\tvalidate: validateIndonesianPhoneNumber,\n\t\tvalidationErrorMessage: IDR_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Indonesia (IDR). */\nexport const IDR_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Indonesia\",\n\tcurrency: CURRENCY.IDR,\n\tsymbolNative: \"Rp\",\n\tlocale: \"id-ID\",\n\tpaymentMethod: \"QRIS\",\n\tpaymentAddressName: \"PHONE_NUMBER\",\n\ttimezone: \"Asia/Jakarta\",\n\ttimezone_name: \"WIB\",\n\tflag: \"🇮🇩\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1ee-1f1e9.png\",\n\tphoneCode: \"+62\",\n\ttelegramSupportChannel: \"https://t.me/p2pmeindonesia\",\n\ttwitterUsername: \"p2pdotmeID\",\n\tsmsCountryCodes: [],\n\tprecision: 0,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport { isNepalFonepayQr, payQrCandidate } from \"../qr-validator\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const INR_PLACEHOLDER = \"merchant@upi\";\nexport const INR_VALIDATION_ERROR = \"Please enter a valid UPI ID (e.g., username@bankname)\";\n\n/**\n * Validates UPI ID format.\n * UPI ID format: username@bankname (e.g., john@paytm, user@ybl, 8658404239@kotak811)\n */\nexport function validateUPIId(upiId: string): boolean {\n\tif (!upiId || upiId.trim().length === 0) return false;\n\tconst upiRegex = /^[a-zA-Z0-9.\\-_]{2,256}@[a-zA-Z0-9]{2,64}$/;\n\treturn upiRegex.test(upiId.trim());\n}\n\n/** Payment ID field configuration for INR (India, UPI). */\nexport const INR_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"upi\",\n\t\tlabel: \"UPI_ID\",\n\t\tplaceholder: INR_PLACEHOLDER,\n\t\tdisplayLabel: \"UPI ID\",\n\t\tvalidate: validateUPIId,\n\t\tvalidationErrorMessage: INR_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for India (INR). */\nexport const INR_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"India\",\n\tcurrency: CURRENCY.INR,\n\tsymbolNative: \"₹\",\n\tlocale: \"en-IN\",\n\tpaymentMethod: \"UPI\",\n\tpaymentAddressName: \"UPI_ID\",\n\ttimezone: \"Asia/Kolkata\",\n\ttimezone_name: \"IST\",\n\tflag: \"🇮🇳\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1ee-1f1f3.png\",\n\tphoneCode: \"+91\",\n\ttelegramSupportChannel: \"https://t.me/P2Pdotme\",\n\ttwitterUsername: \"P2Pdotme\",\n\tsmsCountryCodes: [\"IN\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isNepalFonepayQr(candidate) ? candidate : null;\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const MEX_PLACEHOLDER = \"012345678901234567\";\nexport const MEX_VALIDATION_ERROR =\n\t\"Please enter a valid CLABE (18 digits), card number, or phone number\";\n\n/**\n * Validates Mexican payment IDs (CLABE, card number, or phone number).\n * CLABE: 18 digits. Card: 16 digits. Phone: 10 digits.\n */\nexport function validateMexicanPaymentId(paymentId: string): boolean {\n\tif (!paymentId || paymentId.trim().length === 0) return false;\n\n\tconst trimmed = paymentId.trim().replace(/\\D/g, \"\");\n\n\tif (/^\\d{18}$/.test(trimmed)) return true;\n\tif (/^\\d{16}$/.test(trimmed)) return true;\n\tif (/^\\d{10}$/.test(trimmed)) return true;\n\n\treturn false;\n}\n\n/** Payment ID field configuration for MEX (Mexico, SPEI). */\nexport const MEX_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"clabe\",\n\t\tlabel: \"CLABE_ID\",\n\t\tplaceholder: MEX_PLACEHOLDER,\n\t\tdisplayLabel: \"CLABE\",\n\t\tvalidate: validateMexicanPaymentId,\n\t\tvalidationErrorMessage: MEX_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Mexico (MEX). */\nexport const MEX_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Mexico\",\n\tcurrency: CURRENCY.MEX,\n\tinternationalFormat: \"MXN\",\n\tsymbolNative: \"Mx\",\n\tlocale: \"es-MX\",\n\tpaymentMethod: \"SPEI\",\n\tpaymentAddressName: \"CLABE_ID\",\n\ttimezone: \"America/Mexico_City\",\n\ttimezone_name: \"CST\",\n\tflag: \"🇲🇽\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f2-1f1fd.png\",\n\tphoneCode: \"+52\",\n\ttelegramSupportChannel: \"https://t.me/p2pdotmex\",\n\ttwitterUsername: \"p2pmemexico\",\n\tsmsCountryCodes: [\"MX\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: true,\n\tdisabledPaymentTypes: [\"PAY\"],\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const NGN_PLACEHOLDER = \"0123456789\";\nexport const NGN_PLACEHOLDER_BANK = \"Bank Name\";\nexport const NGN_PLACEHOLDER_ACCOUNT_NAME = \"Account Name\";\nexport const NGN_VALIDATION_ERROR = \"Please enter a valid 10-digit account number\";\nexport const NGN_BANK_NAME_VALIDATION_ERROR = \"Please enter a valid bank name\";\nexport const NGN_ACCOUNT_NAME_VALIDATION_ERROR = \"Please enter a valid account name\";\n\n/**\n * Validates Nigerian bank account number (NUBAN format, 10 digits).\n */\nexport function validateNigerianAccountNumber(accountNumber: string): boolean {\n\tif (!accountNumber || accountNumber.trim().length === 0) return false;\n\tconst cleaned = accountNumber.trim().replace(/\\D/g, \"\");\n\treturn /^\\d{10}$/.test(cleaned);\n}\n\n/**\n * Validates a Nigerian bank account holder name (non-empty, letters/spaces/.'- only).\n */\nexport function validateNigerianAccountName(accountName: string): boolean {\n\tif (!accountName || accountName.trim().length === 0) return false;\n\treturn /^[A-Za-z][A-Za-z .'-]{1,}$/.test(accountName.trim());\n}\n\n/** Payment ID field configuration for NGN (Nigeria, NIP). */\nexport const NGN_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"account\",\n\t\tlabel: \"ACCOUNT_NUMBER\",\n\t\tplaceholder: NGN_PLACEHOLDER,\n\t\tdisplayLabel: \"Account Number\",\n\t\tvalidate: validateNigerianAccountNumber,\n\t\tvalidationErrorMessage: NGN_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"bank-name\",\n\t\tlabel: \"BANK_NAME\",\n\t\tplaceholder: NGN_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Bank Name\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: NGN_BANK_NAME_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"account-name\",\n\t\tlabel: \"ACCOUNT_NAME\",\n\t\tplaceholder: NGN_PLACEHOLDER_ACCOUNT_NAME,\n\t\tdisplayLabel: \"Account Name\",\n\t\tvalidate: validateNigerianAccountName,\n\t\tvalidationErrorMessage: NGN_ACCOUNT_NAME_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Nigeria (NGN). */\nexport const NGN_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Nigeria\",\n\tcurrency: CURRENCY.NGN,\n\tsymbolNative: \"₦\",\n\tlocale: \"en-NG\",\n\tpaymentMethod: \"NIP\",\n\tpaymentAddressName: \"ACCOUNT_NUMBER\",\n\ttimezone: \"Africa/Lagos\",\n\ttimezone_name: \"WAT\",\n\tflag: \"🇳🇬\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f3-1f1ec.png\",\n\tphoneCode: \"+234\",\n\ttelegramSupportChannel: \"https://t.me/p2pmengn\",\n\ttwitterUsername: \"p2pmeNigeria\",\n\tsmsCountryCodes: [],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n};\n","import { CURRENCY } from \"../currency\";\nimport { isPeruvianPayQr, payQrCandidate, validatePeruvianQr } from \"../qr-validator\";\nimport { type CountryOption, PACKED_PAYMENT_ID_SEP, type PaymentIdFieldConfig } from \"../types\";\n\nexport { validatePeruvianQr };\n\nexport const PEN_PLACEHOLDER = \"CCI (20 digits) or Yape/Plin phone (9XXXXXXXX)\";\nexport const PEN_PHONE_PLACEHOLDER = \"PERU_PHONE_PLACEHOLDER\";\nexport const PEN_CCI_PLACEHOLDER = \"PERU_CCI_PLACEHOLDER\";\nexport const PEN_VALIDATION_ERROR = \"Please enter a valid 20-digit CCI or Yape/Plin phone number\";\nexport const PEN_PHONE_VALIDATION_ERROR = \"Please enter a valid Yape/Plin phone (9XXXXXXXX)\";\nexport const PEN_CCI_VALIDATION_ERROR = \"Please enter a valid 20-digit CCI\";\n\n/** Validates a 20-digit Peruvian CCI (spaces ignored). */\nexport function validatePeruvianCci(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\treturn /^\\d{20}$/.test(value.trim().replace(/\\s+/g, \"\"));\n}\n\n/**\n * Validates a Yape/Plin phone: `9` + 8 digits, optional `+51` / `51` prefix.\n */\nexport function validatePeruvianPhone(value: string): boolean {\n\tif (!value || value.trim().length === 0) return false;\n\tconst phone = value\n\t\t.trim()\n\t\t.replace(/\\s+/g, \"\")\n\t\t.replace(/^\\+?51/, \"\");\n\treturn /^9\\d{8}$/.test(phone);\n}\n\n/**\n * Validates a Peruvian payment key for the legacy single-field path.\n * Accepts either a 20-digit CCI or a Yape/Plin phone number.\n */\nexport function validatePeruvianPaymentKey(value: string): boolean {\n\treturn validatePeruvianCci(value) || validatePeruvianPhone(value);\n}\n\n/** Normalizes a CCI or Yape/Plin phone to the stored digit form. */\nexport function normalizePeruvianPaymentKey(value: string): string {\n\tconst trimmed = value.trim();\n\tconst cci = trimmed.replace(/\\s+/g, \"\");\n\tif (/^\\d{20}$/.test(cci)) return cci;\n\tconst phone = trimmed.replace(/\\s+/g, \"\").replace(/^\\+?51/, \"\");\n\tif (/^9\\d{8}$/.test(phone)) return phone;\n\treturn cci;\n}\n\nexport type PeruvianPaymentIdParts = {\n\tqr: string | null;\n\tphone: string | null;\n\tcci: string | null;\n};\n\nfunction looksLikeTlv(value: string): boolean {\n\tif (value.length < 5 || !/^\\d{4}/.test(value)) return false;\n\tconst len = Number.parseInt(value.slice(2, 4), 10);\n\treturn !Number.isNaN(len) && len >= 0 && 4 + len <= value.length;\n}\n\n/**\n * Phone from a TLV value. Skips hex hashes (Yape tag 39) so `998765432`\n * inside a checksum never counts. Allows `+51` / `51` glued to a 9XXXXXXXX.\n */\nfunction phoneFromTlvValue(value: string): string | null {\n\tconst trimmed = value.trim();\n\tif (!trimmed) return null;\n\tif (validatePeruvianPhone(trimmed)) return normalizePeruvianPaymentKey(trimmed);\n\tif (/^[0-9a-f]+$/i.test(trimmed) && /[a-f]/i.test(trimmed)) return null;\n\tconst match = trimmed.match(/(^|[^0-9])(\\+?51)?(9\\d{8})(?![0-9])/);\n\tif (!match) return null;\n\tconst candidate = `${match[2] ?? \"\"}${match[3]}`;\n\treturn validatePeruvianPhone(candidate) ? normalizePeruvianPaymentKey(candidate) : null;\n}\n\n/**\n * Walks EMVCo TLV (merchant-account templates, additional data, and any nested\n * TLV) and returns the first Yape/Plin phone found.\n */\nfunction extractPeruvianPhoneFromQr(payload: string): string | null {\n\tlet pos = 0;\n\twhile (pos + 4 <= payload.length) {\n\t\tconst tag = payload.slice(pos, pos + 2);\n\t\tconst len = Number.parseInt(payload.slice(pos + 2, pos + 4), 10);\n\t\tif (Number.isNaN(len) || len < 0 || pos + 4 + len > payload.length) break;\n\t\tconst value = payload.slice(pos + 4, pos + 4 + len);\n\t\tconst direct = phoneFromTlvValue(value);\n\t\tif (direct) return direct;\n\t\tif ((tag >= \"02\" && tag <= \"51\") || tag === \"62\" || tag === \"64\" || looksLikeTlv(value)) {\n\t\t\tconst nested = extractPeruvianPhoneFromQr(value);\n\t\t\tif (nested) return nested;\n\t\t}\n\t\tpos += 4 + len;\n\t}\n\tconst glued = payload.match(/\\+51\\s*9\\d{8}/);\n\tif (glued && validatePeruvianPhone(glued[0])) {\n\t\treturn normalizePeruvianPaymentKey(glued[0]);\n\t}\n\treturn null;\n}\n\nfunction parsePeruvianFallback(value: string): Pick<PeruvianPaymentIdParts, \"phone\" | \"cci\"> {\n\tconst trimmed = value.trim();\n\tif (!trimmed) return { phone: null, cci: null };\n\tconst tokens = trimmed\n\t\t.split(\"|\")\n\t\t.map((token) => token.trim())\n\t\t.filter(Boolean);\n\tlet phone: string | null = null;\n\tlet cci: string | null = null;\n\tfor (const token of tokens) {\n\t\tif (!phone && validatePeruvianPhone(token)) {\n\t\t\tphone = normalizePeruvianPaymentKey(token);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!cci && validatePeruvianCci(token)) {\n\t\t\tcci = normalizePeruvianPaymentKey(token);\n\t\t}\n\t}\n\treturn { phone, cci };\n}\n\n/**\n * Splits a stored PEN payment ID into QR payload and/or typed fallback.\n * Formats: EMVCo blob, CCI, phone, `phone|cci`, or `blob||fallback`.\n */\nexport function parsePeruvianPaymentId(value: string | null | undefined): PeruvianPaymentIdParts {\n\tif (!value || typeof value !== \"string\") {\n\t\treturn { qr: null, phone: null, cci: null };\n\t}\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\tconst fallback = parsePeruvianFallback(right);\n\t\tconst qr = validatePeruvianQr(left) ? left : null;\n\t\treturn {\n\t\t\tqr,\n\t\t\tphone: fallback.phone ?? (qr ? extractPeruvianPhoneFromQr(qr) : null),\n\t\t\tcci: fallback.cci,\n\t\t};\n\t}\n\tif (validatePeruvianQr(trimmed)) {\n\t\treturn {\n\t\t\tqr: trimmed,\n\t\t\tphone: extractPeruvianPhoneFromQr(trimmed),\n\t\t\tcci: null,\n\t\t};\n\t}\n\tconst fallback = parsePeruvianFallback(trimmed);\n\treturn { qr: null, phone: fallback.phone, cci: fallback.cci };\n}\n\nexport function serializePeruvianPaymentId(\n\tqr: string | null | undefined,\n\tphone: string | null | undefined,\n\tcci: string | null | undefined,\n): string {\n\tconst q = qr?.trim() && validatePeruvianQr(qr.trim()) ? qr.trim() : \"\";\n\tconst p = phone?.trim() && validatePeruvianPhone(phone) ? normalizePeruvianPaymentKey(phone) : \"\";\n\tconst c = cci?.trim() && validatePeruvianCci(cci) ? normalizePeruvianPaymentKey(cci) : \"\";\n\tconst fallback = p && c ? `${p}|${c}` : p || c;\n\tif (q && fallback) return `${q}${PACKED_PAYMENT_ID_SEP}${fallback}`;\n\treturn q || fallback;\n}\n\nfunction isValidPeruvianTypedRest(value: string): boolean {\n\tconst nonempty = value\n\t\t.split(\"|\")\n\t\t.map((token) => token.trim())\n\t\t.filter((token) => token.length > 0);\n\tif (nonempty.length === 0) return false;\n\treturn nonempty.every((token) => validatePeruvianPhone(token) || validatePeruvianCci(token));\n}\n\n/** QR payload and/or CCI and/or Yape/Plin phone. A packed rest must be valid typed fields. */\nexport function validatePeruvianPaymentId(value: string): boolean {\n\tif (!value || typeof value !== \"string\") return false;\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\tconst qrOk = validatePeruvianQr(left);\n\t\tif (right.trim()) return qrOk && isValidPeruvianTypedRest(right);\n\t\treturn qrOk;\n\t}\n\tif (validatePeruvianQr(trimmed)) return true;\n\treturn isValidPeruvianTypedRest(trimmed);\n}\n\n/** Payment ID field configuration for PEN (Peru). At least one of phone / CCI. */\nexport const PEN_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PERU_PHONE_LABEL\",\n\t\tplaceholder: PEN_PHONE_PLACEHOLDER,\n\t\tdisplayLabel: \"Yape / Plin phone\",\n\t\tvalidate: validatePeruvianPhone,\n\t\tvalidationErrorMessage: PEN_PHONE_VALIDATION_ERROR,\n\t\toptional: true,\n\t},\n\t{\n\t\tkey: \"cci\",\n\t\tlabel: \"PERU_CCI_LABEL\",\n\t\tplaceholder: PEN_CCI_PLACEHOLDER,\n\t\tdisplayLabel: \"CCI\",\n\t\tvalidate: validatePeruvianCci,\n\t\tvalidationErrorMessage: PEN_CCI_VALIDATION_ERROR,\n\t\toptional: true,\n\t},\n];\n\n/** Country option for Peru (PEN). */\nexport const PEN_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Peru\",\n\tcurrency: CURRENCY.PEN,\n\tsymbolNative: \"S/\",\n\tlocale: \"es-PE\",\n\tpaymentMethod: \"YAPE_PLIN_CCI\",\n\tpaymentAddressName: \"YAPE_PLIN_CCI_DETAILS\",\n\ttimezone: \"America/Lima\",\n\ttimezone_name: \"PET\",\n\tflag: \"🇵🇪\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f5-1f1ea.png\",\n\tphoneCode: \"+51\",\n\ttelegramSupportChannel: \"https://t.me/p2pmeperu\",\n\ttwitterUsername: \"p2pmePeru\",\n\tsmsCountryCodes: [\"PE\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tuploadPaymentQR: true,\n\tvalidateQr: validatePeruvianQr,\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isPeruvianPayQr(candidate) ? candidate : null;\n\t},\n\thydrateFieldsFromQr: (qr) => {\n\t\tconst phone = extractPeruvianPhoneFromQr(qr);\n\t\treturn phone ? { phone } : {};\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport { isPhilippinePayQr, payQrCandidate } from \"../qr-validator\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\n\nexport const PHP_PLACEHOLDER_PHONE = \"9171234567\";\nexport const PHP_PLACEHOLDER_BANK = \"GCash\";\n\nexport const PHP_VALIDATION_ERROR_PHONE =\n\t\"Please enter a valid Philippine mobile number (e.g., 9171234567)\";\nexport const PHP_BANK_NAME_VALIDATION_ERROR = \"Please enter the receiving bank or e-wallet\";\n\n/**\n * Validates a Philippine mobile number for InstaPay (GCash / Maya).\n * Mobile numbers are 10 digits starting with 9, and are commonly written\n * locally as `09XXXXXXXXX` or internationally as `+63 9XXXXXXXXX`.\n */\nexport function validatePhilippinePhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\tif (/[a-zA-Z]/.test(phoneNumber)) return false;\n\n\tconst cleaned = phoneNumber.trim().replace(/\\D/g, \"\");\n\n\tif (/^9\\d{9}$/.test(cleaned)) return true;\n\tif (/^09\\d{9}$/.test(cleaned)) return true;\n\tif (/^639\\d{9}$/.test(cleaned)) return true;\n\n\treturn false;\n}\n\n/**\n * Payment ID field configuration for PHP (Philippines, InstaPay).\n *\n * InstaPay is an interbank rail, so the receiving institution has to be named\n * alongside the number — it may be an e-wallet (GCash, Maya), a digital bank\n * (GoTyme) or a local bank. For e-wallets the account number is the mobile\n * number the wallet is registered to.\n */\nexport const PHP_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: PHP_PLACEHOLDER_PHONE,\n\t\tdisplayLabel: \"Phone Number\",\n\t\tvalidate: validatePhilippinePhoneNumber,\n\t\tvalidationErrorMessage: PHP_VALIDATION_ERROR_PHONE,\n\t},\n\t{\n\t\tkey: \"bank-name\",\n\t\tlabel: \"BANK_NAME\",\n\t\tplaceholder: PHP_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Bank Name\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: PHP_BANK_NAME_VALIDATION_ERROR,\n\t},\n];\n\n/**\n * Country option for the Philippines (PHP).\n *\n * Payment channel is InstaPay — merchants are free to receive through GCash or\n * Maya, both of which settle over InstaPay. Scan & Pay supports the QR Ph\n * standard only (wallet-proprietary GCash-only / Maya-only QRs are ignored),\n * mirroring how QRIS is handled for Indonesia, so PAY stays enabled.\n */\nexport const PHP_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Philippines\",\n\tcurrency: CURRENCY.PHP,\n\tsymbolNative: \"₱\",\n\tlocale: \"en-PH\",\n\tpaymentMethod: \"INSTAPAY\",\n\tpaymentAddressName: \"PHONE_NUMBER\",\n\ttimezone: \"Asia/Manila\",\n\ttimezone_name: \"PST\",\n\tflag: \"🇵🇭\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1f5-1f1ed.png\",\n\tphoneCode: \"+63\",\n\ttelegramSupportChannel: \"https://t.me/p2pdotmeph\",\n\ttwitterUsername: \"p2pdotmeph\",\n\tsmsCountryCodes: [\"PH\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isPhilippinePayQr(candidate) ? candidate : null;\n\t},\n};\n","import { CURRENCY } from \"../currency\";\nimport type { CountryOption, PaymentIdFieldConfig } from \"../types\";\nimport { EUR_PLACEHOLDER, EUR_VALIDATION_ERROR, validateRevolutId } from \"./eur\";\n\nexport const USD_PLACEHOLDER = EUR_PLACEHOLDER;\nexport const USD_VALIDATION_ERROR = EUR_VALIDATION_ERROR;\n\n/** Payment ID field configuration for USD (Revolut USD). */\nexport const USD_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"revolut\",\n\t\tlabel: \"REVOLUT_ID\",\n\t\tplaceholder: USD_PLACEHOLDER,\n\t\tdisplayLabel: \"Revolut ID\",\n\t\tvalidate: validateRevolutId,\n\t\tvalidationErrorMessage: USD_VALIDATION_ERROR,\n\t},\n];\n\n/** Country option for Revolut USD. */\nexport const USD_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Revolut USD\",\n\tcurrency: CURRENCY.USD,\n\tsymbolNative: \"$\",\n\tlocale: \"en-US\",\n\tpaymentMethod: \"REVOLUT\",\n\tpaymentAddressName: \"REVOLUT_ID\",\n\ttimezone: \"America/New_York\",\n\ttimezone_name: \"EST\",\n\tflag: \"\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f310.png\",\n\tphoneCode: \"+1\",\n\ttelegramSupportChannel: \"https://t.me/P2Pdotme\",\n\ttwitterUsername: \"P2Pdotme\",\n\tsmsCountryCodes: [\"US\"],\n\tprecision: 2,\n\tisAlpha: true,\n\tdisabled: true,\n\tdisabledPaymentTypes: [\"PAY\"],\n};\n","import { CURRENCY } from \"../currency\";\nimport { isVenezuelanPayQr, payQrCandidate, validateVenezuelanQr } from \"../qr-validator\";\nimport { type CountryOption, PACKED_PAYMENT_ID_SEP, type PaymentIdFieldConfig } from \"../types\";\n\nexport { validateVenezuelanQr };\n\nexport const VEN_PLACEHOLDER = \"04121234567\";\nexport const VEN_PLACEHOLDER_RIF = \"V12345678\";\nexport const VEN_PLACEHOLDER_BANK = \"Banesco\";\n\nexport const VEN_VALIDATION_ERROR = \"Please enter a valid phone number (e.g., 04121234567)\";\nexport const VEN_VALIDATION_ERROR_RIF =\n\t\"Please enter a valid Cédula/RIF (e.g., V12345678, P12345678)\";\nexport const VEN_VALIDATION_ERROR_BANK = \"Please enter a bank name\";\n\n/** Allowed Venezuelan document prefixes for Pago Móvil (SENIAT / banking). */\nexport const VEN_RIF_PREFIXES = [\"V\", \"E\", \"J\", \"G\", \"R\", \"P\"] as const;\n\n/**\n * Validates Venezuelan phone number for Pago Movil.\n * Format: 04XX-XXXXXXX (11 digits starting with 04).\n */\nexport function validateVenezuelanPhoneNumber(phoneNumber: string): boolean {\n\tif (!phoneNumber || phoneNumber.trim().length === 0) return false;\n\n\tconst cleaned = phoneNumber.trim().replace(/\\D/g, \"\");\n\n\tif (/^04\\d{9}$/.test(cleaned)) return true;\n\tif (/^4\\d{9}$/.test(cleaned)) return true;\n\n\treturn false;\n}\n\n/**\n * Validates Venezuelan Cédula/RIF for Pago Móvil.\n * Format: prefix (V|E|J|G|R|P) followed by digits.\n * Includes natural persons (V), foreigners (E), companies (J), government (G),\n * residual (R), and passport-linked accounts (P).\n */\nexport function validateVenezuelanRif(rif: string): boolean {\n\tif (!rif || rif.trim().length === 0) return false;\n\tconst trimmed = rif.trim().toUpperCase();\n\treturn /^[VEJGRP]\\d+$/.test(trimmed);\n}\n\nfunction isValidVenezuelanCompound(value: string): boolean {\n\tconst parts = value.split(\"|\");\n\tif (parts.length !== 3) return false;\n\treturn (\n\t\tvalidateVenezuelanPhoneNumber(parts[0]) &&\n\t\tvalidateVenezuelanRif(parts[1]) &&\n\t\tparts[2].trim().length > 0\n\t);\n}\n\nexport type VenezuelanPaymentIdParts = {\n\tqr: string | null;\n\tcompound: string | null;\n};\n\n/**\n * Splits a stored VEN payment ID into its QR payload and/or typed fallback.\n * Formats: S7B blob, `phone|rif|bank`, or `blob||phone|rif|bank`.\n */\nexport function parseVenezuelanPaymentId(\n\tvalue: string | null | undefined,\n): VenezuelanPaymentIdParts {\n\tif (!value || typeof value !== \"string\") return { qr: null, compound: null };\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\treturn {\n\t\t\tqr: validateVenezuelanQr(left) ? left : null,\n\t\t\tcompound: isValidVenezuelanCompound(right) ? right : null,\n\t\t};\n\t}\n\tif (validateVenezuelanQr(trimmed)) return { qr: trimmed, compound: null };\n\tif (isValidVenezuelanCompound(trimmed)) return { qr: null, compound: trimmed };\n\treturn { qr: null, compound: null };\n}\n\nexport function serializeVenezuelanPaymentId(\n\tqr: string | null | undefined,\n\tcompound: string | null | undefined,\n): string {\n\tconst q = qr?.trim() && validateVenezuelanQr(qr.trim()) ? qr.trim() : \"\";\n\tconst c = compound?.trim() || \"\";\n\tif (q && c) return `${q}${PACKED_PAYMENT_ID_SEP}${c}`;\n\treturn q || c;\n}\n\n/**\n * Accepts a Suiche 7B QR payload, the typed `phone|rif|bank` fallback,\n * or both packed as `qr||phone|rif|bank`. A non-empty packed rest must be a\n * valid compound — QR does not mask an incomplete trio.\n */\nexport function validateVenezuelanPaymentId(value: string): boolean {\n\tif (!value || typeof value !== \"string\") return false;\n\tconst trimmed = value.trim();\n\tconst sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);\n\tif (sep >= 0) {\n\t\tconst left = trimmed.slice(0, sep);\n\t\tconst right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);\n\t\tconst qrOk = validateVenezuelanQr(left);\n\t\tif (right.trim()) return qrOk && isValidVenezuelanCompound(right);\n\t\treturn qrOk;\n\t}\n\tconst { qr, compound } = parseVenezuelanPaymentId(trimmed);\n\treturn qr !== null || compound !== null;\n}\n\n/** Payment ID field configuration for VEN (Venezuela, Pago Móvil). */\nexport const VEN_PAYMENT_FIELDS: PaymentIdFieldConfig[] = [\n\t{\n\t\tkey: \"phone\",\n\t\tlabel: \"PHONE_NUMBER\",\n\t\tplaceholder: VEN_PLACEHOLDER,\n\t\tdisplayLabel: \"Phone\",\n\t\tvalidate: validateVenezuelanPhoneNumber,\n\t\tvalidationErrorMessage: VEN_VALIDATION_ERROR,\n\t},\n\t{\n\t\tkey: \"rif\",\n\t\tlabel: \"RIF_LABEL\",\n\t\tplaceholder: VEN_PLACEHOLDER_RIF,\n\t\tdisplayLabel: \"Cédula/RIF\",\n\t\tvalidate: validateVenezuelanRif,\n\t\tvalidationErrorMessage: VEN_VALIDATION_ERROR_RIF,\n\t},\n\t{\n\t\tkey: \"bank\",\n\t\tlabel: \"BANK_LABEL\",\n\t\tplaceholder: VEN_PLACEHOLDER_BANK,\n\t\tdisplayLabel: \"Banco\",\n\t\tvalidate: (v: string) => v.trim().length > 0,\n\t\tvalidationErrorMessage: VEN_VALIDATION_ERROR_BANK,\n\t},\n];\n\n/** Country option for Venezuela (VEN). */\nexport const VEN_COUNTRY_OPTION: CountryOption = {\n\tcountry: \"Venezuela\",\n\tcurrency: CURRENCY.VEN,\n\tinternationalFormat: \"VES\",\n\tsymbolNative: \"Bs\",\n\tlocale: \"es-VE\",\n\tpaymentMethod: \"PAGO_MOVIL\",\n\tpaymentAddressName: \"PAGO_MOVIL_DETAILS\",\n\ttimezone: \"America/Caracas\",\n\ttimezone_name: \"VET\",\n\tflag: \"🇻🇪\",\n\tflagUrl: \"https://cdn.jsdelivr.net/gh/twitter/twemoji@14.0.2/assets/72x72/1f1fb-1f1ea.png\",\n\tphoneCode: \"+58\",\n\ttelegramSupportChannel: \"https://t.me/p2pmevenezuela\",\n\ttwitterUsername: \"p2pmevenezuela\",\n\tsmsCountryCodes: [\"VE\"],\n\tprecision: 2,\n\tisAlpha: false,\n\tdisabled: false,\n\tdisabledPaymentTypes: [],\n\tuploadPaymentQR: true,\n\tvalidateQr: validateVenezuelanQr,\n\tgetPayQrPayload: (paymentId) => {\n\t\tconst candidate = payQrCandidate(paymentId);\n\t\treturn isVenezuelanPayQr(candidate) ? candidate : null;\n\t},\n};\n","import {\n\tARS_COUNTRY_OPTION,\n\tBOB_COUNTRY_OPTION,\n\tBRL_COUNTRY_OPTION,\n\tCOP_COUNTRY_OPTION,\n\tCUP_COUNTRY_OPTION,\n\tECU_COUNTRY_OPTION,\n\tEUR_COUNTRY_OPTION,\n\tIDR_COUNTRY_OPTION,\n\tINR_COUNTRY_OPTION,\n\tMEX_COUNTRY_OPTION,\n\tNGN_COUNTRY_OPTION,\n\tPEN_COUNTRY_OPTION,\n\tPHP_COUNTRY_OPTION,\n\tUSD_COUNTRY_OPTION,\n\tVEN_COUNTRY_OPTION,\n} from \"./currencies\";\nimport type { CurrencyCode } from \"./currency\";\nimport { PAYMENT_ID_FIELDS } from \"./payment-fields\";\nimport type { CountryOption } from \"./types\";\n\n/** All supported countries with their currency metadata, payment methods, and display config. */\nexport const COUNTRY_OPTIONS: readonly CountryOption[] = [\n\tINR_COUNTRY_OPTION,\n\tIDR_COUNTRY_OPTION,\n\tBRL_COUNTRY_OPTION,\n\tARS_COUNTRY_OPTION,\n\tMEX_COUNTRY_OPTION,\n\tVEN_COUNTRY_OPTION,\n\tBOB_COUNTRY_OPTION,\n\tNGN_COUNTRY_OPTION,\n\tCOP_COUNTRY_OPTION,\n\tCUP_COUNTRY_OPTION,\n\tECU_COUNTRY_OPTION,\n\tPEN_COUNTRY_OPTION,\n\tPHP_COUNTRY_OPTION,\n\tEUR_COUNTRY_OPTION,\n\tUSD_COUNTRY_OPTION,\n];\n\nexport function getCountryOption(\n\tcurrency: CurrencyCode | null | undefined,\n): CountryOption | undefined {\n\tif (!currency) return undefined;\n\treturn COUNTRY_OPTIONS.find((c) => c.currency === currency);\n}\n\n/**\n * Whether the merchant/seller provides payment details by uploading a QR image.\n * Distinct from PAY (`disabledPaymentTypes`), where the buyer scans a QR.\n */\nexport function uploadsPaymentQR(currency: CurrencyCode | null | undefined): boolean {\n\treturn getCountryOption(currency)?.uploadPaymentQR === true;\n}\n\n/**\n * Whether a stored payment ID may include a QR payload (`qr||fields` or\n * standalone QR). Derived from `validateQr` — no extra flag.\n */\nexport function usesPackedPaymentId(currency: CurrencyCode | null | undefined): boolean {\n\treturn getCountryOption(currency)?.validateQr != null;\n}\n\n/**\n * Apps mount the catalog form (PackedPaymentInput): QR upload and typed\n * fields may both show — they are not exclusive.\n */\nexport function usesCatalogPaymentForm(currency: CurrencyCode | null | undefined): boolean {\n\tif (!currency) return false;\n\treturn uploadsPaymentQR(currency) || (PAYMENT_ID_FIELDS[currency]?.length ?? 0) > 1;\n}\n\n/**\n * i18n key for a warning shown before the payer sends fiat, or `null` when the\n * currency has none. Apps translate with `t(key)` — do not branch on currency.\n */\nexport function getTransferWarning(currency: CurrencyCode | null | undefined): string | null {\n\treturn getCountryOption(currency)?.transferWarning ?? null;\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { COUNTRY_OPTIONS } from \"../country\";\nimport type { CurrencyCode } from \"../types\";\nimport { QRParserError, type QRParserErrorCode } from \"./errors\";\n\n/** Currencies that support QR parsing — derived from countries where PAY is not disabled. */\nexport const SUPPORTED_QR_CURRENCIES: readonly CurrencyCode[] = COUNTRY_OPTIONS.filter(\n\t(c) => !c.disabledPaymentTypes.includes(\"PAY\"),\n).map((c) => c.currency);\n\nexport type SupportedCurrency = (typeof SUPPORTED_QR_CURRENCIES)[number];\n\n/** Internal — options forwarded to per-currency parsers (today, only PIX uses them). */\nexport interface ParseQRConfig {\n\tproxyUrl?: string;\n\torderId?: string;\n}\n\n/** Single-object params for `parseQR`. */\nexport interface ParseQRParams {\n\treadonly qrData: string;\n\treadonly currency: SupportedCurrency;\n\t/** Exchange rate: 1 USDC = `sellPrice` fiat. */\n\treadonly sellPrice: number;\n\t/** Required for dynamic PIX (BRL) QRs to bypass CORS on the bank endpoint. */\n\treadonly proxyUrl?: string;\n\t/** Forwarded to `proxyUrl` for tracing / correlation. */\n\treadonly orderId?: string;\n}\n\nexport interface ParsedQR {\n\tpaymentAddress: string;\n\tamount?: {\n\t\tusdc: number;\n\t\tfiat: number;\n\t};\n}\n\nexport type ParseResult = Result<ParsedQR, QRParserError>;\n\nexport function success(data: ParsedQR): ParseResult {\n\treturn ok(data);\n}\n\nexport function failure(\n\tcode: QRParserErrorCode,\n\tmessage: string,\n\tcontext?: Record<string, unknown>,\n): ParseResult {\n\treturn err(new QRParserError(message, { code, context }));\n}\n","/**\n * Calculate CRC-16-CCITT-FALSE checksum.\n * Polynomial 0x1021, initial value 0xFFFF.\n * Used by EMVCo-compliant QR codes (PIX, MercadoPago).\n */\nexport function calculateCRC16(data: string): string {\n\tconst payload = `${data}6304`;\n\tconst polynomial = 0x1021;\n\tlet result = 0xffff;\n\n\tconst encoder = new TextEncoder();\n\tconst bytes = encoder.encode(payload);\n\n\tfor (let offset = 0; offset < bytes.length; offset++) {\n\t\tresult ^= bytes[offset] << 8;\n\t\tfor (let bitwise = 0; bitwise < 8; bitwise++) {\n\t\t\tresult <<= 1;\n\t\t\tif (result & 0x10000) {\n\t\t\t\tresult ^= polynomial;\n\t\t\t}\n\t\t\tresult &= 0xffff;\n\t\t}\n\t}\n\n\treturn result.toString(16).toUpperCase().padStart(4, \"0\");\n}\n\n/**\n * Verify CRC-16 checksum of an EMVCo QR string.\n * Expects the string to end with \"6304\" + 4 hex CRC digits.\n */\nexport function verifyCRC16(qrData: string): { valid: boolean; error?: string } {\n\tif (!qrData || qrData.length < 8) {\n\t\treturn { valid: false, error: \"QR data too short for CRC verification\" };\n\t}\n\n\t// The CRC tag is always the final data object: \"63\" (tag) + \"04\" (length) +\n\t// 4 hex CRC digits. It must sit at a fixed position (length - 8). Locating it\n\t// positionally — rather than with lastIndexOf(\"6304\") — avoids a false match\n\t// when the CRC value itself is \"6304\" (payload ends \"...63046304\").\n\tconst crcTagIndex = qrData.length - 8;\n\tif (qrData.substring(crcTagIndex, crcTagIndex + 4) !== \"6304\") {\n\t\treturn { valid: false, error: \"Missing or misplaced CRC tag (6304)\" };\n\t}\n\n\tconst providedCrc = qrData.substring(crcTagIndex + 4, crcTagIndex + 8);\n\tif (!/^[0-9A-Fa-f]{4}$/.test(providedCrc)) {\n\t\treturn { valid: false, error: \"Invalid CRC hex format\" };\n\t}\n\n\tconst dataBeforeCrc = qrData.substring(0, crcTagIndex);\n\tconst calculatedCrc = calculateCRC16(dataBeforeCrc);\n\n\tif (calculatedCrc !== providedCrc.toUpperCase()) {\n\t\treturn {\n\t\t\tvalid: false,\n\t\t\terror: `CRC mismatch: expected ${calculatedCrc}, got ${providedCrc.toUpperCase()}`,\n\t\t};\n\t}\n\n\treturn { valid: true };\n}\n","/**\n * Parse a TLV (Tag-Length-Value) encoded string.\n * Tags are 2 digits, lengths are 2 digits (EMVCo standard).\n * Returns all parsed tag-value pairs. Stops gracefully on truncated data.\n */\nexport function parseTLV(data: string): { tag: string; value: string }[] {\n\tconst entries: { tag: string; value: string }[] = [];\n\tlet pos = 0;\n\n\twhile (pos + 4 <= data.length) {\n\t\tconst tag = data.substring(pos, pos + 2);\n\t\tconst lengthStr = data.substring(pos + 2, pos + 4);\n\n\t\tif (!/^[0-9]{2}$/.test(tag) || !/^[0-9]{2}$/.test(lengthStr)) {\n\t\t\tbreak;\n\t\t}\n\n\t\tconst length = parseInt(lengthStr, 10);\n\t\tif (pos + 4 + length > data.length) {\n\t\t\tbreak;\n\t\t}\n\n\t\tconst value = data.substring(pos + 4, pos + 4 + length);\n\t\tentries.push({ tag, value });\n\t\tpos += 4 + length;\n\t}\n\n\treturn entries;\n}\n\n/**\n * Extract specific tags from a TLV string.\n * Returns a record mapping tag IDs to their values.\n * Only includes tags present in the `tags` array.\n */\nexport function extractTags(data: string, tags: string[]): Record<string, string> {\n\tconst result: Record<string, string> = {};\n\tconst tagSet = new Set(tags);\n\n\tfor (const entry of parseTLV(data)) {\n\t\tif (tagSet.has(entry.tag)) {\n\t\t\tresult[entry.tag] = entry.value;\n\t\t}\n\t}\n\n\treturn result;\n}\n","import type { ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags } from \"../utils/tlv\";\n\nexport function parseMercadoPago(qrData: string, _sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tconst isARS = trimmed.includes(\"5303032\");\n\tconst isAR = trimmed.includes(\"5802AR\");\n\tif (!isARS && !isAR) {\n\t\treturn failure(\"INVALID_QR\", \"Not an ARS/Argentina QR code\");\n\t}\n\n\tconst crc = verifyCRC16(trimmed);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid QR checksum\");\n\t}\n\n\tconst tags = extractTags(trimmed, [\"59\"]);\n\tconst merchantName = tags[\"59\"] || \"Unknown\";\n\n\treturn success({ paymentAddress: merchantName });\n}\n","/** Plain decimal, EMVCo-style: digits with an optional single fractional part. */\nconst DECIMAL_AMOUNT = /^\\d+(\\.\\d+)?$/;\n\n/**\n * Parse a fiat amount string and convert to usdc using sellPrice.\n * Returns null unless the whole string is a finite, positive decimal amount and\n * sellPrice is a finite positive number. Rejects trailing garbage (\"12abc\"),\n * exponent/Infinity/NaN forms that parseFloat would otherwise accept, and a\n * zero/negative sellPrice that would produce an Infinite or negative usdc value.\n */\nexport function parseAmount(\n\tamountStr: string,\n\tsellPrice: number,\n): { usdc: number; fiat: number } | null {\n\tif (!amountStr) return null;\n\tconst trimmed = amountStr.trim();\n\tif (!DECIMAL_AMOUNT.test(trimmed)) return null;\n\n\tconst fiat = Number(trimmed);\n\tif (!Number.isFinite(fiat) || fiat <= 0) return null;\n\tif (!Number.isFinite(sellPrice) || sellPrice <= 0) return null;\n\n\treturn { usdc: fiat / sellPrice, fiat };\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst BOB_TAGS = { AMOUNT: \"54\", CURRENCY: \"53\", COUNTRY: \"58\" } as const;\n\n/**\n * Bolivia QR Simple encrypted envelope: `<base64 ciphertext>|<hex checksum>`\n * (Yape Bs, BancoSol, Banco Fie dynamic QR). The payload is bank-encrypted, so\n * amount/account cannot be read — the buyer re-renders the exact blob for their\n * bank app to scan. The checksum is a hex digest whose length varies by bank\n * (24 hex for Banco Fie, 32 hex for Yape/BancoSol).\n */\nfunction isBolivianEncryptedQr(payload: string): boolean {\n\tif (payload.includes(\"||\")) return false;\n\tconst sep = payload.lastIndexOf(\"|\");\n\tif (sep < 40) return false;\n\tconst blob = payload.slice(0, sep);\n\tconst tag = payload.slice(sep + 1);\n\tif (blob.length % 4 !== 0) return false;\n\tif (!/^[A-Za-z0-9+/]+={0,2}$/.test(blob)) return false;\n\treturn /^(?:[0-9A-Fa-f]{24}|[0-9A-Fa-f]{32})$/.test(tag);\n}\n\n/**\n * Parses a Bolivian QR Simple QR. Accepts either the encrypted envelope\n * (`<base64>|<hex>`) or an EMVCo static QR (country tag 58 `BO`, currency tag\n * 53 `068`). Returns the raw payload verbatim as `paymentAddress` so the buyer app\n * can re-render the exact QR. Static EMVCo QRs carry no amount unless tag 54 is\n * present, which is converted to usdc via `sellPrice`; encrypted QRs carry none.\n */\nexport function parseBolivia(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tif (isBolivianEncryptedQr(trimmed)) {\n\t\treturn success({ paymentAddress: trimmed });\n\t}\n\n\tconst tags = extractTags(trimmed, [BOB_TAGS.AMOUNT, BOB_TAGS.CURRENCY, BOB_TAGS.COUNTRY]);\n\n\tif (tags[BOB_TAGS.COUNTRY] !== \"BO\" || tags[BOB_TAGS.CURRENCY] !== \"068\") {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Bolivia QR Simple QR\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: trimmed };\n\n\tconst amountStr = tags[BOB_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import { err, ok, type Result } from \"neverthrow\";\nimport { QRParserError, type QRParserErrorCode } from \"../errors\";\nimport type { ParsedQR, ParseQRConfig, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { parseTLV } from \"../utils/tlv\";\n\nconst PIX_TAGS = {\n\tPAYLOAD_FORMAT: \"00\",\n\tPIX_KEY_INFO: \"26\",\n\tAMOUNT: \"54\",\n\tMERCHANT_NAME: \"59\",\n\tCRC: \"63\",\n} as const;\n\nfunction parsePIXKeyInfo(data: string): { pixKey: string | null; location: string | null } {\n\tconst result = { pixKey: null as string | null, location: null as string | null };\n\n\tfor (const entry of parseTLV(data)) {\n\t\tif (entry.tag === \"01\") {\n\t\t\tif (entry.value.includes(\"http\") || entry.value.includes(\"://\")) {\n\t\t\t\tresult.location = entry.value.startsWith(\"http\") ? entry.value : `https://${entry.value}`;\n\t\t\t} else {\n\t\t\t\tresult.pixKey = entry.value;\n\t\t\t}\n\t\t} else if (entry.tag === \"25\") {\n\t\t\tresult.location = entry.value.startsWith(\"http\") ? entry.value : `https://${entry.value}`;\n\t\t}\n\t}\n\n\treturn result;\n}\n\nfunction safeBase64Decode(base64String: string): string {\n\tif (typeof globalThis.atob === \"function\") {\n\t\treturn globalThis.atob(base64String);\n\t}\n\tthrow new Error(\"No base64 decoder available\");\n}\n\ninterface PIXJWTPayload {\n\tstatus?: string;\n\ttxid?: string;\n\tvalor?: { original?: string };\n\tcalendario?: { expiracao?: number };\n}\n\nfunction parseJWT(token: string): PIXJWTPayload | null {\n\ttry {\n\t\tconst parts = token.trim().split(\".\");\n\t\tif (parts.length !== 3 || !parts[0] || !parts[1] || !parts[2]) return null;\n\t\tconst json = safeBase64Decode(parts[1]);\n\t\treturn JSON.parse(json) as PIXJWTPayload;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nfunction dynamicErr(\n\tcode: QRParserErrorCode,\n\tmessage: string,\n): Result<{ dynamicAmount?: string }, QRParserError> {\n\treturn err(new QRParserError(message, { code }));\n}\n\nasync function fetchDynamicData(\n\tlocation: string,\n\tproxyUrl: string,\n\torderId?: string,\n): Promise<Result<{ dynamicAmount?: string }, QRParserError>> {\n\ttry {\n\t\tconst url = new URL(`${proxyUrl}/pix`);\n\t\turl.searchParams.set(\"locationUrl\", location);\n\t\tif (orderId) {\n\t\t\turl.searchParams.set(\"orderId\", orderId);\n\t\t}\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod: \"GET\",\n\t\t\theaders: { Accept: \"*/*\" },\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\treturn dynamicErr(\"FETCH_FAILED\", `Proxy error: ${response.status} ${response.statusText}`);\n\t\t}\n\n\t\tconst jwtToken = await response.text();\n\t\tconst payload = parseJWT(jwtToken);\n\n\t\tif (!payload) {\n\t\t\treturn dynamicErr(\"FETCH_FAILED\", \"Failed to parse dynamic PIX response\");\n\t\t}\n\n\t\treturn ok({ dynamicAmount: payload.valor?.original });\n\t} catch (error) {\n\t\tconst msg = error instanceof Error ? error.message : \"Unknown error\";\n\t\treturn dynamicErr(\"FETCH_FAILED\", `Failed to fetch dynamic PIX data: ${msg}`);\n\t}\n}\n\nexport async function parsePIX(\n\tqrData: string,\n\tsellPrice: number,\n\tconfig: ParseQRConfig,\n): Promise<ParseResult> {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tconst crc = verifyCRC16(trimmed);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid QR checksum\");\n\t}\n\n\tconst allTags: Record<string, string> = {};\n\tfor (const entry of parseTLV(trimmed)) {\n\t\tallTags[entry.tag] = entry.value;\n\t}\n\n\tif (!allTags[PIX_TAGS.PAYLOAD_FORMAT]) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid PIX QR format\");\n\t}\n\n\tconst merchantName = allTags[PIX_TAGS.MERCHANT_NAME] || \"MERCHANT_NOT_FOUND\";\n\n\tlet dynamicAmount: string | undefined;\n\tconst pixKeyData = allTags[PIX_TAGS.PIX_KEY_INFO];\n\tif (pixKeyData) {\n\t\tconst { location } = parsePIXKeyInfo(pixKeyData);\n\t\tif (location) {\n\t\t\tif (!config.proxyUrl) {\n\t\t\t\treturn failure(\"FETCH_FAILED\", \"proxyUrl is required for dynamic PIX QR codes\");\n\t\t\t}\n\t\t\tconst dynamicResult = await fetchDynamicData(location, config.proxyUrl, config.orderId);\n\t\t\tif (dynamicResult.isErr()) {\n\t\t\t\treturn err(dynamicResult.error);\n\t\t\t}\n\t\t\tdynamicAmount = dynamicResult.value.dynamicAmount;\n\t\t}\n\t}\n\n\tconst fiatAmountStr = dynamicAmount || allTags[PIX_TAGS.AMOUNT];\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tif (fiatAmountStr) {\n\t\tconst amount = parseAmount(fiatAmountStr, sellPrice);\n\t\tif (amount) {\n\t\t\tresult.amount = amount;\n\t\t}\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst COP_CURRENCY_CODE = \"5303170\";\nconst CO_COUNTRY_CODE = \"5802CO\";\n\nconst DIAN_KEYS = [\"NumFac\", \"CUFE\", \"Cufe\", \"NitFac\", \"ValFac\", \"ValTolFac\"] as const;\n\nfunction parseDianInvoice(qrData: string): ParseResult | null {\n\tconst hasDianMarker = DIAN_KEYS.some(\n\t\t(k) => qrData.includes(`${k}:`) || qrData.includes(`${k} :`),\n\t);\n\tif (!hasDianMarker) return null;\n\n\tconst fields: Record<string, string> = {};\n\tconst segments = qrData.split(/[\\n,;]+/);\n\tfor (const segment of segments) {\n\t\tconst idx = segment.indexOf(\":\");\n\t\tif (idx === -1) continue;\n\t\tconst key = segment.substring(0, idx).trim();\n\t\tconst value = segment.substring(idx + 1).trim();\n\t\tif (key && value && !fields[key]) fields[key] = value;\n\t}\n\n\tconst cufe = fields.CUFE || fields.Cufe || fields.cufe;\n\tconst numFac = fields.NumFac;\n\tconst paymentAddress = cufe || numFac;\n\n\tif (!paymentAddress) {\n\t\treturn failure(\"INVALID_QR\", \"Missing CUFE / NumFac in DIAN invoice QR\");\n\t}\n\n\treturn success({ paymentAddress });\n}\n\nfunction parseColombianEMV(qrData: string, sellPrice: number): ParseResult | null {\n\tconst isCO = qrData.includes(CO_COUNTRY_CODE);\n\tconst isCOP = qrData.includes(COP_CURRENCY_CODE);\n\tif (!isCO || !isCOP) return null;\n\n\tconst tags = extractTags(qrData, [\"54\", \"59\"]);\n\tconst merchantName = tags[\"59\"] || \"MERCHANT_NOT_FOUND\";\n\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tconst amountStr = tags[\"54\"];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (amount) result.amount = amount;\n\t}\n\n\treturn success(result);\n}\n\n/**\n * Parses a Colombian QR. Supports DIAN electronic invoice QRs\n * (key:value text with `CUFE`/`NumFac`) and EMVCo-encoded QRs from\n * Colombian payment networks (Nequi, Bre-B/RBM) identified by country\n * `5802CO` and currency `5303170`.\n */\nexport function parseCOP(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tconst dian = parseDianInvoice(trimmed);\n\tif (dian) return dian;\n\n\tconst emv = parseColombianEMV(trimmed, sellPrice);\n\tif (emv) return emv;\n\n\treturn failure(\"INVALID_QR\", \"Not a recognized Colombian QR code\");\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\n\nconst TRANSFERMOVIL_PREFIX = \"TRANSFERMOVIL_ETECSA\";\n\nconst CARD_INDEX = 2;\nconst PHONE_INDEX = 3;\nconst AMOUNT_INDEX = 4;\n\nfunction normalizePhone(phone: string): string | null {\n\tconst cleaned = phone.replace(/\\D/g, \"\");\n\tif (/^\\d{8}$/.test(cleaned)) return cleaned;\n\tif (/^53\\d{8}$/.test(cleaned)) return cleaned.slice(2);\n\treturn null;\n}\n\nfunction normalizeCard(card: string): string | null {\n\tconst cleaned = card.replace(/[\\s-]/g, \"\");\n\treturn /^\\d{16}$/.test(cleaned) ? cleaned : null;\n}\n\n/**\n * Parses a Cuban Transfermóvil QR code\n * (`TRANSFERMOVIL_ETECSA,<operation>,<card>,<phone>,<amount>`) and returns the\n * payment address as the compound `phone|card` pair used for CUP. The amount\n * field is optional — when present it is converted to usdc via `sellPrice`.\n */\nexport function parseTransfermovil(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tconst parts = trimmed.split(\",\").map((p) => p.trim());\n\n\tif (parts[0]?.toUpperCase() !== TRANSFERMOVIL_PREFIX) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Cuban (Transfermóvil) QR code\");\n\t}\n\n\tif (parts.length <= PHONE_INDEX) {\n\t\treturn failure(\"INVALID_QR\", \"Transfermóvil QR is missing card or phone fields\");\n\t}\n\n\tconst card = normalizeCard(parts[CARD_INDEX] ?? \"\");\n\tif (!card) {\n\t\treturn failure(\"INVALID_QR\", \"Transfermóvil QR has an invalid card number\");\n\t}\n\n\tconst phone = normalizePhone(parts[PHONE_INDEX] ?? \"\");\n\tif (!phone) {\n\t\treturn failure(\"INVALID_QR\", \"Transfermóvil QR has an invalid phone number\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: `${phone}|${card}` };\n\n\tconst amountStr = parts[AMOUNT_INDEX];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\n\nconst DEUNA_HOST = \"pagar.deuna.app\";\n\n/**\n * Parses a DeUna (Banco Pichincha, Ecuador) payment QR. DeUna QRs are plain URLs of\n * the form `https://pagar.deuna.app/<slug>/merchant?id=<hash>`; the `id` query param\n * is the merchant payment address. DeUna QRs carry no amount, so `_sellPrice` is unused.\n */\nexport function parseDeUna(qrData: string, _sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(qrData.trim());\n\t} catch {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid DeUna URL\");\n\t}\n\n\tif (url.hostname !== DEUNA_HOST) {\n\t\treturn failure(\"INVALID_QR\", `Unexpected DeUna host: ${url.hostname}`);\n\t}\n\n\tconst id = url.searchParams.get(\"id\");\n\tif (!id) {\n\t\treturn failure(\"INVALID_QR\", \"Missing DeUna merchant id\");\n\t}\n\n\treturn success({ paymentAddress: id });\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst QRIS_TAGS = { AMOUNT: \"54\", MERCHANT_NAME: \"59\" } as const;\n\nexport function parseQRIS(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tconst tags = extractTags(trimmed, [QRIS_TAGS.AMOUNT, QRIS_TAGS.MERCHANT_NAME]);\n\n\tconst merchantName = tags[QRIS_TAGS.MERCHANT_NAME];\n\tif (!merchantName) {\n\t\treturn failure(\"INVALID_QR\", \"Missing merchant name\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tconst amountStr = tags[QRIS_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst UPI_ID_REGEX = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+$/;\n\nconst FONEPAY_COUNTRY_TAG = \"58\" as const;\n\n/**\n * Fonepay (Nepal NPQR) EMVCo QR. Indian UPI apps can pay these through the\n * NPCI–Fonepay cross-border link, so an INR PAY order accepts them. Detected by\n * the `fonepay.com` reverse-domain marker (merchant account template, tag 26),\n * country tag 58 = `NP`, and a valid EMVCo CRC-16.\n */\nfunction isFonepayQr(payload: string): boolean {\n\tif (!payload.includes(\"fonepay.com\")) return false;\n\tif (!verifyCRC16(payload).valid) return false;\n\treturn extractTags(payload, [FONEPAY_COUNTRY_TAG])[FONEPAY_COUNTRY_TAG] === \"NP\";\n}\n\nexport function parseUPI(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\t// Fonepay (Nepal) EMVCo QR — returned verbatim as `paymentAddress` so the\n\t// payer's UPI app can re-render and scan the exact merchant QR. Any embedded\n\t// amount (tag 54) is denominated in NPR, not INR, so it is not read; the\n\t// payer enters the amount in their app.\n\tif (isFonepayQr(trimmed)) {\n\t\treturn success({ paymentAddress: trimmed });\n\t}\n\n\tlet paramString: string;\n\tif (trimmed.startsWith(\"upi://pay?\")) {\n\t\tparamString = trimmed.substring(10);\n\t} else if (trimmed.includes(\"?\")) {\n\t\tparamString = trimmed.split(\"?\")[1];\n\t} else {\n\t\tparamString = trimmed;\n\t}\n\n\tconst params = new URLSearchParams(paramString);\n\tconst pa = params.get(\"pa\");\n\n\tif (!pa) {\n\t\treturn failure(\"INVALID_QR\", \"Missing UPI payment address\");\n\t}\n\n\tif (!UPI_ID_REGEX.test(pa)) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid UPI ID format\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: pa };\n\n\tconst amountStr = params.get(\"am\");\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags, parseTLV } from \"../utils/tlv\";\n\nconst NGN_TAGS = {\n\tAMOUNT: \"54\",\n\tCURRENCY: \"53\",\n\tCOUNTRY: \"58\",\n\tMERCHANT_NAME: \"59\",\n} as const;\n\nconst NGN_CURRENCY_CODE = \"566\";\nconst NIBSS_AID = \"NG.COM.NIBSSPLC.QR\";\n\nfunction isNQR(qrData: string): boolean {\n\tif (qrData.includes(NIBSS_AID)) return true;\n\tif (qrData.includes(`5303${NGN_CURRENCY_CODE}`)) return true;\n\tif (qrData.includes(\"5802NG\")) return true;\n\treturn false;\n}\n\nfunction parseEMVCoNQR(qrData: string, sellPrice: number): ParseResult {\n\tconst crc = verifyCRC16(qrData);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", \"Invalid QR checksum\");\n\t}\n\n\tif (!isNQR(qrData)) {\n\t\treturn failure(\"INVALID_QR\", \"Not a Nigerian (NQR) QR code\");\n\t}\n\n\tconst tags = extractTags(qrData, [NGN_TAGS.AMOUNT, NGN_TAGS.MERCHANT_NAME]);\n\n\tconst merchantName = tags[NGN_TAGS.MERCHANT_NAME];\n\tif (!merchantName) {\n\t\treturn failure(\"INVALID_QR\", \"Missing merchant name\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: merchantName };\n\n\tconst amountStr = tags[NGN_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n\nfunction parseSPD(qrData: string, sellPrice: number): ParseResult {\n\t// Short Payment Descriptor (Czech spec). Seen in the wild for some\n\t// Nigerian account-based QRs: `SPD*1.0*ACC:<nuban>*AM:<amount>*MSG:<text>*`.\n\tconst parts = qrData.split(\"*\").filter((p) => p.length > 0);\n\tif (parts.length < 2 || parts[0] !== \"SPD\") {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid SPD QR code\");\n\t}\n\n\tconst fields: Record<string, string> = {};\n\tfor (const part of parts.slice(2)) {\n\t\tconst colon = part.indexOf(\":\");\n\t\tif (colon === -1) continue;\n\t\tfields[part.substring(0, colon).toUpperCase()] = part.substring(colon + 1);\n\t}\n\n\tconst account = fields.ACC;\n\tif (!account) {\n\t\treturn failure(\"INVALID_QR\", \"Missing ACC field in SPD QR\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: account };\n\n\tconst amountStr = fields.AM;\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr.replace(/,/g, \"\"), sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n\n/**\n * Parses a Nigerian QR code. Supports NIBSS NQR (EMVCo MPM with AID\n * `NG.COM.NIBSSPLC.QR`) and the SPD `SPD*1.0*ACC:...*AM:...*` format.\n */\nexport function parseNGN(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\n\tif (trimmed.startsWith(\"SPD*\")) {\n\t\treturn parseSPD(trimmed, sellPrice);\n\t}\n\n\t// Sanity-check that this looks like EMVCo TLV before CRC-ing.\n\tif (parseTLV(trimmed).length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Nigerian QR code\");\n\t}\n\n\treturn parseEMVCoNQR(trimmed, sellPrice);\n}\n","import { validatePeruvianQr } from \"../../country/qr-validator\";\nimport type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst PEN_TAGS = { AMOUNT: \"54\" } as const;\n\n/**\n * Parses a Peruvian Yape/Plin EMVCo QR. Uses the same structural + CRC check\n * as SELL upload (`validatePeruvianQr`: country PE, currency 604, CRC-16).\n * Returns the raw payload verbatim as `paymentAddress` so the merchant can\n * re-render the exact QR. Static QRs carry no amount; tag 54 converts via\n * `sellPrice`.\n */\nexport function parsePeru(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tif (!validatePeruvianQr(trimmed)) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Peru Yape/Plin QR\");\n\t}\n\n\tconst tags = extractTags(trimmed, [PEN_TAGS.AMOUNT]);\n\tconst result: ParsedQR = { paymentAddress: trimmed };\n\n\tconst amountStr = tags[PEN_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import type { ParsedQR, ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\nimport { parseAmount } from \"../utils/amount\";\nimport { verifyCRC16 } from \"../utils/crc16\";\nimport { extractTags } from \"../utils/tlv\";\n\nconst PHP_TAGS = { AMOUNT: \"54\", CURRENCY: \"53\", COUNTRY: \"58\" } as const;\n\n/**\n * Parses a Philippine QR Ph EMVCo QR. Validates it is a genuine QR Ph\n * (country tag 58 `PH`, currency tag 53 `608`, valid CRC) and returns the raw\n * payload verbatim as `paymentAddress` so the buyer app can re-render the exact\n * QR. Wallet-proprietary GCash-only / Maya-only QRs are not QR Ph and are\n * rejected by the country/currency check. Static QRs carry no amount; when tag\n * 54 is present it is converted to usdc via `sellPrice`.\n */\nexport function parseQRPh(qrData: string, sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tconst tags = extractTags(trimmed, [PHP_TAGS.AMOUNT, PHP_TAGS.CURRENCY, PHP_TAGS.COUNTRY]);\n\n\tif (tags[PHP_TAGS.COUNTRY] !== \"PH\" || tags[PHP_TAGS.CURRENCY] !== \"608\") {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid QR Ph code\");\n\t}\n\n\tconst crc = verifyCRC16(trimmed);\n\tif (!crc.valid) {\n\t\treturn failure(\"INVALID_QR\", crc.error ?? \"Invalid QR Ph checksum\");\n\t}\n\n\tconst result: ParsedQR = { paymentAddress: trimmed };\n\n\tconst amountStr = tags[PHP_TAGS.AMOUNT];\n\tif (amountStr) {\n\t\tconst amount = parseAmount(amountStr, sellPrice);\n\t\tif (!amount) {\n\t\t\treturn failure(\"INVALID_AMOUNT\", \"Invalid amount in QR\");\n\t\t}\n\t\tresult.amount = amount;\n\t}\n\n\treturn success(result);\n}\n","import { validateVenezuelanQr } from \"../../country/qr-validator\";\nimport type { ParseResult } from \"../types\";\nimport { failure, success } from \"../types\";\n\n/**\n * Same envelope as SELL upload: opaque `base64?merchantId=NNNN` (blob ≥ 40).\n * Prefer `validateVenezuelanQr` from `@p2pdotme/sdk/country`; this alias stays\n * for `@p2pdotme/sdk/qr-parsers` callers.\n */\nexport function isPagoMovilQr(qrData: string): boolean {\n\treturn validateVenezuelanQr(qrData);\n}\n\n/**\n * Parses a Venezuelan Pago Móvil / Suiche 7B QR. Uses the same envelope as\n * SELL upload (`validateVenezuelanQr`). Returns the raw payload verbatim as\n * `paymentAddress` so the merchant can re-render the exact QR.\n */\nexport function parsePagoMovil(qrData: string, _sellPrice: number): ParseResult {\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tconst trimmed = qrData.trim();\n\tif (!validateVenezuelanQr(trimmed)) {\n\t\treturn failure(\"INVALID_QR\", \"Not a valid Venezuelan QR code\");\n\t}\n\n\treturn success({ paymentAddress: trimmed });\n}\n","import { CURRENCY, getCountryOption } from \"../country\";\nimport { parseMercadoPago } from \"./parsers/ars\";\nimport { parseBolivia } from \"./parsers/bob\";\nimport { parsePIX } from \"./parsers/brl\";\nimport { parseCOP } from \"./parsers/cop\";\nimport { parseTransfermovil } from \"./parsers/cup\";\nimport { parseDeUna } from \"./parsers/ecu\";\nimport { parseQRIS } from \"./parsers/idr\";\nimport { parseUPI } from \"./parsers/inr\";\nimport { parseNGN } from \"./parsers/ngn\";\nimport { parsePeru } from \"./parsers/pen\";\nimport { parseQRPh } from \"./parsers/php\";\nimport { parsePagoMovil } from \"./parsers/ven\";\nimport type { ParseQRParams, ParseResult } from \"./types\";\nimport { failure } from \"./types\";\n\n/**\n * Parses a QR string for the given currency and returns the extracted payment data.\n *\n * This dispatcher is `async` because dynamic PIX (BRL) QRs store the amount\n * behind a location URL pointing at the issuing bank's PIX endpoint. The SDK\n * resolves that URL via a CORS-bypassing proxy (see `proxyUrl`), which returns\n * a signed JWT whose `valor.original` field holds the amount. All other parsers\n * (UPI, QRIS, QR Ph, MercadoPago, PagoMovil, Transfermóvil) and static PIX are synchronous and\n * resolve immediately.\n *\n * When the country has `validateQr` (PEN, VEN, BOB), Scan & Pay must pass the\n * same check as SELL upload — not a looser envelope.\n */\nexport async function parseQR(params: ParseQRParams): Promise<ParseResult> {\n\tconst { qrData, currency, sellPrice, proxyUrl, orderId } = params;\n\n\tif (!qrData || typeof qrData !== \"string\" || qrData.trim().length === 0) {\n\t\treturn failure(\"INVALID_QR\", \"QR data is empty or invalid\");\n\t}\n\n\tlet result: ParseResult;\n\tswitch (currency) {\n\t\tcase CURRENCY.INR:\n\t\t\tresult = parseUPI(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.IDR:\n\t\t\tresult = parseQRIS(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.BRL:\n\t\t\tresult = await parsePIX(qrData, sellPrice, { proxyUrl, orderId });\n\t\t\tbreak;\n\t\tcase CURRENCY.ARS:\n\t\t\tresult = parseMercadoPago(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.VEN:\n\t\t\tresult = parsePagoMovil(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.BOB:\n\t\t\tresult = parseBolivia(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.NGN:\n\t\t\tresult = parseNGN(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.COP:\n\t\t\tresult = parseCOP(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.CUP:\n\t\t\tresult = parseTransfermovil(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.ECU:\n\t\t\tresult = parseDeUna(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.PEN:\n\t\t\tresult = parsePeru(qrData, sellPrice);\n\t\t\tbreak;\n\t\tcase CURRENCY.PHP:\n\t\t\tresult = parseQRPh(qrData, sellPrice);\n\t\t\tbreak;\n\t\tdefault:\n\t\t\treturn failure(\"INVALID_CURRENCY\", `Currency \"${currency}\" is not supported`);\n\t}\n\n\tconst validateQr = getCountryOption(currency)?.validateQr;\n\tif (result.isOk() && validateQr && !validateQr(qrData.trim())) {\n\t\treturn failure(\"INVALID_QR\", \"QR does not match catalog validateQr\");\n\t}\n\n\treturn result;\n}\n"],"mappings":"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","names":["isBolivianEncryptedQr","import_neverthrow"]}