@agent-cards/sdk 0.21.0 → 0.23.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 (113) hide show
  1. package/CHANGELOG.md +87 -2
  2. package/PREFLIGHT.md +32 -0
  3. package/README.md +1177 -23
  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 +302 -56
  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-facts.d.ts +96 -0
  18. package/dist/card-endpoint-facts.js +275 -0
  19. package/dist/card-endpoint-page.d.ts +54 -0
  20. package/dist/card-endpoint-page.js +69 -0
  21. package/dist/card-endpoint.d.ts +298 -0
  22. package/dist/card-endpoint.js +438 -0
  23. package/dist/card-fields.generated.d.ts +5 -3
  24. package/dist/card-fields.generated.js +284 -7
  25. package/dist/cdp.d.ts +5 -1
  26. package/dist/cdp.js +1155 -141
  27. package/dist/client.d.ts +456 -20
  28. package/dist/client.js +1072 -104
  29. package/dist/creditguard.generated.d.ts +79 -0
  30. package/dist/creditguard.generated.js +662 -0
  31. package/dist/cse-body.js +11 -1
  32. package/dist/cybersource-flex.d.ts +30 -0
  33. package/dist/cybersource-flex.generated.d.ts +27 -1
  34. package/dist/cybersource-flex.generated.js +42 -2
  35. package/dist/cybersource-flex.js +46 -0
  36. package/dist/datatrans.d.ts +96 -0
  37. package/dist/datatrans.generated.d.ts +64 -0
  38. package/dist/datatrans.generated.js +426 -0
  39. package/dist/datatrans.js +178 -0
  40. package/dist/ekashu.d.ts +35 -0
  41. package/dist/ekashu.js +55 -0
  42. package/dist/elavon-converge.d.ts +37 -0
  43. package/dist/elavon-converge.generated.d.ts +49 -0
  44. package/dist/elavon-converge.generated.js +439 -0
  45. package/dist/elavon-converge.js +84 -0
  46. package/dist/eprotect.d.ts +152 -0
  47. package/dist/eprotect.generated.d.ts +77 -0
  48. package/dist/eprotect.generated.js +498 -0
  49. package/dist/eprotect.js +222 -0
  50. package/dist/hosted-form.d.ts +3 -2
  51. package/dist/hosted-form.js +3 -2
  52. package/dist/index.d.ts +17 -3
  53. package/dist/index.js +6 -1
  54. package/dist/juspay-gate.d.ts +82 -0
  55. package/dist/juspay-gate.js +126 -0
  56. package/dist/juspay.generated.d.ts +89 -0
  57. package/dist/juspay.generated.js +705 -0
  58. package/dist/lifecycle.d.ts +60 -6
  59. package/dist/lifecycle.js +122 -8
  60. package/dist/merchant-card-endpoints.generated.d.ts +122 -0
  61. package/dist/merchant-card-endpoints.generated.js +4261 -0
  62. package/dist/merchant-hosted.d.ts +4 -1
  63. package/dist/merchant-hosted.js +5 -2
  64. package/dist/merchant-session-probe.d.ts +48 -0
  65. package/dist/merchant-session-probe.js +53 -0
  66. package/dist/merchant-session.generated.d.ts +69 -0
  67. package/dist/merchant-session.generated.js +919 -0
  68. package/dist/payrails.d.ts +133 -0
  69. package/dist/payrails.generated.d.ts +73 -0
  70. package/dist/payrails.generated.js +596 -0
  71. package/dist/payrails.js +305 -0
  72. package/dist/pelecard.d.ts +32 -0
  73. package/dist/pelecard.generated.d.ts +32 -0
  74. package/dist/pelecard.generated.js +283 -0
  75. package/dist/pelecard.js +42 -0
  76. package/dist/pre-claim.d.ts +81 -2
  77. package/dist/pre-claim.js +1023 -5
  78. package/dist/preflight-catalog.json +2267 -288
  79. package/dist/preflight-schemas.json +183 -6
  80. package/dist/preflight.generated.d.ts +1 -1
  81. package/dist/preflight.generated.js +243 -4
  82. package/dist/preparation.d.ts +12 -3
  83. package/dist/preparation.js +63 -12
  84. package/dist/prepared-processor.d.ts +84 -5
  85. package/dist/prepared-processor.js +147 -6
  86. package/dist/primer.d.ts +53 -0
  87. package/dist/primer.generated.d.ts +38 -0
  88. package/dist/primer.generated.js +624 -0
  89. package/dist/primer.js +82 -0
  90. package/dist/registry.d.ts +100 -8
  91. package/dist/registry.js +85 -11
  92. package/dist/shiji-sts.d.ts +144 -0
  93. package/dist/shiji-sts.generated.d.ts +72 -0
  94. package/dist/shiji-sts.generated.js +913 -0
  95. package/dist/shiji-sts.js +222 -0
  96. package/dist/stripe-checkout.generated.d.ts +1 -1
  97. package/dist/stripe-checkout.generated.js +764 -9
  98. package/dist/stripe-setup.d.ts +16 -0
  99. package/dist/stripe-setup.js +61 -0
  100. package/dist/substitutions.generated.js +187 -29
  101. package/dist/verifone.d.ts +68 -0
  102. package/dist/verifone.generated.d.ts +51 -0
  103. package/dist/verifone.generated.js +1413 -0
  104. package/dist/verifone.js +119 -0
  105. package/dist/voice-types.d.ts +27 -0
  106. package/dist/voice.d.ts +6 -1
  107. package/dist/voice.js +6 -1
  108. package/dist/wspay.d.ts +41 -0
  109. package/dist/wspay.generated.d.ts +41 -0
  110. package/dist/wspay.generated.js +370 -0
  111. package/dist/wspay.js +65 -0
  112. package/examples/preflight/kernel-native/inventory.json +1 -1
  113. 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: 7e4df686b96640378871d711a94bb545232ca858824a30c20e5a9d242a255f3b
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
  }
