@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
@@ -0,0 +1,16 @@
1
+ /** A SetupIntent confirm's path, as Stripe.js sends it. */
2
+ export declare const STRIPE_SETUP_INTENT_CONFIRM: RegExp;
3
+ /**
4
+ * The page's SetupIntent confirm, paying with `paymentMethod` instead of the
5
+ * card it carries: every `payment_method_data[...]` field goes (the card, and
6
+ * the billing details and telemetry Stripe.js sends beside it; the payment
7
+ * method already carries the billing details of the approved request), and
8
+ * `payment_method=<id>` takes the place of the first one. Every other field
9
+ * stays byte for byte as the page wrote it (client_secret, key,
10
+ * expected_payment_method_type, use_stripe_sdk, return_url, mandate and option
11
+ * fields). Null, and nothing is rewritten, when the id is not a payment
12
+ * method's, when a key cannot be decoded, when the body already names another
13
+ * way to pay (payment_method, source, confirmation_token), or when it carries
14
+ * no payment_method_data at all.
15
+ */
16
+ export declare function setupConfirmPayingWith(body: string, paymentMethod: string): string | null;
@@ -0,0 +1,61 @@
1
+ // The SetupIntent confirm a Stripe card save continues with, shared by both
2
+ // adapters (cdp.ts claimStripeContinuation). Kept in its own module, outside the
3
+ // package's published entries, so its tests can reach it.
4
+ //
5
+ // A page that creates the payment method first (createPaymentMethod, which the
6
+ // cardholder approves) and then confirms a SetupIntent usually pays with the
7
+ // pm_ it got back: Twilio's Payment Element page with paymentMethodCreation
8
+ // 'manual' does, on basil and v3 Stripe.js (flows harness F12). Some pages send
9
+ // the card again inline instead, as payment_method_data[card][number] in
10
+ // POST /v1/setup_intents/{seti}/confirm: confirmCardSetup with the Card
11
+ // Element after createPaymentMethod does (F12b). The card there is whatever the
12
+ // agent typed; the approved payment method holds the cardholder's card. So that
13
+ // confirm continues paying with the approved payment method instead, once the
14
+ // API allows it.
15
+ /** A SetupIntent confirm's path, as Stripe.js sends it. */
16
+ export const STRIPE_SETUP_INTENT_CONFIRM = /^\/v1\/setup_intents\/seti_[A-Za-z0-9]+\/confirm$/;
17
+ const PAYMENT_METHOD_ID = /^pm_[A-Za-z0-9]{1,250}$/;
18
+ // The other ways a confirm can say what it pays with. A body that already names
19
+ // one is not the shape rewritten here.
20
+ const OTHER_WAYS_TO_PAY = new Set(['payment_method', 'source', 'confirmation_token']);
21
+ /**
22
+ * The page's SetupIntent confirm, paying with `paymentMethod` instead of the
23
+ * card it carries: every `payment_method_data[...]` field goes (the card, and
24
+ * the billing details and telemetry Stripe.js sends beside it; the payment
25
+ * method already carries the billing details of the approved request), and
26
+ * `payment_method=<id>` takes the place of the first one. Every other field
27
+ * stays byte for byte as the page wrote it (client_secret, key,
28
+ * expected_payment_method_type, use_stripe_sdk, return_url, mandate and option
29
+ * fields). Null, and nothing is rewritten, when the id is not a payment
30
+ * method's, when a key cannot be decoded, when the body already names another
31
+ * way to pay (payment_method, source, confirmation_token), or when it carries
32
+ * no payment_method_data at all.
33
+ */
34
+ export function setupConfirmPayingWith(body, paymentMethod) {
35
+ if (!PAYMENT_METHOD_ID.test(paymentMethod) || typeof body !== 'string' || body === '')
36
+ return null;
37
+ const kept = [];
38
+ let replaced = false;
39
+ for (const chunk of body.split('&')) {
40
+ if (chunk === '')
41
+ continue;
42
+ const eq = chunk.indexOf('=');
43
+ let key;
44
+ try {
45
+ key = decodeURIComponent((eq < 0 ? chunk : chunk.slice(0, eq)).replace(/\+/g, ' '));
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ if (OTHER_WAYS_TO_PAY.has(key))
51
+ return null;
52
+ if (key.startsWith('payment_method_data[')) {
53
+ if (!replaced)
54
+ kept.push(`payment_method=${paymentMethod}`);
55
+ replaced = true;
56
+ continue;
57
+ }
58
+ kept.push(chunk);
59
+ }
60
+ return replaced ? kept.join('&') : null;
61
+ }
@@ -1,6 +1,6 @@
1
1
  // @ts-nocheck
2
2
  // Generated from @agent-cards/payment-core. Do not edit.
3
- // artifact-sha256: 4079aa7704abbbbec052a96cb73463527ac5d027cd23780305c1e2ac954a5e88
3
+ // artifact-sha256: 6209fa4a22c349d03eed24ea4ff9e626334c5975ec515a4b7f6a83592343274f
4
4
  // src/adyen-jwe.js
5
5
  var ADYEN_JWE_HEADER = { alg: "RSA-OAEP-256", enc: "A256GCM", version: "1" };
6
6
  var BASE64URL = /^[A-Za-z0-9_-]+$/;
@@ -78,6 +78,28 @@ function pathsOverlap(a, b) {
78
78
  return false;
79
79
  return true;
80
80
  }
81
+ function sameKeyIgnoringCase(key, step) {
82
+ return !FORBIDDEN_KEYS.has(key) && (key.toLowerCase() === step.toLowerCase() || key.toUpperCase() === step.toUpperCase());
83
+ }
84
+ function valuesIgnoringCase(root, path) {
85
+ let nodes = [root];
86
+ for (const step of path) {
87
+ const next = [];
88
+ for (const node of nodes) {
89
+ if (typeof step === "number") {
90
+ if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
91
+ next.push(node[step]);
92
+ }
93
+ else if (isPlainObject(node)) {
94
+ for (const key of Object.keys(node))
95
+ if (sameKeyIgnoringCase(key, step))
96
+ next.push(node[key]);
97
+ }
98
+ }
99
+ nodes = next;
100
+ }
101
+ return nodes;
102
+ }
81
103
  function describePath(path) {
82
104
  return path.length ? path.map((step) => typeof step === "number" ? `[${step}]` : `.${step}`).join("").replace(/^\./, "") : "(root)";
83
105
  }
@@ -117,7 +139,8 @@ function checkedSet(set, path, names, remove) {
117
139
  }
118
140
  return entries;
119
141
  }
