@agent-cards/sdk 0.20.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 +198 -0
- package/PREFLIGHT.md +314 -0
- package/README.md +864 -0
- package/dist/adyen-merchant-hosted.generated.d.ts +277 -0
- package/dist/adyen-merchant-hosted.generated.js +1902 -0
- package/dist/adyen.generated.d.ts +24 -0
- package/dist/adyen.generated.js +64 -0
- package/dist/agentcard.d.ts +72 -0
- package/dist/agentcard.js +71 -0
- package/dist/api-module.d.ts +54 -0
- package/dist/api-module.js +85 -0
- package/dist/attachment.d.ts +11 -0
- package/dist/attachment.js +50 -0
- package/dist/braintree.d.ts +2 -0
- package/dist/braintree.generated.d.ts +10 -0
- package/dist/braintree.generated.js +302 -0
- package/dist/braintree.js +2 -0
- package/dist/builtin-registry.generated.d.ts +2 -0
- package/dist/builtin-registry.generated.js +1 -0
- package/dist/buy.d.ts +374 -0
- package/dist/buy.js +314 -0
- package/dist/card-fields.generated.d.ts +3 -0
- package/dist/card-fields.generated.js +88 -0
- package/dist/cdp.d.ts +202 -0
- package/dist/cdp.js +2540 -0
- package/dist/checkout-com.generated.d.ts +4 -0
- package/dist/checkout-com.generated.js +183 -0
- package/dist/client.d.ts +929 -0
- package/dist/client.js +1896 -0
- package/dist/cse-body.d.ts +25 -0
- package/dist/cse-body.js +41 -0
- package/dist/fiserv.d.ts +65 -0
- package/dist/fiserv.generated.d.ts +73 -0
- package/dist/fiserv.generated.js +830 -0
- package/dist/fiserv.js +104 -0
- package/dist/hosted-form.d.ts +44 -0
- package/dist/hosted-form.js +78 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +26 -0
- package/dist/languages.d.ts +23 -0
- package/dist/languages.js +76 -0
- package/dist/lifecycle.d.ts +235 -0
- package/dist/lifecycle.js +405 -0
- package/dist/mercado-checkout.d.ts +20 -0
- package/dist/mercado-checkout.generated.d.ts +52 -0
- package/dist/mercado-checkout.generated.js +198 -0
- package/dist/mercado-checkout.js +108 -0
- package/dist/merchant-handoff.d.ts +54 -0
- package/dist/merchant-handoff.js +100 -0
- package/dist/merchant-hosted.d.ts +140 -0
- package/dist/merchant-hosted.js +170 -0
- package/dist/merchant-total-watch.d.ts +115 -0
- package/dist/merchant-total-watch.js +268 -0
- package/dist/merchant-total.d.ts +257 -0
- package/dist/merchant-total.js +383 -0
- package/dist/owned-shop.generated.d.ts +24 -0
- package/dist/owned-shop.generated.js +108 -0
- package/dist/paysafe.generated.d.ts +12 -0
- package/dist/paysafe.generated.js +87 -0
- package/dist/playwright.d.ts +3 -0
- package/dist/playwright.js +3 -0
- package/dist/pre-claim.d.ts +123 -0
- package/dist/pre-claim.js +386 -0
- package/dist/preflight-capabilities.generated.d.ts +1311 -0
- package/dist/preflight-capabilities.generated.js +2007 -0
- package/dist/preflight-catalog.json +5031 -0
- package/dist/preflight-playwright.d.ts +34 -0
- package/dist/preflight-playwright.js +355 -0
- package/dist/preflight-schemas.json +1144 -0
- package/dist/preflight.d.ts +1 -0
- package/dist/preflight.generated.d.ts +2023 -0
- package/dist/preflight.generated.js +604 -0
- package/dist/preflight.js +2 -0
- package/dist/preparation.d.ts +38 -0
- package/dist/preparation.js +191 -0
- package/dist/prepared-processor.d.ts +43 -0
- package/dist/prepared-processor.js +172 -0
- package/dist/recurly.generated.d.ts +1 -0
- package/dist/recurly.generated.js +87 -0
- package/dist/registry.d.ts +145 -0
- package/dist/registry.js +338 -0
- package/dist/requests.d.ts +66 -0
- package/dist/requests.js +76 -0
- package/dist/spreedly.generated.d.ts +10 -0
- package/dist/spreedly.generated.js +332 -0
- package/dist/stripe-checkout.d.ts +81 -0
- package/dist/stripe-checkout.generated.d.ts +82 -0
- package/dist/stripe-checkout.generated.js +1153 -0
- package/dist/stripe-checkout.js +140 -0
- package/dist/substitute.d.ts +38 -0
- package/dist/substitute.js +23 -0
- package/dist/substitutions.generated.d.ts +11 -0
- package/dist/substitutions.generated.js +818 -0
- package/dist/voice-pkce.d.ts +49 -0
- package/dist/voice-pkce.js +147 -0
- package/dist/voice-retry.d.ts +6 -0
- package/dist/voice-retry.js +27 -0
- package/dist/voice-stream.generated.d.ts +118 -0
- package/dist/voice-stream.generated.js +170 -0
- package/dist/voice-types.d.ts +708 -0
- package/dist/voice-types.js +16 -0
- package/dist/voice.d.ts +365 -0
- package/dist/voice.js +807 -0
- package/dist/webhooks.d.ts +87 -0
- package/dist/webhooks.js +136 -0
- package/examples/existing-browser.mjs +62 -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-native/README.md +112 -0
- package/examples/preflight/kernel-native/documented-adapters.json +113 -0
- package/examples/preflight/kernel-native/inventory.json +242 -0
- package/examples/preflight/kernel-native/qualification.mjs +182 -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 +53 -0
- package/readme.test.mjs +53 -0
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
// Generated from @agent-cards/payment-core. Do not edit.
|
|
2
|
+
// source-sha256: 667b42af10e827f2c772927b1ff5491e5775a7d6b397ca72c8ddd093cdce2005
|
|
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-15.1';
|
|
7
|
+
// `url_length` bounds the origin and path that discovery matches. `raw_url_length` bounds the
|
|
8
|
+
// observed location including its query, which never takes part in matching or evidence.
|
|
9
|
+
export const CHECKOUT_PREFLIGHT_LIMITS = Object.freeze({ frames: 64, signals: 2048, snapshot_bytes: 262144, url_length: 4096, raw_url_length: 65536 });
|
|
10
|
+
// Identification-only IDs, checked against the canonical catalog by the core tests.
|
|
11
|
+
// No card destination, request method, or request body pattern lives here.
|
|
12
|
+
const KNOWN_PSPS = CHECKOUT_PREFLIGHT_CAPABILITIES.processors.map(processor => processor.psp);
|
|
13
|
+
const PROCESSOR_BY_ID = new Map(CHECKOUT_PREFLIGHT_CAPABILITIES.processors.map(processor => [processor.psp, processor]));
|
|
14
|
+
const KINDS = ['document', 'frame', 'script', 'iframe', 'form'];
|
|
15
|
+
const SCENARIOS = ['one_time', 'save_card', 'subscription_initial', 'subscription_renewal'];
|
|
16
|
+
const OBSERVATION_REASONS = ['collection_timeout', 'frame_limit', 'signal_limit', 'snapshot_limit', 'frame_unavailable', 'navigation_changed', 'invalid_observation', 'invalid_url', 'card_entry_unrecognized', 'collection_incomplete'];
|
|
17
|
+
const RULES = [
|
|
18
|
+
{ id: 'stripe-js-v3', psp: 'stripe', kinds: ['script', 'frame', 'iframe'], hostname: /^js\.stripe\.com$/, pathname: /^\/v3(?:\/|$)/, origin: 'https://js.stripe.com', path_pattern: '/v3/*' },
|
|
19
|
+
{ 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' },
|
|
20
|
+
{ 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' },
|
|
21
|
+
{ 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' },
|
|
22
|
+
{ 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' },
|
|
23
|
+
{ 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/*' },
|
|
24
|
+
{ id: 'braintree-hosted-fields', psp: 'braintree', kinds: ['frame', 'iframe'], hostname: /^assets\.braintreegateway\.com$/, pathname: /^\/web\//, origin: 'https://assets.braintreegateway.com', path_pattern: '/web/*' },
|
|
25
|
+
{ 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}/*' },
|
|
26
|
+
{ 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/*' },
|
|
27
|
+
{ 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' },
|
|
28
|
+
{ 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' },
|
|
29
|
+
{ id: 'vgs-collect-script', psp: 'vgs', kinds: ['script'], hostname: /^js\.verygoodvault\.com$/, pathname: /^\/vgs-collect\//, origin: 'https://js.verygoodvault.com', path_pattern: '/vgs-collect/*' },
|
|
30
|
+
{ 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: '/*' },
|
|
31
|
+
];
|
|
32
|
+
// SDK/page fingerprints identify implemented processor families, not an active payment operation.
|
|
33
|
+
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/[^/]+/[0-9]+\\.[0-9]+\\.[0-9]+/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" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/development-resources/live-endpoints/", "reviewed_on": "2026-09-15" }, { "kind": "partner_observation", "reference": "kernel-discovery-2026-09-14:adyen-secured-fields-4.9.0-live-us", "reviewed_on": "2026-09-15" }], hostname: new RegExp("^checkoutshopper\\-live\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/securedfields/[^/]+/[0-9]+\\.[0-9]+\\.[0-9]+/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/[^/]+/[0-9]+\\.[0-9]+\\.[0-9]+/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" }, { "kind": "primary_source", "reference": "https://docs.adyen.com/development-resources/live-endpoints/", "reviewed_on": "2026-09-15" }], hostname: new RegExp("^checkoutshopper\\-test\\.adyen\\.com$"), pathname: new RegExp("^/checkoutshopper/securedfields/[^/]+/[0-9]+\\.[0-9]+\\.[0-9]+/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": "spreedly-iframe-v1-script", "psp": "spreedly", "kinds": ["script"], "origin": "https://core.spreedly.com", "path_pattern": "^/iframe/iframe-v1\\.min\\.js$", "sources": [{ "kind": "primary_source", "reference": "https://core.spreedly.com/iframe/iframe-v1.min.js", "reviewed_on": "2026-09-16" }], hostname: new RegExp("^core\\.spreedly\\.com$"), pathname: new RegExp("^/iframe/iframe-v1\\.min\\.js$") }, { "id": "spreedly-native-card-frame", "psp": "spreedly", "kinds": ["frame", "iframe", "document"], "origin": "https://core.spreedly.com", "path_pattern": "^/v1/embedded/(?:number|cvv)-frame-1\\.[0-9]{1,4}\\.html$", "sources": [{ "kind": "primary_source", "reference": "https://core.spreedly.com/iframe/iframe-v1.min.js", "reviewed_on": "2026-09-16" }], hostname: new RegExp("^core\\.spreedly\\.com$"), pathname: new RegExp("^/v1/embedded/(?:number|cvv)-frame-1\\.[0-9]{1,4}\\.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$") });
|
|
34
|
+
// Hosted, regional and versioned product variants seen on live checkouts (Kernel discovery report,
|
|
35
|
+
// 2026-09-14). A variant the implemented adapter does not admit still identifies its processor and
|
|
36
|
+
// stays unknown through `variant_unverified`; no entry here expands a recognized card destination.
|
|
37
|
+
const documented = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-15' });
|
|
38
|
+
const observed = (reference) => ({ kind: 'partner_observation', reference: `kernel-discovery-2026-09-14:${reference}`, reviewed_on: '2026-09-15' });
|
|
39
|
+
RULES.push({ id: 'tranzila-terminal-hosted-page', psp: 'tranzila', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^direct\.tranzila\.com$/, pathname: /^\/(?!process(?:\/|$)|done(?:\/|$))[A-Za-z0-9_-]+\/?$/, origin: 'https://direct.tranzila.com', path_pattern: '/:terminal/',
|
|
40
|
+
sources: [documented('https://docs.tranzila.com/docs/payments-and-billing/hosted-payment-page'), observed('tranzila-terminal-root-hosted-page')],
|
|
41
|
+
variant_unverified: 'The implemented Tranzila hosted form covers direct.tranzila.com/:terminal/iframenew.php; a terminal-root hosted payment page is not admitted.' }, { id: 'square-card-element-frame', psp: 'square', kinds: ['frame', 'iframe'], hostname: /^web\.squarecdn\.com$/, pathname: /^\/[0-9]+\.[0-9]+\.[0-9]+\/single-card-element-iframe\.html$/, origin: 'https://web.squarecdn.com', path_pattern: '/:version/single-card-element-iframe.html',
|
|
42
|
+
sources: [documented('https://developer.squareup.com/docs/web-payments/take-card-payment'), observed('square-single-card-element-iframe-1.84.5')] }, { id: 'authorize-net-sim-hosted-form', psp: 'authorize_net', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^secure\.authorize\.net$/, pathname: /^\/gateway\/transact\.dll$/, origin: 'https://secure.authorize.net', path_pattern: '/gateway/transact.dll',
|
|
43
|
+
sources: [documented('https://developer.authorize.net/api/upgrade_guide.html'), observed('authorize-net-hosted-transact-form')],
|
|
44
|
+
variant_unverified: 'The implemented Authorize.Net adapter covers Accept.js card requests; the hosted SIM payment form at secure.authorize.net/gateway/transact.dll posts the card as a top-level form and is not admitted.' }, { id: 'worldpay-hosted-payment-page', psp: 'worldpay', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^secure\.worldpay\.com$/, pathname: /^\/wcc\/(?:purchase|card)$/, origin: 'https://secure.worldpay.com', path_pattern: '/wcc/{purchase,card}',
|
|
45
|
+
sources: [documented('https://docs.worldpay.com/access/products/checkout/web'), observed('worldpay-hosted-payment-page-card')],
|
|
46
|
+
variant_unverified: 'The implemented Worldpay adapter covers Access Checkout card sessions; Worldpay hosted payment pages under secure.worldpay.com/wcc post the card to Worldpay directly and are not admitted.' }, { id: 'rapyd-hosted-checkout-page', psp: 'rapyd', kinds: ['document', 'frame', 'iframe'], hostname: /^checkout\.rapyd\.net$/, pathname: /^\/$/, origin: 'https://checkout.rapyd.net', path_pattern: '/',
|
|
47
|
+
sources: [documented('https://docs.rapyd.net/en/hosted-checkout-page.html'), observed('rapyd-hosted-checkout-page')] }, { id: 'dlocal-smart-fields-parent', psp: 'dlocal', kinds: ['script'], hostname: /^static\.dlocal\.com$/, pathname: /^\/modules\/fields\/[0-9]+\.[0-9]+\.[0-9]+\/(?:parent|iframe)\.js$/, origin: 'https://static.dlocal.com', path_pattern: '/modules/fields/:version/{parent,iframe}.js',
|
|
48
|
+
sources: [documented('https://docs.dlocal.com/docs/set-up-smart-fields'), observed('dlocal-smart-fields-6.8.3')] }, { id: 'dlocal-smart-card-fields', psp: 'dlocal', kinds: ['frame', 'iframe', 'document'], hostname: /^static\.dlocal\.com$/, pathname: /^\/modules\/fields\/[0-9]+\.[0-9]+\.[0-9]+\/inner-(?:pan|card)\.html$/, origin: 'https://static.dlocal.com', path_pattern: '/modules/fields/:version/inner-{pan,card}.html',
|
|
49
|
+
sources: [documented('https://docs.dlocal.com/docs/set-up-smart-fields'), observed('dlocal-smart-fields-6.8.3')] }, { id: 'mercado-pago-secure-fields-frame', psp: 'mercado_pago', kinds: ['frame', 'iframe', 'document'], hostname: /^secure-fields\.mercadopago\.com$/, pathname: /^\/$/, origin: 'https://secure-fields.mercadopago.com', path_pattern: '/',
|
|
50
|
+
sources: [documented('https://sdk.mercadopago.com/js/v2'), observed('mercado-pago-secure-fields-frames')] }, { id: 'mercado-pago-checkout-pro-card-form', psp: 'mercado_pago', kinds: ['document', 'frame', 'iframe'], hostname: /^www\.mercadopago\.com\.ar$/, pathname: /^\/checkout\/v1\/payment\/redirect\/[^/]+\/card-form\/?$/, origin: 'https://www.mercadopago.com.ar', path_pattern: '/checkout/v1/payment/redirect/:preference/card-form/',
|
|
51
|
+
sources: [documented('https://www.mercadopago.com.ar/developers/es/docs/checkout-pro/landing'), observed('mercado-pago-checkout-pro-guest-card-form')] },
|
|
52
|
+
// Gravy (Gr4vy) Secure Fields: the merchant page loads secure-fields.js from the
|
|
53
|
+
// merchant's own CDN host, cdn.<merchant>.gr4vy.app, or cdn.sandbox.<merchant>.gr4vy.app
|
|
54
|
+
// in test. The bounded merchant label mirrors the recognizer's api.<merchant> hosts and
|
|
55
|
+
// the VGS tenant rule above; gr4vy.app is Gravy's own domain, so no third party can
|
|
56
|
+
// stand up a host that satisfies it. Written from the published integration contract,
|
|
57
|
+
// not yet confirmed against a live merchant page.
|
|
58
|
+
{ id: 'gravy-secure-fields-script', psp: 'gravy', kinds: ['script'], hostname: /^cdn\.[a-z0-9-]+\.gr4vy\.app$/, pathname: /^\/secure-fields\.js$/, origin: 'https://cdn.:merchant.gr4vy.app', path_pattern: '/secure-fields.js',
|
|
59
|
+
sources: [documented('https://docs.gr4vy.com/guides/payments/secure-fields')] }, { id: 'gravy-secure-fields-script-sandbox', psp: 'gravy', kinds: ['script'], hostname: /^cdn\.sandbox\.[a-z0-9-]+\.gr4vy\.app$/, pathname: /^\/secure-fields\.js$/, origin: 'https://cdn.sandbox.:merchant.gr4vy.app', path_pattern: '/secure-fields.js',
|
|
60
|
+
sources: [documented('https://docs.gr4vy.com/guides/payments/secure-fields')] });
|
|
61
|
+
// Fiserv Commerce Hub, read 2026-09-23 from Home Depot's OrangePay frame and Fiserv's
|
|
62
|
+
// docs: payment-fields.js renders one Secure Data Capture iframe per card field, and
|
|
63
|
+
// the Checkout JS SDK is Commerce Hub's other browser entry. Fiserv ships dark (its
|
|
64
|
+
// definition names a capability), so the bundled catalog declares no support for it:
|
|
65
|
+
// these assets identify fiserv and the result stays unknown.
|
|
66
|
+
const fiservSource = (reference) => ({ kind: 'primary_source', reference, reviewed_on: '2026-09-23' });
|
|
67
|
+
RULES.push({ id: 'fiserv-commercehub-payment-fields', psp: 'fiserv', kinds: ['script'], hostname: /^commercehub-checkout\.fiservapps\.com$/, pathname: /^\/sdk\/[0-9]+\.[0-9]+\.[0-9]+\/payment-fields\.js$/, origin: 'https://commercehub-checkout.fiservapps.com', path_pattern: '/sdk/:version/payment-fields.js',
|
|
68
|
+
sources: [fiservSource('https://commercehub-checkout.fiservapps.com/sdk/3.8.14/payment-fields.js'), fiservSource('https://orangepay-ecommerce.hdpayments.homedepot.com/env.js')] }, { id: 'fiserv-commercehub-checkout-script', psp: 'fiserv', kinds: ['script'], hostname: /^commercehub-checkout\.fiservapps\.com$/, pathname: /^\/sdk\/[0-9]+\.[0-9]+\.[0-9]+\/checkout\.js$/, origin: 'https://commercehub-checkout.fiservapps.com', path_pattern: '/sdk/:version/checkout.js',
|
|
69
|
+
sources: [fiservSource('https://developer.fiserv.com/product/CommerceHub/docs/Online-Commerce/Integration-Options/Hosted-Checkout/Hosted-Checkout-Version-Release')] }, { id: 'fiserv-secure-data-capture-frame', psp: 'fiserv', kinds: ['frame', 'iframe', 'document'], hostname: /^commercehub-secure-data-capture\.fiservapps\.com$/, pathname: /^\/sdk-secure\/[0-9]+\.[0-9]+\.[0-9]+\/iframe\.html$/, origin: 'https://commercehub-secure-data-capture.fiservapps.com', path_pattern: '/sdk-secure/:version/iframe.html',
|
|
70
|
+
sources: [fiservSource('https://commercehub-checkout.fiservapps.com/sdk/3.8.14/payment-fields.js'), fiservSource('https://commercehub-secure-data-capture.fiservapps.com/sdk-secure/3.8.14/iframe.html')] });
|
|
71
|
+
// GMO Payment Gateway PG Multipayment (dark: its processor declares a capability, so the bundled
|
|
72
|
+
// catalog does not list it and these signals stay unknown until a client opts in). Token v2's
|
|
73
|
+
// mp-token.js identifies the processor. Token v1's ext/js/token.js (a JSONP GET), GMO Elements'
|
|
74
|
+
// card iframe and Link-type hosted pages identify it too, but no Vault mode covers them, so each
|
|
75
|
+
// keeps the result unknown through `variant_unverified`. Sources: the reviewed SDK bundles
|
|
76
|
+
// (mp-token.js sha256 1fedeab0...dbe0f7, test 9e6cd8a3...56e2aa) and GMO's public token docs.
|
|
77
|
+
const gmoSources = [
|
|
78
|
+
{ kind: 'primary_source', reference: 'https://static.mul-pay.jp/payment/js/mp-token.js', reviewed_on: '2026-09-25' },
|
|
79
|
+
{ kind: 'official_documentation', reference: 'https://docs.gmo-pg.com/mulpay/docs/developer-support/mp-token/overview', reviewed_on: '2026-09-25' },
|
|
80
|
+
];
|
|
81
|
+
RULES.push({ id: 'gmo-mp-token-script', psp: 'gmo_pg', kinds: ['script'], hostname: /^static\.mul-pay\.jp$/, pathname: /^\/payment\/js\/mp-token\.js$/, origin: 'https://static.mul-pay.jp', path_pattern: '/payment/js/mp-token.js',
|
|
82
|
+
sources: gmoSources }, { id: 'gmo-token-v1-script', psp: 'gmo_pg', kinds: ['script'], hostname: /^static\.mul-pay\.jp$/, pathname: /^\/ext\/js\/token\.js$/, origin: 'https://static.mul-pay.jp', path_pattern: '/ext/js/token.js',
|
|
83
|
+
sources: [{ kind: 'primary_source', reference: 'https://static.mul-pay.jp/ext/js/token.js', reviewed_on: '2026-09-25' }],
|
|
84
|
+
variant_unverified: 'GMO token v1 (ext/js/token.js) sends the card as a JSONP GET; only token v2 (mp-token.js) getToken on the shared-key path is admitted.' }, { id: 'gmo-elements-card-frame', psp: 'gmo_pg', kinds: ['frame', 'iframe', 'document'], hostname: /^static\.mul-pay\.jp$/, pathname: /^\/payment\/js\/elements\/static\/formElement\.html$/, origin: 'https://static.mul-pay.jp', path_pattern: '/payment/js/elements/static/formElement.html',
|
|
85
|
+
sources: gmoSources,
|
|
86
|
+
variant_unverified: 'GMO Elements takes the card in GMO\'s own iframes and encrypts it under a merchant-registered key; it is not admitted.' }, { id: 'gmo-link-hosted-page', psp: 'gmo_pg', kinds: ['document', 'frame', 'iframe', 'form'], hostname: /^p01\.mul-pay\.jp$/, pathname: /^\/link\//, origin: 'https://p01.mul-pay.jp', path_pattern: '/link/*',
|
|
87
|
+
sources: [{ kind: 'official_documentation', reference: 'https://docs.gmo-pg.com/mulpay/docs/developer-support/test-environment/overview', reviewed_on: '2026-09-25' }],
|
|
88
|
+
variant_unverified: 'A GMO Link-type payment page takes the card on GMO\'s own hosted page; it is not admitted.' });
|
|
89
|
+
// Keep sandbox evidence truthful instead of labelling it with a production origin.
|
|
90
|
+
for (const [baseId, suffix, hostname, origin] of [
|
|
91
|
+
['gmo-mp-token-script', 'test', /^stg\.static\.mul-pay\.jp$/, 'https://stg.static.mul-pay.jp'],
|
|
92
|
+
['gmo-token-v1-script', 'test', /^stg\.static\.mul-pay\.jp$/, 'https://stg.static.mul-pay.jp'],
|
|
93
|
+
['gmo-elements-card-frame', 'test', /^stg\.static\.mul-pay\.jp$/, 'https://stg.static.mul-pay.jp'],
|
|
94
|
+
['gmo-link-hosted-page', 'test', /^pt01\.mul-pay\.jp$/, 'https://pt01.mul-pay.jp'],
|
|
95
|
+
['square-web-sdk', 'sandbox', /^sandbox\.web\.squarecdn\.com$/, 'https://sandbox.web.squarecdn.com'],
|
|
96
|
+
['paysafe-js', 'test', /^hosted\.test\.paysafe\.com$/, 'https://hosted.test.paysafe.com'],
|
|
97
|
+
['worldpay-checkout-sdk', 'try', /^try\.access\.worldpay\.com$/, 'https://try.access.worldpay.com'],
|
|
98
|
+
['authorize-net-accept', 'test', /^jstest\.authorize\.net$/, 'https://jstest.authorize.net'],
|
|
99
|
+
['tranzila-terminal-hosted-page', 'directng', /^directng\.tranzila\.com$/, 'https://directng.tranzila.com'],
|
|
100
|
+
['square-card-element-frame', 'sandbox', /^sandbox\.web\.squarecdn\.com$/, 'https://sandbox.web.squarecdn.com'],
|
|
101
|
+
['authorize-net-sim-hosted-form', 'secure2', /^secure2\.authorize\.net$/, 'https://secure2.authorize.net'],
|
|
102
|
+
['worldpay-hosted-payment-page', 'test', /^secure-test\.worldpay\.com$/, 'https://secure-test.worldpay.com'],
|
|
103
|
+
['rapyd-hosted-checkout-page', 'sandbox', /^sandboxcheckout\.rapyd\.net$/, 'https://sandboxcheckout.rapyd.net'],
|
|
104
|
+
// Adyen's regional live hosts (docs.adyen.com/development-resources/live-endpoints/).
|
|
105
|
+
['adyen-secured-fields-live', 'us', /^checkoutshopper-live-us\.adyen\.com$/, 'https://checkoutshopper-live-us.adyen.com'],
|
|
106
|
+
['adyen-secured-fields-live', 'au', /^checkoutshopper-live-au\.adyen\.com$/, 'https://checkoutshopper-live-au.adyen.com'],
|
|
107
|
+
['adyen-secured-fields-live', 'apse', /^checkoutshopper-live-apse\.adyen\.com$/, 'https://checkoutshopper-live-apse.adyen.com'],
|
|
108
|
+
['adyen-secured-fields-live', 'in', /^checkoutshopper-live-in\.adyen\.com$/, 'https://checkoutshopper-live-in.adyen.com'],
|
|
109
|
+
['adyen-secured-fields-live', 'nea', /^checkoutshopper-live-nea\.adyen\.com$/, 'https://checkoutshopper-live-nea.adyen.com'],
|
|
110
|
+
// Commerce Hub's "Sandbox and Cert" environment (payment-fields.js 3.8.14 environment table).
|
|
111
|
+
['fiserv-commercehub-payment-fields', 'cert', /^commercehub-checkout-cert\.fiservapps\.com$/, 'https://commercehub-checkout-cert.fiservapps.com'],
|
|
112
|
+
['fiserv-commercehub-checkout-script', 'cert', /^commercehub-checkout-cert\.fiservapps\.com$/, 'https://commercehub-checkout-cert.fiservapps.com'],
|
|
113
|
+
['fiserv-secure-data-capture-frame', 'cert', /^commercehub-secure-data-capture-cert\.fiservapps\.com$/, 'https://commercehub-secure-data-capture-cert.fiservapps.com'],
|
|
114
|
+
]) {
|
|
115
|
+
RULES.push({ ...RULES.find(rule => rule.id === baseId), id: `${baseId}-${suffix}`, hostname, origin });
|
|
116
|
+
}
|
|
117
|
+
const SOURCES = {
|
|
118
|
+
stripe: [{ kind: 'official_documentation', reference: 'https://docs.stripe.com/js/including', reviewed_on: '2026-09-11' }],
|
|
119
|
+
square: [{ kind: 'official_documentation', reference: 'https://developer.squareup.com/docs/web-payments/quickstart/add-sdk-to-web-client', reviewed_on: '2026-09-11' }],
|
|
120
|
+
mollie: [{ kind: 'saved_observation', reference: 'mollie-haak-2026-09-11:hosted-credit-card-document-and-v2-script', reviewed_on: '2026-09-11' }],
|
|
121
|
+
braintree: [{ kind: 'official_documentation', reference: 'https://developer.paypal.com/braintree/docs/start/hello-client/javascript/v3/', reviewed_on: '2026-09-11' }],
|
|
122
|
+
paysafe: [{ kind: 'official_documentation', reference: 'https://developer.paysafe.com/en/api-docs/paysafe-js/overview/', reviewed_on: '2026-09-11' }],
|
|
123
|
+
worldpay: [{ kind: 'official_documentation', reference: 'https://developer.worldpay.com/products/checkout/web', reviewed_on: '2026-09-11' }],
|
|
124
|
+
authorize_net: [{ kind: 'official_documentation', reference: 'https://developer.authorize.net/api/reference/features/acceptjs.html', reviewed_on: '2026-09-11' }],
|
|
125
|
+
razorpay: [{ kind: 'official_documentation', reference: 'https://razorpay.com/docs/payments/payment-gateway/web-integration/standard/integration-steps/', reviewed_on: '2026-09-11' }],
|
|
126
|
+
vgs: [{ kind: 'official_documentation', reference: 'https://docs.verygoodsecurity.com/vault/developer-tools/vgs-collect/js/integration', reviewed_on: '2026-09-11' }],
|
|
127
|
+
};
|
|
128
|
+
const FLOWS = [
|
|
129
|
+
{ id: 'mollie.hosted_components', psp: 'mollie', operation: 'mollie.card_request', operation_version: 1, mode: 'token', server_constraint: 'eligible', limitations: [
|
|
130
|
+
'The payment request must match the supported Mollie Components card-token format.',
|
|
131
|
+
'Authentication and merchant confirmation must finish in the merchant browser.',
|
|
132
|
+
'Merchant configuration, card eligibility and processor approval still determine whether the purchase succeeds.',
|
|
133
|
+
] },
|
|
134
|
+
{ id: 'vgs.iframe_bound_tokenization', psp: 'vgs', operation: 'vgs.iframe_bound_tokenization', operation_version: 1, mode: 'token', server_constraint: 'unsupported', limitations: [
|
|
135
|
+
'VGS requires tokenization from its own iframe; the Vault cannot replay this flow from the approval page.',
|
|
136
|
+
] },
|
|
137
|
+
];
|
|
138
|
+
const FLOW_BY_ID = new Map(FLOWS.map(flow => [flow.id, flow]));
|
|
139
|
+
const RULE_BY_ID = new Map(RULES.map(rule => [rule.id, rule]));
|
|
140
|
+
const DEFAULT_PROFILE = {
|
|
141
|
+
schema_version: 1, profile_version: 'preflight-2', catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION,
|
|
142
|
+
integration: { id: 'direct_sdk', version: 'bundled' },
|
|
143
|
+
// The bundled profile repeats each processor's projected status. A processor whose card
|
|
144
|
+
// request the SDK cannot pause at all, Gravy's PUT today, is declared unsupported here
|
|
145
|
+
// as well as in its scenarios, so the exported profile never contradicts the assessment.
|
|
146
|
+
processor_entries: CHECKOUT_PREFLIGHT_CAPABILITIES.processors.map(processor => ({
|
|
147
|
+
psp: processor.psp, mode: processor.mode, scenario: 'one_time', status: processor.status,
|
|
148
|
+
})),
|
|
149
|
+
entries: [{ flow: 'mollie.hosted_components', operation: 'mollie.card_request', operation_version: 1, mode: 'token', scenario: 'one_time', status: 'supported' }],
|
|
150
|
+
};
|
|
151
|
+
const clone = (value) => JSON.parse(JSON.stringify(value));
|
|
152
|
+
const record = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
|
|
153
|
+
const exactKeys = (value, keys) => Object.keys(value).every(key => keys.includes(key));
|
|
154
|
+
const id = (value) => typeof value === 'string' && /^[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$/.test(value);
|
|
155
|
+
const frameId = (value) => Number.isSafeInteger(value) && value >= 0 && value < CHECKOUT_PREFLIGHT_LIMITS.frames;
|
|
156
|
+
const unique = (items) => [...new Set(items)];
|
|
157
|
+
function dateTime(value) {
|
|
158
|
+
if (typeof value !== 'string' || value.length > 40)
|
|
159
|
+
return false;
|
|
160
|
+
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);
|
|
161
|
+
if (!match)
|
|
162
|
+
return false;
|
|
163
|
+
const [year, month, day, hour, minute, second, zoneHour = 0, zoneMinute = 0] = match.slice(1).map(part => part === undefined ? 0 : Number(part));
|
|
164
|
+
const leap = year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
|
|
165
|
+
const days = [31, leap ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
166
|
+
return month >= 1 && month <= 12 && day >= 1 && day <= days[month - 1] && hour <= 23 && minute <= 59 && second <= 59
|
|
167
|
+
&& zoneHour <= 23 && zoneMinute <= 59 && Number.isFinite(Date.parse(value));
|
|
168
|
+
}
|
|
169
|
+
function metadata(value) {
|
|
170
|
+
return KINDS.includes(value.kind) && frameId(value.frame_id) && (value.parent_frame_id === null || frameId(value.parent_frame_id))
|
|
171
|
+
&& (value.frame_id !== 0 || value.parent_frame_id === null)
|
|
172
|
+
&& value.parent_frame_id !== value.frame_id && (value.visible === null || typeof value.visible === 'boolean');
|
|
173
|
+
}
|
|
174
|
+
function consistentParents(signals) {
|
|
175
|
+
const parents = new Map();
|
|
176
|
+
for (const signal of signals) {
|
|
177
|
+
if (parents.has(signal.frame_id) && parents.get(signal.frame_id) !== signal.parent_frame_id)
|
|
178
|
+
return false;
|
|
179
|
+
parents.set(signal.frame_id, signal.parent_frame_id);
|
|
180
|
+
}
|
|
181
|
+
for (const frame of parents.keys()) {
|
|
182
|
+
const visited = new Set();
|
|
183
|
+
let current = frame;
|
|
184
|
+
while (current !== null && current !== undefined) {
|
|
185
|
+
if (visited.has(current))
|
|
186
|
+
return false;
|
|
187
|
+
visited.add(current);
|
|
188
|
+
current = parents.get(current);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
function cleanObservation(input) {
|
|
194
|
+
if (!record(input) || !exactKeys(input, ['complete', 'truncated', 'reasons']) || typeof input.complete !== 'boolean' || typeof input.truncated !== 'boolean'
|
|
195
|
+
|| !Array.isArray(input.reasons) || input.reasons.length > 32 || Array.from(input.reasons).some(reason => !OBSERVATION_REASONS.includes(reason))) {
|
|
196
|
+
return { complete: false, truncated: false, reasons: ['invalid_observation'] };
|
|
197
|
+
}
|
|
198
|
+
const reasons = unique(input.reasons);
|
|
199
|
+
const complete = input.complete && !input.truncated && reasons.length === 0;
|
|
200
|
+
return { complete, truncated: input.truncated, reasons: complete ? [] : unique([...reasons, 'collection_incomplete']) };
|
|
201
|
+
}
|
|
202
|
+
const byteLength = (value) => new TextEncoder().encode(JSON.stringify(value)).length;
|
|
203
|
+
function incomplete(observation, reason, truncated = false) {
|
|
204
|
+
observation.complete = false;
|
|
205
|
+
observation.truncated ||= truncated;
|
|
206
|
+
observation.reasons = unique([...observation.reasons, reason]);
|
|
207
|
+
}
|
|
208
|
+
/** Normalize observations locally. Unknown assets are omitted; no URL leaves this function. */
|
|
209
|
+
export function normalizeCheckoutSignals(input) {
|
|
210
|
+
const snapshot = { schema_version: 1, catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION, signals: [], observation: cleanObservation(record(input) ? input.observation : null) };
|
|
211
|
+
if (!record(input) || !exactKeys(input, ['signals', 'observation']) || !Array.isArray(input.signals)) {
|
|
212
|
+
incomplete(snapshot.observation, 'invalid_observation');
|
|
213
|
+
return snapshot;
|
|
214
|
+
}
|
|
215
|
+
if (input.signals.length > CHECKOUT_PREFLIGHT_LIMITS.signals)
|
|
216
|
+
incomplete(snapshot.observation, 'signal_limit', true);
|
|
217
|
+
const seen = new Set();
|
|
218
|
+
for (const raw of input.signals.slice(0, CHECKOUT_PREFLIGHT_LIMITS.signals)) {
|
|
219
|
+
if (!record(raw) || !exactKeys(raw, ['kind', 'url', 'frame_id', 'parent_frame_id', 'visible']) || !metadata(raw)) {
|
|
220
|
+
incomplete(snapshot.observation, 'invalid_observation');
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (typeof raw.url !== 'string' || raw.url.length > CHECKOUT_PREFLIGHT_LIMITS.raw_url_length) {
|
|
224
|
+
incomplete(snapshot.observation, 'invalid_url');
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
let url;
|
|
228
|
+
try {
|
|
229
|
+
url = new URL(raw.url);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
incomplete(snapshot.observation, 'invalid_url');
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
// Matching reads the origin and path only. A long query string on an unrelated wallet or
|
|
236
|
+
// widget frame is dropped here rather than invalidating the whole observation.
|
|
237
|
+
if (url.origin.length + url.pathname.length > CHECKOUT_PREFLIGHT_LIMITS.url_length) {
|
|
238
|
+
incomplete(snapshot.observation, 'invalid_url');
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
// Non-HTTPS local/blank frames provide no PSP evidence. Credentials and nondefault ports are refused.
|
|
242
|
+
if (url.protocol !== 'https:' || url.username || url.password || url.port)
|
|
243
|
+
continue;
|
|
244
|
+
for (const rule of RULES) {
|
|
245
|
+
if (!rule.kinds.includes(raw.kind) || !rule.hostname.test(url.hostname) || !rule.pathname.test(url.pathname))
|
|
246
|
+
continue;
|
|
247
|
+
const signal = { rule_id: rule.id, kind: raw.kind, frame_id: raw.frame_id, parent_frame_id: raw.parent_frame_id, visible: raw.visible };
|
|
248
|
+
const key = JSON.stringify(signal);
|
|
249
|
+
if (!seen.has(key)) {
|
|
250
|
+
// One observed asset can match more than one reviewed fingerprint.
|
|
251
|
+
// Bound normalized evidence independently of the raw observation count.
|
|
252
|
+
if (snapshot.signals.length >= CHECKOUT_PREFLIGHT_LIMITS.signals) {
|
|
253
|
+
incomplete(snapshot.observation, 'signal_limit', true);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
snapshot.signals.push(signal);
|
|
257
|
+
seen.add(key);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
while (byteLength(snapshot) > CHECKOUT_PREFLIGHT_LIMITS.snapshot_bytes && snapshot.signals.length) {
|
|
262
|
+
snapshot.signals.pop();
|
|
263
|
+
incomplete(snapshot.observation, 'snapshot_limit', true);
|
|
264
|
+
}
|
|
265
|
+
if (!consistentParents(snapshot.signals))
|
|
266
|
+
incomplete(snapshot.observation, 'invalid_observation');
|
|
267
|
+
return snapshot;
|
|
268
|
+
}
|
|
269
|
+
/** The returned catalog is metadata only. It does not change card destination recognition. */
|
|
270
|
+
export function getCheckoutPreflightCatalog() {
|
|
271
|
+
return clone({
|
|
272
|
+
schema_version: 1, catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION, scope: 'preflight_hint',
|
|
273
|
+
integration_version_note: 'bundled describes the capabilities accompanying this helper, not a claim about another installed SDK version.',
|
|
274
|
+
limits: CHECKOUT_PREFLIGHT_LIMITS,
|
|
275
|
+
known_psps: KNOWN_PSPS.map(psp => ({ psp, detection: RULES.some(rule => rule.psp === psp) ? 'reviewed_rules' : 'unknown' })),
|
|
276
|
+
rules: RULES.map(({ hostname, pathname, ...rule }) => ({ ...rule, hostname_pattern: hostname.source, pathname_pattern: pathname.source, sources: rule.sources ?? SOURCES[rule.psp] })),
|
|
277
|
+
processors: CHECKOUT_PREFLIGHT_CAPABILITIES.processors, exclusions: CHECKOUT_PREFLIGHT_CAPABILITIES.exclusions,
|
|
278
|
+
flows: FLOWS, direct_sdk_profile: DEFAULT_PROFILE,
|
|
279
|
+
kernel_profile_template: { schema_version: 1, profile_version: 'kernel-template-1', catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION,
|
|
280
|
+
integration: { id: 'kernel_native', version: 'REPLACE-WITH-ADAPTER-VERSION' }, entries: [], processor_entries: [] },
|
|
281
|
+
observation_reason_codes: OBSERVATION_REASONS,
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
// Registrable-site approximation for provider hosts; a public-suffix list is deliberately not bundled.
|
|
285
|
+
function site(hostname) {
|
|
286
|
+
const labels = hostname.toLowerCase().split('.').filter(Boolean);
|
|
287
|
+
const secondLevel = ['com', 'co', 'net', 'org', 'gov', 'edu', 'ac', 'or', 'ne'];
|
|
288
|
+
const count = labels.length > 2 && labels[labels.length - 1].length === 2 && secondLevel.includes(labels[labels.length - 2]) ? 3 : 2;
|
|
289
|
+
return labels.slice(-count).join('.');
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Whether a visible card-entry frame on `hostname` can belong to a processor already evidenced in the
|
|
293
|
+
* snapshot. A collector reports `card_entry_unrecognized` when this is false, so co-loaded SDK scripts
|
|
294
|
+
* cannot be presented as the processor behind card controls that no reviewed fingerprint explains, and
|
|
295
|
+
* a card form with no processor evidence at all is reported rather than passing as an empty page.
|
|
296
|
+
* The hostname stays local to the caller; nothing here enters evidence.
|
|
297
|
+
*/
|
|
298
|
+
export function cardEntryAttributable(input, hostname) {
|
|
299
|
+
if (!validSnapshot(input) || typeof hostname !== 'string' || !hostname)
|
|
300
|
+
return false;
|
|
301
|
+
const evidenced = new Set(input.signals.map(signal => RULE_BY_ID.get(signal.rule_id).psp));
|
|
302
|
+
if (!evidenced.size)
|
|
303
|
+
return false;
|
|
304
|
+
const target = site(hostname);
|
|
305
|
+
return RULES.some(rule => {
|
|
306
|
+
if (!evidenced.has(rule.psp))
|
|
307
|
+
return false;
|
|
308
|
+
if (rule.hostname.test(hostname))
|
|
309
|
+
return true;
|
|
310
|
+
// A templated origin (the VGS tenant frame) is covered by its hostname pattern above.
|
|
311
|
+
try {
|
|
312
|
+
return site(new URL(rule.origin).hostname) === target;
|
|
313
|
+
}
|
|
314
|
+
catch {
|
|
315
|
+
return false;
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
function validSnapshot(input) {
|
|
320
|
+
return record(input) && exactKeys(input, ['schema_version', 'catalog_version', 'signals', 'observation'])
|
|
321
|
+
&& input.schema_version === 1 && input.catalog_version === CHECKOUT_PREFLIGHT_CATALOG_VERSION
|
|
322
|
+
&& Array.isArray(input.signals) && input.signals.length <= CHECKOUT_PREFLIGHT_LIMITS.signals
|
|
323
|
+
&& Array.from(input.signals).every(signal => record(signal) && exactKeys(signal, ['rule_id', 'kind', 'frame_id', 'parent_frame_id', 'visible']) && metadata(signal)
|
|
324
|
+
&& id(signal.rule_id) && !!RULE_BY_ID.get(signal.rule_id)?.kinds.includes(signal.kind))
|
|
325
|
+
&& consistentParents(input.signals)
|
|
326
|
+
&& record(input.observation) && exactKeys(input.observation, ['complete', 'truncated', 'reasons'])
|
|
327
|
+
&& typeof input.observation.complete === 'boolean' && typeof input.observation.truncated === 'boolean'
|
|
328
|
+
&& Array.isArray(input.observation.reasons) && input.observation.reasons.length <= 32
|
|
329
|
+
&& Array.from(input.observation.reasons).every(reason => OBSERVATION_REASONS.includes(reason))
|
|
330
|
+
&& byteLength(input) <= CHECKOUT_PREFLIGHT_LIMITS.snapshot_bytes;
|
|
331
|
+
}
|
|
332
|
+
function validIntegration(value) {
|
|
333
|
+
return record(value) && exactKeys(value, ['id', 'version']) && ['direct_sdk', 'kernel_native'].includes(value.id) && (value.version === null || id(value.version));
|
|
334
|
+
}
|
|
335
|
+
function profileError(profile, integration, now) {
|
|
336
|
+
if (!record(profile) || !exactKeys(profile, ['schema_version', 'profile_version', 'catalog_version', 'integration', 'entries', 'processor_entries', 'expires_at'])
|
|
337
|
+
|| profile.schema_version !== 1 || !id(profile.profile_version) || !validIntegration(profile.integration) || !id(profile.integration.version)
|
|
338
|
+
|| !Array.isArray(profile.entries) || profile.entries.length > 256
|
|
339
|
+
|| (profile.processor_entries !== undefined && (!Array.isArray(profile.processor_entries) || profile.processor_entries.length > 256)))
|
|
340
|
+
return 'integration_profile_invalid';
|
|
341
|
+
if (profile.catalog_version !== CHECKOUT_PREFLIGHT_CATALOG_VERSION)
|
|
342
|
+
return 'integration_profile_incompatible';
|
|
343
|
+
if (profile.integration.id !== integration.id || profile.integration.version !== integration.version)
|
|
344
|
+
return 'integration_profile_mismatch';
|
|
345
|
+
if (profile.expires_at !== undefined) {
|
|
346
|
+
const expires = dateTime(profile.expires_at) ? Date.parse(profile.expires_at) : NaN;
|
|
347
|
+
const time = now === undefined ? Date.now() : dateTime(now) ? Date.parse(now) : NaN;
|
|
348
|
+
if (!Number.isFinite(expires) || !Number.isFinite(time))
|
|
349
|
+
return 'integration_profile_invalid';
|
|
350
|
+
if (expires <= time)
|
|
351
|
+
return 'integration_profile_expired';
|
|
352
|
+
}
|
|
353
|
+
const entries = new Set();
|
|
354
|
+
for (const entry of profile.entries) {
|
|
355
|
+
if (!record(entry) || !exactKeys(entry, ['flow', 'operation', 'operation_version', 'mode', 'scenario', 'status', 'limitations'])
|
|
356
|
+
|| !id(entry.flow) || !id(entry.operation) || !SCENARIOS.includes(entry.scenario) || !['supported', 'unsupported'].includes(entry.status)
|
|
357
|
+
|| (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)))))
|
|
358
|
+
return 'integration_profile_invalid';
|
|
359
|
+
const flow = FLOW_BY_ID.get(entry.flow);
|
|
360
|
+
if (!flow || entry.operation !== flow.operation || entry.operation_version !== flow.operation_version || entry.mode !== flow.mode)
|
|
361
|
+
return 'integration_profile_incompatible';
|
|
362
|
+
const key = `${entry.flow}:${entry.scenario}`;
|
|
363
|
+
if (entries.has(key))
|
|
364
|
+
return 'integration_profile_invalid';
|
|
365
|
+
entries.add(key);
|
|
366
|
+
}
|
|
367
|
+
const processors = new Set();
|
|
368
|
+
for (const entry of profile.processor_entries ?? []) {
|
|
369
|
+
if (!record(entry) || !exactKeys(entry, ['psp', 'mode', 'scenario', 'status', 'limitations'])
|
|
370
|
+
|| !id(entry.psp) || !SCENARIOS.includes(entry.scenario) || !['supported', 'unsupported'].includes(entry.status)
|
|
371
|
+
|| (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)))))
|
|
372
|
+
return 'integration_profile_invalid';
|
|
373
|
+
const processor = PROCESSOR_BY_ID.get(entry.psp);
|
|
374
|
+
if (!processor || processor.mode !== entry.mode)
|
|
375
|
+
return 'integration_profile_incompatible';
|
|
376
|
+
const key = `${entry.psp}:${entry.scenario}`;
|
|
377
|
+
if (processors.has(key))
|
|
378
|
+
return 'integration_profile_invalid';
|
|
379
|
+
processors.add(key);
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
}
|
|
383
|
+
/** Classify a sanitized snapshot. No browser, HTTP, or payment operations occur here. */
|
|
384
|
+
export function assessCheckoutSupport(input, options = {}) {
|
|
385
|
+
const opts = record(options) ? options : {};
|
|
386
|
+
const fallbackIntegrationId = record(opts.integration) && opts.integration.id === 'kernel_native' ? 'kernel_native' : 'direct_sdk';
|
|
387
|
+
const integration = validIntegration(opts.integration) ? clone(opts.integration) : {
|
|
388
|
+
id: fallbackIntegrationId,
|
|
389
|
+
version: opts.integration === undefined ? 'bundled' : null,
|
|
390
|
+
};
|
|
391
|
+
const result = {
|
|
392
|
+
schema_version: 1, catalog_version: CHECKOUT_PREFLIGHT_CATALOG_VERSION, integration,
|
|
393
|
+
scenario: opts.scenario !== undefined && SCENARIOS.includes(opts.scenario) ? opts.scenario : 'one_time', scenario_defaulted: opts.scenario === undefined,
|
|
394
|
+
psp: null, flow: null, mode: null, status: 'unknown', support_scope: null, checkout_flow_status: 'unknown', candidates: [], reason_codes: [], limitations: [], missing_evidence: [],
|
|
395
|
+
observation: { complete: false, truncated: false, reasons: ['invalid_observation'] }, scope: 'preflight_hint', merchant_confirmation_required: true,
|
|
396
|
+
};
|
|
397
|
+
if (!record(options) || !exactKeys(opts, ['scenario', 'integration', 'profile', 'now']) || (opts.scenario !== undefined && !SCENARIOS.includes(opts.scenario))
|
|
398
|
+
|| (opts.integration !== undefined && !validIntegration(opts.integration)) || (opts.now !== undefined && !dateTime(opts.now))) {
|
|
399
|
+
result.reason_codes = ['invalid_options'];
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
if (!validSnapshot(input)) {
|
|
403
|
+
result.reason_codes = [record(input) && input.catalog_version !== CHECKOUT_PREFLIGHT_CATALOG_VERSION ? 'catalog_incompatible' : 'invalid_snapshot'];
|
|
404
|
+
return result;
|
|
405
|
+
}
|
|
406
|
+
result.observation = cleanObservation(input.observation);
|
|
407
|
+
const candidates = new Map();
|
|
408
|
+
const activeFlows = new Set();
|
|
409
|
+
const activePsps = new Set();
|
|
410
|
+
const activeFlowFrames = new Set();
|
|
411
|
+
let missingCorroboration = false;
|
|
412
|
+
for (const signal of input.signals) {
|
|
413
|
+
const rule = RULE_BY_ID.get(signal.rule_id);
|
|
414
|
+
const active = signal.visible === true && (['frame', 'iframe', 'form'].includes(signal.kind)
|
|
415
|
+
|| signal.kind === 'document' && signal.frame_id === 0 && signal.parent_frame_id === null);
|
|
416
|
+
const candidate = candidates.get(rule.psp) ?? { psp: rule.psp, relevance: 'undetermined', evidence: [] };
|
|
417
|
+
candidate.evidence.push({ ...clone(signal), origin: rule.origin, path_pattern: rule.path_pattern });
|
|
418
|
+
if (active) {
|
|
419
|
+
candidate.relevance = 'active';
|
|
420
|
+
activePsps.add(rule.psp);
|
|
421
|
+
if (rule.flow) {
|
|
422
|
+
// A hosted route can also display an expired/error page. Require the reviewed
|
|
423
|
+
// Components script in that document; a similarly named asset elsewhere is insufficient.
|
|
424
|
+
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);
|
|
425
|
+
if (corroborated) {
|
|
426
|
+
activeFlows.add(rule.flow);
|
|
427
|
+
activeFlowFrames.add(signal.frame_id);
|
|
428
|
+
}
|
|
429
|
+
else
|
|
430
|
+
missingCorroboration = true;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
candidates.set(rule.psp, candidate);
|
|
434
|
+
}
|
|
435
|
+
result.candidates = [...candidates.values()].sort((a, b) => a.psp.localeCompare(b.psp));
|
|
436
|
+
if (missingCorroboration && activeFlows.size === 0)
|
|
437
|
+
result.reason_codes.push('flow_corroboration_missing');
|
|
438
|
+
if (activePsps.size === 1)
|
|
439
|
+
result.psp = [...activePsps][0];
|
|
440
|
+
// Script-only evidence names the processor only when the scan saw everything. On a partial scan
|
|
441
|
+
// the sole candidate stays listed as undetermined instead of becoming the selected route.
|
|
442
|
+
else if (activePsps.size === 0 && candidates.size === 1 && result.observation.complete)
|
|
443
|
+
result.psp = [...candidates.keys()][0];
|
|
444
|
+
if (!result.observation.complete)
|
|
445
|
+
result.reason_codes.push('observation_incomplete');
|
|
446
|
+
if (!input.signals.length) {
|
|
447
|
+
result.reason_codes.push('psp_not_detected');
|
|
448
|
+
result.missing_evidence.push('payment_page_signal');
|
|
449
|
+
}
|
|
450
|
+
if (activePsps.size > 1 || activeFlows.size > 1 || activeFlowFrames.size > 1 || activePsps.size === 0 && candidates.size > 1)
|
|
451
|
+
result.reason_codes.push('ambiguous_checkout');
|
|
452
|
+
if (activeFlows.size !== 1 || result.psp === null) {
|
|
453
|
+
result.reason_codes.push('checkout_flow_unidentified');
|
|
454
|
+
result.missing_evidence.push('active_checkout_flow');
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
const flow = FLOW_BY_ID.get([...activeFlows][0]);
|
|
458
|
+
if (flow.psp === result.psp) {
|
|
459
|
+
result.flow = flow.id;
|
|
460
|
+
result.mode = flow.mode;
|
|
461
|
+
result.limitations.push(...flow.limitations);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (!result.flow && result.candidates.length)
|
|
465
|
+
result.limitations.push('Processor assets alone do not establish the active card payment flow.');
|
|
466
|
+
const profile = opts.profile === undefined ? (integration.id === 'direct_sdk' && integration.version === 'bundled' ? DEFAULT_PROFILE : undefined) : opts.profile;
|
|
467
|
+
let error = profile === undefined ? 'integration_profile_missing' : profileError(profile, integration, opts.now);
|
|
468
|
+
if (integration.version === null)
|
|
469
|
+
error = 'integration_version_missing';
|
|
470
|
+
if (error) {
|
|
471
|
+
result.reason_codes.push(error);
|
|
472
|
+
result.missing_evidence.push('compatible_integration_profile');
|
|
473
|
+
}
|
|
474
|
+
result.reason_codes = unique(result.reason_codes);
|
|
475
|
+
const ambiguous = result.reason_codes.includes('ambiguous_checkout');
|
|
476
|
+
result.checkout_flow_status = ambiguous ? 'ambiguous' : result.flow ? 'identified' : 'unknown';
|
|
477
|
+
// Processor capability and observed checkout compatibility answer different questions.
|
|
478
|
+
// Missing flow evidence is advisory when an identified processor has an explicit
|
|
479
|
+
// implementation declaration. Incomplete, contradictory or invalid input stays unknown.
|
|
480
|
+
if (!result.observation.complete || ambiguous || error || !result.psp || missingCorroboration && !result.flow)
|
|
481
|
+
return result;
|
|
482
|
+
const flow = result.flow ? FLOW_BY_ID.get(result.flow) : undefined;
|
|
483
|
+
if (flow?.server_constraint === 'unsupported') {
|
|
484
|
+
result.status = 'unsupported';
|
|
485
|
+
result.support_scope = 'checkout_flow';
|
|
486
|
+
result.reason_codes.push('vault_flow_unsupported');
|
|
487
|
+
return result;
|
|
488
|
+
}
|
|
489
|
+
// A hidden document cannot lend its scripts to a visible card checkout. Script-only
|
|
490
|
+
// observations with no known hidden container can still identify an available adapter.
|
|
491
|
+
const eligible = input.signals.some(signal => RULE_BY_ID.get(signal.rule_id).psp === result.psp
|
|
492
|
+
&& (signal.kind === 'script' ? signal.visible !== false && !input.signals.some(container => container.frame_id === signal.frame_id && ['document', 'frame'].includes(container.kind) && container.visible !== true)
|
|
493
|
+
: signal.visible === true));
|
|
494
|
+
if (!eligible) {
|
|
495
|
+
result.reason_codes.push('payment_signal_inactive');
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
498
|
+
{
|
|
499
|
+
// A native declaration cannot expand the shared Vault's admitted processor variants.
|
|
500
|
+
const incompatible = input.signals.map(signal => ({ signal, rule: RULE_BY_ID.get(signal.rule_id) }))
|
|
501
|
+
.filter(({ signal, rule }) => rule.psp === result.psp && rule.variant_unverified
|
|
502
|
+
&& (signal.kind === 'script' ? signal.visible !== false : signal.visible === true));
|
|
503
|
+
if (incompatible.length) {
|
|
504
|
+
result.reason_codes.push('processor_variant_unverified');
|
|
505
|
+
result.limitations.push(...incompatible.map(({ rule }) => rule.variant_unverified));
|
|
506
|
+
return result;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
const processor = PROCESSOR_BY_ID.get(result.psp);
|
|
510
|
+
const scenario = processor?.scenarios[result.scenario];
|
|
511
|
+
if (processor) {
|
|
512
|
+
const bundledSdk = integration.id === 'direct_sdk' && integration.version === 'bundled';
|
|
513
|
+
if (bundledSdk)
|
|
514
|
+
result.limitations.push(...processor.limitations);
|
|
515
|
+
for (const variant of processor.unsupported_variants) {
|
|
516
|
+
if (bundledSdk || variant.constraint === 'vault')
|
|
517
|
+
result.limitations.push(variant.reason);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
if (scenario && (scenario.status === 'unsupported' || scenario.status === 'outside_vault')) {
|
|
521
|
+
result.status = 'unsupported';
|
|
522
|
+
result.mode = processor.mode;
|
|
523
|
+
result.support_scope = 'processor_integration';
|
|
524
|
+
result.reason_codes.push(scenario.reason);
|
|
525
|
+
return result;
|
|
526
|
+
}
|
|
527
|
+
const profileValue = profile;
|
|
528
|
+
const entry = flow && profileValue.entries.find(entry => entry.flow === flow.id && entry.scenario === result.scenario);
|
|
529
|
+
if (entry) {
|
|
530
|
+
result.status = entry.status;
|
|
531
|
+
result.support_scope = 'checkout_flow';
|
|
532
|
+
result.reason_codes.push(entry.status === 'supported' ? 'flow_supported_by_integration' : 'integration_flow_unsupported');
|
|
533
|
+
result.limitations.push(...(entry.limitations ?? []));
|
|
534
|
+
return result;
|
|
535
|
+
}
|
|
536
|
+
if (!processor)
|
|
537
|
+
return result;
|
|
538
|
+
result.mode = processor.mode;
|
|
539
|
+
const processorEntry = profileValue.processor_entries?.find(entry => entry.psp === processor.psp && entry.scenario === result.scenario);
|
|
540
|
+
if (!processorEntry) {
|
|
541
|
+
result.reason_codes.push(flow ? 'integration_flow_undeclared' : 'integration_processor_undeclared');
|
|
542
|
+
result.missing_evidence.push(flow ? 'integration_flow_capability' : 'integration_processor_capability');
|
|
543
|
+
return result;
|
|
544
|
+
}
|
|
545
|
+
result.status = processorEntry.status;
|
|
546
|
+
result.support_scope = 'processor_integration';
|
|
547
|
+
result.reason_codes.push(processorEntry.status === 'supported' ? 'processor_supported_by_integration' : 'integration_processor_unsupported');
|
|
548
|
+
result.limitations.push(...(processorEntry.limitations ?? []));
|
|
549
|
+
result.limitations = unique(result.limitations);
|
|
550
|
+
return result;
|
|
551
|
+
}
|
|
552
|
+
/** Machine-readable transport shapes; semantic compatibility is checked by the classifier. */
|
|
553
|
+
export function getCheckoutPreflightSchemas() {
|
|
554
|
+
const text = { type: 'string', minLength: 1, maxLength: 120, pattern: '^[A-Za-z0-9][A-Za-z0-9._+-]{0,119}$' };
|
|
555
|
+
const integration = { type: 'object', additionalProperties: false, required: ['id', 'version'], properties: { id: { enum: ['direct_sdk', 'kernel_native'] }, version: { anyOf: [text, { type: 'null' }] } } };
|
|
556
|
+
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 } } } };
|
|
557
|
+
const signal = { type: 'object', additionalProperties: false, required: ['rule_id', 'kind', 'frame_id', 'parent_frame_id', 'visible'], properties: {
|
|
558
|
+
rule_id: { enum: RULES.map(rule => rule.id) }, kind: { enum: KINDS }, frame_id: { type: 'integer', minimum: 0, maximum: 63 },
|
|
559
|
+
parent_frame_id: { anyOf: [{ type: 'integer', minimum: 0, maximum: 63 }, { type: 'null' }] }, visible: { type: ['boolean', 'null'] },
|
|
560
|
+
} };
|
|
561
|
+
const profile = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
562
|
+
required: ['schema_version', 'profile_version', 'catalog_version', 'integration', 'entries'], properties: {
|
|
563
|
+
schema_version: { const: 1 }, profile_version: text, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION }, integration,
|
|
564
|
+
processor_entries: { type: 'array', maxItems: 256, items: {
|
|
565
|
+
type: 'object', additionalProperties: false, required: ['psp', 'mode', 'scenario', 'status'], properties: {
|
|
566
|
+
psp: { enum: KNOWN_PSPS }, mode: { enum: ['token', 'cse', 'hosted_form'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
|
|
567
|
+
limitations: { type: 'array', maxItems: 16, items: { type: 'string', maxLength: 256, pattern: '^[^\\u0000-\\u001f]*$' } },
|
|
568
|
+
},
|
|
569
|
+
} },
|
|
570
|
+
expires_at: { type: 'string', format: 'date-time', maxLength: 40 }, entries: { type: 'array', maxItems: 256, items: {
|
|
571
|
+
type: 'object', additionalProperties: false, required: ['flow', 'operation', 'operation_version', 'mode', 'scenario', 'status'], properties: {
|
|
572
|
+
flow: { enum: FLOWS.map(flow => flow.id) }, operation: { enum: FLOWS.map(flow => flow.operation) }, operation_version: { const: 1 },
|
|
573
|
+
mode: { enum: ['token', 'cse', 'hosted_form'] }, scenario: { enum: SCENARIOS }, status: { enum: ['supported', 'unsupported'] },
|
|
574
|
+
limitations: { type: 'array', maxItems: 16, items: { type: 'string', maxLength: 256, pattern: '^[^\\u0000-\\u001f]*$' } },
|
|
575
|
+
},
|
|
576
|
+
} },
|
|
577
|
+
} };
|
|
578
|
+
const snapshot = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
579
|
+
required: ['schema_version', 'catalog_version', 'signals', 'observation'], properties: {
|
|
580
|
+
schema_version: { const: 1 }, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION },
|
|
581
|
+
signals: { type: 'array', maxItems: CHECKOUT_PREFLIGHT_LIMITS.signals, items: signal }, observation,
|
|
582
|
+
} };
|
|
583
|
+
const evidence = { ...signal, required: [...signal.required, 'origin', 'path_pattern'], properties: { ...signal.properties, origin: { type: 'string', maxLength: 256 }, path_pattern: { type: 'string', maxLength: 256 } } };
|
|
584
|
+
const result = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
585
|
+
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: {
|
|
586
|
+
schema_version: { const: 1 }, catalog_version: { const: CHECKOUT_PREFLIGHT_CATALOG_VERSION }, integration,
|
|
587
|
+
scenario: { enum: SCENARIOS }, scenario_defaulted: { type: 'boolean' }, psp: { type: ['string', 'null'] }, flow: { type: ['string', 'null'] }, mode: { enum: ['token', 'cse', 'hosted_form', null] },
|
|
588
|
+
support_scope: { enum: ['processor_integration', 'checkout_flow', null] }, checkout_flow_status: { enum: ['identified', 'unknown', 'ambiguous'] },
|
|
589
|
+
status: { enum: ['supported', 'unsupported', 'unknown'] }, candidates: { type: 'array', items: { type: 'object', additionalProperties: false, required: ['psp', 'relevance', 'evidence'], properties: {
|
|
590
|
+
psp: { type: 'string' }, relevance: { enum: ['active', 'undetermined'] }, evidence: { type: 'array', items: evidence },
|
|
591
|
+
} } }, reason_codes: { type: 'array', items: { type: 'string' } }, limitations: { type: 'array', items: { type: 'string' } }, missing_evidence: { type: 'array', items: { type: 'string' } }, observation,
|
|
592
|
+
scope: { const: 'preflight_hint' }, merchant_confirmation_required: { const: true },
|
|
593
|
+
} };
|
|
594
|
+
const rawSignalProperties = { ...signal.properties, url: { type: 'string', maxLength: CHECKOUT_PREFLIGHT_LIMITS.raw_url_length } };
|
|
595
|
+
delete rawSignalProperties.rule_id;
|
|
596
|
+
const observations = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false,
|
|
597
|
+
required: ['signals', 'observation'], properties: { signals: { type: 'array', maxItems: CHECKOUT_PREFLIGHT_LIMITS.signals, items: {
|
|
598
|
+
type: 'object', additionalProperties: false, required: ['url', 'kind', 'frame_id', 'parent_frame_id', 'visible'], properties: rawSignalProperties,
|
|
599
|
+
} }, observation } };
|
|
600
|
+
const options = { $schema: 'https://json-schema.org/draft/2020-12/schema', type: 'object', additionalProperties: false, properties: {
|
|
601
|
+
scenario: { enum: SCENARIOS }, integration, profile, now: { type: 'string', format: 'date-time', maxLength: 40 },
|
|
602
|
+
} };
|
|
603
|
+
return clone({ observations, options, snapshot, profile, result });
|
|
604
|
+
}
|