@agent-cards/sdk 0.23.28 → 0.24.1

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 (110) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/PREFLIGHT.md +7 -1
  3. package/README.md +197 -102
  4. package/dist/aci.d.ts +52 -22
  5. package/dist/aci.js +79 -15
  6. package/dist/adyen-merchant-hosted.generated.d.ts +2 -2
  7. package/dist/adyen-merchant-hosted.generated.js +586 -55
  8. package/dist/agent-amount.generated.js +1 -1
  9. package/dist/agentcard.d.ts +4 -5
  10. package/dist/agentcard.js +4 -5
  11. package/dist/amadeus.d.ts +9 -3
  12. package/dist/amadeus.js +33 -4
  13. package/dist/api-module.d.ts +1 -7
  14. package/dist/api-module.js +3 -5
  15. package/dist/{voice-retry.js → api-retry.js} +1 -1
  16. package/dist/api-types.d.ts +53 -0
  17. package/dist/{voice-types.js → api-types.js} +7 -2
  18. package/dist/approvals.d.ts +166 -0
  19. package/dist/approvals.js +213 -0
  20. package/dist/aurus.d.ts +33 -0
  21. package/dist/aurus.js +75 -0
  22. package/dist/builtin-registry.generated.js +1 -1
  23. package/dist/buy.d.ts +20 -101
  24. package/dist/buy.js +19 -74
  25. package/dist/capabilities.generated.js +1 -1
  26. package/dist/card-fields.generated.js +2864 -249
  27. package/dist/cardlink-vpos.d.ts +43 -0
  28. package/dist/cardlink-vpos.js +90 -0
  29. package/dist/cdp.d.ts +3 -13
  30. package/dist/cdp.js +244 -175
  31. package/dist/checkout-com.generated.js +5 -1
  32. package/dist/client.d.ts +170 -22
  33. package/dist/client.js +281 -40
  34. package/dist/creditguard.generated.d.ts +4 -4
  35. package/dist/cybersource-flex.generated.d.ts +39 -1
  36. package/dist/cybersource-flex.generated.js +100 -11
  37. package/dist/datatrans.generated.js +22 -1
  38. package/dist/eghl.d.ts +40 -0
  39. package/dist/eghl.js +72 -0
  40. package/dist/elavon-converge.generated.js +5 -3
  41. package/dist/gmo.generated.js +7 -2
  42. package/dist/hold.d.ts +8 -0
  43. package/dist/hold.js +43 -2
  44. package/dist/hosted-entry.js +4 -0
  45. package/dist/index.d.ts +9 -15
  46. package/dist/index.js +6 -10
  47. package/dist/lifecycle.d.ts +9 -61
  48. package/dist/lifecycle.js +24 -10
  49. package/dist/merchant-card-endpoints.generated.d.ts +6 -1
  50. package/dist/merchant-card-endpoints.generated.js +344 -19
  51. package/dist/merchant-hosted.d.ts +8 -1
  52. package/dist/nike-order.d.ts +51 -0
  53. package/dist/nike-order.js +81 -0
  54. package/dist/oceanpayment.d.ts +38 -0
  55. package/dist/oceanpayment.js +70 -0
  56. package/dist/paddle-tokenex.generated.js +111 -11
  57. package/dist/payplug.d.ts +41 -0
  58. package/dist/payplug.js +71 -0
  59. package/dist/payrails.generated.js +33 -8
  60. package/dist/pre-claim.d.ts +11 -0
  61. package/dist/pre-claim.js +130 -7
  62. package/dist/preflight-capabilities.generated.d.ts +0 -4
  63. package/dist/preflight-capabilities.generated.js +0 -5
  64. package/dist/preflight-catalog.json +200 -6
  65. package/dist/preflight-playwright.d.ts +2 -2
  66. package/dist/preflight-playwright.js +63 -14
  67. package/dist/preflight-schemas.json +18 -0
  68. package/dist/preflight.generated.d.ts +5 -4
  69. package/dist/preflight.generated.js +63 -3
  70. package/dist/preparation.d.ts +7 -0
  71. package/dist/preparation.js +21 -0
  72. package/dist/prepared-processor.d.ts +18 -5
  73. package/dist/prepared-processor.js +48 -8
  74. package/dist/primer.generated.js +2889 -268
  75. package/dist/processout.generated.d.ts +61 -0
  76. package/dist/processout.generated.js +577 -0
  77. package/dist/requests.js +1 -1
  78. package/dist/shiji-sts.generated.js +111 -11
  79. package/dist/stripe-checkout.generated.js +2761 -319
  80. package/dist/substitutions.generated.js +553 -26
  81. package/dist/tap-payments.generated.js +111 -11
  82. package/dist/twoc2p.generated.js +111 -11
  83. package/dist/url-masks/bluesnap.d.ts +2 -0
  84. package/dist/url-masks/bluesnap.js +15 -0
  85. package/dist/url-masks/oceanpayment.d.ts +2 -0
  86. package/dist/url-masks/oceanpayment.js +13 -0
  87. package/dist/url-masks/payplug.d.ts +2 -0
  88. package/dist/url-masks/payplug.js +13 -0
  89. package/dist/url-masks/vtex_payments.d.ts +2 -0
  90. package/dist/url-masks/vtex_payments.js +12 -0
  91. package/dist/url-masks.generated.d.ts +1 -1
  92. package/dist/url-masks.generated.js +6 -2
  93. package/dist/verifone.generated.js +42 -2
  94. package/dist/vtex-payments.generated.d.ts +127 -0
  95. package/dist/vtex-payments.generated.js +544 -0
  96. package/dist/webhooks.d.ts +2 -2
  97. package/dist/webhooks.js +2 -2
  98. package/examples/preflight/kernel-native/inventory.json +1 -1
  99. package/package.json +6 -8
  100. package/dist/languages.d.ts +0 -23
  101. package/dist/languages.js +0 -76
  102. package/dist/voice-pkce.d.ts +0 -49
  103. package/dist/voice-pkce.js +0 -147
  104. package/dist/voice-stream.generated.d.ts +0 -118
  105. package/dist/voice-stream.generated.js +0 -170
  106. package/dist/voice-types.d.ts +0 -735
  107. package/dist/voice.d.ts +0 -373
  108. package/dist/voice.js +0 -815
  109. package/readme.test.mjs +0 -53
  110. /package/dist/{voice-retry.d.ts → api-retry.d.ts} +0 -0
