@agent-cards/checkout 0.13.0 → 0.15.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Approve an Adyen Sessions checkout before Pay with `prepare({ psp: 'adyen', environment: 'production' | 'sandbox' })`. The cardholder approves and picks the card first; when the merchant's Sessions `/payments` request pauses, the device encrypts the approved card under the merchant's Adyen key and the request continues from your browser, so Adyen Web's own 60-second request timeout covers only the pause, the bind and the encryption. `sandbox` is Adyen's test host with a `test_` client key; `production` is the live host families with a `live_` key. A stored-card, single-blob or store-the-card request never uses the approval. The matching API, Vault and database migration are required.
6
+
7
+ - Approve Spreedly's native hosted-card checkout with `prepare({ psp: 'spreedly', environment: 'shared' })`. The Vault tokenizes the selected card over the encrypted relay and returns the native response. Matching API, Vault, relay and database releases are required; tokenization alone does not confirm payment. Saved-card updates and other Spreedly flows remain unsupported.
8
+
5
9
  - Identify hosted, regional, and versioned processor variants seen on live checkouts: Adyen secured fields on the regional live hosts and any release, the Square card element frame, dLocal Smart Fields releases, Mercado Pago secure fields and the guest Checkout Pro card form, and the Rapyd hosted checkout page. The Tranzila terminal-root page, the Authorize.Net hosted payment form, and Worldpay hosted payment pages identify their processor and return `unknown` with `processor_variant_unverified`. The catalog is `2026-09-15.1`; issue native profiles against it.
6
10
 
7
11
  - Report a sole script-only candidate as `psp` only when the observation is complete. On a partial scan the script stays in `candidates` as `undetermined`, so a co-loaded SDK is never presented as the selected processor.
package/PREFLIGHT.md CHANGED
@@ -171,6 +171,7 @@ The catalog includes reviewed discovery rules and direct SDK implementation capa
171
171
  | Mollie | `token` |
172
172
  | Paysafe | `token` |
173
173
  | Recurly | `token` |
174
+ | Spreedly | `token` |
174
175
  | Moneris | `token` |
175
176
  | Bambora | `token` |
176
177
  | Global Payments | `token` |
package/README.md CHANGED
@@ -84,8 +84,8 @@ Agentcard derives it.
84
84
  Then let your agent click "Pay" like it always does. `attachToCdp` pauses the
85
85
  request for approval and resumes it only while the merchant request remains
86
86
  live. Merchant timeouts still apply: Square's observed tokenization deadline
87
- is about 10 seconds, and Braintree's native request timeout is 60 seconds, including approval and token handoff. For human approval, use
88
- `controller.prepare()` before the first Pay action as shown below.
87
+ is about 10 seconds, Braintree's native request timeout is 60 seconds, and Adyen Web's own request timeout abandons its Sessions payment call 60 seconds after Pay (observed on Adyen Web 6.41 and 6.44), each including approval and handoff. These are the processors' limits, not ones the SDK enforces: the SDK's own authorization wait stays 15 minutes. For human approval, use
88
+ `controller.prepare()` before the first Pay action as shown below. For Adyen, `prepare()` moves the approval before Pay, so only the device-side encryption runs inside Adyen Web's minute.
89
89
 
90
90
  Playwright:
91
91
 
@@ -136,7 +136,7 @@ Coverage is specific to the processor request format, merchant setup, browser tr
136
136
  | Checkout.com | supported |
137
137
  | Mercado Pago | Card tokenization and prepared checkout are implemented. Guest Checkout Pro in Mexico also corrects the issuer for one native card association when the selected card has the same brand and type. One live MXN 40 Lotería Chida purchase with published SDK 0.10.0 completed automatically through Pay; the merchant confirmed paid status and PDF fulfillment. The historical SDK result remains unknown because the private receipt adapter rejected a relative download URL; a separate read with the corrected adapter confirms that same paid receipt. Independent processor capture/settlement and other country or integration paths remain unverified. |
138
138
  | VGS Collect (Very Good Security; Wolt) | not supported: VGS's proxy aliases only submissions from its own iframe, so a replay from the cardholder's device is refused by the merchant (verified on Wolt, 2026-09-03). Not recognized, so the agent's browser is not paused there |
