@p2pdotme/sdk 1.2.16 → 1.2.17

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.
package/dist/country.d.ts CHANGED
@@ -29,6 +29,8 @@ type CurrencyCode = (typeof CURRENCY)[keyof typeof CURRENCY];
29
29
  */
30
30
  declare const CURRENCY_CODES: [CurrencyCode, ...CurrencyCode[]];
31
31
 
32
+ /** Joins an optional QR payload with typed fields (`qr||phone|cci`). */
33
+ declare const PACKED_PAYMENT_ID_SEP = "||";
32
34
  interface PaymentIdFieldConfig {
33
35
  readonly key: string;
34
36
  readonly label: string;
@@ -36,6 +38,11 @@ interface PaymentIdFieldConfig {
36
38
  readonly displayLabel: string | null;
37
39
  readonly validate: (value: string) => boolean;
38
40
  readonly validationErrorMessage: string;
41
+ /**
42
+ * When true, an empty value is allowed. If every field is optional, at least
43
+ * one field must still be filled (see `validatePaymentIdFields`).
44
+ */
45
+ readonly optional?: boolean;
39
46
  }
40
47
  interface CountryOption {
41
48
  readonly country: string;
@@ -57,14 +64,97 @@ interface CountryOption {
57
64
  readonly isAlpha: boolean;
58
65
  readonly disabled: boolean;
59
66
  readonly disabledPaymentTypes: readonly string[];
67
+ /**
68
+ * Merchant/seller provides payment details by uploading a QR image.
69
+ * Distinct from PAY, where the buyer scans a QR. Default: false.
70
+ */
71
+ readonly uploadPaymentQR?: boolean;
72
+ /** Structural check for a standalone QR payload (no `||` pack). */
73
+ readonly validateQr?: (payload: string) => boolean;
74
+ /**
75
+ * Fill catalog fields from a validated QR (e.g. Yape/Plin phone in EMVCo).
76
+ * Only used when the typed fallback did not already set the key.
77
+ */
78
+ readonly hydrateFieldsFromQr?: (qr: string) => Partial<Record<string, string>>;
60
79
  }
61
80
 
62
81
  /** All supported countries with their currency metadata, payment methods, and display config. */
63
82
  declare const COUNTRY_OPTIONS: readonly CountryOption[];
83
+ declare function getCountryOption(currency: CurrencyCode | null | undefined): CountryOption | undefined;
84
+ /**
85
+ * Whether the merchant/seller provides payment details by uploading a QR image.
86
+ * Distinct from PAY (`disabledPaymentTypes`), where the buyer scans a QR.
87
+ */
88
+ declare function uploadsPaymentQR(currency: CurrencyCode | null | undefined): boolean;
89
+ /**
90
+ * Whether a stored payment ID may include a QR payload (`qr||fields` or
91
+ * standalone QR). Derived from `validateQr` — no extra flag.
92
+ */
93
+ declare function usesPackedPaymentId(currency: CurrencyCode | null | undefined): boolean;
94
+ /**
95
+ * Apps mount the catalog form (PackedPaymentInput): QR upload and/or
96
+ * more than one typed field.
97
+ */
98
+ declare function usesCatalogPaymentForm(currency: CurrencyCode | null | undefined): boolean;
64
99
 
65
100
  /** Payment ID field configuration for each supported currency. */
66
101
  declare const PAYMENT_ID_FIELDS: Record<CurrencyCode, PaymentIdFieldConfig[]>;
67
102
 
103
+ /**
104
+ * Yape/Plin EMVCo: country PE, currency 604, CRC-16/CCITT-FALSE.
105
+ * CRC is required on upload so we never persist a truncated screenshot.
106
+ * (PAY render in the merchant is looser; that path does not use this.)
107
+ */
108
+ declare function validatePeruvianQr(payload: string): boolean;
109
+ /**
110
+ * Suiche 7B / Pago Móvil collection QR: `base64?merchantId=NNNN&…`.
111
+ * The base64 is bank AES — we cannot decode phone/RIF from it, only the
112
+ * envelope. Reject packed `||` IDs so a stored compound string is never
113
+ * treated as a QR. Scan & Pay uses the looser `isPagoMovilQr` envelope.
114
+ */
115
+ declare function validateVenezuelanQr(payload: string): boolean;
116
+
117
+ /** Validates a 20-digit Peruvian CCI (spaces ignored). */
118
+ declare function validatePeruvianCci(value: string): boolean;
119
+ /**
120
+ * Validates a Yape/Plin phone: `9` + 8 digits, optional `+51` / `51` prefix.
121
+ */
122
+ declare function validatePeruvianPhone(value: string): boolean;
123
+ /**
124
+ * Validates a Peruvian payment key for the legacy single-field path.
125
+ * Accepts either a 20-digit CCI or a Yape/Plin phone number.
126
+ */
127
+ declare function validatePeruvianPaymentKey(value: string): boolean;
128
+ type PeruvianPaymentIdParts = {
129
+ qr: string | null;
130
+ phone: string | null;
131
+ cci: string | null;
132
+ };
133
+ /** QR payload and/or CCI and/or Yape/Plin phone. */
134
+ declare function validatePeruvianPaymentId(value: string): boolean;
135
+
136
+ /**
137
+ * Validates Venezuelan phone number for Pago Movil.
138
+ * Format: 04XX-XXXXXXX (11 digits starting with 04).
139
+ */
140
+ declare function validateVenezuelanPhoneNumber(phoneNumber: string): boolean;
141
+ /**
142
+ * Validates Venezuelan Cédula/RIF for Pago Móvil.
143
+ * Format: prefix (V|E|J|G|R|P) followed by digits.
144
+ * Includes natural persons (V), foreigners (E), companies (J), government (G),
145
+ * residual (R), and passport-linked accounts (P).
146
+ */
147
+ declare function validateVenezuelanRif(rif: string): boolean;
148
+ type VenezuelanPaymentIdParts = {
149
+ qr: string | null;
150
+ compound: string | null;
151
+ };
152
+ /**
153
+ * Accepts a Suiche 7B QR payload, the typed `phone|rif|bank` fallback,
154
+ * or both packed as `qr||phone|rif|bank`.
155
+ */
156
+ declare function validateVenezuelanPaymentId(value: string): boolean;
157
+
68
158
  /**
69
159
  * Validates Argentine payment IDs (CBU, CVU, or Alias).
70
160
  * CBU/CVU: 22 digits with checksum. Alias: 6-20 alphanumeric characters.
@@ -139,18 +229,6 @@ declare function validateNigerianAccountNumber(accountNumber: string): boolean;
139
229
  */
140
230
  declare function validateNigerianAccountName(accountName: string): boolean;
141
231
 
142
- /**
143
- * Validates a Peruvian payment key for the SELL text-key path.
144
- * Accepts either a 20-digit CCI (Código de Cuenta Interbancario, spaces ignored)
145
- * or a Yape/Plin phone number (optional `+51`/`51` prefix, then `9` + 8 digits).
146
- */
147
- declare function validatePeruvianPaymentKey(value: string): boolean;
148
- /**
149
- * Validates a Yape/Plin EMVCo QR payload: parseable TLV, country (tag 58) `PE`,
150
- * currency (tag 53) `604` (PEN), and a matching CRC-16/CCITT-FALSE (tag 63).
151
- */
152
- declare function validatePeruvianQr(payload: string): boolean;
153
-
154
232
  /**
155
233
  * Validates a Philippine mobile number for InstaPay (GCash / Maya).
156
234
  * Mobile numbers are 10 digits starting with 9, and are commonly written
@@ -158,27 +236,52 @@ declare function validatePeruvianQr(payload: string): boolean;
158
236
  */
159
237
  declare function validatePhilippinePhoneNumber(phoneNumber: string): boolean;
160
238
 
161
- /**
162
- * Validates Venezuelan phone number for Pago Movil.
163
- * Format: 04XX-XXXXXXX (11 digits starting with 04).
164
- */
165
- declare function validateVenezuelanPhoneNumber(phoneNumber: string): boolean;
166
- /**
167
- * Validates Venezuelan Cédula/RIF for Pago Móvil.
168
- * Format: prefix (V|E|J|G|R|P) followed by digits.
169
- * Includes natural persons (V), foreigners (E), companies (J), government (G),
170
- * residual (R), and passport-linked accounts (P).
171
- */
172
- declare function validateVenezuelanRif(rif: string): boolean;
173
-
174
239
  /** Serializes multiple fields into a pipe-separated string. */
175
240
  declare function serializeCompoundPaymentId(...fields: string[]): string;
176
241
  /** Deserializes a pipe-separated payment ID into its component fields. */
177
242
  declare function deserializeCompoundPaymentId(paymentId: string): string[];
178
243
  /**
179
244
  * Formats a compound payment ID for display using optional labels.
180
- * Fields without a label are shown as-is, fields with a label are shown as "Label: value".
245
+ * Empty parts (optional fields left blank) are omitted.
181
246
  */
182
247
  declare function formatCompoundPaymentIdForDisplay(paymentId: string, labels: (string | null)[]): string;
248
+ /**
249
+ * Validates a stored payment ID against `PAYMENT_ID_FIELDS`.
250
+ * Optional fields may be empty; if every field is optional, at least one must
251
+ * be filled. A legacy single token (no `|`) matches any one field's validator.
252
+ */
253
+ declare function validatePaymentIdFields(fields: readonly PaymentIdFieldConfig[], paymentId: string): boolean;
254
+ /**
255
+ * Splits a stored payment ID into per-field values for form hydration.
256
+ * A legacy single token is assigned to the first field whose validator matches.
257
+ */
258
+ declare function assignPaymentIdToFieldValues(fields: readonly PaymentIdFieldConfig[], paymentId: string): Record<string, string>;
259
+ declare function unpackPackedPaymentId(paymentId: string): {
260
+ qr: string;
261
+ rest: string;
262
+ };
263
+ /**
264
+ * QR blob from a stored payment ID, or `null` if none / invalid.
265
+ */
266
+ declare function getStoredQrPayload(currency: CurrencyCode | null | undefined, paymentId: string | null | undefined): string | null;
267
+ /**
268
+ * Builds a stored payment ID: optional validated QR packed with catalog fields.
269
+ */
270
+ declare function packStoredPaymentId(currency: CurrencyCode, qr: string | null | undefined, fieldValues: Record<string, string>): string;
271
+ /**
272
+ * Validates a stored payment ID: optional packed QR, then catalog fields.
273
+ * QR-only is valid when the country exposes `validateQr`.
274
+ */
275
+ declare function validateStoredPaymentId(currency: CurrencyCode, paymentId: string): boolean;
276
+ /**
277
+ * Typed-field values for form hydration. Packed QR prefix is stripped.
278
+ * `hydrateFieldsFromQr` fills keys the typed fallback left empty (PEN phone).
279
+ */
280
+ declare function assignStoredPaymentIdToFieldValues(currency: CurrencyCode, paymentId: string): Record<string, string>;
281
+ /**
282
+ * Human-readable stored ID: labeled typed fields, never the raw QR blob.
283
+ * QR-only returns an empty string so the caller can substitute a label.
284
+ */
285
+ declare function formatStoredPaymentIdForDisplay(currency: CurrencyCode, paymentId: string): string;
183
286
 
184
- export { COUNTRY_OPTIONS, CURRENCY, CURRENCY_CODES, type CountryOption, type CurrencyCode, PAYMENT_ID_FIELDS, type PaymentIdFieldConfig, deserializeCompoundPaymentId, formatCompoundPaymentIdForDisplay, serializeCompoundPaymentId, validateArgentinePaymentId, validateColombianPaymentId, validateCubanCardNumber, validateCubanPhoneNumber, validateEcuadorianAccountName, validateEcuadorianAccountNumber, validateEcuadorianCedula, validateIndonesianPhoneNumber, validateMexicanPaymentId, validateNigerianAccountName, validateNigerianAccountNumber, validatePIXId, validatePeruvianPaymentKey, validatePeruvianQr, validatePhilippinePhoneNumber, validateRevolutId, validateUPIId, validateVenezuelanPhoneNumber, validateVenezuelanRif };
287
+ export { COUNTRY_OPTIONS, CURRENCY, CURRENCY_CODES, type CountryOption, type CurrencyCode, PACKED_PAYMENT_ID_SEP, PAYMENT_ID_FIELDS, type PaymentIdFieldConfig, type PeruvianPaymentIdParts, type VenezuelanPaymentIdParts, assignPaymentIdToFieldValues, assignStoredPaymentIdToFieldValues, deserializeCompoundPaymentId, formatCompoundPaymentIdForDisplay, formatStoredPaymentIdForDisplay, getCountryOption, getStoredQrPayload, packStoredPaymentId, serializeCompoundPaymentId, unpackPackedPaymentId, uploadsPaymentQR, usesCatalogPaymentForm, usesPackedPaymentId, validateArgentinePaymentId, validateColombianPaymentId, validateCubanCardNumber, validateCubanPhoneNumber, validateEcuadorianAccountName, validateEcuadorianAccountNumber, validateEcuadorianCedula, validateIndonesianPhoneNumber, validateMexicanPaymentId, validateNigerianAccountName, validateNigerianAccountNumber, validatePIXId, validatePaymentIdFields, validatePeruvianCci, validatePeruvianPaymentId, validatePeruvianPaymentKey, validatePeruvianPhone, validatePeruvianQr, validatePhilippinePhoneNumber, validateRevolutId, validateStoredPaymentId, validateUPIId, validateVenezuelanPaymentId, validateVenezuelanPhoneNumber, validateVenezuelanQr, validateVenezuelanRif };
package/dist/country.mjs CHANGED
@@ -597,18 +597,10 @@ var NGN_COUNTRY_OPTION = {
597
597
  disabledPaymentTypes: []
598
598
  };
599
599
 
600
- // src/country/currencies/pen.ts
601
- var PEN_PLACEHOLDER = "CCI (20 digits) or Yape/Plin phone (9XXXXXXXX)";
602
- var PEN_VALIDATION_ERROR = "Please enter a valid 20-digit CCI or Yape/Plin phone number";
603
- function validatePeruvianPaymentKey(value) {
604
- if (!value || value.trim().length === 0) return false;
605
- const trimmed = value.trim();
606
- const cci = trimmed.replace(/\s+/g, "");
607
- if (/^\d{20}$/.test(cci)) return true;
608
- const phone = trimmed.replace(/\s+/g, "").replace(/^\+?51/, "");
609
- if (/^9\d{8}$/.test(phone)) return true;
610
- return false;
611
- }
600
+ // src/country/types.ts
601
+ var PACKED_PAYMENT_ID_SEP = "||";
602
+
603
+ // src/country/qr-validator.ts
612
604
  function crc16ccitt(s) {
613
605
  let crc = 65535;
614
606
  for (let i = 0; i < s.length; i++) {
@@ -643,14 +635,145 @@ function validatePeruvianQr(payload) {
643
635
  const expected = crc16ccitt(data.substring(0, marker + 4));
644
636
  return expected.toUpperCase() === crcTag.toUpperCase();
645
637
  }
638
+ function validateVenezuelanQr(payload) {
639
+ if (!payload || typeof payload !== "string") return false;
640
+ const trimmed = payload.trim();
641
+ if (trimmed.includes(PACKED_PAYMENT_ID_SEP)) return false;
642
+ const qIdx = trimmed.indexOf("?");
643
+ if (qIdx < 40) return false;
644
+ const blob = trimmed.substring(0, qIdx);
645
+ if (!/^[A-Za-z0-9+/=]+$/.test(blob)) return false;
646
+ return /(?:^|[?&])merchantId=\d{3,4}(?:&|$)/.test(trimmed.substring(qIdx));
647
+ }
648
+
649
+ // src/country/currencies/pen.ts
650
+ var PEN_PHONE_PLACEHOLDER = "PERU_PHONE_PLACEHOLDER";
651
+ var PEN_CCI_PLACEHOLDER = "PERU_CCI_PLACEHOLDER";
652
+ var PEN_PHONE_VALIDATION_ERROR = "Please enter a valid Yape/Plin phone (9XXXXXXXX)";
653
+ var PEN_CCI_VALIDATION_ERROR = "Please enter a valid 20-digit CCI";
654
+ function validatePeruvianCci(value) {
655
+ if (!value || value.trim().length === 0) return false;
656
+ return /^\d{20}$/.test(value.trim().replace(/\s+/g, ""));
657
+ }
658
+ function validatePeruvianPhone(value) {
659
+ if (!value || value.trim().length === 0) return false;
660
+ const phone = value.trim().replace(/\s+/g, "").replace(/^\+?51/, "");
661
+ return /^9\d{8}$/.test(phone);
662
+ }
663
+ function validatePeruvianPaymentKey(value) {
664
+ return validatePeruvianCci(value) || validatePeruvianPhone(value);
665
+ }
666
+ function normalizePeruvianPaymentKey(value) {
667
+ const trimmed = value.trim();
668
+ const cci = trimmed.replace(/\s+/g, "");
669
+ if (/^\d{20}$/.test(cci)) return cci;
670
+ const phone = trimmed.replace(/\s+/g, "").replace(/^\+?51/, "");
671
+ if (/^9\d{8}$/.test(phone)) return phone;
672
+ return cci;
673
+ }
674
+ function looksLikeTlv(value) {
675
+ if (value.length < 5 || !/^\d{4}/.test(value)) return false;
676
+ const len = Number.parseInt(value.slice(2, 4), 10);
677
+ return !Number.isNaN(len) && len >= 0 && 4 + len <= value.length;
678
+ }
679
+ function phoneFromTlvValue(value) {
680
+ const trimmed = value.trim();
681
+ if (!trimmed) return null;
682
+ if (validatePeruvianPhone(trimmed)) return normalizePeruvianPaymentKey(trimmed);
683
+ if (/^[0-9a-f]+$/i.test(trimmed) && /[a-f]/i.test(trimmed)) return null;
684
+ const match = trimmed.match(/(^|[^0-9])(\+?51)?(9\d{8})(?![0-9])/);
685
+ if (!match) return null;
686
+ const candidate = `${match[2] ?? ""}${match[3]}`;
687
+ return validatePeruvianPhone(candidate) ? normalizePeruvianPaymentKey(candidate) : null;
688
+ }
689
+ function extractPeruvianPhoneFromQr(payload) {
690
+ let pos = 0;
691
+ while (pos + 4 <= payload.length) {
692
+ const tag = payload.slice(pos, pos + 2);
693
+ const len = Number.parseInt(payload.slice(pos + 2, pos + 4), 10);
694
+ if (Number.isNaN(len) || len < 0 || pos + 4 + len > payload.length) break;
695
+ const value = payload.slice(pos + 4, pos + 4 + len);
696
+ const direct = phoneFromTlvValue(value);
697
+ if (direct) return direct;
698
+ if (tag >= "02" && tag <= "51" || tag === "62" || tag === "64" || looksLikeTlv(value)) {
699
+ const nested = extractPeruvianPhoneFromQr(value);
700
+ if (nested) return nested;
701
+ }
702
+ pos += 4 + len;
703
+ }
704
+ const glued = payload.match(/\+51\s*9\d{8}/);
705
+ if (glued && validatePeruvianPhone(glued[0])) {
706
+ return normalizePeruvianPaymentKey(glued[0]);
707
+ }
708
+ return null;
709
+ }
710
+ function parsePeruvianFallback(value) {
711
+ const trimmed = value.trim();
712
+ if (!trimmed) return { phone: null, cci: null };
713
+ const tokens = trimmed.split("|").map((token) => token.trim()).filter(Boolean);
714
+ let phone = null;
715
+ let cci = null;
716
+ for (const token of tokens) {
717
+ if (!phone && validatePeruvianPhone(token)) {
718
+ phone = normalizePeruvianPaymentKey(token);
719
+ continue;
720
+ }
721
+ if (!cci && validatePeruvianCci(token)) {
722
+ cci = normalizePeruvianPaymentKey(token);
723
+ }
724
+ }
725
+ return { phone, cci };
726
+ }
727
+ function parsePeruvianPaymentId(value) {
728
+ if (!value || typeof value !== "string") {
729
+ return { qr: null, phone: null, cci: null };
730
+ }
731
+ const trimmed = value.trim();
732
+ const sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);
733
+ if (sep >= 0) {
734
+ const left = trimmed.slice(0, sep);
735
+ const right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);
736
+ const fallback2 = parsePeruvianFallback(right);
737
+ const qr = validatePeruvianQr(left) ? left : null;
738
+ return {
739
+ qr,
740
+ phone: fallback2.phone ?? (qr ? extractPeruvianPhoneFromQr(qr) : null),
741
+ cci: fallback2.cci
742
+ };
743
+ }
744
+ if (validatePeruvianQr(trimmed)) {
745
+ return {
746
+ qr: trimmed,
747
+ phone: extractPeruvianPhoneFromQr(trimmed),
748
+ cci: null
749
+ };
750
+ }
751
+ const fallback = parsePeruvianFallback(trimmed);
752
+ return { qr: null, phone: fallback.phone, cci: fallback.cci };
753
+ }
754
+ function validatePeruvianPaymentId(value) {
755
+ if (!value || typeof value !== "string") return false;
756
+ const { qr, phone, cci } = parsePeruvianPaymentId(value);
757
+ return Boolean(qr || phone || cci);
758
+ }
646
759
  var PEN_PAYMENT_FIELDS = [
647
760
  {
648
- key: "paymentId",
649
- label: "CCI_YAPE_PLIN",
650
- placeholder: PEN_PLACEHOLDER,
651
- displayLabel: "CCI / Yape / Plin",
652
- validate: validatePeruvianPaymentKey,
653
- validationErrorMessage: PEN_VALIDATION_ERROR
761
+ key: "phone",
762
+ label: "PERU_PHONE_LABEL",
763
+ placeholder: PEN_PHONE_PLACEHOLDER,
764
+ displayLabel: "Yape / Plin phone",
765
+ validate: validatePeruvianPhone,
766
+ validationErrorMessage: PEN_PHONE_VALIDATION_ERROR,
767
+ optional: true
768
+ },
769
+ {
770
+ key: "cci",
771
+ label: "PERU_CCI_LABEL",
772
+ placeholder: PEN_CCI_PLACEHOLDER,
773
+ displayLabel: "CCI",
774
+ validate: validatePeruvianCci,
775
+ validationErrorMessage: PEN_CCI_VALIDATION_ERROR,
776
+ optional: true
654
777
  }
655
778
  ];
656
779
  var PEN_COUNTRY_OPTION = {
@@ -671,7 +794,13 @@ var PEN_COUNTRY_OPTION = {
671
794
  precision: 2,
672
795
  isAlpha: true,
673
796
  disabled: false,
674
- disabledPaymentTypes: []
797
+ disabledPaymentTypes: [],
798
+ uploadPaymentQR: true,
799
+ validateQr: validatePeruvianQr,
800
+ hydrateFieldsFromQr: (qr) => {
801
+ const phone = extractPeruvianPhoneFromQr(qr);
802
+ return phone ? { phone } : {};
803
+ }
675
804
  };
676
805
 
677
806
  // src/country/currencies/php.ts
@@ -780,6 +909,32 @@ function validateVenezuelanRif(rif) {
780
909
  const trimmed = rif.trim().toUpperCase();
781
910
  return /^[VEJGRP]\d+$/.test(trimmed);
782
911
  }
912
+ function isValidVenezuelanCompound(value) {
913
+ const parts = value.split("|");
914
+ if (parts.length !== 3) return false;
915
+ return validateVenezuelanPhoneNumber(parts[0]) && validateVenezuelanRif(parts[1]) && parts[2].trim().length > 0;
916
+ }
917
+ function parseVenezuelanPaymentId(value) {
918
+ if (!value || typeof value !== "string") return { qr: null, compound: null };
919
+ const trimmed = value.trim();
920
+ const sep = trimmed.indexOf(PACKED_PAYMENT_ID_SEP);
921
+ if (sep >= 0) {
922
+ const left = trimmed.slice(0, sep);
923
+ const right = trimmed.slice(sep + PACKED_PAYMENT_ID_SEP.length);
924
+ return {
925
+ qr: validateVenezuelanQr(left) ? left : null,
926
+ compound: isValidVenezuelanCompound(right) ? right : null
927
+ };
928
+ }
929
+ if (validateVenezuelanQr(trimmed)) return { qr: trimmed, compound: null };
930
+ if (isValidVenezuelanCompound(trimmed)) return { qr: null, compound: trimmed };
931
+ return { qr: null, compound: null };
932
+ }
933
+ function validateVenezuelanPaymentId(value) {
934
+ if (!value || typeof value !== "string") return false;
935
+ const { qr, compound } = parseVenezuelanPaymentId(value);
936
+ return qr !== null || compound !== null;
937
+ }
783
938
  var VEN_PAYMENT_FIELDS = [
784
939
  {
785
940
  key: "phone",
@@ -825,27 +980,11 @@ var VEN_COUNTRY_OPTION = {
825
980
  precision: 2,
826
981
  isAlpha: false,
827
982
  disabled: false,
828
- disabledPaymentTypes: []
983
+ disabledPaymentTypes: [],
984
+ uploadPaymentQR: true,
985
+ validateQr: validateVenezuelanQr
829
986
  };
830
987
 
831
- // src/country/countries.ts
832
- var COUNTRY_OPTIONS = [
833
- INR_COUNTRY_OPTION,
834
- IDR_COUNTRY_OPTION,
835
- BRL_COUNTRY_OPTION,
836
- ARS_COUNTRY_OPTION,
837
- MEX_COUNTRY_OPTION,
838
- VEN_COUNTRY_OPTION,
839
- NGN_COUNTRY_OPTION,
840
- COP_COUNTRY_OPTION,
841
- CUP_COUNTRY_OPTION,
842
- ECU_COUNTRY_OPTION,
843
- PEN_COUNTRY_OPTION,
844
- PHP_COUNTRY_OPTION,
845
- EUR_COUNTRY_OPTION,
846
- USD_COUNTRY_OPTION
847
- ];
848
-
849
988
  // src/country/payment-fields.ts
850
989
  var PAYMENT_ID_FIELDS = {
851
990
  INR: INR_PAYMENT_FIELDS,
@@ -864,6 +1003,38 @@ var PAYMENT_ID_FIELDS = {
864
1003
  PHP: PHP_PAYMENT_FIELDS
865
1004
  };
866
1005
 
1006
+ // src/country/countries.ts
1007
+ var COUNTRY_OPTIONS = [
1008
+ INR_COUNTRY_OPTION,
1009
+ IDR_COUNTRY_OPTION,
1010
+ BRL_COUNTRY_OPTION,
1011
+ ARS_COUNTRY_OPTION,
1012
+ MEX_COUNTRY_OPTION,
1013
+ VEN_COUNTRY_OPTION,
1014
+ NGN_COUNTRY_OPTION,
1015
+ COP_COUNTRY_OPTION,
1016
+ CUP_COUNTRY_OPTION,
1017
+ ECU_COUNTRY_OPTION,
1018
+ PEN_COUNTRY_OPTION,
1019
+ PHP_COUNTRY_OPTION,
1020
+ EUR_COUNTRY_OPTION,
1021
+ USD_COUNTRY_OPTION
1022
+ ];
1023
+ function getCountryOption(currency) {
1024
+ if (!currency) return void 0;
1025
+ return COUNTRY_OPTIONS.find((c) => c.currency === currency);
1026
+ }
1027
+ function uploadsPaymentQR(currency) {
1028
+ return getCountryOption(currency)?.uploadPaymentQR === true;
1029
+ }
1030
+ function usesPackedPaymentId(currency) {
1031
+ return getCountryOption(currency)?.validateQr != null;
1032
+ }
1033
+ function usesCatalogPaymentForm(currency) {
1034
+ if (!currency) return false;
1035
+ return uploadsPaymentQR(currency) || (PAYMENT_ID_FIELDS[currency]?.length ?? 0) > 1;
1036
+ }
1037
+
867
1038
  // src/country/validators.ts
868
1039
  function serializeCompoundPaymentId(...fields) {
869
1040
  return fields.join("|");
@@ -873,16 +1044,171 @@ function deserializeCompoundPaymentId(paymentId) {
873
1044
  }
874
1045
  function formatCompoundPaymentIdForDisplay(paymentId, labels) {
875
1046
  const parts = deserializeCompoundPaymentId(paymentId);
876
- return parts.map((part, i) => labels[i] ? `${labels[i]}: ${part}` : part).join(" | ");
1047
+ return parts.map((part, i) => {
1048
+ if (!part?.trim()) return null;
1049
+ return labels[i] ? `${labels[i]}: ${part}` : part;
1050
+ }).filter((part) => part !== null).join(" | ");
1051
+ }
1052
+ function validatePaymentIdFields(fields, paymentId) {
1053
+ if (!paymentId || paymentId.trim().length === 0) return false;
1054
+ if (!fields.length) return false;
1055
+ if (fields.length === 1) {
1056
+ return fields[0].validate(paymentId);
1057
+ }
1058
+ const parts = deserializeCompoundPaymentId(paymentId);
1059
+ if (parts.length === 1) {
1060
+ if (!fields.some((field) => field.optional)) return false;
1061
+ return fields.some((field) => field.validate(parts[0]));
1062
+ }
1063
+ const values = fields.map((_, i) => (parts[i] ?? "").trim());
1064
+ const eachOk = fields.every((field, i) => {
1065
+ const value = values[i];
1066
+ if (!value) return field.optional === true;
1067
+ return field.validate(value);
1068
+ });
1069
+ if (!eachOk) return false;
1070
+ return values.some((value) => value.length > 0);
1071
+ }
1072
+ function assignPaymentIdToFieldValues(fields, paymentId) {
1073
+ const result = {};
1074
+ for (const field of fields) result[field.key] = "";
1075
+ if (!paymentId) return result;
1076
+ const { rest } = unpackPackedPaymentId(paymentId);
1077
+ if (!rest) return result;
1078
+ const tokens = deserializeCompoundPaymentId(rest).map((part) => part.trim()).filter((part) => part.length > 0);
1079
+ const claimed = /* @__PURE__ */ new Set();
1080
+ for (const token of tokens) {
1081
+ const field = fields.find((item) => !claimed.has(item.key) && item.validate(token));
1082
+ if (field) {
1083
+ result[field.key] = token;
1084
+ claimed.add(field.key);
1085
+ }
1086
+ }
1087
+ if (claimed.size === 0 && tokens.length === 1 && fields.length === 1) {
1088
+ result[fields[0].key] = tokens[0];
1089
+ return result;
1090
+ }
1091
+ if (claimed.size < Math.min(tokens.length, fields.length)) {
1092
+ fields.forEach((field, i) => {
1093
+ if (!result[field.key]) result[field.key] = tokens[i] ?? "";
1094
+ });
1095
+ }
1096
+ return result;
1097
+ }
1098
+ function unpackPackedPaymentId(paymentId) {
1099
+ const sep = paymentId.indexOf(PACKED_PAYMENT_ID_SEP);
1100
+ if (sep < 0) return { qr: "", rest: paymentId };
1101
+ return {
1102
+ qr: paymentId.slice(0, sep),
1103
+ rest: paymentId.slice(sep + PACKED_PAYMENT_ID_SEP.length)
1104
+ };
1105
+ }
1106
+ function isStandaloneQr(validateQr, value) {
1107
+ return !!validateQr?.(value);
1108
+ }
1109
+ function getStoredQrPayload(currency, paymentId) {
1110
+ if (!currency || !paymentId) return null;
1111
+ const option = getCountryOption(currency);
1112
+ if (!option?.validateQr) return null;
1113
+ const trimmed = paymentId.trim();
1114
+ const { qr } = unpackPackedPaymentId(trimmed);
1115
+ if (qr && option.validateQr(qr)) return qr;
1116
+ if (isStandaloneQr(option.validateQr, trimmed)) return trimmed;
1117
+ return null;
1118
+ }
1119
+ function packedTypedRest(fields, fieldValues) {
1120
+ const parts = fields.map((field) => {
1121
+ const value = (fieldValues[field.key] ?? "").trim();
1122
+ if (!value) return "";
1123
+ return field.validate(value) ? value : "";
1124
+ });
1125
+ if (!parts.some((part) => part.length > 0)) return "";
1126
+ return serializeCompoundPaymentId(...parts);
1127
+ }
1128
+ function packStoredPaymentId(currency, qr, fieldValues) {
1129
+ const fields = PAYMENT_ID_FIELDS[currency] ?? [];
1130
+ const option = getCountryOption(currency);
1131
+ const trimmedQr = qr?.trim() ?? "";
1132
+ const q = trimmedQr && option?.validateQr?.(trimmedQr) ? trimmedQr : "";
1133
+ const rest = packedTypedRest(fields, fieldValues);
1134
+ if (q && rest) return `${q}${PACKED_PAYMENT_ID_SEP}${rest}`;
1135
+ return q || rest;
1136
+ }
1137
+ function fieldsMatchStoredId(fields, paymentId) {
1138
+ if (validatePaymentIdFields(fields, paymentId)) return true;
1139
+ const assigned = assignPaymentIdToFieldValues(fields, paymentId);
1140
+ const filled = fields.filter((field) => (assigned[field.key] || "").trim().length > 0);
1141
+ if (filled.length === 0) return false;
1142
+ return fields.every(
1143
+ (field) => field.optional === true || (assigned[field.key] || "").trim().length > 0
1144
+ );
1145
+ }
1146
+ function validateStoredPaymentId(currency, paymentId) {
1147
+ if (!paymentId || paymentId.trim().length === 0) return false;
1148
+ const fields = PAYMENT_ID_FIELDS[currency];
1149
+ if (!fields?.length) return false;
1150
+ const option = getCountryOption(currency);
1151
+ const trimmed = paymentId.trim();
1152
+ const { qr, rest } = unpackPackedPaymentId(trimmed);
1153
+ if (qr) {
1154
+ if (!option?.validateQr?.(qr)) return false;
1155
+ if (!rest.trim()) return true;
1156
+ return fieldsMatchStoredId(fields, rest);
1157
+ }
1158
+ if (isStandaloneQr(option?.validateQr, trimmed)) return true;
1159
+ return fieldsMatchStoredId(fields, trimmed);
1160
+ }
1161
+ function assignStoredPaymentIdToFieldValues(currency, paymentId) {
1162
+ const fields = PAYMENT_ID_FIELDS[currency] ?? [];
1163
+ const option = getCountryOption(currency);
1164
+ const trimmed = paymentId.trim();
1165
+ const { qr, rest } = unpackPackedPaymentId(trimmed);
1166
+ let qrPayload = "";
1167
+ let typed = rest.trim();
1168
+ if (qr && option?.validateQr?.(qr)) {
1169
+ qrPayload = qr;
1170
+ } else if (isStandaloneQr(option?.validateQr, trimmed)) {
1171
+ qrPayload = trimmed;
1172
+ typed = "";
1173
+ }
1174
+ const values = assignPaymentIdToFieldValues(fields, typed);
1175
+ if (qrPayload && option?.hydrateFieldsFromQr) {
1176
+ const extra = option.hydrateFieldsFromQr(qrPayload);
1177
+ for (const [key, value] of Object.entries(extra)) {
1178
+ if (value && !values[key]) values[key] = value;
1179
+ }
1180
+ }
1181
+ return values;
1182
+ }
1183
+ function formatStoredPaymentIdForDisplay(currency, paymentId) {
1184
+ const fields = PAYMENT_ID_FIELDS[currency] ?? [];
1185
+ const values = assignStoredPaymentIdToFieldValues(currency, paymentId);
1186
+ if (fields.length <= 1) return (values[fields[0]?.key ?? ""] || "").trim();
1187
+ return fields.map((field) => {
1188
+ const value = (values[field.key] || "").trim();
1189
+ if (!value) return null;
1190
+ return field.displayLabel ? `${field.displayLabel}: ${value}` : value;
1191
+ }).filter((part) => part !== null).join(" | ");
877
1192
  }
878
1193
  export {
879
1194
  COUNTRY_OPTIONS,
880
1195
  CURRENCY,
881
1196
  CURRENCY_CODES,
1197
+ PACKED_PAYMENT_ID_SEP,
882
1198
  PAYMENT_ID_FIELDS,
1199
+ assignPaymentIdToFieldValues,
1200
+ assignStoredPaymentIdToFieldValues,
883
1201
  deserializeCompoundPaymentId,
884
1202
  formatCompoundPaymentIdForDisplay,
1203
+ formatStoredPaymentIdForDisplay,
1204
+ getCountryOption,
1205
+ getStoredQrPayload,
1206
+ packStoredPaymentId,
885
1207
  serializeCompoundPaymentId,
1208
+ unpackPackedPaymentId,
1209
+ uploadsPaymentQR,
1210
+ usesCatalogPaymentForm,
1211
+ usesPackedPaymentId,
886
1212
  validateArgentinePaymentId,
887
1213
  validateColombianPaymentId,
888
1214
  validateCubanCardNumber,
@@ -895,12 +1221,19 @@ export {
895
1221
  validateNigerianAccountName,
896
1222
  validateNigerianAccountNumber,
897
1223
  validatePIXId,
1224
+ validatePaymentIdFields,
1225
+ validatePeruvianCci,
1226
+ validatePeruvianPaymentId,
898
1227
  validatePeruvianPaymentKey,
1228
+ validatePeruvianPhone,
899
1229
  validatePeruvianQr,
900
1230
  validatePhilippinePhoneNumber,
901
1231
  validateRevolutId,
1232
+ validateStoredPaymentId,
902
1233
  validateUPIId,
1234
+ validateVenezuelanPaymentId,
903
1235
  validateVenezuelanPhoneNumber,
1236
+ validateVenezuelanQr,
904
1237
  validateVenezuelanRif
905
1238
  };
906
1239
  //# sourceMappingURL=country.mjs.map