@@ -385,6 +385,7 @@
385
385
  "mollie-components-v1-script",
386
386
  "mollie-hosted-credit-card",
387
387
  "braintree-web-sdk",
388
+ "braintree-web-wallet",
388
389
  "braintree-hosted-fields",
389
390
  "paysafe-js",
390
391
  "worldpay-checkout-sdk",
@@ -466,6 +467,8 @@
466
467
  "mercado-pago-checkout-pro-card-form",
467
468
  "gravy-secure-fields-script",
468
469
  "gravy-secure-fields-script-sandbox",
470
+ "bambora-na-custom-checkout-fields",
471
+ "worldpay-wpg-hosted-payment-page",
469
472
  "paypal-hosted-fields-payments-sdk",
470
473
  "paypal-card-fields-v5",
471
474
  "paypal-card-fields-v6",
@@ -481,6 +484,10 @@
481
484
  "gmo-link-hosted-page",
482
485
  "cybersource-flex-microform-script",
483
486
  "cybersource-flex-microform-field",
487
+ "cybersource-unified-checkout-script",
488
+ "cybersource-unified-checkout-frame",
489
+ "aurus-ecom-iframe",
490
+ "aurus-ecom-init-script",
484
491
  "juspay-hypercheckout-payment-page",
485
492
  "pelecard-payment-page",
486
493
  "pelecard-payment-script",
@@ -524,6 +531,8 @@
524
531
  "gmo-link-hosted-page-test",
525
532
  "cybersource-flex-microform-script-test",
526
533
  "cybersource-flex-microform-field-test",
534
+ "cybersource-unified-checkout-script-test",
535
+ "cybersource-unified-checkout-frame-test",
527
536
  "cybersource-secure-acceptance-silent-order-post-test",
528
537
  "cybersource-secure-acceptance-silent-order-post-in",
529
538
  "worldpay-eprotect-js-api-prelive",
@@ -1021,6 +1030,7 @@
1021
1030
  "mollie-components-v1-script",
1022
1031
  "mollie-hosted-credit-card",
1023
1032
  "braintree-web-sdk",
1033
+ "braintree-web-wallet",
1024
1034
  "braintree-hosted-fields",
1025
1035
  "paysafe-js",
1026
1036
  "worldpay-checkout-sdk",
@@ -1102,6 +1112,8 @@
1102
1112
  "mercado-pago-checkout-pro-card-form",
1103
1113
  "gravy-secure-fields-script",
1104
1114
  "gravy-secure-fields-script-sandbox",
1115
+ "bambora-na-custom-checkout-fields",
1116
+ "worldpay-wpg-hosted-payment-page",
1105
1117
  "paypal-hosted-fields-payments-sdk",
1106
1118
  "paypal-card-fields-v5",
1107
1119
  "paypal-card-fields-v6",
@@ -1117,6 +1129,10 @@
1117
1129
  "gmo-link-hosted-page",
1118
1130
  "cybersource-flex-microform-script",
1119
1131
  "cybersource-flex-microform-field",
1132
+ "cybersource-unified-checkout-script",
1133
+ "cybersource-unified-checkout-frame",
1134
+ "aurus-ecom-iframe",
1135
+ "aurus-ecom-init-script",
1120
1136
  "juspay-hypercheckout-payment-page",
1121
1137
  "pelecard-payment-page",
1122
1138
  "pelecard-payment-script",
@@ -1160,6 +1176,8 @@
1160
1176
  "gmo-link-hosted-page-test",
1161
1177
  "cybersource-flex-microform-script-test",
1162
1178
  "cybersource-flex-microform-field-test",
1179
+ "cybersource-unified-checkout-script-test",
1180
+ "cybersource-unified-checkout-frame-test",
1163
1181
  "cybersource-secure-acceptance-silent-order-post-test",
1164
1182
  "cybersource-secure-acceptance-silent-order-post-in",
1165
1183
  "worldpay-eprotect-js-api-prelive",
@@ -148,6 +148,11 @@ export declare function getCheckoutPreflightCatalog(): {
148
148
  path_pattern: string;
149
149
  flow?: string;
150
150
  variant_unverified?: string;
151
+ /**
152
+ * Why the asset never takes a card: a wallet button, a fraud collector, or the client those load. When no card frame
153
+ * is active and scripts alone name several processors, one whose every asset says this is not the card form's.
154
+ */
155
+ card_free?: string;
151
156
  }[];
