@agent-cards/sdk 0.24.0 → 0.24.2
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 +32 -0
- package/README.md +61 -4
- package/dist/adyen-merchant-hosted.generated.d.ts +1 -1
- package/dist/adyen-merchant-hosted.generated.js +128 -35
- package/dist/agent-amount.generated.js +1 -1
- package/dist/aurus.d.ts +33 -0
- package/dist/aurus.js +75 -0
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/capabilities.generated.js +1 -1
- package/dist/card-fields.generated.js +1854 -440
- package/dist/cardlink-vpos.d.ts +43 -0
- package/dist/cardlink-vpos.js +90 -0
- package/dist/cdp.js +22 -0
- package/dist/checkout-com.generated.js +5 -1
- package/dist/client.d.ts +75 -0
- package/dist/client.js +75 -0
- package/dist/cybersource-flex.generated.d.ts +39 -1
- package/dist/cybersource-flex.generated.js +100 -11
- package/dist/eghl.d.ts +40 -0
- package/dist/eghl.js +72 -0
- package/dist/gmo.generated.js +7 -2
- package/dist/hosted-entry.js +2 -0
- package/dist/lifecycle.d.ts +10 -0
- package/dist/lifecycle.js +25 -0
- package/dist/paddle-tokenex.generated.js +7 -2
- package/dist/payrails.generated.js +7 -2
- package/dist/pre-claim.d.ts +5 -0
- package/dist/pre-claim.js +90 -2
- package/dist/preflight-catalog.json +179 -1
- package/dist/preflight-schemas.json +16 -0
- package/dist/preflight.generated.d.ts +5 -0
- package/dist/preflight.generated.js +49 -3
- package/dist/preparation.d.ts +7 -0
- package/dist/preparation.js +21 -0
- package/dist/primer.generated.js +1856 -442
- package/dist/shiji-sts.generated.js +7 -2
- package/dist/stripe-checkout.generated.js +1837 -475
- package/dist/substitutions.generated.js +94 -1
- package/dist/tap-payments.generated.js +7 -2
- package/dist/token-repeats.d.ts +32 -0
- package/dist/token-repeats.js +64 -0
- package/dist/twoc2p.generated.js +7 -2
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +2 -2
package/dist/pre-claim.js
CHANGED
|
@@ -88,10 +88,13 @@ import { isRedsysEntry, isRedsysHost, redsysEntryUrl } from './redsys.js';
|
|
|
88
88
|
import { isArmedPrzelewy24Url, isPrzelewy24PayStep } from './przelewy24.js';
|
|
89
89
|
import { isArmedBarionUrl, isBarionPayStep } from './barion.js';
|
|
90
90
|
import { isArmedPayplugUrl, isPayplugPayStep } from './payplug.js';
|
|
91
|
+
import { isArmedCardlinkVposUrl, isCardlinkVposEntry } from './cardlink-vpos.js';
|
|
92
|
+
import { isArmedEghlUrl, isEghlPayStep } from './eghl.js';
|
|
91
93
|
import { isQuickpayCardLookup, isQuickpayEntry, quickpayEntryUrl } from './quickpay.js';
|
|
92
94
|
import { isArmedComgateUrl } from './comgate.js';
|
|
93
95
|
import { isArmedEximbayUrl, isEximbayEntry } from './eximbay.js';
|
|
94
96
|
import { isArmedOceanpaymentUrl, isOceanpaymentSendPay } from './oceanpayment.js';
|
|
97
|
+
import { aurusPreClaim } from './aurus.js';
|
|
95
98
|
import { PAYPAL_HOSTED_FIELDS_REASON, isPaypalHostedFieldsConfirm, paypalHostedFieldsBody } from './paypal-hosted-fields.js';
|
|
96
99
|
import { PAYPAL_CARD_FIELDS_REASON, PAYPAL_GUEST_CHECKOUT_REASON, isPaypalCardFieldsConfirm, isPaypalGraphql, paypalCardFieldsBody, paypalGraphqlCard, paypalGraphqlNamesCardOperation, } from './paypal-card-forms.js';
|
|
97
100
|
import { CYBERSOURCE_SECURE_ACCEPTANCE_REASON, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, cybersourceSecureAcceptanceRequest, isCybersourceSecureAcceptanceUrl, isSecureAcceptanceFramePost, } from './cybersource-secure-acceptance.js';
|
|
@@ -999,6 +1002,50 @@ const tokenexIframePreClaim = (ctx) => {
|
|
|
999
1002
|
return ctx.lifecycle.getState().status === 'awaiting_merchant'
|
|
1000
1003
|
? repeatAfterHandoff(ctx, repeat, TOKENEX_IFRAME_PSP) : answerRepeat(ctx, repeat, TOKENEX_IFRAME_PSP);
|
|
1001
1004
|
};
|
|
1005
|
+
// Any processor's plain token request, sent again by the page byte for byte after its approved answer reached it
|
|
1006
|
+
// (token-repeats.ts). Last among the gates, so a processor with its own repeat rule (Datatrans, the TokenEx iframe) or
|
|
1007
|
+
// its own hold (Stripe, a card endpoint) decides first. While the lifecycle reads the re-send as answerable
|
|
1008
|
+
// (repeatAnswerable: the same approval awaits the merchant, and nothing but a pending read holds it), the gate asks the
|
|
1009
|
+
// merchant resolver and answers with the stored token only while the order still reads pending, as repeatAfterHandoff
|
|
1010
|
+
// does. Every other card request, another amount or approval among them, defers to the terminal arbiter and the
|
|
1011
|
+
// adapters' hold, as before.
|
|
1012
|
+
const tokenRepeatPreClaim = (ctx) => {
|
|
1013
|
+
const { url, method } = ctx.request;
|
|
1014
|
+
if (!ctx.tokenRepeats || method.toUpperCase() !== 'POST' || !ctx.lifecycle.repeatAnswerable?.() || !ctx.lifecycle.hasMerchantResolver?.())
|
|
1015
|
+
return undefined;
|
|
1016
|
+
// A processor with its own repeat rule keeps its own limits: a re-send its rule declined is a new question.
|
|
1017
|
+
if (isDatatransHost(url) || isTokenexIframePath(url))
|
|
1018
|
+
return undefined;
|
|
1019
|
+
if (ctx.terminal() || ctx.interceptionAborted() || ctx.lifecycleAborted())
|
|
1020
|
+
return undefined;
|
|
1021
|
+
// Read only here, past every cheaper check: a body the adapter cannot read is no re-send of an answered one.
|
|
1022
|
+
let body;
|
|
1023
|
+
try {
|
|
1024
|
+
body = ctx.request.body;
|
|
1025
|
+
}
|
|
1026
|
+
catch {
|
|
1027
|
+
return undefined;
|
|
1028
|
+
}
|
|
1029
|
+
const repeats = ctx.tokenRepeats;
|
|
1030
|
+
const repeat = repeats.answerFor(ctx.frame, url, body, ctx.lifecycle.getState().authorizationId);
|
|
1031
|
+
if (!repeat)
|
|
1032
|
+
return undefined;
|
|
1033
|
+
// A re-send the page never got an answer for gives its place in the count back.
|
|
1034
|
+
return tokenRepeatWhilePending(ctx, repeat).then((decision) => {
|
|
1035
|
+
if (decision?.kind !== 'answer')
|
|
1036
|
+
repeats.released(repeat.authorizationId);
|
|
1037
|
+
return decision;
|
|
1038
|
+
}, (error) => { repeats.released(repeat.authorizationId); throw error; });
|
|
1039
|
+
};
|
|
1040
|
+
async function tokenRepeatWhilePending(ctx, repeat) {
|
|
1041
|
+
if (!await ctx.lifecycle.merchantOrderPending())
|
|
1042
|
+
return undefined;
|
|
1043
|
+
// The resolver answered asynchronously: the same approval must still be the one awaiting the merchant.
|
|
1044
|
+
if (!ctx.lifecycle.repeatAnswerable() || ctx.lifecycle.getState().authorizationId !== repeat.authorizationId
|
|
1045
|
+
|| ctx.terminal() || ctx.interceptionAborted() || ctx.lifecycleAborted())
|
|
1046
|
+
return undefined;
|
|
1047
|
+
return answerRepeat(ctx, repeat, 'token');
|
|
1048
|
+
}
|
|
1002
1049
|
async function tokenexIframeBlock(ctx, reason) {
|
|
1003
1050
|
ctx.onEvent?.({ type: 'blocked', detail: reason });
|
|
1004
1051
|
await ctx.effects.abort();
|
|
@@ -1743,6 +1790,47 @@ export const payplugPreClaim = (ctx) => {
|
|
|
1743
1790
|
return undefined;
|
|
1744
1791
|
return processorPageAbort(ctx);
|
|
1745
1792
|
};
|
|
1793
|
+
// eGHL's payment page (eghl.ts). Once this build's registry serves eGHL's entry (a capability the API serves only to
|
|
1794
|
+
// Agentcard's own company), every request to eGHL's hosts pauses. The merchant's entry, a GET of /IPG/payment.aspx whose
|
|
1795
|
+
// query names the payment, is no step of this gate's: it defers, and the terminal arbiter pauses it for the claim flow
|
|
1796
|
+
// like any card request, so the cardholder's device pays it. Every step toward paying on the page itself aborts as an
|
|
1797
|
+
// unsupported checkout: every POST to eGHL's hosts, the card page's card post to the entry's own address among them. So
|
|
1798
|
+
// eGHL never sees the card the agent typed. The page's own GETs continue. With eGHL not served, the gate defers, and
|
|
1799
|
+
// every request continues as before. Synchronous until it must abort, like every gate. Exported for the gate registry's
|
|
1800
|
+
// tests.
|
|
1801
|
+
export const eghlPreClaim = (ctx) => {
|
|
1802
|
+
const { url, method } = ctx.request;
|
|
1803
|
+
if (!isEghlPayStep(url, method) || !isArmedEghlUrl(ctx.vault, url))
|
|
1804
|
+
return undefined;
|
|
1805
|
+
return processorPageAbort(ctx);
|
|
1806
|
+
};
|
|
1807
|
+
// Cardlink's VPOS (cardlink-vpos.ts). Once this build's registry serves the entry (a capability the API serves only to
|
|
1808
|
+
// Agentcard's own company), every request to the VPOS's hosts pauses, and this gate judges the POSTs:
|
|
1809
|
+
// - the merchant's entry, an entry body posted to /vpos/shophandlermpi on one of the three live hosts, defers, and the
|
|
1810
|
+
// terminal arbiter pauses it for the claim flow like any card request: the cardholder's device pays it;
|
|
1811
|
+
// - every other POST (the card page's post and its card check, which share the entry's URL and carry the page's own
|
|
1812
|
+
// fields, a body the adapter could not read, and anything on Cardlink's test hosts) aborts as an unsupported
|
|
1813
|
+
// checkout, so the VPOS never sees the card the agent typed.
|
|
1814
|
+
// A GET (the card page's script, its status poll) is not this gate's and continues. With the lane not served, the gate
|
|
1815
|
+
// defers, and every request continues as before. Synchronous until it must abort, like every gate. Exported for the gate
|
|
1816
|
+
// registry's tests.
|
|
1817
|
+
export const cardlinkVposPreClaim = (ctx) => {
|
|
1818
|
+
const { url, method } = ctx.request;
|
|
1819
|
+
if (String(method).toUpperCase() !== 'POST' || !isArmedCardlinkVposUrl(ctx.vault, url))
|
|
1820
|
+
return undefined;
|
|
1821
|
+
// The body is a getter an adapter can fail in (Playwright's postData() throws on a body it cannot decode): a body that
|
|
1822
|
+
// cannot be read is no entry this gate can tell from a card post, so it fails closed, aborted and reported like one.
|
|
1823
|
+
let body;
|
|
1824
|
+
try {
|
|
1825
|
+
body = ctx.request.body;
|
|
1826
|
+
}
|
|
1827
|
+
catch {
|
|
1828
|
+
return processorPageAbort(ctx);
|
|
1829
|
+
}
|
|
1830
|
+
if (isCardlinkVposEntry(url, method, body))
|
|
1831
|
+
return undefined;
|
|
1832
|
+
return processorPageAbort(ctx);
|
|
1833
|
+
};
|
|
1746
1834
|
// NMI's hosted payment page (ekashu.ts). Once this build's registry serves NMI's entry (a
|
|
1747
1835
|
// capability the API serves only to a company Agentcard turned NMI on for), every request to
|
|
1748
1836
|
// NMI's two hosts pauses, and this gate judges the POSTs:
|
|
@@ -2577,9 +2665,9 @@ async function unsupportedContinue(ctx) {
|
|
|
2577
2665
|
* arbiter runs after all of them and is not part of this list. A gate appended
|
|
2578
2666
|
* after Stripe is never offered a step Stripe claimed: Stripe answers, aborts or
|
|
2579
2667
|
* pauses each one itself. */
|
|
2580
|
-
export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, nikeOrderPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, tokenexIframePreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, sbpsPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, redsysPreClaim, quickpayPreClaim, comgatePreClaim, przelewy24PreClaim, transbankPreClaim, eximbayPreClaim, barionPreClaim, payplugPreClaim, paypalHostedFieldsPreClaim,
|
|
2668
|
+
export const PSP_PRE_CLAIM_GATES = [cardEndpointResendPreClaim, cardEndpointPreClaim, nikeOrderPreClaim, mercadoPreClaim, juspayPreClaim, braintreePreClaim, stripePreClaim, merchantHostedPreClaim, verifonePreClaim, fiservPreClaim, gmoPreClaim, cybersourceFlexPreClaim, datatransPreClaim, tokenexIframePreClaim, pelecardPreClaim, aciPreClaim, creditguardPreClaim, primerPreClaim, payrailsPreClaim, convergePreClaim, eprotectPreClaim, sbpsPreClaim, wspayPreClaim, shijiStsPreClaim, ekashuPreClaim, redsysPreClaim, quickpayPreClaim, comgatePreClaim, przelewy24PreClaim, transbankPreClaim, eximbayPreClaim, barionPreClaim, payplugPreClaim, cardlinkVposPreClaim, eghlPreClaim, paypalHostedFieldsPreClaim,
|
|
2581
2669
|
paypalCardFormsPreClaim, cybersourceSecureAcceptancePreClaim, worldlineMyCheckoutPreClaim, paddleVgsPayPreClaim, ebanxPreClaim, payrexxPreClaim, hipayPreClaim, paytrailPreClaim,
|
|
2582
|
-
oceanpaymentPreClaim];
|
|
2670
|
+
oceanpaymentPreClaim, aurusPreClaim, tokenRepeatPreClaim];
|
|
2583
2671
|
/** Run each gate in order; the first to return a decision wins. If none does,
|
|
2584
2672
|
* the terminal arbiter decides between continue, abort (unsupported) and pause.
|
|
2585
2673
|
* Returns synchronously while every gate defers synchronously (an ordinary card
|
|
@@ -211,7 +211,26 @@
|
|
|
211
211
|
"origin": "https://js.braintreegateway.com",
|
|
212
212
|
"path_pattern": "/web/:version/js/*",
|
|
213
213
|
"hostname_pattern": "^js\\.braintreegateway\\.com$",
|
|
214
|
-
"pathname_pattern": "^\\/web\\/(?![^/]*-payments-sdk)[^/]+\\/js\\/",
|
|
214
|
+
"pathname_pattern": "^\\/web\\/(?![^/]*-payments-sdk)[^/]+\\/js\\/(?!(?:client|paypal|paypal-checkout|venmo|apple-pay|google-payment|data-collector|local-payment)(?:\\.min)?\\.js$)",
|
|
215
|
+
"sources": [
|
|
216
|
+
{
|
|
217
|
+
"kind": "official_documentation",
|
|
218
|
+
"reference": "https://developer.paypal.com/braintree/docs/start/hello-client/javascript/v3/",
|
|
219
|
+
"reviewed_on": "2026-09-11"
|
|
220
|
+
}
|
|
221
|
+
]
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"id": "braintree-web-wallet",
|
|
225
|
+
"psp": "braintree",
|
|
226
|
+
"kinds": [
|
|
227
|
+
"script"
|
|
228
|
+
],
|
|
229
|
+
"origin": "https://js.braintreegateway.com",
|
|
230
|
+
"path_pattern": "/web/:version/js/{client,paypal,paypal-checkout,venmo,apple-pay,google-payment,data-collector,local-payment}[.min].js",
|
|
231
|
+
"card_free": "braintree-web's client, wallet and fraud components collect no card; Hosted Fields and Drop-in do.",
|
|
232
|
+
"hostname_pattern": "^js\\.braintreegateway\\.com$",
|
|
233
|
+
"pathname_pattern": "^\\/web\\/(?![^/]*-payments-sdk)[^/]+\\/js\\/(?:client|paypal|paypal-checkout|venmo|apple-pay|google-payment|data-collector|local-payment)(?:\\.min)?\\.js$",
|
|
215
234
|
"sources": [
|
|
216
235
|
{
|
|
217
236
|
"kind": "official_documentation",
|
|
@@ -2566,6 +2585,92 @@
|
|
|
2566
2585
|
"hostname_pattern": "^flex\\.cybersource\\.com$",
|
|
2567
2586
|
"pathname_pattern": "^\\/microform\\/bundle\\/v2\\.[0-9]+\\.[0-9]+\\/iframe\\.html$"
|
|
2568
2587
|
},
|
|
2588
|
+
{
|
|
2589
|
+
"id": "cybersource-unified-checkout-script",
|
|
2590
|
+
"psp": "cybersource_flex",
|
|
2591
|
+
"kinds": [
|
|
2592
|
+
"script"
|
|
2593
|
+
],
|
|
2594
|
+
"origin": "https://up.cybersource.com",
|
|
2595
|
+
"path_pattern": "/uc/v1/assets/:asset/UnifiedCheckout.js",
|
|
2596
|
+
"sources": [
|
|
2597
|
+
{
|
|
2598
|
+
"kind": "saved_observation",
|
|
2599
|
+
"reference": "napaonline-2026-10-31:unified-checkout-v1.8.2-mce-orc-ga-frames",
|
|
2600
|
+
"reviewed_on": "2026-10-31"
|
|
2601
|
+
},
|
|
2602
|
+
{
|
|
2603
|
+
"kind": "official_documentation",
|
|
2604
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/unified-checkout/developer/all/rest/unified-checkout/uc-intro.html",
|
|
2605
|
+
"reviewed_on": "2026-10-31"
|
|
2606
|
+
}
|
|
2607
|
+
],
|
|
2608
|
+
"hostname_pattern": "^up\\.cybersource\\.com$",
|
|
2609
|
+
"pathname_pattern": "^\\/uc\\/v1\\/assets\\/[A-Za-z0-9_-]{16,1024}\\/UnifiedCheckout\\.js$"
|
|
2610
|
+
},
|
|
2611
|
+
{
|
|
2612
|
+
"id": "cybersource-unified-checkout-frame",
|
|
2613
|
+
"psp": "cybersource_flex",
|
|
2614
|
+
"kinds": [
|
|
2615
|
+
"frame",
|
|
2616
|
+
"iframe",
|
|
2617
|
+
"document"
|
|
2618
|
+
],
|
|
2619
|
+
"origin": "https://up.cybersource.com",
|
|
2620
|
+
"path_pattern": "/uc/v1/assets/:asset/{mce,orc,ga}/*.html",
|
|
2621
|
+
"sources": [
|
|
2622
|
+
{
|
|
2623
|
+
"kind": "saved_observation",
|
|
2624
|
+
"reference": "napaonline-2026-10-31:unified-checkout-v1.8.2-mce-orc-ga-frames",
|
|
2625
|
+
"reviewed_on": "2026-10-31"
|
|
2626
|
+
},
|
|
2627
|
+
{
|
|
2628
|
+
"kind": "official_documentation",
|
|
2629
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/unified-checkout/developer/all/rest/unified-checkout/uc-intro.html",
|
|
2630
|
+
"reviewed_on": "2026-10-31"
|
|
2631
|
+
}
|
|
2632
|
+
],
|
|
2633
|
+
"hostname_pattern": "^up\\.cybersource\\.com$",
|
|
2634
|
+
"pathname_pattern": "^\\/uc\\/v1\\/assets\\/[A-Za-z0-9_-]{16,1024}\\/(?:mce\\/mce|orc\\/orc|ga\\/ga)\\.html$"
|
|
2635
|
+
},
|
|
2636
|
+
{
|
|
2637
|
+
"id": "aurus-ecom-iframe",
|
|
2638
|
+
"psp": "aurus",
|
|
2639
|
+
"kinds": [
|
|
2640
|
+
"frame",
|
|
2641
|
+
"iframe",
|
|
2642
|
+
"document"
|
|
2643
|
+
],
|
|
2644
|
+
"origin": "https://pecst01.aurusepay.com",
|
|
2645
|
+
"path_pattern": "/storeservices/ecom/getiframe",
|
|
2646
|
+
"sources": [
|
|
2647
|
+
{
|
|
2648
|
+
"kind": "saved_observation",
|
|
2649
|
+
"reference": "oreillyauto-2026-10-31:aurus-ecom-getiframe-oneTimeToken",
|
|
2650
|
+
"reviewed_on": "2026-10-31"
|
|
2651
|
+
}
|
|
2652
|
+
],
|
|
2653
|
+
"hostname_pattern": "^pecst[0-9]{2}\\.aurusepay\\.com$",
|
|
2654
|
+
"pathname_pattern": "^\\/storeservices\\/ecom\\/getiframe$"
|
|
2655
|
+
},
|
|
2656
|
+
{
|
|
2657
|
+
"id": "aurus-ecom-init-script",
|
|
2658
|
+
"psp": "aurus",
|
|
2659
|
+
"kinds": [
|
|
2660
|
+
"script"
|
|
2661
|
+
],
|
|
2662
|
+
"origin": "https://pecst01.aurusepay.com",
|
|
2663
|
+
"path_pattern": "/storeservices/ecom/ecominitjs",
|
|
2664
|
+
"sources": [
|
|
2665
|
+
{
|
|
2666
|
+
"kind": "saved_observation",
|
|
2667
|
+
"reference": "oreillyauto-2026-10-31:aurus-ecom-getiframe-oneTimeToken",
|
|
2668
|
+
"reviewed_on": "2026-10-31"
|
|
2669
|
+
}
|
|
2670
|
+
],
|
|
2671
|
+
"hostname_pattern": "^pecst[0-9]{2}\\.aurusepay\\.com$",
|
|
2672
|
+
"pathname_pattern": "^\\/storeservices\\/ecom\\/ecominitjs$"
|
|
2673
|
+
},
|
|
2569
2674
|
{
|
|
2570
2675
|
"id": "juspay-hypercheckout-payment-page",
|
|
2571
2676
|
"psp": "juspay",
|
|
@@ -3417,6 +3522,31 @@
|
|
|
3417
3522
|
"hostname_pattern": "^payment\\.pay1\\.checkout\\.worldline-solutions\\.com$",
|
|
3418
3523
|
"pathname_pattern": "^\\/checkout$"
|
|
3419
3524
|
},
|
|
3525
|
+
{
|
|
3526
|
+
"id": "bigcommerce-hosted-card-field",
|
|
3527
|
+
"psp": "bigcommerce",
|
|
3528
|
+
"kinds": [
|
|
3529
|
+
"document",
|
|
3530
|
+
"frame",
|
|
3531
|
+
"iframe"
|
|
3532
|
+
],
|
|
3533
|
+
"origin": "https://payments.bigcommerce.com",
|
|
3534
|
+
"path_pattern": "/pay/hosted_forms/:form/field",
|
|
3535
|
+
"sources": [
|
|
3536
|
+
{
|
|
3537
|
+
"kind": "saved_observation",
|
|
3538
|
+
"reference": "drsturm-2026-11-06:bigcommerce-hosted-form-field-1.997.1",
|
|
3539
|
+
"reviewed_on": "2026-11-06"
|
|
3540
|
+
},
|
|
3541
|
+
{
|
|
3542
|
+
"kind": "primary_source",
|
|
3543
|
+
"reference": "https://checkout-sdk.bigcommerce.com/v1/loader-v1.997.1.js",
|
|
3544
|
+
"reviewed_on": "2026-11-06"
|
|
3545
|
+
}
|
|
3546
|
+
],
|
|
3547
|
+
"hostname_pattern": "^payments\\.bigcommerce\\.com$",
|
|
3548
|
+
"pathname_pattern": "^\\/pay\\/hosted_forms\\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\\/field$"
|
|
3549
|
+
},
|
|
3420
3550
|
{
|
|
3421
3551
|
"id": "gmo-mp-token-script-test",
|
|
3422
3552
|
"psp": "gmo_pg",
|
|
@@ -3565,6 +3695,54 @@
|
|
|
3565
3695
|
"hostname_pattern": "^testflex\\.cybersource\\.com$",
|
|
3566
3696
|
"pathname_pattern": "^\\/microform\\/bundle\\/v2\\.[0-9]+\\.[0-9]+\\/iframe\\.html$"
|
|
3567
3697
|
},
|
|
3698
|
+
{
|
|
3699
|
+
"id": "cybersource-unified-checkout-script-test",
|
|
3700
|
+
"psp": "cybersource_flex",
|
|
3701
|
+
"kinds": [
|
|
3702
|
+
"script"
|
|
3703
|
+
],
|
|
3704
|
+
"origin": "https://testup.cybersource.com",
|
|
3705
|
+
"path_pattern": "/uc/v1/assets/:asset/UnifiedCheckout.js",
|
|
3706
|
+
"sources": [
|
|
3707
|
+
{
|
|
3708
|
+
"kind": "saved_observation",
|
|
3709
|
+
"reference": "napaonline-2026-10-31:unified-checkout-v1.8.2-mce-orc-ga-frames",
|
|
3710
|
+
"reviewed_on": "2026-10-31"
|
|
3711
|
+
},
|
|
3712
|
+
{
|
|
3713
|
+
"kind": "official_documentation",
|
|
3714
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/unified-checkout/developer/all/rest/unified-checkout/uc-intro.html",
|
|
3715
|
+
"reviewed_on": "2026-10-31"
|
|
3716
|
+
}
|
|
3717
|
+
],
|
|
3718
|
+
"hostname_pattern": "^testup\\.cybersource\\.com$",
|
|
3719
|
+
"pathname_pattern": "^\\/uc\\/v1\\/assets\\/[A-Za-z0-9_-]{16,1024}\\/UnifiedCheckout\\.js$"
|
|
3720
|
+
},
|
|
3721
|
+
{
|
|
3722
|
+
"id": "cybersource-unified-checkout-frame-test",
|
|
3723
|
+
"psp": "cybersource_flex",
|
|
3724
|
+
"kinds": [
|
|
3725
|
+
"frame",
|
|
3726
|
+
"iframe",
|
|
3727
|
+
"document"
|
|
3728
|
+
],
|
|
3729
|
+
"origin": "https://testup.cybersource.com",
|
|
3730
|
+
"path_pattern": "/uc/v1/assets/:asset/{mce,orc,ga}/*.html",
|
|
3731
|
+
"sources": [
|
|
3732
|
+
{
|
|
3733
|
+
"kind": "saved_observation",
|
|
3734
|
+
"reference": "napaonline-2026-10-31:unified-checkout-v1.8.2-mce-orc-ga-frames",
|
|
3735
|
+
"reviewed_on": "2026-10-31"
|
|
3736
|
+
},
|
|
3737
|
+
{
|
|
3738
|
+
"kind": "official_documentation",
|
|
3739
|
+
"reference": "https://developer.cybersource.com/docs/cybs/en-us/unified-checkout/developer/all/rest/unified-checkout/uc-intro.html",
|
|
3740
|
+
"reviewed_on": "2026-10-31"
|
|
3741
|
+
}
|
|
3742
|
+
],
|
|
3743
|
+
"hostname_pattern": "^testup\\.cybersource\\.com$",
|
|
3744
|
+
"pathname_pattern": "^\\/uc\\/v1\\/assets\\/[A-Za-z0-9_-]{16,1024}\\/(?:mce\\/mce|orc\\/orc|ga\\/ga)\\.html$"
|
|
3745
|
+
},
|
|
3568
3746
|
{
|
|
3569
3747
|
"id": "cybersource-secure-acceptance-silent-order-post-test",
|
|
3570
3748
|
"psp": "cybersource_secure_acceptance",
|
|
@@ -385,6 +385,7 @@
|
|
|
385
385
|
"mollie-components-v1-script",
|
|
386
386
|
"mollie-hosted-credit-card",
|
|
387
387
|
"braintree-web-sdk",
|
|
388
|
+
"braintree-web-wallet",
|
|
388
389
|
"braintree-hosted-fields",
|
|
389
390
|
"paysafe-js",
|
|
390
391
|
"worldpay-checkout-sdk",
|
|
@@ -483,6 +484,10 @@
|
|
|
483
484
|
"gmo-link-hosted-page",
|
|
484
485
|
"cybersource-flex-microform-script",
|
|
485
486
|
"cybersource-flex-microform-field",
|
|
487
|
+
"cybersource-unified-checkout-script",
|
|
488
|
+
"cybersource-unified-checkout-frame",
|
|
489
|
+
"aurus-ecom-iframe",
|
|
490
|
+
"aurus-ecom-init-script",
|
|
486
491
|
"juspay-hypercheckout-payment-page",
|
|
487
492
|
"pelecard-payment-page",
|
|
488
493
|
"pelecard-payment-script",
|
|
@@ -520,12 +525,15 @@
|
|
|
520
525
|
"amadeus-data-collection-page",
|
|
521
526
|
"worldline-mycheckout-page",
|
|
522
527
|
"worldline-mycheckout-card-form",
|
|
528
|
+
"bigcommerce-hosted-card-field",
|
|
523
529
|
"gmo-mp-token-script-test",
|
|
524
530
|
"gmo-token-v1-script-test",
|
|
525
531
|
"gmo-elements-card-frame-test",
|
|
526
532
|
"gmo-link-hosted-page-test",
|
|
527
533
|
"cybersource-flex-microform-script-test",
|
|
528
534
|
"cybersource-flex-microform-field-test",
|
|
535
|
+
"cybersource-unified-checkout-script-test",
|
|
536
|
+
"cybersource-unified-checkout-frame-test",
|
|
529
537
|
"cybersource-secure-acceptance-silent-order-post-test",
|
|
530
538
|
"cybersource-secure-acceptance-silent-order-post-in",
|
|
531
539
|
"worldpay-eprotect-js-api-prelive",
|
|
@@ -1023,6 +1031,7 @@
|
|
|
1023
1031
|
"mollie-components-v1-script",
|
|
1024
1032
|
"mollie-hosted-credit-card",
|
|
1025
1033
|
"braintree-web-sdk",
|
|
1034
|
+
"braintree-web-wallet",
|
|
1026
1035
|
"braintree-hosted-fields",
|
|
1027
1036
|
"paysafe-js",
|
|
1028
1037
|
"worldpay-checkout-sdk",
|
|
@@ -1121,6 +1130,10 @@
|
|
|
1121
1130
|
"gmo-link-hosted-page",
|
|
1122
1131
|
"cybersource-flex-microform-script",
|
|
1123
1132
|
"cybersource-flex-microform-field",
|
|
1133
|
+
"cybersource-unified-checkout-script",
|
|
1134
|
+
"cybersource-unified-checkout-frame",
|
|
1135
|
+
"aurus-ecom-iframe",
|
|
1136
|
+
"aurus-ecom-init-script",
|
|
1124
1137
|
"juspay-hypercheckout-payment-page",
|
|
1125
1138
|
"pelecard-payment-page",
|
|
1126
1139
|
"pelecard-payment-script",
|
|
@@ -1158,12 +1171,15 @@
|
|
|
1158
1171
|
"amadeus-data-collection-page",
|
|
1159
1172
|
"worldline-mycheckout-page",
|
|
1160
1173
|
"worldline-mycheckout-card-form",
|
|
1174
|
+
"bigcommerce-hosted-card-field",
|
|
1161
1175
|
"gmo-mp-token-script-test",
|
|
1162
1176
|
"gmo-token-v1-script-test",
|
|
1163
1177
|
"gmo-elements-card-frame-test",
|
|
1164
1178
|
"gmo-link-hosted-page-test",
|
|
1165
1179
|
"cybersource-flex-microform-script-test",
|
|
1166
1180
|
"cybersource-flex-microform-field-test",
|
|
1181
|
+
"cybersource-unified-checkout-script-test",
|
|
1182
|
+
"cybersource-unified-checkout-frame-test",
|
|
1167
1183
|
"cybersource-secure-acceptance-silent-order-post-test",
|
|
1168
1184
|
"cybersource-secure-acceptance-silent-order-post-in",
|
|
1169
1185
|
"worldpay-eprotect-js-api-prelive",
|
|
@@ -148,6 +148,11 @@ export declare function getCheckoutPreflightCatalog(): {
|
|
|
148
148
|
path_pattern: string;
|
|
149
149
|
flow?: string;
|
|
150
150
|
variant_unverified?: string;
|
|
151
|
+
/**
|
|
152
|
+
* Why the asset never takes a card: a wallet button, a fraud collector, or the client those load. When no card frame
|
|
153
|
+
* is active and scripts alone name several processors, one whose every asset says this is not the card form's.
|
|
154
|
+
*/
|
|
155
|
+
card_free?: string;
|
|
151
156
|
}[];
|
|
152
157
|
processors: readonly [{
|
|
153
158
|
readonly psp: "spreedly";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated from @agent-cards/payment-core. Do not edit.
|
|
2
|
-
// source-sha256:
|
|
2
|
+
// source-sha256: 84246b0b75dadb1798e0c9bd4ab7a8ab8567df86ea490fb20c9f0f890309e7df
|
|
3
3
|
import { CHECKOUT_PREFLIGHT_CAPABILITIES } from './preflight-capabilities.generated.js';
|
|
4
4
|
/** Advisory page discovery. These rules never admit a payment destination. */
|
|
5
5
|
export const CHECKOUT_PREFLIGHT_VERSION = '1';
|
|
@@ -23,7 +23,13 @@ const RULES = [
|
|
|
23
23
|
// A braintree-web build whose version carries -payments-sdk (3.32.0-payments-sdk-dev) is PayPal's
|
|
24
24
|
// (paypal-hosted-fields-payments-sdk below): its card goes to PayPal, never to Braintree, so neither
|
|
25
25
|
// Braintree rule takes one.
|
|
26
|
-
{ id: 'braintree-web-sdk', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)[^/]+\/js
|
|
26
|
+
{ id: 'braintree-web-sdk', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)[^/]+\/js\/(?!(?:client|paypal|paypal-checkout|venmo|apple-pay|google-payment|data-collector|local-payment)(?:\.min)?\.js$)/, origin: 'https://js.braintreegateway.com', path_pattern: '/web/:version/js/*' },
|
|
27
|
+
// braintree-web's client and its wallet and fraud components take no card: a page that loads only these runs a
|
|
28
|
+
// PayPal, Venmo, Apple Pay or Google Pay button through Braintree, and its card boxes belong to another processor.
|
|
29
|
+
// AutoZone's checkout loads client, paypal-checkout and data-collector 3.125.0 beside Worldpay eProtect's JS API, whose
|
|
30
|
+
// script reads the card from the page's own fields (2026-10-31).
|
|
31
|
+
{ id: 'braintree-web-wallet', psp: 'braintree', kinds: ['script'], hostname: /^js\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)[^/]+\/js\/(?:client|paypal|paypal-checkout|venmo|apple-pay|google-payment|data-collector|local-payment)(?:\.min)?\.js$/, origin: 'https://js.braintreegateway.com', path_pattern: '/web/:version/js/{client,paypal,paypal-checkout,venmo,apple-pay,google-payment,data-collector,local-payment}[.min].js',
|
|
32
|
+
card_free: 'braintree-web\'s client, wallet and fraud components collect no card; Hosted Fields and Drop-in do.' },
|
|
27
33
|
{ id: 'braintree-hosted-fields', psp: 'braintree', kinds: ['frame', 'iframe'], hostname: /^assets\.braintreegateway\.com$/, pathname: /^\/web\/(?![^/]*-payments-sdk)/, origin: 'https://assets.braintreegateway.com', path_pattern: '/web/*' },
|
|
28
34
|
{ 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}/*' },
|
|
29
35
|
{ 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/*' },
|
|
@@ -197,6 +203,27 @@ const cybersourceSources = [
|
|
|
197
203
|
RULES.push({ id: 'cybersource-flex-microform-script', psp: 'cybersource_flex', kinds: ['script'], hostname: /^flex\.cybersource\.com$/, pathname: /^\/microform\/bundle\/v2(?:\.[0-9]+\.[0-9]+)?\/flex-microform\.min\.js$/, origin: 'https://flex.cybersource.com', path_pattern: '/microform/bundle/v2{,.:minor.:patch}/flex-microform.min.js',
|
|
198
204
|
sources: cybersourceSources }, { id: 'cybersource-flex-microform-field', psp: 'cybersource_flex', kinds: ['frame', 'iframe', 'document'], hostname: /^flex\.cybersource\.com$/, pathname: /^\/microform\/bundle\/v2\.[0-9]+\.[0-9]+\/iframe\.html$/, origin: 'https://flex.cybersource.com', path_pattern: '/microform/bundle/v:version/iframe.html',
|
|
199
205
|
sources: cybersourceSources });
|
|
206
|
+
// CyberSource Unified Checkout v1 sends the card to the same Flex v2 tokens URL, sealed by its orchestrator frame
|
|
207
|
+
// under the capture context (cybersource-unified-checkout.js), so its script and frames name cybersource_flex too.
|
|
208
|
+
// Reviewed 2026-10-31 from napaonline.com's checkout (UnifiedCheckout.js v1.8.2), with no card sent.
|
|
209
|
+
const unifiedCheckoutSources = [
|
|
210
|
+
{ kind: 'saved_observation', reference: 'napaonline-2026-10-31:unified-checkout-v1.8.2-mce-orc-ga-frames', reviewed_on: '2026-10-31' },
|
|
211
|
+
{ kind: 'official_documentation', reference: 'https://developer.cybersource.com/docs/cybs/en-us/unified-checkout/developer/all/rest/unified-checkout/uc-intro.html', reviewed_on: '2026-10-31' },
|
|
212
|
+
];
|
|
213
|
+
RULES.push({ id: 'cybersource-unified-checkout-script', psp: 'cybersource_flex', kinds: ['script'], hostname: /^up\.cybersource\.com$/, pathname: /^\/uc\/v1\/assets\/[A-Za-z0-9_-]{16,1024}\/UnifiedCheckout\.js$/, origin: 'https://up.cybersource.com', path_pattern: '/uc/v1/assets/:asset/UnifiedCheckout.js',
|
|
214
|
+
sources: unifiedCheckoutSources }, { id: 'cybersource-unified-checkout-frame', psp: 'cybersource_flex', kinds: ['frame', 'iframe', 'document'], hostname: /^up\.cybersource\.com$/, pathname: /^\/uc\/v1\/assets\/[A-Za-z0-9_-]{16,1024}\/(?:mce\/mce|orc\/orc|ga\/ga)\.html$/, origin: 'https://up.cybersource.com', path_pattern: '/uc/v1/assets/:asset/{mce,orc,ga}/*.html',
|
|
215
|
+
sources: unifiedCheckoutSources });
|
|
216
|
+
// Aurus Pay's ecom card frame (processors/aurus.js): the store frames /storeservices/ecom/getiframe on
|
|
217
|
+
// pecst<NN>.aurusepay.com, the card fields sit inside it, and its own ecominitjs sends the sealed card to
|
|
218
|
+
// /storeservices/ecom/oneTimeToken on the same host. Reviewed 2026-10-31 at oreillyauto.com's payment step, with every
|
|
219
|
+
// aurusepay.com request but a GET aborted and only a public test card typed. Aurus is served dark, so no catalog
|
|
220
|
+
// processor entry backs the candidate and the result stays unknown unless the caller is served Aurus.
|
|
221
|
+
const aurusSources = [
|
|
222
|
+
{ kind: 'saved_observation', reference: 'oreillyauto-2026-10-31:aurus-ecom-getiframe-oneTimeToken', reviewed_on: '2026-10-31' },
|
|
223
|
+
];
|
|
224
|
+
RULES.push({ id: 'aurus-ecom-iframe', psp: 'aurus', kinds: ['frame', 'iframe', 'document'], hostname: /^pecst[0-9]{2}\.aurusepay\.com$/, pathname: /^\/storeservices\/ecom\/getiframe$/, origin: 'https://pecst01.aurusepay.com', path_pattern: '/storeservices/ecom/getiframe',
|
|
225
|
+
sources: aurusSources }, { id: 'aurus-ecom-init-script', psp: 'aurus', kinds: ['script'], hostname: /^pecst[0-9]{2}\.aurusepay\.com$/, pathname: /^\/storeservices\/ecom\/ecominitjs$/, origin: 'https://pecst01.aurusepay.com', path_pattern: '/storeservices/ecom/ecominitjs',
|
|
226
|
+
sources: aurusSources });
|
|
200
227
|
// Juspay HyperCheckout: Juspay's own hosted payment page (the Session API's payment_links.web),
|
|
201
228
|
// which a merchant embeds in an iframe (IndiGo's payment step). The card request that page makes
|
|
202
229
|
// has not been observed, so the Vault does not take it over, and the rule tells the agent so
|
|
@@ -344,6 +371,15 @@ RULES.push({ id: 'worldline-mycheckout-page', psp: 'worldline_mycheckout', kinds
|
|
|
344
371
|
pathname: /^\/checkout\/(?:payment-product-details\/)?[1-9][0-9]{0,9}-[0-9a-f]{32}:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}:[0-9a-f]{32}$/,
|
|
345
372
|
origin: 'https://payment.pay1.checkout.worldline-solutions.com', path_pattern: '/checkout/{payment-product-details/}:hosted_checkout', sources: worldlineSources }, { id: 'worldline-mycheckout-card-form', psp: 'worldline_mycheckout', kinds: ['form'], hostname: /^payment\.pay1\.checkout\.worldline-solutions\.com$/, pathname: /^\/checkout$/,
|
|
346
373
|
origin: 'https://payment.pay1.checkout.worldline-solutions.com', path_pattern: '/checkout', sources: worldlineSources });
|
|
374
|
+
// BigCommerce's own card form, read 2026-11-06 at drsturm.com: the store's /checkout/payment/hosted-field iframes
|
|
375
|
+
// redirect to one field document per card input on payments.bigcommerce.com, which loads checkout-sdk's hosted-form
|
|
376
|
+
// module and sends the card to BigCommerce's payments API (processors/bigcommerce.js), whichever gateway charges it.
|
|
377
|
+
// BigCommerce ships dark, so the field names bigcommerce and the result stays unknown, as GMO's does.
|
|
378
|
+
RULES.push({ id: 'bigcommerce-hosted-card-field', psp: 'bigcommerce', kinds: ['document', 'frame', 'iframe'], hostname: /^payments\.bigcommerce\.com$/,
|
|
379
|
+
pathname: /^\/pay\/hosted_forms\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\/field$/, origin: 'https://payments.bigcommerce.com',
|
|
380
|
+
path_pattern: '/pay/hosted_forms/:form/field',
|
|
381
|
+
sources: [{ kind: 'saved_observation', reference: 'drsturm-2026-11-06:bigcommerce-hosted-form-field-1.997.1', reviewed_on: '2026-11-06' },
|
|
382
|
+
{ kind: 'primary_source', reference: 'https://checkout-sdk.bigcommerce.com/v1/loader-v1.997.1.js', reviewed_on: '2026-11-06' }] });
|
|
347
383
|
// A processor that ships dark (its definition declares a capability) is left out of the bundled
|
|
348
384
|
// catalog. Datatrans's and the TokenEx iframe's discovery rules wait with it: they join RULES only once KNOWN_PSPS, the
|
|
349
385
|
// bundled catalog's processors, lists its psp. Until then its assets are not evidence, so a page
|
|
@@ -382,6 +418,8 @@ for (const [baseId, suffix, hostname, origin] of [
|
|
|
382
418
|
['gmo-link-hosted-page', 'test', /^pt01\.mul-pay\.jp$/, 'https://pt01.mul-pay.jp'],
|
|
383
419
|
['cybersource-flex-microform-script', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
|
|
384
420
|
['cybersource-flex-microform-field', 'test', /^testflex\.cybersource\.com$/, 'https://testflex.cybersource.com'],
|
|
421
|
+
['cybersource-unified-checkout-script', 'test', /^testup\.cybersource\.com$/, 'https://testup.cybersource.com'],
|
|
422
|
+
['cybersource-unified-checkout-frame', 'test', /^testup\.cybersource\.com$/, 'https://testup.cybersource.com'],
|
|
385
423
|
// Secure Acceptance's test host and its production host in India serve the same /silent/ paths.
|
|
386
424
|
['cybersource-secure-acceptance-silent-order-post', 'test', /^testsecureacceptance\.cybersource\.com$/, 'https://testsecureacceptance.cybersource.com'],
|
|
387
425
|
['cybersource-secure-acceptance-silent-order-post', 'in', /^secureacceptance\.in\.cybersource\.com$/, 'https://secureacceptance.in.cybersource.com'],
|
|
@@ -788,19 +826,27 @@ export function assessCheckoutSupport(input, options = {}) {
|
|
|
788
826
|
result.candidates = [...candidates.values()].sort((a, b) => a.psp.localeCompare(b.psp));
|
|
789
827
|
if (missingCorroboration && activeFlows.size === 0)
|
|
790
828
|
result.reason_codes.push('flow_corroboration_missing');
|
|
829
|
+
// Scripts alone naming several processors: one whose every asset is card-free (a wallet button, a fraud collector)
|
|
830
|
+
// is not the card form's. When exactly one other is left, the page's card form is that one's.
|
|
831
|
+
const cardCapable = activePsps.size === 0 && candidates.size > 1
|
|
832
|
+
? [...candidates.values()].filter(candidate => candidate.evidence.some(evidence => !RULE_BY_ID.get(evidence.rule_id).card_free))
|
|
833
|
+
: [];
|
|
834
|
+
const cardFormPsp = cardCapable.length === 1 ? cardCapable[0].psp : null;
|
|
791
835
|
if (activePsps.size === 1)
|
|
792
836
|
result.psp = [...activePsps][0];
|
|
793
837
|
// Script-only evidence names the processor only when the scan saw everything. On a partial scan
|
|
794
838
|
// the sole candidate stays listed as undetermined instead of becoming the selected route.
|
|
795
839
|
else if (activePsps.size === 0 && candidates.size === 1 && result.observation.complete)
|
|
796
840
|
result.psp = [...candidates.keys()][0];
|
|
841
|
+
else if (cardFormPsp !== null && result.observation.complete)
|
|
842
|
+
result.psp = cardFormPsp;
|
|
797
843
|
if (!result.observation.complete)
|
|
798
844
|
result.reason_codes.push('observation_incomplete');
|
|
799
845
|
if (!input.signals.length) {
|
|
800
846
|
result.reason_codes.push('psp_not_detected');
|
|
801
847
|
result.missing_evidence.push('payment_page_signal');
|
|
802
848
|
}
|
|
803
|
-
if (activePsps.size > 1 || activeFlows.size > 1 || activeFlowFrames.size > 1 || activePsps.size === 0 && candidates.size > 1)
|
|
849
|
+
if (activePsps.size > 1 || activeFlows.size > 1 || activeFlowFrames.size > 1 || activePsps.size === 0 && candidates.size > 1 && cardFormPsp === null)
|
|
804
850
|
result.reason_codes.push('ambiguous_checkout');
|
|
805
851
|
if (activeFlows.size !== 1 || result.psp === null) {
|
|
806
852
|
result.reason_codes.push('checkout_flow_unidentified');
|
package/dist/preparation.d.ts
CHANGED
|
@@ -25,6 +25,13 @@ export declare class PreparationGate {
|
|
|
25
25
|
* own method is what binds: a POST for most processors, the registration GET for Worldpay eProtect.
|
|
26
26
|
*/
|
|
27
27
|
claim(requestUrl: string, requestMethod: string, requestBody?: string | null, requestHeaders?: Record<string, string>): PreparedCheckout | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Whether a same-document navigation of the prepared page to `url` leaves the document this
|
|
30
|
+
* preparation read: the same origin, path and query, whatever the fragment. A single-page checkout
|
|
31
|
+
* that calls history.replaceState with its own URL after each server action (Al's on VTEX) keeps
|
|
32
|
+
* its preparation; a history update to another path or query does not.
|
|
33
|
+
*/
|
|
34
|
+
keepsDocument(url: unknown): boolean;
|
|
28
35
|
assertDocument(): Promise<void>;
|
|
29
36
|
private readDocument;
|
|
30
37
|
isEngaged(): boolean;
|
package/dist/preparation.js
CHANGED
|
@@ -3,6 +3,17 @@ import { amountRefusal, validAmountInput } from './client.js';
|
|
|
3
3
|
import { matchesPreparation, validPreparationEnvironment, preparationEndpoint, preparationBindsByShape, preparationRequestMethod, preparedRequestRefusal } from './prepared-processor.js';
|
|
4
4
|
import { SECURE_ACCEPTANCE_PSP, secureAcceptanceCheckoutProfile } from './cybersource-secure-acceptance.js';
|
|
5
5
|
import { creditguardPageProcessUrl, creditguardProcessTxId } from './creditguard.generated.js';
|
|
6
|
+
/** A page URL without its fragment, or undefined when it does not parse. */
|
|
7
|
+
function documentWithoutFragment(url) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = new URL(url);
|
|
10
|
+
parsed.hash = '';
|
|
11
|
+
return parsed.href;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
6
17
|
/** A local, one-use rendezvous. It never starts or retries a merchant request. */
|
|
7
18
|
export class PreparationGate {
|
|
8
19
|
opts;
|
|
@@ -195,6 +206,16 @@ export class PreparationGate {
|
|
|
195
206
|
clearTimeout(this.expiryTimer);
|
|
196
207
|
return prepared;
|
|
197
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Whether a same-document navigation of the prepared page to `url` leaves the document this
|
|
211
|
+
* preparation read: the same origin, path and query, whatever the fragment. A single-page checkout
|
|
212
|
+
* that calls history.replaceState with its own URL after each server action (Al's on VTEX) keeps
|
|
213
|
+
* its preparation; a history update to another path or query does not.
|
|
214
|
+
*/
|
|
215
|
+
keepsDocument(url) {
|
|
216
|
+
const kept = typeof url === 'string' ? documentWithoutFragment(url) : undefined;
|
|
217
|
+
return kept !== undefined && this.documentUrl !== '' && kept === documentWithoutFragment(this.documentUrl);
|
|
218
|
+
}
|
|
198
219
|
async assertDocument() {
|
|
199
220
|
if (this.documentUrl && await this.readDocument() !== this.documentUrl)
|
|
200
221
|
throw new CheckoutPreparationError(this.prepared?.id ?? null, 'merchant_document_changed');
|