@agent-cards/sdk 0.23.9 → 0.23.10
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 +40 -0
- package/PREFLIGHT.md +7 -5
- package/README.md +181 -92
- package/dist/aci.d.ts +7 -5
- package/dist/aci.generated.d.ts +2 -1
- package/dist/aci.generated.js +14 -4
- package/dist/aci.js +28 -7
- package/dist/amadeus-checkout.generated.js +2 -2
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/card-fields.generated.js +163 -9
- package/dist/cdp.js +79 -34
- package/dist/client.d.ts +61 -31
- package/dist/client.js +99 -42
- package/dist/creditguard.generated.d.ts +1 -1
- package/dist/creditguard.generated.js +1 -3
- package/dist/cse-body.d.ts +7 -6
- package/dist/cse-body.js +8 -7
- package/dist/datatrans.generated.js +8 -5
- package/dist/elavon-converge.d.ts +1 -1
- package/dist/elavon-converge.generated.js +7 -4
- package/dist/elavon-converge.js +4 -3
- package/dist/fiserv.d.ts +9 -5
- package/dist/fiserv.generated.d.ts +3 -3
- package/dist/fiserv.generated.js +50 -20
- package/dist/fiserv.js +10 -6
- package/dist/payrails.d.ts +78 -18
- package/dist/payrails.generated.d.ts +12 -2
- package/dist/payrails.generated.js +213 -18
- package/dist/payrails.js +165 -44
- package/dist/pre-claim.js +51 -29
- package/dist/preflight-capabilities.generated.d.ts +0 -106
- package/dist/preflight-capabilities.generated.js +0 -153
- package/dist/preflight-catalog.json +61 -181
- package/dist/preflight-schemas.json +6 -6
- package/dist/preflight.generated.d.ts +0 -106
- package/dist/preflight.generated.js +27 -15
- package/dist/preparation.d.ts +2 -0
- package/dist/preparation.js +4 -2
- package/dist/prepared-processor.d.ts +19 -5
- package/dist/prepared-processor.js +21 -5
- package/dist/primer.generated.d.ts +5 -1
- package/dist/primer.generated.js +200 -33
- package/dist/registry.d.ts +18 -12
- package/dist/registry.js +11 -8
- package/dist/shiji-sts.d.ts +24 -22
- package/dist/shiji-sts.generated.d.ts +1 -1
- package/dist/shiji-sts.generated.js +12 -8
- package/dist/shiji-sts.js +49 -46
- package/dist/stripe-checkout.generated.js +146 -49
- package/dist/verifone.generated.js +192 -17
- package/dist/verifone.js +1 -1
- package/dist/wspay.d.ts +17 -16
- package/dist/wspay.generated.d.ts +6 -1
- package/dist/wspay.generated.js +13 -3
- package/dist/wspay.js +9 -11
- package/examples/preflight/kernel-native/README.md +1 -1
- package/examples/preflight/kernel-native/inventory.json +1 -19
- package/package.json +3 -3
package/dist/cdp.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BUILTIN_REGISTRY, cardUrlPatterns } from './registry.js';
|
|
2
2
|
import { AdyenTestPlatformRefusedError, AmountRequiredError, ApprovalDeclinedError, ApprovalTimeoutError, CardEncryptedError, CheckoutApiError, CheckoutCancelledError, CheckoutPreparationError, MerchantTotalError, PaymentOutcomeUnknownError, PreparationRequiredError, ProcessorRefusedError, UnsupportedCapabilityError, UnsupportedModeError, } from './client.js';
|
|
3
3
|
import { PreparationGate } from './preparation.js';
|
|
4
|
+
import { preparedApprovalAnswersRetry } from './prepared-processor.js';
|
|
4
5
|
import { StripeCheckoutGate } from './stripe-checkout.js';
|
|
5
6
|
import { MercadoCheckoutGate } from './mercado-checkout.js';
|
|
6
7
|
import { MERCADO_CHECKOUT_PATTERN } from './mercado-checkout.generated.js';
|
|
@@ -328,15 +329,15 @@ function cseAuthorizedDetail(replay, url, shijiCvv = null) {
|
|
|
328
329
|
* getCvvId companion right before the continue (shiji-sts.ts). It is written
|
|
329
330
|
* only into `approved`, the request the approval was created from, byte for
|
|
330
331
|
* byte, so the accessToken and keySequence are the ones the API reviewed, and
|
|
331
|
-
* the cvv is bound to the reviewed getCvvId keys the approval
|
|
332
|
-
* (`
|
|
333
|
-
* takes cseContinuationBody (cse-body.ts cseBody), and `shijiCvv` and
|
|
334
|
-
* `
|
|
332
|
+
* the cvv is bound to the SHA-256 of the reviewed getCvvId keys the approval
|
|
333
|
+
* served (`shijiCvvKeySequenceSha256`): no keys, no continuation. Every other
|
|
334
|
+
* processor takes cseContinuationBody (cse-body.ts cseBody), and `shijiCvv` and
|
|
335
|
+
* `shijiCvvKeySequenceSha256` are null.
|
|
335
336
|
*/
|
|
336
337
|
function cseContinuation(opts, url, body, replay, approved, preparation) {
|
|
337
338
|
const shiji = shijiStsTokenContinuation(url, body, replay.substitutions, approved, replay.kind === 'merchant_hosted' || replay.kind === 'verifone' ? undefined : replay.shijiStsCompanion);
|
|
338
|
-
return shiji ? { body: shiji.body, shijiCvv: shiji.cvv,
|
|
339
|
-
: { body: cseContinuationBody(opts, body, replay, url, preparation), shijiCvv: null,
|
|
339
|
+
return shiji ? { body: shiji.body, shijiCvv: shiji.cvv, shijiCvvKeySequenceSha256: shiji.cvvKeySequenceSha256 }
|
|
340
|
+
: { body: cseContinuationBody(opts, body, replay, url, preparation), shijiCvv: null, shijiCvvKeySequenceSha256: null };
|
|
340
341
|
}
|
|
341
342
|
/**
|
|
342
343
|
* The opaque page id a Shiji STS companion is bound to: the CDP session and
|
|
@@ -1287,7 +1288,10 @@ function sameCheckoutRequest(first, retry) {
|
|
|
1287
1288
|
* answer is a token or ciphertext, and any request for the same purchase can
|
|
1288
1289
|
* carry it. No for a hosted form (a navigation: there is no request to
|
|
1289
1290
|
* answer once the page has moved on), a prepared checkout (single use, bound
|
|
1290
|
-
* to one native request by design)
|
|
1291
|
+
* to one native request by design) other than a Bambora token create, whose
|
|
1292
|
+
* card frame gives up 5 seconds after sending it and whose page asks again
|
|
1293
|
+
* with the same body (preparedApprovalAnswersRetry), from that same card frame
|
|
1294
|
+
* only (sameFrame), a native Stripe Checkout step, a
|
|
1291
1295
|
* Mercado Pago request whose claim is bound to one document read, a Juspay
|
|
1292
1296
|
* /txns (the device pays that request's own order; a page that clicks Pay
|
|
1293
1297
|
* again creates a new order, which the earlier answer would send to the
|
|
@@ -1303,12 +1307,23 @@ function sameCheckoutRequest(first, retry) {
|
|
|
1303
1307
|
* getTokenId while the cardholder decides. Unknown modes (a duck-typed vault
|
|
1304
1308
|
* without checkoutModeOf) are taken as `token`.
|
|
1305
1309
|
*/
|
|
1306
|
-
function survivesAbandonment(opts, url, method, navigation, bound) {
|
|
1310
|
+
function survivesAbandonment(opts, url, method, navigation, bound, preparation) {
|
|
1307
1311
|
if (navigation || bound || isShijiStsHost(url))
|
|
1308
1312
|
return false;
|
|
1313
|
+
if (preparation && !preparedApprovalAnswersRetry(preparation.psp))
|
|
1314
|
+
return false;
|
|
1309
1315
|
const mode = typeof opts.vault?.checkoutModeOf === 'function' ? opts.vault.checkoutModeOf(url, method) : 'token';
|
|
1310
1316
|
return mode === 'token' || mode === 'cse';
|
|
1311
1317
|
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Whether `frameId` on `sessionId` is `owner`, the frame a prepared request came from. A prepared
|
|
1320
|
+
* approval that outlives its request (survivesAbandonment) belongs to that card frame for as long as
|
|
1321
|
+
* it lasts: only that frame's retry is answered from it, and the frame's removal stops the attachment
|
|
1322
|
+
* even after the page gave the request up. A frame with no id is nobody's.
|
|
1323
|
+
*/
|
|
1324
|
+
function sameFrame(owner, sessionId, frameId) {
|
|
1325
|
+
return !!owner && typeof owner.frameId === 'string' && owner.frameId !== '' && owner.frameId === frameId && owner.sessionId === sessionId;
|
|
1326
|
+
}
|
|
1312
1327
|
/**
|
|
1313
1328
|
* One card request's attempt at an approval, and the browser request that
|
|
1314
1329
|
* currently carries it. Separate from explicit cancellation: the client can
|
|
@@ -2284,9 +2299,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2284
2299
|
if (!(method === 'Page.frameDetached' && params.reason === 'swap')) {
|
|
2285
2300
|
microforms.forget(method === 'Page.frameDetached' ? params.frameId : method === 'Target.detachedFromTarget' ? params.targetId : undefined);
|
|
2286
2301
|
}
|
|
2302
|
+
// A prepared request's card frame ends its approval even once the page gave the request up and its
|
|
2303
|
+
// live frame id is gone; a target's detach is still read from the session, which a loss keeps.
|
|
2287
2304
|
if (activeRequest && (method === 'Target.detachedFromTarget' ? activeRequest.sessionId === params.sessionId
|
|
2288
2305
|
: method === 'Inspector.detached' ? activeRequest.sessionId === sessionId
|
|
2289
|
-
: activeRequest.sessionId === sessionId && activeRequest.frameId === params.frameId)
|
|
2306
|
+
: (activeRequest.sessionId === sessionId && activeRequest.frameId === params.frameId)
|
|
2307
|
+
|| sameFrame(activeRequest.preparedFrame, sessionId, params.frameId)))
|
|
2290
2308
|
activeRequest.attempt.stop();
|
|
2291
2309
|
// The frame or target the bound getTokenId came from is gone, so its getCvvId cannot follow:
|
|
2292
2310
|
// drop the companion binding and the CVV ciphertext it holds (a binding elsewhere stays).
|
|
@@ -2477,8 +2495,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2477
2495
|
return;
|
|
2478
2496
|
}
|
|
2479
2497
|
let preparation;
|
|
2498
|
+
// The page asking again while a prepared approval outlives its first request (survivesAbandonment):
|
|
2499
|
+
// the preparation is already bound to that approval, so the request is judged as its retry below
|
|
2500
|
+
// and never claimed again.
|
|
2501
|
+
const preparedRetry = awaitingApproval && activeRequest?.attempt.lost === true && preparationGate.isConsumed();
|
|
2480
2502
|
try {
|
|
2481
|
-
preparation = stripeStep ? undefined : preparationGate.claim(request.url, request.method, pausedBody(request), request.headers);
|
|
2503
|
+
preparation = stripeStep || preparedRetry ? undefined : preparationGate.claim(request.url, request.method, pausedBody(request), request.headers);
|
|
2482
2504
|
}
|
|
2483
2505
|
catch (error) {
|
|
2484
2506
|
opts.onEvent?.({ type: 'blocked', detail: failureSummary(error) });
|
|
@@ -2499,7 +2521,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2499
2521
|
&& typeof networkId === 'string' && networkId) {
|
|
2500
2522
|
const retryBody = pausedBody(request);
|
|
2501
2523
|
const retry = retryBody === null ? null : requestIdentity(opts, request.url, request.method, retryBody, documentKey(await documentUrlOf(readDocumentUrl, lifecycle.abort.signal)), await pageAmountOf(opts), flexPause?.shape);
|
|
2502
|
-
|
|
2524
|
+
// A prepared approval is answered only in the card frame it was bound in: the same request from
|
|
2525
|
+
// another frame (a replacement for a removed one, or a second card frame) is refused below.
|
|
2526
|
+
if (retry && activeRequest?.attempt.lost && sameCheckoutRequest(activeRequest.identity, retry)
|
|
2527
|
+
&& (!activeRequest.preparedFrame || sameFrame(activeRequest.preparedFrame, sessionId, frameId))) {
|
|
2503
2528
|
if (preparation)
|
|
2504
2529
|
preparationGate.retireUnboundClaim();
|
|
2505
2530
|
activeRequest.requestId = requestId;
|
|
@@ -2535,7 +2560,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2535
2560
|
}
|
|
2536
2561
|
// Same stop condition as the Playwright adapter: once a failure proves
|
|
2537
2562
|
// retrying is pointless, fail the request without calling the API again.
|
|
2538
|
-
|
|
2563
|
+
// A request that skipped the spent preparation's claim and was not bound as
|
|
2564
|
+
// its retry (the approval ended while it was judged) is refused as that
|
|
2565
|
+
// claim would refuse it, never asked as a new approval.
|
|
2566
|
+
if (terminal || lifecycle.isBlocked() || awaitingApproval || preparedRetry || Date.now() < quietUntil) {
|
|
2539
2567
|
let repeatedAuthorizationId = null;
|
|
2540
2568
|
if (lastSubmitted) {
|
|
2541
2569
|
const body = pausedBody(request);
|
|
@@ -2543,7 +2571,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2543
2571
|
repeatedAuthorizationId = lastSubmitted.authorizationId;
|
|
2544
2572
|
}
|
|
2545
2573
|
}
|
|
2546
|
-
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
2574
|
+
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
2575
|
+
: preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
2547
2576
|
opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
|
|
2548
2577
|
if (preparation)
|
|
2549
2578
|
preparationGate.retireUnboundClaim();
|
|
@@ -2571,8 +2600,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2571
2600
|
awaitingApproval = true;
|
|
2572
2601
|
// A card endpoint approval never outlives its request: the device answers that
|
|
2573
2602
|
// request, and a retry after the page gave up on it is refused, not bound.
|
|
2574
|
-
const attempt = merchantAttempt(lifecycle, survivesAbandonment(opts, request.url, request.method, resourceType === 'Document', !!
|
|
2575
|
-
|| juspayCheckout.bindsApproval(request.url, request.method)));
|
|
2603
|
+
const attempt = merchantAttempt(lifecycle, survivesAbandonment(opts, request.url, request.method, resourceType === 'Document', !!stripeStep || !!cardEndpoint || mercadoCheckout.requiresDocument(request.url, request.method)
|
|
2604
|
+
|| juspayCheckout.bindsApproval(request.url, request.method), preparation));
|
|
2576
2605
|
let handoffStarted = false;
|
|
2577
2606
|
// The approval the device gave, and whether it reached a browser request.
|
|
2578
2607
|
let approvedId = null;
|
|
@@ -2614,7 +2643,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2614
2643
|
}
|
|
2615
2644
|
attempt.bind({ requestId, sessionId, request });
|
|
2616
2645
|
activeRequest = { attempt, requestId, networkId, sessionId, frameId, request,
|
|
2617
|
-
identity: requestIdentity(opts, request.url, request.method, body, undefined, undefined, flexPause?.shape)
|
|
2646
|
+
identity: requestIdentity(opts, request.url, request.method, body, undefined, undefined, flexPause?.shape),
|
|
2647
|
+
preparedFrame: preparation ? { sessionId, frameId } : undefined };
|
|
2618
2648
|
// An ACI card-number request: the page's CVC and expiry requests for its checkout are held
|
|
2619
2649
|
// until this approval ends (see AciGate). One whose checkout already has a card-number
|
|
2620
2650
|
// request running or answered never begins; the pre-claim gate refuses it before this.
|
|
@@ -2787,7 +2817,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2787
2817
|
const continuation = cseContinuation(opts, deliverRequest.url, deliverBody, replay, { url: request.url, body }, preparation);
|
|
2788
2818
|
const postData = Buffer.from(continuation.body).toString('base64');
|
|
2789
2819
|
if (continuation.shijiCvv !== null) {
|
|
2790
|
-
shijiCompanions.bind({ url: request.url, method: request.method, body }, cdpShijiPage(sessionId, frameId), continuation.shijiCvv, continuation.
|
|
2820
|
+
shijiCompanions.bind({ url: request.url, method: request.method, body }, cdpShijiPage(sessionId, frameId), continuation.shijiCvv, continuation.shijiCvvKeySequenceSha256 ?? []);
|
|
2791
2821
|
}
|
|
2792
2822
|
handoffStarted = true;
|
|
2793
2823
|
if (replay.kind === 'verifone') {
|
|
@@ -3276,7 +3306,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3276
3306
|
}
|
|
3277
3307
|
});
|
|
3278
3308
|
page.on?.('framedetached', (frame) => {
|
|
3279
|
-
|
|
3309
|
+
// As in attachToCdp: a prepared request's card frame ends its approval even after the page gave the request up.
|
|
3310
|
+
if (activeRequest && (activeRequest.frames.includes(frame) || activeRequest.preparedFrames?.includes(frame)))
|
|
3280
3311
|
activeRequest.attempt.stop();
|
|
3281
3312
|
const gone = frame && typeof frame === 'object' ? playwrightShijiFrames.get(frame) : undefined;
|
|
3282
3313
|
if (gone)
|
|
@@ -3304,7 +3335,10 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3304
3335
|
// so the ACI gate routes them (never ACI's widget script, /bins or /v1/collect).
|
|
3305
3336
|
// The matcher sees only the URL. Worldpay eProtect's registration is a GET, so its family and
|
|
3306
3337
|
// the vendor's give-up reports are routed by the eProtect gate (never its scripts).
|
|
3307
|
-
|
|
3338
|
+
// Gravy's session fields are a PUT, so a URL a PUT recognizer claims is routed too; the
|
|
3339
|
+
// pre-claim gates then pause only the PUT and continue the URL's other methods.
|
|
3340
|
+
(url) => opts.vault.isCardRequest(url.toString()) || opts.vault.isCardRequest(url.toString(), 'PUT')
|
|
3341
|
+
|| guards.matches(url.toString()) || stripeCheckout.matches(url.toString()) || mercadoCheckout.matches(url.toString())
|
|
3308
3342
|
|| eprotect.matches(url.toString())
|
|
3309
3343
|
// An armed merchant profile's endpoint or a sibling of it (merchant-hosted.ts), as attachToCdp arms it.
|
|
3310
3344
|
|| (typeof opts.vault.merchantProfileOf === 'function' && opts.vault.merchantProfileOf(url.toString()) !== null)
|
|
@@ -3458,8 +3492,10 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3458
3492
|
if (withoutCard === 'continue')
|
|
3459
3493
|
return route.fallback();
|
|
3460
3494
|
let preparation;
|
|
3495
|
+
// As in attachToCdp: the retry of a prepared request whose approval outlived it is never claimed again.
|
|
3496
|
+
const preparedRetry = awaitingApproval && activeRequest?.attempt.lost === true && preparationGate.isConsumed();
|
|
3461
3497
|
try {
|
|
3462
|
-
preparation = stripeStep ? undefined : preparationGate.claim(request.url(), request.method(), request.postData() ?? '', request.headers());
|
|
3498
|
+
preparation = stripeStep || preparedRetry ? undefined : preparationGate.claim(request.url(), request.method(), request.postData() ?? '', request.headers());
|
|
3463
3499
|
}
|
|
3464
3500
|
catch (error) {
|
|
3465
3501
|
opts.onEvent?.({ type: 'blocked', detail: failureSummary(error) });
|
|
@@ -3473,15 +3509,17 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3473
3509
|
// merchantAttempt and the same check in attachToCdp).
|
|
3474
3510
|
if (awaitingApproval && activeRequest?.attempt.lost && !terminal && !setupStop.signal.aborted && !lifecycle.isCancelled()) {
|
|
3475
3511
|
const retry = requestIdentity(opts, request.url(), request.method(), request.postData() ?? '', documentKey(await documentUrlOf(readDocumentUrl, lifecycle.abort.signal)), await pageAmountOf(opts), flexPause?.shape);
|
|
3476
|
-
|
|
3512
|
+
const retryFrames = [];
|
|
3513
|
+
try {
|
|
3514
|
+
for (let frame = request.frame?.(); frame; frame = frame.parentFrame?.())
|
|
3515
|
+
retryFrames.push(frame);
|
|
3516
|
+
}
|
|
3517
|
+
catch { /* covered by requestfailed */ }
|
|
3518
|
+
// As in attachToCdp: a prepared approval is answered only in the card frame it was bound in.
|
|
3519
|
+
if (activeRequest?.attempt.lost && sameCheckoutRequest(activeRequest.identity, retry)
|
|
3520
|
+
&& (!activeRequest.preparedFrames || (retryFrames.length > 0 && retryFrames[0] === activeRequest.preparedFrames[0]))) {
|
|
3477
3521
|
if (preparation)
|
|
3478
3522
|
preparationGate.retireUnboundClaim();
|
|
3479
|
-
const retryFrames = [];
|
|
3480
|
-
try {
|
|
3481
|
-
for (let frame = request.frame?.(); frame; frame = frame.parentFrame?.())
|
|
3482
|
-
retryFrames.push(frame);
|
|
3483
|
-
}
|
|
3484
|
-
catch { /* covered by requestfailed */ }
|
|
3485
3523
|
activeRequest.request = request;
|
|
3486
3524
|
activeRequest.frames = retryFrames;
|
|
3487
3525
|
opts.onEvent?.({ type: 'card_request_paused', detail: { url: eventUrl(opts.vault, request.url()), recognizer: recognizerNameOf(opts, request.url(), request.method()), authorizationId: lifecycle.getState().authorizationId, retry: true } });
|
|
@@ -3507,7 +3545,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3507
3545
|
// CDP adapter's Fetch.failRequest uses, so a refused request resolves
|
|
3508
3546
|
// identically whichever adapter is attached. An Amadeus card request is
|
|
3509
3547
|
// 'failed' (ERR_FAILED) in both too, see failsAsNetworkError (refusalFailure).
|
|
3510
|
-
|
|
3548
|
+
// As in attachToCdp: an unbound request that skipped the spent preparation's claim is refused.
|
|
3549
|
+
if (terminal || lifecycle.isBlocked() || awaitingApproval || preparedRetry || Date.now() < quietUntil) {
|
|
3511
3550
|
let repeatedAuthorizationId = null;
|
|
3512
3551
|
if (lastSubmitted) {
|
|
3513
3552
|
let body = null;
|
|
@@ -3519,7 +3558,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3519
3558
|
repeatedAuthorizationId = lastSubmitted.authorizationId;
|
|
3520
3559
|
}
|
|
3521
3560
|
}
|
|
3522
|
-
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
3561
|
+
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
3562
|
+
: preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
3523
3563
|
opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
|
|
3524
3564
|
if (preparation)
|
|
3525
3565
|
preparationGate.retireUnboundClaim();
|
|
@@ -3546,8 +3586,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3546
3586
|
navigation = false;
|
|
3547
3587
|
}
|
|
3548
3588
|
// A card endpoint approval never outlives its request (see attachToCdp).
|
|
3549
|
-
const attempt = merchantAttempt(lifecycle, survivesAbandonment(opts, request.url(), request.method(), navigation, !!
|
|
3550
|
-
|| juspayCheckout.bindsApproval(request.url(), request.method())));
|
|
3589
|
+
const attempt = merchantAttempt(lifecycle, survivesAbandonment(opts, request.url(), request.method(), navigation, !!stripeStep || !!cardEndpoint || mercadoCheckout.requiresDocument(request.url(), request.method())
|
|
3590
|
+
|| juspayCheckout.bindsApproval(request.url(), request.method()), preparation));
|
|
3551
3591
|
const frames = [];
|
|
3552
3592
|
try {
|
|
3553
3593
|
for (let frame = request.frame?.(); frame; frame = frame.parentFrame?.())
|
|
@@ -3555,9 +3595,13 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3555
3595
|
}
|
|
3556
3596
|
catch { /* requestfailed/page close still cover unavailable frame metadata */ }
|
|
3557
3597
|
// Judged on the request that currently carries the attempt: after a
|
|
3558
|
-
// retry binds, the first request's failure is history, not a loss.
|
|
3598
|
+
// retry binds, the first request's failure is history, not a loss. A
|
|
3599
|
+
// prepared request's own frames are judged for as long as the approval
|
|
3600
|
+
// lasts, as framedetached judges them: their removal ends it.
|
|
3559
3601
|
const assertRequestLive = () => {
|
|
3560
3602
|
const current = attempt.target;
|
|
3603
|
+
if (preparation && frames.some((frame) => frame.isDetached?.()))
|
|
3604
|
+
attempt.stop();
|
|
3561
3605
|
if (current && (current.request.failure?.() || current.frames.some((frame) => frame.isDetached?.())))
|
|
3562
3606
|
attempt.lose();
|
|
3563
3607
|
if (page.isClosed?.())
|
|
@@ -3587,7 +3631,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3587
3631
|
const payToInterceptMs = readPayToInterceptMs(opts.payClickedAt, approvalStartedAt);
|
|
3588
3632
|
attempt.bind({ route, request, frames });
|
|
3589
3633
|
activeRequest = { request, frames, attempt,
|
|
3590
|
-
identity: requestIdentity(opts, request.url(), request.method(), body, undefined, undefined, flexPause?.shape)
|
|
3634
|
+
identity: requestIdentity(opts, request.url(), request.method(), body, undefined, undefined, flexPause?.shape),
|
|
3635
|
+
preparedFrames: preparation ? [...frames] : undefined };
|
|
3591
3636
|
// An ACI card-number request: see the same step in attachToCdp.
|
|
3592
3637
|
if (!aci.begin(request.url(), request.method(), body, preparation?.merchantOrigin)) {
|
|
3593
3638
|
throw new CheckoutPreparationError(preparation?.id ?? null, ACI_CHECKOUT_REUSED);
|
|
@@ -3728,7 +3773,7 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3728
3773
|
if (boundPage === null)
|
|
3729
3774
|
throw new ShijiStsError('unsupported');
|
|
3730
3775
|
assertRequestLive();
|
|
3731
|
-
shijiCompanions.bind({ url: request.url(), method: request.method(), body }, boundPage, continuation.shijiCvv, continuation.
|
|
3776
|
+
shijiCompanions.bind({ url: request.url(), method: request.method(), body }, boundPage, continuation.shijiCvv, continuation.shijiCvvKeySequenceSha256 ?? []);
|
|
3732
3777
|
}
|
|
3733
3778
|
handoffStarted = true;
|
|
3734
3779
|
if (replay.kind === 'verifone') {
|
package/dist/client.d.ts
CHANGED
|
@@ -41,11 +41,13 @@ export interface PausedRequest {
|
|
|
41
41
|
facts?: CardEndpointFactsRecord;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
44
|
-
* A Payrails card save (mode device_encrypt) only: the
|
|
45
|
-
* Payrails init payload named
|
|
46
|
-
* the
|
|
47
|
-
*
|
|
48
|
-
*
|
|
44
|
+
* A Payrails card save or card-form payment (mode device_encrypt) only: the
|
|
45
|
+
* session key the page's Payrails init payload named
|
|
46
|
+
* (vaultConfiguration.encryptionPublicKey), which the request itself does not
|
|
47
|
+
* carry, and for a card-form payment the payload's holderReference, which the
|
|
48
|
+
* payment's ciphertext carries and its body does not. The adapters capture
|
|
49
|
+
* both; a caller running its own interception passes them here, and a request
|
|
50
|
+
* without them is refused before anything is created.
|
|
49
51
|
*/
|
|
50
52
|
payrails?: PayrailsSidecar;
|
|
51
53
|
}
|
|
@@ -54,7 +56,7 @@ export interface PausedRequest {
|
|
|
54
56
|
* only recognizers in these modes, so a request this build cannot complete
|
|
55
57
|
* is never paused) and sent on every create. `device_encrypt`: the adapters
|
|
56
58
|
* capture the Payrails session key the page's init payload names (payrails.ts),
|
|
57
|
-
* and the cardholder's device encrypts and sends the save.
|
|
59
|
+
* and the cardholder's device encrypts and sends the save or card-form payment.
|
|
58
60
|
*/
|
|
59
61
|
export declare const SUPPORTED_MODES: readonly CheckoutMode[];
|
|
60
62
|
/**
|
|
@@ -124,14 +126,17 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
124
126
|
* `primer_payment_instruments`: Primer's card tokenization, which this build
|
|
125
127
|
* judges before it pauses (primer.ts) and hands back to the page only as a
|
|
126
128
|
* single-use token. A merchant's client session can save the card after the
|
|
127
|
-
* payment with no sign in that request, so the API creates a Primer
|
|
128
|
-
* authorization only
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
129
|
+
* payment with no sign in that request, so the API creates a live Primer
|
|
130
|
+
* authorization only when it names the checkout page's https origin
|
|
131
|
+
* (checkout_origin, which this build sends from the page's origin): the
|
|
132
|
+
* approval says the merchant keeps the card on file, under the merchant name
|
|
133
|
+
* the create sent, which Agentcard cannot confirm, and pays only with a card
|
|
134
|
+
* the person stored. A refused create aborts the paused request with nothing
|
|
135
|
+
* sent. A gated recognizer never sits in BUILTIN_REGISTRY, so it is paused only
|
|
136
|
+
* after a sync.
|
|
137
|
+
* `payrails_device_encrypt`: Payrails' card save and card-form payment, which
|
|
138
|
+
* this build pauses only once it has bound the request to a captured session
|
|
139
|
+
* (payrails.ts). A gated
|
|
135
140
|
* recognizer never sits in BUILTIN_REGISTRY, so it is paused only after a sync.
|
|
136
141
|
* `converge_hpp_process`: Elavon Converge's Hosted Payment Page card request
|
|
137
142
|
* (elavon-converge.ts), paused in token mode and judged by the pre-claim gate.
|
|
@@ -183,6 +188,22 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
183
188
|
* finds a card in it (cybersource-secure-acceptance.ts). Served on the same terms,
|
|
184
189
|
* so every other company keeps the stop every build applies to a card posted to
|
|
185
190
|
* Secure Acceptance.
|
|
191
|
+
* `gravy_session_fields_put`: Gravy Secure Fields' update of a checkout session,
|
|
192
|
+
* the one card request a recognizer declares as a PUT. This build pauses that PUT
|
|
193
|
+
* from Gravy's controller frame as it pauses a POST (isRecognizerRequest), creates
|
|
194
|
+
* the authorization with the PUT, and answers the frame with Gravy's own answer to
|
|
195
|
+
* the cardholder's device. Served on the same terms; an older build never sees the
|
|
196
|
+
* entry, and every company Agentcard has not turned Gravy on for keeps today's path,
|
|
197
|
+
* where the request continues to Gravy untouched.
|
|
198
|
+
* `moneris_hosted_tokenization`: Moneris Hosted Tokenization's card request, the
|
|
199
|
+
* frame's form post to /HPPtoken/request.php. This build pauses it only when it
|
|
200
|
+
* carries the number, the expiry and the CVD the cardholder's device fills, fails a
|
|
201
|
+
* frame that leaves the expiry or CVD to the merchant's page in the browser with a
|
|
202
|
+
* `blocked` event, `request_without_card`, and answers with Moneris's token as the
|
|
203
|
+
* API recorded it. The device sends the card through the checkout relay. Served on
|
|
204
|
+
* the same terms: only to a build that lists it, for a company Agentcard turned
|
|
205
|
+
* Moneris on for. A gated recognizer never sits in BUILTIN_REGISTRY, so it is paused
|
|
206
|
+
* only after a sync.
|
|
186
207
|
*/
|
|
187
208
|
export declare const SUPPORTED_CAPABILITIES: readonly string[];
|
|
188
209
|
/**
|
|
@@ -276,12 +297,13 @@ export interface CseReplay extends ExecutionMetadata {
|
|
|
276
297
|
authorizationId: string;
|
|
277
298
|
substitutions: Substitutions;
|
|
278
299
|
/**
|
|
279
|
-
* Shiji STS only: the getCvvId keySequence
|
|
280
|
-
* served beside the set (`shiji_sts_companion`). The adapters bind the
|
|
281
|
-
* (shiji-sts.ts ShijiStsCompanions), so the
|
|
300
|
+
* Shiji STS only: the SHA-256 of each getCvvId keySequence the API reviewed as the booking page's
|
|
301
|
+
* own, served beside the set (`shiji_sts_companion.key_sequence_sha256`). The adapters bind the
|
|
302
|
+
* companion getCvvId to a key that hashes to one of them (shiji-sts.ts ShijiStsCompanions), so the
|
|
303
|
+
* device's CVV goes under no other key.
|
|
282
304
|
*/
|
|
283
305
|
shijiStsCompanion?: Readonly<{
|
|
284
|
-
|
|
306
|
+
keySequenceSha256: readonly string[];
|
|
285
307
|
}>;
|
|
286
308
|
amountAuthority?: AmountAuthority;
|
|
287
309
|
}
|
|
@@ -401,12 +423,15 @@ export interface CardEndpointReplay extends ExecutionMetadata {
|
|
|
401
423
|
/**
|
|
402
424
|
* The device_encrypt flow (Payrails): the cardholder's device encrypted the
|
|
403
425
|
* real card under the session key the page's init payload named and sent the
|
|
404
|
-
* save to Payrails itself. This is Payrails'
|
|
405
|
-
* allow-list
|
|
406
|
-
*
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
*
|
|
426
|
+
* save or card-form payment to Payrails itself. This is Payrails' answer,
|
|
427
|
+
* rebuilt from an allow-list, to replay into the paused request as in the token
|
|
428
|
+
* flow: for a save its 201 (the instrument's identifiers, its status, the
|
|
429
|
+
* network, the BIN cut to six digits, the last four, the expiry, the holder
|
|
430
|
+
* name and the fingerprint); for a card-form payment its 2xx naming the
|
|
431
|
+
* authorize action (its name, id, time and execution link, which the page's
|
|
432
|
+
* Payrails SDK polls for the issuer's answer). The ciphertext the device made
|
|
433
|
+
* never reaches the runtime or the page. Never an autopilot result: autopilot
|
|
434
|
+
* refuses this mode.
|
|
410
435
|
*/
|
|
411
436
|
export interface DeviceEncryptReplay extends ExecutionMetadata {
|
|
412
437
|
mode: 'device_encrypt';
|
|
@@ -430,11 +455,14 @@ export interface PrepareCheckoutOptions {
|
|
|
430
455
|
* card request is then the one request this preparation pays.
|
|
431
456
|
*
|
|
432
457
|
* A Fiserv checkout (psp 'fiserv'): the id of the key Agentcard pinned for the
|
|
433
|
-
* merchant
|
|
458
|
+
* merchant: `homedepot_orangepay` for Blinds.com and homedepot.com, and
|
|
459
|
+
* `agentcard_sandbox` in the sandbox environment. Required for Fiserv,
|
|
434
460
|
* and refused for every processor but Adyen and Fiserv. The cardholder is asked to
|
|
435
461
|
* approve a payment to the merchant that key belongs to, so the checkout's `merchant`
|
|
436
|
-
* must be that merchant's name (`
|
|
437
|
-
* one card capture this preparation pays
|
|
462
|
+
* must be that merchant's name (`Home Depot` for `homedepot_orangepay`, `Agentcard
|
|
463
|
+
* sandbox` for `agentcard_sandbox`), and the one card capture this preparation pays
|
|
464
|
+
* must be that merchant's, under that key or under a credentials session's key,
|
|
465
|
+
* which the envelope's key replaces.
|
|
438
466
|
*
|
|
439
467
|
* A Verifone checkout (psp 'verifone'): the id of the Verifone profile Agentcard
|
|
440
468
|
* reviewed for the merchant, which this client must have armed and enabled
|
|
@@ -1198,8 +1226,9 @@ export declare class VaultClient {
|
|
|
1198
1226
|
private mergedRegistry;
|
|
1199
1227
|
/**
|
|
1200
1228
|
* The recogniser that claims a card request, or null; what a paused request names as
|
|
1201
|
-
* `recognizer`. A POST to a recognised endpoint,
|
|
1202
|
-
* GET (Worldpay eProtect) that
|
|
1229
|
+
* `recognizer`. A POST to a recognised endpoint, a PUT for a recognizer whose card request is
|
|
1230
|
+
* a PUT (Gravy), or for a recognizer whose card request is a GET (Worldpay eProtect) that
|
|
1231
|
+
* exact GET, query included.
|
|
1203
1232
|
*/
|
|
1204
1233
|
recognizerOf(url: string, method?: string): Recognizer | null;
|
|
1205
1234
|
/** Refresh recognizers from the API so new PSPs work without a redeploy. */
|
|
@@ -1244,8 +1273,9 @@ export declare class VaultClient {
|
|
|
1244
1273
|
cardOnFilePolicy(url: string, method?: string): string | null;
|
|
1245
1274
|
/**
|
|
1246
1275
|
* True when this request is a card tokenization we can take over: a POST to
|
|
1247
|
-
* a recognized endpoint,
|
|
1248
|
-
* (
|
|
1276
|
+
* a recognized endpoint, a PUT for a recognizer whose card request is a PUT
|
|
1277
|
+
* (Gravy), or for a recognizer whose card request is a GET (Worldpay eProtect)
|
|
1278
|
+
* that exact GET, query included.
|
|
1249
1279
|
*/
|
|
1250
1280
|
isCardRequest(url: string, method?: string): boolean;
|
|
1251
1281
|
/**
|