152
157
  processors: readonly [{
153
158
  readonly psp: "spreedly";
@@ -1340,10 +1345,6 @@ export declare function getCheckoutPreflightCatalog(): {
1340
1345
  readonly psp: "vgs";
1341
1346
  readonly reason: "iframe_bound_tokenization";
1342
1347
  readonly action: "human_checkout";
1343
- }, {
1344
- readonly psp: "bluesnap";
1345
- readonly reason: "multi_request_tokenization_not_supported";
1346
- readonly action: "human_checkout";
1347
1348
  }, {
1348
1349
  readonly psp: "stripe";
1349
1350
  readonly reason: "unbound_token_to_payment_intent_continuation";
@@ -1,5 +1,5 @@
1
1
  // Generated from @agent-cards/payment-core. Do not edit.
2
- // source-sha256: 639ffa689e8dfcf093eeba7e34b86c570e53ab17970976ae462a41fd2120d753
2
+ // source-sha256: 1ac6ffa819d0bb72040d0a95cac79f780aeae4f5dea22599fbc4c5ec6c6c8687
3
3
  import { CHECKOUT_PREFLIGHT_CAPABILITIES } from './preflight-capabilities.generated.js';
4
4
  /** Advisory page discovery. These rules never admit a payment destination. */
5
5
  export const CHECKOUT_PREFLIGHT_VERSION = '1';
@@ -23,7 +23,13 @@ const RULES = [
23
23
  // A braintree-web build whose version carries -payments-sdk (3.32.0-payments-sdk-dev) is PayPal's
24
24
  // (paypal-hosted-fields-payments-sdk below): its card goes to PayPal, never to Braintree, so neither
25
25
  // Braintree rule takes one.
26
- { id: 'braintree-web-sdk', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)[^/]+\/js\//, origin: 'https://js.braintreegateway.com', path_pattern: '/web/:version/js/*' },
26
+ { id: 'braintree-web-sdk', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)[^/]+\/js\/(?!(?:client|paypal|paypal-checkout|venmo|apple-pay|google-payment|data-collector|local-payment)(?:\.min)?\.js$)/, origin: 'https://js.braintreegateway.com', path_pattern: '/web/:version/js/*' },
27
+ // braintree-web's client and its wallet and fraud components take no card: a page that loads only these runs a
28
+ // PayPal, Venmo, Apple Pay or Google Pay button through Braintree, and its card boxes belong to another processor.
29
+ // AutoZone's checkout loads client, paypal-checkout and data-collector 3.125.0 beside Worldpay eProtect's JS API, whose
30
+ // script reads the card from the page's own fields (2026-10-31).
31
+ { id: 'braintree-web-wallet', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)[^/]+\/js\/(?:client|paypal|paypal-checkout|venmo|apple-pay|google-payment|data-collector|local-payment)(?:\.min)?\.js$/, origin: 'https://js.braintreegateway.com', path_pattern: '/web/:version/js/{client,paypal,paypal-checkout,venmo,apple-pay,google-payment,data-collector,local-payment}[.min].js',
32
+ card_free: 'braintree-web\'s client, wallet and fraud components collect no card; Hosted Fields and Drop-in do.' },
27
33
  { id: 'braintree-hosted-fields', psp: 'braintree', kinds: ['frame', 'iframe'], hostname: /^assets\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)/, origin: 'https://assets.braintreegateway.com', path_pattern: '/web/*' },
28
34
  { id: 'paysafe-js', psp: 'paysafe', kinds: ['script', 'frame', 'iframe'], hostname: /^hosted\.paysafe\.com$/, pathname: /^\/(?:js|checkout)\//, origin: 'https://hosted.paysafe.com', path_pattern: '/{js,checkout}/*' },
29
35
  { id: 'worldpay-checkout-sdk', psp: 'worldpay', kinds: ['script', 'frame', 'iframe'], hostname: /^access\.worldpay\.com$/, pathname: /^\/access-checkout\//, origin: 'https://access.worldpay.com', path_pattern: '/access-checkout/*' },
@@ -69,6 +75,29 @@ RULES.push({ id: 'tranzila-terminal-hosted-page', psp: 'tranzila', kinds: ['docu
69
75
  { id: 'gravy-secure-fields-script', psp: 'gravy', kinds: ['script'], hostname: /^cdn\.[a-z0-9-]+\.gr4vy\.app$/, pathname: GRAVY_SECURE_FIELDS_SCRIPT, origin: 'https://cdn.:merchant.gr4vy.app', path_pattern: '/secure-fields/{latest,:version}/secure-fields.js',
70
76
  sources: GRAVY_SOURCES }, { id: 'gravy-secure-fields-script-sandbox', psp: 'gravy', kinds: ['script'], hostname: /^cdn\.sandbox\.[a-z0-9-]+\.gr4vy\.app$/, pathname: GRAVY_SECURE_FIELDS_SCRIPT, origin: 'https://cdn.sandbox.:merchant.gr4vy.app', path_pattern: '/secure-fields/{latest,:version}/secure-fields.js',
71
77
  sources: GRAVY_SOURCES });
78
+ // Card frames the catalog scan of 2026-10-23 read at stores' card forms (no card typed, nothing paid). Each
79
+ // rule names its processor from a frame on the processor's own host. A file on the store's own host that only
80
+ // carries the processor's name, such as the copy of Bambora's customcheckout.js that Kent serves itself, names
81
+ // nothing.
82
+ const catalogScan = (reference) => ({ kind: 'saved_observation', reference: `catalog-psp-scan-2026-10-23:${reference}`, reviewed_on: '2026-10-23' });
83
+ RULES.push(
84
+ // Bambora Custom Checkout draws each card field as an iframe from the release its library was built for:
85
+ // /customcheckout/1/customcheckout.js draws 1.0.5-39 (the reviewed rule above), and Kent's own copy draws
86
+ // 1.0.2. The release names the frame and nothing more. Payment-core's bambora entry matches the card request
87
+ // on its own (/scripts/tokenization/tokens), so a release whose frame sent the card anywhere else would send
88
+ // only the placeholder card the agent typed.
89
+ { id: 'bambora-na-custom-checkout-fields', psp: 'bambora', kinds: ['frame', 'iframe', 'document'], hostname: /^libs\.na\.bambora\.com$/,
90
+ pathname: /^\/customcheckout\/1\.[0-9]{1,3}\.[0-9]{1,3}(?:-[0-9]{1,4})?\/iframe\.html$/, origin: 'https://libs.na.bambora.com', path_pattern: '/customcheckout/1.:minor.:patch{,-:build}/iframe.html',
91
+ sources: [{ kind: 'primary_source', reference: 'https://docs.na.worldline-solutions.com/build-your-integration/checkout-form/custom-checkout/reference', reviewed_on: '2026-09-11' },
92
+ catalogScan('kent-ca-custom-checkout-1.0.2-card-fields')] },
93
+ // Worldpay's hosted payment pages on payments.worldpay.com: Razer's checkout opens
94
+ // /app/hpp-iframe/integration/wpg/corporate in an iframe, which lands on /app/hpp/<build>/payment/start, where
95
+ // the shopper types the card on Worldpay's own page. The SDK's worldpay entry takes over Access Checkout's card
96
+ // session only, so the page names worldpay and stays unknown, as the secure.worldpay.com/wcc pages do.
97
+ { id: 'worldpay-wpg-hosted-payment-page', psp: 'worldpay', kinds: ['document', 'frame', 'iframe'], hostname: /^payments\.worldpay\.com$/,
98
+ pathname: /^\/app\/hpp(?:-iframe)?\//, origin: 'https://payments.worldpay.com', path_pattern: '/app/{hpp,hpp-iframe}/*',
99
+ sources: [catalogScan('razer-worldpay-hosted-payment-page')],
100
+ variant_unverified: 'The implemented Worldpay adapter covers Access Checkout card sessions; Worldpay hosted payment pages under payments.worldpay.com/app/hpp take the card on Worldpay\'s own page and are not admitted.' });
72
101
  // PayPal's legacy Hosted Fields (its JS SDK's components=hosted-fields), reviewed 2026-10-07 from Planet Golf
73
102
  // UK's payment step (read live 2026-10-06, SDK 5.0.580) and the two frame files it loads. The SDK renders each
74
103
  // card field as braintree-web's 3.32.0-payments-sdk-dev hosted-fields-input-frame.min.html (sha256
@@ -174,6 +203,27 @@ const cybersourceSources = [
174
203
  RULES.push({ id: 'cybersource-flex-microform-script', psp: 'cybersource_flex', kinds: ['script'], hostname: /^flex\.cybersource\.com$/, pathname: /^\/microform\/bundle\/v2(?:\.[0-9]+\.[0-9]+)?\/flex-microform\.min\.js$/, origin: 'https://flex.cybersource.com', path_pattern: '/microform/bundle/v2{,.:minor.:patch}/flex-microform.min.js',
175
204
  sources: cybersourceSources }, { id: 'cybersource-flex-microform-field', psp: 'cybersource_flex', kinds: ['frame', 'iframe', 'document'], hostname: /^flex\.cybersource\.com$/, pathname: /^\/microform\/bundle\/v2\.[0-9]+\.[0-9]+\/iframe\.html$/, origin: 'https://flex.cybersource.com', path_pattern: '/microform/bundle/v:version/iframe.html',
176
205
  sources: cybersourceSources });
206
+ // CyberSource Unified Checkout v1 sends the card to the same Flex v2 tokens URL, sealed by its orchestrator frame
207
+ // under the capture context (cybersource-unified-checkout.js), so its script and frames name cybersource_flex too.
208
+ // Reviewed 2026-10-31 from napaonline.com's checkout (UnifiedCheckout.js v1.8.2), with no card sent.
209
+ const unifiedCheckoutSources = [
210
+ { kind: 'saved_observation', reference: 'napaonline-2026-10-31:unified-checkout-v1.8.2-mce-orc-ga-frames', reviewed_on: '2026-10-31' },
211
+ { kind: 'official_documentation', reference: 'https://developer.cybersource.com/docs/cybs/en-us/unified-checkout/developer/all/rest/unified-checkout/uc-intro.html', reviewed_on: '2026-10-31' },
212
+ ];
213
+ RULES.push({ id: 'cybersource-unified-checkout-script', psp: 'cybersource_flex', kinds: ['script'], hostname: /^up\.cybersource\.com$/, pathname: /^\/uc\/v1\/assets\/[A-Za-z0-9_-]{16,1024}\/UnifiedCheckout\.js$/, origin: 'https://up.cybersource.com', path_pattern: '/uc/v1/assets/:asset/UnifiedCheckout.js',
214
+ sources: unifiedCheckoutSources }, { id: 'cybersource-unified-checkout-frame', psp: 'cybersource_flex', kinds: ['frame', 'iframe', 'document'], hostname: /^up\.cybersource\.com$/, pathname: /^\/uc\/v1\/assets\/[A-Za-z0-9_-]{16,1024}\/(?:mce\/mce|orc\/orc|ga\/ga)\.html$/, origin: 'https://up.cybersource.com', path_pattern: '/uc/v1/assets/:asset/{mce,orc,ga}/*.html',
215
+ sources: unifiedCheckoutSources });
216
+ // Aurus Pay's ecom card frame (processors/aurus.js): the store frames /storeservices/ecom/getiframe on
217
+ // pecst<NN>.aurusepay.com, the card fields sit inside it, and its own ecominitjs sends the sealed card to
218
+ // /storeservices/ecom/oneTimeToken on the same host. Reviewed 2026-10-31 at oreillyauto.com's payment step, with every
219
+ // aurusepay.com request but a GET aborted and only a public test card typed. Aurus is served dark, so no catalog
220
+ // processor entry backs the candidate and the result stays unknown unless the caller is served Aurus.
221
+ const aurusSources = [
222
+ { kind: 'saved_observation', reference: 'oreillyauto-2026-10-31:aurus-ecom-getiframe-oneTimeToken', reviewed_on: '2026-10-31' },
223
+ ];
224
+ RULES.push({ id: 'aurus-ecom-iframe', psp: 'aurus', kinds: ['frame', 'iframe', 'document'], hostname: /^pecst[0-9]{2}\.aurusepay\.com$/, pathname: /^\/storeservices\/ecom\/getiframe$/, origin: 'https://pecst01.aurusepay.com', path_pattern: '/storeservices/ecom/getiframe',
225
+ sources: aurusSources }, { id: 'aurus-ecom-init-script', psp: 'aurus', kinds: ['script'], hostname: /^pecst[0-9]{2}\.aurusepay\.com$/, pathname: /^\/storeservices\/ecom\/ecominitjs$/, origin: 'https://pecst01.aurusepay.com', path_pattern: '/storeservices/ecom/ecominitjs',
226
+ sources: aurusSources });
177
227
  // Juspay HyperCheckout: Juspay's own hosted payment page (the Session API's payment_links.web),
178
228
  // which a merchant embeds in an iframe (IndiGo's payment step). The card request that page makes
179
229
  // has not been observed, so the Vault does not take it over, and the rule tells the agent so
@@ -359,6 +409,8 @@ for (const [baseId, suffix, hostname, origin] of [
359
409
  ['gmo-link-hosted-page', 'test', /^pt01\.mul-pay\.jp$/, 'https://pt01.mul-pay.jp'],
360
410
  ['cybersource-flex-microform-script', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
361
411
  ['cybersource-flex-microform-field', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
412
+ ['cybersource-unified-checkout-script', 'test', /^testup\.cybersource\.com$/, 'https://testup.cybersource.com'],
413
+ ['cybersource-unified-checkout-frame', 'test', /^testup\.cybersource\.com$/, 'https://testup.cybersource.com'],
362
414
  // Secure Acceptance's test host and its production host in India serve the same /silent/ paths.
363
415
  ['cybersource-secure-acceptance-silent-order-post', 'test', /^testsecureacceptance\.cybersource\.com$/, 'https://testsecureacceptance.cybersource.com'],
364
416
  ['cybersource-secure-acceptance-silent-order-post', 'in', /^secureacceptance\.in\.cybersource\.com$/, 'https://secureacceptance.in.cybersource.com'],
@@ -765,19 +817,27 @@ export function assessCheckoutSupport(input, options = {}) {
765
817
  result.candidates = [...candidates.values()].sort((a, b) => a.psp.localeCompare(b.psp));
766
818
  if (missingCorroboration && activeFlows.size === 0)
767
819
  result.reason_codes.push('flow_corroboration_missing');
820
+ // Scripts alone naming several processors: one whose every asset is card-free (a wallet button, a fraud collector)
821
+ // is not the card form's. When exactly one other is left, the page's card form is that one's.
822
+ const cardCapable = activePsps.size === 0 && candidates.size > 1
823
+ ? [...candidates.values()].filter(candidate => candidate.evidence.some(evidence => !RULE_BY_ID.get(evidence.rule_id).card_free))
824
+ : [];
825
+ const cardFormPsp = cardCapable.length === 1 ? cardCapable[0].psp : null;
768
826
  if (activePsps.size === 1)
769
827
  result.psp = [...activePsps][0];
770
828
  // Script-only evidence names the processor only when the scan saw everything. On a partial scan
771
829
  // the sole candidate stays listed as undetermined instead of becoming the selected route.
772
830
  else if (activePsps.size === 0 && candidates.size === 1 && result.observation.complete)
773
831
  result.psp = [...candidates.keys()][0];
832
+ else if (cardFormPsp !== null && result.observation.complete)
833
+ result.psp = cardFormPsp;
774
834
  if (!result.observation.complete)
775
835
  result.reason_codes.push('observation_incomplete');
776
836
  if (!input.signals.length) {
777
837
  result.reason_codes.push('psp_not_detected');
778
838
  result.missing_evidence.push('payment_page_signal');
779
839
  }
780
- if (activePsps.size > 1 || activeFlows.size > 1 || activeFlowFrames.size > 1 || activePsps.size === 0 && candidates.size > 1)
840
+ if (activePsps.size > 1 || activeFlows.size > 1 || activeFlowFrames.size > 1 || activePsps.size === 0 && candidates.size > 1 && cardFormPsp === null)
781
841
  result.reason_codes.push('ambiguous_checkout');
782
842
  if (activeFlows.size !== 1 || result.psp === null) {
783
843
  result.reason_codes.push('checkout_flow_unidentified');
@@ -25,6 +25,13 @@ export declare class PreparationGate {
25
25
  * own method is what binds: a POST for most processors, the registration GET for Worldpay eProtect.
26
26
  */
27
27
  claim(requestUrl: string, requestMethod: string, requestBody?: string | null, requestHeaders?: Record<string, string>): PreparedCheckout | undefined;
28
+ /**
29
+ * Whether a same-document navigation of the prepared page to `url` leaves the document this
30
+ * preparation read: the same origin, path and query, whatever the fragment. A single-page checkout
31
+ * that calls history.replaceState with its own URL after each server action (Al's on VTEX) keeps
32
+ * its preparation; a history update to another path or query does not.
33
+ */
34
+ keepsDocument(url: unknown): boolean;
28
35
  assertDocument(): Promise<void>;
29
36
  private readDocument;
30
37
  isEngaged(): boolean;
@@ -3,6 +3,17 @@ import { amountRefusal, validAmountInput } from './client.js';
3
3
  import { matchesPreparation, validPreparationEnvironment, preparationEndpoint, preparationBindsByShape, preparationRequestMethod, preparedRequestRefusal } from './prepared-processor.js';
4
4
  import { SECURE_ACCEPTANCE_PSP, secureAcceptanceCheckoutProfile } from './cybersource-secure-acceptance.js';
5
5
  import { creditguardPageProcessUrl, creditguardProcessTxId } from './creditguard.generated.js';
6
+ /** A page URL without its fragment, or undefined when it does not parse. */
7
+ function documentWithoutFragment(url) {
8
+ try {
9
+ const parsed = new URL(url);
10
+ parsed.hash = '';
11
+ return parsed.href;
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ }
6
17
  /** A local, one-use rendezvous. It never starts or retries a merchant request. */
7
18
  export class PreparationGate {
8
19
  opts;
@@ -195,6 +206,16 @@ export class PreparationGate {
195
206
  clearTimeout(this.expiryTimer);
196
207
  return prepared;
197
208
  }
209
+ /**
210
+ * Whether a same-document navigation of the prepared page to `url` leaves the document this
211
+ * preparation read: the same origin, path and query, whatever the fragment. A single-page checkout
212
+ * that calls history.replaceState with its own URL after each server action (Al's on VTEX) keeps
213
+ * its preparation; a history update to another path or query does not.
214
+ */
215
+ keepsDocument(url) {
216
+ const kept = typeof url === 'string' ? documentWithoutFragment(url) : undefined;
217
+ return kept !== undefined && this.documentUrl !== '' && kept === documentWithoutFragment(this.documentUrl);
218
+ }
198
219
  async assertDocument() {
199
220
  if (this.documentUrl && await this.readDocument() !== this.documentUrl)
200
221
  throw new CheckoutPreparationError(this.prepared?.id ?? null, 'merchant_document_changed');
@@ -37,8 +37,20 @@
37
37
  * approval after Pay. Its environments are its API hosts: api.openpay.<mx|co|pe> (production) and
38
38
  * sandbox-api.openpay.<mx|co|pe> (sandbox). The token URL names the merchant in its path, so no URL is
39
39
  * prepared in advance: the claim binds the request by its host's environment and its shape, as ACI's.
40
+ *
41
+ * vtex_payments: prepared only. Ollie's headless checkout aborts its card post to the store's VTEX vault 12 seconds after
42
+ * it starts it, too soon for an approval after Pay. Its one environment is production: VTEX has no test host. The card
43
+ * URL names the store's account in its host and VTEX's transaction in its path, neither known before Pay, so the claim
44
+ * binds the request by its shape, as Openpay's. Ollie sends the same post again on a non-OK answer or a network error,
45
+ * then once more to api.vtexvault.com: the first post spends the approval, and each later one is refused like any
46
+ * request on a spent preparation, so one approval is one card use.
47
+ *
48
+ * processout: prepared only. ProcessOut.js gives its card frame 20 seconds from the tokenize message before it calls
49
+ * the page's error callback, too soon for an approval after the shop's continue press. Its environments share one
50
+ * host, api.processout.com: the project id in the card URL's query names them, proj_ (production) and test-proj_
51
+ * (sandbox), so the claim binds the request by its project's environment and its shape, as Openpay's.
40
52
  */
41
- export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com' | 'paysafe' | 'fiserv' | 'cybersource_flex' | 'aci' | 'creditguard' | 'worldpay_eprotect' | 'amadeus_checkout' | 'verifone' | 'cybersource_secure_acceptance' | 'paddle_vgs' | 'wix_payments' | 'paygent' | 'hipay' | 'openpay_bbva';
53
+ export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com' | 'paysafe' | 'fiserv' | 'cybersource_flex' | 'aci' | 'creditguard' | 'worldpay_eprotect' | 'amadeus_checkout' | 'verifone' | 'cybersource_secure_acceptance' | 'paddle_vgs' | 'wix_payments' | 'paygent' | 'hipay' | 'openpay_bbva' | 'vtex_payments' | 'processout';
42
54
  export type PreparationEnvironment = 'production' | 'sandbox' | 'shared';
43
55
  /**
44
56
  * How the approved card reaches the processor: the device's own request (token), or
@@ -77,7 +89,8 @@ export declare function preparedApprovalAnswersRetry(psp: string): boolean;
77
89
  * live, testsecureacceptance is test. So are Paddle VGS's two card URLs: vault is live,
78
90
  * sandbox-vault is test. So are Paygent's two token hosts: token is live, sandbox is test. So are
79
91
  * HiPay's two vault hosts: secure2-vault is live, stage-secure2-vault is test. So are Openpay's API
80
- * hosts: api.openpay.<mx|co|pe> is live, sandbox-api.openpay.<mx|co|pe> is test.
92
+ * hosts: api.openpay.<mx|co|pe> is live, sandbox-api.openpay.<mx|co|pe> is test. ProcessOut's share
93
+ * one host, and the card URL's project id attests them: proj_ is live, test-proj_ is test.
81
94
  */
82
95
  export declare function validPreparationEnvironment(psp: string, environment: string): boolean;
83
96
  /**
@@ -90,9 +103,9 @@ export declare function aciPreparationUrl(environment: PreparationEnvironment):
90
103
  /**
91
104
  * A processor whose card request names an id the merchant mints only at Pay time (ACI's checkout
92
105
  * id), or the merchant's own id (Openpay's token URL carries the merchant id in its path and the
93
- * country in its host), so no URL can be prepared in advance: whether the adapters can intercept it
94
- * is asked of the registry (VaultClient.recognizesProcessor), not of the prepared endpoint, which
95
- * only names the preparation.
106
+ * country in its host, ProcessOut's card URL the project id in its query), so no URL can be prepared
107
+ * in advance: whether the adapters can intercept it is asked of the registry
108
+ * (VaultClient.recognizesProcessor), not of the prepared endpoint, which only names the preparation.
96
109
  */
97
110
  export declare function preparationBindsByShape(psp: string): boolean;
98
111
  /**
@@ -18,6 +18,8 @@ import { WIX_PAYMENTS_TOKENIZE_URL, isPreparedWixPaymentsRequest } from './wix-p
18
18
  import { paygentEnvironment, paygentPreparationUrl, paygentRequestRefusal } from './paygent.generated.js';
19
19
  import { HIPAY_TOKEN_URLS, hipayRequestRefusal, hipayTokenEnvironment, isPreparedHipayRequest } from './hipay.generated.js';
20
20
  import { OPENPAY_PREPARATION_URLS, isPreparedOpenpayRequest, openpayRequestRefusal, openpayTokenEnvironment } from './openpay.generated.js';
21
+ import { VTEX_PAYMENTS_PREPARATION_URL, isPreparedVtexPaymentsRequest, vtexPaymentsCardTarget, vtexPaymentsRequestRefusal } from './vtex-payments.generated.js';
22
+ import { PROCESSOUT_PREPARATION_URLS, isPreparedProcessoutRequest, processoutEnvironment, processoutRequestRefusal } from './processout.generated.js';
21
23
  export function preparationMode(psp) {
22
24
  return psp === 'adyen' || psp === 'fiserv' || psp === 'verifone' ? 'cse' : 'token';
23
25
  }
@@ -54,16 +56,17 @@ export function preparedApprovalAnswersRetry(psp) {
54
56
  * live, testsecureacceptance is test. So are Paddle VGS's two card URLs: vault is live,
55
57
  * sandbox-vault is test. So are Paygent's two token hosts: token is live, sandbox is test. So are
56
58
  * HiPay's two vault hosts: secure2-vault is live, stage-secure2-vault is test. So are Openpay's API
57
- * hosts: api.openpay.<mx|co|pe> is live, sandbox-api.openpay.<mx|co|pe> is test.
59
+ * hosts: api.openpay.<mx|co|pe> is live, sandbox-api.openpay.<mx|co|pe> is test. ProcessOut's share
60
+ * one host, and the card URL's project id attests them: proj_ is live, test-proj_ is test.
58
61
  */
59
62
  export function validPreparationEnvironment(psp, environment) {
60
63
  if (psp === 'bambora' || psp === 'mercado_pago' || psp === 'recurly' || psp === 'spreedly')
61
64
  return environment === 'shared';
62
- // Wix Payments has production alone: its tokenizer has no test host.
63
- if (psp === 'wix_payments')
65
+ // Wix Payments has production alone: its tokenizer has no test host. VTEX Payments too: every store's vault is live.
66
+ if (psp === 'wix_payments' || psp === 'vtex_payments')
64
67
  return environment === 'production';
65
68
  return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe', 'fiserv', 'cybersource_flex', 'aci', 'creditguard', 'worldpay_eprotect', 'amadeus_checkout', 'verifone',
66
- 'cybersource_secure_acceptance', 'paddle_vgs', 'paygent', 'hipay', 'openpay_bbva'].includes(psp) && ['production', 'sandbox'].includes(environment);
69
+ 'cybersource_secure_acceptance', 'paddle_vgs', 'paygent', 'hipay', 'openpay_bbva', 'processout'].includes(psp) && ['production', 'sandbox'].includes(environment);
67
70
  }
68
71
  /**
69
72
  * ACI's prepared endpoint, the literal the API names (backend checkout-prepared-processor.ts): its
@@ -77,12 +80,12 @@ export function aciPreparationUrl(environment) {
77
80
  /**
78
81
  * A processor whose card request names an id the merchant mints only at Pay time (ACI's checkout
79
82
  * id), or the merchant's own id (Openpay's token URL carries the merchant id in its path and the
80
- * country in its host), so no URL can be prepared in advance: whether the adapters can intercept it
81
- * is asked of the registry (VaultClient.recognizesProcessor), not of the prepared endpoint, which
82
- * only names the preparation.
83
+ * country in its host, ProcessOut's card URL the project id in its query), so no URL can be prepared
84
+ * in advance: whether the adapters can intercept it is asked of the registry
85
+ * (VaultClient.recognizesProcessor), not of the prepared endpoint, which only names the preparation.
83
86
  */
84
87
  export function preparationBindsByShape(psp) {
85
- return psp === 'aci' || psp === 'openpay_bbva';
88
+ return psp === 'aci' || psp === 'openpay_bbva' || psp === 'vtex_payments' || psp === 'processout';
86
89
  }
87
90
  /**
88
91
  * The endpoint a CreditGuard preparation stands for, per environment, as the API advertises it: the
@@ -124,6 +127,15 @@ export function preparationEndpoint(psp, environment) {
124
127
  // and the claim binds the real token URL by environment and shape (matchesPreparedRequest), as ACI's.
125
128
  if (psp === 'openpay_bbva')
126
129
  return OPENPAY_PREPARATION_URLS[environment];
130
+ // VTEX Payments: the literal that only names the preparation. Its "prepared" is no transaction id, so no card request
131
+ // is ever sent to it, and the claim binds the store's own card post by its shape (matchesPreparedRequest).
132
+ if (psp === 'vtex_payments')
133
+ return VTEX_PAYMENTS_PREPARATION_URL;
134
+ // ProcessOut: the literal the API names (backend checkout-prepared-processor.ts). It only names the preparation and its
135
+ // environment: its "prepared" is shorter than any project id, so no card request is ever sent to it, and the claim binds
136
+ // the real card URL by its project's environment and its shape (matchesPreparedRequest), as Openpay's.
137
+ if (psp === 'processout')
138
+ return PROCESSOUT_PREPARATION_URLS[environment];
127
139
  // A Verifone endpoint is its reviewed profile's leg, never one per environment (verifone.ts).
128
140
  if (psp === 'verifone')
129
141
  throw new Error('unsupported_preparation_processor');
@@ -304,6 +316,19 @@ export function matchesPreparedRequest(psp, environment, requestUrl, method, bod
304
316
  // merchant a later release reviews needs no new build of this library.
305
317
  if (psp === 'openpay_bbva')
306
318
  return isPreparedOpenpayRequest(requestUrl, method, body ?? null, environment, headers ?? {});
319
+ // VTEX Payments: a post to a store's own vault and payment-core's whole request rule (Ollie's JSON content type and
320
+ // one credit card line for the URL's transaction), the rule the API and the Vault bind with too. Ollie's resend to
321
+ // api.vtexvault.com never binds. Which store a request may reach is the API's to judge at the bind and the Vault's
322
+ // before it writes the card, against the reviewed pins each holds, so a store a later release reviews needs no new SDK.
323
+ if (psp === 'vtex_payments')
324
+ return isPreparedVtexPaymentsRequest(requestUrl, method, body ?? null, environment, headers ?? {});
325
+ // ProcessOut: a card URL whose project names the environment and payment-core's whole request rule (the JSON content
326
+ // type, the API version, the URL's own project credential and tokenizeCard's body), the rule the API and the Vault bind
327
+ // with too. Which project a live request may name is the API's to judge at the bind and the Vault's before it writes
328
+ // the card, against the reviewed pin each of them holds for the approved checkout origin, so a store a later release
329
+ // reviews needs no new build of this library.
330
+ if (psp === 'processout')
331
+ return isPreparedProcessoutRequest(requestUrl, method, body ?? null, environment, headers ?? {});
307
332
  if (psp === 'creditguard')
308
333
  return matchesPreparedCreditguard(environment, requestUrl, body, headers, binding);
309
334
  // A Verifone request is judged only through the profile its preparation names (matchesPreparation).
@@ -479,6 +504,21 @@ export function preparedRequestRefusal(psp, environment, requestUrl, body, heade
479
504
  return 'checkout_changed';
480
505
  return openpayRequestRefusal({ url: requestUrl, method: 'POST', headers, body: body ?? null }) ?? 'checkout_changed';
481
506
  }
507
+ // A VTEX post on a vault host names what payment-core's request rule refused (vtex_payments_fallback_refused for
508
+ // Ollie's resend to api.vtexvault.com, vtex_payments_installments, ...); any other request is `checkout_changed`.
509
+ if (psp === 'vtex_payments') {
510
+ if (environment !== 'production' || vtexPaymentsCardTarget(requestUrl) === null)
511
+ return 'checkout_changed';
512
+ return vtexPaymentsRequestRefusal({ url: requestUrl, method: 'POST', headers, body: body ?? null }) ?? 'checkout_changed';
513
+ }
514
+ // A ProcessOut card request whose project names the prepared environment names what payment-core's request rule
515
+ // refused (processout_headers_refused, processout_body_unsupported); the other environment's project, another query
516
+ // or path, or any other request is `checkout_changed`.
517
+ if (psp === 'processout') {
518
+ if (processoutEnvironment(requestUrl) !== environment)
519
+ return 'checkout_changed';
520
+ return processoutRequestRefusal({ url: requestUrl, method: 'POST', headers, body: body ?? null }) ?? 'checkout_changed';
521
+ }
482
522
  if (psp !== 'amadeus_checkout' || amadeusEnvironment(requestUrl) !== environment)
483
523
  return 'checkout_changed';
484
524
  const merchantHost = merchantHostOf(merchantOrigin);