139
- | Adyen | supported (mode `cse`): the vault encrypts the card for Adyen on the cardholder's device and your browser sends it |
139
+ | Adyen | supported (mode `cse`): the vault encrypts the card for Adyen on the cardholder's device and your browser sends it. Sessions flow only: the paused request is `/checkoutshopper/v1/sessions/{id}/payments` on Adyen's own hosts; a merchant that posts the encrypted fields to its own server is not recognized, so nothing pauses there. Without a preparation, approval starts at Pay and must land before Adyen Web's own request timeout (observed at 60 seconds on Adyen Web 6.41 and 6.44; not enforced by the SDK); `prepare({ psp: 'adyen' })` moves the approval before Pay |
140
140
  | Tranzila | supported (mode `hosted_form`): the cardholder finishes on Tranzila's own page; the paused form navigation resolves to a synthetic page, and you poll the merchant's order state |
141
141
 
142
142
  The recognizer list is fetched from the API at runtime (`vault.syncRegistry()`),
@@ -514,8 +514,28 @@ await page.getByRole('button', { name: 'Pay', exact: true }).click();
514
514
  | Bambora | `shared` | `/scripts/tokenization/tokens` on `api.bam.shift4api.net` or `api.na.bambora.com` |
515
515
  | Mercado Pago | `shared` | `api.mercadopago.com/v1/card_tokens` with a fresh card body |
516
516
  | Recurly | `shared` | Form-encoded POST to `/js/v1/token` on `api.recurly.com` or `api.eu.recurly.com` |
517
+ | Spreedly | `shared` | Native iframe JSON POST to `/v1/payment_methods/restricted.json` on `core.spreedly.com` |
518
+ | Adyen | `production` or `sandbox` | The Sessions `/checkoutshopper/v1/sessions/{id}/payments` POST on the matching Adyen host family (`sandbox` is `checkoutshopper-test.adyen.com` with a `test_` client key; `production` is the live hosts with a `live_` key), carrying a fresh card's four encrypted fields |
517
519
 
518
- Use `environment: 'shared'` for Bambora, Mercado Pago and Recurly because the same endpoint serves test and live requests. Agentcard cannot establish the processor's test mode from that URL or a credential prefix. Configure test mode through the merchant's processor account when testing. Agentcard's own `sandbox` flag remains separate. Prepared Worldpay, Bambora, Mercado Pago and Recurly requests reject saved-card and recurring request bodies; a refused request retires the local preparation. Reconcile any existing merchant attempt before creating a new attachment.
520
+ Use `environment: 'shared'` for Bambora, Mercado Pago, Recurly and Spreedly because the same endpoint serves test and live requests. Agentcard cannot establish the processor's test mode from that URL or a credential prefix. Configure test mode through the merchant's processor account when testing. Agentcard's own `sandbox` flag remains separate. Prepared Worldpay, Bambora, Mercado Pago and Recurly requests reject saved-card and recurring request bodies; a refused request retires the local preparation. Reconcile any existing merchant attempt before creating a new attachment.
521
+
522
+ Prepare an Adyen Sessions checkout before the agent clicks Pay:
523
+
524
+ ```ts
525
+ await controller.prepare({ psp: 'adyen', environment: 'production' }); // 'sandbox' for Adyen's test host
526
+ ```
527
+
528
+ After approval, click Pay once. adyen-web encrypts the agent's placeholder digits and posts its Sessions `/payments` request; the SDK pauses it and binds it to the approval, the cardholder's device (still on the approval page) encrypts the approved card under the merchant's Adyen key, and the request continues from your browser with only the four encrypted fields swapped and `brand` dropped. Adyen answers your browser, so the merchant's order state is the outcome to poll. The bound request must be a fresh `scheme` card on the approved host family with a client key of that environment; a stored-card, single-blob or store-the-card request never uses the approval. Adyen Web's own 60-second request timeout then covers only the pause, the bind and the device's encryption.
529
+
530
+ Prepare a Spreedly checkout before submitting the merchant's card form:
531
+
532
+ ```ts
533
+ await controller.prepare({ psp: 'spreedly', environment: 'shared' });
534
+ ```
535
+
536
+ After approval, submit the form once. The Vault replaces the card fields on your device and sends the native request through an encrypted connection to Spreedly. The merchant's environment and signed session stay unchanged. The API, Vault, relay and preparation migration must support Spreedly before you use this flow; the API refuses preparations while its relay rollout flag is disabled.
537
+
538
+ Initial coverage includes the hosted iframe's new-card request. Saved-card CVV updates, Express, wallets, bank accounts and gateway purchase APIs require separate support. A returned card token does not confirm payment; wait for the merchant's result before reporting success. Autopilot is unavailable for Spreedly.
519
539
 
