@agent-cards/checkout 0.7.0 → 0.9.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 +15 -1
- package/PREFLIGHT.md +294 -0
- package/README.md +16 -3
- package/dist/braintree.d.ts +2 -10
- package/dist/braintree.generated.d.ts +10 -0
- package/dist/braintree.generated.js +302 -0
- package/dist/braintree.js +2 -302
- package/dist/builtin-registry.generated.d.ts +2 -0
- package/dist/builtin-registry.generated.js +1 -0
- package/dist/cdp.d.ts +4 -2
- package/dist/cdp.js +38 -6
- package/dist/client.d.ts +13 -4
- package/dist/client.js +80 -8
- package/dist/index.d.ts +1 -1
- package/dist/lifecycle.d.ts +1 -0
- package/dist/lifecycle.js +27 -1
- package/dist/owned-shop.generated.d.ts +24 -0
- package/dist/owned-shop.generated.js +108 -0
- package/dist/playwright.d.ts +3 -0
- package/dist/playwright.js +3 -0
- package/dist/preflight-capabilities.generated.d.ts +1165 -0
- package/dist/preflight-capabilities.generated.js +1799 -0
- package/dist/preflight-catalog.json +3892 -0
- package/dist/preflight-playwright.d.ts +34 -0
- package/dist/preflight-playwright.js +262 -0
- package/dist/preflight-schemas.json +1063 -0
- package/dist/preflight.d.ts +1 -0
- package/dist/preflight.generated.d.ts +1867 -0
- package/dist/preflight.generated.js +482 -0
- package/dist/preflight.js +2 -0
- package/dist/registry.d.ts +2 -19
- package/dist/registry.js +3 -188
- package/dist/substitute.d.ts +5 -9
- package/dist/substitute.js +5 -69
- package/dist/substitutions.generated.d.ts +10 -0
- package/dist/substitutions.generated.js +66 -0
- package/examples/preflight/classify-direct.mjs +21 -0
- package/examples/preflight/classify-kernel.mjs +30 -0
- package/examples/preflight/inspect-browser.mjs +44 -0
- package/examples/preflight/kernel-profile.empty.json +11 -0
- package/examples/preflight/mollie-hosted.observations.json +23 -0
- package/examples/preflight/mollie-hosted.result.json +103 -0
- package/examples/preflight/stripe-script.direct.result.json +92 -0
- package/examples/preflight/stripe-script.observations.json +16 -0
- package/examples/preflight/stripe-script.result.json +87 -0
- package/package.json +16 -7
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
// Generated from @agent-cards/payment-core. Do not edit.
|
|
2
|
+
// source-sha256: ced5aecd22684c680a53b9e33dff1c5333cd36823f6fe3b4b8e84f10f6f73c53
|
|
3
|
+
import { CHECKOUT_PREFLIGHT_CAPABILITIES } from './preflight-capabilities.generated.js';
|
|
4
|
+
/** Advisory page discovery. These rules never admit a payment destination. */
|
|
5
|
+
export const CHECKOUT_PREFLIGHT_VERSION = '1';
|
|
6
|
+
export const CHECKOUT_PREFLIGHT_CATALOG_VERSION = '2026-09-11.2';
|
|
7
|
+
export const CHECKOUT_PREFLIGHT_LIMITS = Object.freeze({ frames: 64, signals: 2048, snapshot_bytes: 262144, url_length: 4096 });
|
|
8
|
+
// Identification-only IDs, checked against the canonical catalog by the core tests.
|
|
9
|
+
// No card destination, request method, or request body pattern lives here.
|
|
10
|
+
const KNOWN_PSPS = CHECKOUT_PREFLIGHT_CAPABILITIES.processors.map(processor => processor.psp);
|
|
11
|
+
const PROCESSOR_BY_ID = new Map(CHECKOUT_PREFLIGHT_CAPABILITIES.processors.map(processor => [processor.psp, processor]));
|
|
12
|
+
const KINDS = ['document', 'frame', 'script', 'iframe', 'form'];
|
|
13
|
+
const SCENARIOS = ['one_time', 'save_card', 'subscription_initial', 'subscription_renewal'];
|
|
14
|
+
const OBSERVATION_REASONS = ['collection_timeout', 'frame_limit', 'signal_limit', 'snapshot_limit', 'frame_unavailable', 'navigation_changed', 'invalid_observation', 'invalid_url', 'collection_incomplete'];
|
|
15
|
+
const RULES = [
|
|
16
|
+
{ id: 'stripe-js-v3', psp: 'stripe', kinds: ['script', 'frame', 'iframe'], hostname: /^js\.stripe\.com$/, pathname: /^\/v3(?:\/|$)/, origin: 'https://js.stripe.com', path_pattern: '/v3/*' },
|
|
17
|
+
{ id: 'square-web-sdk', psp: 'square', kinds: ['script', 'frame', 'iframe'], hostname: /^web\.squarecdn\.com$/, pathname: /^\/v1\/square\.js$/, origin: 'https://web.squarecdn.com', path_pattern: '/v1/square.js' },
|
|
18
|
+
{ id: 'mollie-components-script', psp: 'mollie', kinds: ['script'], hostname: /^js\.mollie\.com$/, pathname: /^\/v2\/mollie\.js$/, origin: 'https://js.mollie.com', path_pattern: '/v2/mollie.js' },
|
|
19
|
+
{ id: 'mollie-components-v1-script', psp: 'mollie', kinds: ['script'], hostname: /^js\.mollie\.com$/, pathname: /^\/v1\/mollie\.js$/, origin: 'https://js.mollie.com', path_pattern: '/v1/mollie.js' },
|
|
20
|
+
{ id: 'mollie-hosted-credit-card', psp: 'mollie', kinds: ['document', 'frame', 'iframe'], hostname: /^www\.mollie\.com$/, pathname: /^\/checkout\/credit-card\/session\/[A-Za-z0-9_-]+\/?$/, origin: 'https://www.mollie.com', path_pattern: '/checkout/credit-card/session/:session', flow: 'mollie.hosted_components' },
|
|
21
|
+
{ id: 'braintree-web-sdk', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/[^/]+\/js\//, origin: 'https://js.braintreegateway.com', path_pattern: '/web/:version/js/*' },
|
|
22
|
+
{ id: 'braintree-hosted-fields', psp: 'braintree', kinds: ['frame', 'iframe'], hostname: /^assets\.braintreegateway\.com$/, pathname: /^\/web\//, origin: 'https://assets.braintreegateway.com', path_pattern: '/web/*' },
|
|
23
|
+
{ id: 'paysafe-js', psp: 'paysafe', kinds: ['script', 'frame', 'iframe'], hostname: /^hosted\.paysafe\.com$/, pathname: /^\/(?:js|checkout)\//, origin: 'https://hosted.paysafe.com', path_pattern: '/{js,checkout}/*' },
|
|
24
|
+
{ id: 'worldpay-checkout-sdk', psp: 'worldpay', kinds: ['script', 'frame', 'iframe'], hostname: /^access\.worldpay\.com$/, pathname: /^\/access-checkout\//, origin: 'https://access.worldpay.com', path_pattern: '/access-checkout/*' },
|
|
25
|
+
{ id: 'authorize-net-accept', psp: 'authorize_net', kinds: ['script'], hostname: /^js\.authorize\.net$/, pathname: /^\/v[13]\/Accept(?:UI)?\.js$/, origin: 'https://js.authorize.net', path_pattern: '/v{1,3}/Accept{UI}.js' },
|
|
26
|
+
{ id: 'razorpay-checkout-script', psp: 'razorpay', kinds: ['script'], hostname: /^checkout\.razorpay\.com$/, pathname: /^\/v1\/checkout\.js$/, origin: 'https://checkout.razorpay.com', path_pattern: '/v1/checkout.js' },
|
|
27
|
+
{ id: 'vgs-collect-script', psp: 'vgs', kinds: ['script'], hostname: /^js\.verygoodvault\.com$/, pathname: /^\/vgs-collect\//, origin: 'https://js.verygoodvault.com', path_pattern: '/vgs-collect/*' },
|
|
28
|
+
{ id: 'vgs-collect-frame', psp: 'vgs', kinds: ['frame', 'iframe'], hostname: /^[a-z0-9-]+\.(?:live|sandbox)\.verygoodproxy\.com$/, pathname: /^\//, origin: 'https://:tenant.:environment.verygoodproxy.com', path_pattern: '/*' },
|
|
29
|
+
];
|
|
30
|
+
// SDK/page fingerprints identify implemented processor families, not an active payment operation.
|
|
31
|
+
RULES.push({ "id": "checkout-frames-v2-script", "psp": "checkout_com", "kinds": ["script"], "origin": "https://cdn.checkout.com", "path_pattern": "^/js/framesv2\\.min\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://checkoutdocs.readme.io/docs/frames-reference", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://cdn.checkout.com/js/framesv2.min.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^cdn\\.checkout\\.com$"), pathname: new RegExp("^/js/framesv2\\.min\\.js$") }, { "id": "adyen-web-legacy-live", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-live.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/web-best-practices/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-secured-fields-live", "psp": "adyen", "kinds": ["frame", "iframe", "document"], "origin": "https://checkoutshopper-live.adyen.com", "path_pattern": "^/checkoutshopper/securedfields/[^/]+/5\\.5\\.1/securedFields\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://checkoutshopper-live.cdn.adyen.com/checkoutshopper/sdk/6.11.0/adyen.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/securedfields/[^/]+/5\\.5\\.1/securedFields\\.html$") }, { "id": "adyen-web-legacy-test", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-test.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/web-best-practices/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-test\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-secured-fields-test", "psp": "adyen", "kinds": ["frame", "iframe", "document"], "origin": "https://checkoutshopper-test.adyen.com", "path_pattern": "^/checkoutshopper/securedfields/[^/]+/5\\.5\\.1/securedFields\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://checkoutshopper-live.cdn.adyen.com/checkoutshopper/sdk/6.11.0/adyen.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-test\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/securedfields/[^/]+/5\\.5\\.1/securedFields\\.html$") }, { "id": "stripe-js-named", "psp": "stripe", "kinds": ["script"], "origin": "https://js.stripe.com", "path_pattern": "^/(?:acacia|basil|clover|dahlia)/stripe\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.stripe.com/sdks/stripejs-versioning", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.stripe.com/sdks/stripejs-versioning?locale=en-GB", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.stripe.com/checkout/elements-with-checkout-sessions-api/changelog?locale=en-GB", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.stripe\\.com$"), pathname: new RegExp("^/(?:acacia|basil|clover|dahlia)/stripe\\.js$") }, { "id": "shopify-card-fields-script", "psp": "shopify", "kinds": ["script"], "origin": "https://checkout.pci.shopifyinc.com", "path_pattern": "^/build/[a-f0-9]+/card_fields\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://checkout.pci.shopifyinc.com/build/04ed4e1/card_fields.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\.pci\\.shopifyinc\\.com$"), pathname: new RegExp("^/build/[a-f0-9]+/card_fields\\.js$") }, { "id": "shopify-card-number-frame", "psp": "shopify", "kinds": ["frame", "iframe", "document"], "origin": "https://checkout.pci.shopifyinc.com", "path_pattern": "^/build/[a-f0-9]+/number-(?:ltr|rtl)\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://checkout.pci.shopifyinc.com/build/04ed4e1/card_fields.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\.pci\\.shopifyinc\\.com$"), pathname: new RegExp("^/build/[a-f0-9]+/number-(?:ltr|rtl)\\.html$") }, { "id": "shopify-card-fields-legacy-script", "psp": "shopify", "kinds": ["script"], "origin": "https://checkout.shopifycs.com", "path_pattern": "^/dist/card_fields\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://checkout.shopifycs.com/dist/card_fields.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\.shopifycs\\.com$"), pathname: new RegExp("^/dist/card_fields\\.js$") }, { "id": "stripe-js-script", "psp": "stripe", "kinds": ["script"], "origin": "https://js.stripe.com", "path_pattern": "^/v3/?$", "sources": [{ "kind": "primary_source", "reference": "https://docs.stripe.com/js/including", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.stripe\\.com$"), pathname: new RegExp("^/v3/?$") }, { "id": "braintree-dropin-script", "psp": "braintree", "kinds": ["script"], "origin": "https://js.braintreegateway.com", "path_pattern": "^/web/dropin/[0-9]+\\.[0-9]+\\.[0-9]+/js/dropin(?:\\.min)?\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.paypal.com/braintree/docs/start/hello-client/javascript/v3/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.braintreegateway\\.com$"), pathname: new RegExp("^/web/dropin/[0-9]+\\.[0-9]+\\.[0-9]+/js/dropin(?:\\.min)?\\.js$") }, { "id": "checkout-flow-script", "psp": "checkout_com", "kinds": ["script"], "origin": "https://checkout-web-components.checkout.com", "path_pattern": "^/index\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://www.checkout.com/docs/get-started", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://checkout-web-components.checkout.com/index.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\-web\\-components\\.checkout\\.com$"), pathname: new RegExp("^/index\\.js$") }, { "id": "checkout-flow-frame", "psp": "checkout_com", "kinds": ["frame", "iframe", "document"], "origin": "https://checkout-web-components.checkout.com", "path_pattern": "^/pages/[0-9]+\\.[0-9]+\\.[0-9]+/index\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://checkout-web-components.checkout.com/index.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\-web\\-components\\.checkout\\.com$"), pathname: new RegExp("^/pages/[0-9]+\\.[0-9]+\\.[0-9]+/index\\.html$") }, { "id": "adyen-web-script", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-live.cdn.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/standard/integration/drop-in/best-practices", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\.cdn\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-web-script-host-2", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-test.cdn.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/standard/integration/drop-in/best-practices", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-test\\.cdn\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-web-script-host-3", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-live-us.cdn.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/standard/integration/drop-in/best-practices", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\-us\\.cdn\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-web-script-host-4", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-live-au.cdn.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/standard/integration/drop-in/best-practices", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\-au\\.cdn\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-web-script-host-5", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-live-nea.cdn.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/standard/integration/drop-in/best-practices", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\-nea\\.cdn\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "adyen-web-script-host-6", "psp": "adyen", "kinds": ["script"], "origin": "https://checkoutshopper-live-in.cdn.adyen.com", "path_pattern": "^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.adyen.com/standard/integration/drop-in/best-practices", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/online-payments/build-your-integration/sessions-flow/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkoutshopper\\-live\\-in\\.cdn\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/sdk/[0-9]+\\.[0-9]+\\.[0-9]+/adyen\\.js$") }, { "id": "tranzila-hosted-page", "psp": "tranzila", "kinds": ["frame", "iframe", "document", "form"], "origin": "https://direct.tranzila.com", "path_pattern": "^/[A-Za-z0-9_-]+/iframenew\\.php$", "sources": [{ "kind": "primary_source", "reference": "https://docs.tranzila.com/docs/payments-and-billing/iframe-integration", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.tranzila.com/docs/payments-and-billing/iframe-integration-directng", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.tranzila.com/docs/emv-pinpad/emv-iframe-integration", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^direct\\.tranzila\\.com$"), pathname: new RegExp("^/[A-Za-z0-9_-]+/iframenew\\.php$") }, { "id": "tranzila-hosted-page-host-2", "psp": "tranzila", "kinds": ["frame", "iframe", "document", "form"], "origin": "https://directng.tranzila.com", "path_pattern": "^/[A-Za-z0-9_-]+/iframenew\\.php$", "sources": [{ "kind": "primary_source", "reference": "https://docs.tranzila.com/docs/payments-and-billing/iframe-integration", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.tranzila.com/docs/payments-and-billing/iframe-integration-directng", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.tranzila.com/docs/emv-pinpad/emv-iframe-integration", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^directng\\.tranzila\\.com$"), pathname: new RegExp("^/[A-Za-z0-9_-]+/iframenew\\.php$") }, { "id": "square-web-script", "psp": "square", "kinds": ["script"], "origin": "https://web.squarecdn.com", "path_pattern": "^/v1/square\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.squareup.com/docs/web-payments/quickstart/add-sdk-to-web-client", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^web\\.squarecdn\\.com$"), pathname: new RegExp("^/v1/square\\.js$") }, { "id": "square-web-script-host-2", "psp": "square", "kinds": ["script"], "origin": "https://sandbox.web.squarecdn.com", "path_pattern": "^/v1/square\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.squareup.com/docs/web-payments/quickstart/add-sdk-to-web-client", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^sandbox\\.web\\.squarecdn\\.com$"), pathname: new RegExp("^/v1/square\\.js$") }, { "id": "authorize-net-accept-script", "psp": "authorize_net", "kinds": ["script"], "origin": "https://js.authorize.net", "path_pattern": "^/v1/Accept\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.authorize.net/api/reference/features/acceptjs.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.authorize\\.net$"), pathname: new RegExp("^/v1/Accept\\.js$") }, { "id": "authorize-net-accept-script-host-2", "psp": "authorize_net", "kinds": ["script"], "origin": "https://jstest.authorize.net", "path_pattern": "^/v1/Accept\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.authorize.net/api/reference/features/acceptjs.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^jstest\\.authorize\\.net$"), pathname: new RegExp("^/v1/Accept\\.js$") }, { "id": "authorize-net-acceptui-script", "psp": "authorize_net", "kinds": ["script"], "origin": "https://js.authorize.net", "path_pattern": "^/v3/AcceptUI\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.authorize.net/api/reference/features/acceptjs.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.authorize\\.net$"), pathname: new RegExp("^/v3/AcceptUI\\.js$") }, { "id": "authorize-net-acceptui-script-host-2", "psp": "authorize_net", "kinds": ["script"], "origin": "https://jstest.authorize.net", "path_pattern": "^/v3/AcceptUI\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.authorize.net/api/reference/features/acceptjs.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^jstest\\.authorize\\.net$"), pathname: new RegExp("^/v3/AcceptUI\\.js$") }, { "id": "worldpay-checkout-script", "psp": "worldpay", "kinds": ["script"], "origin": "https://access.worldpay.com", "path_pattern": "^/access-checkout/v2/checkout\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.worldpay.com/access/products/checkout/web", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^access\\.worldpay\\.com$"), pathname: new RegExp("^/access-checkout/v2/checkout\\.js$") }, { "id": "worldpay-checkout-script-host-2", "psp": "worldpay", "kinds": ["script"], "origin": "https://try.access.worldpay.com", "path_pattern": "^/access-checkout/v2/checkout\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.worldpay.com/access/products/checkout/web", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^try\\.access\\.worldpay\\.com$"), pathname: new RegExp("^/access-checkout/v2/checkout\\.js$") }, { "id": "nuvei-websdk-script", "psp": "nuvei", "kinds": ["script"], "origin": "https://cdn.safecharge.com", "path_pattern": "^/safecharge_resources/v1/websdk/safecharge\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.nuvei.com/documentation/accept-payment/web-sdk/quick-start-for-web-sdk/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://cdn.safecharge.com/safecharge_resources/v1/websdk/safecharge.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^cdn\\.safecharge\\.com$"), pathname: new RegExp("^/safecharge_resources/v1/websdk/safecharge\\.js$") }, { "id": "nuvei-card-fields-frame", "psp": "nuvei", "kinds": ["frame", "iframe", "document"], "origin": "https://cdn.safecharge.com", "path_pattern": "^/safecharge_resources/v1/websdk/sfc_inner_card\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://docs.nuvei.com/documentation/accept-payment/web-sdk/nuvei-fields/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://cdn.safecharge.com/safecharge_resources/v1/websdk/sfc_inner_card.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^cdn\\.safecharge\\.com$"), pathname: new RegExp("^/safecharge_resources/v1/websdk/sfc_inner_card\\.html$") }, { "id": "airwallex-components-script", "psp": "airwallex", "kinds": ["script"], "origin": "https://static.airwallex.com", "path_pattern": "^/components/sdk/v1/index\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://www.airwallex.com/docs/payments/get-started/quickstart", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://www.airwallex.com/docs/payments/test-and-go-live/upgrade-to-airwallex.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://www.airwallex.com/docs/payouts/test-and-go-live/upgrade-to-airwallex.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^static\\.airwallex\\.com$"), pathname: new RegExp("^/components/sdk/v1/index\\.js$") }, { "id": "airwallex-elements-legacy-script", "psp": "airwallex", "kinds": ["script"], "origin": "https://checkout.airwallex.com", "path_pattern": "^/assets/elements\\.bundle\\.min\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://www.airwallex.com/docs/payments/test-and-go-live/upgrade-to-airwallex.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\.airwallex\\.com$"), pathname: new RegExp("^/assets/elements\\.bundle\\.min\\.js$") }, { "id": "rapyd-checkout-toolkit-script", "psp": "rapyd", "kinds": ["script"], "origin": "https://checkouttoolkit.rapyd.net", "path_pattern": "^/$", "sources": [{ "kind": "primary_source", "reference": "https://docs.rapyd.net/en/checkout-toolkit-integration.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkouttoolkit\\.rapyd\\.net$"), pathname: new RegExp("^/$") }, { "id": "rapyd-checkout-toolkit-script-host-2", "psp": "rapyd", "kinds": ["script"], "origin": "https://sandboxcheckouttoolkit.rapyd.net", "path_pattern": "^/$", "sources": [{ "kind": "primary_source", "reference": "https://docs.rapyd.net/en/checkout-toolkit-integration.html", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^sandboxcheckouttoolkit\\.rapyd\\.net$"), pathname: new RegExp("^/$") }, { "id": "dlocal-smart-fields-script", "psp": "dlocal", "kinds": ["script"], "origin": "https://js.dlocal.com", "path_pattern": "^/$", "sources": [{ "kind": "primary_source", "reference": "https://docs.dlocal.com/docs/set-up-smart-fields", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.dlocal.com/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://static.dlocal.com/modules/fields/6.18.2/iframe.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.dlocal\\.com$"), pathname: new RegExp("^/$") }, { "id": "dlocal-smart-fields-sandbox-script", "psp": "dlocal", "kinds": ["script"], "origin": "https://js-sandbox.dlocal.com", "path_pattern": "^/$", "sources": [{ "kind": "primary_source", "reference": "https://docs.dlocal.com/docs/set-up-smart-fields", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.dlocal.com/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://static.dlocal.com/modules/fields/6.18.2/iframe.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\-sandbox\\.dlocal\\.com$"), pathname: new RegExp("^/$") }, { "id": "dlocal-smart-fields-parent-6-18-2", "psp": "dlocal", "kinds": ["script"], "origin": "https://static.dlocal.com", "path_pattern": "^/modules/fields/6\\.18\\.2/parent\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.dlocal.com/docs/set-up-smart-fields", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.dlocal.com/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://static.dlocal.com/modules/fields/6.18.2/iframe.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^static\\.dlocal\\.com$"), pathname: new RegExp("^/modules/fields/6\\.18\\.2/parent\\.js$") }, { "id": "dlocal-smart-card-fields-6-18-2", "psp": "dlocal", "kinds": ["frame", "iframe", "document"], "origin": "https://static.dlocal.com", "path_pattern": "^/modules/fields/6\\.18\\.2/inner-(?:pan|card)\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://docs.dlocal.com/docs/set-up-smart-fields", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.dlocal.com/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://static.dlocal.com/modules/fields/6.18.2/iframe.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^static\\.dlocal\\.com$"), pathname: new RegExp("^/modules/fields/6\\.18\\.2/inner-(?:pan|card)\\.html$") }, { "id": "ebanx-js-latest", "psp": "ebanx", "kinds": ["script"], "origin": "https://ebanx-js.ebanx.com", "path_pattern": "^/latest/dist/ebanx\\.min\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.ebanx.com/docs/pay-in/features/cards/tokenization/tokenization-ebanx-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://ebanx-js.ebanx.com/latest/dist/ebanx.min.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^ebanx\\-js\\.ebanx\\.com$"), pathname: new RegExp("^/latest/dist/ebanx\\.min\\.js$") }, { "id": "mercado-pago-sdk-v2", "psp": "mercado_pago", "kinds": ["script"], "origin": "https://sdk.mercadopago.com", "path_pattern": "^/js/v2$", "sources": [{ "kind": "primary_source", "reference": "https://github.com/mercadopago/sdk-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://sdk.mercadopago.com/js/v2", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^sdk\\.mercadopago\\.com$"), pathname: new RegExp("^/js/v2$") }, { "id": "payu-gpo-sdk", "psp": "payu", "kinds": ["script"], "origin": "https://secure.payu.com", "path_pattern": "^/javascript/sdk$", "sources": [{ "kind": "primary_source", "reference": "https://developers.payu.com/europe/docs/checkout/secure-elements/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://secure.payu.com/javascript/sdk", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^secure\\.payu\\.com$"), pathname: new RegExp("^/javascript/sdk$") }, { "id": "payu-gpo-sdk-sandbox", "psp": "payu", "kinds": ["script"], "origin": "https://secure.snd.payu.com", "path_pattern": "^/javascript/sdk$", "sources": [{ "kind": "primary_source", "reference": "https://developers.payu.com/europe/docs/checkout/secure-elements/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://secure.payu.com/javascript/sdk", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^secure\\.snd\\.payu\\.com$"), pathname: new RegExp("^/javascript/sdk$") }, { "id": "payu-gpo-sdk-sandbox-host-2", "psp": "payu", "kinds": ["script"], "origin": "https://merch-prod.snd.payu.com", "path_pattern": "^/javascript/sdk$", "sources": [{ "kind": "primary_source", "reference": "https://developers.payu.com/europe/docs/checkout/secure-elements/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://secure.payu.com/javascript/sdk", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^merch\\-prod\\.snd\\.payu\\.com$"), pathname: new RegExp("^/javascript/sdk$") }, { "id": "payu-gpo-secure-form", "psp": "payu", "kinds": ["frame", "iframe", "document"], "origin": "https://secure.payu.com", "path_pattern": "^/front/secure-form/form/$", "sources": [{ "kind": "primary_source", "reference": "https://developers.payu.com/europe/docs/checkout/secure-elements/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://secure.payu.com/javascript/sdk", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^secure\\.payu\\.com$"), pathname: new RegExp("^/front/secure-form/form/$") }, { "id": "razorpay-standard-checkout-v1", "psp": "razorpay", "kinds": ["script"], "origin": "https://checkout.razorpay.com", "path_pattern": "^/v1/checkout\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://razorpay.com/docs/payments/payment-gateway/web-integration/standard/integration-steps/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://checkout.razorpay.com/v1/checkout.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^checkout\\.razorpay\\.com$"), pathname: new RegExp("^/v1/checkout\\.js$") }, { "id": "mollie-components-v1", "psp": "mollie", "kinds": ["script"], "origin": "https://js.mollie.com", "path_pattern": "^/v1/mollie\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.mollie.com/docs/mollie-components", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.mollie.com/docs/custom-checkout-components", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.mollie.com/v2/mollie.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.mollie\\.com$"), pathname: new RegExp("^/v1/mollie\\.js$") }, { "id": "mollie-components-v2", "psp": "mollie", "kinds": ["script"], "origin": "https://js.mollie.com", "path_pattern": "^/v2/mollie\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.mollie.com/docs/mollie-components", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://docs.mollie.com/docs/custom-checkout-components", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.mollie.com/v2/mollie.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.mollie\\.com$"), pathname: new RegExp("^/v2/mollie\\.js$") }, { "id": "paysafe-js-v1-latest", "psp": "paysafe", "kinds": ["script"], "origin": "https://hosted.paysafe.com", "path_pattern": "^/js/v1/latest/paysafe\\.min\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.paysafe.com/en/api-docs/paysafe-js/overview/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://hosted.paysafe.com/js/v1/latest/paysafe.min.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://developer.paysafe.com/en/api-docs/3ds/using-the-sdk/including-the-3d-secure-2-sdk/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^hosted\\.paysafe\\.com$"), pathname: new RegExp("^/js/v1/latest/paysafe\\.min\\.js$") }, { "id": "paysafe-js-v1-fields", "psp": "paysafe", "kinds": ["frame", "iframe", "document"], "origin": "https://hosted.paysafe.com", "path_pattern": "^/js/v1/latest/index\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://developer.paysafe.com/en/api-docs/paysafe-js/overview/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://hosted.paysafe.com/js/v1/latest/paysafe.min.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://developer.paysafe.com/en/api-docs/3ds/using-the-sdk/including-the-3d-secure-2-sdk/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^hosted\\.paysafe\\.com$"), pathname: new RegExp("^/js/v1/latest/index\\.html$") }, { "id": "paysafe-js-v1-fields-host-2", "psp": "paysafe", "kinds": ["frame", "iframe", "document"], "origin": "https://hosted.test.paysafe.com", "path_pattern": "^/js/v1/latest/index\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://developer.paysafe.com/en/api-docs/paysafe-js/overview/", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://hosted.paysafe.com/js/v1/latest/paysafe.min.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://developer.paysafe.com/en/api-docs/3ds/using-the-sdk/including-the-3d-secure-2-sdk/", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^hosted\\.test\\.paysafe\\.com$"), pathname: new RegExp("^/js/v1/latest/index\\.html$") }, { "id": "recurly-js-v4", "psp": "recurly", "kinds": ["script"], "origin": "https://js.recurly.com", "path_pattern": "^/v4/recurly\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://recurly.com/developers/reference/recurly-js/index.html", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://github.com/recurly/recurly-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.recurly.com/v4/recurly.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.recurly\\.com$"), pathname: new RegExp("^/v4/recurly\\.js$") }, { "id": "recurly-hosted-field-v1", "psp": "recurly", "kinds": ["frame", "iframe", "document"], "origin": "https://api.recurly.com", "path_pattern": "^/js/v1/field\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://recurly.com/developers/reference/recurly-js/index.html", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://github.com/recurly/recurly-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.recurly.com/v4/recurly.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^api\\.recurly\\.com$"), pathname: new RegExp("^/js/v1/field\\.html$") }, { "id": "recurly-hosted-field-v1-host-2", "psp": "recurly", "kinds": ["frame", "iframe", "document"], "origin": "https://api.eu.recurly.com", "path_pattern": "^/js/v1/field\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://recurly.com/developers/reference/recurly-js/index.html", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://github.com/recurly/recurly-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.recurly.com/v4/recurly.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^api\\.eu\\.recurly\\.com$"), pathname: new RegExp("^/js/v1/field\\.html$") }, { "id": "moneris-hosted-tokenization", "psp": "moneris", "kinds": ["frame", "iframe", "document"], "origin": "https://www3.moneris.com", "path_pattern": "^/HPPtoken/index\\.php$", "sources": [{ "kind": "primary_source", "reference": "https://developer.moneris.com/hosted-solutions/docs/setup-guide-tokenization", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://developer.moneris.com/moneris-api/docs/purchase-with-payment-token", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://www3.moneris.com/HPPtoken/hpp_common.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^www3\\.moneris\\.com$"), pathname: new RegExp("^/HPPtoken/index\\.php$") }, { "id": "moneris-hosted-tokenization-host-2", "psp": "moneris", "kinds": ["frame", "iframe", "document"], "origin": "https://esqa.moneris.com", "path_pattern": "^/HPPtoken/index\\.php$", "sources": [{ "kind": "primary_source", "reference": "https://developer.moneris.com/hosted-solutions/docs/setup-guide-tokenization", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://developer.moneris.com/moneris-api/docs/purchase-with-payment-token", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://www3.moneris.com/HPPtoken/hpp_common.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^esqa\\.moneris\\.com$"), pathname: new RegExp("^/HPPtoken/index\\.php$") }, { "id": "moneris-new-io-hosted-tokenization", "psp": "moneris", "kinds": ["frame", "iframe", "document"], "origin": "https://mpg1t.moneris.io", "path_pattern": "^/HPPtoken/index\\.php$", "sources": [{ "kind": "primary_source", "reference": "https://developer.moneris.com/hosted-solutions/docs/setup-guide-tokenization", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://developer.moneris.com/moneris-api/docs/purchase-with-payment-token", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://www3.moneris.com/HPPtoken/hpp_common.js", "reviewed_on": "2026-09-11" }], "variant_unverified": "The current canonical Moneris recognizer contains moneris.com hosts only; moneris.io is not admitted.", hostname: new RegExp("^mpg1t\\.moneris\\.io$"), pathname: new RegExp("^/HPPtoken/index\\.php$") }, { "id": "bambora-na-custom-checkout-1", "psp": "bambora", "kinds": ["script"], "origin": "https://libs.na.bambora.com", "path_pattern": "^/customcheckout/1/customcheckout\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://docs.na.worldline-solutions.com/build-your-integration/checkout-form/custom-checkout/reference", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://libs.na.bambora.com/customcheckout/1/customcheckout.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://libs.na.bambora.com/customcheckout/1.0.5-39/customcheckoutremote.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^libs\\.na\\.bambora\\.com$"), pathname: new RegExp("^/customcheckout/1/customcheckout\\.js$") }, { "id": "bambora-na-fields-1-0-5-39", "psp": "bambora", "kinds": ["frame", "iframe", "document"], "origin": "https://libs.na.bambora.com", "path_pattern": "^/customcheckout/1\\.0\\.5-39/iframe\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://docs.na.worldline-solutions.com/build-your-integration/checkout-form/custom-checkout/reference", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://libs.na.bambora.com/customcheckout/1/customcheckout.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://libs.na.bambora.com/customcheckout/1.0.5-39/customcheckoutremote.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^libs\\.na\\.bambora\\.com$"), pathname: new RegExp("^/customcheckout/1\\.0\\.5-39/iframe\\.html$") }, { "id": "global-payments-js-4-1-3", "psp": "global_payments", "kinds": ["script"], "origin": "https://js.globalpay.com", "path_pattern": "^/4\\.1\\.3/globalpayments\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.globalpayments.com/docs/integration-options/sdk/javascript", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://github.com/globalpayments/globalpayments-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.globalpay.com/4.1.3/globalpayments.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.globalpay.com/v1/globalpayments.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.globalpay\\.com$"), pathname: new RegExp("^/4\\.1\\.3/globalpayments\\.js$") }, { "id": "global-payments-js-v1-alias", "psp": "global_payments", "kinds": ["script"], "origin": "https://js.globalpay.com", "path_pattern": "^/v1/globalpayments\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://developer.globalpayments.com/docs/integration-options/sdk/javascript", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://github.com/globalpayments/globalpayments-js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.globalpay.com/4.1.3/globalpayments.js", "reviewed_on": "2026-09-11" }, { "kind": "primary_source", "reference": "https://js.globalpay.com/v1/globalpayments.js", "reviewed_on": "2026-09-11" }], hostname: new RegExp("^js\\.globalpay\\.com$"), pathname: new RegExp("^/v1/globalpayments\\.js$") });
|
|
32
|
+
// Keep sandbox evidence truthful instead of labelling it with a production origin.
|
|
33
|
+
for (const [baseId, suffix, hostname, origin] of [
|
|
34
|
+
['square-web-sdk', 'sandbox', /^sandbox\.web\.squarecdn\.com$/, 'https://sandbox.web.squarecdn.com'],
|
|
35
|
+
['paysafe-js', 'test', /^hosted\.test\.paysafe\.com$/, 'https://hosted.test.paysafe.com'],
|
|
36
|
+
['worldpay-checkout-sdk', 'try', /^try\.access\.worldpay\.com$/, 'https://try.access.worldpay.com'],
|
|
37
|
+
['authorize-net-accept', 'test', /^jstest\.authorize\.net$/, 'https://jstest.authorize.net'],
|
|
38
|
+
]) {
|
|
39
|
+
RULES.push({ ...RULES.find(rule => rule.id === baseId), id: `${baseId}-${suffix}`, hostname, origin });
|
|
40
|
+
}
|
|
41
|
+
const SOURCES = {
|
|
42
|
+
stripe: [{ kind: 'official_documentation', reference: 'https://docs.stripe.com/js/including', reviewed_on: '2026-09-11' }],
|
|
43
|
+
square: [{ kind: 'official_documentation', reference: 'https://developer.squareup.com/docs/web-payments/quickstart/add-sdk-to-web-client', reviewed_on: '2026-09-11' }],
|
|
44
|
+
mollie: [{ kind: 'saved_observation', reference: 'mollie-haak-2026-09-11:hosted-credit-card-document-and-v2-script', reviewed_on: '2026-09-11' }],
|
|
45
|
+
braintree: [{ kind: 'official_documentation', reference: 'https://developer.paypal.com/braintree/docs/start/hello-client/javascript/v3/', reviewed_on: '2026-09-11' }],
|
|
46
|
+
paysafe: [{ kind: 'official_documentation', reference: 'https://developer.paysafe.com/en/api-docs/paysafe-js/overview/', reviewed_on: '2026-09-11' }],
|
|
47
|
+
worldpay: [{ kind: 'official_documentation', reference: 'https://developer.worldpay.com/products/checkout/web', reviewed_on: '2026-09-11' }],
|
|
48
|
+
authorize_net: [{ kind: 'official_documentation', reference: 'https://developer.authorize.net/api/reference/features/acceptjs.html', reviewed_on: '2026-09-11' }],
|
|
49
|
+
razorpay: [{ kind: 'official_documentation', reference: 'https://razorpay.com/docs/payments/payment-gateway/web-integration/standard/integration-steps/', reviewed_on: '2026-09-11' }],
|
|
50
|
+
vgs: [{ kind: 'official_documentation', reference: 'https://docs.verygoodsecurity.com/vault/developer-tools/vgs-collect/js/integration', reviewed_on: '2026-09-11' }],
|
|
51
|
+
};
|
|
52
|
+
const FLOWS = [
|
|
53
|
+
{ id: 'mollie.hosted_components', psp: 'mollie', operation: 'mollie.card_request', operation_version: 1, mode: 'token', server_constraint: 'eligible', limitations: [
|
|
54
|
+
'The payment request must match the supported Mollie Components card-token format.',
|
|
55
|
+
'Authentication and merchant confirmation must finish in the merchant browser.',
|
|
56
|
+
'Merchant configuration, card eligibility and processor approval still determine whether the purchase succeeds.',
|
|
57
|
+
] },
|
|
58
|
+
{ id: 'vgs.iframe_bound_tokenization', psp: 'vgs', operation: 'vgs.iframe_bound_tokenization', operation_version: 1, mode: 'token', server_constraint: 'unsupported', limitations: [
|
|
59
|
+
'VGS requires tokenization from its own iframe; the Vault cannot replay this flow from the approval page.',
|
|
60
|
+
] },
|
|
61
|
+
];
|
|
62
|
+
const FLOW_BY_ID = new Map(FLOWS.map(flow => [flow.id, flow]));
|
|
63
|
+
const RULE_BY_ID = new Map(RULES.map(rule => [rule.id, rule]));
|
|
64
|
+
const DEFAULT_PROFILE = {
|
|
65
|
+
schema_version: 1, profile_version: 'preflight-2', catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION,
|
|
66
|
+
integration: { id: 'direct_sdk', version: 'bundled' },
|
|
67
|
+
processor_entries: CHECKOUT_PREFLIGHT_CAPABILITIES.processors.map(processor => ({
|
|
68
|
+
psp: processor.psp, mode: processor.mode, scenario: 'one_time', status: 'supported',
|
|
69
|
+
})),
|
|
70
|
+
entries: [{ flow: 'mollie.hosted_components', operation: 'mollie.card_request', operation_version: 1, mode: 'token', scenario: 'one_time', status: 'supported' }],
|
|
71
|
+
};
|
|
72
|
+
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
73
|
+
const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
74
|
+
const exactKeys = (value, keys) => Object.keys(value).every(key => keys.includes(key));
|
|
75
|
+
const id = (value) => typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$/.test(value);
|
|
76
|
+
const frameId = (value) => Number.isSafeInteger(value) && value >= 0 && value < CHECKOUT_PREFLIGHT_LIMITS.frames;
|
|
77
|
+
const unique = (items) => [...new Set(items)];
|
|
78
|
+
function dateTime(value) {
|
|
79
|
+
if (typeof value !== 'string' || value.length > 40)
|
|
80
|
+
return false;
|
|
81
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?(?:Z|[+-](\d{2}):(\d{2}))$/.exec(value);
|
|
82
|
+
if (!match)
|
|
83
|
+
return false;
|
|
84
|
+
const [year, month, day, hour, minute, second, zoneHour = 0, zoneMinute = 0] = match.slice(1).map(part => part === undefined ? 0 : Number(part));
|
|
85
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
86
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
87
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= days[month - 1] && hour <= 23 && minute <= 59 && second <= 59
|
|
88
|
+
&& zoneHour <= 23 && zoneMinute <= 59 && Number.isFinite(Date.parse(value));
|
|
89
|
+
}
|
|
90
|
+
function metadata(value) {
|
|
91
|
+
return KINDS.includes(value.kind) && frameId(value.frame_id) && (value.parent_frame_id === null || frameId(value.parent_frame_id))
|
|
92
|
+
&& (value.frame_id !== 0 || value.parent_frame_id === null)
|
|
93
|
+
&& value.parent_frame_id !== value.frame_id && (value.visible === null || typeof value.visible === 'boolean');
|
|
94
|
+
}
|
|
95
|
+
function consistentParents(signals) {
|
|
96
|
+
const parents = new Map();
|
|
97
|
+
for (const signal of signals) {
|
|
98
|
+
if (parents.has(signal.frame_id) && parents.get(signal.frame_id) !== signal.parent_frame_id)
|
|
99
|
+
return false;
|
|
100
|
+
parents.set(signal.frame_id, signal.parent_frame_id);
|
|
101
|
+
}
|
|
102
|
+
for (const frame of parents.keys()) {
|
|
103
|
+
const visited = new Set();
|
|
104
|
+
let current = frame;
|
|
105
|
+
while (current !== null && current !== undefined) {
|
|
106
|
+
if (visited.has(current))
|
|
107
|
+
return false;
|
|
108
|
+
visited.add(current);
|
|
109
|
+
current = parents.get(current);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
function cleanObservation(input) {
|
|
115
|
+
if (!record(input) || !exactKeys(input, ['complete', 'truncated', 'reasons']) || typeof input.complete !== 'boolean' || typeof input.truncated !== 'boolean'
|
|
116
|
+
|| !Array.isArray(input.reasons) || input.reasons.length > 32 || Array.from(input.reasons).some(reason => !OBSERVATION_REASONS.includes(reason))) {
|
|
117
|
+
return { complete: false, truncated: false, reasons: ['invalid_observation'] };
|
|
118
|
+
}
|
|
119
|
+
const reasons = unique(input.reasons);
|
|
120
|
+
const complete = input.complete && !input.truncated && reasons.length === 0;
|
|
121
|
+
return { complete, truncated: input.truncated, reasons: complete ? [] : unique([...reasons, 'collection_incomplete']) };
|
|
122
|
+
}
|
|
123
|
+
const byteLength = (value) => new TextEncoder().encode(JSON.stringify(value)).length;
|
|
124
|
+
function incomplete(observation, reason, truncated = false) {
|
|
125
|
+
observation.complete = false;
|
|
126
|
+
observation.truncated ||= truncated;
|
|
127
|
+
observation.reasons = unique([...observation.reasons, reason]);
|
|
128
|
+
}
|
|
129
|
+
/** Normalize observations locally. Unknown assets are omitted; no URL leaves this function. */
|
|
130
|
+
export function normalizeCheckoutSignals(input) {
|
|
131
|
+
const snapshot = { schema_version: 1, catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION, signals: [], observation: cleanObservation(record(input) ? input.observation : null) };
|
|
132
|
+
if (!record(input) || !exactKeys(input, ['signals', 'observation']) || !Array.isArray(input.signals)) {
|
|
133
|
+
incomplete(snapshot.observation, 'invalid_observation');
|
|
134
|
+
return snapshot;
|
|
135
|
+
}
|
|
136
|
+
if (input.signals.length > CHECKOUT_PREFLIGHT_LIMITS.signals)
|
|
137
|
+
incomplete(snapshot.observation, 'signal_limit', true);
|
|
138
|
+
const seen = new Set();
|
|
139
|
+
for (const raw of input.signals.slice(0, CHECKOUT_PREFLIGHT_LIMITS.signals)) {
|
|
140
|
+
if (!record(raw) || !exactKeys(raw, ['kind', 'url', 'frame_id', 'parent_frame_id', 'visible']) || !metadata(raw)) {
|
|
141
|
+
incomplete(snapshot.observation, 'invalid_observation');
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (typeof raw.url !== 'string' || raw.url.length > CHECKOUT_PREFLIGHT_LIMITS.url_length) {
|
|
145
|
+
incomplete(snapshot.observation, 'invalid_url');
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
let url;
|
|
149
|
+
try {
|
|
150
|
+
url = new URL(raw.url);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
incomplete(snapshot.observation, 'invalid_url');
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
// Non-HTTPS local/blank frames provide no PSP evidence. Credentials and nondefault ports are refused.
|
|
157
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.port)
|
|
158
|
+
continue;
|
|
159
|
+
for (const rule of RULES) {
|
|
160
|
+
if (!rule.kinds.includes(raw.kind) || !rule.hostname.test(url.hostname) || !rule.pathname.test(url.pathname))
|
|
161
|
+
continue;
|
|
162
|
+
const signal = { rule_id: rule.id, kind: raw.kind, frame_id: raw.frame_id, parent_frame_id: raw.parent_frame_id, visible: raw.visible };
|
|
163
|
+
const key = JSON.stringify(signal);
|
|
164
|
+
if (!seen.has(key)) {
|
|
165
|
+
// One observed asset can match more than one reviewed fingerprint.
|
|
166
|
+
// Bound normalized evidence independently of the raw observation count.
|
|
167
|
+
if (snapshot.signals.length >= CHECKOUT_PREFLIGHT_LIMITS.signals) {
|
|
168
|
+
incomplete(snapshot.observation, 'signal_limit', true);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
snapshot.signals.push(signal);
|
|
172
|
+
seen.add(key);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
while (byteLength(snapshot) > CHECKOUT_PREFLIGHT_LIMITS.snapshot_bytes && snapshot.signals.length) {
|
|
177
|
+
snapshot.signals.pop();
|
|
178
|
+
incomplete(snapshot.observation, 'snapshot_limit', true);
|
|
179
|
+
}
|
|
180
|
+
if (!consistentParents(snapshot.signals))
|
|
181
|
+
incomplete(snapshot.observation, 'invalid_observation');
|
|
182
|
+
return snapshot;
|
|
183
|
+
}
|
|
184
|
+
/** The returned catalog is metadata only. It does not change card destination recognition. */
|
|
185
|
+
export function getCheckoutPreflightCatalog() {
|
|
186
|
+
return clone({
|
|
187
|
+
schema_version: 1, catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION, scope: 'preflight_hint',
|
|
188
|
+
integration_version_note: 'bundled describes the capabilities accompanying this helper, not a claim about another installed SDK version.',
|
|
189
|
+
limits: CHECKOUT_PREFLIGHT_LIMITS,
|
|
190
|
+
known_psps: KNOWN_PSPS.map(psp => ({ psp, detection: RULES.some(rule => rule.psp === psp) ? 'reviewed_rules' : 'unknown' })),
|
|
191
|
+
rules: RULES.map(({ hostname, pathname, ...rule }) => ({ ...rule, hostname_pattern: hostname.source, pathname_pattern: pathname.source, sources: rule.sources ?? SOURCES[rule.psp] })),
|
|
192
|
+
processors: CHECKOUT_PREFLIGHT_CAPABILITIES.processors, exclusions: CHECKOUT_PREFLIGHT_CAPABILITIES.exclusions,
|
|
193
|
+
flows: FLOWS, direct_sdk_profile: DEFAULT_PROFILE,
|
|
194
|
+
kernel_profile_template: { schema_version: 1, profile_version: 'kernel-template-1', catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION,
|
|
195
|
+
integration: { id: 'kernel_native', version: 'REPLACE-WITH-ADAPTER-VERSION' }, entries: [], processor_entries: [] },
|
|
196
|
+
observation_reason_codes: OBSERVATION_REASONS,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function validSnapshot(input) {
|
|
200
|
+
return record(input) && exactKeys(input, ['schema_version', 'catalog_version', 'signals', 'observation'])
|
|
201
|
+
&& input.schema_version === 1 && input.catalog_version === CHECKOUT_PREFLIGHT_CATALOG_VERSION
|
|
202
|
+
&& Array.isArray(input.signals) && input.signals.length <= CHECKOUT_PREFLIGHT_LIMITS.signals
|
|
203
|
+
&& Array.from(input.signals).every(signal => record(signal) && exactKeys(signal, ['rule_id', 'kind', 'frame_id', 'parent_frame_id', 'visible']) && metadata(signal)
|
|
204
|
+
&& id(signal.rule_id) && !!RULE_BY_ID.get(signal.rule_id)?.kinds.includes(signal.kind))
|
|
205
|
+
&& consistentParents(input.signals)
|
|
206
|
+
&& record(input.observation) && exactKeys(input.observation, ['complete', 'truncated', 'reasons'])
|
|
207
|
+
&& typeof input.observation.complete === 'boolean' && typeof input.observation.truncated === 'boolean'
|
|
208
|
+
&& Array.isArray(input.observation.reasons) && input.observation.reasons.length <= 32
|
|
209
|
+
&& Array.from(input.observation.reasons).every(reason => OBSERVATION_REASONS.includes(reason))
|
|
210
|
+
&& byteLength(input) <= CHECKOUT_PREFLIGHT_LIMITS.snapshot_bytes;
|
|
211
|
+
}
|
|
212
|
+
function validIntegration(value) {
|
|
213
|
+
return record(value) && exactKeys(value, ['id', 'version']) && ['direct_sdk', 'kernel_native'].includes(value.id) && (value.version === null || id(value.version));
|
|
214
|
+
}
|
|
215
|
+
function profileError(profile, integration, now) {
|
|
216
|
+
if (!record(profile) || !exactKeys(profile, ['schema_version', 'profile_version', 'catalog_version', 'integration', 'entries', 'processor_entries', 'expires_at'])
|
|
217
|
+
|| profile.schema_version !== 1 || !id(profile.profile_version) || !validIntegration(profile.integration) || !id(profile.integration.version)
|
|
218
|
+
|| !Array.isArray(profile.entries) || profile.entries.length > 256
|
|
219
|
+
|| (profile.processor_entries !== undefined && (!Array.isArray(profile.processor_entries) || profile.processor_entries.length > 256)))
|
|
220
|
+
return 'integration_profile_invalid';
|
|
221
|
+
if (profile.catalog_version !== CHECKOUT_PREFLIGHT_CATALOG_VERSION)
|
|
222
|
+
return 'integration_profile_incompatible';
|
|
223
|
+
if (profile.integration.id !== integration.id || profile.integration.version !== integration.version)
|
|
224
|
+
return 'integration_profile_mismatch';
|
|
225
|
+
if (profile.expires_at !== undefined) {
|
|
226
|
+
const expires = dateTime(profile.expires_at) ? Date.parse(profile.expires_at) : NaN;
|
|
227
|
+
const time = now === undefined ? Date.now() : dateTime(now) ? Date.parse(now) : NaN;
|
|
228
|
+
if (!Number.isFinite(expires) || !Number.isFinite(time))
|
|
229
|
+
return 'integration_profile_invalid';
|
|
230
|
+
if (expires <= time)
|
|
231
|
+
return 'integration_profile_expired';
|
|
232
|
+
}
|
|
233
|
+
const entries = new Set();
|
|
234
|
+
for (const entry of profile.entries) {
|
|
235
|
+
if (!record(entry) || !exactKeys(entry, ['flow', 'operation', 'operation_version', 'mode', 'scenario', 'status', 'limitations'])
|
|
236
|
+
|| !id(entry.flow) || !id(entry.operation) || !SCENARIOS.includes(entry.scenario) || !['supported', 'unsupported'].includes(entry.status)
|
|
237
|
+
|| (entry.limitations !== undefined && (!Array.isArray(entry.limitations) || entry.limitations.length > 16 || Array.from(entry.limitations).some(text => typeof text !== 'string' || text.length > 256 || /[\x00-\x1f]/.test(text)))))
|
|
238
|
+
return 'integration_profile_invalid';
|
|
239
|
+
const flow = FLOW_BY_ID.get(entry.flow);
|
|
240
|
+
if (!flow || entry.operation !== flow.operation || entry.operation_version !== flow.operation_version || entry.mode !== flow.mode)
|
|
241
|
+
return 'integration_profile_incompatible';
|
|
242
|
+
const key = `${entry.flow}:${entry.scenario}`;
|
|
243
|
+
if (entries.has(key))
|
|
244
|
+
return 'integration_profile_invalid';
|
|
245
|
+
entries.add(key);
|
|
246
|
+
}
|
|
247
|
+
const processors = new Set();
|
|
248
|
+
for (const entry of profile.processor_entries ?? []) {
|
|
249
|
+
if (!record(entry) || !exactKeys(entry, ['psp', 'mode', 'scenario', 'status', 'limitations'])
|
|
250
|
+
|| !id(entry.psp) || !SCENARIOS.includes(entry.scenario) || !['supported', 'unsupported'].includes(entry.status)
|
|
251
|
+
|| (entry.limitations !== undefined && (!Array.isArray(entry.limitations) || entry.limitations.length > 16 || Array.from(entry.limitations).some(text => typeof text !== 'string' || text.length > 256 || /[\x00-\x1f]/.test(text)))))
|
|
252
|
+
return 'integration_profile_invalid';
|
|
253
|
+
const processor = PROCESSOR_BY_ID.get(entry.psp);
|
|
254
|
+
if (!processor || processor.mode !== entry.mode)
|
|
255
|
+
return 'integration_profile_incompatible';
|
|
256
|
+
const key = `${entry.psp}:${entry.scenario}`;
|
|
257
|
+
if (processors.has(key))
|
|
258
|
+
return 'integration_profile_invalid';
|
|
259
|
+
processors.add(key);
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
/** Classify a sanitized snapshot. No browser, HTTP, or payment operations occur here. */
|
|
264
|
+
export function assessCheckoutSupport(input, options = {}) {
|
|
265
|
+
const opts = record(options) ? options : {};
|
|
266
|
+
const fallbackIntegrationId = record(opts.integration) && opts.integration.id === 'kernel_native' ? 'kernel_native' : 'direct_sdk';
|
|
267
|
+
const integration = validIntegration(opts.integration) ? clone(opts.integration) : {
|
|
268
|
+
id: fallbackIntegrationId,
|
|
269
|
+
version: opts.integration === undefined ? 'bundled' : null,
|
|
270
|
+
};
|
|
271
|
+
const result = {
|
|
272
|
+
schema_version: 1, catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION, integration,
|
|
273
|
+
scenario: opts.scenario !== undefined && SCENARIOS.includes(opts.scenario) ? opts.scenario : 'one_time', scenario_defaulted: opts.scenario === undefined,
|
|
274
|
+
psp: null, flow: null, mode: null, status: 'unknown', support_scope: null, checkout_flow_status: 'unknown', candidates: [], reason_codes: [], limitations: [], missing_evidence: [],
|
|
275
|
+
observation: { complete: false, truncated: false, reasons: ['invalid_observation'] }, scope: 'preflight_hint', merchant_confirmation_required: true,
|
|
276
|
+
};
|
|
277
|
+
if (!record(options) || !exactKeys(opts, ['scenario', 'integration', 'profile', 'now']) || (opts.scenario !== undefined && !SCENARIOS.includes(opts.scenario))
|
|
278
|
+
|| (opts.integration !== undefined && !validIntegration(opts.integration)) || (opts.now !== undefined && !dateTime(opts.now))) {
|
|
279
|
+
result.reason_codes = ['invalid_options'];
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
if (!validSnapshot(input)) {
|
|
283
|
+
result.reason_codes = [record(input) && input.catalog_version !== CHECKOUT_PREFLIGHT_CATALOG_VERSION ? 'catalog_incompatible' : 'invalid_snapshot'];
|
|
284
|
+
return result;
|
|
285
|
+
}
|
|
286
|
+
result.observation = cleanObservation(input.observation);
|
|
287
|
+
const candidates = new Map();
|
|
288
|
+
const activeFlows = new Set();
|
|
289
|
+
const activePsps = new Set();
|
|
290
|
+
const activeFlowFrames = new Set();
|
|
291
|
+
let missingCorroboration = false;
|
|
292
|
+
for (const signal of input.signals) {
|
|
293
|
+
const rule = RULE_BY_ID.get(signal.rule_id);
|
|
294
|
+
const active = signal.visible === true && (['frame', 'iframe', 'form'].includes(signal.kind)
|
|
295
|
+
|| signal.kind === 'document' && signal.frame_id === 0 && signal.parent_frame_id === null);
|
|
296
|
+
const candidate = candidates.get(rule.psp) ?? { psp: rule.psp, relevance: 'undetermined', evidence: [] };
|
|
297
|
+
candidate.evidence.push({ ...clone(signal), origin: rule.origin, path_pattern: rule.path_pattern });
|
|
298
|
+
if (active) {
|
|
299
|
+
candidate.relevance = 'active';
|
|
300
|
+
activePsps.add(rule.psp);
|
|
301
|
+
if (rule.flow) {
|
|
302
|
+
// A hosted route can also display an expired/error page. Require the reviewed
|
|
303
|
+
// Components script in that document; a similarly named asset elsewhere is insufficient.
|
|
304
|
+
const corroborated = rule.flow !== 'mollie.hosted_components' || signal.kind !== 'iframe' && input.signals.some(other => other.rule_id === 'mollie-components-script' && other.kind === 'script' && other.frame_id === signal.frame_id && other.parent_frame_id === signal.parent_frame_id);
|
|
305
|
+
if (corroborated) {
|
|
306
|
+
activeFlows.add(rule.flow);
|
|
307
|
+
activeFlowFrames.add(signal.frame_id);
|
|
308
|
+
}
|
|
309
|
+
else
|
|
310
|
+
missingCorroboration = true;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
candidates.set(rule.psp, candidate);
|
|
314
|
+
}
|
|
315
|
+
result.candidates = [...candidates.values()].sort((a, b) => a.psp.localeCompare(b.psp));
|
|
316
|
+
if (missingCorroboration && activeFlows.size === 0)
|
|
317
|
+
result.reason_codes.push('flow_corroboration_missing');
|
|
318
|
+
if (activePsps.size === 1)
|
|
319
|
+
result.psp = [...activePsps][0];
|
|
320
|
+
else if (activePsps.size === 0 && candidates.size === 1)
|
|
321
|
+
result.psp = [...candidates.keys()][0];
|
|
322
|
+
if (!result.observation.complete)
|
|
323
|
+
result.reason_codes.push('observation_incomplete');
|
|
324
|
+
if (!input.signals.length) {
|
|
325
|
+
result.reason_codes.push('psp_not_detected');
|
|
326
|
+
result.missing_evidence.push('payment_page_signal');
|
|
327
|
+
}
|
|
328
|
+
if (activePsps.size > 1 || activeFlows.size > 1 || activeFlowFrames.size > 1 || activePsps.size === 0 && candidates.size > 1)
|
|
329
|
+
result.reason_codes.push('ambiguous_checkout');
|
|
330
|
+
if (activeFlows.size !== 1 || result.psp === null) {
|
|
331
|
+
result.reason_codes.push('checkout_flow_unidentified');
|
|
332
|
+
result.missing_evidence.push('active_checkout_flow');
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
const flow = FLOW_BY_ID.get([...activeFlows][0]);
|
|
336
|
+
if (flow.psp === result.psp) {
|
|
337
|
+
result.flow = flow.id;
|
|
338
|
+
result.mode = flow.mode;
|
|
339
|
+
result.limitations.push(...flow.limitations);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (!result.flow && result.candidates.length)
|
|
343
|
+
result.limitations.push('Processor assets alone do not establish the active card payment flow.');
|
|
344
|
+
const profile = opts.profile === undefined ? (integration.id === 'direct_sdk' && integration.version === 'bundled' ? DEFAULT_PROFILE : undefined) : opts.profile;
|
|
345
|
+
let error = profile === undefined ? 'integration_profile_missing' : profileError(profile, integration, opts.now);
|
|
346
|
+
if (integration.version === null)
|
|
347
|
+
error = 'integration_version_missing';
|
|
348
|
+
if (error) {
|
|
349
|
+
result.reason_codes.push(error);
|
|
350
|
+
result.missing_evidence.push('compatible_integration_profile');
|
|
351
|
+
}
|
|
352
|
+
result.reason_codes = unique(result.reason_codes);
|
|
353
|
+
const ambiguous = result.reason_codes.includes('ambiguous_checkout');
|
|
354
|
+
result.checkout_flow_status = ambiguous ? 'ambiguous' : result.flow ? 'identified' : 'unknown';
|
|
355
|
+
// Processor capability and observed checkout compatibility answer different questions.
|
|
356
|
+
// Missing flow evidence is advisory when an identified processor has an explicit
|
|
357
|
+
// implementation declaration. Incomplete, contradictory or invalid input stays unknown.
|
|
358
|
+
if (!result.observation.complete || ambiguous || error || !result.psp || missingCorroboration && !result.flow)
|
|
359
|
+
return result;
|
|
360
|
+
const flow = result.flow ? FLOW_BY_ID.get(result.flow) : undefined;
|
|
361
|
+
if (flow?.server_constraint === 'unsupported') {
|
|
362
|
+
result.status = 'unsupported';
|
|
363
|
+
result.support_scope = 'checkout_flow';
|
|
364
|
+
result.reason_codes.push('vault_flow_unsupported');
|
|
365
|
+
return result;
|
|
366
|
+
}
|
|
367
|
+
// A hidden document cannot lend its scripts to a visible card checkout. Script-only
|
|
368
|
+
// observations with no known hidden container can still identify an available adapter.
|
|
369
|
+
const eligible = input.signals.some(signal => RULE_BY_ID.get(signal.rule_id).psp === result.psp
|
|
370
|
+
&& (signal.kind === 'script' ? signal.visible !== false && !input.signals.some(container => container.frame_id === signal.frame_id && ['document', 'frame'].includes(container.kind) && container.visible !== true)
|
|
371
|
+
: signal.visible === true));
|
|
372
|
+
if (!eligible) {
|
|
373
|
+
result.reason_codes.push('payment_signal_inactive');
|
|
374
|
+
return result;
|
|
375
|
+
}
|
|
376
|
+
{
|
|
377
|
+
// A native declaration cannot expand the shared Vault's admitted processor variants.
|
|
378
|
+
const incompatible = input.signals.map(signal => ({ signal, rule: RULE_BY_ID.get(signal.rule_id) }))
|
|
379
|
+
.filter(({ signal, rule }) => rule.psp === result.psp && rule.variant_unverified
|
|
380
|
+
&& (signal.kind === 'script' ? signal.visible !== false : signal.visible === true));
|
|
381
|
+
if (incompatible.length) {
|
|
382
|
+
result.reason_codes.push('processor_variant_unverified');
|
|
383
|
+
result.limitations.push(...incompatible.map(({ rule }) => rule.variant_unverified));
|
|
384
|
+
return result;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
const processor = PROCESSOR_BY_ID.get(result.psp);
|
|
388
|
+
const scenario = processor?.scenarios[result.scenario];
|
|
389
|
+
if (processor) {
|
|
390
|
+
const bundledSdk = integration.id === 'direct_sdk' && integration.version === 'bundled';
|
|
391
|
+
if (bundledSdk)
|
|
392
|
+
result.limitations.push(...processor.limitations);
|
|
393
|
+
for (const variant of processor.unsupported_variants) {
|
|
394
|
+
if (bundledSdk || variant.constraint === 'vault')
|
|
395
|
+
result.limitations.push(variant.reason);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (scenario && (scenario.status === 'unsupported' || scenario.status === 'outside_vault')) {
|
|
399
|
+
result.status = 'unsupported';
|
|
400
|
+
result.mode = processor.mode;
|
|
401
|
+
result.support_scope = 'processor_integration';
|
|
402
|
+
result.reason_codes.push(scenario.reason);
|
|
403
|
+
return result;
|
|
404
|
+
}
|
|
405
|
+
const profileValue = profile;
|
|
406
|
+
const entry = flow && profileValue.entries.find(entry => entry.flow === flow.id && entry.scenario === result.scenario);
|
|
407
|
+
if (entry) {
|
|
408
|
+
result.status = entry.status;
|
|
409
|
+
result.support_scope = 'checkout_flow';
|
|
410
|
+
result.reason_codes.push(entry.status === 'supported' ? 'flow_supported_by_integration' : 'integration_flow_unsupported');
|
|
411
|
+
result.limitations.push(...(entry.limitations ?? []));
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
if (!processor)
|
|
415
|
+
return result;
|
|
416
|
+
result.mode = processor.mode;
|
|
417
|
+
const processorEntry = profileValue.processor_entries?.find(entry => entry.psp === processor.psp && entry.scenario === result.scenario);
|
|
418
|
+
if (!processorEntry) {
|
|
419
|
+
result.reason_codes.push(flow ? 'integration_flow_undeclared' : 'integration_processor_undeclared');
|
|
420
|
+
result.missing_evidence.push(flow ? 'integration_flow_capability' : 'integration_processor_capability');
|
|
421
|
+
return result;
|
|
422
|
+
}
|
|
423
|
+
result.status = processorEntry.status;
|
|
424
|
+
result.support_scope = 'processor_integration';
|
|
425
|
+
result.reason_codes.push(processorEntry.status === 'supported' ? 'processor_supported_by_integration' : 'integration_processor_unsupported');
|
|
426
|
+
result.limitations.push(...(processorEntry.limitations ?? []));
|
|
427
|
+
result.limitations = unique(result.limitations);
|
|
428
|
+
return result;
|
|
429
|
+
}
|
|
430
|
+
/** Machine-readable transport shapes; semantic compatibility is checked by the classifier. */
|
|
431
|
+
export function getCheckoutPreflightSchemas() {
|
|
432
|
+
const text = { type: 'string', minLength: 1, maxLength: 120, pattern: '^[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$' };
|
|
433
|
+
const integration = { type: 'object', additionalProperties: false, required: ['id', 'version'], properties: { id: { enum: ['direct_sdk', 'kernel_native'] }, version: { anyOf: [text, { type: 'null' }] } } };
|
|
434
|
+
const observation = { type: 'object', additionalProperties: false, required: ['complete', 'truncated', 'reasons'], properties: { complete: { type: 'boolean' }, truncated: { type: 'boolean' }, reasons: { type: 'array', maxItems: 32, uniqueItems: true, items: { enum: OBSERVATION_REASONS } } } };
|
|
435
|
+
const signal = { type: 'object', additionalProperties: false, required: ['rule_id', 'kind', 'frame_id', 'parent_frame_id', 'visible'], properties: {
|
|
436
|
+
rule_id: { enum: RULES.map(rule => rule.id) }, kind: { enum: KINDS }, frame_id: { type: 'integer', minimum: 0, maximum: 63 },
|
|
437
|
+
parent_frame_id: { anyOf: [{ type: 'integer', minimum: 0, maximum: 63 }, { type: 'null' }] }, visible: { type: ['boolean', 'null'] },
|
|
438
|
+
} };
|
|
439
|
+
const profile = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
440
|
+
required: ['schema_version', 'profile_version', 'catalog_version', 'integration', 'entries'], properties: {
|
|
441
|
+
schema_version: { const: 1 }, profile_version: text, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION }, integration,
|
|
442
|
+
processor_entries: { type: 'array', maxItems: 256, items: {
|
|
443
|
+
type: 'object', additionalProperties: false, required: ['psp', 'mode', 'scenario', 'status'], properties: {
|
|
444
|
+
psp: { enum: KNOWN_PSPS }, mode: { enum: ['token', 'cse', 'hosted_form'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
|
|
445
|
+
limitations: { type: 'array', maxItems: 16, items: { type: 'string', maxLength: 256, pattern: '^[^\\u0000-\\u001f]*$' } },
|
|
446
|
+
},
|
|
447
|
+
} },
|
|
448
|
+
expires_at: { type: 'string', format: 'date-time', maxLength: 40 }, entries: { type: 'array', maxItems: 256, items: {
|
|
449
|
+
type: 'object', additionalProperties: false, required: ['flow', 'operation', 'operation_version', 'mode', 'scenario', 'status'], properties: {
|
|
450
|
+
flow: { enum: FLOWS.map(flow => flow.id) }, operation: { enum: FLOWS.map(flow => flow.operation) }, operation_version: { const: 1 },
|
|
451
|
+
mode: { enum: ['token', 'cse', 'hosted_form'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
|
|
452
|
+
limitations: { type: 'array', maxItems: 16, items: { type: 'string', maxLength: 256, pattern: '^[^\\u0000-\\u001f]*$' } },
|
|
453
|
+
},
|
|
454
|
+
} },
|
|
455
|
+
} };
|
|
456
|
+
const snapshot = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
457
|
+
required: ['schema_version', 'catalog_version', 'signals', 'observation'], properties: {
|
|
458
|
+
schema_version: { const: 1 }, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION },
|
|
459
|
+
signals: { type: 'array', maxItems: CHECKOUT_PREFLIGHT_LIMITS.signals, items: signal }, observation,
|
|
460
|
+
} };
|
|
461
|
+
const evidence = { ...signal, required: [...signal.required, 'origin', 'path_pattern'], properties: { ...signal.properties, origin: { type: 'string', maxLength: 256 }, path_pattern: { type: 'string', maxLength: 256 } } };
|
|
462
|
+
const result = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
463
|
+
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: {
|
|
464
|
+
schema_version: { const: 1 }, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION }, integration,
|
|
465
|
+
scenario: { enum: SCENARIOS }, scenario_defaulted: { type: 'boolean' }, psp: { type: ['string', 'null'] }, flow: { type: ['string', 'null'] }, mode: { enum: ['token', 'cse', 'hosted_form', null] },
|
|
466
|
+
support_scope: { enum: ['processor_integration', 'checkout_flow', null] }, checkout_flow_status: { enum: ['identified', 'unknown', 'ambiguous'] },
|
|
467
|
+
status: { enum: ['supported', 'unsupported', 'unknown'] }, candidates: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['psp', 'relevance', 'evidence'], properties: {
|
|
468
|
+
psp: { type: 'string' }, relevance: { enum: ['active', 'undetermined'] }, evidence: { type: 'array', items: evidence },
|
|
469
|
+
} } }, reason_codes: { type: 'array', items: { type: 'string' } }, limitations: { type: 'array', items: { type: 'string' } }, missing_evidence: { type: 'array', items: { type: 'string' } }, observation,
|
|
470
|
+
scope: { const: 'preflight_hint' }, merchant_confirmation_required: { const: true },
|
|
471
|
+
} };
|
|
472
|
+
const rawSignalProperties = { ...signal.properties, url: { type: 'string', maxLength: CHECKOUT_PREFLIGHT_LIMITS.url_length } };
|
|
473
|
+
delete rawSignalProperties.rule_id;
|
|
474
|
+
const observations = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
475
|
+
required: ['signals', 'observation'], properties: { signals: { type: 'array', maxItems: CHECKOUT_PREFLIGHT_LIMITS.signals, items: {
|
|
476
|
+
type: 'object', additionalProperties: false, required: ['url', 'kind', 'frame_id', 'parent_frame_id', 'visible'], properties: rawSignalProperties,
|
|
477
|
+
} }, observation } };
|
|
478
|
+
const options = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false, properties: {
|
|
479
|
+
scenario: { enum: SCENARIOS }, integration, profile, now: { type: 'string', format: 'date-time', maxLength: 40 },
|
|
480
|
+
} };
|
|
481
|
+
return clone({ observations, options, snapshot, profile, result });
|
|
482
|
+
}
|