120
- function swapMerchantHostedFields(body, spec) {
142
+ var ADYEN_JWE = /* @__PURE__ */ Object.freeze({ accepts: isAdyenJweCompact, label: "an Adyen JWE" });
143
+ function swapMerchantHostedFields(body, spec, ciphertext = ADYEN_JWE) {
121
144
  if (!isPlainObject(spec))
122
145
  throw new SubstitutionError("refusing a substitution that is not an object");
123
146
  for (const member of Object.keys(spec)) {
@@ -136,8 +159,8 @@ function swapMerchantHostedFields(body, spec) {
136
159
  for (const name of names) {
137
160
  if (FORBIDDEN_KEYS.has(name))
138
161
  throw new SubstitutionError(`refusing substitution field ${name}`);
139
- if (!isAdyenJweCompact(fields[name]))
140
- throw new SubstitutionError(`substitution for ${name} is not an Adyen JWE`);
162
+ if (!ciphertext.accepts(fields[name]))
163
+ throw new SubstitutionError(`substitution for ${name} is not ${ciphertext.label}`);
141
164
  }
142
165
  if (!Array.isArray(remove))
143
166
  throw new SubstitutionError("substitutions.remove is not a list");
@@ -169,8 +192,8 @@ function swapMerchantHostedFields(body, spec) {
169
192
  const dummy = Object.prototype.hasOwnProperty.call(holder.value, name) ? holder.value[name] : void 0;
170
193
  if (typeof dummy !== "string")
171
194
  throw new SubstitutionError(`the paused body has no string field ${name} at ${describePath(path)}`);
172
- if (!isAdyenJweCompact(dummy))
173
- throw new SubstitutionError(`the paused field ${name} is not an Adyen JWE`);
195
+ if (!ciphertext.accepts(dummy))
196
+ throw new SubstitutionError(`the paused field ${name} is not ${ciphertext.label}`);
174
197
  if (swaps.has(dummy))
175
198
  throw new SubstitutionError("two paused fields carry the same ciphertext");
176
199
  swaps.set(dummy, fields[name]);
@@ -247,6 +270,12 @@ function merchantAmountCardPaths(profile) {
247
270
  const rules = [source, isPlainObject(source.afterPayment) ? source.afterPayment : {}].flatMap((stage) => [...Array.isArray(stage.bind) ? stage.bind : [], ...Array.isArray(stage.match) ? stage.match : []]);
248
271
  return rules.flatMap((rule) => isPlainObject(rule) ? [rule.card, rule.whenCard] : []).filter(Array.isArray);
249
272
  }
273
+ function merchantBodyAmountPaths(profile) {
274
+ const amount = isPlainObject(profile) ? profile.amount : void 0;
275
+ if (Array.isArray(amount))
276
+ return [amount];
277
+ return isPlainObject(amount) ? [amount.value, amount.currency].filter(Array.isArray) : [];
278
+ }
250
279
  // src/merchant-profiles.js
251
280
  var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
252
281
  {
@@ -549,6 +578,154 @@ var MERCHANT_HOSTED_PROFILES = /* @__PURE__ */ deepFreeze([
549
578
  clientKey: "live_YJLFKT47JJG2RI7QIYX6HTKKDEDOEGOH",
550
579
  publicKeySha256: "da33611149dff3ab715be8671c0550bb8aec6112983b2473db9ee7afb829c110"
551
580
  }
581
+ },
582
+ {
583
+ // Read Sep 25, 2026, from public code only: the WooCommerce plugin super-payments 1.40.1 and
584
+ // cdn.superpayments.com/js/v2/payment.webcomponents.script.js as it stood on Sep 19.
585
+ // WePrescribe's page runs Super's <super-checkout> component, which picks the card leg per
586
+ // checkout session: ADYEN, under Super's own Adyen account (adyen-web 6.36.0 in Super's frame
587
+ // web-checkout.superpayments.com/adyen-card-host, which hands state.data to the page by
588
+ // postMessage), or STRIPE, a Payment Element the stripe definition already pays. On the ADYEN
589
+ // leg handleAdyenSubmit posts, with the session's Bearer token, {amount (the gross, minor
590
+ // units), currency, fundingMethod: {amount (the card's net share), type CARD, providerArgs:
591
+ // {type ADYEN, paymentMethod: {type CARD, the four JWEs, holderName, postalCode},
592
+ // browserInfo}}, rewardsToUse} from the page to api.superpayments.com. The card is charged
593
+ // fundingMethod.amount, so that is what the agent's amount must equal. No brand rides in the
594
+ // body. A saved card sends type SAVED_CARD with storedPaymentMethodId and no JWEs. Super's
595
+ // save-card component posts ciphertext to client.superpayments.com/setup-intent/confirm,
596
+ // which the WooCommerce checkout never calls. Public key read from checkoutshopper-live.adyen.com
597
+ // on 2026-09-25 and again on 2026-09-30 (UTC), unchanged. The path carries the checkout
598
+ // session id, which no preparation can name before the card request exists, so the review
599
+ // rules hold this profile in observe. A HAR still has to confirm the leg Super picks for a
600
+ // WePrescribe order and the body that leg posts.
601
+ id: "super_payments_checkout",
602
+ merchant: "Super Payments",
603
+ status: "observe",
604
+ origins: [{ checkout: "https://weprescribe.co.uk", endpoint: "https://api.superpayments.com" }],
605
+ method: "POST",
606
+ path: "/v3/checkout-sessions/:checkoutSessionId/payment",
607
+ query: {},
608
+ requires: [
609
+ { path: ["fundingMethod", "type"], equals: "CARD" },
610
+ { path: ["fundingMethod", "providerArgs", "type"], equals: "ADYEN" }
611
+ ],
612
+ holder: ["fundingMethod", "providerArgs", "paymentMethod"],
613
+ holderType: "CARD",
614
+ fields: {
615
+ encryptedCardNumber: "encryptedCardNumber",
616
+ encryptedExpiryMonth: "encryptedExpiryMonth",
617
+ encryptedExpiryYear: "encryptedExpiryYear",
618
+ encryptedSecurityCode: "encryptedSecurityCode"
619
+ },
620
+ remove: [],
621
+ refuse: [
622
+ { path: ["fundingMethod", "providerArgs", "paymentMethod", "storedPaymentMethodId"], when: "present" }
623
+ ],
624
+ amount: { value: ["fundingMethod", "amount"], currency: ["currency"], format: "number", unit: "minor" },
625
+ otherCardRequests: [{ origin: "https://client.superpayments.com", path: "/setup-intent/confirm" }],
626
+ adyen: {
627
+ environment: "live",
628
+ clientKey: "live_5MO5NXLPV5HPFJZWKYBAGA5RRMHEYWSD",
629
+ publicKeySha256: "b1cd67db43491625b433c9b864beea8a0f0351378bcd3e9e1e0f19e3aad38fcf"
630
+ }
631
+ },
632
+ {
633
+ // Read Sep 25, 2026, from public code only.
634
+ // www.corepoweryoga.com asks its API for a hosted payment page (the card-free POST
635
+ // api2.corepoweryoga.com/stored-payment answers hosted_payment_uri) and frames it: Zenoti
636
+ // Payments, Zenoti's Adyen platform, at corepoweryoga.zenoti.com/OnlinePayments/AdyentHPP.aspx,
637
+ // a cross-origin frame running adyen-web v6 Drop-in in the Advanced flow. Its makePayment
638
+ // (fintech-cdn.zenoti.com .../AdyentHPP.js, SHA-256 95e14da8...5df39) posts {processorId,
639
+ // subscriberId, transactionSource, paymentRequest: {paymentMethod: adyen-web's
640
+ // state.data.paymentMethod, type: that method's type, reference, BrowserInformation,
641
+ // shopperLocale, signature, channel, amount: {currency, value}, brand: onBrand's code for the
642
+ // typed card, FirstRecurringCharge}} to AdyentHPP.aspx/ProcessTransaction on the frame's own
643
+ // origin, and Zenoti's server calls Adyen /payments. The pair names the top page, the checkout
644
+ // the SDK prepares on. paymentRequest.brand describes the agent's dummy, so the approved card's
645
+ // brand is written there, beside the holder, and adyen-web's own brand at the holder is dropped.
646
+ // This request saves the card on file: CorePower then charges the stored card on its server
647
+ // (api2.corepoweryoga.com/purchase-invoice-cc-on-file) and memberships bill it monthly. The
648
+ // client key and environment arrive with the frame's per-session token, which only a signed-in
649
+ // shopper's page reads, so the key is a draft until a HAR pins it.
650
+ id: "zenoti_corepoweryoga_hpp",
651
+ merchant: "CorePower Yoga",
652
+ status: "observe",
653
+ origins: [{ checkout: "https://www.corepoweryoga.com", endpoint: "https://corepoweryoga.zenoti.com" }],
654
+ method: "POST",
655
+ path: "/OnlinePayments/AdyentHPP.aspx/ProcessTransaction",
656
+ query: {},
657
+ requires: [{ path: ["paymentRequest", "type"], equals: "scheme" }],
658
+ holder: ["paymentRequest", "paymentMethod"],
659
+ fields: {
660
+ encryptedCardNumber: "encryptedCardNumber",
661
+ encryptedExpiryMonth: "encryptedExpiryMonth",
662
+ encryptedExpiryYear: "encryptedExpiryYear",
663
+ encryptedSecurityCode: "encryptedSecurityCode"
664
+ },
665
+ remove: ["brand"],
666
+ refuse: [
667
+ { path: ["paymentRequest", "storePaymentMethod"], when: "true" },
668
+ { path: ["paymentRequest", "paymentMethod", "storedPaymentMethodId"], when: "present" },
669
+ { path: ["paymentRequest", "paymentMethod", "recurringDetailReference"], when: "present" }
670
+ ],
671
+ amount: ["paymentRequest", "amount"],
672
+ facts: [
673
+ // adyen-web's brand codes, as onBrand reports them.
674
+ {
675
+ path: ["paymentRequest", "brand"],
676
+ fact: "brand",
677
+ map: { visa: "visa", mastercard: "mc", amex: "amex", discover: "discover", diners: "diners", jcb: "jcb" }
678
+ }
679
+ ],
680
+ adyen: { environment: "live", clientKey: null, publicKeySha256: null }
681
+ },
682
+ {
683
+ // Read Sep 25, 2026, from public code only: the Cathay Payment Page bundle on
684
+ // payment.cathaypacific.com (main.14d368386ca2a9041a823b758307b3c2.js, adyen-web 5.71.5
685
+ // securedfields, environment live), where flight bookings and manage-booking pay. The page
686
+ // builds one outgoing request: amount and currency from the booking (a decimal
687
+ // number in major units; the cash part when miles pay the rest), the four JWEs at its root as
688
+ // encrypted_card_number, encrypted_expiry_month, encrypted_expiry_year and
689
+ // encrypted_security_code, raw_card_bin (the typed card's BIN, as adyen-web's issuerBin or
690
+ // binValue reports it), store_a_card, pay_with_stored_card, stored_card_token and
691
+ // pay_with_adyen_web true. signAndAuthorise posts {sign_request: that request, hpp_signature}
692
+ // to /cpp-paypage/v1/public/sign (header signapi: authorise, or its discount, currency, split,
693
+ // full-miles or bank-form variant), then a copy made before signing, plus the returned
694
+ // cpp_signature, to /cpp-paypage/v1/miles-plus-cash-authorise, which charges it. The signature
695
+ // covers the JWEs, so the card request is the signing step and the charge is its then request.
696
+ // raw_card_bin describes the dummy, so the approved card's BIN6 is written there, never more
697
+ // (adyen-web can report eight digits). The client key comes from document.cppData per
698
+ // session, so the key is a draft until a HAR pins it. The page's other card form sends a card
699
+ // alias (alias_card_number) in place of the JWEs, and a refuse rule below turns it away.
700
+ id: "cathay_cpp_sign",
701
+ merchant: "Cathay Pacific",
702
+ status: "observe",
703
+ origins: [{ checkout: "https://payment.cathaypacific.com", endpoint: "https://api.cathaypacific.com" }],
704
+ method: "POST",
705
+ path: "/cpp-paypage/v1/public/sign",
706
+ query: {},
707
+ requires: [
708
+ { path: ["sign_request", "pay_with_adyen_web"], equals: true },
709
+ { path: ["sign_request", "pay_with_stored_card"], equals: false },
710
+ { path: ["sign_request", "stored_card_token"], equals: "" }
711
+ ],
712
+ holder: ["sign_request"],
713
+ fields: {
714
+ encryptedCardNumber: "encrypted_card_number",
715
+ encryptedExpiryMonth: "encrypted_expiry_month",
716
+ encryptedExpiryYear: "encrypted_expiry_year",
717
+ encryptedSecurityCode: "encrypted_security_code"
718
+ },
719
+ remove: [],
720
+ refuse: [
721
+ { path: ["sign_request", "store_a_card"], when: "true" },
722
+ { path: ["sign_request", "stored_card_id"], when: "present" },
723
+ { path: ["sign_request", "alias_card_number"], when: "present" }
724
+ ],
725
+ amount: { value: ["sign_request", "amount"], currency: ["sign_request", "currency"], format: "number", unit: "major" },
726
+ facts: [{ path: ["sign_request", "raw_card_bin"], fact: "bin6" }],
727
+ then: { method: "POST", path: "/cpp-paypage/v1/miles-plus-cash-authorise", query: {}, holder: [] },
728
+ adyen: { environment: "live", clientKey: null, publicKeySha256: null }
552
729
  }
553
730
  ]);
554
731
  function adyenKeyHost(environment) {
@@ -627,7 +804,7 @@ function merchantProfileFactsRefusal(profile) {
627
804
  const list = (value) => Array.isArray(value) ? value : [];
628
805
  const reviewed = {
629
806
  written: [...isPlainObject(profile.fields) ? Object.values(profile.fields) : [], ...list(profile.remove)],
630
- read: [...list(profile.requires), ...list(profile.refuse)].map((rule) => rule?.path).concat([profile.amount], merchantAmountCardPaths(profile)).filter(Array.isArray)
807
+ read: [...list(profile.requires), ...list(profile.refuse)].map((rule) => rule?.path).concat(merchantBodyAmountPaths(profile), merchantAmountCardPaths(profile)).filter(Array.isArray)
631
808
  };
632
809
  for (const [i, entry] of facts.entries()) {
633
810
  const refusal = factRefusal(entry, i, holder, besideHolder, reviewed);
@@ -642,28 +819,6 @@ function merchantProfileFactsRefusal(profile) {
642
819
  function merchantProfileRecognizerById(id) {
643
820
  return typeof id === "string" ? MERCHANT_PROFILE_RECOGNIZERS.find((entry) => entry.id === id) ?? null : null;
644
821
  }
645
- function sameKeyIgnoringCase(key, step) {
646
- return !FORBIDDEN_KEYS.has(key) && (key.toLowerCase() === step.toLowerCase() || key.toUpperCase() === step.toUpperCase());
647
- }
648
- function valuesIgnoringCase(root, path) {
649
- let nodes = [root];
650
- for (const step of path) {
651
- const next = [];
652
- for (const node of nodes) {
653
- if (typeof step === "number") {
654
- if (Array.isArray(node) && Number.isSafeInteger(step) && step >= 0 && step < node.length)
655
- next.push(node[step]);
656
- }
657
- else if (isPlainObject(node)) {
658
- for (const key of Object.keys(node))
659
- if (sameKeyIgnoringCase(key, step))
660
- next.push(node[key]);
661
- }
662
- }
663
- nodes = next;
664
- }
665
- return nodes;
666
- }
667
822
  function merchantBodyRefusal(profile, body) {
668
823
  const p = merchantProfileRecognizerById(typeof profile === "string" ? profile : profile?.id);
669
824
  if (!p)
@@ -785,6 +940,9 @@ function substituteMerchantHosted(body, sub, opts) {
785
940
  const profile = merchantProfileRecognizerById(sub.profile);
786
941
  if (!profile)
787
942
  throw new SubstitutionError("the substitution names no merchant profile Agentcard reviewed");
943
+ if (profile.then !== void 0) {
944
+ throw new SubstitutionError(`${profile.merchant} takes the card in a second request too, and Agentcard does not write it there yet`);
945
+ }
788
946
  if (!sameSteps(sub.path, profile.holder)) {
789
947
  throw new SubstitutionError(`the substitution path is not where ${profile.merchant}'s card request holds the card`);
790
948
  }
@@ -0,0 +1,68 @@
1
+ /** 'observe': paused, reported and aborted, never paid. 'enabled': a prepared checkout may pay its reviewed leg. */
2
+ export type VerifoneProfileStatus = 'observe' | 'enabled';
3
+ /** A reviewed Verifone profile this client armed: its id, the merchant it names, its environment and its effective status. */
4
+ export interface VerifoneProfile {
5
+ readonly id: string;
6
+ readonly merchant: string;
7
+ readonly environment: 'production' | 'sandbox';
8
+ readonly status: VerifoneProfileStatus;
9
+ }
10
+ /** A profile's verdict on a paused request; see classifyVerifoneProfileRequest. */
11
+ export type VerifoneRequestVerdict = {
12
+ verdict: 'pause';
13
+ leg: string;
14
+ } | {
15
+ verdict: 'abort';
16
+ reason: string;
17
+ } | {
18
+ verdict: 'pass';
19
+ };
20
+ /** The one substitution a Verifone approval writes: the device's blob at the leg's holder and field. */
21
+ export interface VerifoneSubstitutions {
22
+ encoding: 'json';
23
+ kind: 'verifone';
24
+ profile: string;
25
+ blob: string;
26
+ }
27
+ /** Whether an entry the API served is a Verifone profile (`{ verifone_profile }`), never a recognizer. */
28
+ export declare function isVerifoneProfileEntry(entry: unknown): boolean;
29
+ /**
30
+ * The profiles this client arms: only the ones the API served, each once, in this
31
+ * build's order. A served entry is `{ verifone_profile: <the profile's wire
32
+ * projection> }`, and the first one that names a profile with exactly this build's
33
+ * rules (origins, legs, body rules, key subkey: everything but `status`) and a status
34
+ * this build knows decides it: 'enabled' only when both that entry and this build say
35
+ * so. A profile the API did not serve, or served with other rules, is not armed.
36
+ */
37
+ export declare function armServedVerifoneProfiles(entries?: readonly unknown[]): VerifoneProfile[];
38
+ /** The armed profile whose endpoint origin a URL is on, or null. */
39
+ export declare function verifoneProfileFor(profiles: readonly VerifoneProfile[], url: string): VerifoneProfile | null;
40
+ /** The Fetch.enable globs that pause every request to every armed profile's endpoint origins. */
41
+ export declare function verifoneProfileUrlPatterns(profiles: readonly VerifoneProfile[]): string[];
42
+ /** The verdict payment-core gives a request at an armed profile: pause (its reviewed leg), abort (a blob it cannot pay, with the reason) or pass. */
43
+ export declare function classifyVerifoneProfileRequest(profile: VerifoneProfile, url: string, method: string, body: string | null | undefined): VerifoneRequestVerdict;
44
+ /** The URL an event names for a request at an armed profile: its origin and, for a leg, the leg's path template; never a query or an id. */
45
+ export declare function verifoneTemplatedUrl(profileId: string, url: string): string;
46
+ /** The endpoint a Verifone preparation pays from a checkout page: the profile's leg URL, or null. */
47
+ export declare function verifoneProfileEndpointFor(profileId: string, checkoutOrigin: string): string | null;
48
+ /** Whether a paused request is the one a Verifone preparation for this profile consents to. */
49
+ export declare function isPreparedVerifoneRequest(url: string, method: string, body: string | null, profileId: string): boolean;
50
+ /** Whether an approval's substitution is exactly a Verifone blob for this profile. */
51
+ export declare function isVerifoneSubstitution(sub: unknown, profileId: string): sub is VerifoneSubstitutions;
52
+ /**
53
+ * The continuation of a Verifone approval: the live paused body with the device's blob in place
54
+ * of the page's placeholder blob. The API keeps no merchant body, so the live body must first
55
+ * hash to the one the API checked when the authorization was created (`bodySha256`); a page that
56
+ * rewrote its body while the cardholder decided is refused rather than paid. payment-core's
57
+ * substituteVerifoneBlob then reads the leg's rules on the live body again, writes only the blob,
58
+ * and refuses a profile that is not enabled or a blob under any other key. Throws
59
+ * SubstitutionError; nothing leaves the browser when it does.
60
+ */
61
+ export declare function substituteVerifoneBody(request: {
62
+ url: string;
63
+ method: string;
64
+ body: string;
65
+ }, replay: {
66
+ substitutions: VerifoneSubstitutions;
67
+ bodySha256: string;
68
+ }): string;
@@ -0,0 +1,51 @@
1
+ declare var VERIFONE_KEY_PINS: any;
2
+ declare function verifoneKeyPinShapeRefusal(entry: any): "verifone_key_pin_invalid" | "verifone_key_pin_mismatch" | null;
3
+ declare var VERIFONE_MERCHANT_PROFILES: any;
4
+ declare function verifoneProfileRefusal(profile: any, pins?: any): string | null;
5
+ declare var VERIFONE_PROFILE_RECOGNIZERS: any;
6
+ declare var VERIFONE_BLOB_PREFIX: string;
7
+ declare function verifoneBlobInfo(value: any): Readonly<{
8
+ recipientKeyId: string;
9
+ wrappedKeyLength: any;
10
+ armorHeaders: readonly any[];
11
+ }> | null;
12
+ declare function isVerifoneBlob(value: any): boolean;
13
+ declare var VERIFONE_TEST_PANS: readonly string[];
14
+ declare var VERIFONE_CLOCK_SKEW_MS: number;
15
+ declare var VERIFONE_BLOB_MAX: number;
16
+ declare function carriesVerifoneBlob(body: any): any;
17
+ declare function verifoneCardPlaintext(card: any, nowMs: any): string;
18
+ declare function verifoneClockSkewRefusal(deviceNowMs: any, serverNowMs: any): "verifone_clock_unknown" | "verifone_clock_skew" | null;
19
+ declare var VERIFONE_SUBSTITUTION: Readonly<{
20
+ at: "merchant_profile";
21
+ fields: readonly string[];
22
+ }>;
23
+ declare function classifyVerifoneRequest(profileId: any, url: any, method: any, body: any): Readonly<{
24
+ verdict: "pass";
25
+ }> | Readonly<{
26
+ verdict: "abort";
27
+ reason: any;
28
+ }> | Readonly<{
29
+ verdict: "pause";
30
+ leg: any;
31
+ }>;
32
+ declare function verifoneLegName(profileId: any, url: any, method: any, checkoutOrigin: any): any;
33
+ declare function verifonePlaceholderBlob(profileId: any, request: any): string | null;
34
+ declare function encryptVerifoneCard(card: any, request: any, facilities: any): Promise<string>;
35
+ declare function verifoneSubstitution(profileId: any, blob: any): {
36
+ encoding: string;
37
+ kind: string;
38
+ profile: any;
39
+ blob: string;
40
+ };
41
+ declare function substituteVerifoneBlob(request: any, sub: any): string;
42
+ declare function verifoneApprovedBlobRefusal(blob: any, context: any): "verifone_substitution_invalid" | "verifone_approval_context_invalid" | "verifone_profile_unknown" | "verifone_request_invalid" | "verifone_key_unpinned" | "verifone_environment_mismatch" | "verifone_key_mismatch" | "verifone_blob_unchanged" | null;
43
+ declare function isPreparedVerifoneRequest(url: any, method: any, body: any, profileId: any): boolean;
44
+ declare function verifoneProfileEndpointFor(profileId: any, checkoutOrigin: any): string | null;
45
+ declare function verifoneProfileTemplatedUrl(profileId: any, url: any): string;
46
+ declare function verifoneProfileUrlGlobs(profileId: any): unknown[];
47
+ declare function verifoneProfileAmountPath(profileId: any): any[] | null;
48
+ declare function verifoneProfileEnvironment(profileId: any): any;
49
+ declare function verifoneMerchantProfile(id: any): any;
50
+ declare function verifoneProfileRulesMatch(profileId: any, served: any): boolean;
51
+ export { VERIFONE_BLOB_MAX, VERIFONE_BLOB_PREFIX, VERIFONE_CLOCK_SKEW_MS, VERIFONE_KEY_PINS, VERIFONE_MERCHANT_PROFILES, VERIFONE_PROFILE_RECOGNIZERS, VERIFONE_SUBSTITUTION, VERIFONE_TEST_PANS, carriesVerifoneBlob, classifyVerifoneRequest, encryptVerifoneCard, isPreparedVerifoneRequest, isVerifoneBlob, substituteVerifoneBlob, verifoneApprovedBlobRefusal, verifoneBlobInfo, verifoneCardPlaintext, verifoneClockSkewRefusal, verifoneKeyPinShapeRefusal, verifoneLegName, verifoneMerchantProfile, verifonePlaceholderBlob, verifoneProfileAmountPath, verifoneProfileEndpointFor, verifoneProfileEnvironment, verifoneProfileRefusal, verifoneProfileRulesMatch, verifoneProfileTemplatedUrl, verifoneProfileUrlGlobs, verifoneSubstitution };