@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,5 +1,5 @@
1
1
  // Generated from @agent-cards/payment-core. Do not edit.
2
- // source-sha256: 3a36f98936b5a93687dd4a052d1d4513935a093736b19475f023b5a0847c6b4a
2
+ // source-sha256: 3adb75196b940ea807d3290f096e585d6519543d18ec6426daf8b0031acbacb8
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';
@@ -68,6 +68,22 @@ RULES.push({ id: 'fiserv-commercehub-payment-fields', psp: 'fiserv', kinds: ['sc
68
68
  sources: [fiservSource('https://commercehub-checkout.fiservapps.com/sdk/3.8.14/payment-fields.js'), fiservSource('https://orangepay-ecommerce.hdpayments.homedepot.com/env.js')] }, { id: 'fiserv-commercehub-checkout-script', psp: 'fiserv', kinds: ['script'], hostname: /^commercehub-checkout\.fiservapps\.com$/, pathname: /^\/sdk\/[0-9]+\.[0-9]+\.[0-9]+\/checkout\.js$/, origin: 'https://commercehub-checkout.fiservapps.com', path_pattern: '/sdk/:version/checkout.js',
69
69
  sources: [fiservSource('https://developer.fiserv.com/product/CommerceHub/docs/Online-Commerce/Integration-Options/Hosted-Checkout/Hosted-Checkout-Version-Release')] }, { id: 'fiserv-secure-data-capture-frame', psp: 'fiserv', kinds: ['frame', 'iframe', 'document'], hostname: /^commercehub-secure-data-capture\.fiservapps\.com$/, pathname: /^\/sdk-secure\/[0-9]+\.[0-9]+\.[0-9]+\/iframe\.html$/, origin: 'https://commercehub-secure-data-capture.fiservapps.com', path_pattern: '/sdk-secure/:version/iframe.html',
70
70
  sources: [fiservSource('https://commercehub-checkout.fiservapps.com/sdk/3.8.14/payment-fields.js'), fiservSource('https://commercehub-secure-data-capture.fiservapps.com/sdk-secure/3.8.14/iframe.html')] });
71
+ // NMI Hosted Payment Page (formerly eKashu): the merchant's own entry form posts top-level to
72
+ // live.ekashu.com, and NMI's card page is that host's root document (integration guide AE; the
73
+ // archived card pages in test/fixtures/ekashu). NMI ships dark (its definition names a
74
+ // capability), so the bundled catalog declares no support for it: these identify ekashu and the
75
+ // result stays unknown.
76
+ const ekashuSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-30' });
77
+ RULES.push({ id: 'ekashu-hosted-payment-page', psp: 'ekashu', kinds: ['form', 'document'], hostname: /^live\.ekashu\.com$/, pathname: /^\/(?:index\.php)?$/, origin: 'https://live.ekashu.com', path_pattern: '/',
78
+ sources: [ekashuSource('https://www.nmi.com/wp-content/uploads/2024/06/Version-AE-eKashu-Hosted-payment-page-Integration-Guide.pdf'), ekashuSource('https://web.archive.org/web/20260307103902/https://live.ekashu.com/')] });
79
+ // Verifone eCommerce, read 2026-09-25 from Verifone's own hosts and docs: a merchant page
80
+ // that encrypts the card itself (Secure Card Capture) loads verifone.js from Verifone's
81
+ // jsclient host for its region, byte-identical on every host. Verifone ships dark (its
82
+ // definition names a capability), so the bundled catalog declares no support for it:
83
+ // the script identifies verifone and the result stays unknown.
84
+ const verifoneSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
85
+ RULES.push({ id: 'verifone-secure-card-capture-script', psp: 'verifone', kinds: ['script'], hostname: /^us\.jsclient\.verifone\.cloud$/, pathname: /^\/verifone\.js$/, origin: 'https://us.jsclient.verifone.cloud', path_pattern: '/verifone.js',
86
+ sources: [verifoneSource('https://us.jsclient.verifone.cloud/verifone.js'), verifoneSource('https://verifone.cloud/docs/online-payments/securecardcapture')] });
71
87
  // GMO Payment Gateway PG Multipayment (dark: its processor declares a capability, so the bundled
72
88
  // catalog does not list it and these signals stay unknown until a client opts in). Token v2's
73
89
  // mp-token.js identifies the processor. Token v1's ext/js/token.js (a JSONP GET), GMO Elements'
@@ -98,6 +114,173 @@ const cybersourceSources = [
98
114
  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',
99
115
  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',
100
116
  sources: cybersourceSources });
