@agent-cards/sdk 0.23.10 → 0.23.12

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,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: bbfd8946fd3963bf32fe66d5c17ba50dd69e141187c1d5896ee60c33b3aa0e41
3
+ // artifact-sha256: b3391e7d6137e269555a23062ce848aa7a7f6f02e626fd568b1e9597c02e5ce6
4
4
  // src/card-number-key.js
5
5
  var CARD_NUMBER_KEY = /^(number|card_number|cardnumber|cardNum|card_num|pan)$/i;
6
6
  // src/json-path.js
@@ -83,6 +83,7 @@ var ONE_TIME_RECURRING_FLAG = "false";
83
83
  var CARD_SAVING_FLAGS = /* @__PURE__ */ Object.freeze(["ssl_get_token", "ssl_add_token"]);
84
84
  var CREDENTIAL_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_account_id", "ssl_merchant_id", "ssl_user_id", "ssl_pin", "ssl_vendor_id"]);
85
85
  var AMOUNT_ADD_ON_FIELDS = /* @__PURE__ */ Object.freeze(["ssl_tip_amount", "ssl_cashback_amount"]);
86
+ var SOLVED_CAPTCHA = /^[A-Za-z0-9_-]{1,4096}$/;
86
87
  var CARD_NUMBER = /^\d{12,19}$/;
87
88
  var EXPIRY_MMYY = /^(0[1-9]|1[0-2])\d{2}$/;
88
89
  var CVC = /^\d{3,4}$/;
@@ -138,7 +139,7 @@ function convergeBodyVerdict(body) {
138
139
  return refuse("converge_credential_refused");
139
140
  if (WALLET_FIELDS.some((key) => owns(data, key)))
140
141
  return refuse("converge_wallet_refused");
141
- if (owns(parsed, "captchaToken"))
142
+ if (owns(parsed, "captchaToken") && !(typeof parsed.captchaToken === "string" && SOLVED_CAPTCHA.test(parsed.captchaToken)))
142
143
  return refuse("converge_captcha_refused");
143
144
  if (SERVICE_FEE_FIELDS.some((key) => owns(data, key)))
144
145
  return refuse("converge_service_fee_refused");
@@ -347,8 +348,79 @@ function amadeusBodyKind(body) {
347
348
  return record2(value.data) && holdsPan(value.data.mopdata) ? "card" : "other";
348
349
  return "other";
349
350
  }
351
+ // src/paddle-vgs.js
352
+ var PADDLE_VGS_PSP = "paddle_vgs";
353
+ var PADDLE_VGS_CARDS_URLS = /* @__PURE__ */ Object.freeze({
354
+ production: "https://vault.paddle.com/cards",
355
+ sandbox: "https://sandbox-vault.paddle.com/cards"
356
+ });
357
+ var BODY_LIMIT2 = 4096;
358
+ var ROOT_KEYS2 = /* @__PURE__ */ Object.freeze(["data"]);
359
+ var DATA_KEYS = /* @__PURE__ */ Object.freeze(["type", "attributes"]);
360
+ var ATTRIBUTE_KEYS = /* @__PURE__ */ Object.freeze(["pan", "cvc", "exp_month", "exp_year", "cardholder"]);
361
+ var CARD_NUMBER4 = /^[0-9]{12,19}$/;
362
+ var SECURITY_CODE2 = /^[0-9]{3,4}$/;
363
+ var NAME_LIMIT = 256;
364
+ var CONTROL = /[\u0000-\u001f\u007f]/;
365
+ var cardLikeName = (name) => (name.match(/[0-9]/g) ?? []).length >= 12;
366
+ var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
367
+ var sameKeys = (value, keys) => {
368
+ const own = Object.keys(value);
369
+ return own.length === keys.length && own.every((key, at) => key === keys[at]);
370
+ };
371
+ var refuse4 = (code) => Object.freeze({ verdict: "refuse", code });
372
+ var CARD4 = /* @__PURE__ */ Object.freeze({ verdict: "card" });
373
+ function paddleVgsEnvironment(url) {
374
+ if (url === PADDLE_VGS_CARDS_URLS.production)
375
+ return "production";
376
+ if (url === PADDLE_VGS_CARDS_URLS.sandbox)
377
+ return "sandbox";
378
+ return null;
379
+ }
380
+ function isPaddleVgsCardsUrl(url) {
381
+ return paddleVgsEnvironment(url) !== null;
382
+ }
383
+ function readBody2(body) {
384
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT2)
385
+ return null;
386
+ let value;
387
+ try {
388
+ value = JSON.parse(body);
389
+ if (!record3(value) || JSON.stringify(value) !== body)
390
+ return null;
391
+ }
392
+ catch {
393
+ return null;
394
+ }
395
+ return value;
396
+ }
397
+ function paddleVgsBodyVerdict(body) {
398
+ const value = readBody2(body);
399
+ if (value === null || !sameKeys(value, ROOT_KEYS2) || !record3(value.data) || !sameKeys(value.data, DATA_KEYS) || value.data.type !== "cards" || !record3(value.data.attributes) || !sameKeys(value.data.attributes, ATTRIBUTE_KEYS)) {
400
+ return refuse4("paddle_vgs_body_unsupported");
401
+ }
402
+ const card = value.data.attributes;
403
+ const holder = card.cardholder;
404
+ if (typeof card.pan !== "string" || !CARD_NUMBER4.test(card.pan) || typeof card.cvc !== "string" || !SECURITY_CODE2.test(card.cvc) || !Number.isInteger(card.exp_month) || card.exp_month < 1 || card.exp_month > 12 || !Number.isInteger(card.exp_year) || card.exp_year < 0 || card.exp_year > 99 || !record3(holder) || !sameKeys(holder, ["name"]) || typeof holder.name !== "string" || holder.name.length > NAME_LIMIT || CONTROL.test(holder.name) || cardLikeName(holder.name)) {
405
+ return refuse4("paddle_vgs_body_unsupported");
406
+ }
407
+ return CARD4;
408
+ }
409
+ var pin2 = (fields) => Object.freeze({
410
+ ...fields,
411
+ origins: Object.freeze([...fields.origins]),
412
+ sellerClaim: fields.sellerClaim ? Object.freeze({ ...fields.sellerClaim }) : null
413
+ });
414
+ var PADDLE_VGS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
415
+ pin2({ id: "rectangle_pro", merchant: "Rectangle Pro", origins: ["https://rectangleapp.com"], sellerClaim: null })
416
+ ]);
350
417
  // src/card-fields.js