520
540
  Prepare Recurly before clicking the merchant's payment button:
521
541
 
@@ -0,0 +1,24 @@
1
+ /** Adyen Web Sessions payments request, before it consumes an approval.
2
+ * adyen-web (Drop-in and the Card component in the Sessions flow) posts one
3
+ * JSON body to /checkoutshopper/v1/sessions/{id}/payments on the merchant's
4
+ * Adyen host, with `clientKey` as its only query parameter, and carries the
5
+ * card as four JWE blobs under paymentMethod. Those four blobs are what the
6
+ * cardholder's device replaces; nothing else in the body is rewritten. Test
7
+ * and live are separate host families AND separate client-key prefixes, so a
8
+ * preparation names one of them and the request must agree on both. A body
9
+ * that pays with a stored card, carries a single encrypted blob, or asks
10
+ * Adyen to store the card is not the purchase the person approved and never
11
+ * consumes readiness. This module never rewrites a body.
12
+ */
13
+ export type AdyenPreparationEnvironment = 'production' | 'sandbox';
14
+ export declare const ADYEN_ENCRYPTED_CARD_FIELDS: readonly ["encryptedCardNumber", "encryptedExpiryMonth", "encryptedExpiryYear", "encryptedSecurityCode"];
15
+ /** Which preparation environment a hostname belongs to: Adyen's test host, one of its live host families, or neither. */
16
+ export declare function adyenPreparationEnvironment(hostname: string): AdyenPreparationEnvironment | null;
17
+ /** The URL a preparation advertises for the approved host family: every registry recognizes it, and it is never a real session. */
18
+ export declare function adyenPreparationUrl(environment: AdyenPreparationEnvironment): string;
19
+ /** A Sessions payments URL on the approved host family whose only query is a client key of that same environment. */
20
+ export declare function isPreparedAdyenUrl(rawUrl: string, environment: AdyenPreparationEnvironment): boolean;
21
+ /** A fresh scheme card carrying the four encrypted fields the device replaces; stored cards, single-blob encryption and store requests never consume consent. */
22
+ export declare function isPreparedAdyenBody(body: string | null | undefined): boolean;
23
+ /** Processor identity, environment and request shape are part of the device's prior consent. */
24
+ export declare function isPreparedAdyenRequest(rawUrl: string, method: string, body: string | null | undefined, environment: AdyenPreparationEnvironment): boolean;
@@ -0,0 +1,64 @@
1
+ export const ADYEN_ENCRYPTED_CARD_FIELDS = ['encryptedCardNumber', 'encryptedExpiryMonth', 'encryptedExpiryYear', 'encryptedSecurityCode'];
2
+ const SESSIONS_PAYMENTS = /^\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]{1,256}\/payments$/;
3
+ const TEST_HOST = /^checkoutshopper-test\.adyen\.com$/;
4
+ const LIVE_HOST = /^(?:checkoutshopper-live(?:-[a-z]+)?\.adyen\.com|(?:[a-z0-9-]+\.)*adyenpayments\.com)$/;
5
+ const CLIENT_KEY = /^(test|live)_[A-Za-z0-9]{32}$/;
6
+ const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
7
+ const text = (value, max = 4096) => typeof value === 'string' && value.length > 0 && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
8
+ /** Which preparation environment a hostname belongs to: Adyen's test host, one of its live host families, or neither. */
9
+ export function adyenPreparationEnvironment(hostname) {
10
+ const host = hostname.toLowerCase();
11
+ if (TEST_HOST.test(host))
12
+ return 'sandbox';
13
+ if (LIVE_HOST.test(host))
14
+ return 'production';
15
+ return null;
16
+ }
17
+ /** The URL a preparation advertises for the approved host family: every registry recognizes it, and it is never a real session. */
18
+ export function adyenPreparationUrl(environment) {
19
+ return `https://checkoutshopper-${environment === 'production' ? 'live' : 'test'}.adyen.com/checkoutshopper/v1/sessions/prepared/payments`;
20
+ }
21
+ /** A Sessions payments URL on the approved host family whose only query is a client key of that same environment. */
22
+ export function isPreparedAdyenUrl(rawUrl, environment) {
23
+ let url;
24
+ try {
25
+ url = new URL(rawUrl);
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ if (url.protocol !== 'https:' || url.port || url.username || url.password || url.hash)
31
+ return false;
32
+ if (adyenPreparationEnvironment(url.hostname) !== environment || !SESSIONS_PAYMENTS.test(url.pathname))
33
+ return false;
34
+ const names = [...url.searchParams.keys()];
35
+ if (names.length !== 1 || names[0] !== 'clientKey')
36
+ return false;
37
+ const key = url.searchParams.get('clientKey') ?? '';
38
+ const match = CLIENT_KEY.exec(key);
39
+ return !!match && match[0] === key && (match[1] === 'live') === (environment === 'production');
40
+ }
41
+ /** A fresh scheme card carrying the four encrypted fields the device replaces; stored cards, single-blob encryption and store requests never consume consent. */
42
+ export function isPreparedAdyenBody(body) {
43
+ if (typeof body !== 'string' || !body || body.length > 65536)
44
+ return false;
45
+ let parsed;
46
+ try {
47
+ parsed = JSON.parse(body);
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ if (!record(parsed) || parsed.storePaymentMethod === true)
53
+ return false;
54
+ const method = parsed.paymentMethod;
55
+ if (!record(method) || method.type !== 'scheme')
56
+ return false;
57
+ if ('storedPaymentMethodId' in method || 'encryptedCard' in method || 'recurringDetailReference' in method)
58
+ return false;
59
+ return ADYEN_ENCRYPTED_CARD_FIELDS.every(field => text(method[field]));
60
+ }
61
+ /** Processor identity, environment and request shape are part of the device's prior consent. */
62
+ export function isPreparedAdyenRequest(rawUrl, method, body, environment) {
63
+ return method.toUpperCase() === 'POST' && isPreparedAdyenUrl(rawUrl, environment) && isPreparedAdyenBody(body);
64
+ }
@@ -1 +1 @@
1
- export const BUILTIN_REGISTRY = [{ "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i] }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
1
+ export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i] }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { type CheckoutMode, type Recognizer } from './registry.js';
2
2
  import type { Substitutions } from './substitute.js';
3
- import { type PreparationProcessor } from './prepared-processor.js';
3
+ import { type PreparationMode, type PreparationProcessor } from './prepared-processor.js';
4
4
  export interface PausedRequest {
5
5
  url: string;
6
6
  method: string;
@@ -147,6 +147,8 @@ export interface PreparedCheckout {
147
147
  readonly status: 'ready';
148
148
  readonly psp: PreparationProcessor;
149
149
  readonly environment: 'production' | 'sandbox' | 'shared';
150
+ /** How the approved card reaches the processor: ciphertext the device produces (Adyen, `cse`) or the device's own request (`token`). */
151
+ readonly mode: PreparationMode;
150
152
  readonly expiresAt: string;
151
153
  readonly cardId: string;
152
154
  readonly user: string;
package/dist/client.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { BUILTIN_REGISTRY, cardUrlPatterns as deriveCardUrlPatterns, findRecognizer, } from './registry.js';
2
2
  import { isMercadoTokenRequest, parseMercadoCheckoutContext, validateMercadoProcessorContext } from './mercado-checkout.generated.js';
3
- import { matchesPreparedRequest, validPreparationEnvironment } from './prepared-processor.js';
3
+ import { matchesPreparedRequest, preparationMode, validPreparationEnvironment } from './prepared-processor.js';
4
4
  import { hasOwnedShopMarker, parseOwnedShopOrder, parseOwnedShopReceipt } from './owned-shop.generated.js';
5
5
  import { classifyStripeCheckoutRequest, encodeStripeCheckoutContext, hasStripeCheckoutMarker, parseStripeCheckoutContext, parseStripeCheckoutResponse, STRIPE_CHECKOUT_CONTEXT_HEADER, } from './stripe-checkout.generated.js';
6
6
  /**
@@ -440,7 +440,7 @@ export class VaultClient {
440
440
  // The separate stop signal prevents dispatch after a slow OAuth exchange.
441
441
  const created = await this.post('/v2/checkout/preparations', {
442
442
  user: input.user, merchant: input.merchant, amount: input.amount, currency: input.currency.toLowerCase(),
443
- ...(input.cardId ? { card_id: input.cardId } : {}), psp: input.psp, mode: 'token',
443
+ ...(input.cardId ? { card_id: input.cardId } : {}), psp: input.psp, mode: preparationMode(input.psp),
444
444
  environment: input.environment, checkout_key: input.checkoutKey, merchant_origin: input.merchantOrigin,
445
445
  }, AbortSignal.timeout(30_000), signal);
446
446
  if (!created || typeof created.id !== 'string' || !/^cprep_[A-Za-z0-9_-]{1,128}$/.test(created.id))
@@ -471,11 +471,11 @@ export class VaultClient {
471
471
  || state.payment_status !== 'not_started' || state.amount_authority !== 'agent'
472
472
  || state.user !== input.user || state.merchant !== input.merchant || state.merchant_origin !== input.merchantOrigin
473
473
  || !Number.isSafeInteger(state.amount) || (typeof input.amount === 'number' && state.amount !== input.amount) || state.currency !== input.currency.toLowerCase()
474
- || state.psp !== input.psp || state.mode !== 'token' || state.environment !== input.environment
474
+ || state.psp !== input.psp || state.mode !== preparationMode(input.psp) || state.environment !== input.environment
475
475
  || state.checkout_key !== input.checkoutKey)
476
476
  throw fail('ready_unconfirmed', id);
477
477
  const prepared = Object.freeze({
478
- id: preparationId, status: 'ready', psp: input.psp, environment: input.environment, expiresAt: state.ready_expires_at,
478
+ id: preparationId, status: 'ready', psp: input.psp, environment: input.environment, mode: preparationMode(input.psp), expiresAt: state.ready_expires_at,
479
479
  cardId: state.card_id, user: input.user, merchant: input.merchant, amount: state.amount,
480
480
  amountDisplay: typeof state.amount_display === 'string' ? state.amount_display : null,
481
481
  currency: input.currency.toLowerCase(), merchantOrigin: input.merchantOrigin, checkoutKey: input.checkoutKey,
@@ -582,7 +582,7 @@ export class VaultClient {
582
582
  // clientSideEncrypted entry (an older registry, a hand-built one) still
583
583
  // refuses here, exactly as before.
584
584
  const mode = rec.mode ?? 'token';
585
- if (preparation && (rec.psp !== preparation.psp || mode !== 'token'))
585
+ if (preparation && (rec.psp !== preparation.psp || mode !== preparationMode(preparation.psp)))
586
586
  throw new CheckoutPreparationError(preparation.id, 'checkout_changed');
587
587
  if (rec.clientSideEncrypted && mode !== 'cse')
588
588
  throw new CardEncryptedError(rec.psp);
@@ -3,6 +3,69 @@ export declare const CHECKOUT_PREFLIGHT_CAPABILITIES: {
3
3
  readonly scope: "processor_integration";
4
4
  readonly integration: "direct_sdk";
5
5
  readonly processors: readonly [{
6
+ readonly psp: "spreedly";
7
+ readonly status: "supported";
8
+ readonly mode: "token";
9
+ readonly operations: readonly [{
10
+ readonly id: "spreedly.card_request";
11
+ readonly effect: "processor_specific_card_request";
12
+ readonly amount_binding: "unestablished";
13
+ readonly psp: "spreedly";
14
+ readonly version: 1;
15
+ readonly mode: "token";
16
+ readonly autopilot_eligible: false;
17
+ readonly requires: "reviewed_final_payment_and_continuation_evidence";
18
+ }];
19
+ readonly scenarios: {
20
+ readonly one_time: {
21
+ readonly status: "supported";
22
+ readonly reason: "card_request_adapter_implemented";
23
+ readonly requirements: readonly ["actual_payment_request_validation", "merchant_confirmation"];
24
+ };
25
+ readonly save_card: {
26
+ readonly status: "conditional";
27
+ readonly reason: "merchant_card_storage_and_consent_required";
28
+ readonly requirements: readonly ["actual_payment_request_validation", "merchant_confirmation", "stored_card_consent", "merchant_card_saved"];
29
+ };
30
+ readonly subscription_initial: {
31
+ readonly status: "conditional";
32
+ readonly reason: "merchant_card_storage_and_consent_required";
33
+ readonly requirements: readonly ["actual_payment_request_validation", "merchant_confirmation", "stored_card_consent", "merchant_subscription_confirmation"];
34
+ };
35
+ readonly subscription_renewal: {
36
+ readonly status: "outside_vault";
37
+ readonly reason: "renewal_managed_by_merchant";
38
+ readonly requirements: readonly ["verify_merchant_billing"];
39
+ };
40
+ };
41
+ readonly limitations: readonly ["Only the listed request format is recognized; a recognized processor is not verified merchant coverage.", "The adapter handles the Spreedly iframe v1 restricted endpoint for new credit cards through the encrypted relay.", "The merchant environment and optional signed iframe session must remain valid. Expired signatures require a fresh merchant checkout.", "Spreedly creates a payment-method token; the merchant still completes any gateway payment and authentication."];
42
+ readonly unsupported_variants: readonly [{
43
+ readonly variant: "general_javascript_api";
44
+ readonly status: "unsupported";
45
+ readonly constraint: "direct_sdk";
46
+ readonly reason: "The general payment_methods.json endpoint is outside the reviewed native iframe path.";
47
+ }, {
48
+ readonly variant: "saved_card_or_recache";
49
+ readonly status: "unsupported";
50
+ readonly constraint: "direct_sdk";
51
+ readonly reason: "Stored tokens and CVV-only recache requests are outside new-card tokenization.";
52
+ }, {
53
+ readonly variant: "click_to_pay_or_bank_account";
54
+ readonly status: "unsupported";
55
+ readonly constraint: "direct_sdk";
56
+ readonly reason: "Click to Pay and bank-account request shapes require separate validation.";
57
+ }, {
58
+ readonly variant: "gateway_purchase_or_distribution";
59
+ readonly status: "unsupported";
60
+ readonly constraint: "direct_sdk";
61
+ readonly reason: "Gateway transactions and payment-method distribution are outside the card tokenization adapter.";
62
+ }];
63
+ readonly processor_amount: "none";
64
+ readonly challenge_surface: "merchant_browser";
65
+ readonly merchant_confirmation_required: true;
66
+ readonly live_validation: "not_established_by_catalog";
67
+ readonly sources: readonly ["src/definitions.js", "src/operations.js", "src/backend.js", "src/spreedly.js", "../checkout/src/prepared-processor.ts"];
68
+ }, {
6
69
  readonly psp: "shopify";
7
70
  readonly status: "supported";
8
71
  readonly mode: "token";
@@ -287,7 +350,7 @@ export declare const CHECKOUT_PREFLIGHT_CAPABILITIES: {
287
350
  readonly requirements: readonly ["verify_merchant_billing"];
288
351
  };
289
352
  };
290
- readonly limitations: readonly ["Four encrypted card fields in the Adyen Sessions payments body are required.", "Single-blob encryption, wallets, and stored-card variants are not covered."];
353
+ readonly limitations: readonly ["Four encrypted card fields in the Adyen Sessions payments body are required.", "Single-blob encryption, wallets, and stored-card variants are not covered.", "Adyen Web abandons its Sessions payment request 60 seconds after Pay. A prepared checkout moves the approval before Pay, so only the device-side encryption runs inside that window."];
291
354
  readonly unsupported_variants: readonly [{
292
355
  readonly variant: "single_blob_encryption";
293
356
  readonly status: "unsupported";
@@ -308,7 +371,7 @@ export declare const CHECKOUT_PREFLIGHT_CAPABILITIES: {
308
371
  readonly challenge_surface: "merchant_browser";
309
372
  readonly merchant_confirmation_required: true;
310
373
  readonly live_validation: "not_established_by_catalog";
311
- readonly sources: readonly ["src/definitions.js", "src/operations.js", "src/backend.js"];
374
+ readonly sources: readonly ["src/definitions.js", "src/operations.js", "src/backend.js", "src/adyen.ts", "../checkout/src/prepared-processor.ts"];
312
375
  }, {
313
376
  readonly psp: "tranzila";
314
377
  readonly status: "supported";
@@ -5,6 +5,103 @@ export const CHECKOUT_PREFLIGHT_CAPABILITIES = {
5
5
  "scope": "processor_integration",
6
6
  "integration": "direct_sdk",
7
7
  "processors": [
8
+ {
9
+ "psp": "spreedly",
10
+ "status": "supported",
11
+ "mode": "token",
12
+ "operations": [
13
+ {
14
+ "id": "spreedly.card_request",
15
+ "effect": "processor_specific_card_request",
16
+ "amount_binding": "unestablished",
17
+ "psp": "spreedly",
18
+ "version": 1,
19
+ "mode": "token",
20
+ "autopilot_eligible": false,
21
+ "requires": "reviewed_final_payment_and_continuation_evidence"
22
+ }
23
+ ],
24
+ "scenarios": {
25
+ "one_time": {
26
+ "status": "supported",
27
+ "reason": "card_request_adapter_implemented",
28
+ "requirements": [
29
+ "actual_payment_request_validation",
30
+ "merchant_confirmation"
31
+ ]
32
+ },
33
+ "save_card": {
34
+ "status": "conditional",
35
+ "reason": "merchant_card_storage_and_consent_required",
36
+ "requirements": [
37
+ "actual_payment_request_validation",
38
+ "merchant_confirmation",
39
+ "stored_card_consent",
40
+ "merchant_card_saved"
41
+ ]
42
+ },
43
+ "subscription_initial": {
44
+ "status": "conditional",
45
+ "reason": "merchant_card_storage_and_consent_required",
46
+ "requirements": [
47
+ "actual_payment_request_validation",
48
+ "merchant_confirmation",
49
+ "stored_card_consent",
50
+ "merchant_subscription_confirmation"
51
+ ]
52
+ },
53
+ "subscription_renewal": {
54
+ "status": "outside_vault",
55
+ "reason": "renewal_managed_by_merchant",
56
+ "requirements": [
57
+ "verify_merchant_billing"
58
+ ]
59
+ }
60
+ },
61
+ "limitations": [
62
+ "Only the listed request format is recognized; a recognized processor is not verified merchant coverage.",
63
+ "The adapter handles the Spreedly iframe v1 restricted endpoint for new credit cards through the encrypted relay.",
64
+ "The merchant environment and optional signed iframe session must remain valid. Expired signatures require a fresh merchant checkout.",
65
+ "Spreedly creates a payment-method token; the merchant still completes any gateway payment and authentication."
66
+ ],
67
+ "unsupported_variants": [
68
+ {
69
+ "variant": "general_javascript_api",
70
+ "status": "unsupported",
71
+ "constraint": "direct_sdk",
72
+ "reason": "The general payment_methods.json endpoint is outside the reviewed native iframe path."
73
+ },
74
+ {
75
+ "variant": "saved_card_or_recache",
76
+ "status": "unsupported",
77
+ "constraint": "direct_sdk",
78
+ "reason": "Stored tokens and CVV-only recache requests are outside new-card tokenization."
79
+ },
80
+ {
81
+ "variant": "click_to_pay_or_bank_account",
82
+ "status": "unsupported",
83
+ "constraint": "direct_sdk",
84
+ "reason": "Click to Pay and bank-account request shapes require separate validation."
85
+ },
86
+ {
87
+ "variant": "gateway_purchase_or_distribution",
88
+ "status": "unsupported",
89
+ "constraint": "direct_sdk",
90
+ "reason": "Gateway transactions and payment-method distribution are outside the card tokenization adapter."
91
+ }
92
+ ],
93
+ "processor_amount": "none",
94
+ "challenge_surface": "merchant_browser",
95
+ "merchant_confirmation_required": true,
96
+ "live_validation": "not_established_by_catalog",
97
+ "sources": [
98
+ "src/definitions.js",
99
+ "src/operations.js",
100
+ "src/backend.js",
101
+ "src/spreedly.js",
102
+ "../checkout/src/prepared-processor.ts"
103
+ ]
104
+ },
8
105
  {
9
106
  "psp": "shopify",
10
107
  "status": "supported",
@@ -435,7 +532,8 @@ export const CHECKOUT_PREFLIGHT_CAPABILITIES = {
435
532
  },
436
533
  "limitations": [
437
534
  "Four encrypted card fields in the Adyen Sessions payments body are required.",
438
- "Single-blob encryption, wallets, and stored-card variants are not covered."
535
+ "Single-blob encryption, wallets, and stored-card variants are not covered.",
536
+ "Adyen Web abandons its Sessions payment request 60 seconds after Pay. A prepared checkout moves the approval before Pay, so only the device-side encryption runs inside that window."
439
537
  ],
440
538
  "unsupported_variants": [
441
539
  {
@@ -464,7 +562,9 @@ export const CHECKOUT_PREFLIGHT_CAPABILITIES = {
464
562
  "sources": [
465
563
  "src/definitions.js",
466
564
  "src/operations.js",
467
- "src/backend.js"
565
+ "src/backend.js",
566
+ "src/adyen.ts",
567
+ "../checkout/src/prepared-processor.ts"
468
568
  ]
469
569
  },
470
570
  {