117
+ // Juspay HyperCheckout: Juspay's own hosted payment page (the Session API's payment_links.web),
118
+ // which a merchant embeds in an iframe (IndiGo's payment step). The card request that page makes
119
+ // has not been observed, so the Vault does not take it over, and the rule tells the agent so
120
+ // before it types a placeholder. Juspay is served dark, so no catalog processor entry backs the
121
+ // candidate and the result stays unknown either way.
122
+ RULES.push({ id: 'juspay-hypercheckout-payment-page', psp: 'juspay', kinds: ['document', 'frame', 'iframe'], hostname: /^api\.juspay\.in$/, pathname: /^\/orders\/ordeh_[A-Za-z0-9]+\/payment-page$/, origin: 'https://api.juspay.in', path_pattern: '/orders/:order/payment-page',
123
+ sources: [{ kind: 'primary_source', reference: 'https://juspay.io/in/docs/api-reference/docs/express-checkout/session', reviewed_on: '2026-09-23' }],
124
+ variant_unverified: 'The Juspay HyperCheckout payment page at /orders/:order/payment-page sends the card from Juspay\'s own page, a request the Vault does not take over.' });
125
+ // Pelecard Iframe/Redirect 2.0, reviewed from Pelecard's public payment page scripts and three archived
126
+ // payment pages on 2026-09-25. The processor ships dark: until the bundled catalog serves pelecard, a
127
+ // lone Pelecard signal names the processor and stays unknown. The legacy 1.0 page on
128
+ // gateway.pelecard.biz posts the card as a top-level form and stays unknown through `variant_unverified`.
129
+ const pelecardSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
130
+ RULES.push({ id: 'pelecard-payment-page', psp: 'pelecard', kinds: ['document', 'frame', 'iframe'], hostname: /^gateway21\.pelecard\.biz$/, pathname: /^\/PaymentGW\/?$/, origin: 'https://gateway21.pelecard.biz', path_pattern: '/PaymentGW',
131
+ sources: [pelecardSource('https://gateway21.pelecard.biz/ManualIframe/Chart'), pelecardSource('https://docs.pelecard.com/documentation/iframe-redirect-programmer-manual-version-06-2025/')] }, { id: 'pelecard-payment-script', psp: 'pelecard', kinds: ['script'], hostname: /^gateway21\.pelecard\.biz$/, pathname: /^\/Scripts\/Payment\/(?:Payment|General)\.js$/, origin: 'https://gateway21.pelecard.biz', path_pattern: '/Scripts/Payment/{Payment,General}.js',
132
+ sources: [pelecardSource('https://gateway21.pelecard.biz/Scripts/Payment/Payment.js'), pelecardSource('https://gateway21.pelecard.biz/Scripts/Payment/General.js')] }, { id: 'pelecard-legacy-payment-form', psp: 'pelecard', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^gateway\.pelecard\.biz$/, pathname: /^\/PayProcess\/Transaction$/, origin: 'https://gateway.pelecard.biz', path_pattern: '/PayProcess/Transaction',
133
+ sources: [pelecardSource('https://gateway.pelecard.biz/')],
134
+ variant_unverified: 'The implemented Pelecard adapter covers the Iframe/Redirect 2.0 card submission on gateway21 and gateway20.pelecard.biz; the legacy 1.0 page on gateway.pelecard.biz posts the card as a top-level form and is not admitted.' });
135
+ // ACI Worldwide COPYandPAY (Halfords), reviewed from ACI's own widget files on 2026-09-25. The
136
+ // processor ships dark: until the bundled catalog serves aci, a lone ACI signal names the
137
+ // processor and stays unknown. The widget script URL carries ?checkoutId=, which never matches.
138
+ const aciSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
139
+ RULES.push({ id: 'aci-copyandpay-widget-script', psp: 'aci', kinds: ['script'], hostname: /^eu-prod\.oppwa\.com$/, pathname: /^\/v1\/paymentWidgets\.js$/, origin: 'https://eu-prod.oppwa.com', path_pattern: '/v1/paymentWidgets.js',
140
+ sources: [aciSource('https://eu-prod.oppwa.com/v1/paymentWidgets.js'), aciSource('https://docs.aciworldwide.com/integration-guide')] }, { id: 'aci-copyandpay-card-frame', psp: 'aci', kinds: ['frame', 'iframe', 'document'], hostname: /^eu-prod\.oppwa\.com$/, pathname: /^\/v1\/pciIframe\.html$/, origin: 'https://eu-prod.oppwa.com', path_pattern: '/v1/pciIframe.html',
141
+ sources: [aciSource('https://eu-prod.oppwa.com/v1/pciIframe.html'), aciSource('https://eu-prod.oppwa.com/v1/paymentWidgets.js')] });
142
+ // Hyp CreditGuard PPS, reviewed from the public PPS shells and bundles and the Redirect API 2.6 guide
143
+ // on 2026-09-25. The processor ships dark: until the bundled catalog serves creditguard, a lone PPS
144
+ // signal names the processor and stays unknown. The legacy CGMPI page identifies its family and
145
+ // stays unknown through `variant_unverified`.
146
+ const creditguardSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
147
+ RULES.push({ id: 'creditguard-pps-hosted-page', psp: 'creditguard', kinds: ['document', 'frame', 'iframe'], hostname: /^pps\.creditguard\.co\.il$/, pathname: /^\/$/, origin: 'https://pps.creditguard.co.il', path_pattern: '/',
148
+ sources: [creditguardSource('https://www.creditguard.co.il/wp-content/uploads/2021/10/CG-Gateway-Redirect-API-for-PPS.pdf'), creditguardSource('https://pps.creditguard.co.il/')] }, { id: 'creditguard-pps-template-script', psp: 'creditguard', kinds: ['script'], hostname: /^pps\.creditguard\.co\.il$/, pathname: /^\/templates\/[A-Za-z0-9_-]{1,64}\/main\.js$/, origin: 'https://pps.creditguard.co.il', path_pattern: '/templates/:template/main.js',
149
+ sources: [creditguardSource('https://pps.creditguard.co.il/templates/cgt-basic/main.js')] }, { id: 'creditguard-legacy-mpi-page', psp: 'creditguard', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^cgmpi\.creditguard\.co\.il$/, pathname: /^\/CGMPI_Server\/PerformTransaction$/, origin: 'https://cgmpi.creditguard.co.il', path_pattern: '/CGMPI_Server/PerformTransaction',
150
+ sources: [creditguardSource('https://www.creditguard.co.il/wp-content/uploads/2020/11/CG-Gateway-Redirect-API.pdf')],
151
+ variant_unverified: 'The implemented CreditGuard adapter covers the PPS page on pps, pps2 and ppsuat.creditguard.co.il; the legacy CGMPI page on cgmpi.creditguard.co.il is not admitted.' });
152
+ // Primer Web SDK v2, reviewed from Primer's public SDK bundles (sdk.primer.io/web/v2.63.0 and v2.65.0,
153
+ // the @primer-io/checkout-web 2.65.0 loader) and Printful's add-card chunk on 2026-09-25. One host serves
154
+ // the SDK for sandbox and production sessions alike; the session's pciUrl decides where the card goes. The
155
+ // processor ships dark: until the bundled catalog serves primer, a lone Primer signal names the processor
156
+ // and stays unknown. Primer Checkout (@primer-io/primer-js) loads its api-controller from
157
+ // /web/hosted-pages/ and stays unknown through `variant_unverified`.
158
+ const primerSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
159
+ RULES.push({ id: 'primer-core-sdk-script', psp: 'primer', kinds: ['script'], hostname: /^sdk\.primer\.io$/, pathname: /^\/web\/v2\.[0-9]+\.[0-9]+\/PrimerCoreSDK\.min\.js$/, origin: 'https://sdk.primer.io', path_pattern: '/web/v2.:minor.:patch/PrimerCoreSDK.min.js',
160
+ sources: [primerSource('https://sdk.primer.io/web/v2.63.0/PrimerCoreSDK.min.js'), primerSource('https://www.npmjs.com/package/@primer-io/checkout-web/v/2.65.0')] }, { id: 'primer-card-frame', psp: 'primer', kinds: ['document', 'frame', 'iframe'], hostname: /^sdk\.primer\.io$/, pathname: /^\/web\/v2\.[0-9]+\.[0-9]+\/(?:hosted-input|api-controller)\.html$/, origin: 'https://sdk.primer.io', path_pattern: '/web/v2.:minor.:patch/{hosted-input,api-controller}.html',
161
+ sources: [primerSource('https://sdk.primer.io/web/v2.63.0/hosted-input.html'), primerSource('https://sdk.primer.io/web/v2.63.0/api-controller.html')] }, { id: 'primer-checkout-api-controller', psp: 'primer', kinds: ['document', 'frame', 'iframe', 'script'], hostname: /^sdk\.primer\.io$/, pathname: /^\/web\/hosted-pages\/v[0-9]+\.[0-9]+\.[0-9]+\/api-controller\.(?:html|mjs)$/, origin: 'https://sdk.primer.io', path_pattern: '/web/hosted-pages/:version/api-controller.{html,mjs}',
162
+ sources: [primerSource('https://sdk.primer.io/web/hosted-pages/v1.1.0/api-controller.mjs'), primerSource('https://www.npmjs.com/package/@primer-io/primer-js/v/1.10.1')],
163
+ variant_unverified: 'The implemented Primer adapter covers the Web SDK v2 card tokenization; Primer Checkout (primer-js) sends the card from a hosted api-controller that gives up on the request after 30 seconds and is not admitted.' });
164
+ // Payrails web SDK v6, reviewed from Payrails' public CDN files, the @payrails/web-sdk 6.5.0 loader
165
+ // and Blacklane's static bundles on 2026-09-25. The loader injects
166
+ // assets.payrails.io/web-sdk/<v6, or a build featureConfig.sdkVersion pins>/payrails.js, and the card
167
+ // form mounts its fields as assets.payrails.io/apps/secure-frame/<v6 or x.y.z>/index.html iframes.
168
+ // Only 6.x is matched, the body and JWE shape the Vault checks. window.__payrailsWebSdk__, the global
169
+ // the bundle publishes, is page state rather than a loaded resource, so discovery never reads it.
170
+ // The processor ships dark: until the bundled catalog serves payrails, a Payrails signal names it and
171
+ // stays unknown. Once served, only the card-form save is covered, and it is conditional on consent.
172
+ const payrailsSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
173
+ RULES.push({ id: 'payrails-web-sdk-v6-script', psp: 'payrails', kinds: ['script'], hostname: /^assets\.payrails\.io$/, pathname: /^\/web-sdk\/v?6(?:\.[0-9]+\.[0-9]+)?\/payrails\.js$/, origin: 'https://assets.payrails.io', path_pattern: '/web-sdk/{v6,6.:minor.:patch}/payrails.js',
174
+ sources: [payrailsSource('https://assets.payrails.io/web-sdk/v6/payrails.js'), payrailsSource('https://www.npmjs.com/package/@payrails/web-sdk/v/6.5.0')] }, { id: 'payrails-secure-frame-v6', psp: 'payrails', kinds: ['frame', 'iframe', 'document'], hostname: /^assets\.payrails\.io$/, pathname: /^\/apps\/secure-frame\/(?:v6|6\.[0-9]+\.[0-9]+)\/index\.html$/, origin: 'https://assets.payrails.io', path_pattern: '/apps/secure-frame/{v6,6.:minor.:patch}/index.html',
175
+ sources: [payrailsSource('https://assets.payrails.io/apps/secure-frame/v6/index.html'), payrailsSource('https://docs.payrails.com/docs/secure-fields')] });
176
+ // Elavon Converge Hosted Payment Page (NY DMV), reviewed from Converge's own page shell and bundle
177
+ // on 2026-09-25 (byte-identical on the production and demo hosts). The processor ships dark: until
178
+ // the bundled catalog serves elavon_converge, a lone Converge signal names the processor and stays
179
+ // unknown. The page URL carries ?ssl_txn_auth_token=, which never takes part in matching. The
180
+ // Lightbox modal and Checkout.js are named as unverified variants.
181
+ const convergeSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
182
+ RULES.push({ id: 'elavon-converge-hosted-payment-page', psp: 'elavon_converge', kinds: ['document', 'frame', 'iframe'], hostname: /^api\.convergepay\.com$/, pathname: /^\/hosted-payments\/$/, origin: 'https://api.convergepay.com', path_pattern: '/hosted-payments/',
183
+ sources: [convergeSource('https://api.convergepay.com/hosted-payments/'), convergeSource('https://developer.elavon.com/products/hosted-payment-page/v1/take-a-payment')] }, { id: 'elavon-converge-hpp-bundle', psp: 'elavon_converge', kinds: ['script'], hostname: /^api\.convergepay\.com$/, pathname: /^\/hosted-payments\/assets\/hpp-[0-9]+\.[0-9]+\.[0-9]+\.js$/, origin: 'https://api.convergepay.com', path_pattern: '/hosted-payments/assets/hpp-:version.js',
184
+ sources: [convergeSource('https://api.convergepay.com/hosted-payments/assets/hpp-2.505.07.js')] }, { id: 'elavon-converge-lightbox', psp: 'elavon_converge', kinds: ['document', 'frame', 'iframe'], hostname: /^api\.convergepay\.com$/, pathname: /^\/hosted-payments\/hpm\/?$/, origin: 'https://api.convergepay.com', path_pattern: '/hosted-payments/hpm',
185
+ sources: [convergeSource('https://api.demo.convergepay.com/hosted-payments/hpm'), convergeSource('https://api.convergepay.com/hosted-payments/PayWithConverge.js')],
186
+ variant_unverified: 'The implemented Elavon Converge adapter covers the Hosted Payment Page; the Lightbox payment modal at hosted-payments/hpm posts to its own endpoint and is not admitted.' }, { id: 'elavon-converge-checkout-js', psp: 'elavon_converge', kinds: ['script'], hostname: /^api\.convergepay\.com$/, pathname: /^\/hosted-payments\/(?:Checkout|PayWithConverge)\.js$/, origin: 'https://api.convergepay.com', path_pattern: '/hosted-payments/{Checkout,PayWithConverge}.js',
187
+ sources: [convergeSource('https://api.convergepay.com/hosted-payments/Checkout.js'), convergeSource('https://api.convergepay.com/hosted-payments/PayWithConverge.js')],
188
+ variant_unverified: 'The implemented Elavon Converge adapter covers the Hosted Payment Page; Checkout.js and the Lightbox launcher post the card from the merchant page and are not admitted.' });
189
+ // Worldpay eProtect: the vendor's JS API scripts, its iframe client and the Worldpay-origin payframe
190
+ // identify the processor. While its definition ships dark it is outside the bundled capabilities, so
191
+ // a page using it stays unknown here. B&H serves the same script from a domain it names itself,
192
+ // which is identified and refused; any other merchant-named domain stays unidentified, because a
193
+ // host wildcard here would make every card frame attributable to eProtect.
194
+ const eprotectSources = [
195
+ { kind: 'primary_source', reference: 'https://request.eprotect.vantivcnp.com/eProtect/eProtect-api3.js', reviewed_on: '2026-09-23' },
196
+ { kind: 'official_documentation', reference: 'https://docs.worldpay.com/assets/pdf/Worldpay_eProtect_Integration_Guide.pdf', reviewed_on: '2026-09-23' },
197
+ ];
198
+ const eprotectScript = /^\/eProtect\/(?:eProtect|litle)-api[23]\.js$/;
199
+ RULES.push({ id: 'worldpay-eprotect-js-api', psp: 'worldpay_eprotect', kinds: ['script'], hostname: /^request\.eprotect\.vantivcnp\.com$/, pathname: eprotectScript,
200
+ origin: 'https://request.eprotect.vantivcnp.com', path_pattern: '/eProtect/{eProtect,litle}-api{2,3}.js', sources: eprotectSources }, { id: 'worldpay-eprotect-iframe-client', psp: 'worldpay_eprotect', kinds: ['script'], hostname: /^request\.eprotect\.vantivcnp\.com$/,
201
+ pathname: /^\/eProtect\/js\/(?:payframe|eProtect-iframe)-client[34](?:\.min)?\.js$/, origin: 'https://request.eprotect.vantivcnp.com',
202
+ path_pattern: '/eProtect/js/{payframe,eProtect-iframe}-client{3,4}[.min].js', sources: eprotectSources }, { id: 'worldpay-eprotect-payframe', psp: 'worldpay_eprotect', kinds: ['frame', 'iframe', 'document'], hostname: /^request\.eprotect\.vantivcnp\.com$/,
203
+ pathname: /^\/eProtect\/payframe_[A-Za-z0-9]{1,64}_[A-Za-z0-9_-]{1,96}\.html$/, origin: 'https://request.eprotect.vantivcnp.com',
204
+ path_pattern: '/eProtect/payframe_:paypageId_:style.html', sources: eprotectSources }, { id: 'worldpay-eprotect-merchant-domain', psp: 'worldpay_eprotect', kinds: ['script'], hostname: /^paypage\.bhphotovideo\.com$/, pathname: eprotectScript,
205
+ origin: 'https://paypage.bhphotovideo.com', path_pattern: '/eProtect/{eProtect,litle}-api{2,3}.js', sources: eprotectSources,
206
+ variant_unverified: 'This eProtect script is served from a merchant-named domain, not a Worldpay eProtect host; only Worldpay’s own eProtect hosts are covered.' });
207
+ // WSPay Components (WSPay's build of Monri Components), reviewed from the vendor's public bundles
208
+ // on 2026-09-23. The processor ships dark: until the bundled catalog serves wspay, a lone WSPay
209
+ // signal names the processor and stays unknown. Monri Components on ipg.monri.com and the WSPay
210
+ // Form hosted page identify their family and stay unknown through `variant_unverified`.
211
+ const wspaySource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-23' });
212
+ RULES.push({ id: 'wspay-components-script', psp: 'wspay', kinds: ['script'], hostname: /^proxy\.wspay\.biz$/, pathname: /^\/dist\/(?:components|monri)\.js$/, origin: 'https://proxy.wspay.biz', path_pattern: '/dist/{components,monri}.js',
213
+ sources: [wspaySource('https://proxy.wspay.biz/dist/components.js'), wspaySource('https://proxy.wspay.biz/dist/monri.js')] }, { id: 'wspay-components-card-frame', psp: 'wspay', kinds: ['frame', 'iframe', 'document'], hostname: /^proxy\.wspay\.biz$/, pathname: /^\/v2\/js\/payment-method-component-dynamic\.html$/, origin: 'https://proxy.wspay.biz', path_pattern: '/v2/js/payment-method-component-dynamic.html',
214
+ sources: [wspaySource('https://proxy.wspay.biz/dist/components.js'), wspaySource('https://proxy.wspay.biz/v2/js/payment-method-component-dynamic.html')] }, { id: 'wspay-form-hosted-page', psp: 'wspay', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^form\.wspay\.biz$/, pathname: /^\/authorization(?:\.aspx)?$/, origin: 'https://form.wspay.biz', path_pattern: '/authorization{,.aspx}',
215
+ sources: [wspaySource('https://docs.wspay.info/wspayform')],
216
+ variant_unverified: 'The implemented WSPay adapter covers the WSPay Components card confirm on proxy.wspay.biz; the WSPay Form hosted page posts the card to form.wspay.biz and is not admitted.' }, { id: 'monri-components-script', psp: 'monri', kinds: ['script'], hostname: /^ipg\.monri\.com$/, pathname: /^\/dist\/(?:components|monri)\.js$/, origin: 'https://ipg.monri.com', path_pattern: '/dist/{components,monri}.js',
217
+ sources: [wspaySource('https://ipg.monri.com/en/documentation/components'), wspaySource('https://ipg.monri.com/dist/components.js')],
218
+ variant_unverified: 'Monri Components on ipg.monri.com confirm the card on Monri’s own hosts, which need the relay route; only the WSPay Components confirm on proxy.wspay.biz is implemented.' }, { id: 'monri-components-card-frame', psp: 'monri', kinds: ['frame', 'iframe', 'document'], hostname: /^ipg\.monri\.com$/, pathname: /^\/v2\/js\/payment-(?:method-component-dynamic|component)\.html$/, origin: 'https://ipg.monri.com', path_pattern: '/v2/js/payment-{method-component-dynamic,component}.html',
219
+ sources: [wspaySource('https://ipg.monri.com/dist/components.js')],
220
+ variant_unverified: 'Monri Components on ipg.monri.com confirm the card on Monri’s own hosts, which need the relay route; only the WSPay Components confirm on proxy.wspay.biz is implemented.' });
221
+ // Shiji STS behind the MyCheck Wallet V3 (dark: its processor declares a capability, so the bundled
222
+ // catalog does not list it and these signals stay unknown until a client opts in). The in-page
223
+ // wallet (main.js) and the STS proxy script identify the processor. The wallet's iframe integration
224
+ // (sdk.js, wallet-iframe.html) and Shiji's ps-js-apm library identify it too, but no Vault mode covers
225
+ // them, so each keeps the result unknown through `variant_unverified`. Sources: the reviewed bundles
226
+ // (sts.proxy.v2.1.js sha256 aee72b81...346ade84, wallet main.js 7d28c3bf...03580fdb).
227
+ const shijiSources = [
228
+ { kind: 'primary_source', reference: 'https://cdn.shijipayment.com/sts/sts.proxy.v2.1.js', reviewed_on: '2026-09-25' },
229
+ { kind: 'primary_source', reference: 'https://wallet-sdk-v3.mycheckapp.com/latest/static/js/main.js', reviewed_on: '2026-09-25' },
230
+ ];
231
+ RULES.push({ id: 'mycheck-wallet-v3-script', psp: 'shiji_sts', kinds: ['script'], hostname: /^wallet-sdk-v3\.mycheckapp\.com$/, pathname: /^\/[A-Za-z0-9._-]+\/static\/js\/main\.js$/, origin: 'https://wallet-sdk-v3.mycheckapp.com', path_pattern: '/:version/static/js/main.js',
232
+ sources: shijiSources }, { id: 'shiji-sts-proxy-script', psp: 'shiji_sts', kinds: ['script'], hostname: /^cdn\.shijipayment\.com$/, pathname: /^\/sts\/sts\.proxy\.v2\.1\.js$/, origin: 'https://cdn.shijipayment.com', path_pattern: '/sts/sts.proxy.v2.1.js',
233
+ sources: shijiSources }, { id: 'mycheck-wallet-v3-iframe-sdk', psp: 'shiji_sts', kinds: ['script'], hostname: /^wallet-sdk-v3\.mycheckapp\.com$/, pathname: /^\/[A-Za-z0-9._-]+\/sdk\.js$/, origin: 'https://wallet-sdk-v3.mycheckapp.com', path_pattern: '/:version/sdk.js',
234
+ sources: [{ kind: 'primary_source', reference: 'https://wallet-sdk-v3.mycheckapp.com/latest/sdk.js', reviewed_on: '2026-09-25' }],
235
+ variant_unverified: 'The MyCheck wallet iframe integration sends the card from MyCheck\'s own frame; only the in-page wallet (main.js) is admitted.' }, { id: 'mycheck-wallet-v3-iframe', psp: 'shiji_sts', kinds: ['frame', 'iframe', 'document'], hostname: /^wallet-sdk-v3\.mycheckapp\.com$/, pathname: /^\/wallet-iframe\.html$/, origin: 'https://wallet-sdk-v3.mycheckapp.com', path_pattern: '/wallet-iframe.html',
236
+ sources: [{ kind: 'primary_source', reference: 'https://wallet-sdk-v3.mycheckapp.com/wallet-iframe.html', reviewed_on: '2026-09-25' }],
237
+ variant_unverified: 'The MyCheck wallet iframe integration sends the card from MyCheck\'s own frame; only the in-page wallet (main.js) is admitted.' }, { id: 'shiji-ps-js-apm-script', psp: 'shiji_sts', kinds: ['script'], hostname: /^cdn\.shijipayment\.com$/, pathname: /^\/libraries\/shijipayments\.latest\.min\.js$/, origin: 'https://cdn.shijipayment.com', path_pattern: '/libraries/shijipayments.latest.min.js',
238
+ sources: [{ kind: 'primary_source', reference: 'https://cdn.shijipayment.com/libraries/shijipayments.latest.min.js', reviewed_on: '2026-09-25' }],
239
+ variant_unverified: 'Shiji\'s ps-js-apm library can take its STS key and URL from server configuration; only the MyCheck Wallet V3 path under the pinned key is admitted.' });
240
+ // Amadeus Payment Platform (Outpayce), reviewed from Amadeus's public Checkout Web SDK (the 7.1.0
241
+ // bundle, its form-field pages and user guide under prd.payment.amadeus.com/checkout/sdk/) and the
242
+ // Data Collection Page script on paypages.payment.amadeus.com on 2026-09-25. The SDK loads its tunnel,
243
+ // pan and cvv frames from the same versioned directory; the DCP page is framed from /ATPDCP/land/.
244
+ // The processor ships dark: until the bundled catalog serves amadeus_checkout, a lone Amadeus signal
245
+ // names the processor and stays unknown.
246
+ const amadeusSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
247
+ RULES.push({ id: 'amadeus-checkout-sdk-script', psp: 'amadeus_checkout', kinds: ['script'], hostname: /^prd\.payment\.amadeus\.com$/, pathname: /^\/checkout\/sdk\/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/sdk-es(?:5|2015|2022)\.js$/, origin: 'https://prd.payment.amadeus.com', path_pattern: '/checkout/sdk/:version/sdk-{es5,es2015,es2022}.js',
248
+ sources: [amadeusSource('https://prd.payment.amadeus.com/checkout/sdk/7.1.0/sdk-es2022.js'), amadeusSource('https://prd.payment.amadeus.com/checkout/sdk/manifest.json')] }, { id: 'amadeus-checkout-sdk-frame', psp: 'amadeus_checkout', kinds: ['document', 'frame', 'iframe'], hostname: /^prd\.payment\.amadeus\.com$/, pathname: /^\/checkout\/sdk\/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/form-fields\/(?:tunnel|pan|cvv)\.html$/, origin: 'https://prd.payment.amadeus.com', path_pattern: '/checkout/sdk/:version/form-fields/{tunnel,pan,cvv}.html',
249
+ sources: [amadeusSource('https://prd.payment.amadeus.com/checkout/sdk/7.1.0/form-fields/tunnel.html'), amadeusSource('https://github.com/outpaycelab/outpayce-checkout-sdk-web/blob/main/userguide.md')] }, { id: 'amadeus-data-collection-page', psp: 'amadeus_checkout', kinds: ['document', 'frame', 'iframe'], hostname: /^paypages\.payment\.amadeus\.com$/, pathname: /^\/1ASIATP\/ATPDCP\/land\/?$/, origin: 'https://paypages.payment.amadeus.com', path_pattern: '/1ASIATP/ATPDCP/land/',
250
+ sources: [amadeusSource('https://paypages.payment.amadeus.com/1ASIATP/ATPDCP/static/dcppages/standard/js/standard-acb53a79a0.min.js')] });
251
+ // Worldline Connect MyCheckout (dark: its processor declares a capability, so the bundled catalog
252
+ // does not list it and these signals stay unknown until a client opts in). The hosted payment page
253
+ // itself identifies the processor: /checkout/<hosted checkout> on the `payment.` subdomain of a
254
+ // data center (pay1 Europe, pay2 US), and its card form's action, /checkout. Read from Worldline's
255
+ // template bundle and a recorded MyCheckout card page (Aveda Belgium, Sep 2025).
256
+ const worldlineSources = [
257
+ { kind: 'primary_source', reference: 'https://assets.pay1.checkout.worldline-solutions.com/templates/generic/responsive/wro/html-footer.js', reviewed_on: '2026-09-30' },
258
+ { kind: 'official_documentation', reference: 'https://docs.connect.worldline-solutions.com/documentation/hosted-payment-pages/', reviewed_on: '2026-09-30' },
259
+ ];
260
+ RULES.push({ id: 'worldline-mycheckout-page', psp: 'worldline_mycheckout', kinds: ['document', 'frame', 'iframe'], hostname: /^payment\.pay1\.checkout\.worldline-solutions\.com$/,
261
+ pathname: /^\/checkout\/(?:payment-product-details\/)?[1-9][0-9]{0,9}-[0-9a-f]{32}:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}:[0-9a-f]{32}$/,
262
+ origin: 'https://payment.pay1.checkout.worldline-solutions.com', path_pattern: '/checkout/{payment-product-details/}:hosted_checkout', sources: worldlineSources }, { id: 'worldline-mycheckout-card-form', psp: 'worldline_mycheckout', kinds: ['form'], hostname: /^payment\.pay1\.checkout\.worldline-solutions\.com$/, pathname: /^\/checkout$/,
263
+ origin: 'https://payment.pay1.checkout.worldline-solutions.com', path_pattern: '/checkout', sources: worldlineSources });
264
+ // A processor that ships dark (its definition declares a capability) is left out of the bundled
265
+ // catalog. Datatrans's discovery rules wait with it: they join RULES only once KNOWN_PSPS, the
266
+ // bundled catalog's processors, lists its psp. Until then its assets are not evidence, so a page
267
+ // that loads them, alone or beside another processor's SDK, reads exactly as it did before the
268
+ // processor existed. (The dark GMO, CyberSource Flex, Juspay, Pelecard, ACI, CreditGuard,
269
+ // Primer, Payrails, Elavon Converge, Worldpay eProtect, WSPay, Shiji STS, Amadeus and Worldline
270
+ // MyCheckout rules above join RULES now, and their signals stay unknown.)
271
+ const SERVED_ONLY_RULES = [];
272
+ // Datatrans / PCI Proxy Secure Fields 2.0.0, reviewed from Datatrans's public library and iframe
273
+ // script (pay.datatrans.com/upp/payment/js/secure-fields-2.0.0.js and .min.js, field.js 3.24.602)
274
+ // and the PCI Proxy docs on 2026-09-25. The library loads the card and CVV iframes from the
275
+ // paymentField document on the same host. Dark behind datatrans_secure_fields_tokenize.
276
+ const datatransSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-25' });
277
+ for (const [suffix, hostname, origin] of [['', /^pay\.datatrans\.com$/, 'https://pay.datatrans.com'],
278
+ ['-sandbox', /^pay\.sandbox\.datatrans\.com$/, 'https://pay.sandbox.datatrans.com']]) {
279
+ SERVED_ONLY_RULES.push({ id: `datatrans-secure-fields-script${suffix}`, psp: 'datatrans', kinds: ['script'], hostname, pathname: /^\/upp\/payment\/js\/secure-fields-2\.0\.0(?:\.min)?\.js$/, origin, path_pattern: '/upp/payment/js/secure-fields-2.0.0{,.min}.js',
280
+ sources: [datatransSource('https://pay.datatrans.com/upp/payment/js/secure-fields-2.0.0.js'), datatransSource('https://docs.pci-proxy.com/docs/secure-fields-quick-start')] }, { id: `datatrans-secure-fields-frame${suffix}`, psp: 'datatrans', kinds: ['document', 'frame', 'iframe'], hostname, pathname: /^\/upp\/payment\/SecureFields\/paymentField$/, origin, path_pattern: '/upp/payment/SecureFields/paymentField',
281
+ sources: [datatransSource('https://pay.datatrans.com/upp/payment/SecureFields/field.js'), datatransSource('https://docs.datatrans.ch/docs/secure-fields')] });
282
+ }
283
+ RULES.push(...SERVED_ONLY_RULES.filter(rule => KNOWN_PSPS.includes(rule.psp)));
101
284
  // Keep sandbox evidence truthful instead of labelling it with a production origin.
