@agent-cards/sdk 0.21.0 → 0.22.0

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.
Files changed (103) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/PREFLIGHT.md +30 -0
  3. package/README.md +1070 -21
  4. package/dist/aci.d.ts +173 -0
  5. package/dist/aci.generated.d.ts +87 -0
  6. package/dist/aci.generated.js +306 -0
  7. package/dist/aci.js +371 -0
  8. package/dist/adyen-merchant-hosted.generated.d.ts +3 -3
  9. package/dist/adyen-merchant-hosted.generated.js +283 -49
  10. package/dist/agentcard.d.ts +9 -2
  11. package/dist/agentcard.js +4 -2
  12. package/dist/amadeus-checkout.generated.d.ts +57 -0
  13. package/dist/amadeus-checkout.generated.js +549 -0
  14. package/dist/amadeus.d.ts +41 -0
  15. package/dist/amadeus.js +81 -0
  16. package/dist/builtin-registry.generated.js +1 -1
  17. package/dist/card-endpoint.d.ts +177 -0
  18. package/dist/card-endpoint.js +286 -0
  19. package/dist/card-fields.generated.d.ts +5 -3
  20. package/dist/card-fields.generated.js +284 -7
  21. package/dist/cdp.d.ts +5 -1
  22. package/dist/cdp.js +841 -135
  23. package/dist/client.d.ts +403 -19
  24. package/dist/client.js +949 -98
  25. package/dist/creditguard.generated.d.ts +79 -0
  26. package/dist/creditguard.generated.js +662 -0
  27. package/dist/cse-body.js +11 -1
  28. package/dist/datatrans.d.ts +96 -0
  29. package/dist/datatrans.generated.d.ts +64 -0
  30. package/dist/datatrans.generated.js +426 -0
  31. package/dist/datatrans.js +178 -0
  32. package/dist/elavon-converge.d.ts +37 -0
  33. package/dist/elavon-converge.generated.d.ts +49 -0
  34. package/dist/elavon-converge.generated.js +439 -0
  35. package/dist/elavon-converge.js +84 -0
  36. package/dist/eprotect.d.ts +152 -0
  37. package/dist/eprotect.generated.d.ts +77 -0
  38. package/dist/eprotect.generated.js +498 -0
  39. package/dist/eprotect.js +222 -0
  40. package/dist/hosted-form.d.ts +3 -2
  41. package/dist/hosted-form.js +3 -2
  42. package/dist/index.d.ts +17 -3
  43. package/dist/index.js +6 -1
  44. package/dist/juspay-gate.d.ts +82 -0
  45. package/dist/juspay-gate.js +126 -0
  46. package/dist/juspay.generated.d.ts +89 -0
  47. package/dist/juspay.generated.js +705 -0
  48. package/dist/lifecycle.d.ts +57 -6
  49. package/dist/lifecycle.js +119 -8
  50. package/dist/merchant-card-endpoints.generated.d.ts +63 -0
  51. package/dist/merchant-card-endpoints.generated.js +1103 -0
  52. package/dist/merchant-hosted.d.ts +4 -1
  53. package/dist/merchant-hosted.js +5 -2
  54. package/dist/merchant-session-probe.d.ts +48 -0
  55. package/dist/merchant-session-probe.js +53 -0
  56. package/dist/merchant-session.generated.d.ts +69 -0
  57. package/dist/merchant-session.generated.js +919 -0
  58. package/dist/payrails.d.ts +133 -0
  59. package/dist/payrails.generated.d.ts +73 -0
  60. package/dist/payrails.generated.js +596 -0
  61. package/dist/payrails.js +305 -0
  62. package/dist/pelecard.d.ts +32 -0
  63. package/dist/pelecard.generated.d.ts +32 -0
  64. package/dist/pelecard.generated.js +283 -0
  65. package/dist/pelecard.js +42 -0
  66. package/dist/pre-claim.d.ts +72 -2
  67. package/dist/pre-claim.js +857 -5
  68. package/dist/preflight-catalog.json +2218 -287
  69. package/dist/preflight-schemas.json +179 -6
  70. package/dist/preflight.generated.d.ts +1 -1
  71. package/dist/preflight.generated.js +233 -4
  72. package/dist/preparation.d.ts +5 -2
  73. package/dist/preparation.js +36 -10
  74. package/dist/prepared-processor.d.ts +84 -5
  75. package/dist/prepared-processor.js +147 -6
  76. package/dist/primer.d.ts +53 -0
  77. package/dist/primer.generated.d.ts +38 -0
  78. package/dist/primer.generated.js +624 -0
  79. package/dist/primer.js +82 -0
  80. package/dist/registry.d.ts +100 -8
  81. package/dist/registry.js +85 -11
  82. package/dist/shiji-sts.d.ts +144 -0
  83. package/dist/shiji-sts.generated.d.ts +72 -0
  84. package/dist/shiji-sts.generated.js +913 -0
  85. package/dist/shiji-sts.js +222 -0
  86. package/dist/stripe-checkout.generated.d.ts +1 -1
  87. package/dist/stripe-checkout.generated.js +699 -9
  88. package/dist/stripe-setup.d.ts +16 -0
  89. package/dist/stripe-setup.js +61 -0
  90. package/dist/substitutions.generated.js +187 -29
  91. package/dist/verifone.d.ts +68 -0
  92. package/dist/verifone.generated.d.ts +51 -0
  93. package/dist/verifone.generated.js +1401 -0
  94. package/dist/verifone.js +119 -0
  95. package/dist/voice-types.d.ts +27 -0
  96. package/dist/voice.d.ts +6 -1
  97. package/dist/voice.js +6 -1
  98. package/dist/wspay.d.ts +41 -0
  99. package/dist/wspay.generated.d.ts +41 -0
  100. package/dist/wspay.generated.js +370 -0
  101. package/dist/wspay.js +65 -0
  102. package/examples/preflight/kernel-native/inventory.json +1 -1
  103. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 582753ad6e0ff77d4f5c10416b259928d1e00ea236a2f9d55af5865ef9c08243
