@p2pdotme/sdk 1.2.15 → 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.
@@ -253,7 +253,105 @@ var NGN_COUNTRY_OPTION = {
253
253
  disabledPaymentTypes: []
254
254
  };
255
255
 
256
+ // src/country/types.ts
257
+ var PACKED_PAYMENT_ID_SEP = "||";
258
+
259
+ // src/country/qr-validator.ts
260
+ function crc16ccitt(s) {
261
+ let crc = 65535;
262
+ for (let i = 0; i < s.length; i++) {
263
+ crc ^= s.charCodeAt(i) << 8;
264
+ for (let j = 0; j < 8; j++) {
265
+ crc = crc & 32768 ? crc << 1 ^ 4129 : crc << 1;
266
+ crc &= 65535;
267
+ }
268
+ }
269
+ return crc.toString(16).toUpperCase().padStart(4, "0");
270
+ }
271
+ function validatePeruvianQr(payload) {
272
+ if (!payload || payload.trim().length === 0) return false;
273
+ const data = payload.trim();
274
+ const tags = {};
275
+ let pos = 0;
276
+ while (pos + 4 <= data.length) {
277
+ const tag = data.substring(pos, pos + 2);
278
+ const lengthStr = data.substring(pos + 2, pos + 4);
279
+ if (!/^[0-9]{2}$/.test(tag) || !/^[0-9]{2}$/.test(lengthStr)) break;
280
+ const length = Number.parseInt(lengthStr, 10);
281
+ if (pos + 4 + length > data.length) break;
282
+ tags[tag] = data.substring(pos + 4, pos + 4 + length);
283
+ pos += 4 + length;
284
+ }
285
+ if (tags["58"] !== "PE") return false;
286
+ if (tags["53"] !== "604") return false;
287
+ const crcTag = tags["63"];
288
+ if (!crcTag) return false;
289
+ const marker = data.lastIndexOf("6304");
290
+ if (marker === -1) return false;
291
+ const expected = crc16ccitt(data.substring(0, marker + 4));
292
+ return expected.toUpperCase() === crcTag.toUpperCase();
293
+ }
294
+ function validateVenezuelanQr(payload) {
295
+ if (!payload || typeof payload !== "string") return false;
296
+ const trimmed = payload.trim();
297
+ if (trimmed.includes(PACKED_PAYMENT_ID_SEP)) return false;
298
+ const qIdx = trimmed.indexOf("?");
299
+ if (qIdx < 40) return false;
300
+ const blob = trimmed.substring(0, qIdx);
301
+ if (!/^[A-Za-z0-9+/=]+$/.test(blob)) return false;
302
+ return /(?:^|[?&])merchantId=\d{3,4}(?:&|$)/.test(trimmed.substring(qIdx));
303
+ }
304
+
256
305
  // src/country/currencies/pen.ts
