@agent-cards/sdk 0.23.20 → 0.23.22
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 +55 -2
- package/PREFLIGHT.md +6 -0
- package/README.md +169 -5
- package/dist/amadeus-checkout.generated.d.ts +6 -1
- package/dist/amadeus-checkout.generated.js +26 -2
- package/dist/card-fields.generated.js +2341 -127
- package/dist/cdp.js +199 -28
- package/dist/client.d.ts +177 -9
- package/dist/client.js +387 -37
- package/dist/comgate.d.ts +50 -0
- package/dist/comgate.js +80 -0
- package/dist/creditguard.generated.js +4 -2
- package/dist/cse-body.js +11 -0
- package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
- package/dist/ebanx.d.ts +1 -1
- package/dist/ebanx.js +1 -1
- package/dist/eprotect.generated.d.ts +1 -1
- package/dist/eximbay.d.ts +46 -0
- package/dist/eximbay.js +69 -0
- package/dist/gmo.generated.js +7 -3
- package/dist/hipay.generated.d.ts +5 -5
- package/dist/hipay.generated.js +5 -2
- package/dist/hold.d.ts +1 -1
- package/dist/hold.js +10 -4
- package/dist/hosted-entry.d.ts +4 -0
- package/dist/hosted-entry.js +32 -0
- package/dist/index.d.ts +1 -0
- package/dist/lifecycle.d.ts +9 -4
- package/dist/lifecycle.js +16 -4
- package/dist/paddle-tokenex.generated.js +7 -3
- package/dist/paygent.generated.d.ts +60 -0
- package/dist/paygent.generated.js +425 -0
- package/dist/payrails.generated.js +7 -3
- package/dist/payrexx.d.ts +91 -0
- package/dist/payrexx.generated.d.ts +91 -0
- package/dist/payrexx.generated.js +536 -0
- package/dist/payrexx.js +141 -0
- package/dist/pre-claim.d.ts +30 -1
- package/dist/pre-claim.js +257 -39
- package/dist/preflight-catalog.json +58 -0
- package/dist/preflight-schemas.json +4 -0
- package/dist/preflight.generated.js +26 -2
- package/dist/prepared-processor.d.ts +7 -2
- package/dist/prepared-processor.js +16 -2
- package/dist/primer.generated.js +2469 -255
- package/dist/przelewy24.d.ts +47 -0
- package/dist/przelewy24.js +88 -0
- package/dist/registry.d.ts +6 -5
- package/dist/registry.js +27 -7
- package/dist/shiji-sts.generated.js +7 -3
- package/dist/stripe-checkout.generated.js +896 -19
- package/dist/toast-seal.generated.d.ts +37 -0
- package/dist/toast-seal.generated.js +625 -0
- package/dist/toast.generated.d.ts +41 -0
- package/dist/toast.generated.js +113 -0
- package/dist/tokenex-iframe.d.ts +93 -0
- package/dist/tokenex-iframe.generated.d.ts +81 -0
- package/dist/tokenex-iframe.generated.js +403 -0
- package/dist/tokenex-iframe.js +195 -0
- package/dist/transbank.d.ts +45 -0
- package/dist/transbank.js +73 -0
- package/dist/twoc2p.generated.js +7 -3
- package/examples/preflight/kernel-native/inventory.json +1 -1
- package/package.json +3 -3
package/dist/cdp.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BUILTIN_REGISTRY, cardUrlPatterns } from './registry.js';
|
|
2
|
-
import { AdyenTestPlatformRefusedError, AmountRequiredError, ApprovalDeclinedError, ApprovalTimeoutError, CardEncryptedError, CheckoutApiError, CheckoutCancelledError, CheckoutPreparationError, MerchantTotalError, PaymentOutcomeUnknownError, PreparationRequiredError, ProcessorRefusedError, UnsupportedCapabilityError, UnsupportedModeError, } from './client.js';
|
|
2
|
+
import { AdyenTestPlatformRefusedError, AmountRequiredError, ApprovalDeclinedError, ApprovalTimeoutError, CardEncryptedError, CheckoutApiError, CheckoutCancelledError, CheckoutPreparationError, MerchantTotalError, PaymentOutcomeUnknownError, PreparationRequiredError, ProcessorRefusedError, UnsupportedCapabilityError, UnsupportedModeError, redactUrl, } from './client.js';
|
|
3
3
|
import { PreparationGate } from './preparation.js';
|
|
4
4
|
import { preparedApprovalAnswersRetry } from './prepared-processor.js';
|
|
5
5
|
import { StripeCheckoutGate } from './stripe-checkout.js';
|
|
@@ -11,16 +11,23 @@ import { EprotectGate, classifyEprotectRequest, eprotectScriptAnswer } from './e
|
|
|
11
11
|
import { dispatchPreClaim, eventUrl, isArmedCreditguardCall, routesHipayVault } from './pre-claim.js';
|
|
12
12
|
import { GMO_TOKEN_V1_PATTERNS, gmoRequestShop, isGmoTokenV1Request, recognizesGmo } from './gmo.js';
|
|
13
13
|
import { isPaddleVgsCardsUrl } from './paddle-vgs.generated.js';
|
|
14
|
+
import { isToastPaymentMethodsUrl, readToastBody } from './toast.generated.js';
|
|
14
15
|
import { PADDLE_VGS_PAY_PATTERNS, PaddleVgsPayGate, isPaddleCheckoutService, recognizesPaddleVgs } from './paddle-vgs-pay.js';
|
|
15
16
|
import { DatatransRepeats } from './datatrans.js';
|
|
17
|
+
import { TokenexIframeRepeats, isArmedTokenexIframeUrl } from './tokenex-iframe.js';
|
|
16
18
|
import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
|
|
17
19
|
import { paddleTokenexTokenContinuation } from './paddle-tokenex.js';
|
|
18
20
|
import { twoC2pContinuation, twoC2pRetire } from './twoc2p.js';
|
|
19
21
|
import { isArmedEkashuUrl } from './ekashu.js';
|
|
22
|
+
import { isArmedTransbankUrl } from './transbank.js';
|
|
20
23
|
import { isArmedRedsysUrl } from './redsys.js';
|
|
24
|
+
import { isArmedPrzelewy24Url } from './przelewy24.js';
|
|
21
25
|
import { isArmedQuickpayUrl } from './quickpay.js';
|
|
26
|
+
import { COMGATE_CARD_PATTERNS, isArmedComgateUrl, servesComgate } from './comgate.js';
|
|
27
|
+
import { isArmedEximbayUrl } from './eximbay.js';
|
|
22
28
|
import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
|
|
23
29
|
import { EBANX_RESEND_PATTERNS, EbanxFollowUps, isEbanxResendUrl } from './ebanx.js';
|
|
30
|
+
import { PayrexxFollowUps } from './payrexx.js';
|
|
24
31
|
import { PAYPAL_CARD_FORM_PATTERNS, isPaypalCardFormUrl } from './paypal-card-forms.js';
|
|
25
32
|
import { CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, SecureAcceptanceFollowUpWatch, isCybersourceSecureAcceptanceUrl, secureAcceptanceFollowUp, secureAcceptanceFrameAnswer, secureAcceptanceReplyLast4, } from './cybersource-secure-acceptance.js';
|
|
26
33
|
import { CheckoutAttachmentError, attachmentDeadline, attachmentFailure, withinAttachmentDeadline } from './attachment.js';
|
|
@@ -1284,9 +1291,23 @@ function requestIdentity(opts, url, method, body, documentUrl, pageAmount, flexS
|
|
|
1284
1291
|
// out, so the shop joins the identity: a retry that names another shop needs its own approval.
|
|
1285
1292
|
const shop = gmoRequestShop(url, body);
|
|
1286
1293
|
const identity = shop === undefined ? shaped.shape : `${shaped.shape};gmo_shop:${JSON.stringify(shop)}`;
|
|
1287
|
-
return { url, method, documentUrl, pageAmount, body: `${identity}${paddleVgsRetryKey(url, body, headers)}`,
|
|
1294
|
+
return { url, method, documentUrl, pageAmount, body: `${identity}${paddleVgsRetryKey(url, body, headers)}${toastRetryKey(url, body)}`,
|
|
1288
1295
|
amountEvidence: opts.amount != null || pageAmount !== undefined || shaped.namesAmount };
|
|
1289
1296
|
}
|
|
1297
|
+
/**
|
|
1298
|
+
* What a Toast card request's retry must repeat, beyond its shape: the sealed card opens at any Toast
|
|
1299
|
+
* restaurant whose payment intent names it, and the frame seals the ZIP inside it, so the payment session
|
|
1300
|
+
* and the ZIP join the identity. A request under another session or with another ZIP never receives the
|
|
1301
|
+
* card the cardholder approved for the first, while the frame's retry, its placeholder sealed afresh, still
|
|
1302
|
+
* does. A body Toast's reader refuses joins whole, so it matches nothing but itself. Empty for every other
|
|
1303
|
+
* request.
|
|
1304
|
+
*/
|
|
1305
|
+
function toastRetryKey(url, body) {
|
|
1306
|
+
if (!isToastPaymentMethodsUrl(url))
|
|
1307
|
+
return '';
|
|
1308
|
+
const read = readToastBody(body);
|
|
1309
|
+
return `;toast:${JSON.stringify(read.ok ? [read.sessionSecret, read.postalCode] : body)}`;
|
|
1310
|
+
}
|
|
1290
1311
|
/**
|
|
1291
1312
|
* What a Paddle VGS card request's retry must repeat byte for byte, beyond its shape: Paddle's frame
|
|
1292
1313
|
* sends its second request with the same body under the same bearer, and the bearer names the
|
|
@@ -1750,6 +1771,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1750
1771
|
let setupFailureReported = false;
|
|
1751
1772
|
const ownedSessions = new Set([pageSessionId]);
|
|
1752
1773
|
const sessionTypes = new Map([[pageSessionId, 'page']]);
|
|
1774
|
+
// Each window this attachment adopted (adoptOpenedWindow): its session, and its target's id, which is its main frame's.
|
|
1775
|
+
const windowTargets = new Map();
|
|
1753
1776
|
const sessionParents = new Map();
|
|
1754
1777
|
// The frame in its parent session that a child target hangs from (TargetInfo.parentFrameId), so a
|
|
1755
1778
|
// frame lookup can climb from a cross-origin frame into the document that frames it.
|
|
@@ -1861,6 +1884,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1861
1884
|
const juspayCheckout = new JuspayCheckoutGate(opts.vault);
|
|
1862
1885
|
// The approved EBANX token request on this attachment and the sends claimed under it (ebanx.ts).
|
|
1863
1886
|
const ebanxFollowUps = new EbanxFollowUps();
|
|
1887
|
+
// The approved Payrexx card request on this attachment and the 3-D Secure sends claimed under it (payrexx.ts).
|
|
1888
|
+
const payrexxFollowUps = new PayrexxFollowUps();
|
|
1864
1889
|
const aci = new AciGate(opts.vault);
|
|
1865
1890
|
const eprotect = new EprotectGate(opts.vault);
|
|
1866
1891
|
// The merchant's follow-up request after a delivered Secure Acceptance reply (cybersource-secure-acceptance.ts).
|
|
@@ -1874,6 +1899,20 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1874
1899
|
preparationFrameId = tree.frameTree.frame.id;
|
|
1875
1900
|
return tree.frameTree.frame.url;
|
|
1876
1901
|
};
|
|
1902
|
+
// The URL the page's main frame last committed: its target's URL when the attachment is set up, then kept from
|
|
1903
|
+
// Page.frameNavigated and Page.navigatedWithinDocument, and from the page target's own reports (notePageTarget),
|
|
1904
|
+
// which name a document the page commits before this attachment has enabled its Page events. Chromium leaves
|
|
1905
|
+
// Page.getFrameTree unanswered on the page's session while that session's own top-level navigation is paused (a
|
|
1906
|
+
// hosted page entry the page sends this window to, by script or through the merchant's redirect), so
|
|
1907
|
+
// readDocumentUrl would wait out its bound and the create would name no checkout page. For that one paused request
|
|
1908
|
+
// the page is still the document it last committed.
|
|
1909
|
+
let committedTopUrl;
|
|
1910
|
+
/** A TargetInfo of the page's own target names the URL the page last committed: the target list, and each report. */
|
|
1911
|
+
const notePageTarget = (info) => {
|
|
1912
|
+
if (pageTargetId !== undefined && info?.targetId === pageTargetId && info?.type === 'page' && typeof info.url === 'string' && info.url) {
|
|
1913
|
+
committedTopUrl = info.url.split('#')[0];
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1877
1916
|
// prepare({ tokenizeBeforePay: true }) sends the card Microform whose field documents are committed
|
|
1878
1917
|
// now. A field's commit can come before this attachment enabled its session, so no event reports
|
|
1879
1918
|
// it; each iframe session's own frame tree, and the page's, is read once first (as rereadMicroforms
|
|
@@ -1908,7 +1947,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1908
1947
|
const frame = find(tree?.frameTree, 0);
|
|
1909
1948
|
if (!frame)
|
|
1910
1949
|
return null;
|
|
1911
|
-
return { navigation: frame.parentId || sessionId !== pageSessionId ? 'frame' : 'top',
|
|
1950
|
+
return { navigation: frame.parentId || (sessionId !== pageSessionId && !(sessionId && windowTargets.has(sessionId))) ? 'frame' : 'top',
|
|
1951
|
+
url: typeof frame.url === 'string' ? frame.url : undefined };
|
|
1912
1952
|
};
|
|
1913
1953
|
/**
|
|
1914
1954
|
* Which frame a paused document request navigates, as documentFrame reads it, without the frame tree where
|
|
@@ -1924,6 +1964,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1924
1964
|
return 'frame';
|
|
1925
1965
|
if (sessionId === pageSessionId && pageTargetId !== undefined && frameId === pageTargetId)
|
|
1926
1966
|
return 'top';
|
|
1967
|
+
// An adopted window's main frame is that window's own top-level document, and its frame tree is as unanswered.
|
|
1968
|
+
if (sessionId && windowTargets.get(sessionId) === frameId)
|
|
1969
|
+
return 'top';
|
|
1927
1970
|
return (await documentFrame(sessionId, frameId))?.navigation ?? null;
|
|
1928
1971
|
};
|
|
1929
1972
|
/**
|
|
@@ -2117,6 +2160,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2117
2160
|
let lastSubmitted = null;
|
|
2118
2161
|
// The approved Datatrans answers, per frameId: the repeat rule (datatrans.ts).
|
|
2119
2162
|
const datatransRepeats = new DatatransRepeats();
|
|
2163
|
+
// The approved TokenEx iframe answers, per frameId (the card-number frame is an out-of-process iframe whose own
|
|
2164
|
+
// session pauses its post; its frameId names it): the repeat rule (tokenex-iframe.ts).
|
|
2165
|
+
const tokenexIframeRepeats = new TokenexIframeRepeats();
|
|
2120
2166
|
// The card product each Worldline MyCheckout hosted checkout chose (worldline-mycheckout.ts).
|
|
2121
2167
|
const worldlineProducts = new WorldlineMyCheckoutProducts();
|
|
2122
2168
|
// The Shiji STS getCvvId companion of the getTokenId this attachment last continued (shiji-sts.ts).
|
|
@@ -2161,11 +2207,14 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2161
2207
|
// EBANX's 3-D Secure calls and hosted confirm, which send the typed card again
|
|
2162
2208
|
// after its token: the cardholder's device sends one the approved token request
|
|
2163
2209
|
// covers, and any other is refused (see ebanx.ts).
|
|
2210
|
+
// ComGate's card hosts are armed only for an org the API served ComGate to, so the
|
|
2211
|
+
// gate can fail every card post there while the gateway link pauses (see comgate.ts).
|
|
2164
2212
|
const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
|
|
2165
2213
|
...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
|
|
2166
2214
|
...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
|
|
2167
2215
|
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...PAYPAL_CARD_FORM_PATTERNS,
|
|
2168
|
-
...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS
|
|
2216
|
+
...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS,
|
|
2217
|
+
...(servesComgate(opts.vault) ? COMGATE_CARD_PATTERNS : [])])];
|
|
2169
2218
|
// The hold-only rules' patterns (Bolt's tokenizer; every request a placeholder card can ride, by resource type).
|
|
2170
2219
|
const fetchPatterns = [...urlPatterns.map((urlPattern) => ({ urlPattern, requestStage: 'Request' })),
|
|
2171
2220
|
...(holdGate ? holdGate.patterns().map((p) => ({ ...p, requestStage: 'Request' })) : [])];
|
|
@@ -2294,6 +2343,55 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2294
2343
|
lifecycle.failed(new PaymentOutcomeUnknownError(lifecycle.getState().authorizationId, 'browser_interception_unavailable'));
|
|
2295
2344
|
opts.onEvent?.({ type: 'failed', detail: 'browser_interception_unavailable' });
|
|
2296
2345
|
};
|
|
2346
|
+
/**
|
|
2347
|
+
* A window the merchant's page opened (window.open, or a form or link aimed at a new window), or one such a window
|
|
2348
|
+
* opened in turn, in the merchant's own browser context: it belongs to this checkout, so it is attached and armed
|
|
2349
|
+
* as the page is, and its requests reach the same gates. Chromium reports the window (Target.targetCreated, with its
|
|
2350
|
+
* openerId) as it starts, and the page session's auto-attach never covers a new page target. The attach races the
|
|
2351
|
+
* window's first request: a window that opens on the merchant's page and is sent on to a hosted page by script or
|
|
2352
|
+
* by the merchant's redirect is armed before that entry; a window opened straight onto a hosted page can load it
|
|
2353
|
+
* first, as every window did before. A window reported while this attachment still arms the page waits in
|
|
2354
|
+
* pendingWindows and is adopted once the page is armed, inside the setup deadline and before the attachment reports
|
|
2355
|
+
* ready, its opener judged then, so a window that window opened is adopted after it. A window that is gone before it
|
|
2356
|
+
* could be attached was never armed, as before; one attached but not armed fails the attachment, as a frame does.
|
|
2357
|
+
*/
|
|
2358
|
+
const openedWindows = new Set();
|
|
2359
|
+
const pendingWindows = [];
|
|
2360
|
+
const adoptOpenedWindow = async (info, duringSetup = false) => {
|
|
2361
|
+
if (!info || info.type !== 'page' || typeof info.targetId !== 'string' || !info.targetId || info.targetId === pageTargetId
|
|
2362
|
+
|| openedWindows.has(info.targetId) || info.browserContextId !== merchantContext)
|
|
2363
|
+
return;
|
|
2364
|
+
if (setupStop.signal.aborted)
|
|
2365
|
+
return;
|
|
2366
|
+
if (!attachmentReady && !duringSetup) {
|
|
2367
|
+
pendingWindows.push(info);
|
|
2368
|
+
return;
|
|
2369
|
+
}
|
|
2370
|
+
if (typeof info.openerId !== 'string' || !(info.openerId === pageTargetId || openedWindows.has(info.openerId)))
|
|
2371
|
+
return;
|
|
2372
|
+
openedWindows.add(info.targetId);
|
|
2373
|
+
let child;
|
|
2374
|
+
try {
|
|
2375
|
+
child = (await cdp.send('Target.attachToTarget', { targetId: info.targetId, flatten: true }))?.sessionId;
|
|
2376
|
+
}
|
|
2377
|
+
catch {
|
|
2378
|
+
return;
|
|
2379
|
+
}
|
|
2380
|
+
try {
|
|
2381
|
+
if (typeof child !== 'string' || !child || ownedSessions.has(child) || detachedSessions.has(child) || rejectedSessions.has(child)) {
|
|
2382
|
+
throw new CheckoutAttachmentError('unavailable');
|
|
2383
|
+
}
|
|
2384
|
+
ownedSessions.add(child);
|
|
2385
|
+
sessionTypes.set(child, 'page');
|
|
2386
|
+
sessionParents.set(child, pageSessionId);
|
|
2387
|
+
windowTargets.set(child, info.targetId);
|
|
2388
|
+
await arm(child);
|
|
2389
|
+
opts.onEvent?.({ type: 'window_armed', detail: {} });
|
|
2390
|
+
}
|
|
2391
|
+
catch (error) {
|
|
2392
|
+
failInterception(error);
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2297
2395
|
cdp.on(async (method, params, sessionId) => {
|
|
2298
2396
|
// A frame that committed a new document drops the companion binding made for the getTokenId its
|
|
2299
2397
|
// old document sent, with the device's CVV ciphertext: the document that replaced it never
|
|
@@ -2304,6 +2402,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2304
2402
|
shijiCompanions.releasePage((page) => page === replaced);
|
|
2305
2403
|
}
|
|
2306
2404
|
if ((method === 'Target.targetCreated' || method === 'Target.targetInfoChanged') && targetContextKnown) {
|
|
2405
|
+
// The page's own target reports each document it commits from discovery on, Page events or not.
|
|
2406
|
+
notePageTarget(params?.targetInfo);
|
|
2307
2407
|
try {
|
|
2308
2408
|
assertContextTarget(params?.targetInfo, merchantContext, targetAllowance);
|
|
2309
2409
|
if (params?.targetInfo?.type === 'shared_worker')
|
|
@@ -2312,8 +2412,18 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2312
2412
|
catch (error) {
|
|
2313
2413
|
failInterception(error);
|
|
2314
2414
|
}
|
|
2415
|
+
if (method === 'Target.targetCreated')
|
|
2416
|
+
await adoptOpenedWindow(params?.targetInfo);
|
|
2315
2417
|
return;
|
|
2316
2418
|
}
|
|
2419
|
+
// The page's main frame committed a document, or moved within it: the URL a held top-level navigation leaves.
|
|
2420
|
+
if (sessionId === pageSessionId && method === 'Page.frameNavigated' && !params.frame?.parentId && typeof params.frame?.url === 'string') {
|
|
2421
|
+
committedTopUrl = params.frame.url;
|
|
2422
|
+
}
|
|
2423
|
+
if (sessionId === pageSessionId && method === 'Page.navigatedWithinDocument' && pageTargetId !== undefined
|
|
2424
|
+
&& params?.frameId === pageTargetId && typeof params?.url === 'string') {
|
|
2425
|
+
committedTopUrl = params.url.split('#')[0];
|
|
2426
|
+
}
|
|
2317
2427
|
if (((method === 'Page.frameNavigated' && !params.frame?.parentId) || (method === 'Page.navigatedWithinDocument' && preparationFrameId && params.frameId === preparationFrameId)) && sessionId === pageSessionId) {
|
|
2318
2428
|
preparationGate.invalidate('merchant_document_changed');
|
|
2319
2429
|
mercadoCheckout.invalidate();
|
|
@@ -2353,10 +2463,11 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2353
2463
|
}
|
|
2354
2464
|
// A watched shared worker sent a card request: it went out unpaused, so nothing is approved from here on.
|
|
2355
2465
|
if (method === 'Network.requestWillBeSent' && sessionId && watchedWorkerSessions.has(sessionId) && sharedWorkerCardRequest(params?.request)) {
|
|
2466
|
+
// Origin and path as redactUrl reads them, so a credential a processor writes into the path never shows.
|
|
2356
2467
|
let endpoint = '';
|
|
2357
2468
|
try {
|
|
2358
|
-
|
|
2359
|
-
endpoint =
|
|
2469
|
+
new URL(params.request.url);
|
|
2470
|
+
endpoint = redactUrl(params.request.url);
|
|
2360
2471
|
}
|
|
2361
2472
|
catch { /* unreadable */ }
|
|
2362
2473
|
opts.onEvent?.({ type: 'card_request_unwatched', detail: { endpoint, from: 'shared_worker' } });
|
|
@@ -2641,6 +2752,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2641
2752
|
const { requestId, request, resourceType, networkId, frameId } = params;
|
|
2642
2753
|
// When this request paused: the merchant's total is read as of this moment.
|
|
2643
2754
|
const pausedAt = Date.now();
|
|
2755
|
+
// The page's own top-level navigation, paused: its frame tree cannot be read until this request is decided, so its
|
|
2756
|
+
// document is the one it last committed (committedTopUrl). Every other request reads the frame tree as before.
|
|
2757
|
+
const readPausedDocument = resourceType === 'Document' && sessionId === pageSessionId && pageTargetId !== undefined
|
|
2758
|
+
&& frameId === pageTargetId && committedTopUrl !== undefined ? async () => committedTopUrl : readDocumentUrl;
|
|
2644
2759
|
// A paused document request has not committed either: it only starts the context's check.
|
|
2645
2760
|
if (resourceType === 'Document')
|
|
2646
2761
|
microforms.warm(withFragment(request));
|
|
@@ -2723,7 +2838,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2723
2838
|
cybersourceFlex: { judge: (flexRequest) => microforms.judge(flexRequest, frameId, () => rereadMicroforms(sessionId, frameId)) },
|
|
2724
2839
|
juspay: juspayCheckout,
|
|
2725
2840
|
datatrans: datatransRepeats,
|
|
2841
|
+
tokenexIframe: tokenexIframeRepeats,
|
|
2726
2842
|
ebanx: ebanxFollowUps,
|
|
2843
|
+
payrexx: payrexxFollowUps,
|
|
2727
2844
|
frame: typeof frameId === 'string' && frameId ? frameId : undefined,
|
|
2728
2845
|
payrails,
|
|
2729
2846
|
shijiSts: { companions: shijiCompanions, page: () => cdpShijiPage(sessionId, frameId) },
|
|
@@ -2794,7 +2911,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2794
2911
|
if (awaitingApproval && activeRequest?.attempt.lost && !terminal && !setupStop.signal.aborted && !lifecycle.isCancelled()
|
|
2795
2912
|
&& typeof networkId === 'string' && networkId) {
|
|
2796
2913
|
const retryBody = pausedBody(request);
|
|
2797
|
-
const retry = retryBody === null ? null : requestIdentity(opts, request.url, request.method, retryBody, documentKey(await documentUrlOf(
|
|
2914
|
+
const retry = retryBody === null ? null : requestIdentity(opts, request.url, request.method, retryBody, documentKey(await documentUrlOf(readPausedDocument, lifecycle.abort.signal)), await pageAmountOf(opts), flexPause?.shape, request.headers);
|
|
2798
2915
|
// A prepared approval is answered only in the card frame it was bound in: the same request from
|
|
2799
2916
|
// another frame (a replacement for a removed one, or a second card frame) is refused below.
|
|
2800
2917
|
if (retry && activeRequest?.attempt.lost && sameCheckoutRequest(activeRequest.identity, retry)
|
|
@@ -2837,8 +2954,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2837
2954
|
// A request that skipped the spent preparation's claim and was not bound as
|
|
2838
2955
|
// its retry (the approval ended while it was judged) is refused as that
|
|
2839
2956
|
// claim would refuse it, never asked as a new approval.
|
|
2840
|
-
// An EBANX send still out (a confirm that may pay
|
|
2841
|
-
//
|
|
2957
|
+
// An EBANX or Payrexx send still out (a confirm that may pay, a 3-D Secure step whose answer names the token the
|
|
2958
|
+
// page charges) holds the attachment too: a new card request the page sends meanwhile, a second tap on the hosted
|
|
2959
|
+
// page's Pay among them, would start another payment over it.
|
|
2842
2960
|
const followUpOut = lifecycle.followUpsInFlight();
|
|
2843
2961
|
if (terminal || lifecycle.isBlocked() || awaitingApproval || followUpOut || preparedRetry || Date.now() < quietUntil) {
|
|
2844
2962
|
let repeatedAuthorizationId = null;
|
|
@@ -2849,7 +2967,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2849
2967
|
}
|
|
2850
2968
|
}
|
|
2851
2969
|
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
2852
|
-
: followUpOut ? '
|
|
2970
|
+
: followUpOut ? 'a follow-up send is in flight' : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
2853
2971
|
opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
|
|
2854
2972
|
if (preparation)
|
|
2855
2973
|
preparationGate.retireUnboundClaim();
|
|
@@ -2890,8 +3008,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2890
3008
|
// The approval the device gave, and whether it reached a browser request.
|
|
2891
3009
|
let approvedId = null;
|
|
2892
3010
|
let deliveryBegun = false;
|
|
2893
|
-
// Whether this handoff kept an
|
|
2894
|
-
|
|
3011
|
+
// Whether this handoff kept an approval's follow-ups (ebanxFollowUps.remember, payrexxFollowUps.remember), which a
|
|
3012
|
+
// failed handoff stops.
|
|
3013
|
+
let followUpsRemembered = false;
|
|
2895
3014
|
// The business's pause, so resume resolves once the page has its answer.
|
|
2896
3015
|
let businessPause = null;
|
|
2897
3016
|
// Hold mode: the person's release, settled with what came of it.
|
|
@@ -2948,7 +3067,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2948
3067
|
await preparationGate.assertDocument();
|
|
2949
3068
|
// One bounded read of the document serves both the merchant origin and
|
|
2950
3069
|
// the identity a retry has to repeat (see sameCheckoutRequest).
|
|
2951
|
-
const documentUrl = await documentUrlOf(
|
|
3070
|
+
const documentUrl = await documentUrlOf(readPausedDocument, attempt.signal);
|
|
2952
3071
|
const pageOrigin = pageOriginFrom(documentUrl);
|
|
2953
3072
|
// The agent's amount is a card endpoint's only authority: no page total is read.
|
|
2954
3073
|
const pageAmount = cardEndpoint ? undefined : await pageAmountOf(opts);
|
|
@@ -3225,12 +3344,14 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3225
3344
|
throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
|
|
3226
3345
|
}
|
|
3227
3346
|
// An approved EBANX token request whose read names a window covers the 3-D Secure calls and the confirm the
|
|
3228
|
-
// page sends next (ebanx.ts)
|
|
3229
|
-
//
|
|
3230
|
-
//
|
|
3347
|
+
// page sends next (ebanx.ts), and an approved Payrexx card request whose answer started 3-D Secure covers the
|
|
3348
|
+
// card frame's two 3-D Secure sends (payrexx.ts): kept as the API stored it, the request that raised the
|
|
3349
|
+
// approval. Kept before the page has the answer, since the page may send its first 3-D Secure call the moment
|
|
3350
|
+
// the answer lands; a handoff that then fails stops them (the catch below).
|
|
3231
3351
|
if (replay.mode !== 'device_encrypt') {
|
|
3232
3352
|
ebanxFollowUps.remember({ url: request.url, body }, replay);
|
|
3233
|
-
|
|
3353
|
+
payrexxFollowUps.remember({ url: request.url, body }, replay);
|
|
3354
|
+
followUpsRemembered = true;
|
|
3234
3355
|
}
|
|
3235
3356
|
await cdp.send('Fetch.fulfillRequest', {
|
|
3236
3357
|
requestId: deliverTo,
|
|
@@ -3240,6 +3361,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3240
3361
|
}, deliverOn);
|
|
3241
3362
|
// The frame that received it: the paused request's, or its retry's once rebound.
|
|
3242
3363
|
datatransRepeats.remember(activeRequest?.frameId || undefined, deliverRequest.url, deliverBody, replay);
|
|
3364
|
+
tokenexIframeRepeats.remember(activeRequest?.frameId || undefined, deliverRequest.url, deliverBody, replay);
|
|
3243
3365
|
attempt.assertLive();
|
|
3244
3366
|
// An ACI card-number request answered: its checkout's held CVC and expiry requests get the
|
|
3245
3367
|
// same answer, and its /payment submission may continue (see AciGate).
|
|
@@ -3267,10 +3389,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3267
3389
|
authorizationId: approvedId ?? lifecycle.getState().authorizationId, detail: failureSummary(err) });
|
|
3268
3390
|
// Ignore the failure event caused by our own safe decline/error abort.
|
|
3269
3391
|
const dead = attempt.target ?? { requestId, sessionId };
|
|
3270
|
-
// An
|
|
3271
|
-
//
|
|
3272
|
-
if (
|
|
3392
|
+
// An answer this handoff kept follow-ups for and did not finish handing over: nothing after it is claimed
|
|
3393
|
+
// (ebanx.ts EbanxFollowUps.stop, payrexx.ts PayrexxFollowUps.stop).
|
|
3394
|
+
if (followUpsRemembered) {
|
|
3273
3395
|
ebanxFollowUps.stop();
|
|
3396
|
+
payrexxFollowUps.stop();
|
|
3397
|
+
}
|
|
3274
3398
|
// The business's resume settles once the request is answered: a terminal read is an answer (failed, nothing charged), a lost handoff is not.
|
|
3275
3399
|
const settlePause = () => { if (businessPause) {
|
|
3276
3400
|
if (isApprovalOutcome(err))
|
|
@@ -3338,6 +3462,11 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3338
3462
|
targetContextKnown = true;
|
|
3339
3463
|
// A page target's id is its main frame's id: a navigation of that frame is the page's own (pausedNavigation).
|
|
3340
3464
|
pageTargetId = typeof targetInfo.targetId === 'string' && targetInfo.targetId ? targetInfo.targetId : undefined;
|
|
3465
|
+
// The page's URL as its target reports it: the document a held top-level navigation leaves until the page commits
|
|
3466
|
+
// another one (the Page.frameNavigated branch above, and notePageTarget for a commit before the Page events are on).
|
|
3467
|
+
// No frame tree read: a navigation the page starts while this attachment arms would hold it.
|
|
3468
|
+
if (committedTopUrl === undefined && typeof targetInfo.url === 'string' && targetInfo.url)
|
|
3469
|
+
committedTopUrl = targetInfo.url.split('#')[0];
|
|
3341
3470
|
await cdp.send('Target.setDiscoverTargets', { discover: true });
|
|
3342
3471
|
assertActive();
|
|
3343
3472
|
const { targetInfos } = await cdp.send('Target.getTargets');
|
|
@@ -3346,6 +3475,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3346
3475
|
throw new CheckoutAttachmentError('unavailable');
|
|
3347
3476
|
for (const target of targetInfos)
|
|
3348
3477
|
assertContextTarget(target, merchantContext, targetAllowance);
|
|
3478
|
+
// The page may have committed another document since its TargetInfo was read; the list names the one it holds.
|
|
3479
|
+
for (const target of targetInfos)
|
|
3480
|
+
notePageTarget(target);
|
|
3349
3481
|
for (const target of targetInfos)
|
|
3350
3482
|
if (target?.type === 'shared_worker')
|
|
3351
3483
|
await watchSharedWorker(target);
|
|
@@ -3354,13 +3486,24 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3354
3486
|
// but their Fetch setup finishes later. Do not invite the caller to press
|
|
3355
3487
|
// Pay while one of those already-running frames is still unprotected, or
|
|
3356
3488
|
// while a shared worker reported during setup is not yet watched.
|
|
3357
|
-
while
|
|
3358
|
-
|
|
3489
|
+
// A window the page opened while this attachment armed it is attached and armed here too, in the order Chromium
|
|
3490
|
+
// reported it (adoptOpenedWindow), and the frames and workers such a window brings are waited for as the page's
|
|
3491
|
+
// own are, all under the setup deadline. One that cannot be armed fails the attachment.
|
|
3492
|
+
while (arming.size || workerWatches.size || pendingWindows.length) {
|
|
3493
|
+
if (arming.size || workerWatches.size)
|
|
3494
|
+
await Promise.all([...arming.values(), ...workerWatches.values()]);
|
|
3495
|
+
else
|
|
3496
|
+
await adoptOpenedWindow(pendingWindows.shift(), true);
|
|
3497
|
+
assertActive();
|
|
3498
|
+
}
|
|
3359
3499
|
assertActive();
|
|
3360
3500
|
}, setupTimeoutMs, setupStop.signal);
|
|
3361
3501
|
if (setupStop.signal.aborted)
|
|
3362
3502
|
throw attachmentFailure(setupStop.signal.reason);
|
|
3363
3503
|
attachmentReady = true;
|
|
3504
|
+
// A window reported after that drain, before this line, is adopted the way a later window is.
|
|
3505
|
+
for (const info of pendingWindows.splice(0))
|
|
3506
|
+
void adoptOpenedWindow(info);
|
|
3364
3507
|
opts.onEvent?.({ type: 'fetch_armed', detail: { patterns: urlPatterns, ...(holdGate ? { hold: true, holdRules: holdGate.rules.map((r) => r.psp) } : {}), ...(bypassServiceWorkers ? { serviceWorkers: 'bypass' } : {}) } });
|
|
3365
3508
|
}
|
|
3366
3509
|
catch (error) {
|
|
@@ -3424,6 +3567,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3424
3567
|
const juspayCheckout = new JuspayCheckoutGate(opts.vault);
|
|
3425
3568
|
// The approved EBANX token request on this attachment and the sends claimed under it (ebanx.ts).
|
|
3426
3569
|
const ebanxFollowUps = new EbanxFollowUps();
|
|
3570
|
+
// The approved Payrexx card request on this attachment and the 3-D Secure sends claimed under it (payrexx.ts).
|
|
3571
|
+
const payrexxFollowUps = new PayrexxFollowUps();
|
|
3427
3572
|
const aci = new AciGate(opts.vault);
|
|
3428
3573
|
let navigationBodyReplaced;
|
|
3429
3574
|
const eprotect = new EprotectGate(opts.vault);
|
|
@@ -3700,6 +3845,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3700
3845
|
let lastSubmitted = null;
|
|
3701
3846
|
// The approved Datatrans answers, per Frame: the repeat rule (datatrans.ts).
|
|
3702
3847
|
const datatransRepeats = new DatatransRepeats();
|
|
3848
|
+
// The approved TokenEx iframe answers, per Frame: the repeat rule (tokenex-iframe.ts).
|
|
3849
|
+
const tokenexIframeRepeats = new TokenexIframeRepeats();
|
|
3703
3850
|
// The card product each Worldline MyCheckout hosted checkout chose (worldline-mycheckout.ts).
|
|
3704
3851
|
const worldlineProducts = new WorldlineMyCheckoutProducts();
|
|
3705
3852
|
const frameOf = (request) => { try {
|
|
@@ -3720,8 +3867,10 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3720
3867
|
// The matcher sees only the URL. Worldpay eProtect's registration is a GET, so its family and
|
|
3721
3868
|
// the vendor's give-up reports are routed by the eProtect gate (never its scripts).
|
|
3722
3869
|
// Gravy's session fields are a PUT, so a URL a PUT recognizer claims is routed too; the
|
|
3723
|
-
// pre-claim gates then pause only the PUT and continue the URL's other methods.
|
|
3870
|
+
// pre-claim gates then pause only the PUT and continue the URL's other methods. A hosted page
|
|
3871
|
+
// entered with a plain GET (hosted-entry.ts, ComGate's gateway link) is routed by its exact link.
|
|
3724
3872
|
(url) => opts.vault.isCardRequest(url.toString()) || opts.vault.isCardRequest(url.toString(), 'PUT')
|
|
3873
|
+
|| opts.vault.isCardRequest(url.toString(), 'GET')
|
|
3725
3874
|
|| guards.matches(url.toString()) || stripeCheckout.matches(url.toString()) || mercadoCheckout.matches(url.toString())
|
|
3726
3875
|
|| eprotect.matches(url.toString())
|
|
3727
3876
|
// An armed merchant profile's endpoint or a sibling of it (merchant-hosted.ts), as attachToCdp arms it.
|
|
@@ -3739,9 +3888,16 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3739
3888
|
|| aci.matches(url.toString())
|
|
3740
3889
|
// Every Payrails host, routed only for an org the API served Payrails to (see attachToCdp).
|
|
3741
3890
|
|| (payrails !== null && isPayrailsHost(url.toString()))
|
|
3891
|
+
// Every request to Przelewy24's pay wall hosts once its entry is served (przelewy24.ts), as attachToCdp arms
|
|
3892
|
+
// the hosts: the card method's post, the card form's post and the Saferpay resolver reach the Przelewy24
|
|
3893
|
+
// gate and fail.
|
|
3894
|
+
|| isArmedPrzelewy24Url(opts.vault, url.toString())
|
|
3742
3895
|
// Every request to an NMI host whose entry is served (ekashu.ts), as attachToCdp arms the
|
|
3743
3896
|
// host: the card page's post and its 3-D Secure calls reach the NMI gate and fail.
|
|
3744
3897
|
|| isArmedEkashuUrl(opts.vault, url.toString())
|
|
3898
|
+
// Every request to a Webpay host once its entry is served (transbank.ts), as attachToCdp arms the hosts: the
|
|
3899
|
+
// token's submit and Webpay's card steps reach the Webpay gate and fail, and the entry pauses.
|
|
3900
|
+
|| isArmedTransbankUrl(opts.vault, url.toString())
|
|
3745
3901
|
// A CreditGuard page's own calls beside its submit (its card lookup and its 3-D Secure calls), for an
|
|
3746
3902
|
// org the API served CreditGuard to, as attachToCdp arms its hosts: the CreditGuard gate answers them
|
|
3747
3903
|
// under a ready preparation and lets them go on with none (pre-claim.ts isArmedCreditguardCall).
|
|
@@ -3752,6 +3908,15 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3752
3908
|
// Every request to QuickPay's host once its entry is served (quickpay.ts), as attachToCdp arms the host:
|
|
3753
3909
|
// the window's card post, its other card requests and its card lookup reach the QuickPay gate and fail.
|
|
3754
3910
|
|| isArmedQuickpayUrl(opts.vault, url.toString())
|
|
3911
|
+
// Every request to one of ComGate's card hosts once its entry is served (comgate.ts), as attachToCdp arms them:
|
|
3912
|
+
// the gateway's card post and every other card call there reach the ComGate gate and fail.
|
|
3913
|
+
|| isArmedComgateUrl(opts.vault, url.toString())
|
|
3914
|
+
// Every request on the TokenEx iframe's /iframe/v3 path once its entry is served (tokenex-iframe.ts), as attachToCdp
|
|
3915
|
+
// arms its hosts: a card post the recognizer's exact URL does not match reaches the TokenEx gate and fails.
|
|
3916
|
+
|| isArmedTokenexIframeUrl(opts.vault, url.toString())
|
|
3917
|
+
// Every request to an Eximbay host once its entry is served there (eximbay.ts), as attachToCdp arms the host: the
|
|
3918
|
+
// step, the window's lookups and its card post reach the Eximbay gate and fail.
|
|
3919
|
+
|| isArmedEximbayUrl(opts.vault, url.toString())
|
|
3755
3920
|
// PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
|
|
3756
3921
|
|| isPaypalHostedFieldsConfirm(url.toString())
|
|
3757
3922
|
// PayPal's Card Fields confirm and GraphQL on www.paypal.com, for every org, as attachToCdp arms them (paypal-card-forms.ts).
|
|
@@ -3857,7 +4022,9 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3857
4022
|
} },
|
|
3858
4023
|
juspay: juspayCheckout,
|
|
3859
4024
|
datatrans: datatransRepeats,
|
|
4025
|
+
tokenexIframe: tokenexIframeRepeats,
|
|
3860
4026
|
ebanx: ebanxFollowUps,
|
|
4027
|
+
payrexx: payrexxFollowUps,
|
|
3861
4028
|
frame: frameOf(request),
|
|
3862
4029
|
payrails,
|
|
3863
4030
|
shijiSts: { companions: shijiCompanions, page: async () => (await playwrightShijiDocumentPage(request)) ?? `pw:none:${++playwrightShijiSeq}` },
|
|
@@ -3952,7 +4119,7 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3952
4119
|
// identically whichever adapter is attached. An Amadeus card request is
|
|
3953
4120
|
// 'failed' (ERR_FAILED) in both too, see failsAsNetworkError (refusalFailure).
|
|
3954
4121
|
// As in attachToCdp: an unbound request that skipped the spent preparation's claim is refused.
|
|
3955
|
-
// As in attachToCdp: an EBANX send still out holds the attachment too.
|
|
4122
|
+
// As in attachToCdp: an EBANX or Payrexx send still out holds the attachment too.
|
|
3956
4123
|
const followUpOut = lifecycle.followUpsInFlight();
|
|
3957
4124
|
if (terminal || lifecycle.isBlocked() || awaitingApproval || followUpOut || preparedRetry || Date.now() < quietUntil) {
|
|
3958
4125
|
let repeatedAuthorizationId = null;
|
|
@@ -3967,7 +4134,7 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3967
4134
|
}
|
|
3968
4135
|
}
|
|
3969
4136
|
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
3970
|
-
: followUpOut ? '
|
|
4137
|
+
: followUpOut ? 'a follow-up send is in flight' : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
3971
4138
|
opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
|
|
3972
4139
|
if (preparation)
|
|
3973
4140
|
preparationGate.retireUnboundClaim();
|
|
@@ -4021,7 +4188,7 @@ export async function attachToPlaywright(page, opts) {
|
|
|
4021
4188
|
let approvedId = null;
|
|
4022
4189
|
let deliveryBegun = false;
|
|
4023
4190
|
// See the same flag in attachToCdp.
|
|
4024
|
-
let
|
|
4191
|
+
let followUpsRemembered = false;
|
|
4025
4192
|
// A prepared Amadeus SDK add must be sent within its window (see amadeus.ts).
|
|
4026
4193
|
let disarmSendWindow = null;
|
|
4027
4194
|
try {
|
|
@@ -4239,10 +4406,12 @@ export async function attachToPlaywright(page, opts) {
|
|
|
4239
4406
|
// See the same step in attachToCdp: kept before the page has the answer.
|
|
4240
4407
|
if (replay.mode !== 'device_encrypt') {
|
|
4241
4408
|
ebanxFollowUps.remember({ url: request.url(), body }, replay);
|
|
4242
|
-
|
|
4409
|
+
payrexxFollowUps.remember({ url: request.url(), body }, replay);
|
|
4410
|
+
followUpsRemembered = true;
|
|
4243
4411
|
}
|
|
4244
4412
|
await deliverTo.fulfill({ status: replay.status, headers: withCorsHeaders(replay.headers, cors.headers), body: replay.body });
|
|
4245
4413
|
datatransRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
|
|
4414
|
+
tokenexIframeRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
|
|
4246
4415
|
assertRequestLive();
|
|
4247
4416
|
// See the same step in attachToCdp.
|
|
4248
4417
|
aci.answered(deliverRequest.url(), { status: replay.status, headers: replay.headers, body: replay.body }, replay.authorizationId);
|
|
@@ -4257,8 +4426,10 @@ export async function attachToPlaywright(page, opts) {
|
|
|
4257
4426
|
catch (err) {
|
|
4258
4427
|
const dead = attempt.target?.route ?? route;
|
|
4259
4428
|
// See the same step in attachToCdp.
|
|
4260
|
-
if (
|
|
4429
|
+
if (followUpsRemembered) {
|
|
4261
4430
|
ebanxFollowUps.stop();
|
|
4431
|
+
payrexxFollowUps.stop();
|
|
4432
|
+
}
|
|
4262
4433
|
if (activeRequest?.attempt === attempt)
|
|
4263
4434
|
activeRequest = null;
|
|
4264
4435
|
try {
|