@@ -57,9 +79,14 @@ var PLACEHOLDER_NAME = /^[A-Za-z][A-Za-z0-9_]{0,31}$/;
57
79
  var LITERAL_SEGMENT = /^[A-Za-z0-9._~@+-]{1,256}$/;
58
80
  var QUERY_KEY = /^[A-Za-z0-9._~-]{1,64}$/;
59
81
  var QUERY_LITERAL = /^[A-Za-z0-9._~,-]{0,256}$/;
82
+ var QUERY_PREFIX = /^(?:[A-Za-z0-9._~,-]|%[0-9A-F]{2}){1,64}$/;
60
83
  var MAX_QUERIES = 8;
61
84
  var MAX_PAIRS = 16;
62
85
  var placeholderValue = (value) => PLACEHOLDER_VALUE.test(value) && value !== "." && !value.includes("..");
86
+ function queryPlaceholder(value) {
87
+ const mark = value.lastIndexOf(":");
88
+ return mark === -1 ? null : { prefix: value.slice(0, mark), name: value.slice(mark + 1) };
89
+ }
63
90
  function pathPlaceholders(template, pathname) {
64
91
  if (typeof template !== "string" || typeof pathname !== "string")
65
92
  return null;
@@ -104,10 +131,14 @@ function queryPlaceholders(query, text) {
104
131
  const written = got[i].slice(mark + 1);
105
132
  if (typeof value !== "string")
106
133
  return null;
107
- if (value.startsWith(":")) {
108
- if (!placeholderValue(written))
134
+ const placeholder = queryPlaceholder(value);
135
+ if (placeholder) {
136
+ if (!written.startsWith(placeholder.prefix))
137
+ return null;
138
+ const rest = written.slice(placeholder.prefix.length);
139
+ if (!placeholderValue(rest))
109
140
  return null;
110
- values[value.slice(1)] = written;
141
+ values[placeholder.name] = rest;
111
142
  }
112
143
  else if (written !== value) {
113
144
  return null;
@@ -156,9 +187,11 @@ function templatePlaceholders(template) {
156
187
  const queries = Array.isArray(template?.queries) ? template.queries : [template?.query];
157
188
  for (const query of queries) {
158
189
  if (query && typeof query === "object") {
159
- for (const value of Object.values(query))
160
- if (typeof value === "string" && value.startsWith(":"))
161
- names.push(value.slice(1));
190
+ for (const value of Object.values(query)) {
191
+ const placeholder = typeof value === "string" ? queryPlaceholder(value) : null;
192
+ if (placeholder)
193
+ names.push(placeholder.name);
194
+ }
162
195
  }
163
196
  }
164
197
  return names;
@@ -193,7 +226,8 @@ function queryTemplateRefusal(query) {
193
226
  return `the query key "${key}" is not unreserved characters`;
194
227
  if (typeof value !== "string")
195
228
  return `the query value of ${key} is not text`;
196
- if (value.startsWith(":") ? !PLACEHOLDER_NAME.test(value.slice(1)) : !QUERY_LITERAL.test(value)) {
229
+ const placeholder = queryPlaceholder(value);
230
+ if (placeholder ? !PLACEHOLDER_NAME.test(placeholder.name) || placeholder.prefix !== "" && !QUERY_PREFIX.test(placeholder.prefix) : !QUERY_LITERAL.test(value)) {
197
231
  return `the query value of ${key} is neither a literal nor a :name placeholder`;
198
232
  }
199
233
  }
@@ -713,6 +747,55 @@ function adyenAmount(value) {
713
747
  const currency = typeof value.currency === "string" && /^[A-Za-z]{3}$/.test(value.currency) ? value.currency.toUpperCase() : null;
714
748
  return minor === null || !currency ? null : { minor, currency };
715
749
  }
750
+ var BODY_AMOUNT_MEMBERS = ["value", "currency", "format", "unit"];
751
+ function merchantBodyAmountRefusal(profile) {
752
+ if (!isPlainObject(profile))
753
+ return "the profile is not an object";
754
+ const amount = profile.amount;
755
+ if (amount === null)
756
+ return null;
757
+ if (Array.isArray(amount))
758
+ return pathRefusal(amount, "the body amount");
759
+ const members = membersRefusal(amount, BODY_AMOUNT_MEMBERS, BODY_AMOUNT_MEMBERS, "the body amount");
760
+ if (members)
761
+ return members;
762
+ const paths = pathRefusal(amount.value, "the body amount's value") ?? pathRefusal(amount.currency, "the body amount's currency");
763
+ if (paths)
764
+ return paths;
765
+ if (pathsOverlap(amount.value, amount.currency))
766
+ return "the body amount's value and currency are read at the same place";
767
+ if (!FORMATS.includes(amount.format))
768
+ return `the body amount's format is not one of ${FORMATS.join(", ")}`;
769
+ if (!["major", "minor"].includes(amount.unit))
770
+ return "the body amount's unit is not major or minor";
771
+ if (amount.format !== "number" && amount.unit !== "major")
772
+ return "a decimal body amount is in major units";
773
+ return null;
774
+ }
775
+ function readMerchantBodyAmount(profile, body) {
776
+ const refusal = merchantBodyAmountRefusal(profile);
777
+ if (refusal)
778
+ throw new Error(`Refusing to pay: ${profile?.merchant ?? "this merchant"}'s body amount is not a reviewed declaration: ${refusal}.`);
779
+ const amount = profile.amount;
780
+ if (amount === null)
781
+ return { kind: "unnamed" };
782
+ if (Array.isArray(amount)) {
783
+ const at = jsonAt(body, amount);
784
+ if (!at.found)
785
+ return { kind: "absent" };
786
+ const named = adyenAmount(at.value);
787
+ return named ? { kind: "amount", amountCents: named.minor, currency: named.currency } : { kind: "unreadable" };
788
+ }
789
+ const value = jsonAt(body, amount.value);
790
+ if (!value.found)
791
+ return { kind: "absent" };
792
+ const currency = jsonAt(body, amount.currency);
793
+ if (!currency.found || typeof currency.value !== "string" || !/^[A-Za-z]{3}$/.test(currency.value))
794
+ return { kind: "unreadable" };
795
+ const code = currency.value.toUpperCase();
796
+ const minor = totalMinor(value.value, amount.format, amount.unit, code);
797
+ return minor === null ? { kind: "unreadable" } : { kind: "amount", amountCents: minor, currency: code };
798
+ }
716
799
  function readMerchantAmount(profile, input) {
717
800
  const stage = isPlainObject(profile) ? profile.amountSource : void 0;
718
801
  if (!isPlainObject(stage))
@@ -776,18 +859,15 @@ function readMerchantAmount(profile, input) {
776
859
  return fail("stale", `the card request was built from another ${merchant} response than the latest one (${describePath(rule.card)})`);
777
860
  }
778
861
  }
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
- }
862
+ if (merchantBodyAmountRefusal(profile) !== null)
863
+ return fail("unreviewed", `${merchant}'s body amount is not a reviewed declaration`);
864
+ const named = readMerchantBodyAmount(profile, ctx.body);
865
+ const bodyAmount = named.kind === "unnamed" ? "none" : named.kind === "absent" ? "absent" : "equal";
866
+ if (named.kind === "unreadable") {
867
+ return fail("unreadable", `the amount in ${merchant}'s card request is not the ${Array.isArray(profile.amount) ? "{value, currency}" : "amount"} Agentcard reviewed`);
868
+ }
869
+ if (named.kind === "amount" && (named.currency !== reading.currency || named.amountCents !== reading.amountCents)) {
870
+ return fail("mismatch", `${merchant}'s card request asks for ${named.amountCents} ${named.currency}, but its own total is ${reading.amountCents} ${reading.currency}`);
791
871
  }
792
872
  return {
793
873
  ok: true,
@@ -1307,6 +1387,154 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
1307
1387
  clientKey: "live_YJLFKT47JJG2RI7QIYX6HTKKDEDOEGOH",
1308
1388
  publicKeySha256: "da33611149dff3ab715be8671c0550bb8aec6112983b2473db9ee7afb829c110"
1309
1389
  }
1390
+ },
1391
+ {
1392
+ // Read Sep 25, 2026, from public code only: the WooCommerce plugin super-payments 1.40.1 and
1393
+ // cdn.superpayments.com/js/v2/payment.webcomponents.script.js as it stood on Sep 19.
1394
+ // WePrescribe's page runs Super's <super-checkout> component, which picks the card leg per
1395
+ // checkout session: ADYEN, under Super's own Adyen account (adyen-web 6.36.0 in Super's frame
1396
+ // web-checkout.superpayments.com/adyen-card-host, which hands state.data to the page by
1397
+ // postMessage), or STRIPE, a Payment Element the stripe definition already pays. On the ADYEN
1398
+ // leg handleAdyenSubmit posts, with the session's Bearer token, {amount (the gross, minor
1399
+ // units), currency, fundingMethod: {amount (the card's net share), type CARD, providerArgs:
1400
+ // {type ADYEN, paymentMethod: {type CARD, the four JWEs, holderName, postalCode},
1401
+ // browserInfo}}, rewardsToUse} from the page to api.superpayments.com. The card is charged
1402
+ // fundingMethod.amount, so that is what the agent's amount must equal. No brand rides in the
1403
+ // body. A saved card sends type SAVED_CARD with storedPaymentMethodId and no JWEs. Super's
1404
+ // save-card component posts ciphertext to client.superpayments.com/setup-intent/confirm,
1405
+ // which the WooCommerce checkout never calls. Public key read from checkoutshopper-live.adyen.com
1406
+ // on 2026-09-25 and again on 2026-09-30 (UTC), unchanged. The path carries the checkout
1407
+ // session id, which no preparation can name before the card request exists, so the review
1408
+ // rules hold this profile in observe. A HAR still has to confirm the leg Super picks for a
1409
+ // WePrescribe order and the body that leg posts.
1410
+ id: "super_payments_checkout",
1411
+ merchant: "Super Payments",
1412
+ status: "observe",
1413
+ origins: [{ checkout: "https://weprescribe.co.uk", endpoint: "https://api.superpayments.com" }],
1414
+ method: "POST",
1415
+ path: "/v3/checkout-sessions/:checkoutSessionId/payment",
1416
+ query: {},
1417
+ requires: [
1418
+ { path: ["fundingMethod", "type"], equals: "CARD" },
1419
+ { path: ["fundingMethod", "providerArgs", "type"], equals: "ADYEN" }
1420
+ ],
1421
+ holder: ["fundingMethod", "providerArgs", "paymentMethod"],
1422
+ holderType: "CARD",
1423
+ fields: {
1424
+ encryptedCardNumber: "encryptedCardNumber",
1425
+ encryptedExpiryMonth: "encryptedExpiryMonth",
1426
+ encryptedExpiryYear: "encryptedExpiryYear",
1427
+ encryptedSecurityCode: "encryptedSecurityCode"
1428
+ },
1429
+ remove: [],
1430
+ refuse: [
1431
+ { path: ["fundingMethod", "providerArgs", "paymentMethod", "storedPaymentMethodId"], when: "present" }
1432
+ ],
1433
+ amount: { value: ["fundingMethod", "amount"], currency: ["currency"], format: "number", unit: "minor" },
1434
+ otherCardRequests: [{ origin: "https://client.superpayments.com", path: "/setup-intent/confirm" }],
1435
+ adyen: {
1436
+ environment: "live",
1437
+ clientKey: "live_5MO5NXLPV5HPFJZWKYBAGA5RRMHEYWSD",
1438
+ publicKeySha256: "b1cd67db43491625b433c9b864beea8a0f0351378bcd3e9e1e0f19e3aad38fcf"
1439
+ }
1440
+ },
1441
+ {
1442
+ // Read Sep 25, 2026, from public code only.
1443
+ // www.corepoweryoga.com asks its API for a hosted payment page (the card-free POST
1444
+ // api2.corepoweryoga.com/stored-payment answers hosted_payment_uri) and frames it: Zenoti
1445
+ // Payments, Zenoti's Adyen platform, at corepoweryoga.zenoti.com/OnlinePayments/AdyentHPP.aspx,
1446
+ // a cross-origin frame running adyen-web v6 Drop-in in the Advanced flow. Its makePayment
1447
+ // (fintech-cdn.zenoti.com .../AdyentHPP.js, SHA-256 95e14da8...5df39) posts {processorId,
1448
+ // subscriberId, transactionSource, paymentRequest: {paymentMethod: adyen-web's
1449
+ // state.data.paymentMethod, type: that method's type, reference, BrowserInformation,
1450
+ // shopperLocale, signature, channel, amount: {currency, value}, brand: onBrand's code for the
1451
+ // typed card, FirstRecurringCharge}} to AdyentHPP.aspx/ProcessTransaction on the frame's own
1452
+ // origin, and Zenoti's server calls Adyen /payments. The pair names the top page, the checkout
1453
+ // the SDK prepares on. paymentRequest.brand describes the agent's dummy, so the approved card's
1454
+ // brand is written there, beside the holder, and adyen-web's own brand at the holder is dropped.
1455
+ // This request saves the card on file: CorePower then charges the stored card on its server
1456
+ // (api2.corepoweryoga.com/purchase-invoice-cc-on-file) and memberships bill it monthly. The
1457
+ // client key and environment arrive with the frame's per-session token, which only a signed-in
1458
+ // shopper's page reads, so the key is a draft until a HAR pins it.
1459
+ id: "zenoti_corepoweryoga_hpp",
1460
+ merchant: "CorePower Yoga",
1461
+ status: "observe",
1462
+ origins: [{ checkout: "https://www.corepoweryoga.com", endpoint: "https://corepoweryoga.zenoti.com" }],
1463
+ method: "POST",
1464
+ path: "/OnlinePayments/AdyentHPP.aspx/ProcessTransaction",
1465
+ query: {},
1466
+ requires: [{ path: ["paymentRequest", "type"], equals: "scheme" }],
1467
+ holder: ["paymentRequest", "paymentMethod"],
1468
+ fields: {
1469
+ encryptedCardNumber: "encryptedCardNumber",
1470
+ encryptedExpiryMonth: "encryptedExpiryMonth",
1471
+ encryptedExpiryYear: "encryptedExpiryYear",
1472
+ encryptedSecurityCode: "encryptedSecurityCode"
1473
+ },
1474
+ remove: ["brand"],
1475
+ refuse: [
1476
+ { path: ["paymentRequest", "storePaymentMethod"], when: "true" },
1477
+ { path: ["paymentRequest", "paymentMethod", "storedPaymentMethodId"], when: "present" },
1478
+ { path: ["paymentRequest", "paymentMethod", "recurringDetailReference"], when: "present" }
1479
+ ],
1480
+ amount: ["paymentRequest", "amount"],
1481
+ facts: [
1482
+ // adyen-web's brand codes, as onBrand reports them.
1483
+ {
1484
+ path: ["paymentRequest", "brand"],
1485
+ fact: "brand",
1486
+ map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover", diners: "diners", jcb: "jcb" }
1487
+ }
1488
+ ],
1489
+ adyen: { environment: "live", clientKey: null, publicKeySha256: null }
1490
+ },
1491
+ {
1492
+ // Read Sep 25, 2026, from public code only: the Cathay Payment Page bundle on
1493
+ // payment.cathaypacific.com (main.14d368386ca2a9041a823b758307b3c2.js, adyen-web 5.71.5
1494
+ // securedfields, environment live), where flight bookings and manage-booking pay. The page
1495
+ // builds one outgoing request: amount and currency from the booking (a decimal
1496
+ // number in major units; the cash part when miles pay the rest), the four JWEs at its root as
1497
+ // encrypted_card_number, encrypted_expiry_month, encrypted_expiry_year and
1498
+ // encrypted_security_code, raw_card_bin (the typed card's BIN, as adyen-web's issuerBin or
1499
+ // binValue reports it), store_a_card, pay_with_stored_card, stored_card_token and
1500
+ // pay_with_adyen_web true. signAndAuthorise posts {sign_request: that request, hpp_signature}
1501
+ // to /cpp-paypage/v1/public/sign (header signapi: authorise, or its discount, currency, split,
1502
+ // full-miles or bank-form variant), then a copy made before signing, plus the returned
1503
+ // cpp_signature, to /cpp-paypage/v1/miles-plus-cash-authorise, which charges it. The signature
1504
+ // covers the JWEs, so the card request is the signing step and the charge is its then request.
1505
+ // raw_card_bin describes the dummy, so the approved card's BIN6 is written there, never more
1506
+ // (adyen-web can report eight digits). The client key comes from document.cppData per
1507
+ // session, so the key is a draft until a HAR pins it. The page's other card form sends a card
1508
+ // alias (alias_card_number) in place of the JWEs, and a refuse rule below turns it away.
1509
+ id: "cathay_cpp_sign",
1510
+ merchant: "Cathay Pacific",
1511
+ status: "observe",
1512
+ origins: [{ checkout: "https://payment.cathaypacific.com", endpoint: "https://api.cathaypacific.com" }],
1513
+ method: "POST",
1514
+ path: "/cpp-paypage/v1/public/sign",
1515
+ query: {},
1516
+ requires: [
1517
+ { path: ["sign_request", "pay_with_adyen_web"], equals: true },
1518
+ { path: ["sign_request", "pay_with_stored_card"], equals: false },
1519
+ { path: ["sign_request", "stored_card_token"], equals: "" }
1520
+ ],
1521
+ holder: ["sign_request"],
1522
+ fields: {
1523
+ encryptedCardNumber: "encrypted_card_number",
1524
+ encryptedExpiryMonth: "encrypted_expiry_month",
1525
+ encryptedExpiryYear: "encrypted_expiry_year",
1526
+ encryptedSecurityCode: "encrypted_security_code"
1527
+ },
1528
+ remove: [],
1529
+ refuse: [
1530
+ { path: ["sign_request", "store_a_card"], when: "true" },
1531
+ { path: ["sign_request", "stored_card_id"], when: "present" },
1532
+ { path: ["sign_request", "alias_card_number"], when: "present" }
1533
+ ],
1534
+ amount: { value: ["sign_request", "amount"], currency: ["sign_request", "currency"], format: "number", unit: "major" },
1535
+ facts: [{ path: ["sign_request", "raw_card_bin"], fact: "bin6" }],
1536
+ then: { method: "POST", path: "/cpp-paypage/v1/miles-plus-cash-authorise", query: {}, holder: [] },
1537
+ adyen: { environment: "live", clientKey: null, publicKeySha256: null }
1310
1538
  }
1311
1539
  ]);
1312
1540
  function adyenKeyHost(environment) {
@@ -1352,9 +1580,11 @@ var IPV4_HOST = /^\d{1,3}(?:\.\d{1,3}){3}$/;
1352
1580
  function underDomain(hostname, domain) {
1353
1581
  return hostname === domain || hostname.endsWith(`.${domain}`);
1354
1582
  }
1583
+ 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
1584
  function reviewedDomain(hostname) {
1356
1585
  const labels = hostname.split(".");
1357
- return labels.length > 2 ? labels.slice(1).join(".") : hostname;
1586
+ const kept = TWO_LABEL_SUFFIXES.has(labels.slice(-2).join(".")) ? 3 : 2;
1587
+ return labels.length > kept ? labels.slice(1).join(".") : hostname;
1358
1588
  }
1359
1589
  function declaredEndpointRefusal(endpoint) {
1360
1590
  let url;
@@ -1380,28 +1610,6 @@ function declaredEndpointRefusal(endpoint) {
1380
1610
  return `${url.hostname} belongs to a merchant Agentcard reviewed, so it cannot be declared`;
1381
1611
  return null;
1382
1612
  }
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
1613
  function merchantBodyRefusal(profile, body) {
1406
1614
  const p = merchantProfileRecognizerById(typeof profile === "string" ? profile : profile?.id);
1407
1615
  if (!p)
@@ -1639,6 +1847,10 @@ function reviewedProfile(profileId) {
1639
1847
  throw new Error("Refusing to pay: unknown merchant profile.");
1640
1848
  return profile;
1641
1849
  }
1850
+ function otherCardRequestOf(profile, url) {
1851
+ const others = Array.isArray(profile.otherCardRequests) ? profile.otherCardRequests : [];
1852
+ return others.find((other) => other.origin === url.origin && pathMatchesTemplate(other.path, url.pathname));
1853
+ }
1642
1854
  function merchantProfileUrlRelation(profileId, rawUrl) {
1643
1855
  const profile = reviewedProfile(profileId);
1644
1856
  if (typeof rawUrl !== "string")
@@ -1654,9 +1866,12 @@ function merchantProfileUrlRelation(profileId, rawUrl) {
1654
1866
  }
1655
1867
  if (url.protocol !== "https:" || url.port || url.username || url.password)
1656
1868
  return null;
1657
- if (!profile.origins.some((pair) => pair.endpoint === url.origin))
1658
- return null;
1659
- return pathMatchesTemplate(profile.path, url.pathname) ? "sibling" : null;
1869
+ const onEndpoint = profile.origins.some((pair) => pair.endpoint === url.origin);
1870
+ if (onEndpoint && pathMatchesTemplate(profile.path, url.pathname))
1871
+ return "sibling";
1872
+ if (onEndpoint && isPlainObject(profile.then) && pathMatchesTemplate(profile.then.path, url.pathname))
1873
+ return "then";
1874
+ return otherCardRequestOf(profile, url) ? "other" : null;
1660
1875
  }
1661
1876
  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
1877
  var LEGACY_BLOB = /adyenjs_0_1_\d{1,4}(?:\$|%24)/i;
@@ -1738,15 +1953,30 @@ function holderRefusal(profile, parsed) {
1738
1953
  if (new Set(values).size !== values.length)
1739
1954
  return "two encrypted card fields in the paused body are the same";
1740
1955
  const type = holder.value.type;
1741
- if (type !== void 0 && type !== "scheme")
1956
+ if (type !== void 0 && type !== (profile.holderType ?? "scheme"))
1742
1957
  return "the paused body pays with something other than a card";
1743
1958
  return null;
1744
1959
  }
1745
1960
  function amountRefusal(profile, parsed) {
1746
1961
  const priced = isPlainObject(profile.amountSource);
1747
- if (!Array.isArray(profile.amount)) {
1962
+ if (profile.amount === null || profile.amount === void 0) {
1748
1963
  return priced ? null : `${profile.merchant}'s card request names no amount, so it cannot be checked against the amount the cardholder approves`;
1749
1964
  }
1965
+ if (!Array.isArray(profile.amount)) {
1966
+ let read;
1967
+ try {
1968
+ read = readMerchantBodyAmount(profile, parsed);
1969
+ }
1970
+ catch {
1971
+ read = { kind: "unreadable" };
1972
+ }
1973
+ if (read.kind === "amount")
1974
+ return null;
1975
+ if (read.kind === "absent") {
1976
+ 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`;
1977
+ }
1978
+ return `the amount where ${profile.merchant}'s card request holds it is not the amount Agentcard reviewed, so it cannot be checked`;
1979
+ }
1750
1980
  const at = jsonAt(parsed, profile.amount);
1751
1981
  if (!at.found) {
1752
1982
  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 +2004,12 @@ function classifyMerchantRequest(profileId, rawUrl, method, body) {
1774
2004
  const relation = merchantProfileUrlRelation(profile.id, rawUrl);
1775
2005
  if (!relation || !carriesAdyenCardCiphertext(body))
1776
2006
  return PASS;
1777
- if (relation === "sibling") {
2007
+ if (relation === "sibling" || relation === "other") {
1778
2008
  return abort(`the paused request sends a card to ${profile.merchant} somewhere other than the card request Agentcard reviewed`);
1779
2009
  }
2010
+ if (relation === "then") {
2011
+ 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`);
2012
+ }
1780
2013
  if (typeof method !== "string" || method.toUpperCase() !== profile.method) {
1781
2014
  return abort(`the paused request sends a card to ${profile.merchant} as ${String(method).toUpperCase()}, not as the ${profile.method} Agentcard reviewed`);
1782
2015
  }
@@ -1816,16 +2049,23 @@ function declaredTemplatedUrl(endpoint) {
1816
2049
  }
1817
2050
  function merchantProfileTemplatedUrl(profileId, rawUrl) {
1818
2051
  const profile = reviewedProfile(profileId);
1819
- let origin = null;
2052
+ let url = null;
1820
2053
  try {
1821
- origin = new URL(rawUrl).origin;
2054
+ url = new URL(rawUrl);
1822
2055
  }
1823
2056
  catch {
1824
- origin = null;
2057
+ url = null;
2058
+ }
2059
+ const origin = url ? url.origin : null;
2060
+ const relation = url ? merchantProfileUrlRelation(profile.id, rawUrl) : null;
2061
+ if (relation === "other") {
2062
+ const other = otherCardRequestOf(profile, url);
2063
+ return `${other.origin}${other.path}`;
1825
2064
  }
1826
2065
  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}` : ""}`;
2066
+ const request = relation === "then" ? profile.then : profile;
2067
+ const query = Object.entries(request.query).map(([key, value]) => `${key}=${value}`).join("&");
2068
+ return `${pair.endpoint}${request.path}${query ? `?${query}` : ""}`;
1829
2069
  }
1830
2070
  function keyPaths(root, out) {
1831
2071
  const stack = [[root, ""]];
@@ -1858,10 +2098,16 @@ function merchantBodyKeyPathSha256(body) {
1858
2098
  return sha256Hex([...paths].sort().join("\n"));
1859
2099
  }
1860
2100
  var globEscape = (text) => text.replace(/[*?\\]/g, (char) => `\\${char}`);
2101
+ var templateGlob = (template) => template.split("/").map((segment) => segment.startsWith(":") ? "*" : globEscape(segment)).join("/");
1861
2102
  function merchantProfileUrlGlobs(profileId) {
1862
2103
  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}*`))];
2104
+ const endpoints = profile.origins.map((pair) => pair.endpoint);
2105
+ const others = Array.isArray(profile.otherCardRequests) ? profile.otherCardRequests : [];
2106
+ return [... /* @__PURE__ */new Set([
2107
+ ...endpoints.map((origin) => `${origin}${templateGlob(profile.path)}*`),
2108
+ ...isPlainObject(profile.then) ? endpoints.map((origin) => `${origin}${templateGlob(profile.then.path)}*`) : [],
2109
+ ...others.map((other) => `${other.origin}${templateGlob(other.path)}*`)
2110
+ ])];
1865
2111
  }
1866
2112
  function declaredUrlGlobs(endpoint) {
1867
2113
  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 };