306
+ function validatePeruvianPhone(value) {
307
+ if (!value || value.trim().length === 0) return false;
308
+ const phone = value.trim().replace(/\s+/g, "").replace(/^\+?51/, "");
309
+ return /^9\d{8}$/.test(phone);
310
+ }
311
+ function normalizePeruvianPaymentKey(value) {
312
+ const trimmed = value.trim();
313
+ const cci = trimmed.replace(/\s+/g, "");
314
+ if (/^\d{20}$/.test(cci)) return cci;
315
+ const phone = trimmed.replace(/\s+/g, "").replace(/^\+?51/, "");
316
+ if (/^9\d{8}$/.test(phone)) return phone;
317
+ return cci;
318
+ }
319
+ function looksLikeTlv(value) {
320
+ if (value.length < 5 || !/^\d{4}/.test(value)) return false;
321
+ const len = Number.parseInt(value.slice(2, 4), 10);
322
+ return !Number.isNaN(len) && len >= 0 && 4 + len <= value.length;
323
+ }
324
+ function phoneFromTlvValue(value) {
325
+ const trimmed = value.trim();
326
+ if (!trimmed) return null;
327
+ if (validatePeruvianPhone(trimmed)) return normalizePeruvianPaymentKey(trimmed);
328
+ if (/^[0-9a-f]+$/i.test(trimmed) && /[a-f]/i.test(trimmed)) return null;
329
+ const match = trimmed.match(/(^|[^0-9])(\+?51)?(9\d{8})(?![0-9])/);
330
+ if (!match) return null;
331
+ const candidate = `${match[2] ?? ""}${match[3]}`;
332
+ return validatePeruvianPhone(candidate) ? normalizePeruvianPaymentKey(candidate) : null;
333
+ }
334
+ function extractPeruvianPhoneFromQr(payload) {
335
+ let pos = 0;
336
+ while (pos + 4 <= payload.length) {
337
+ const tag = payload.slice(pos, pos + 2);
338
+ const len = Number.parseInt(payload.slice(pos + 2, pos + 4), 10);
339
+ if (Number.isNaN(len) || len < 0 || pos + 4 + len > payload.length) break;
340
+ const value = payload.slice(pos + 4, pos + 4 + len);
341
+ const direct = phoneFromTlvValue(value);
342
+ if (direct) return direct;
343
+ if (tag >= "02" && tag <= "51" || tag === "62" || tag === "64" || looksLikeTlv(value)) {
344
+ const nested = extractPeruvianPhoneFromQr(value);
345
+ if (nested) return nested;
346
+ }
347
+ pos += 4 + len;
348
+ }
349
+ const glued = payload.match(/\+51\s*9\d{8}/);
350
+ if (glued && validatePeruvianPhone(glued[0])) {
351
+ return normalizePeruvianPaymentKey(glued[0]);
352
+ }
353
+ return null;
354
+ }
257
355
  var PEN_COUNTRY_OPTION = {
258
356
  country: "Peru",
259
357
  currency: CURRENCY.PEN,
@@ -272,7 +370,13 @@ var PEN_COUNTRY_OPTION = {
272
370
  precision: 2,
273
371
  isAlpha: true,
274
372
  disabled: false,
275
- disabledPaymentTypes: []
373
+ disabledPaymentTypes: [],
374
+ uploadPaymentQR: true,
375
+ validateQr: validatePeruvianQr,
376
+ hydrateFieldsFromQr: (qr) => {
377
+ const phone = extractPeruvianPhoneFromQr(qr);
378
+ return phone ? { phone } : {};
379
+ }
276
380
  };
277
381
 
278
382
  // src/country/currencies/php.ts
@@ -339,7 +443,9 @@ var VEN_COUNTRY_OPTION = {
339
443
  precision: 2,
340
444
  isAlpha: false,
341
445
  disabled: false,
342
- disabledPaymentTypes: []
446
+ disabledPaymentTypes: [],
447
+ uploadPaymentQR: true,
448
+ validateQr: validateVenezuelanQr
343
449
  };
344
450
 
345
451
  // src/country/countries.ts
@@ -900,17 +1006,22 @@ function parseQRPh(qrData, sellPrice) {
900
1006
  }
901
1007
 
902
1008
  // src/qr-parsers/parsers/ven.ts
903
- function parsePagoMovil(qrData, _sellPrice) {
1009
+ function isPagoMovilQr(qrData) {
904
1010
  if (!qrData || typeof qrData !== "string" || qrData.trim().length === 0) {
905
- return failure("INVALID_QR", "QR data is empty or invalid");
1011
+ return false;
906
1012
  }
907
1013
  const trimmed = qrData.trim();
908
1014
  const qIdx = trimmed.indexOf("?");
909
- if (qIdx === -1) {
910
- return failure("INVALID_QR", "Not a valid Venezuelan QR code");
911
- }
1015
+ if (qIdx === -1) return false;
912
1016
  const payload = trimmed.substring(0, qIdx);
913
- if (!payload || !/^[A-Za-z0-9+/=]+$/.test(payload)) {
1017
+ return !!payload && /^[A-Za-z0-9+/=]+$/.test(payload);
1018
+ }
1019
+ function parsePagoMovil(qrData, _sellPrice) {
1020
+ if (!qrData || typeof qrData !== "string" || qrData.trim().length === 0) {
1021
+ return failure("INVALID_QR", "QR data is empty or invalid");
1022
+ }
1023
+ const trimmed = qrData.trim();
1024
+ if (!isPagoMovilQr(trimmed)) {
914
1025
  return failure("INVALID_QR", "Not a valid Venezuelan QR code");
915
1026
  }
916
1027
  return success({ paymentAddress: trimmed });
@@ -951,6 +1062,7 @@ async function parseQR(params) {
951
1062
  }
952
1063
  export {
953
1064
  QRParserError,
1065
+ isPagoMovilQr,
954
1066
  parseQR
955
1067
  };
956
1068
  //# sourceMappingURL=qr-parsers.mjs.map