351
418
  function processorGate(recognizer, url, body) {
419
+ if (recognizer && recognizer.psp === PADDLE_VGS_PSP) {
420
+ if (url !== void 0 && !isPaddleVgsCardsUrl(url))
421
+ return { verdict: "refuse", code: "paddle_vgs_url_refused" };
422
+ return paddleVgsBodyVerdict(body);
423
+ }
352
424
  if (recognizer && recognizer.psp === GRAVY_PSP)
353
425
  return gravyBodyVerdict(body);
354
426
  if (recognizer && recognizer.psp === MONERIS_PSP) {
@@ -469,12 +541,12 @@ var KEEP_ON_FILE_REFUSAL = "Refusing to pay: this Primer payment would keep the
469
541
  var UNSUPPORTED_SDK_REFUSAL = "Refusing to pay: this checkout uses a Primer SDK Agentcard does not support yet. Nothing was sent.";
470
542
  var NETWORK_MISMATCH_REFUSAL = "Refusing to pay: this Primer payment names a card network that is not your card's. Nothing was sent.";
471
543
  var FILL_REFUSAL = "Could not fill the card fields for Primer. Nothing was sent.";
472
- var BODY_LIMIT2 = 16384;
544
+ var BODY_LIMIT3 = 16384;
473
545
  var RESPONSE_LIMIT = 32768;
474
- var ROOT_KEYS2 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
546
+ var ROOT_KEYS3 = ["paymentInstrument", "userDescription", "tokenType", "paymentFlow"];
475
547
  var INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear", "cardholderName", "preferredNetwork"];
476
548
  var REQUIRED_INSTRUMENT_KEYS = ["number", "cvv", "expirationMonth", "expirationYear"];
477
- var record3 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
549
+ var record4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
478
550
  var owns4 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
479
551
  var exact = (pattern, value) => typeof value === "string" && pattern.exec(value)?.[0] === value;
480
552
  var text = (value, max) => typeof value === "string" && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
@@ -497,7 +569,7 @@ function isPrimerTokenizeUrl(url, method = "POST") {
497
569
  return parsed.href === url && parsed.protocol === "https:" && !parsed.port && !parsed.username && !parsed.password && !parsed.search && !parsed.hash && PRIMER_PCI_HOSTS.includes(parsed.hostname) && parsed.pathname === PRIMER_TOKENIZE_PATH;
498
570
  }
499
571
  function judgeBody(body) {
500
- if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT2)
572
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT3)
501
573
  return null;
502
574
  let value;
503
575
  try {
@@ -506,12 +578,12 @@ function judgeBody(body) {
506
578
  catch {
507
579
  return null;
508
580
  }
509
- if (!record3(value) || !roundTrips(value, body))
581
+ if (!record4(value) || !roundTrips(value, body))
510
582
  return null;
511
- if (!Object.keys(value).every((key) => ROOT_KEYS2.includes(key)) || !owns4(value, "paymentInstrument"))
583
+ if (!Object.keys(value).every((key) => ROOT_KEYS3.includes(key)) || !owns4(value, "paymentInstrument"))
512
584
  return null;
513
585
  const instrument = value.paymentInstrument;
514
- if (!record3(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns4(instrument, key)))
586
+ if (!record4(instrument) || !Object.keys(instrument).every((key) => INSTRUMENT_KEYS.includes(key)) || !REQUIRED_INSTRUMENT_KEYS.every((key) => owns4(instrument, key)))
515
587
  return null;
516
588
  if (!digits(instrument.number, 12, 19) || !digits(instrument.cvv, 3, 4) || !exact(MONTH, instrument.expirationMonth) || !exact(YEAR, instrument.expirationYear))
517
589
  return null;
@@ -539,7 +611,7 @@ var CLIENT_TOKEN = /^[A-Za-z0-9._~+/=-]{16,4096}$/;
539
611
  var REQUEST_ID = /^[A-Za-z0-9-]{1,64}$/;
540
612
  var API_VERSION = /^2\.[0-9]{1,2}$/;
541
613
  function readHeaders(headers) {
542
- if (!record3(headers))
614
+ if (!record4(headers))
543
615
  return null;
544
616
  const folded = /* @__PURE__ */ new Map();
545
617
  for (const [name, value] of Object.entries(headers)) {
@@ -586,7 +658,7 @@ function onTokenizePath(url) {
586
658
  }
587
659
  }
588
660
  function primerTokenizeRefusal(request) {
589
- if (!record3(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
661
+ if (!record4(request) || request.method !== "POST" || !isPrimerTokenizeUrl(request.url))
590
662
  return "unsupported";
591
663
  return headersRefusal(request.headers) ?? primerBodyRefusal(request.body);
592
664
  }
@@ -601,7 +673,7 @@ function primerCardOnFileRefusal(url, options, policy = PRIMER_SESSION_CARD_ON_F
601
673
  return "unsupported";
602
674
  if (new URL(url).hostname !== LIVE_PCI_HOST)
603
675
  return null;
604
- return primerCardOnFileDisclosure(url, policy) && record3(options) && options.keepOnFile === true ? null : "keep_on_file";
676
+ return primerCardOnFileDisclosure(url, policy) && record4(options) && options.keepOnFile === true ? null : "keep_on_file";
605
677
  }
606
678
  function primerRefusalMessage(reason) {
607
679
  if (reason === "keep_on_file")
@@ -685,7 +757,7 @@ function readAnswer(body) {
685
757
  return null;
686
758
  try {
687
759
  const value = JSON.parse(body);
688
- return record3(value) ? value : null;
760
+ return record4(value) ? value : null;
689
761
  }
690
762
  catch {
691
763
  return null;
@@ -694,16 +766,16 @@ function readAnswer(body) {
694
766
  var tokenValue = (value) => exact(TOKEN, value) && !panLike(value);
695
767
  var optional = (value, key, check) => !owns4(value, key) || value[key] === null || check(value[key]);
696
768
  function cardDetails(data) {
697
- if (!record3(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
769
+ if (!record4(data) || !exact(/^[0-9]{4}$/, data.last4Digits))
698
770
  return null;
699
- if (!optional(data, "first6Digits", (v) => exact(/^[0-9]{6}$/, v)) || !optional(data, "expirationMonth", (v) => exact(RESPONSE_MONTH, v)) || !optional(data, "expirationYear", (v) => exact(RESPONSE_YEAR, v)) || !optional(data, "cardholderName", (v) => plain(v, 256)) || !optional(data, "network", (v) => exact(NETWORK_NAME, v)) || !optional(data, "isNetworkTokenized", (v) => typeof v === "boolean") || !optional(data, "binData", record3))
771
+ if (!optional(data, "first6Digits", (v) => exact(/^[0-9]{6}$/, v)) || !optional(data, "expirationMonth", (v) => exact(RESPONSE_MONTH, v)) || !optional(data, "expirationYear", (v) => exact(RESPONSE_YEAR, v)) || !optional(data, "cardholderName", (v) => plain(v, 256)) || !optional(data, "network", (v) => exact(NETWORK_NAME, v)) || !optional(data, "isNetworkTokenized", (v) => typeof v === "boolean") || !optional(data, "binData", record4))
700
772
  return null;
701
773
  const safe = { last4Digits: data.last4Digits };
702
774
  for (const key of ["first6Digits", "expirationMonth", "expirationYear", "cardholderName", "network", "isNetworkTokenized"]) {
703
775
  if (owns4(data, key) && data[key] !== null)
704
776
  safe[key] = data[key];
705
777
  }
706
- if (record3(data.binData)) {
778
+ if (record4(data.binData)) {
707
779
  const bin = {};
708
780
  for (const key of BIN_KEYS) {
709
781
  if (!owns4(data.binData, key) || data.binData[key] === null)
@@ -719,7 +791,7 @@ function cardDetails(data) {
719
791
  function threeDsSummary(value) {
720
792
  if (value === void 0 || value === null)
721
793
  return null;
722
- if (!record3(value) || !exact(CODE, value.responseCode) || !optional(value, "reasonCode", (v) => exact(CODE, v)) || !optional(value, "reasonText", (v) => plain(v, 256)) || !optional(value, "protocolVersion", (v) => v === "" || exact(PROTOCOL, v)) || !optional(value, "challengeIssued", (v) => typeof v === "boolean"))
794
+ if (!record4(value) || !exact(CODE, value.responseCode) || !optional(value, "reasonCode", (v) => exact(CODE, v)) || !optional(value, "reasonText", (v) => plain(v, 256)) || !optional(value, "protocolVersion", (v) => v === "" || exact(PROTOCOL, v)) || !optional(value, "challengeIssued", (v) => typeof v === "boolean"))
723
795
  return void 0;
724
796
  const safe = { responseCode: value.responseCode };
725
797
  for (const key of ["reasonCode", "reasonText", "protocolVersion", "challengeIssued"]) {
@@ -747,7 +819,7 @@ function rebuildToken(value) {
747
819
  }
748
820
  function primerErrorId(body) {
749
821
  const value = readAnswer(body);
750
- const id = value && record3(value.error) ? value.error.errorId : null;
822
+ const id = value && record4(value.error) ? value.error.errorId : null;
751
823
  return exact(ERROR_ID, id) && !panLike(id) ? id : null;
752
824
  }
753
825
  function matchesCard(details, card) {
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 57dffa8575e71c819c694d818e272c955303add0ed4b083d69bab1606cd6150f
3
+ // artifact-sha256: dd47d4d2d97c39acb2385272d1a2bf3e647741839234283b848b67fd5ce5c114
4
4
  // src/deep-freeze.js
5
5
  function deepFreeze(value) {
6
6
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -211,7 +211,18 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
211
211
  // The result page Tranzila's card form names when the terminal set no
212
212
  // return URL of its own (read on both hosts on 2026-10-10): the result
213
213
  // goes back to Tranzila, which already holds it. Matched exactly.
214
- processorResultUrls: ["https://direct.tranzila.com/done/", "https://directng.tranzila.com/process/fail"]
214
+ processorResultUrls: ["https://direct.tranzila.com/done/", "https://directng.tranzila.com/process/fail"],
215
+ // Tranzila's own ALTCHA proof of work, on every card form read on both
216
+ // hosts on 2026-10-10 (altcha 1.0.7, SHA-256, searched up to 50,000):
217
+ // Tranzila's script hashes the page's challenge until it finds the
218
+ // number, writes base64 JSON {algorithm, challenge, number, salt,
219
+ // signature, took} into `altcha`, and only then submits the form.
220
+ // Nobody answers anything and the proof names no browser, cookie or
221
+ // address, so it rides the device's one send as the page wrote it, once
222
+ // the create finds it is one solved proof (backend.js isAltchaProof).
223
+ // Every other challenge field is still refused (hosted_form_gated).
224
+ // Removing this line refuses `altcha` again.
225
+ proofOfWork: "altcha"
215
226
  }
216
227
  },
217
228
  {
@@ -1407,6 +1418,33 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
1407
1418
  // A checked "remember" asks Worldline to keep the card for later purchases.
1408
1419
  token: "remember"
1409
1420
  }
1421
+ },
1422
+ {
1423
+ psp: "paddle_vgs",
1424
+ // Paddle's checkout frame (buy.paddle.com) on a checkout whose card method names VGS as its
1425
+ // tokenizer: one JSON:API POST to /cards on vault.paddle.com, a VGS inbound proxy Paddle runs
1426
+ // (sandbox-vault.paddle.com in test), with the checkout's own bearer JWT. PAN@
1427
+ // data.attributes.pan, with cvc, exp_month (1 to 12) and exp_year (two digits) beside it, all
1428
+ // numbers but the pan and the cvc. paddle-vgs.js holds the request rule the pause decision, the
1429
+ // API's create, the prepared bind and the device all read: exactly one of the two URLs, the
1430
+ // frame's Content-Type and bearer, and the frame's body byte for byte. VGS answers with the
1431
+ // stored card's id, which the frame sends on to Paddle's /pay. The frame aborts this request
1432
+ // after 10 seconds and sends it once more, so the lane is prepared only (preparationRequired):
1433
+ // the cardholder approves before the agent clicks Pay. The card leaves the cardholder's device
1434
+ // by a direct fetch, since the proxy answers any origin. The checkouts that name TokenEx or
1435
+ // Spreedly send the card elsewhere and are not this entry.
1436
+ // Ships dark: served only to a client that declares `paddle_vgs_cards`, gated off for every org
1437
+ // until an operator lists one (`paddle_vgs:<org>`), and autopilot never sends this request
1438
+ // (autopilot.js DEVICE_REPLAY_REFUSED_OPERATIONS). The environment is pinned (S7):
1439
+ // sandbox-vault only on test rows and only with documented test cards, vault only on live rows.
1440
+ capability: "paddle_vgs_cards",
1441
+ match: "(?:sandbox-)?vault\\.paddle\\.com/cards",
1442
+ hosts: ["^vault\\.paddle\\.com$", "^sandbox-vault\\.paddle\\.com$"],
1443
+ sandboxHosts: ["^sandbox-vault\\.paddle\\.com$"],
1444
+ hostEnvironmentPinned: true,
1445
+ preparationRequired: true,
1446
+ encoding: "json",
1447
+ passthroughHeaders: ["^authorization$"]
1410
1448
  }
1411
1449
  ]);
1412
1450
  var PSP_RECOGNIZERS = deepFreeze(PROCESSOR_DEFINITIONS.map(({ compose, encryptedCardFields, deviceSteps, deviceEncrypt, ...definition }) => definition));
@@ -1509,6 +1547,15 @@ var pin = (fields) => Object.freeze({ ...fields, origins: Object.freeze([...fiel
1509
1547
  var MONERIS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
1510
1548
  pin({ id: "jack_org_raisin", merchant: "Jack.org", origins: ["https://jack.akaraisin.com"], profileIds: ["htL7TR9H7ICYN2B"] })
1511
1549
  ]);
1550
+ // src/paddle-vgs.js
1551
+ var pin2 = (fields) => Object.freeze({
1552
+ ...fields,
1553
+ origins: Object.freeze([...fields.origins]),
1554
+ sellerClaim: fields.sellerClaim ? Object.freeze({ ...fields.sellerClaim }) : null
1555
+ });
1556
+ var PADDLE_VGS_MERCHANT_PINS = /* @__PURE__ */ Object.freeze([
1557
+ pin2({ id: "rectangle_pro", merchant: "Rectangle Pro", origins: ["https://rectangleapp.com"], sellerClaim: null })
1558
+ ]);
1512
1559
  // src/cybersource-secure-acceptance.js
1513
1560
  var SA_BODY_LIMIT = 32 * 1024;
1514
1561
  var SA_REPLY_LIMIT = 64 * 1024;
@@ -1596,6 +1643,7 @@ var ALLOWED_MEDIA_TYPES = {
1596
1643
  jwe: ["application/jwt"]
1597
1644
  };
1598
1645
  var JWE_CONTENT_TYPE = "application/jwt; charset=utf-8";
1646
+ var PADDLE_VGS_MEDIA_TYPE = "application/vnd.api+json";
1599
1647
  function contentTypeFor(pspOrEntry, headers) {
1600
1648
  const rec = trustedClientRecognizer(pspOrEntry);
1601
1649
  if (rec.encoding === "jwe") {
@@ -1605,6 +1653,13 @@ function contentTypeFor(pspOrEntry, headers) {
1605
1653
  throw new Error("Refusing to pay: this processor takes its sealed card only as application/jwt.");
1606
1654
  return JWE_CONTENT_TYPE;
1607
1655
  }
1656
+ if (rec.psp === "paddle_vgs") {
1657
+ const captured = Object.entries(headers || {}).find(([k]) => /^content-type$/i.test(k));
1658
+ if (!captured || typeof captured[1] !== "string" || captured[1].trim().toLowerCase() !== PADDLE_VGS_MEDIA_TYPE) {
1659
+ throw new Error("Refusing to pay: this processor takes its card only as application/vnd.api+json.");
1660
+ }
1661
+ return PADDLE_VGS_MEDIA_TYPE;
1662
+ }
1608
1663
  const encoding = rec && rec.encoding === "form" ? "form" : "json";
1609
1664
  const allowed = ALLOWED_MEDIA_TYPES[encoding];
1610
1665
  const fallback = allowed[0];
@@ -1624,6 +1679,8 @@ function replayHeaders(pspOrEntry, captured = {}) {
1624
1679
  const headers = { "content-type": contentTypeFor(rec, captured) };
1625
1680
  if (rec.psp === "creditguard")
1626
1681
  headers.accept = "application/json";
1682
+ if (rec.psp === "paddle_vgs")
1683
+ headers.accept = PADDLE_VGS_MEDIA_TYPE;
1627
1684
  for (const [name, value] of Object.entries(captured)) {
1628
1685
  if (rec.headers.some((pattern) => pattern.test(name)) && !HEADER_NEVER_FORWARD.test(name))
1629
1686
  headers[name] = value;
@@ -0,0 +1,27 @@
1
+ /** Whether `url` is one of MyCheckout's four card-form endpoints, written exactly (payment-core's rule). */
2
+ export declare function isWorldlineMyCheckoutEndpoint(url: string): boolean;
3
+ /** The `blocked` detail for a card submit whose product is not the one its page chose. */
4
+ export declare const WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED = "worldline_mycheckout_product_changed";
5
+ /** The `blocked` detail for a card submit that names no product, on a hosted checkout whose page chose one. */
6
+ export declare const WORLDLINE_MYCHECKOUT_PRODUCT_MISSING = "worldline_mycheckout_product_missing";
7
+ export type WorldlineMyCheckoutRefusal = typeof WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED | typeof WORLDLINE_MYCHECKOUT_PRODUCT_MISSING;
8
+ /**
9
+ * The card products one attachment saw chosen, per hosted checkout: its `publicMerchantId` and
10
+ * `token`, the pair payment-core's duplicate guard reads as one hosted checkout
11
+ * (WORLDLINE_MYCHECKOUT_DEDUPE).
12
+ */
13
+ export declare class WorldlineMyCheckoutProducts {
14
+ private readonly chosen;
15
+ /**
16
+ * Read one POST to a card-form endpoint. A product selection (no card, and neither the Pay nor
17
+ * the cancel pair) records the product it chose for its hosted checkout, or forgets the choice
18
+ * when it picks the card group, which leaves the product to the card. A card submit is judged:
19
+ * WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED when its paymentProductId is another product than the one
20
+ * chosen for its hosted checkout, WORLDLINE_MYCHECKOUT_PRODUCT_MISSING when it names none. Null for
21
+ * every other post, and for a card submit on the card group or on a hosted checkout whose choice
22
+ * this attachment never saw: the API and the device judge those as before. A body payment-core
23
+ * cannot read is never judged here either.
24
+ */
25
+ judge(url: string, body: string | null): WorldlineMyCheckoutRefusal | null;
26
+ private remember;
27
+ }
@@ -0,0 +1,29 @@
1
+ declare var WORLDLINE_MYCHECKOUT_PSP: string;
2
+ declare var WORLDLINE_MYCHECKOUT_CAPABILITY: string;
3
+ declare var WORLDLINE_MYCHECKOUT_ENDPOINTS: Readonly<{
4
+ production: readonly string[];
5
+ sandbox: readonly string[];
6
+ }>;
7
+ declare function worldlineMyCheckoutEnvironment(url: any): "production" | "sandbox" | undefined;
8
+ declare function isWorldlineMyCheckoutUrl(url: any): boolean;
9
+ declare var WORLDLINE_MYCHECKOUT_CARD_PRODUCTS: Readonly<{
10
+ visa: readonly string[];
11
+ mastercard: readonly string[];
12
+ amex: readonly string[];
13
+ discover: readonly string[];
14
+ jcb: readonly string[];
15
+ diners: readonly string[];
16
+ }>;
17
+ declare function worldlineMyCheckoutProductNetwork(productId: any): string | null;
18
+ declare function parseWorldlineMyCheckoutForm(body: any): {
19
+ refusal: string;
20
+ pairs?: undefined;
21
+ } | {
22
+ pairs: string[][];
23
+ refusal?: undefined;
24
+ };
25
+ declare function worldlineMyCheckoutCardholderName(name: any): string | null;
26
+ declare function worldlineMyCheckoutTemplateRefusal(body: any): string | null;
27
+ declare function worldlineMyCheckoutRequestRefusal(request: any): string | null;
28
+ declare var WORLDLINE_MYCHECKOUT_DEDUPE: readonly string[];
29
+ export { WORLDLINE_MYCHECKOUT_CAPABILITY, WORLDLINE_MYCHECKOUT_CARD_PRODUCTS, WORLDLINE_MYCHECKOUT_DEDUPE, WORLDLINE_MYCHECKOUT_ENDPOINTS, WORLDLINE_MYCHECKOUT_PSP, isWorldlineMyCheckoutUrl, parseWorldlineMyCheckoutForm, worldlineMyCheckoutCardholderName, worldlineMyCheckoutEnvironment, worldlineMyCheckoutProductNetwork, worldlineMyCheckoutRequestRefusal, worldlineMyCheckoutTemplateRefusal };
@@ -0,0 +1,162 @@
1
+ // @ts-nocheck
2
+ // Generated from @agent-cards/payment-core. Do not edit.
3
+ // artifact-sha256: 486a32aa0f96f59666d4a43e42fee9f493cd3059b27ad3f92b97ad6b249133f5
4
+ // src/worldline-mycheckout.js
5
+ var WORLDLINE_MYCHECKOUT_PSP = "worldline_mycheckout";
6
+ var WORLDLINE_MYCHECKOUT_CAPABILITY = "worldline_mycheckout_form";
7
+ var WORLDLINE_MYCHECKOUT_ENDPOINTS = /* @__PURE__ */ Object.freeze({
8
+ production: /* @__PURE__ */ Object.freeze([
9
+ "https://payment.pay1.checkout.worldline-solutions.com/checkout",
10
+ "https://payment.pay2.checkout.worldline-solutions.com/checkout"
11
+ ]),
12
+ sandbox: /* @__PURE__ */ Object.freeze([
13
+ "https://payment.pay1.preprod.checkout.worldline-solutions.com/checkout",
14
+ "https://payment.pay2.preprod.checkout.worldline-solutions.com/checkout"
15
+ ])
16
+ });
17
+ function worldlineMyCheckoutEnvironment(url) {
18
+ if (WORLDLINE_MYCHECKOUT_ENDPOINTS.production.includes(url))
19
+ return "production";
20
+ if (WORLDLINE_MYCHECKOUT_ENDPOINTS.sandbox.includes(url))
21
+ return "sandbox";
22
+ return void 0;
23
+ }
24
+ function isWorldlineMyCheckoutUrl(url) {
25
+ return worldlineMyCheckoutEnvironment(url) !== void 0;
26
+ }
27
+ var WORLDLINE_MYCHECKOUT_CARD_PRODUCTS = /* @__PURE__ */ Object.freeze({
28
+ visa: /* @__PURE__ */ Object.freeze(["1", "114", "122"]),
29
+ mastercard: /* @__PURE__ */ Object.freeze(["3", "119"]),
30
+ amex: /* @__PURE__ */ Object.freeze(["2"]),
31
+ discover: /* @__PURE__ */ Object.freeze(["128"]),
32
+ jcb: /* @__PURE__ */ Object.freeze(["125"]),
33
+ diners: /* @__PURE__ */ Object.freeze(["132"])
34
+ });
35
+ function worldlineMyCheckoutProductNetwork(productId) {
36
+ for (const [network, ids] of Object.entries(WORLDLINE_MYCHECKOUT_CARD_PRODUCTS)) {
37
+ if (ids.includes(productId))
38
+ return network;
39
+ }
40
+ return null;
41
+ }
42
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
43
+ var FIELDS = {
44
+ publicMerchantId: /^[1-9]\d{0,9}$/,
45
+ variantCode: /^\d{1,10}$/,
46
+ locale: /^[a-z]{2}_[A-Z]{2}$/,
47
+ idempotenceKey: UUID,
48
+ // <merchant id>-<32 hex>:<hosted checkout id>:<32 hex>, the hosted checkout's own id.
49
+ token: /^[1-9]\d{0,9}-[0-9a-f]{32}:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}:[0-9a-f]{32}$/,
50
+ requestToken: UUID,
51
+ isPaymentProductDetailsShown: /^(?:true|false)$/,
52
+ isPaymentProductFiscalNumberShown: /^(?:true|false)$/,
53
+ jsEnabled: /^true$/,
54
+ isFramed: /^(?:true|false)$/,
55
+ paymentProductId: /^\d{1,4}$/,
56
+ paymentProductGroupId: /^cards$/,
57
+ colorDepth: /^\d{0,3}$/,
58
+ screenHeight: /^\d{0,5}$/,
59
+ screenWidth: /^\d{0,5}$/,
60
+ innerHeight: /^\d{0,5}$/,
61
+ innerWidth: /^\d{0,5}$/,
62
+ timezoneOffsetUtcMinutes: /^-?\d{0,4}$/,
63
+ browserLocale: /^[A-Za-z0-9-]{0,35}$/,
64
+ javaEnabled: /^(?:true|false)?$/,
65
+ cardNumber: /^[0-9 ]{12,23}$/,
66
+ // MM/YY as the page's mask writes it; the delimiter may be absent or padded.
67
+ expiryDate: /^(?:0[1-9]|1[0-2])(?: ?\/ ?)?\d{2}$/,
68
+ cvv: /^\d{3,4}$/,
69
+ cardholderName: /^[^\u0000-\u001f\u007f<>]{0,100}$/,
70
+ // The autofill helpers the page folds into expiryDate and then empties.
71
+ expiryDateMonth: /^$/,
72
+ expiryDateYear: /^$/,
73
+ // A checked "remember" asks Worldline to keep the card for later purchases.
74
+ remember: /^$/,
75
+ // Present, empty, on a page that offers Click to Pay; a value is a Click to Pay payment.
76
+ clickToPayCheckoutResponseSignature: /^$/,
77
+ // The Pay button's own pair. The click handler writes it into #buttonValue and appends one
78
+ // more copy per click, so it may repeat, always as doPayment=doPayment.
79
+ doPayment: /^doPayment$/
80
+ };
81
+ var REPEATABLE = /* @__PURE__ */ new Set(["doPayment"]);
82
+ var REQUIRED = ["publicMerchantId", "token", "requestToken", "idempotenceKey", "doPayment", "cardNumber", "expiryDate", "cvv"];
83
+ var NOT_A_CARD_SALE = /* @__PURE__ */ new Set(["cancelPayment", "encryptedPaymentData", "mobileCardholderName"]);
84
+ var BODY_LIMIT = 16384;
85
+ var VALUE_LIMIT = 2048;
86
+ function parseWorldlineMyCheckoutForm(body) {
87
+ if (typeof body !== "string" || body === "" || body.length > BODY_LIMIT || /[^\x21-\x7e]/.test(body))
88
+ return { refusal: "body" };
89
+ const pairs = [];
90
+ const seen = /* @__PURE__ */ new Set();
91
+ for (const chunk of body.split("&")) {
92
+ const eq = chunk.indexOf("=");
93
+ if (eq < 1)
94
+ return { refusal: "body" };
95
+ let key;
96
+ let value;
97
+ try {
98
+ key = decodeURIComponent(chunk.slice(0, eq).replace(/\+/g, " "));
99
+ value = decodeURIComponent(chunk.slice(eq + 1).replace(/\+/g, " "));
100
+ }
101
+ catch {
102
+ return { refusal: "body" };
103
+ }
104
+ if (value.length > VALUE_LIMIT || /[\u0000-\u001f\u007f]/.test(value))
105
+ return { refusal: "body" };
106
+ if (seen.has(key) && !REPEATABLE.has(key))
107
+ return { refusal: "body" };
108
+ seen.add(key);
109
+ pairs.push([key, value]);
110
+ }
111
+ return { pairs };
112
+ }
113
+ var SESSION_FIELDS = /* @__PURE__ */ new Set(["publicMerchantId", "token", "requestToken", "idempotenceKey"]);
114
+ var CARD_FIELDS = /* @__PURE__ */ new Set(["cardNumber", "expiryDate", "cvv", "cardholderName", "expiryDateMonth", "expiryDateYear"]);
115
+ function worldlineMyCheckoutCardholderName(name) {
116
+ if (typeof name !== "string")
117
+ return null;
118
+ const trimmed = name.trim();
119
+ return trimmed !== "" && FIELDS.cardholderName.test(trimmed) ? trimmed : null;
120
+ }
121
+ function worldlineMyCheckoutTemplateRefusal(body) {
122
+ const parsed = parseWorldlineMyCheckoutForm(body);
123
+ if (parsed.refusal)
124
+ return parsed.refusal;
125
+ const values = /* @__PURE__ */ new Map();
126
+ for (const [key, value] of parsed.pairs) {
127
+ if (NOT_A_CARD_SALE.has(key) || key === "clickToPayCheckoutResponseSignature" && value !== "")
128
+ return "not_a_card_sale";
129
+ if (key === "remember" && value !== "")
130
+ return "stored_card";
131
+ if (!Object.hasOwn(FIELDS, key))
132
+ return "unknown_field";
133
+ values.set(key, value);
134
+ }
135
+ for (const key of REQUIRED)
136
+ if (!values.has(key))
137
+ return "missing_field";
138
+ for (const [key, value] of parsed.pairs) {
139
+ if (FIELDS[key].test(value))
140
+ continue;
141
+ return SESSION_FIELDS.has(key) ? "session" : CARD_FIELDS.has(key) ? "card_fields" : "field_value";
142
+ }
143
+ if (values.get("token").split("-")[0] !== values.get("publicMerchantId"))
144
+ return "session";
145
+ const product = values.get("paymentProductId");
146
+ if (product === void 0 ? !values.has("paymentProductGroupId") : worldlineMyCheckoutProductNetwork(product) === null)
147
+ return "product";
148
+ return null;
149
+ }
150
+ function worldlineMyCheckoutRequestRefusal(request) {
151
+ if (!request || typeof request !== "object" || !isWorldlineMyCheckoutUrl(request.url))
152
+ return "endpoint";
153
+ if (request.method !== "POST")
154
+ return "method";
155
+ const headers = request.headers && typeof request.headers === "object" ? request.headers : {};
156
+ const types = Object.entries(headers).filter(([name]) => name.toLowerCase() === "content-type");
157
+ if (types.length !== 1 || typeof types[0][1] !== "string" || !/^application\/x-www-form-urlencoded(?:;\s*charset=utf-8)?$/i.test(types[0][1].trim()))
158
+ return "content_type";
159
+ return worldlineMyCheckoutTemplateRefusal(request.body);
160
+ }
161
+ var WORLDLINE_MYCHECKOUT_DEDUPE = /* @__PURE__ */ Object.freeze(["publicMerchantId", "token"]);
162
+ export { WORLDLINE_MYCHECKOUT_CAPABILITY, WORLDLINE_MYCHECKOUT_CARD_PRODUCTS, WORLDLINE_MYCHECKOUT_DEDUPE, WORLDLINE_MYCHECKOUT_ENDPOINTS, WORLDLINE_MYCHECKOUT_PSP, isWorldlineMyCheckoutUrl, parseWorldlineMyCheckoutForm, worldlineMyCheckoutCardholderName, worldlineMyCheckoutEnvironment, worldlineMyCheckoutProductNetwork, worldlineMyCheckoutRequestRefusal, worldlineMyCheckoutTemplateRefusal };
@@ -0,0 +1,93 @@
1
+ // Worldline Connect MyCheckout (psp `worldline_mycheckout`): the hosted payment page whose card
2
+ // form posts the card as a top-level navigation to /checkout on Worldline's own host. The form
3
+ // rules are payment-core's (worldline-mycheckout.generated.ts): the API applies them at create,
4
+ // and the cardholder's device applies them again before it rebuilds the form with the real card.
5
+ // This module holds the one thing only the SDK can decide: which card product the page chose.
6
+ //
7
+ // A MyCheckout page that offers several card products asks the shopper to choose one first (its
8
+ // select-payment-product page: Visa 1, Mastercard 3, Visa Electron 122, and so on). That choice
9
+ // posts to the same /checkout URL with no card, and continues untouched. The card form it opens
10
+ // then looks the typed number's IIN up with Worldline and, when the answer names another product
11
+ // the page offers, switches its own paymentProductId to that product. With the agent's placeholder
12
+ // typed, the answer describes the placeholder, never the card that pays: on 2026-10-12 CLZ's page
13
+ // read the SDK's Visa placeholder (411111) as Visa Electron (122) and switched the form from the
14
+ // Visa (1) the agent chose. The cardholder's device checks only that the product belongs to the
15
+ // real card's network (Visa takes 1, 114 and 122), so it would have submitted a Visa credit card
16
+ // as Visa Electron. The device never sees the choice and the API stores only the card form; this
17
+ // attachment sees both posts, so the SDK holds the card submit to the product chosen for its
18
+ // hosted checkout.
19
+ //
20
+ // The processor ships dark: the API serves the recognizer only to a build that declares
21
+ // `worldline_mycheckout_form`, for an org Agentcard turned Worldline on for. Without it no
22
+ // MyCheckout post pauses, and none reaches this module.
23
+ import { isWorldlineMyCheckoutUrl, parseWorldlineMyCheckoutForm } from './worldline-mycheckout.generated.js';
24
+ /** Whether `url` is one of MyCheckout's four card-form endpoints, written exactly (payment-core's rule). */
25
+ export function isWorldlineMyCheckoutEndpoint(url) {
26
+ return isWorldlineMyCheckoutUrl(url) === true;
27
+ }
28
+ /** The `blocked` detail for a card submit whose product is not the one its page chose. */
29
+ export const WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED = 'worldline_mycheckout_product_changed';
30
+ /** The `blocked` detail for a card submit that names no product, on a hosted checkout whose page chose one. */
31
+ export const WORLDLINE_MYCHECKOUT_PRODUCT_MISSING = 'worldline_mycheckout_product_missing';
32
+ /** How many hosted checkouts one attachment remembers a choice for; the oldest is forgotten first. */
33
+ const REMEMBERED_CHECKOUTS = 32;
34
+ const PRODUCT_ID = /^\d{1,4}$/;
35
+ /**
36
+ * The card products one attachment saw chosen, per hosted checkout: its `publicMerchantId` and
37
+ * `token`, the pair payment-core's duplicate guard reads as one hosted checkout
38
+ * (WORLDLINE_MYCHECKOUT_DEDUPE).
39
+ */
40
+ export class WorldlineMyCheckoutProducts {
41
+ chosen = new Map();
42
+ /**
43
+ * Read one POST to a card-form endpoint. A product selection (no card, and neither the Pay nor
44
+ * the cancel pair) records the product it chose for its hosted checkout, or forgets the choice
45
+ * when it picks the card group, which leaves the product to the card. A card submit is judged:
46
+ * WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED when its paymentProductId is another product than the one
47
+ * chosen for its hosted checkout, WORLDLINE_MYCHECKOUT_PRODUCT_MISSING when it names none. Null for
48
+ * every other post, and for a card submit on the card group or on a hosted checkout whose choice
49
+ * this attachment never saw: the API and the device judge those as before. A body payment-core
50
+ * cannot read is never judged here either.
51
+ */
52
+ judge(url, body) {
53
+ if (typeof body !== 'string' || !isWorldlineMyCheckoutUrl(url))
54
+ return null;
55
+ const parsed = parseWorldlineMyCheckoutForm(body);
56
+ if (!parsed.pairs)
57
+ return null;
58
+ const values = new Map();
59
+ for (const [key, value] of parsed.pairs)
60
+ if (!values.has(key))
61
+ values.set(key, value);
62
+ const merchant = values.get('publicMerchantId');
63
+ const token = values.get('token');
64
+ if (!merchant || !token)
65
+ return null;
66
+ const checkout = JSON.stringify([merchant, token]);
67
+ const product = values.get('paymentProductId');
68
+ // A card is a non-empty cardNumber, as the recognizer's card fields read it (card-fields.js).
69
+ if (!values.get('cardNumber')) {
70
+ if (values.has('doPayment') || values.has('cancelPayment'))
71
+ return null;
72
+ if (product !== undefined && PRODUCT_ID.test(product))
73
+ this.remember(checkout, product);
74
+ else if (values.get('paymentProductGroupId') === 'cards')
75
+ this.chosen.delete(checkout);
76
+ return null;
77
+ }
78
+ if (values.has('paymentProductGroupId'))
79
+ return null;
80
+ const chosen = this.chosen.get(checkout);
81
+ if (chosen === undefined)
82
+ return null;
83
+ if (!product)
84
+ return WORLDLINE_MYCHECKOUT_PRODUCT_MISSING;
85
+ return product === chosen ? null : WORLDLINE_MYCHECKOUT_PRODUCT_CHANGED;
86
+ }
87
+ remember(checkout, product) {
88
+ this.chosen.delete(checkout);
89
+ this.chosen.set(checkout, product);
90
+ while (this.chosen.size > REMEMBERED_CHECKOUTS)
91
+ this.chosen.delete(this.chosen.keys().next().value);
92
+ }
93
+ }