@agent-cards/sdk 0.23.20 → 0.23.21
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 +41 -2
- package/PREFLIGHT.md +2 -0
- package/README.md +143 -5
- package/dist/card-fields.generated.js +984 -57
- package/dist/cdp.js +146 -10
- package/dist/client.d.ts +104 -3
- package/dist/client.js +233 -23
- package/dist/comgate.d.ts +50 -0
- package/dist/comgate.js +80 -0
- package/dist/cybersource-secure-acceptance.generated.d.ts +1 -1
- package/dist/eprotect.generated.d.ts +1 -1
- 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/paddle-tokenex.generated.js +7 -3
- package/dist/payrails.generated.js +7 -3
- package/dist/pre-claim.d.ts +7 -1
- package/dist/pre-claim.js +127 -20
- package/dist/preflight-catalog.json +58 -0
- package/dist/preflight-schemas.json +4 -0
- package/dist/preflight.generated.js +26 -2
- package/dist/primer.generated.js +1027 -100
- 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 +621 -12
- 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';
|
|
@@ -13,12 +13,16 @@ import { GMO_TOKEN_V1_PATTERNS, gmoRequestShop, isGmoTokenV1Request, recognizesG
|
|
|
13
13
|
import { isPaddleVgsCardsUrl } from './paddle-vgs.generated.js';
|
|
14
14
|
import { PADDLE_VGS_PAY_PATTERNS, PaddleVgsPayGate, isPaddleCheckoutService, recognizesPaddleVgs } from './paddle-vgs-pay.js';
|
|
15
15
|
import { DatatransRepeats } from './datatrans.js';
|
|
16
|
+
import { TokenexIframeRepeats, isArmedTokenexIframeUrl } from './tokenex-iframe.js';
|
|
16
17
|
import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
|
|
17
18
|
import { paddleTokenexTokenContinuation } from './paddle-tokenex.js';
|
|
18
19
|
import { twoC2pContinuation, twoC2pRetire } from './twoc2p.js';
|
|
19
20
|
import { isArmedEkashuUrl } from './ekashu.js';
|
|
21
|
+
import { isArmedTransbankUrl } from './transbank.js';
|
|
20
22
|
import { isArmedRedsysUrl } from './redsys.js';
|
|
23
|
+
import { isArmedPrzelewy24Url } from './przelewy24.js';
|
|
21
24
|
import { isArmedQuickpayUrl } from './quickpay.js';
|
|
25
|
+
import { COMGATE_CARD_PATTERNS, isArmedComgateUrl, servesComgate } from './comgate.js';
|
|
22
26
|
import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
|
|
23
27
|
import { EBANX_RESEND_PATTERNS, EbanxFollowUps, isEbanxResendUrl } from './ebanx.js';
|
|
24
28
|
import { PAYPAL_CARD_FORM_PATTERNS, isPaypalCardFormUrl } from './paypal-card-forms.js';
|
|
@@ -1750,6 +1754,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1750
1754
|
let setupFailureReported = false;
|
|
1751
1755
|
const ownedSessions = new Set([pageSessionId]);
|
|
1752
1756
|
const sessionTypes = new Map([[pageSessionId, 'page']]);
|
|
1757
|
+
// Each window this attachment adopted (adoptOpenedWindow): its session, and its target's id, which is its main frame's.
|
|
1758
|
+
const windowTargets = new Map();
|
|
1753
1759
|
const sessionParents = new Map();
|
|
1754
1760
|
// The frame in its parent session that a child target hangs from (TargetInfo.parentFrameId), so a
|
|
1755
1761
|
// frame lookup can climb from a cross-origin frame into the document that frames it.
|
|
@@ -1874,6 +1880,20 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1874
1880
|
preparationFrameId = tree.frameTree.frame.id;
|
|
1875
1881
|
return tree.frameTree.frame.url;
|
|
1876
1882
|
};
|
|
1883
|
+
// The URL the page's main frame last committed: its target's URL when the attachment is set up, then kept from
|
|
1884
|
+
// Page.frameNavigated and Page.navigatedWithinDocument, and from the page target's own reports (notePageTarget),
|
|
1885
|
+
// which name a document the page commits before this attachment has enabled its Page events. Chromium leaves
|
|
1886
|
+
// Page.getFrameTree unanswered on the page's session while that session's own top-level navigation is paused (a
|
|
1887
|
+
// hosted page entry the page sends this window to, by script or through the merchant's redirect), so
|
|
1888
|
+
// readDocumentUrl would wait out its bound and the create would name no checkout page. For that one paused request
|
|
1889
|
+
// the page is still the document it last committed.
|
|
1890
|
+
let committedTopUrl;
|
|
1891
|
+
/** A TargetInfo of the page's own target names the URL the page last committed: the target list, and each report. */
|
|
1892
|
+
const notePageTarget = (info) => {
|
|
1893
|
+
if (pageTargetId !== undefined && info?.targetId === pageTargetId && info?.type === 'page' && typeof info.url === 'string' && info.url) {
|
|
1894
|
+
committedTopUrl = info.url.split('#')[0];
|
|
1895
|
+
}
|
|
1896
|
+
};
|
|
1877
1897
|
// prepare({ tokenizeBeforePay: true }) sends the card Microform whose field documents are committed
|
|
1878
1898
|
// now. A field's commit can come before this attachment enabled its session, so no event reports
|
|
1879
1899
|
// it; each iframe session's own frame tree, and the page's, is read once first (as rereadMicroforms
|
|
@@ -1908,7 +1928,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1908
1928
|
const frame = find(tree?.frameTree, 0);
|
|
1909
1929
|
if (!frame)
|
|
1910
1930
|
return null;
|
|
1911
|
-
return { navigation: frame.parentId || sessionId !== pageSessionId ? 'frame' : 'top',
|
|
1931
|
+
return { navigation: frame.parentId || (sessionId !== pageSessionId && !(sessionId && windowTargets.has(sessionId))) ? 'frame' : 'top',
|
|
1932
|
+
url: typeof frame.url === 'string' ? frame.url : undefined };
|
|
1912
1933
|
};
|
|
1913
1934
|
/**
|
|
1914
1935
|
* Which frame a paused document request navigates, as documentFrame reads it, without the frame tree where
|
|
@@ -1924,6 +1945,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1924
1945
|
return 'frame';
|
|
1925
1946
|
if (sessionId === pageSessionId && pageTargetId !== undefined && frameId === pageTargetId)
|
|
1926
1947
|
return 'top';
|
|
1948
|
+
// An adopted window's main frame is that window's own top-level document, and its frame tree is as unanswered.
|
|
1949
|
+
if (sessionId && windowTargets.get(sessionId) === frameId)
|
|
1950
|
+
return 'top';
|
|
1927
1951
|
return (await documentFrame(sessionId, frameId))?.navigation ?? null;
|
|
1928
1952
|
};
|
|
1929
1953
|
/**
|
|
@@ -2117,6 +2141,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2117
2141
|
let lastSubmitted = null;
|
|
2118
2142
|
// The approved Datatrans answers, per frameId: the repeat rule (datatrans.ts).
|
|
2119
2143
|
const datatransRepeats = new DatatransRepeats();
|
|
2144
|
+
// The approved TokenEx iframe answers, per frameId (the card-number frame is an out-of-process iframe whose own
|
|
2145
|
+
// session pauses its post; its frameId names it): the repeat rule (tokenex-iframe.ts).
|
|
2146
|
+
const tokenexIframeRepeats = new TokenexIframeRepeats();
|
|
2120
2147
|
// The card product each Worldline MyCheckout hosted checkout chose (worldline-mycheckout.ts).
|
|
2121
2148
|
const worldlineProducts = new WorldlineMyCheckoutProducts();
|
|
2122
2149
|
// The Shiji STS getCvvId companion of the getTokenId this attachment last continued (shiji-sts.ts).
|
|
@@ -2161,11 +2188,14 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2161
2188
|
// EBANX's 3-D Secure calls and hosted confirm, which send the typed card again
|
|
2162
2189
|
// after its token: the cardholder's device sends one the approved token request
|
|
2163
2190
|
// covers, and any other is refused (see ebanx.ts).
|
|
2191
|
+
// ComGate's card hosts are armed only for an org the API served ComGate to, so the
|
|
2192
|
+
// gate can fail every card post there while the gateway link pauses (see comgate.ts).
|
|
2164
2193
|
const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
|
|
2165
2194
|
...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
|
|
2166
2195
|
...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
|
|
2167
2196
|
...(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
|
|
2197
|
+
...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS,
|
|
2198
|
+
...(servesComgate(opts.vault) ? COMGATE_CARD_PATTERNS : [])])];
|
|
2169
2199
|
// The hold-only rules' patterns (Bolt's tokenizer; every request a placeholder card can ride, by resource type).
|
|
2170
2200
|
const fetchPatterns = [...urlPatterns.map((urlPattern) => ({ urlPattern, requestStage: 'Request' })),
|
|
2171
2201
|
...(holdGate ? holdGate.patterns().map((p) => ({ ...p, requestStage: 'Request' })) : [])];
|
|
@@ -2294,6 +2324,55 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2294
2324
|
lifecycle.failed(new PaymentOutcomeUnknownError(lifecycle.getState().authorizationId, 'browser_interception_unavailable'));
|
|
2295
2325
|
opts.onEvent?.({ type: 'failed', detail: 'browser_interception_unavailable' });
|
|
2296
2326
|
};
|
|
2327
|
+
/**
|
|
2328
|
+
* A window the merchant's page opened (window.open, or a form or link aimed at a new window), or one such a window
|
|
2329
|
+
* opened in turn, in the merchant's own browser context: it belongs to this checkout, so it is attached and armed
|
|
2330
|
+
* as the page is, and its requests reach the same gates. Chromium reports the window (Target.targetCreated, with its
|
|
2331
|
+
* openerId) as it starts, and the page session's auto-attach never covers a new page target. The attach races the
|
|
2332
|
+
* window's first request: a window that opens on the merchant's page and is sent on to a hosted page by script or
|
|
2333
|
+
* by the merchant's redirect is armed before that entry; a window opened straight onto a hosted page can load it
|
|
2334
|
+
* first, as every window did before. A window reported while this attachment still arms the page waits in
|
|
2335
|
+
* pendingWindows and is adopted once the page is armed, inside the setup deadline and before the attachment reports
|
|
2336
|
+
* ready, its opener judged then, so a window that window opened is adopted after it. A window that is gone before it
|
|
2337
|
+
* could be attached was never armed, as before; one attached but not armed fails the attachment, as a frame does.
|
|
2338
|
+
*/
|
|
2339
|
+
const openedWindows = new Set();
|
|
2340
|
+
const pendingWindows = [];
|
|
2341
|
+
const adoptOpenedWindow = async (info, duringSetup = false) => {
|
|
2342
|
+
if (!info || info.type !== 'page' || typeof info.targetId !== 'string' || !info.targetId || info.targetId === pageTargetId
|
|
2343
|
+
|| openedWindows.has(info.targetId) || info.browserContextId !== merchantContext)
|
|
2344
|
+
return;
|
|
2345
|
+
if (setupStop.signal.aborted)
|
|
2346
|
+
return;
|
|
2347
|
+
if (!attachmentReady && !duringSetup) {
|
|
2348
|
+
pendingWindows.push(info);
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
if (typeof info.openerId !== 'string' || !(info.openerId === pageTargetId || openedWindows.has(info.openerId)))
|
|
2352
|
+
return;
|
|
2353
|
+
openedWindows.add(info.targetId);
|
|
2354
|
+
let child;
|
|
2355
|
+
try {
|
|
2356
|
+
child = (await cdp.send('Target.attachToTarget', { targetId: info.targetId, flatten: true }))?.sessionId;
|
|
2357
|
+
}
|
|
2358
|
+
catch {
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
try {
|
|
2362
|
+
if (typeof child !== 'string' || !child || ownedSessions.has(child) || detachedSessions.has(child) || rejectedSessions.has(child)) {
|
|
2363
|
+
throw new CheckoutAttachmentError('unavailable');
|
|
2364
|
+
}
|
|
2365
|
+
ownedSessions.add(child);
|
|
2366
|
+
sessionTypes.set(child, 'page');
|
|
2367
|
+
sessionParents.set(child, pageSessionId);
|
|
2368
|
+
windowTargets.set(child, info.targetId);
|
|
2369
|
+
await arm(child);
|
|
2370
|
+
opts.onEvent?.({ type: 'window_armed', detail: {} });
|
|
2371
|
+
}
|
|
2372
|
+
catch (error) {
|
|
2373
|
+
failInterception(error);
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2297
2376
|
cdp.on(async (method, params, sessionId) => {
|
|
2298
2377
|
// A frame that committed a new document drops the companion binding made for the getTokenId its
|
|
2299
2378
|
// old document sent, with the device's CVV ciphertext: the document that replaced it never
|
|
@@ -2304,6 +2383,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2304
2383
|
shijiCompanions.releasePage((page) => page === replaced);
|
|
2305
2384
|
}
|
|
2306
2385
|
if ((method === 'Target.targetCreated' || method === 'Target.targetInfoChanged') && targetContextKnown) {
|
|
2386
|
+
// The page's own target reports each document it commits from discovery on, Page events or not.
|
|
2387
|
+
notePageTarget(params?.targetInfo);
|
|
2307
2388
|
try {
|
|
2308
2389
|
assertContextTarget(params?.targetInfo, merchantContext, targetAllowance);
|
|
2309
2390
|
if (params?.targetInfo?.type === 'shared_worker')
|
|
@@ -2312,8 +2393,18 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2312
2393
|
catch (error) {
|
|
2313
2394
|
failInterception(error);
|
|
2314
2395
|
}
|
|
2396
|
+
if (method === 'Target.targetCreated')
|
|
2397
|
+
await adoptOpenedWindow(params?.targetInfo);
|
|
2315
2398
|
return;
|
|
2316
2399
|
}
|
|
2400
|
+
// The page's main frame committed a document, or moved within it: the URL a held top-level navigation leaves.
|
|
2401
|
+
if (sessionId === pageSessionId && method === 'Page.frameNavigated' && !params.frame?.parentId && typeof params.frame?.url === 'string') {
|
|
2402
|
+
committedTopUrl = params.frame.url;
|
|
2403
|
+
}
|
|
2404
|
+
if (sessionId === pageSessionId && method === 'Page.navigatedWithinDocument' && pageTargetId !== undefined
|
|
2405
|
+
&& params?.frameId === pageTargetId && typeof params?.url === 'string') {
|
|
2406
|
+
committedTopUrl = params.url.split('#')[0];
|
|
2407
|
+
}
|
|
2317
2408
|
if (((method === 'Page.frameNavigated' && !params.frame?.parentId) || (method === 'Page.navigatedWithinDocument' && preparationFrameId && params.frameId === preparationFrameId)) && sessionId === pageSessionId) {
|
|
2318
2409
|
preparationGate.invalidate('merchant_document_changed');
|
|
2319
2410
|
mercadoCheckout.invalidate();
|
|
@@ -2353,10 +2444,11 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2353
2444
|
}
|
|
2354
2445
|
// A watched shared worker sent a card request: it went out unpaused, so nothing is approved from here on.
|
|
2355
2446
|
if (method === 'Network.requestWillBeSent' && sessionId && watchedWorkerSessions.has(sessionId) && sharedWorkerCardRequest(params?.request)) {
|
|
2447
|
+
// Origin and path as redactUrl reads them, so a credential a processor writes into the path never shows.
|
|
2356
2448
|
let endpoint = '';
|
|
2357
2449
|
try {
|
|
2358
|
-
|
|
2359
|
-
endpoint =
|
|
2450
|
+
new URL(params.request.url);
|
|
2451
|
+
endpoint = redactUrl(params.request.url);
|
|
2360
2452
|
}
|
|
2361
2453
|
catch { /* unreadable */ }
|
|
2362
2454
|
opts.onEvent?.({ type: 'card_request_unwatched', detail: { endpoint, from: 'shared_worker' } });
|
|
@@ -2641,6 +2733,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2641
2733
|
const { requestId, request, resourceType, networkId, frameId } = params;
|
|
2642
2734
|
// When this request paused: the merchant's total is read as of this moment.
|
|
2643
2735
|
const pausedAt = Date.now();
|
|
2736
|
+
// The page's own top-level navigation, paused: its frame tree cannot be read until this request is decided, so its
|
|
2737
|
+
// document is the one it last committed (committedTopUrl). Every other request reads the frame tree as before.
|
|
2738
|
+
const readPausedDocument = resourceType === 'Document' && sessionId === pageSessionId && pageTargetId !== undefined
|
|
2739
|
+
&& frameId === pageTargetId && committedTopUrl !== undefined ? async () => committedTopUrl : readDocumentUrl;
|
|
2644
2740
|
// A paused document request has not committed either: it only starts the context's check.
|
|
2645
2741
|
if (resourceType === 'Document')
|
|
2646
2742
|
microforms.warm(withFragment(request));
|
|
@@ -2723,6 +2819,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2723
2819
|
cybersourceFlex: { judge: (flexRequest) => microforms.judge(flexRequest, frameId, () => rereadMicroforms(sessionId, frameId)) },
|
|
2724
2820
|
juspay: juspayCheckout,
|
|
2725
2821
|
datatrans: datatransRepeats,
|
|
2822
|
+
tokenexIframe: tokenexIframeRepeats,
|
|
2726
2823
|
ebanx: ebanxFollowUps,
|
|
2727
2824
|
frame: typeof frameId === 'string' && frameId ? frameId : undefined,
|
|
2728
2825
|
payrails,
|
|
@@ -2794,7 +2891,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2794
2891
|
if (awaitingApproval && activeRequest?.attempt.lost && !terminal && !setupStop.signal.aborted && !lifecycle.isCancelled()
|
|
2795
2892
|
&& typeof networkId === 'string' && networkId) {
|
|
2796
2893
|
const retryBody = pausedBody(request);
|
|
2797
|
-
const retry = retryBody === null ? null : requestIdentity(opts, request.url, request.method, retryBody, documentKey(await documentUrlOf(
|
|
2894
|
+
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
2895
|
// A prepared approval is answered only in the card frame it was bound in: the same request from
|
|
2799
2896
|
// another frame (a replacement for a removed one, or a second card frame) is refused below.
|
|
2800
2897
|
if (retry && activeRequest?.attempt.lost && sameCheckoutRequest(activeRequest.identity, retry)
|
|
@@ -2948,7 +3045,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2948
3045
|
await preparationGate.assertDocument();
|
|
2949
3046
|
// One bounded read of the document serves both the merchant origin and
|
|
2950
3047
|
// the identity a retry has to repeat (see sameCheckoutRequest).
|
|
2951
|
-
const documentUrl = await documentUrlOf(
|
|
3048
|
+
const documentUrl = await documentUrlOf(readPausedDocument, attempt.signal);
|
|
2952
3049
|
const pageOrigin = pageOriginFrom(documentUrl);
|
|
2953
3050
|
// The agent's amount is a card endpoint's only authority: no page total is read.
|
|
2954
3051
|
const pageAmount = cardEndpoint ? undefined : await pageAmountOf(opts);
|
|
@@ -3240,6 +3337,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3240
3337
|
}, deliverOn);
|
|
3241
3338
|
// The frame that received it: the paused request's, or its retry's once rebound.
|
|
3242
3339
|
datatransRepeats.remember(activeRequest?.frameId || undefined, deliverRequest.url, deliverBody, replay);
|
|
3340
|
+
tokenexIframeRepeats.remember(activeRequest?.frameId || undefined, deliverRequest.url, deliverBody, replay);
|
|
3243
3341
|
attempt.assertLive();
|
|
3244
3342
|
// An ACI card-number request answered: its checkout's held CVC and expiry requests get the
|
|
3245
3343
|
// same answer, and its /payment submission may continue (see AciGate).
|
|
@@ -3338,6 +3436,11 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3338
3436
|
targetContextKnown = true;
|
|
3339
3437
|
// A page target's id is its main frame's id: a navigation of that frame is the page's own (pausedNavigation).
|
|
3340
3438
|
pageTargetId = typeof targetInfo.targetId === 'string' && targetInfo.targetId ? targetInfo.targetId : undefined;
|
|
3439
|
+
// The page's URL as its target reports it: the document a held top-level navigation leaves until the page commits
|
|
3440
|
+
// another one (the Page.frameNavigated branch above, and notePageTarget for a commit before the Page events are on).
|
|
3441
|
+
// No frame tree read: a navigation the page starts while this attachment arms would hold it.
|
|
3442
|
+
if (committedTopUrl === undefined && typeof targetInfo.url === 'string' && targetInfo.url)
|
|
3443
|
+
committedTopUrl = targetInfo.url.split('#')[0];
|
|
3341
3444
|
await cdp.send('Target.setDiscoverTargets', { discover: true });
|
|
3342
3445
|
assertActive();
|
|
3343
3446
|
const { targetInfos } = await cdp.send('Target.getTargets');
|
|
@@ -3346,6 +3449,9 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3346
3449
|
throw new CheckoutAttachmentError('unavailable');
|
|
3347
3450
|
for (const target of targetInfos)
|
|
3348
3451
|
assertContextTarget(target, merchantContext, targetAllowance);
|
|
3452
|
+
// The page may have committed another document since its TargetInfo was read; the list names the one it holds.
|
|
3453
|
+
for (const target of targetInfos)
|
|
3454
|
+
notePageTarget(target);
|
|
3349
3455
|
for (const target of targetInfos)
|
|
3350
3456
|
if (target?.type === 'shared_worker')
|
|
3351
3457
|
await watchSharedWorker(target);
|
|
@@ -3354,13 +3460,24 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
3354
3460
|
// but their Fetch setup finishes later. Do not invite the caller to press
|
|
3355
3461
|
// Pay while one of those already-running frames is still unprotected, or
|
|
3356
3462
|
// while a shared worker reported during setup is not yet watched.
|
|
3357
|
-
while
|
|
3358
|
-
|
|
3463
|
+
// A window the page opened while this attachment armed it is attached and armed here too, in the order Chromium
|
|
3464
|
+
// reported it (adoptOpenedWindow), and the frames and workers such a window brings are waited for as the page's
|
|
3465
|
+
// own are, all under the setup deadline. One that cannot be armed fails the attachment.
|
|
3466
|
+
while (arming.size || workerWatches.size || pendingWindows.length) {
|
|
3467
|
+
if (arming.size || workerWatches.size)
|
|
3468
|
+
await Promise.all([...arming.values(), ...workerWatches.values()]);
|
|
3469
|
+
else
|
|
3470
|
+
await adoptOpenedWindow(pendingWindows.shift(), true);
|
|
3471
|
+
assertActive();
|
|
3472
|
+
}
|
|
3359
3473
|
assertActive();
|
|
3360
3474
|
}, setupTimeoutMs, setupStop.signal);
|
|
3361
3475
|
if (setupStop.signal.aborted)
|
|
3362
3476
|
throw attachmentFailure(setupStop.signal.reason);
|
|
3363
3477
|
attachmentReady = true;
|
|
3478
|
+
// A window reported after that drain, before this line, is adopted the way a later window is.
|
|
3479
|
+
for (const info of pendingWindows.splice(0))
|
|
3480
|
+
void adoptOpenedWindow(info);
|
|
3364
3481
|
opts.onEvent?.({ type: 'fetch_armed', detail: { patterns: urlPatterns, ...(holdGate ? { hold: true, holdRules: holdGate.rules.map((r) => r.psp) } : {}), ...(bypassServiceWorkers ? { serviceWorkers: 'bypass' } : {}) } });
|
|
3365
3482
|
}
|
|
3366
3483
|
catch (error) {
|
|
@@ -3700,6 +3817,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3700
3817
|
let lastSubmitted = null;
|
|
3701
3818
|
// The approved Datatrans answers, per Frame: the repeat rule (datatrans.ts).
|
|
3702
3819
|
const datatransRepeats = new DatatransRepeats();
|
|
3820
|
+
// The approved TokenEx iframe answers, per Frame: the repeat rule (tokenex-iframe.ts).
|
|
3821
|
+
const tokenexIframeRepeats = new TokenexIframeRepeats();
|
|
3703
3822
|
// The card product each Worldline MyCheckout hosted checkout chose (worldline-mycheckout.ts).
|
|
3704
3823
|
const worldlineProducts = new WorldlineMyCheckoutProducts();
|
|
3705
3824
|
const frameOf = (request) => { try {
|
|
@@ -3720,8 +3839,10 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3720
3839
|
// The matcher sees only the URL. Worldpay eProtect's registration is a GET, so its family and
|
|
3721
3840
|
// the vendor's give-up reports are routed by the eProtect gate (never its scripts).
|
|
3722
3841
|
// 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.
|
|
3842
|
+
// pre-claim gates then pause only the PUT and continue the URL's other methods. A hosted page
|
|
3843
|
+
// entered with a plain GET (hosted-entry.ts, ComGate's gateway link) is routed by its exact link.
|
|
3724
3844
|
(url) => opts.vault.isCardRequest(url.toString()) || opts.vault.isCardRequest(url.toString(), 'PUT')
|
|
3845
|
+
|| opts.vault.isCardRequest(url.toString(), 'GET')
|
|
3725
3846
|
|| guards.matches(url.toString()) || stripeCheckout.matches(url.toString()) || mercadoCheckout.matches(url.toString())
|
|
3726
3847
|
|| eprotect.matches(url.toString())
|
|
3727
3848
|
// An armed merchant profile's endpoint or a sibling of it (merchant-hosted.ts), as attachToCdp arms it.
|
|
@@ -3739,9 +3860,16 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3739
3860
|
|| aci.matches(url.toString())
|
|
3740
3861
|
// Every Payrails host, routed only for an org the API served Payrails to (see attachToCdp).
|
|
3741
3862
|
|| (payrails !== null && isPayrailsHost(url.toString()))
|
|
3863
|
+
// Every request to Przelewy24's pay wall hosts once its entry is served (przelewy24.ts), as attachToCdp arms
|
|
3864
|
+
// the hosts: the card method's post, the card form's post and the Saferpay resolver reach the Przelewy24
|
|
3865
|
+
// gate and fail.
|
|
3866
|
+
|| isArmedPrzelewy24Url(opts.vault, url.toString())
|
|
3742
3867
|
// Every request to an NMI host whose entry is served (ekashu.ts), as attachToCdp arms the
|
|
3743
3868
|
// host: the card page's post and its 3-D Secure calls reach the NMI gate and fail.
|
|
3744
3869
|
|| isArmedEkashuUrl(opts.vault, url.toString())
|
|
3870
|
+
// Every request to a Webpay host once its entry is served (transbank.ts), as attachToCdp arms the hosts: the
|
|
3871
|
+
// token's submit and Webpay's card steps reach the Webpay gate and fail, and the entry pauses.
|
|
3872
|
+
|| isArmedTransbankUrl(opts.vault, url.toString())
|
|
3745
3873
|
// A CreditGuard page's own calls beside its submit (its card lookup and its 3-D Secure calls), for an
|
|
3746
3874
|
// org the API served CreditGuard to, as attachToCdp arms its hosts: the CreditGuard gate answers them
|
|
3747
3875
|
// under a ready preparation and lets them go on with none (pre-claim.ts isArmedCreditguardCall).
|
|
@@ -3752,6 +3880,12 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3752
3880
|
// Every request to QuickPay's host once its entry is served (quickpay.ts), as attachToCdp arms the host:
|
|
3753
3881
|
// the window's card post, its other card requests and its card lookup reach the QuickPay gate and fail.
|
|
3754
3882
|
|| isArmedQuickpayUrl(opts.vault, url.toString())
|
|
3883
|
+
// Every request to one of ComGate's card hosts once its entry is served (comgate.ts), as attachToCdp arms them:
|
|
3884
|
+
// the gateway's card post and every other card call there reach the ComGate gate and fail.
|
|
3885
|
+
|| isArmedComgateUrl(opts.vault, url.toString())
|
|
3886
|
+
// Every request on the TokenEx iframe's /iframe/v3 path once its entry is served (tokenex-iframe.ts), as attachToCdp
|
|
3887
|
+
// arms its hosts: a card post the recognizer's exact URL does not match reaches the TokenEx gate and fails.
|
|
3888
|
+
|| isArmedTokenexIframeUrl(opts.vault, url.toString())
|
|
3755
3889
|
// PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
|
|
3756
3890
|
|| isPaypalHostedFieldsConfirm(url.toString())
|
|
3757
3891
|
// PayPal's Card Fields confirm and GraphQL on www.paypal.com, for every org, as attachToCdp arms them (paypal-card-forms.ts).
|
|
@@ -3857,6 +3991,7 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3857
3991
|
} },
|
|
3858
3992
|
juspay: juspayCheckout,
|
|
3859
3993
|
datatrans: datatransRepeats,
|
|
3994
|
+
tokenexIframe: tokenexIframeRepeats,
|
|
3860
3995
|
ebanx: ebanxFollowUps,
|
|
3861
3996
|
frame: frameOf(request),
|
|
3862
3997
|
payrails,
|
|
@@ -4243,6 +4378,7 @@ export async function attachToPlaywright(page, opts) {
|
|
|
4243
4378
|
}
|
|
4244
4379
|
await deliverTo.fulfill({ status: replay.status, headers: withCorsHeaders(replay.headers, cors.headers), body: replay.body });
|
|
4245
4380
|
datatransRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
|
|
4381
|
+
tokenexIframeRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
|
|
4246
4382
|
assertRequestLive();
|
|
4247
4383
|
// See the same step in attachToCdp.
|
|
4248
4384
|
aci.answered(deliverRequest.url(), { status: replay.status, headers: replay.headers, body: replay.body }, replay.authorizationId);
|
package/dist/client.d.ts
CHANGED
|
@@ -242,6 +242,13 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
242
242
|
* checkout relay. Served on the same terms: only to a build that lists it, for a company
|
|
243
243
|
* Agentcard turned Payfast on for.
|
|
244
244
|
*
|
|
245
|
+
* `przelewy24_hosted_payment_page`: Przelewy24's pay wall, whose card-free link
|
|
246
|
+
* (secure.przelewy24.pl/trnRequest/<token>, a plain GET the merchant's redirect or script sends
|
|
247
|
+
* the buyer to) this build pauses for the cardholder's device by its exact link (hosted-entry.ts),
|
|
248
|
+
* and whose card method post, card form post and Saferpay resolver this build never lets the
|
|
249
|
+
* agent's browser send (pre-claim.ts, przelewy24.ts). Served on the same terms as NMI's and
|
|
250
|
+
* Redsys's: only to a build that lists it, for a company Agentcard turned Przelewy24 on for.
|
|
251
|
+
*
|
|
245
252
|
* `wix_payments_tokenize`: the card request Wix Cashier's card frame sends to Wix's
|
|
246
253
|
* tokenizer (cashier-pci.wixapps.net), only through a prepared checkout: Wix's payment
|
|
247
254
|
* widget fails the payment 30 seconds after it asks the frame for a token. The
|
|
@@ -258,6 +265,15 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
258
265
|
* the checkout relay. Served on the same terms: only to a build that lists it, for a company
|
|
259
266
|
* Agentcard turned PayMongo on for.
|
|
260
267
|
*
|
|
268
|
+
* `gopay_card_form`: the card post GoPay's card frame sends to its own URL
|
|
269
|
+
* (secure.gopay.com/gp-card-comm/h/<token>) inside the GoPay gateway a shop opens. This build pauses
|
|
270
|
+
* only the frame's own card post and answers it with GoPay's answer as the API recorded it, the
|
|
271
|
+
* empty JSON object the frame reads; the gateway then reads the payment's end from GoPay and runs
|
|
272
|
+
* 3-D Secure in the agent's browser when the bank asks for it. The cardholder's device sends the card
|
|
273
|
+
* through the checkout relay. Served only to a build that lists it, and only for Agentcard's proof
|
|
274
|
+
* company until a reviewed change binds GoPay's payee and amount to the approval: the post names
|
|
275
|
+
* neither. The API takes it only at a shop Agentcard reviewed.
|
|
276
|
+
*
|
|
261
277
|
* `hipay_vault_token`: the token create HiPay's hosted fields send to HiPay's Secure Vault
|
|
262
278
|
* (secure2-vault.hipay-tpp.com, stage-secure2-vault for a test row), only through a prepared
|
|
263
279
|
* checkout: the merchant's page gives the frame 20 seconds after Pay. The cardholder's device
|
|
@@ -279,6 +295,84 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
279
295
|
* Secure step and card lookup this build never lets the agent's browser send (pre-claim.ts,
|
|
280
296
|
* quickpay.ts). Served on the same terms as NMI's and Redsys's: only to a build that lists it, for a
|
|
281
297
|
* company Agentcard turned QuickPay on for.
|
|
298
|
+
*
|
|
299
|
+
* `revolut_card_field`: the card payment Revolut Business's card field or card popup sends from
|
|
300
|
+
* its frame to /api/public/orders/<order token>/payments on merchant.revolut.com, on a merchant's
|
|
301
|
+
* own page or Revolut's hosted checkout. This build pauses only the frame's card payment and
|
|
302
|
+
* answers it with Revolut's answer as the API recorded it: the payment Revolut created, which the
|
|
303
|
+
* frame then follows, through any 3-D Secure step, in the agent's browser. A payment by Apple Pay,
|
|
304
|
+
* Google Pay or Revolut Pay on the same URL carries no card and continues untouched. Revolut's
|
|
305
|
+
* host answers the Vault's origin, so the cardholder's device sends the card itself, with no relay.
|
|
306
|
+
* Served on the same terms: only to a build that lists it, for a company Agentcard turned Revolut
|
|
307
|
+
* on for.
|
|
308
|
+
*
|
|
309
|
+
* `pay_nl_card_token`: the card token request a shop's card form sends to Pay. (pay.nl),
|
|
310
|
+
* Weeztix's sandboxed frame posting its FormData to payment.pay.nl/v1/Payment/getCardToken/json.
|
|
311
|
+
* This build pauses only the form's own multipart body at that exact URL; the cardholder's
|
|
312
|
+
* device sends the card through the checkout relay, and the frame gets the token answer
|
|
313
|
+
* payment-core's judge rebuilt, with the CORS answer for the frame's own origin, null. The
|
|
314
|
+
* request names no amount, so the agent's pair, above zero, is the one the cardholder approves
|
|
315
|
+
* (PAY_NL_PSP below). Served on the same terms: only to a build that lists it, for a company
|
|
316
|
+
* Agentcard turned Pay. on for.
|
|
317
|
+
*
|
|
318
|
+
* `paypal_card_fields`: PayPal Card Fields' confirm, the POST its v5 frame sends to
|
|
319
|
+
* www.paypal.com and its v6 frame to api-m.paypal.com. This build pauses it only in
|
|
320
|
+
* the frames' own shape, refuses one that asks PayPal to keep the card or forces
|
|
321
|
+
* 3-D Secure in the browser with a `blocked` event, and answers the frame with the
|
|
322
|
+
* order's id and status as the API recorded them. The device sends a v5 confirm
|
|
323
|
+
* through the checkout relay and a v6 one directly. Served on the same terms: only
|
|
324
|
+
* to a build that lists it, for a company Agentcard turned PayPal Card Fields on
|
|
325
|
+
* for. Every other company keeps the checkout guard's refusal (paypal-card-forms.ts).
|
|
326
|
+
*
|
|
327
|
+
* `comgate_hosted_payment_page`: ComGate's payment gateway, whose card-free gateway link
|
|
328
|
+
* (payN.comgate.cz/init?id=<transaction id>, a plain GET) this build pauses for the
|
|
329
|
+
* cardholder's device by its exact link (hosted-entry.ts), in the page, in a frame of it or
|
|
330
|
+
* in a window it opened, and whose card hosts this build never lets the agent's browser post
|
|
331
|
+
* to (pre-claim.ts, comgate.ts). Served on the same terms as NMI's and Redsys's: only to a
|
|
332
|
+
* build that lists it, for a company Agentcard turned ComGate on for.
|
|
333
|
+
*
|
|
334
|
+
* `makecommerce_card_frame`: the card post MakeCommerce's card frame sends to its own card endpoint
|
|
335
|
+
* (cc.maksekeskus.ee/card-payment) under the shop's publishable key. This build pauses only the
|
|
336
|
+
* frame's own one-time card payment and answers it with MakeCommerce's answer as the API recorded
|
|
337
|
+
* it: the URL of MakeCommerce's 3-D Secure page, which the frame then opens in the agent's browser.
|
|
338
|
+
* The cardholder's device sends the card through the checkout relay. Served on the same terms: only
|
|
339
|
+
* to a build that lists it, for a company Agentcard turned MakeCommerce on for.
|
|
340
|
+
*
|
|
341
|
+
* `poynt_collect_nonce`: GoDaddy Payments' card request, the POST Poynt Collect's
|
|
342
|
+
* card frame sends to a store's generate-nonce on services.poynt.net. This build
|
|
343
|
+
* pauses it only as the frame writes it, refuses a body that asks Poynt to keep the
|
|
344
|
+
* card in the browser with a `blocked` event, and answers the frame with the nonce as
|
|
345
|
+
* the API recorded it. The device sends the card through the checkout relay. Served
|
|
346
|
+
* on the same terms: only to a build that lists it, for a company Agentcard turned
|
|
347
|
+
* GoDaddy Payments on for.
|
|
348
|
+
*
|
|
349
|
+
* `tokenex_iframe_tokenize`: the card post TokenEx's hosted iframe v3 sends from its card-number frame
|
|
350
|
+
* to its own document URL on htp.tokenex.com (JetBlue's card form). This build pauses only the PCI frame's
|
|
351
|
+
* own card post with its CVV frame, refuses in the browser every other post on that path that carries a
|
|
352
|
+
* card (tokenex-iframe.ts), answers it with TokenEx's token as the API recorded it, rebuilt from
|
|
353
|
+
* payment-core's allow-list, and answers the same frame's byte-identical repeat from that answer while the
|
|
354
|
+
* approval is being handed off. Served on the same terms: only to a build that lists it, for a company
|
|
355
|
+
* Agentcard turned the TokenEx iframe on for.
|
|
356
|
+
*
|
|
357
|
+
* `transbank_webpay_hosted_payment_page`: Transbank's Webpay Plus, whose card-free entry
|
|
358
|
+
* (webpay3g.transbank.cl/webpayserver/initTransaction?token_ws=<token>, the merchant's
|
|
359
|
+
* redirect, a plain GET) this build pauses for the cardholder's device by its exact link
|
|
360
|
+
* (hosted-entry.ts) in the page or a frame of it, and with attachToCdp also in a window the
|
|
361
|
+
* page opened, and whose hosts this build never lets the agent's browser post to (pre-claim.ts,
|
|
362
|
+
* transbank.ts), so the token is never spent there. attachToPlaywright covers the page it is
|
|
363
|
+
* attached to and that page's frames only, and pauses only a direct or scripted navigation to
|
|
364
|
+
* the entry: its page.route sees no HTTP redirect hop, so a merchant's redirect needs attachToCdp.
|
|
365
|
+
* Served on the same terms as NMI's and Redsys's: only to a build that lists it, for a company
|
|
366
|
+
* Agentcard turned Webpay on for.
|
|
367
|
+
*
|
|
368
|
+
* `sumup_checkout_process`: the card request SumUp's Payment Widget sends from its hidden form frame
|
|
369
|
+
* on gateway.sumup.com, a PUT to https://api.sumup.com/v0.2/checkouts/<checkout id>. This build pauses
|
|
370
|
+
* that PUT as it pauses Gravy's, so only the CDP and Playwright adapters see it (the form frame is
|
|
371
|
+
* cross-origin to the page), and answers the frame with SumUp's own answer as the API recorded it: the
|
|
372
|
+
* paid checkout, or the 3-D Secure step the widget then runs in the agent's browser. The cardholder's
|
|
373
|
+
* device sends the card itself, and the API holds the payment to a merchant Agentcard reviewed, at the
|
|
374
|
+
* amount SumUp holds for the checkout. Served on the same terms: only to a build that lists it, for a
|
|
375
|
+
* company Agentcard turned SumUp on for.
|
|
282
376
|
*/
|
|
283
377
|
export declare const SUPPORTED_CAPABILITIES: readonly string[];
|
|
284
378
|
/**
|
|
@@ -838,7 +932,10 @@ export interface PlaceholderCard {
|
|
|
838
932
|
/**
|
|
839
933
|
* The placeholder number for a network (lowercase, as GET /api/v2/vault_cards names it) on a
|
|
840
934
|
* processor's page: the processor's own number for that network when `overrides` names one, else
|
|
841
|
-
* the network's default, else undefined for a network with no placeholder.
|
|
935
|
+
* the network's default, else undefined for a network with no placeholder. On a TokenEx iframe
|
|
936
|
+
* (`tokenex_iframe`) the number must also be one TokenEx's frame reads as that network, since the frame
|
|
937
|
+
* names the brand to the page from the typed digits and the cardholder's device refuses a mismatch: the
|
|
938
|
+
* frame's table takes Visa, Mastercard and American Express, so every other network gets undefined.
|
|
842
939
|
*/
|
|
843
940
|
export declare function placeholderNumber(brand: string, psp?: string, overrides?: Readonly<Record<string, Readonly<Record<string, string>>>>): string | undefined;
|
|
844
941
|
/**
|
|
@@ -861,7 +958,9 @@ export declare class PreparationRequiredError extends Error {
|
|
|
861
958
|
* when the merchant's server created the payment), so the `amount` and
|
|
862
959
|
* `currency` you pass are what the cardholder approves, and for WSPay the
|
|
863
960
|
* figure the API compares with WSPay's answer when that answer names one (a
|
|
864
|
-
* 3-D Secure step names none).
|
|
961
|
+
* 3-D Secure step names none). A Revolut card payment names the order's
|
|
962
|
+
* amount but not its currency, so it takes the same pair, above zero.
|
|
963
|
+
* Thrown locally before any create or
|
|
865
964
|
* prompt, and terminal: the same request without an amount fails the same
|
|
866
965
|
* way. The API answers 400 amount_required for the same case. A page total
|
|
867
966
|
* (`pageAmount`) does not count.
|
|
@@ -1482,7 +1581,9 @@ export declare class VaultClient {
|
|
|
1482
1581
|
* The facts hold only while the cardholder pays with that card. `psp` names the processor
|
|
1483
1582
|
* whose card form the agent types into (the recognizer's name, `worldline_mycheckout`): a
|
|
1484
1583
|
* processor that needs its own number for the card's network gets it, and every other gets the
|
|
1485
|
-
* network's default.
|
|
1584
|
+
* network's default. On a TokenEx iframe (`tokenex_iframe`, JetBlue) the number is one TokenEx's frame
|
|
1585
|
+
* reads as the card's own network, Visa, Mastercard or American Express, and any other network has no
|
|
1586
|
+
* placeholder there. Throws PlaceholderCardError when there is no such card, its network has no
|
|
1486
1587
|
* placeholder, or it carries no expiry.
|
|
1487
1588
|
*/
|
|
1488
1589
|
placeholderCard(input: {
|