@agent-cards/checkout 0.16.0 → 0.17.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 +2 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/cdp.js +24 -1
- package/dist/paysafe.generated.d.ts +12 -0
- package/dist/paysafe.generated.js +87 -0
- package/dist/prepared-processor.d.ts +1 -1
- package/dist/prepared-processor.js +6 -1
- package/dist/stripe-checkout.generated.js +3 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Approve a Paysafe Checkout 1.8.0 fresh-card checkout before Pay with `prepare({ psp: 'paysafe', environment: 'production' | 'sandbox' })`. Phone approval finishes before the hosted checkout starts its card request. One matching request consumes the approval; saved cards and other Paysafe APIs do not use preparation. Matching API, Vault and database releases are required. A token does not confirm merchant payment.
|
|
6
|
+
|
|
5
7
|
- Keep the cardholder's approval when the merchant page gives up on its own card request. A page whose script times out its tokenization while the person is still deciding (Braintree after 60 seconds, Square after about 10) no longer cancels the approval: the SDK reports `merchant_request_lost`, answers the page's next request for the same purchase from the same authorization, and reads `ready_to_submit` with reason `awaiting_merchant_retry` when the approval lands before the page asks again, so one more Pay click completes the purchase with no second prompt. The wait for that request is bounded by `merchantRetryWaitMs` (two minutes by default, never past the approval window); an approval the page never asks for again is retired through the API as `merchant_never_retried` and the controller reads `declined` with that reason. Applies to `token` and `cse` checkouts; hosted forms, prepared checkouts and native Stripe Checkout keep cancelling. `VaultClient.cancelAuthorization` takes the reason and `checkoutModeOf` reads a request's mode. The matching API and Vault releases are required for the new reason; an older API answers its retirement as unknown, which the SDK holds.
|
|
6
8
|
|
|
7
9
|
- Approve a Checkout.com guest card checkout before Pay with `prepare({ psp: 'checkout_com', environment: 'production' | 'sandbox' })`. Phone approval finishes before Flow starts its 12-second tokenization deadline. One fresh card request on the matching API or CAG host consumes the approval; saved-card, wallet and reusable-token requests are refused. Matching API, Vault and database releases are required. A token does not establish merchant payment completion.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i] }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
|
|
1
|
+
export const BUILTIN_REGISTRY = [{ "psp": "spreedly", "match": /core\.spreedly\.com\/v1\/payment_methods\/restricted\.json/i, "hosts": ["^core\\.spreedly\\.com$"], "encoding": "json", "passthroughHeaders": [/^spreedly-environment-key$/i] }, { "psp": "shopify", "match": /(checkout\.pci\.shopifyinc\.com|deposit\.[a-z0-9-]+\.shopifycs\.com)\/sessions/i, "hosts": ["^checkout\\.pci\\.shopifyinc\\.com$", "^deposit\\.[a-z0-9-]+\\.shopifycs\\.com$"], "encoding": "json", "passthroughHeaders": [/^shopify-identification-signature$/i] }, { "psp": "stripe", "match": /api\.stripe\.com\/v1\/(payment_methods|tokens|setup_intents\/seti_\w+\/confirm|payment_intents\/pi_\w+\/confirm)/i, "hosts": ["^api\\.stripe\\.com$"], "encoding": "form", "passthroughHeaders": [/^authorization$/i, /^stripe-version$/i, /^x-stripe-client-user-agent$/i] }, { "psp": "braintree", "match": /payments(\.sandbox)?\.braintree-api\.com\/graphql/i, "hosts": ["^payments\\.braintree-api\\.com$", "^payments\\.sandbox\\.braintree-api\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^braintree-version$/i] }, { "psp": "checkout_com", "match": /(api|card-acquisition-gateway)(\.sandbox)?\.checkout\.com\/tokens/i, "hosts": ["^api\\.checkout\\.com$", "^api\\.sandbox\\.checkout\\.com$", "^card-acquisition-gateway\\.checkout\\.com$", "^card-acquisition-gateway\\.sandbox\\.checkout\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "adyen", "match": /(checkoutshopper-(test|live(-[a-z]+)?)\.adyen\.com|([a-z0-9-]+\.)*adyenpayments\.com)\/checkoutshopper\/v1\/sessions\/[A-Za-z0-9_-]+\/payments/i, "hosts": ["^checkoutshopper-(test|live(-[a-z]+)?)\\.adyen\\.com$", "^([a-z0-9-]+\\.)*adyenpayments\\.com$"], "encoding": "json", "passthroughHeaders": [], "clientSideEncrypted": true, "mode": "cse" }, { "psp": "tranzila", "match": /(direct\.tranzila\.com\/process\/n\/|directng\.tranzila\.com\/process\/?)/i, "hosts": ["^direct\\.tranzila\\.com$", "^directng\\.tranzila\\.com$"], "encoding": "form", "passthroughHeaders": [], "mode": "hosted_form" }, { "psp": "square", "match": /pci-connect\.squareup(?:sandbox)?\.com\/v2\/card-nonce/i, "hosts": ["^pci-connect\\.squareup\\.com$", "^pci-connect\\.squareupsandbox\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "authorize_net", "match": /(api2|apitest)\.authorize\.net\/xml\/v1\/request\.api/i, "hosts": ["^api2\\.authorize\\.net$", "^apitest\\.authorize\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "worldpay", "match": /(try\.)?access\.worldpay\.com\/(sessions\/card|verifiedTokens\/sessions)/i, "hosts": ["^access\\.worldpay\\.com$", "^try\\.access\\.worldpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^accept$/i] }, { "psp": "nuvei", "match": /(secure|ppp-test)\.safecharge\.com\/ppp\/api\/v1\/(?:cardTokenization|clientPayment|websdk\/initPaymentWithCardTokenization)\.do/i, "hosts": ["^secure\\.safecharge\\.com$", "^ppp-test\\.safecharge\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "airwallex", "match": /(checkout(\.sandbox)?\.airwallex\.com|pci-api(\.sandbox)?\.airwallex\.com)\/api\/v1\/pa\/(payment_intents\/[^/?#]+\/confirm|payment_consents\/[^/?#]+\/verify|payment_methods\/create)(\?|$)/i, "hosts": ["^checkout\\.airwallex\\.com$", "^checkout\\.sandbox\\.airwallex\\.com$", "^pci-api\\.airwallex\\.com$", "^pci-api\\.sandbox\\.airwallex\\.com$"], "encoding": "json", "passthroughHeaders": [/^client-secret$/i, /^authorization$/i, /^x-auth-token$/i, /^x-on-behalf-of$/i, /^x-api-version$/i] }, { "psp": "rapyd", "match": /(api|sandboxapi)\.rapyd\.net\/v1\/(?:hosted\/collect\/card\/[^/?#]+\/payment_method|checkout\/checkout_[A-Za-z0-9]+)(?:[?#]|$)/i, "hosts": ["^api\\.rapyd\\.net$", "^sandboxapi\\.rapyd\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "dlocal", "match": /ppmcc(-sandbox)?\.dlocal\.com\/cvault\/credit-card\/temporal/i, "hosts": ["^ppmcc\\.dlocal\\.com$", "^ppmcc-sandbox\\.dlocal\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-fields-api-key$/i, /^x-uow$/i, /^x-dlocal-infrav2$/i] }, { "psp": "ebanx", "match": /(?:(?:customer-frontier|customer|api-local-latam|api|sandbox-local-latam|sandbox)\.ebanx\.com|(?:api-diamond|api|sandbox)\.ebanxpay\.com)\/ws\/token/i, "hosts": ["^customer\\.ebanx\\.com$", "^customer-frontier\\.ebanx\\.com$", "^api\\.ebanx\\.com$", "^sandbox\\.ebanx\\.com$", "^api-local-latam\\.ebanx\\.com$", "^sandbox-local-latam\\.ebanx\\.com$", "^api-diamond\\.ebanxpay\\.com$", "^api\\.ebanxpay\\.com$", "^sandbox\\.ebanxpay\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "mercado_pago", "match": /api\.mercadopago\.com\/v1\/card_tokens(?![\w/-])/i, "hosts": ["^api\\.mercadopago\\.com$"], "encoding": "json", "passthroughHeaders": [/^x-product-id$/i] }, { "psp": "payu", "match": /(secure\.payu\.com|(merch-prod|secure)\.snd\.payu\.com)\/api\/front\/tokens/i, "hosts": ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i] }, { "psp": "razorpay", "match": /api\.razorpay\.com\/v1\/(?:standard_checkout\/)?payments\/create\/(?:ajax|checkout|fees)/i, "hosts": ["^api\\.razorpay\\.com$"], "encoding": "form", "passthroughHeaders": [/^x-razorpay-sessionid$/i, /^x-customer-access-token$/i] }, { "psp": "mollie", "match": /api\.cc\.mollie\.com\/v1\/card-tokens/i, "hosts": ["^api\\.cc\\.mollie\\.com$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "paysafe", "match": /(?:api(\.test)?\.paysafe\.com\/(?:paymenthub\/v1\/singleusepaymenthandles|js\/api\/v1\/tokenize)|hosted(\.test)?\.paysafe\.com\/checkout\/api\/v1\/tokenize)(?![\w/-])/i, "hosts": ["^api\\.paysafe\\.com$", "^api\\.test\\.paysafe\\.com$", "^hosted\\.paysafe\\.com$", "^hosted\\.test\\.paysafe\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-paysafe-credentials$/i, /^correlationid$/i] }, { "psp": "recurly", "match": /api(\.eu)?\.recurly\.com\/js\/v1\/token(?![\w-])/i, "hosts": ["^api\\.recurly\\.com$", "^api\\.eu\\.recurly\\.com$"], "encoding": "form", "passthroughHeaders": [/^recurly-credential-checkout-hostname$/i] }, { "psp": "moneris", "match": /(www3|esqa|gateway|gatewayqa|gatewayt|gatewaydev)\.moneris\.com\/HPPtoken\/request\.php/i, "hosts": ["^www3\\.moneris\\.com$", "^esqa\\.moneris\\.com$", "^gateway\\.moneris\\.com$", "^gatewayqa\\.moneris\\.com$", "^gatewayt\\.moneris\\.com$", "^gatewaydev\\.moneris\\.com$"], "encoding": "form", "passthroughHeaders": [] }, { "psp": "bambora", "match": /(api\.na\.bambora\.com|api\.bam\.shift4api\.net)\/scripts\/tokenization\/tokens/i, "hosts": ["^api\\.na\\.bambora\\.com$", "^api\\.bam\\.shift4api\\.net$"], "encoding": "json", "passthroughHeaders": [] }, { "psp": "global_payments", "match": /(?:api\.heartlandportico\.com\/SecureSubmit\.v1\/api\/token|cert\.api2\.heartlandportico\.com\/Hps\.Exchange\.PosGateway\.Hpf\.v1\/api\/token|apis(?:\.sandbox)?(?:\.eu)?\.globalpay\.com\/ucp\/(?:merchants\/[^/]+\/)?payment-methods)/i, "hosts": ["^api\\.heartlandportico\\.com$", "^cert\\.api2\\.heartlandportico\\.com$", "^apis\\.globalpay\\.com$", "^apis\\.sandbox\\.globalpay\\.com$", "^apis\\.eu\\.globalpay\\.com$", "^apis\\.sandbox\\.eu\\.globalpay\\.com$"], "encoding": "json", "passthroughHeaders": [/^authorization$/i, /^x-gp-version$/i] }];
|
package/dist/cdp.js
CHANGED
|
@@ -766,6 +766,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
766
766
|
const sessionTypes = new Map([[pageSessionId, 'page']]);
|
|
767
767
|
const sessionParents = new Map();
|
|
768
768
|
const detachedSessions = new Set();
|
|
769
|
+
const rejectedSessions = new Set();
|
|
769
770
|
const workerOwners = new Map();
|
|
770
771
|
const ownersWithWorkers = new Set();
|
|
771
772
|
// Network IDs are scoped to their emitting session. Retired records are
|
|
@@ -1153,9 +1154,31 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1153
1154
|
return;
|
|
1154
1155
|
}
|
|
1155
1156
|
if (method === 'Target.attachedToTarget') {
|
|
1157
|
+
const child = params.sessionId;
|
|
1158
|
+
if (rejectedSessions.has(child))
|
|
1159
|
+
return;
|
|
1160
|
+
// A browser-root transport can report another context through an owned
|
|
1161
|
+
// parent session. Release its debugger wait without enrolling it in this
|
|
1162
|
+
// checkout. Remember the exclusion before awaiting cleanup: a duplicate
|
|
1163
|
+
// event with omitted context metadata must never acquire ownership.
|
|
1164
|
+
const childContext = params.targetInfo?.browserContextId;
|
|
1165
|
+
if (contextId(childContext) && childContext !== merchantContext) {
|
|
1166
|
+
if (typeof child !== 'string' || !child || child === pageSessionId
|
|
1167
|
+
|| ownedSessions.has(child) || detachedSessions.has(child))
|
|
1168
|
+
return;
|
|
1169
|
+
rejectedSessions.add(child);
|
|
1170
|
+
// Owned setup may already have stopped. Foreign cleanup has its own
|
|
1171
|
+
// deadline so rejection cannot freeze an unrelated frame or worker.
|
|
1172
|
+
const cleanupStop = new AbortController();
|
|
1173
|
+
const cleanup = (method, params, session) => withinAttachmentDeadline(async () => {
|
|
1174
|
+
await cdp.send(method, params, session);
|
|
1175
|
+
}, setupTimeoutMs, cleanupStop.signal);
|
|
1176
|
+
await cleanup('Runtime.runIfWaitingForDebugger', {}, child).catch(() => cleanup('Target.detachFromTarget', { sessionId: child }, sessionId).catch(() => { }));
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1156
1179
|
if (setupStop.signal.aborted)
|
|
1157
1180
|
return;
|
|
1158
|
-
|
|
1181
|
+
// CDP may omit the context ID for ordinary children; keep those attached.
|
|
1159
1182
|
try {
|
|
1160
1183
|
if (typeof child !== 'string' || !child || !['page', 'iframe', 'worker'].includes(params.targetInfo?.type)) {
|
|
1161
1184
|
throw new CheckoutAttachmentError('unavailable');
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type PaysafePreparationEnvironment = 'production' | 'sandbox';
|
|
2
|
+
export declare function paysafePreparationUrl(environment: string): string;
|
|
3
|
+
/** Exact strings refuse credentials, queries, fragments and URL normalization. */
|
|
4
|
+
export declare function paysafeEnvironment(url: string): PaysafePreparationEnvironment | undefined;
|
|
5
|
+
/** Checkout 1.8.0 starts a 40-second XHR when Pay is pressed. Prior approval may
|
|
6
|
+
* consume only its new-card request, including native clientInfo and headers.
|
|
7
|
+
* Saved cards, alternate APIs and 3DS-only requests require separate review.
|
|
8
|
+
* Source: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
|
|
9
|
+
* e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c.
|
|
10
|
+
* The guard checks original bytes and never changes the request.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isPreparedPaysafeRequest(url: string, method: string, body: string | null, environment: string, headers?: Record<string, string>): boolean;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Generated from @agent-cards/payment-core. Do not edit.
|
|
2
|
+
// source-sha256: 24c7940ae8974c0d24e5a365144c1b07ccc46863ae3094eb92b3c54c8307e837
|
|
3
|
+
import { readTokenizationJson } from './braintree.js';
|
|
4
|
+
const ENDPOINTS = {
|
|
5
|
+
production: 'https://hosted.paysafe.com/checkout/api/v1/tokenize',
|
|
6
|
+
sandbox: 'https://hosted.test.paysafe.com/checkout/api/v1/tokenize',
|
|
7
|
+
};
|
|
8
|
+
const record = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
9
|
+
const keys = (value, allowed) => record(value) && Object.keys(value).every(key => allowed.includes(key));
|
|
10
|
+
const exact = (pattern, value) => typeof value === 'string' && pattern.exec(value)?.[0] === value;
|
|
11
|
+
const text = (value, max = 1024) => typeof value === 'string' && value.length <= max && !/[\u0000-\u001f\u007f]/.test(value);
|
|
12
|
+
const own = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
13
|
+
const uuid = (value) => exact(/^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i, value);
|
|
14
|
+
export function paysafePreparationUrl(environment) {
|
|
15
|
+
if (environment !== 'production' && environment !== 'sandbox')
|
|
16
|
+
throw new Error('unsupported_preparation_environment');
|
|
17
|
+
return ENDPOINTS[environment];
|
|
18
|
+
}
|
|
19
|
+
/** Exact strings refuse credentials, queries, fragments and URL normalization. */
|
|
20
|
+
export function paysafeEnvironment(url) {
|
|
21
|
+
return url === ENDPOINTS.production ? 'production' : url === ENDPOINTS.sandbox ? 'sandbox' : undefined;
|
|
22
|
+
}
|
|
23
|
+
const ADDRESS_FIELDS = ['city', 'country', 'zip', 'street', 'street2', 'state'];
|
|
24
|
+
function billing(value) {
|
|
25
|
+
return keys(value, ADDRESS_FIELDS) && Object.values(value).every(value => text(value))
|
|
26
|
+
&& (!own(value, 'country') || exact(/^[A-Z]{2}$/, value.country));
|
|
27
|
+
}
|
|
28
|
+
function freshCard(value) {
|
|
29
|
+
if (!keys(value, ['card', 'clientInfo', 'billingAddress']) || !keys(value.card, ['cardNum', 'cardExpiry', 'cvv', 'holderName']))
|
|
30
|
+
return false;
|
|
31
|
+
const card = value.card;
|
|
32
|
+
if (!exact(/^[0-9]{12,19}$/, card.cardNum) || !exact(/^[0-9]{3,4}$/, card.cvv)
|
|
33
|
+
|| !keys(card.cardExpiry, ['month', 'year']) || !exact(/^(?:0[1-9]|1[0-2])$/, card.cardExpiry.month)
|
|
34
|
+
|| !exact(/^20[0-9]{2}$/, card.cardExpiry.year) || own(card, 'holderName') && !text(card.holderName)
|
|
35
|
+
|| own(value, 'billingAddress') && !billing(value.billingAddress))
|
|
36
|
+
return false;
|
|
37
|
+
const info = value.clientInfo;
|
|
38
|
+
return keys(info, ['correlationId', 'invocationId', 'version', 'appName'])
|
|
39
|
+
&& uuid(info.correlationId) && uuid(info.invocationId) && info.version === '1.8.0' && info.appName === 'paysafe.checkout';
|
|
40
|
+
}
|
|
41
|
+
const BROWSER_HEADERS = new Set(['accept', 'accept-language', 'accept-encoding', 'content-length', 'origin', 'referer',
|
|
42
|
+
'user-agent', 'host', 'connection', 'cache-control', 'pragma', 'priority', 'dnt',
|
|
43
|
+
'sec-fetch-dest', 'sec-fetch-mode', 'sec-fetch-site', 'sec-fetch-user']);
|
|
44
|
+
function singleUseCredential(value) {
|
|
45
|
+
if (!exact(/^Basic [A-Za-z0-9+/]+={0,2}$/, value) || value.length > 1024)
|
|
46
|
+
return false;
|
|
47
|
+
try {
|
|
48
|
+
const encoded = value.slice(6), decoded = atob(encoded);
|
|
49
|
+
// Checkout receives public OT credentials, never merchant API credentials.
|
|
50
|
+
return btoa(decoded) === encoded && exact(/^OT-[0-9]{1,20}:[!-~]{1,512}$/, decoded);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function nativeHeaders(headers, correlationId) {
|
|
57
|
+
if (!record(headers))
|
|
58
|
+
return false;
|
|
59
|
+
const parsed = new Map();
|
|
60
|
+
let bytes = 0;
|
|
61
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
62
|
+
const lower = name.toLowerCase();
|
|
63
|
+
if (parsed.size >= 64 || parsed.has(lower) || !exact(/^[A-Za-z0-9-]+$/, name) || !text(value, 8192)
|
|
64
|
+
|| (bytes += name.length + value.length) > 32768)
|
|
65
|
+
return false;
|
|
66
|
+
if (!['content-type', 'correlationid', 'x-paysafe-credentials'].includes(lower) && !BROWSER_HEADERS.has(lower)
|
|
67
|
+
&& !exact(/^sec-ch-ua(?:-[a-z0-9-]+)?$/, lower))
|
|
68
|
+
return false;
|
|
69
|
+
parsed.set(lower, value);
|
|
70
|
+
}
|
|
71
|
+
return exact(/^application\/json(?:;\s*charset=utf-8)?$/i, parsed.get('content-type'))
|
|
72
|
+
&& parsed.get('correlationid') === correlationId && singleUseCredential(parsed.get('x-paysafe-credentials'));
|
|
73
|
+
}
|
|
74
|
+
/** Checkout 1.8.0 starts a 40-second XHR when Pay is pressed. Prior approval may
|
|
75
|
+
* consume only its new-card request, including native clientInfo and headers.
|
|
76
|
+
* Saved cards, alternate APIs and 3DS-only requests require separate review.
|
|
77
|
+
* Source: hosted.paysafe.com/checkout/1.8.0/main.bundle.js, SHA-256
|
|
78
|
+
* e9e111ff03073cc936a47674e8e7c5db4a6d1050e1b65bf5e20723116fd5080c.
|
|
79
|
+
* The guard checks original bytes and never changes the request.
|
|
80
|
+
*/
|
|
81
|
+
export function isPreparedPaysafeRequest(url, method, body, environment, headers) {
|
|
82
|
+
if (method !== 'POST' || (environment !== 'production' && environment !== 'sandbox')
|
|
83
|
+
|| paysafeEnvironment(url) !== environment)
|
|
84
|
+
return false;
|
|
85
|
+
const value = readTokenizationJson(body);
|
|
86
|
+
return freshCard(value) && record(value.clientInfo) && nativeHeaders(headers, value.clientInfo.correlationId);
|
|
87
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com';
|
|
1
|
+
export type PreparationProcessor = 'square' | 'braintree' | 'worldpay' | 'bambora' | 'mercado_pago' | 'recurly' | 'spreedly' | 'adyen' | 'checkout_com' | 'paysafe';
|
|
2
2
|
export type PreparationEnvironment = 'production' | 'sandbox' | 'shared';
|
|
3
3
|
/** How the approved card reaches the processor: the device's own request (token), or ciphertext the device produces for this browser to send (cse, Adyen). */
|
|
4
4
|
export type PreparationMode = 'token' | 'cse';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { paysafePreparationUrl, isPreparedPaysafeRequest } from './paysafe.generated.js';
|
|
1
2
|
import { adyenPreparationUrl, isPreparedAdyenRequest } from './adyen.generated.js';
|
|
2
3
|
import { braintreeEnvironment, isPreparedBraintreeRequest, readTokenizationJson } from './braintree.js';
|
|
3
4
|
import { isPreparedRecurlyRequest } from './recurly.generated.js';
|
|
@@ -10,11 +11,13 @@ export function preparationMode(psp) {
|
|
|
10
11
|
export function validPreparationEnvironment(psp, environment) {
|
|
11
12
|
if (psp === 'bambora' || psp === 'mercado_pago' || psp === 'recurly' || psp === 'spreedly')
|
|
12
13
|
return environment === 'shared';
|
|
13
|
-
return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com'].includes(psp) && ['production', 'sandbox'].includes(environment);
|
|
14
|
+
return ['square', 'braintree', 'worldpay', 'adyen', 'checkout_com', 'paysafe'].includes(psp) && ['production', 'sandbox'].includes(environment);
|
|
14
15
|
}
|
|
15
16
|
export function preparationEndpoint(psp, environment) {
|
|
16
17
|
if (!validPreparationEnvironment(psp, environment))
|
|
17
18
|
throw new Error('unsupported_preparation_processor');
|
|
19
|
+
if (psp === 'paysafe')
|
|
20
|
+
return paysafePreparationUrl(environment);
|
|
18
21
|
if (psp === 'checkout_com')
|
|
19
22
|
return checkoutComPreparationUrl(environment);
|
|
20
23
|
if (psp === 'adyen')
|
|
@@ -79,6 +82,8 @@ function freshCardBody(psp, body) {
|
|
|
79
82
|
export function matchesPreparedRequest(psp, environment, requestUrl, method, body, headers) {
|
|
80
83
|
if (method.toUpperCase() !== 'POST' || !validPreparationEnvironment(psp, environment))
|
|
81
84
|
return false;
|
|
85
|
+
if (psp === 'paysafe')
|
|
86
|
+
return isPreparedPaysafeRequest(requestUrl, method, body ?? null, environment, headers);
|
|
82
87
|
if (psp === 'checkout_com')
|
|
83
88
|
return isPreparedCheckoutComRequest(requestUrl, method, body ?? null, environment, headers);
|
|
84
89
|
if (psp === 'adyen')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
3
|
-
// artifact-sha256:
|
|
3
|
+
// artifact-sha256: 0d2e18239c2a0102d3a473e339c8921aac7777e640ad44a2743a922204f2edfc
|
|
4
4
|
// src/definitions.js
|
|
5
5
|
function deepFreeze(value) {
|
|
6
6
|
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
@@ -322,7 +322,8 @@ var PROCESSOR_DEFINITIONS = deepFreeze([
|
|
|
322
322
|
match: "(secure\\.payu\\.com|(merch-prod|secure)\\.snd\\.payu\\.com)/api/front/tokens",
|
|
323
323
|
hosts: ["^secure\\.payu\\.com$", "^(merch-prod|secure)\\.snd\\.payu\\.com$"],
|
|
324
324
|
encoding: "json",
|
|
325
|
-
|
|
325
|
+
// Native checkout attaches a bearer to tokenization; preserve it on replay.
|
|
326
|
+
passthroughHeaders: ["^authorization$"]
|
|
326
327
|
},
|
|
327
328
|
{
|
|
328
329
|
psp: "razorpay",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-cards/checkout",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Let browser agents pay with the user's own card, without your infrastructure ever touching card data.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"_comment_build": "The public SDK keeps zero runtime dependencies. Its build vendors deterministic metadata and substitution artifacts from the internal payment core; TypeScript remains pinned.",
|
|
28
28
|
"build": "node ../payment-core/scripts/build.mjs && node scripts/generate-payment-core.mjs && npx -y -p typescript@5.9.3 tsc && node scripts/generate-preflight-contract.mjs",
|
|
29
29
|
"prepublishOnly": "pnpm build",
|
|
30
|
-
"test": "node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs autopilot.test.mjs stripe-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs minimum-delay.test.mjs paysafe.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs",
|
|
31
|
-
"test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs",
|
|
30
|
+
"test": "node test.mjs && node --test lifecycle.test.mjs merchant-abort.test.mjs preparation.test.mjs braintree.test.mjs autopilot.test.mjs stripe-checkout.test.mjs payment-core.test.mjs prepared-processor.test.mjs minimum-delay.test.mjs paysafe.test.mjs payu.test.mjs attachment.test.mjs worker-targets.test.mjs mercado-checkout.test.mjs mercado-polling.test.mjs && node --test preflight-package.test.mjs preflight-collector.test.mjs && node --test kernel-native-qualification.test.mjs && node --test ../vault/scripts/recurly-validation/watch-duty-sdk-result.test.mjs",
|
|
31
|
+
"test:browser": "node browser.test.mjs && node stripe-browser.test.mjs && node preparation-browser.test.mjs && node checkout-com-browser.test.mjs && node paysafe-preparation-browser.test.mjs && node owned-shop-browser.test.mjs && node spreedly-browser.test.mjs && node worker-browser.test.mjs",
|
|
32
32
|
"check:payment-core": "node scripts/generate-payment-core.mjs --check",
|
|
33
33
|
"test:preflight": "node --test preflight-package.test.mjs preflight-collector.test.mjs kernel-native-qualification.test.mjs",
|
|
34
34
|
"test:preflight:browser": "node preflight-browser.test.mjs",
|