3
+ // artifact-sha256: 83b01fa41dce013a97e4daf34de13d53978f6ef972e0328c67b3380f057b4e17
4
4
  // src/deep-freeze.js
5
5
  function deepFreeze(value) {
6
6
  if (value && typeof value === "object" && !Object.isFrozen(value)) {
@@ -48,6 +48,28 @@ function pathsOverlap(a, b) {
48
48
  return false;
49
49
  return true;
50
50
  }
51
+ function sameKeyIgnoringCase(key, step) {
52
+ return !FORBIDDEN_KEYS.has(key) && (key.toLowerCase() === step.toLowerCase() || key.toUpperCase() === step.toUpperCase());
53
+ }
54
+ function valuesIgnoringCase(root, path) {
55
+ let nodes = [root];
56
+ for (const step of path) {
57
+ const next = [];
58
+ for (const node of nodes) {
59
+ if (typeof step === "number") {
60
+ if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
61
+ next.push(node[step]);
62
+ }
63
+ else if (isPlainObject(node)) {
64
+ for (const key of Object.keys(node))
65
+ if (sameKeyIgnoringCase(key, step))
66
+ next.push(node[key]);
67
+ }
68
+ }
69
+ nodes = next;
70
+ }
71
+ return nodes;
72
+ }
51
73
  function describePath(path) {
52
74
  return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : `.${step}`).join("").replace(/^\./, "") : "(root)";
53
75
  }
@@ -713,6 +735,55 @@ function adyenAmount(value) {
713
735
  const currency = typeof value.currency === "string" && /^[A-Za-z]{3}$/.test(value.currency) ? value.currency.toUpperCase() : null;
714
736
  return minor === null || !currency ? null : { minor, currency };
715
737
  }
738
+ var BODY_AMOUNT_MEMBERS = ["value", "currency", "format", "unit"];
739
+ function merchantBodyAmountRefusal(profile) {
740
+ if (!isPlainObject(profile))
741
+ return "the profile is not an object";
742
+ const amount = profile.amount;
743
+ if (amount === null)
744
+ return null;
745
+ if (Array.isArray(amount))
746
+ return pathRefusal(amount, "the body amount");
747
+ const members = membersRefusal(amount, BODY_AMOUNT_MEMBERS, BODY_AMOUNT_MEMBERS, "the body amount");
748
+ if (members)
749
+ return members;
750
+ const paths = pathRefusal(amount.value, "the body amount's value") ?? pathRefusal(amount.currency, "the body amount's currency");
751
+ if (paths)
752
+ return paths;
753
+ if (pathsOverlap(amount.value, amount.currency))
754
+ return "the body amount's value and currency are read at the same place";
755
+ if (!FORMATS.includes(amount.format))
756
+ return `the body amount's format is not one of ${FORMATS.join(", ")}`;
757
+ if (!["major", "minor"].includes(amount.unit))
758
+ return "the body amount's unit is not major or minor";
759
+ if (amount.format !== "number" && amount.unit !== "major")
760
+ return "a decimal body amount is in major units";
761
+ return null;
762
+ }
763
+ function readMerchantBodyAmount(profile, body) {
764
+ const refusal = merchantBodyAmountRefusal(profile);
765
+ if (refusal)
766
+ throw new Error(`Refusing to pay: ${profile?.merchant ?? "this merchant"}'s body amount is not a reviewed declaration: ${refusal}.`);
767
+ const amount = profile.amount;
768
+ if (amount === null)
769
+ return { kind: "unnamed" };
770
+ if (Array.isArray(amount)) {
771
+ const at = jsonAt(body, amount);
772
+ if (!at.found)
773
+ return { kind: "absent" };
774
+ const named = adyenAmount(at.value);
775
+ return named ? { kind: "amount", amountCents: named.minor, currency: named.currency } : { kind: "unreadable" };
776
+ }
777
+ const value = jsonAt(body, amount.value);
778
+ if (!value.found)
779
+ return { kind: "absent" };
780
+ const currency = jsonAt(body, amount.currency);
781
+ if (!currency.found || typeof currency.value !== "string" || !/^[A-Za-z]{3}$/.test(currency.value))
782
+ return { kind: "unreadable" };
783
+ const code = currency.value.toUpperCase();
784
+ const minor = totalMinor(value.value, amount.format, amount.unit, code);
785
+ return minor === null ? { kind: "unreadable" } : { kind: "amount", amountCents: minor, currency: code };
786
+ }
716
787
  function readMerchantAmount(profile, input) {
717
788
  const stage = isPlainObject(profile) ? profile.amountSource : void 0;
718
789
  if (!isPlainObject(stage))
@@ -776,18 +847,15 @@ function readMerchantAmount(profile, input) {
776
847
  return fail("stale", `the card request was built from another ${merchant} response than the latest one (${describePath(rule.card)})`);
777
848
  }
778
849
  }
779
- let bodyAmount = "none";
780
- if (Array.isArray(profile.amount)) {
781
- const at = jsonAt(ctx.body, profile.amount);
782
- bodyAmount = at.found ? "equal" : "absent";
783
- if (at.found) {
784
- const named = adyenAmount(at.value);
785
- if (!named)
786
- return fail("unreadable", `the amount in ${merchant}'s card request is not the {value, currency} Agentcard reviewed`);
787
- if (named.currency !== reading.currency || named.minor !== reading.amountCents) {
788
- return fail("mismatch", `${merchant}'s card request asks for ${named.minor} ${named.currency}, but its own total is ${reading.amountCents} ${reading.currency}`);
789
- }
790
- }
850
+ if (merchantBodyAmountRefusal(profile) !== null)
851
+ return fail("unreviewed", `${merchant}'s body amount is not a reviewed declaration`);
852
+ const named = readMerchantBodyAmount(profile, ctx.body);
853
+ const bodyAmount = named.kind === "unnamed" ? "none" : named.kind === "absent" ? "absent" : "equal";
854
+ if (named.kind === "unreadable") {
855
+ return fail("unreadable", `the amount in ${merchant}'s card request is not the ${Array.isArray(profile.amount) ? "{value, currency}" : "amount"} Agentcard reviewed`);
856
+ }
857
+ if (named.kind === "amount" && (named.currency !== reading.currency || named.amountCents !== reading.amountCents)) {
858
+ return fail("mismatch", `${merchant}'s card request asks for ${named.amountCents} ${named.currency}, but its own total is ${reading.amountCents} ${reading.currency}`);
791
859
  }
792
860
  return {
793
861
  ok: true,
@@ -1307,6 +1375,154 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
1307
1375
  clientKey: "live_YJLFKT47JJG2RI7QIYX6HTKKDEDOEGOH",
1308
1376
  publicKeySha256: "da33611149dff3ab715be8671c0550bb8aec6112983b2473db9ee7afb829c110"
1309
1377
  }
1378
+ },
1379
+ {
1380
+ // Read Sep 25, 2026, from public code only: the WooCommerce plugin super-payments 1.40.1 and
1381
+ // cdn.superpayments.com/js/v2/payment.webcomponents.script.js as it stood on Sep 19.
1382
+ // WePrescribe's page runs Super's <super-checkout> component, which picks the card leg per
1383
+ // checkout session: ADYEN, under Super's own Adyen account (adyen-web 6.36.0 in Super's frame
1384
+ // web-checkout.superpayments.com/adyen-card-host, which hands state.data to the page by
1385
+ // postMessage), or STRIPE, a Payment Element the stripe definition already pays. On the ADYEN
1386
+ // leg handleAdyenSubmit posts, with the session's Bearer token, {amount (the gross, minor
1387
+ // units), currency, fundingMethod: {amount (the card's net share), type CARD, providerArgs:
1388
+ // {type ADYEN, paymentMethod: {type CARD, the four JWEs, holderName, postalCode},
1389
+ // browserInfo}}, rewardsToUse} from the page to api.superpayments.com. The card is charged
1390
+ // fundingMethod.amount, so that is what the agent's amount must equal. No brand rides in the
1391
+ // body. A saved card sends type SAVED_CARD with storedPaymentMethodId and no JWEs. Super's
1392
+ // save-card component posts ciphertext to client.superpayments.com/setup-intent/confirm,
1393
+ // which the WooCommerce checkout never calls. Public key read from checkoutshopper-live.adyen.com
1394
+ // on 2026-09-25 and again on 2026-09-30 (UTC), unchanged. The path carries the checkout
1395
+ // session id, which no preparation can name before the card request exists, so the review
1396
+ // rules hold this profile in observe. A HAR still has to confirm the leg Super picks for a
1397
+ // WePrescribe order and the body that leg posts.
1398
+ id: "super_payments_checkout",
1399
+ merchant: "Super Payments",
1400
+ status: "observe",
1401
+ origins: [{ checkout: "https://weprescribe.co.uk", endpoint: "https://api.superpayments.com" }],
1402
+ method: "POST",
1403
+ path: "/v3/checkout-sessions/:checkoutSessionId/payment",
1404
+ query: {},
1405
+ requires: [
1406
+ { path: ["fundingMethod", "type"], equals: "CARD" },
1407
+ { path: ["fundingMethod", "providerArgs", "type"], equals: "ADYEN" }
1408
+ ],
1409
+ holder: ["fundingMethod", "providerArgs", "paymentMethod"],
1410
+ holderType: "CARD",
1411
+ fields: {
1412
+ encryptedCardNumber: "encryptedCardNumber",
1413
+ encryptedExpiryMonth: "encryptedExpiryMonth",
1414
+ encryptedExpiryYear: "encryptedExpiryYear",
1415
+ encryptedSecurityCode: "encryptedSecurityCode"
1416
+ },
1417
+ remove: [],
1418
+ refuse: [
1419
+ { path: ["fundingMethod", "providerArgs", "paymentMethod", "storedPaymentMethodId"], when: "present" }
1420
+ ],
1421
+ amount: { value: ["fundingMethod", "amount"], currency: ["currency"], format: "number", unit: "minor" },
1422
+ otherCardRequests: [{ origin: "https://client.superpayments.com", path: "/setup-intent/confirm" }],
1423
+ adyen: {
1424
+ environment: "live",
1425
+ clientKey: "live_5MO5NXLPV5HPFJZWKYBAGA5RRMHEYWSD",
1426
+ publicKeySha256: "b1cd67db43491625b433c9b864beea8a0f0351378bcd3e9e1e0f19e3aad38fcf"
1427
+ }
1428
+ },
1429
+ {
1430
+ // Read Sep 25, 2026, from public code only.
1431
+ // www.corepoweryoga.com asks its API for a hosted payment page (the card-free POST
1432
+ // api2.corepoweryoga.com/stored-payment answers hosted_payment_uri) and frames it: Zenoti
1433
+ // Payments, Zenoti's Adyen platform, at corepoweryoga.zenoti.com/OnlinePayments/AdyentHPP.aspx,
1434
+ // a cross-origin frame running adyen-web v6 Drop-in in the Advanced flow. Its makePayment
1435
+ // (fintech-cdn.zenoti.com .../AdyentHPP.js, SHA-256 95e14da8...5df39) posts {processorId,
1436
+ // subscriberId, transactionSource, paymentRequest: {paymentMethod: adyen-web's
1437
+ // state.data.paymentMethod, type: that method's type, reference, BrowserInformation,
1438
+ // shopperLocale, signature, channel, amount: {currency, value}, brand: onBrand's code for the
1439
+ // typed card, FirstRecurringCharge}} to AdyentHPP.aspx/ProcessTransaction on the frame's own
1440
+ // origin, and Zenoti's server calls Adyen /payments. The pair names the top page, the checkout
1441
+ // the SDK prepares on. paymentRequest.brand describes the agent's dummy, so the approved card's
1442
+ // brand is written there, beside the holder, and adyen-web's own brand at the holder is dropped.
1443
+ // This request saves the card on file: CorePower then charges the stored card on its server
1444
+ // (api2.corepoweryoga.com/purchase-invoice-cc-on-file) and memberships bill it monthly. The
1445
+ // client key and environment arrive with the frame's per-session token, which only a signed-in
1446
+ // shopper's page reads, so the key is a draft until a HAR pins it.
1447
+ id: "zenoti_corepoweryoga_hpp",
1448
+ merchant: "CorePower Yoga",
1449
+ status: "observe",
1450
+ origins: [{ checkout: "https://www.corepoweryoga.com", endpoint: "https://corepoweryoga.zenoti.com" }],
1451
+ method: "POST",
1452
+ path: "/OnlinePayments/AdyentHPP.aspx/ProcessTransaction",
1453
+ query: {},
1454
+ requires: [{ path: ["paymentRequest", "type"], equals: "scheme" }],
1455
+ holder: ["paymentRequest", "paymentMethod"],
1456
+ fields: {
1457
+ encryptedCardNumber: "encryptedCardNumber",
1458
+ encryptedExpiryMonth: "encryptedExpiryMonth",
1459
+ encryptedExpiryYear: "encryptedExpiryYear",
1460
+ encryptedSecurityCode: "encryptedSecurityCode"
1461
+ },
1462
+ remove: ["brand"],
1463
+ refuse: [
1464
+ { path: ["paymentRequest", "storePaymentMethod"], when: "true" },
1465
+ { path: ["paymentRequest", "paymentMethod", "storedPaymentMethodId"], when: "present" },
1466
+ { path: ["paymentRequest", "paymentMethod", "recurringDetailReference"], when: "present" }
1467
+ ],
1468
+ amount: ["paymentRequest", "amount"],
1469
+ facts: [
1470
+ // adyen-web's brand codes, as onBrand reports them.
1471
+ {
1472
+ path: ["paymentRequest", "brand"],
1473
+ fact: "brand",
1474
+ map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover", diners: "diners", jcb: "jcb" }
1475
+ }
1476
+ ],
1477
+ adyen: { environment: "live", clientKey: null, publicKeySha256: null }
1478
+ },
1479
+ {
1480
+ // Read Sep 25, 2026, from public code only: the Cathay Payment Page bundle on
1481
+ // payment.cathaypacific.com (main.14d368386ca2a9041a823b758307b3c2.js, adyen-web 5.71.5
1482
+ // securedfields, environment live), where flight bookings and manage-booking pay. The page
1483
+ // builds one outgoing request: amount and currency from the booking (a decimal
1484
+ // number in major units; the cash part when miles pay the rest), the four JWEs at its root as
1485
+ // encrypted_card_number, encrypted_expiry_month, encrypted_expiry_year and
1486
+ // encrypted_security_code, raw_card_bin (the typed card's BIN, as adyen-web's issuerBin or
1487
+ // binValue reports it), store_a_card, pay_with_stored_card, stored_card_token and
1488
+ // pay_with_adyen_web true. signAndAuthorise posts {sign_request: that request, hpp_signature}
1489
+ // to /cpp-paypage/v1/public/sign (header signapi: authorise, or its discount, currency, split,
1490
+ // full-miles or bank-form variant), then a copy made before signing, plus the returned
1491
+ // cpp_signature, to /cpp-paypage/v1/miles-plus-cash-authorise, which charges it. The signature
1492
+ // covers the JWEs, so the card request is the signing step and the charge is its then request.
1493
+ // raw_card_bin describes the dummy, so the approved card's BIN6 is written there, never more
1494
+ // (adyen-web can report eight digits). The client key comes from document.cppData per
1495
+ // session, so the key is a draft until a HAR pins it. The page's other card form sends a card
1496
+ // alias (alias_card_number) in place of the JWEs, and a refuse rule below turns it away.
1497
+ id: "cathay_cpp_sign",
1498
+ merchant: "Cathay Pacific",
1499
+ status: "observe",
1500
+ origins: [{ checkout: "https://payment.cathaypacific.com", endpoint: "https://api.cathaypacific.com" }],
1501
+ method: "POST",
1502
+ path: "/cpp-paypage/v1/public/sign",
1503
+ query: {},
1504
+ requires: [
1505
+ { path: ["sign_request", "pay_with_adyen_web"], equals: true },
1506
+ { path: ["sign_request", "pay_with_stored_card"], equals: false },
1507
+ { path: ["sign_request", "stored_card_token"], equals: "" }
1508
+ ],
1509
+ holder: ["sign_request"],
1510
+ fields: {
1511
+ encryptedCardNumber: "encrypted_card_number",
1512
+ encryptedExpiryMonth: "encrypted_expiry_month",
1513
+ encryptedExpiryYear: "encrypted_expiry_year",
1514
+ encryptedSecurityCode: "encrypted_security_code"
1515
+ },
1516
+ remove: [],
1517
+ refuse: [
1518
+ { path: ["sign_request", "store_a_card"], when: "true" },
1519
+ { path: ["sign_request", "stored_card_id"], when: "present" },
1520
+ { path: ["sign_request", "alias_card_number"], when: "present" }
1521
+ ],
1522
+ amount: { value: ["sign_request", "amount"], currency: ["sign_request", "currency"], format: "number", unit: "major" },
1523
+ facts: [{ path: ["sign_request", "raw_card_bin"], fact: "bin6" }],
1524
+ then: { method: "POST", path: "/cpp-paypage/v1/miles-plus-cash-authorise", query: {}, holder: [] },
1525
+ adyen: { environment: "live", clientKey: null, publicKeySha256: null }
1310
1526
  }
1311
1527
  ]);