102
285
  for (const [baseId, suffix, hostname, origin] of [
103
286
  ['gmo-mp-token-script', 'test', /^stg\.static\.mul-pay\.jp$/, 'https://stg.static.mul-pay.jp'],
@@ -106,6 +289,23 @@ for (const [baseId, suffix, hostname, origin] of [
106
289
  ['gmo-link-hosted-page', 'test', /^pt01\.mul-pay\.jp$/, 'https://pt01.mul-pay.jp'],
107
290
  ['cybersource-flex-microform-script', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
108
291
  ['cybersource-flex-microform-field', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
292
+ ['worldpay-eprotect-js-api', 'prelive', /^request\.eprotect\.vantivprelive\.com$/, 'https://request.eprotect.vantivprelive.com'],
293
+ ['worldpay-eprotect-iframe-client', 'prelive', /^request\.eprotect\.vantivprelive\.com$/, 'https://request.eprotect.vantivprelive.com'],
294
+ ['worldpay-eprotect-payframe', 'prelive', /^request\.eprotect\.vantivprelive\.com$/, 'https://request.eprotect.vantivprelive.com'],
295
+ ['mycheck-wallet-v3-script', 'sandbox', /^wallet-sdk-v3-sandbox\.mycheckapp\.com$/, 'https://wallet-sdk-v3-sandbox.mycheckapp.com'],
296
+ ['mycheck-wallet-v3-script', 'test', /^wallet-sdk-v3-test\.mycheckapp\.com$/, 'https://wallet-sdk-v3-test.mycheckapp.com'],
297
+ ['shiji-sts-proxy-script', 'dev', /^cdn\.shijipayment\.dev$/, 'https://cdn.shijipayment.dev'],
298
+ ['mycheck-wallet-v3-iframe-sdk', 'sandbox', /^wallet-sdk-v3-sandbox\.mycheckapp\.com$/, 'https://wallet-sdk-v3-sandbox.mycheckapp.com'],
299
+ ['mycheck-wallet-v3-iframe-sdk', 'test', /^wallet-sdk-v3-test\.mycheckapp\.com$/, 'https://wallet-sdk-v3-test.mycheckapp.com'],
300
+ ['mycheck-wallet-v3-iframe', 'sandbox', /^wallet-sdk-v3-sandbox\.mycheckapp\.com$/, 'https://wallet-sdk-v3-sandbox.mycheckapp.com'],
301
+ ['mycheck-wallet-v3-iframe', 'test', /^wallet-sdk-v3-test\.mycheckapp\.com$/, 'https://wallet-sdk-v3-test.mycheckapp.com'],
302
+ // MyCheckout's US data center and the pre-production twins of both.
303
+ ['worldline-mycheckout-page', 'pay2', /^payment\.pay2\.checkout\.worldline-solutions\.com$/, 'https://payment.pay2.checkout.worldline-solutions.com'],
304
+ ['worldline-mycheckout-page', 'preprod', /^payment\.pay1\.preprod\.checkout\.worldline-solutions\.com$/, 'https://payment.pay1.preprod.checkout.worldline-solutions.com'],
305
+ ['worldline-mycheckout-page', 'pay2-preprod', /^payment\.pay2\.preprod\.checkout\.worldline-solutions\.com$/, 'https://payment.pay2.preprod.checkout.worldline-solutions.com'],
306
+ ['worldline-mycheckout-card-form', 'pay2', /^payment\.pay2\.checkout\.worldline-solutions\.com$/, 'https://payment.pay2.checkout.worldline-solutions.com'],
307
+ ['worldline-mycheckout-card-form', 'preprod', /^payment\.pay1\.preprod\.checkout\.worldline-solutions\.com$/, 'https://payment.pay1.preprod.checkout.worldline-solutions.com'],
308
+ ['worldline-mycheckout-card-form', 'pay2-preprod', /^payment\.pay2\.preprod\.checkout\.worldline-solutions\.com$/, 'https://payment.pay2.preprod.checkout.worldline-solutions.com'],
109
309
  ['square-web-sdk', 'sandbox', /^sandbox\.web\.squarecdn\.com$/, 'https://sandbox.web.squarecdn.com'],
110
310
  ['paysafe-js', 'test', /^hosted\.test\.paysafe\.com$/, 'https://hosted.test.paysafe.com'],
111
311
  ['worldpay-checkout-sdk', 'try', /^try\.access\.worldpay\.com$/, 'https://try.access.worldpay.com'],
@@ -115,6 +315,11 @@ for (const [baseId, suffix, hostname, origin] of [
115
315
  ['authorize-net-sim-hosted-form', 'secure2', /^secure2\.authorize\.net$/, 'https://secure2.authorize.net'],
116
316
  ['worldpay-hosted-payment-page', 'test', /^secure-test\.worldpay\.com$/, 'https://secure-test.worldpay.com'],
117
317
  ['rapyd-hosted-checkout-page', 'sandbox', /^sandboxcheckout\.rapyd\.net$/, 'https://sandboxcheckout.rapyd.net'],
318
+ // The pdt and uat copies of the SDK are byte-identical to prd.
319
+ ['amadeus-checkout-sdk-script', 'pdt', /^pdt\.payment\.amadeus\.com$/, 'https://pdt.payment.amadeus.com'],
320
+ ['amadeus-checkout-sdk-script', 'uat', /^uat\.payment\.amadeus\.com$/, 'https://uat.payment.amadeus.com'],
321
+ ['amadeus-checkout-sdk-frame', 'pdt', /^pdt\.payment\.amadeus\.com$/, 'https://pdt.payment.amadeus.com'],
322
+ ['amadeus-checkout-sdk-frame', 'uat', /^uat\.payment\.amadeus\.com$/, 'https://uat.payment.amadeus.com'],
118
323
  // Adyen's regional live hosts (docs.adyen.com/development-resources/live-endpoints/).
119
324
  ['adyen-secured-fields-live', 'us', /^checkoutshopper-live-us\.adyen\.com$/, 'https://checkoutshopper-live-us.adyen.com'],
120
325
  ['adyen-secured-fields-live', 'au', /^checkoutshopper-live-au\.adyen\.com$/, 'https://checkoutshopper-live-au.adyen.com'],
@@ -125,6 +330,40 @@ for (const [baseId, suffix, hostname, origin] of [
125
330
  ['fiserv-commercehub-payment-fields', 'cert', /^commercehub-checkout-cert\.fiservapps\.com$/, 'https://commercehub-checkout-cert.fiservapps.com'],
126
331
  ['fiserv-commercehub-checkout-script', 'cert', /^commercehub-checkout-cert\.fiservapps\.com$/, 'https://commercehub-checkout-cert.fiservapps.com'],
127
332
  ['fiserv-secure-data-capture-frame', 'cert', /^commercehub-secure-data-capture-cert\.fiservapps\.com$/, 'https://commercehub-secure-data-capture-cert.fiservapps.com'],
333
+ // NMI's test platform (integration guide AE, "Form creation").
334
+ ['ekashu-hosted-payment-page', 'test', /^test\.ekashu\.com$/, 'https://test.ekashu.com'],
335
+ // Verifone's other regions and its two sandboxes serve the same verifone.js.
336
+ ['verifone-secure-card-capture-script', 'emea', /^emea\.jsclient\.verifone\.cloud$/, 'https://emea.jsclient.verifone.cloud'],
337
+ ['verifone-secure-card-capture-script', 'uscst', /^uscst\.jsclient\.vficloud\.net$/, 'https://uscst.jsclient.vficloud.net'],
338
+ ['verifone-secure-card-capture-script', 'cst', /^cst\.jsclient\.vficloud\.net$/, 'https://cst.jsclient.vficloud.net'],
339
+ // Juspay's sandbox, and api.juspay.io, the host the Session API's own sample payment link uses.
340
+ ['juspay-hypercheckout-payment-page', 'sandbox', /^sandbox\.juspay\.in$/, 'https://sandbox.juspay.in'],
341
+ ['juspay-hypercheckout-payment-page', 'io', /^api\.juspay\.io$/, 'https://api.juspay.io'],
342
+ // Older Pelecard terminals serve the same page and scripts from gateway20 (live and test terminals alike).
343
+ ['pelecard-payment-page', 'gateway20', /^gateway20\.pelecard\.biz$/, 'https://gateway20.pelecard.biz'],
344
+ ['pelecard-payment-script', 'gateway20', /^gateway20\.pelecard\.biz$/, 'https://gateway20.pelecard.biz'],
345
+ // ACI serves the same widget from its second live host and its two INTEGRATOR_TEST hosts.
346
+ ['aci-copyandpay-widget-script', 'oppwa', /^oppwa\.com$/, 'https://oppwa.com'],
347
+ ['aci-copyandpay-widget-script', 'eu-test', /^eu-test\.oppwa\.com$/, 'https://eu-test.oppwa.com'],
348
+ ['aci-copyandpay-widget-script', 'test', /^test\.oppwa\.com$/, 'https://test.oppwa.com'],
349
+ ['aci-copyandpay-card-frame', 'oppwa', /^oppwa\.com$/, 'https://oppwa.com'],
350
+ ['aci-copyandpay-card-frame', 'eu-test', /^eu-test\.oppwa\.com$/, 'https://eu-test.oppwa.com'],
351
+ ['aci-copyandpay-card-frame', 'test', /^test\.oppwa\.com$/, 'https://test.oppwa.com'],
352
+ // CreditGuard's second production PPS host and its UAT host.
353
+ ['creditguard-pps-hosted-page', 'pps2', /^pps2\.creditguard\.co\.il$/, 'https://pps2.creditguard.co.il'],
354
+ ['creditguard-pps-template-script', 'pps2', /^pps2\.creditguard\.co\.il$/, 'https://pps2.creditguard.co.il'],
355
+ ['creditguard-pps-hosted-page', 'uat', /^ppsuat\.creditguard\.co\.il$/, 'https://ppsuat.creditguard.co.il'],
356
+ ['creditguard-pps-template-script', 'uat', /^ppsuat\.creditguard\.co\.il$/, 'https://ppsuat.creditguard.co.il'],
357
+ // Converge's demo environment serves the same page and bundles.
358
+ ['elavon-converge-hosted-payment-page', 'demo', /^api\.demo\.convergepay\.com$/, 'https://api.demo.convergepay.com'],
359
+ ['elavon-converge-hpp-bundle', 'demo', /^api\.demo\.convergepay\.com$/, 'https://api.demo.convergepay.com'],
360
+ ['elavon-converge-lightbox', 'demo', /^api\.demo\.convergepay\.com$/, 'https://api.demo.convergepay.com'],
361
+ ['elavon-converge-checkout-js', 'demo', /^api\.demo\.convergepay\.com$/, 'https://api.demo.convergepay.com'],
362
+ ['wspay-components-script', 'test', /^proxy-test\.wspay\.biz$/, 'https://proxy-test.wspay.biz'],
363
+ ['wspay-components-card-frame', 'test', /^proxy-test\.wspay\.biz$/, 'https://proxy-test.wspay.biz'],
364
+ ['wspay-form-hosted-page', 'test', /^formtest\.wspay\.biz$/, 'https://formtest.wspay.biz'],
365
+ ['monri-components-script', 'test', /^ipgtest\.monri\.com$/, 'https://ipgtest.monri.com'],
366
+ ['monri-components-card-frame', 'test', /^ipgtest\.monri\.com$/, 'https://ipgtest.monri.com'],
128
367
  ]) {
129
368
  RULES.push({ ...RULES.find(rule => rule.id === baseId), id: `${baseId}-${suffix}`, hostname, origin });
130
369
  }
@@ -577,14 +816,14 @@ export function getCheckoutPreflightSchemas() {
577
816
  schema_version: { const: 1 }, profile_version: text, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION }, integration,
578
817
  processor_entries: { type: 'array', maxItems: 256, items: {
579
818
  type: 'object', additionalProperties: false, required: ['psp', 'mode', 'scenario', 'status'], properties: {
580
- psp: { enum: KNOWN_PSPS }, mode: { enum: ['token', 'cse', 'hosted_form'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
819
+ psp: { enum: KNOWN_PSPS }, mode: { enum: ['token', 'cse', 'hosted_form', 'device_encrypt'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
581
820
  limitations: { type: 'array', maxItems: 16, items: { type: 'string', maxLength: 256, pattern: '^[^\\u0000-\\u001f]*$' } },
582
821
  },
583
822
  } },
584
823
  expires_at: { type: 'string', format: 'date-time', maxLength: 40 }, entries: { type: 'array', maxItems: 256, items: {
585
824
  type: 'object', additionalProperties: false, required: ['flow', 'operation', 'operation_version', 'mode', 'scenario', 'status'], properties: {
586
825
  flow: { enum: FLOWS.map(flow => flow.id) }, operation: { enum: FLOWS.map(flow => flow.operation) }, operation_version: { const: 1 },
587
- mode: { enum: ['token', 'cse', 'hosted_form'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
826
+ mode: { enum: ['token', 'cse', 'hosted_form', 'device_encrypt'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
588
827
  limitations: { type: 'array', maxItems: 16, items: { type: 'string', maxLength: 256, pattern: '^[^\\u0000-\\u001f]*$' } },
589
828
  },
590
829
  } },
@@ -598,7 +837,7 @@ export function getCheckoutPreflightSchemas() {
598
837
  const result = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
599
838
  required: ['schema_version', 'catalog_version', 'integration', 'scenario', 'scenario_defaulted', 'psp', 'flow', 'mode', 'status', 'support_scope', 'checkout_flow_status', 'candidates', 'reason_codes', 'limitations', 'missing_evidence', 'observation', 'scope', 'merchant_confirmation_required'], properties: {
600
839
  schema_version: { const: 1 }, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION }, integration,
601
- scenario: { enum: SCENARIOS }, scenario_defaulted: { type: 'boolean' }, psp: { type: ['string', 'null'] }, flow: { type: ['string', 'null'] }, mode: { enum: ['token', 'cse', 'hosted_form', null] },
840
+ scenario: { enum: SCENARIOS }, scenario_defaulted: { type: 'boolean' }, psp: { type: ['string', 'null'] }, flow: { type: ['string', 'null'] }, mode: { enum: ['token', 'cse', 'hosted_form', 'device_encrypt', null] },
602
841
  support_scope: { enum: ['processor_integration', 'checkout_flow', null] }, checkout_flow_status: { enum: ['identified', 'unknown', 'ambiguous'] },
603
842
  status: { enum: ['supported', 'unsupported', 'unknown'] }, candidates: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['psp', 'relevance', 'evidence'], properties: {
604
843
  psp: { type: 'string' }, relevance: { enum: ['active', 'undetermined'] }, evidence: { type: 'array', items: evidence },
@@ -1,21 +1,30 @@
1
1
  import { type PreparedCheckout } from './client.js';
2
2
  import type { AttachOptions } from './cdp.js';
3
3
  import type { CheckoutLifecycle } from './lifecycle.js';
4
+ import type { LoadedCardMicroform } from './cybersource-flex.js';
4
5
  /** A local, one-use rendezvous. It never starts or retries a merchant request. */
5
6
  export declare class PreparationGate {
6
7
  private readonly opts;
7
8
  private readonly lifecycle;
8
9
  private readonly readDocumentUrl;
10
+ private readonly readMicroform?;
9
11
  private state;
10
12
  private observedRequest;
11
13
  private documentUrl;
12
14
  private prepared?;
13
15
  private stop;
14
16
  private expiryTimer?;
15
- constructor(opts: AttachOptions, lifecycle: CheckoutLifecycle, readDocumentUrl: () => Promise<string>);
17
+ /**
18
+ * `readMicroform`: the adapter's read of the card Microform the page loaded (cybersource-flex.ts
19
+ * loadedCardMicroform), for prepare({ psp: 'cybersource_flex', tokenizeBeforePay: true }).
20
+ */
21
+ constructor(opts: AttachOptions, lifecycle: CheckoutLifecycle, readDocumentUrl: () => Promise<string>, readMicroform?: (() => LoadedCardMicroform | Promise<LoadedCardMicroform>) | undefined);
16
22
  private prepare;
17
- /** Called for every recognized card mutation, before any await or local retry guard. */
18
- claim(requestUrl: string, requestBody?: string | null, requestHeaders?: Record<string, string>): PreparedCheckout | undefined;
23
+ /**
24
+ * Called for every recognized card request, before any await or local retry guard. The request's
25
+ * own method is what binds: a POST for most processors, the registration GET for Worldpay eProtect.
26
+ */
27
+ claim(requestUrl: string, requestMethod: string, requestBody?: string | null, requestHeaders?: Record<string, string>): PreparedCheckout | undefined;
19
28
  assertDocument(): Promise<void>;
20
29
  private readDocument;
21
30
  isEngaged(): boolean;
@@ -1,21 +1,27 @@
1
1
  import { CheckoutPreparationError } from './client.js';
2
- import { validAmountInput } from './client.js';
3
- import { matchesPreparation, validPreparationEnvironment, preparationEndpoint } from './prepared-processor.js';
2
+ import { amountRefusal, validAmountInput } from './client.js';
3
+ import { matchesPreparation, validPreparationEnvironment, preparationEndpoint, preparationBindsByShape, preparationRequestMethod, preparedRequestRefusal } from './prepared-processor.js';
4
4
  /** A local, one-use rendezvous. It never starts or retries a merchant request. */
5
5
  export class PreparationGate {
6
6
  opts;
7
7
  lifecycle;
8
8
  readDocumentUrl;
9
+ readMicroform;
9
10
  state = 'unused';
10
11
  observedRequest = false;
11
12
  documentUrl = '';
12
13
  prepared;
13
14
  stop = new AbortController();
14
15
  expiryTimer;
15
- constructor(opts, lifecycle, readDocumentUrl) {
16
+ /**
17
+ * `readMicroform`: the adapter's read of the card Microform the page loaded (cybersource-flex.ts
18
+ * loadedCardMicroform), for prepare({ psp: 'cybersource_flex', tokenizeBeforePay: true }).
19
+ */
20
+ constructor(opts, lifecycle, readDocumentUrl, readMicroform) {
16
21
  this.opts = opts;
17
22
  this.lifecycle = lifecycle;
18
23
  this.readDocumentUrl = readDocumentUrl;
24
+ this.readMicroform = readMicroform;
19
25
  lifecycle.setPreparationHandler(options => this.prepare(options));
20
26
  lifecycle.abort.signal.addEventListener('abort', () => this.invalidate('cancelled'), { once: true });
21
27
  }
@@ -39,9 +45,35 @@ export class PreparationGate {
39
45
  try {
40
46
  if (!options || !validPreparationEnvironment(options.psp, options.environment))
41
47
  throw new CheckoutPreparationError(null, 'unsupported_processor');
48
+ // CyberSource Flex, tokenized before Pay: the preparation carries the capture context and the
49
+ // fields of the card Microform this page loaded, so the cardholder's device seals the card and
50
+ // gets CyberSource's token when it approves. A page that races createToken against seconds
51
+ // (Alaska Airlines' flight checkout gives it three) is then answered at once after Pay.
52
+ let cybersourceFlex;
53
+ if (options.tokenizeBeforePay !== undefined && typeof options.tokenizeBeforePay !== 'boolean') {
54
+ throw new CheckoutPreparationError(null, 'tokenize_before_pay_invalid');
55
+ }
56
+ if (options.tokenizeBeforePay) {
57
+ if (options.psp !== 'cybersource_flex')
58
+ throw new CheckoutPreparationError(null, 'unsupported_processor');
59
+ const loaded = (await this.readMicroform?.()) ?? { ok: false, reason: 'microform_not_loaded' };
60
+ if (!loaded.ok)
61
+ throw new CheckoutPreparationError(null, loaded.reason);
62
+ cybersourceFlex = { captureContext: loaded.captureContext, microform: { version: loaded.microform.version, fields: [...loaded.microform.fields] } };
63
+ }
42
64
  // A Fiserv checkout's merchantProfile names a key pin, which prepareCheckout checks: its
43
65
  // capture endpoint is checked below like any processor-hosted checkout's.
44
- if (options.merchantProfile !== undefined && options.psp !== 'fiserv') {
66
+ if (options.psp === 'verifone') {
67
+ // A Verifone merchant's own server: a profile the last sync served, which this
68
+ // attachment pauses, enabled for this client.
69
+ const profile = typeof this.opts.vault.verifoneProfile === 'function' && typeof options.merchantProfile === 'string'
70
+ ? this.opts.vault.verifoneProfile(options.merchantProfile) : null;
71
+ if (!profile)
72
+ throw new CheckoutPreparationError(null, 'processor_interception_unavailable');
73
+ if (profile.status !== 'enabled')
74
+ throw new CheckoutPreparationError(null, 'unsupported_checkout');
75
+ }
76
+ else if (options.merchantProfile !== undefined && options.psp !== 'fiserv') {
45
77
  // An Adyen merchant's own endpoint: a profile this build reviewed, which
46
78
  // this attachment pauses, and one Agentcard turned on for this client.
47
79
  const profile = typeof this.opts.vault.merchantProfile === 'function' && typeof options.merchantProfile === 'string'
@@ -55,11 +87,19 @@ export class PreparationGate {
55
87
  }
56
88
  else {
57
89
  const tokenizer = preparationEndpoint(options.psp, options.environment);
58
- if (!this.opts.vault.isCardRequest(tokenizer, 'POST'))
90
+ // ACI's card-number request names a checkout id the merchant mints at Pay time, and a GET card
91
+ // request (Worldpay eProtect) is recognized by its whole query, which the page has not written
92
+ // yet, so no prepared URL is ever their card request: ask whether the registry serves the
93
+ // processor at all instead. A preparation the adapters would never pause could never bind.
94
+ const interceptable = preparationBindsByShape(options.psp) || preparationRequestMethod(options.psp) === 'GET'
95
+ ? typeof this.opts.vault.recognizesProcessor === 'function' && this.opts.vault.recognizesProcessor(options.psp)
96
+ : this.opts.vault.isCardRequest(tokenizer, 'POST');
97
+ if (!interceptable)
59
98
  throw new CheckoutPreparationError(null, 'processor_interception_unavailable');
60
99
  }
61
- if (!validAmountInput(this.opts.amount) || this.opts.amount === 0 || !/^[a-z]{3}$/i.test(this.opts.currency ?? ''))
62
- throw new CheckoutPreparationError(null, 'amount_required');
100
+ if (!validAmountInput(this.opts.amount) || this.opts.amount === 0 || !/^[a-z]{3}$/i.test(this.opts.currency ?? '')) {
101
+ throw new CheckoutPreparationError(null, 'amount_required', undefined, this.opts.amount != null && !validAmountInput(this.opts.amount) ? amountRefusal(this.opts.amount, this.opts.currency) : undefined);
102
+ }
63
103
  if (signal.aborted)
64
104
  throw new CheckoutPreparationError(null, 'cancelled');
65
105
  this.documentUrl = await this.readDocument();
@@ -70,6 +110,7 @@ export class PreparationGate {
70
110
  ...options, user: this.opts.user, merchant: this.opts.merchant,
71
111
  amount: this.opts.amount, currency: this.opts.currency, cardId: this.opts.cardId,
72
112
  merchantOrigin: page.origin, checkoutKey: crypto.randomUUID(), timeoutMs: this.opts.timeoutMs, signal,
113
+ ...(cybersourceFlex ? { cybersourceFlex } : {}),
73
114
  onPreparationCreated: id => this.lifecycle.preparationCreated(id),
74
115
  onApprovalUrl: url => {
75
116
  if (!signal.aborted) {
@@ -83,8 +124,10 @@ export class PreparationGate {
83
124
  });
84
125
  this.prepared = prepared;
85
126
  // A Fiserv preparation also names the key pin the cardholder approves (merchantProfile).
127
+ // One that tokenizes before Pay names the capture context its cardholder approved under.
86
128
  if (prepared.psp !== options.psp || prepared.environment !== options.environment
87
- || prepared.merchantProfile !== options.merchantProfile)
129
+ || prepared.merchantProfile !== options.merchantProfile
130
+ || (prepared.cybersourceFlex?.captureContext ?? null) !== (cybersourceFlex?.captureContext ?? null))
88
131
  throw new CheckoutPreparationError(prepared.id, 'checkout_changed');
89
132
  if (signal.aborted || this.state !== 'preparing') {
90
133
  void this.cancelThenObserve(prepared.id, 'cancelled');
@@ -109,8 +152,11 @@ export class PreparationGate {
109
152
  }
110
153
  // Keep the signal listener after ready: caller cancellation retires the handle too.
111
154
  }
112
- /** Called for every recognized card mutation, before any await or local retry guard. */
113
- claim(requestUrl, requestBody, requestHeaders) {
155
+ /**
156
+ * Called for every recognized card request, before any await or local retry guard. The request's
157
+ * own method is what binds: a POST for most processors, the registration GET for Worldpay eProtect.
158
+ */
159
+ claim(requestUrl, requestMethod, requestBody, requestHeaders) {
114
160
  this.observedRequest = true;
115
161
  if (this.state === 'unused')
116
162
  return undefined;
@@ -121,8 +167,13 @@ export class PreparationGate {
121
167
  const prepared = this.prepared;
122
168
  // A merchant-hosted preparation claims only its profile's reviewed card request.
123
169
  // A Fiserv preparation claims only a capture under the key pin it names.
124
- if (Date.parse(prepared.expiresAt) <= Date.now() || !matchesPreparation(prepared, requestUrl, 'POST', requestBody, requestHeaders)) {
125
- const reason = Date.parse(prepared.expiresAt) <= Date.now() ? 'expired' : 'checkout_changed';
170
+ // A CreditGuard preparation claims only a submit to the page it framed.
171
+ // A Worldpay eProtect preparation claims only its registration GET naming the paypageId it carries.
172
+ if (Date.parse(prepared.expiresAt) <= Date.now() || !matchesPreparation(prepared, requestUrl, requestMethod, requestBody, requestHeaders)) {
173
+ // An Amadeus request payment-core refuses names its own reason (preparedRequestRefusal), so
174
+ // Air France's split expiry reads amadeus_merchant_held_expiry rather than a changed checkout.
175
+ const reason = Date.parse(prepared.expiresAt) <= Date.now() ? 'expired'
176
+ : preparedRequestRefusal(prepared.psp, prepared.environment, requestUrl, requestBody, requestHeaders, prepared.merchantOrigin);
126
177
  this.invalidate(reason);
127
178
  throw new CheckoutPreparationError(prepared.id, reason);
128
179
  }