1312
1528
  function adyenKeyHost(environment) {
@@ -1352,9 +1568,11 @@ var IPV4_HOST = /^\d{1,3}(?:\.\d{1,3}){3}$/;
1352
1568
  function underDomain(hostname, domain) {
1353
1569
  return hostname === domain || hostname.endsWith(`.${domain}`);
1354
1570
  }
1571
+ var TWO_LABEL_SUFFIXES = /* @__PURE__ */ new Set(["co.uk", "org.uk", "ac.uk", "com.au", "net.au", "co.nz", "co.jp", "com.hk", "com.sg", "co.in"]);
1355
1572
  function reviewedDomain(hostname) {
1356
1573
  const labels = hostname.split(".");
1357
- return labels.length > 2 ? labels.slice(1).join(".") : hostname;
1574
+ const kept = TWO_LABEL_SUFFIXES.has(labels.slice(-2).join(".")) ? 3 : 2;
1575
+ return labels.length > kept ? labels.slice(1).join(".") : hostname;
1358
1576
  }
1359
1577
  function declaredEndpointRefusal(endpoint) {
1360
1578
  let url;
@@ -1380,28 +1598,6 @@ function declaredEndpointRefusal(endpoint) {
1380
1598
  return `${url.hostname} belongs to a merchant Agentcard reviewed, so it cannot be declared`;
1381
1599
  return null;
1382
1600
  }
1383
- function sameKeyIgnoringCase(key, step) {
1384
- return !FORBIDDEN_KEYS.has(key) && (key.toLowerCase() === step.toLowerCase() || key.toUpperCase() === step.toUpperCase());
1385
- }
1386
- function valuesIgnoringCase(root, path) {
1387
- let nodes = [root];
1388
- for (const step of path) {
1389
- const next = [];
1390
- for (const node of nodes) {
1391
- if (typeof step === "number") {
1392
- if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
1393
- next.push(node[step]);
1394
- }
1395
- else if (isPlainObject(node)) {
1396
- for (const key of Object.keys(node))
1397
- if (sameKeyIgnoringCase(key, step))
1398
- next.push(node[key]);
1399
- }
1400
- }
1401
- nodes = next;
1402
- }
1403
- return nodes;
1404
- }
1405
1601
  function merchantBodyRefusal(profile, body) {
1406
1602
  const p = merchantProfileRecognizerById(typeof profile === "string" ? profile : profile?.id);
1407
1603
  if (!p)
@@ -1639,6 +1835,10 @@ function reviewedProfile(profileId) {
1639
1835
  throw new Error("Refusing to pay: unknown merchant profile.");
1640
1836
  return profile;
1641
1837
  }
1838
+ function otherCardRequestOf(profile, url) {
1839
+ const others = Array.isArray(profile.otherCardRequests) ? profile.otherCardRequests : [];
1840
+ return others.find((other) => other.origin === url.origin && pathMatchesTemplate(other.path, url.pathname));
1841
+ }
1642
1842
  function merchantProfileUrlRelation(profileId, rawUrl) {
1643
1843
  const profile = reviewedProfile(profileId);
1644
1844
  if (typeof rawUrl !== "string")
@@ -1654,9 +1854,12 @@ function merchantProfileUrlRelation(profileId, rawUrl) {
1654
1854
  }
1655
1855
  if (url.protocol !== "https:" || url.port || url.username || url.password)
1656
1856
  return null;
1657
- if (!profile.origins.some((pair) => pair.endpoint === url.origin))
1658
- return null;
1659
- return pathMatchesTemplate(profile.path, url.pathname) ? "sibling" : null;
1857
+ const onEndpoint = profile.origins.some((pair) => pair.endpoint === url.origin);
1858
+ if (onEndpoint && pathMatchesTemplate(profile.path, url.pathname))
1859
+ return "sibling";
1860
+ if (onEndpoint && isPlainObject(profile.then) && pathMatchesTemplate(profile.then.path, url.pathname))
1861
+ return "then";
1862
+ return otherCardRequestOf(profile, url) ? "other" : null;
1660
1863
  }
1661
1864
  var JWE_AT = /ey[A-Za-z0-9_-]{1,126}\.[A-Za-z0-9_-]{342}\.[A-Za-z0-9_-]{16}\.[A-Za-z0-9_-]{1,1024}\.[A-Za-z0-9_-]{22}/y;
1662
1865
  var LEGACY_BLOB = /adyenjs_0_1_\d{1,4}(?:\$|%24)/i;
@@ -1738,15 +1941,30 @@ function holderRefusal(profile, parsed) {
1738
1941
  if (new Set(values).size !== values.length)
1739
1942
  return "two encrypted card fields in the paused body are the same";
1740
1943
  const type = holder.value.type;
1741
- if (type !== void 0 && type !== "scheme")
1944
+ if (type !== void 0 && type !== (profile.holderType ?? "scheme"))
1742
1945
  return "the paused body pays with something other than a card";
1743
1946
  return null;
1744
1947
  }
1745
1948
  function amountRefusal(profile, parsed) {
1746
1949
  const priced = isPlainObject(profile.amountSource);
1747
- if (!Array.isArray(profile.amount)) {
1950
+ if (profile.amount === null || profile.amount === void 0) {
1748
1951
  return priced ? null : `${profile.merchant}'s card request names no amount, so it cannot be checked against the amount the cardholder approves`;
1749
1952
  }
1953
+ if (!Array.isArray(profile.amount)) {
1954
+ let read;
1955
+ try {
1956
+ read = readMerchantBodyAmount(profile, parsed);
1957
+ }
1958
+ catch {
1959
+ read = { kind: "unreadable" };
1960
+ }
1961
+ if (read.kind === "amount")
1962
+ return null;
1963
+ if (read.kind === "absent") {
1964
+ return priced ? null : `the paused body names no amount where ${profile.merchant}'s card request holds it, so it cannot be checked against the amount the cardholder approves`;
1965
+ }
1966
+ return `the amount where ${profile.merchant}'s card request holds it is not the amount Agentcard reviewed, so it cannot be checked`;
1967
+ }
1750
1968
  const at = jsonAt(parsed, profile.amount);
1751
1969
  if (!at.found) {
1752
1970
  return priced ? null : `the paused body names no amount where ${profile.merchant}'s card request holds it, so it cannot be checked against the amount the cardholder approves`;
@@ -1774,9 +1992,12 @@ function classifyMerchantRequest(profileId, rawUrl, method, body) {
1774
1992
  const relation = merchantProfileUrlRelation(profile.id, rawUrl);
1775
1993
  if (!relation || !carriesAdyenCardCiphertext(body))
1776
1994
  return PASS;
1777
- if (relation === "sibling") {
1995
+ if (relation === "sibling" || relation === "other") {
1778
1996
  return abort(`the paused request sends a card to ${profile.merchant} somewhere other than the card request Agentcard reviewed`);
1779
1997
  }
1998
+ if (relation === "then") {
1999
+ return abort(`the paused request sends ${profile.merchant}'s card again, to the request after the card request Agentcard reviewed, and Agentcard does not write the card there yet`);
2000
+ }
1780
2001
  if (typeof method !== "string" || method.toUpperCase() !== profile.method) {
1781
2002
  return abort(`the paused request sends a card to ${profile.merchant} as ${String(method).toUpperCase()}, not as the ${profile.method} Agentcard reviewed`);
1782
2003
  }
@@ -1816,16 +2037,23 @@ function declaredTemplatedUrl(endpoint) {
1816
2037
  }
1817
2038
  function merchantProfileTemplatedUrl(profileId, rawUrl) {
1818
2039
  const profile = reviewedProfile(profileId);
1819
- let origin = null;
2040
+ let url = null;
1820
2041
  try {
1821
- origin = new URL(rawUrl).origin;
2042
+ url = new URL(rawUrl);
1822
2043
  }
1823
2044
  catch {
1824
- origin = null;
2045
+ url = null;
2046
+ }
2047
+ const origin = url ? url.origin : null;
2048
+ const relation = url ? merchantProfileUrlRelation(profile.id, rawUrl) : null;
2049
+ if (relation === "other") {
2050
+ const other = otherCardRequestOf(profile, url);
2051
+ return `${other.origin}${other.path}`;
1825
2052
  }
1826
2053
  const pair = profile.origins.find((candidate) => candidate.endpoint === origin) ?? profile.origins[0];
1827
- const query = Object.entries(profile.query).map(([key, value]) => `${key}=${value}`).join("&");
1828
- return `${pair.endpoint}${profile.path}${query ? `?${query}` : ""}`;
2054
+ const request = relation === "then" ? profile.then : profile;
2055
+ const query = Object.entries(request.query).map(([key, value]) => `${key}=${value}`).join("&");
2056
+ return `${pair.endpoint}${request.path}${query ? `?${query}` : ""}`;
1829
2057
  }
1830
2058
  function keyPaths(root, out) {
1831
2059
  const stack = [[root, ""]];
@@ -1858,10 +2086,16 @@ function merchantBodyKeyPathSha256(body) {
1858
2086
  return sha256Hex([...paths].sort().join("\n"));
1859
2087
  }
1860
2088
  var globEscape = (text) => text.replace(/[*?\\]/g, (char) => `\\${char}`);
2089
+ var templateGlob = (template) => template.split("/").map((segment) => segment.startsWith(":") ? "*" : globEscape(segment)).join("/");
1861
2090
  function merchantProfileUrlGlobs(profileId) {
1862
2091
  const profile = reviewedProfile(profileId);
1863
- const path = profile.path.split("/").map((segment) => segment.startsWith(":") ? "*" : globEscape(segment)).join("/");
1864
- return [...new Set(profile.origins.map((pair) => `${pair.endpoint}${path}*`))];
2092
+ const endpoints = profile.origins.map((pair) => pair.endpoint);
2093
+ const others = Array.isArray(profile.otherCardRequests) ? profile.otherCardRequests : [];
2094
+ return [... /* @__PURE__ */new Set([
2095
+ ...endpoints.map((origin) => `${origin}${templateGlob(profile.path)}*`),
2096
+ ...isPlainObject(profile.then) ? endpoints.map((origin) => `${origin}${templateGlob(profile.then.path)}*`) : [],
2097
+ ...others.map((other) => `${other.origin}${templateGlob(other.path)}*`)
2098
+ ])];
1865
2099
  }
1866
2100
  function declaredUrlGlobs(endpoint) {
1867
2101
  const url = new URL(endpoint);
@@ -46,14 +46,21 @@ export declare class CheckoutModule {
46
46
  headers: Record<string, string>;
47
47
  body: string;
48
48
  }): ReturnType<VaultClient['checkStripeContinuation']>;
49
+ /** Asks the API whether a Stripe card save may continue with the payment method an approval produced. */
50
+ checkStripeSetupContinuation(authorizationId: string, request: {
51
+ url: string;
52
+ method: string;
53
+ headers: Record<string, string>;
54
+ body: string;
55
+ }): ReturnType<VaultClient['checkStripeSetupContinuation']>;
49
56
  /** True when this request is a card request a recogniser claims. */
50
57
  isCardRequest(url: string, method?: string): boolean;
51
58
  /** The recogniser that claims a card request, or null. */
52
59
  recognizerOf(url: string, method?: string): Recognizer | null;
53
60
  /** How the card would reach the processor on this request, or null when nothing recognises it. */
54
61
  checkoutModeOf(url: string, method?: string): CheckoutMode | null;
55
- /** What happens to a recognised request whose body carries no card. */
56
- withoutCard(url: string, body: string | null | undefined): 'continue' | 'refuse' | null;
62
+ /** What happens to a recognised request whose body carries no card, given the requests this attachment already handed the card to. */
63
+ withoutCard(url: string, body: string | null | undefined, handedTo?: readonly string[]): 'continue' | 'refuse' | null;
57
64
  /** The URL patterns a raw CDP connection hands `Fetch.enable`, for the current recognisers. */
58
65
  cardUrlPatterns(): string[];
59
66
  /** The recognisers: the served or built-in list plus yours (`register`, `list`, `find`). */
package/dist/agentcard.js CHANGED
@@ -42,14 +42,16 @@ export class CheckoutModule {
42
42
  cancelAuthorization(authorizationId, reason) { return this.client.cancelAuthorization(authorizationId, reason); }
43
43
  /** Asks the API whether a Stripe confirmation that pays with an approved token may continue. */
44
44
  checkStripeContinuation(authorizationId, request) { return this.client.checkStripeContinuation(authorizationId, request); }
45
+ /** Asks the API whether a Stripe card save may continue with the payment method an approval produced. */
46
+ checkStripeSetupContinuation(authorizationId, request) { return this.client.checkStripeSetupContinuation(authorizationId, request); }
45
47
  /** True when this request is a card request a recogniser claims. */
46
48
  isCardRequest(url, method = 'POST') { return this.client.isCardRequest(url, method); }
47
49
  /** The recogniser that claims a card request, or null. */
48
50
  recognizerOf(url, method = 'POST') { return this.client.recognizerOf(url, method); }
49
51
  /** How the card would reach the processor on this request, or null when nothing recognises it. */
50
52
  checkoutModeOf(url, method = 'POST') { return this.client.checkoutModeOf(url, method); }
51
- /** What happens to a recognised request whose body carries no card. */
52
- withoutCard(url, body) { return this.client.withoutCard(url, body); }
53
+ /** What happens to a recognised request whose body carries no card, given the requests this attachment already handed the card to. */
54
+ withoutCard(url, body, handedTo = []) { return this.client.withoutCard(url, body, handedTo); }
53
55
  /** The URL patterns a raw CDP connection hands `Fetch.enable`, for the current recognisers. */
54
56
  cardUrlPatterns() { return this.client.cardUrlPatterns(); }
55
57
  /** The recognisers: the served or built-in list plus yours (`register`, `list`, `find`). */
@@ -0,0 +1,57 @@
1
+ declare var AMADEUS_CARD_MOPID: RegExp;
2
+ declare function amadeusBodyKind(body: any): "card" | "other";
3
+ declare var AMADEUS_CHECKOUT_PSP: string;
4
+ declare var AMADEUS_CHECKOUT_CAPABILITY: string;
5
+ declare var AMADEUS_CARD_URLS: Readonly<{
6
+ production: Readonly<{
7
+ sdk: readonly string[];
8
+ dcp: readonly string[];
9
+ }>;
10
+ sandbox: Readonly<{
11
+ sdk: readonly string[];
12
+ dcp: readonly never[];
13
+ }>;
14
+ }>;
15
+ declare var AMADEUS_MERCHANT_EXPIRY_POLICIES: readonly string[];
16
+ declare var AMADEUS_MERCHANT_EXPIRY_POLICY: string;
17
+ declare var AMADEUS_REFUSAL_REASONS: readonly string[];
18
+ declare function readAmadeusBody(body: any): any;
19
+ declare function amadeusCardUrl(url: any): {
20
+ path: string;
21
+ environment: string;
22
+ } | null;
23
+ declare function isAmadeusCardUrl(url: any, method?: string): boolean;
24
+ declare function amadeusEnvironment(url: any): string | undefined;
25
+ declare function amadeusPreparationUrls(environment: any): any[];
26
+ declare function amadeusSdkHostRefusal(headers: any, approvedHost: any): "header_binding" | null;
27
+ declare var AMADEUS_DCP_REVIEWED_KEYS: readonly string[];
28
+ declare function isAmadeusDcpConfig(config: any): boolean;
29
+ declare function amadeusVendorFor(number: any): "visa" | "mastercard" | "amex" | null;
30
+ declare function amadeusRequestRefusal(request: any, card: any, options?: {}): any;
31
+ declare function amadeusRefusalMessage(reason: any): any;
32
+ declare function isPreparedAmadeusRequest(url: any, method: any, body: any, environment: any, headers: any, options?: {}): boolean;
33
+ declare function injectAmadeusCard(body: any, card: any, options?: {}): {
34
+ body: string;
35
+ where: any[];
36
+ };
37
+ declare function amadeusResponseOutcome(status: any, body: any, { url, card }?: {}): {
38
+ outcome: string;
39
+ reason: string;
40
+ feeApplied?: undefined;
41
+ code?: undefined;
42
+ } | {
43
+ outcome: string;
44
+ feeApplied: boolean;
45
+ reason?: undefined;
46
+ code?: undefined;
47
+ } | {
48
+ outcome: string;
49
+ code: any;
50
+ reason?: undefined;
51
+ feeApplied?: undefined;
52
+ };
53
+ declare function sanitizeAmadeusResponse(body: any, url: any, card: any): string;
54
+ declare function amadeusFacts(body: any): {
55
+ refusalCode: any;
56
+ };
57
+ export { AMADEUS_CARD_MOPID, AMADEUS_CARD_URLS, AMADEUS_CHECKOUT_CAPABILITY, AMADEUS_CHECKOUT_PSP, AMADEUS_DCP_REVIEWED_KEYS, AMADEUS_MERCHANT_EXPIRY_POLICIES, AMADEUS_MERCHANT_EXPIRY_POLICY, AMADEUS_REFUSAL_REASONS, amadeusBodyKind, amadeusCardUrl, amadeusEnvironment, amadeusFacts, amadeusPreparationUrls, amadeusRefusalMessage, amadeusRequestRefusal, amadeusResponseOutcome, amadeusSdkHostRefusal, amadeusVendorFor, injectAmadeusCard, isAmadeusCardUrl, isAmadeusDcpConfig, isPreparedAmadeusRequest, readAmadeusBody, sanitizeAmadeusResponse };