@agent-cards/sdk 0.23.12 → 0.23.14
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 +38 -1
- package/LICENSE +38 -0
- package/PREFLIGHT.md +1 -1
- package/README.md +53 -14
- package/dist/adyen-merchant-hosted.generated.js +2 -2
- package/dist/builtin-registry.generated.js +1 -1
- package/dist/card-fields.generated.js +843 -3
- package/dist/cdp.js +101 -13
- package/dist/client.d.ts +91 -2
- package/dist/client.js +205 -13
- package/dist/creditguard.generated.d.ts +100 -2
- package/dist/creditguard.generated.js +333 -3
- package/dist/datatrans.generated.d.ts +19 -4
- package/dist/datatrans.generated.js +15 -3
- package/dist/ebanx.d.ts +107 -0
- package/dist/ebanx.generated.d.ts +78 -0
- package/dist/ebanx.generated.js +577 -0
- package/dist/ebanx.js +159 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/lifecycle.d.ts +35 -3
- package/dist/lifecycle.js +38 -3
- package/dist/paddle-tokenex.d.ts +56 -0
- package/dist/paddle-tokenex.generated.d.ts +67 -0
- package/dist/paddle-tokenex.generated.js +951 -0
- package/dist/paddle-tokenex.js +95 -0
- package/dist/paddle-vgs-pay.d.ts +55 -0
- package/dist/paddle-vgs-pay.js +115 -0
- package/dist/paddle-vgs.generated.d.ts +29 -1
- package/dist/paddle-vgs.generated.js +124 -5
- package/dist/paysafe.generated.d.ts +5 -7
- package/dist/paysafe.generated.js +50 -18
- package/dist/pre-claim.d.ts +46 -1
- package/dist/pre-claim.js +338 -17
- package/dist/preparation.d.ts +13 -0
- package/dist/preparation.js +22 -0
- package/dist/prepared-processor.js +6 -2
- package/dist/primer.generated.js +878 -38
- package/dist/redsys.d.ts +42 -0
- package/dist/redsys.js +68 -0
- package/dist/stripe-checkout.generated.js +479 -11
- package/package.json +9 -6
package/dist/cdp.js
CHANGED
|
@@ -8,13 +8,17 @@ import { MERCADO_CHECKOUT_PATTERN } from './mercado-checkout.generated.js';
|
|
|
8
8
|
import { JuspayCheckoutGate } from './juspay-gate.js';
|
|
9
9
|
import { ACI_CHECKOUT_REUSED, AciGate, playwrightReplacesNavigationBody } from './aci.js';
|
|
10
10
|
import { EprotectGate } from './eprotect.js';
|
|
11
|
-
import { dispatchPreClaim, eventUrl } from './pre-claim.js';
|
|
11
|
+
import { dispatchPreClaim, eventUrl, isArmedCreditguardCall } 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 { PADDLE_VGS_PAY_PATTERNS, PaddleVgsPayGate, isPaddleCheckoutService, recognizesPaddleVgs } from './paddle-vgs-pay.js';
|
|
14
15
|
import { DatatransRepeats } from './datatrans.js';
|
|
15
16
|
import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
|
|
17
|
+
import { paddleTokenexTokenContinuation } from './paddle-tokenex.js';
|
|
16
18
|
import { isArmedEkashuUrl } from './ekashu.js';
|
|
19
|
+
import { isArmedRedsysUrl } from './redsys.js';
|
|
17
20
|
import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
|
|
21
|
+
import { EBANX_RESEND_PATTERNS, EbanxFollowUps, isEbanxResendUrl } from './ebanx.js';
|
|
18
22
|
import { CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, SecureAcceptanceFollowUpWatch, isCybersourceSecureAcceptanceUrl, secureAcceptanceFollowUp, secureAcceptanceFrameAnswer, secureAcceptanceReplyLast4, } from './cybersource-secure-acceptance.js';
|
|
19
23
|
import { CheckoutAttachmentError, attachmentDeadline, attachmentFailure, withinAttachmentDeadline } from './attachment.js';
|
|
20
24
|
import { cseBody } from './cse-body.js';
|
|
@@ -338,8 +342,14 @@ function cseAuthorizedDetail(replay, url, shijiCvv = null) {
|
|
|
338
342
|
*/
|
|
339
343
|
function cseContinuation(opts, url, body, replay, approved, preparation) {
|
|
340
344
|
const shiji = shijiStsTokenContinuation(url, body, replay.substitutions, approved, replay.kind === 'merchant_hosted' || replay.kind === 'verifone' ? undefined : replay.shijiStsCompanion);
|
|
341
|
-
|
|
342
|
-
|
|
345
|
+
if (shiji)
|
|
346
|
+
return { body: shiji.body, shijiCvv: shiji.cvv, shijiCvvKeySequenceSha256: shiji.cvvKeySequenceSha256 };
|
|
347
|
+
// Paddle TokenEx writes two ciphertexts and five metadata facts under the pinned key, which the generic
|
|
348
|
+
// Adyen swap cannot, so it is dispatched by the /pay URL to substitutePaddleTokenex (paddle-tokenex.ts).
|
|
349
|
+
const paddleTokenex = replay.kind === undefined ? paddleTokenexTokenContinuation(url, body, replay.substitutions, replay.paddleTokenexFacts, approved) : null;
|
|
350
|
+
if (paddleTokenex)
|
|
351
|
+
return { body: paddleTokenex.body, shijiCvv: null, shijiCvvKeySequenceSha256: null };
|
|
352
|
+
return { body: cseContinuationBody(opts, body, replay, url, preparation), shijiCvv: null, shijiCvvKeySequenceSha256: null };
|
|
343
353
|
}
|
|
344
354
|
/**
|
|
345
355
|
* The opaque page id a Shiji STS companion is bound to: the CDP session and
|
|
@@ -1532,7 +1542,10 @@ function replayOf(result, timeoutMs) {
|
|
|
1532
1542
|
|| !response.headers || typeof response.headers !== 'object' || Array.isArray(response.headers)) {
|
|
1533
1543
|
throw new Error('An approved token read resumes with response, the processor\'s answer (voice.awaitResult carries it once the purchase finished).');
|
|
1534
1544
|
}
|
|
1535
|
-
|
|
1545
|
+
// The read's processor_context rides as it came: a Paddle VGS answer is handed to the page only with the
|
|
1546
|
+
// approved card's details its /pay names (paddle-vgs-pay.ts checks them before the hand-over).
|
|
1547
|
+
return { mode: 'token', authorizationId, status: response.status, headers: { ...response.headers }, body: response.body, amountVerified: null,
|
|
1548
|
+
...(result.processor_context !== undefined ? { processorContext: result.processor_context } : {}), ...execution };
|
|
1536
1549
|
}
|
|
1537
1550
|
/** The recogniser that claimed a paused request, by processor name, so a caller never re-matches the URL; null on a duck-typed vault without the read. */
|
|
1538
1551
|
function recognizerNameOf(opts, url, method) {
|
|
@@ -1754,6 +1767,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
1754
1767
|
};
|
|
1755
1768
|
// Juspay's tokenize answer and the synthetic tokens it issued (juspay-gate.ts).
|
|
1756
1769
|
const juspayCheckout = new JuspayCheckoutGate(opts.vault);
|
|
1770
|
+
// The approved EBANX token request on this attachment and the sends claimed under it (ebanx.ts).
|
|
1771
|
+
const ebanxFollowUps = new EbanxFollowUps();
|
|
1757
1772
|
const aci = new AciGate(opts.vault);
|
|
1758
1773
|
const eprotect = new EprotectGate(opts.vault);
|
|
1759
1774
|
// The merchant's follow-up request after a delivered Secure Acceptance reply (cybersource-secure-acceptance.ts).
|
|
@@ -2019,6 +2034,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2019
2034
|
// committing a new document (Page.frameNavigated, at the top of the event handler).
|
|
2020
2035
|
const shijiCompanions = new ShijiStsCompanions();
|
|
2021
2036
|
lifecycle.abort.signal.addEventListener('abort', () => shijiCompanions.release(), { once: true });
|
|
2037
|
+
// Paddle's /pay after the stored card's id this attachment handed a frame (paddle-vgs-pay.ts): its
|
|
2038
|
+
// checkout services are held only when the synced registry serves Paddle VGS, and a cancel drops the binding.
|
|
2039
|
+
const paddleVgsPay = new PaddleVgsPayGate(() => recognizesPaddleVgs(opts.vault));
|
|
2040
|
+
lifecycle.abort.signal.addEventListener('abort', () => paddleVgsPay.release(), { once: true });
|
|
2022
2041
|
const derived = typeof opts.vault?.cardUrlPatterns === 'function' ? opts.vault.cardUrlPatterns() : [];
|
|
2023
2042
|
// The endpoint and siblings of every merchant profile this build reviewed
|
|
2024
2043
|
// (merchant-hosted.ts), which a VaultClient arms from the start: a profile's card
|
|
@@ -2042,11 +2061,15 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2042
2061
|
// registry serves: one that carries the typed card is paused only to be
|
|
2043
2062
|
// refused, and one with no card continues (see paypal-hosted-fields.ts).
|
|
2044
2063
|
// CyberSource Secure Acceptance's /silent/ paths are armed the same way, form
|
|
2045
|
-
// navigations included (see cybersource-secure-acceptance.ts)
|
|
2064
|
+
// navigations included (see cybersource-secure-acceptance.ts), and so are
|
|
2065
|
+
// EBANX's 3-D Secure calls and hosted confirm, which send the typed card again
|
|
2066
|
+
// after its token: the cardholder's device sends one the approved token request
|
|
2067
|
+
// covers, and any other is refused (see ebanx.ts).
|
|
2046
2068
|
const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
|
|
2047
2069
|
...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
|
|
2048
2070
|
...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
|
|
2049
|
-
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS
|
|
2071
|
+
...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS,
|
|
2072
|
+
...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS])];
|
|
2050
2073
|
const arm = async (sessionId, resume = false, worker = false) => {
|
|
2051
2074
|
const key = sessionId ?? '__root__';
|
|
2052
2075
|
if (armed.has(key))
|
|
@@ -2476,10 +2499,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2476
2499
|
cybersourceFlex: { judge: (flexRequest) => microforms.judge(flexRequest, frameId, () => rereadMicroforms(sessionId, frameId)) },
|
|
2477
2500
|
juspay: juspayCheckout,
|
|
2478
2501
|
datatrans: datatransRepeats,
|
|
2502
|
+
ebanx: ebanxFollowUps,
|
|
2479
2503
|
frame: typeof frameId === 'string' && frameId ? frameId : undefined,
|
|
2480
2504
|
payrails,
|
|
2481
2505
|
shijiSts: { companions: shijiCompanions, page: () => cdpShijiPage(sessionId, frameId) },
|
|
2482
2506
|
worldlineMyCheckout: worldlineProducts,
|
|
2507
|
+
paddleVgsPay,
|
|
2483
2508
|
});
|
|
2484
2509
|
const preClaim = preClaimResult instanceof Promise ? await preClaimResult : preClaimResult;
|
|
2485
2510
|
if (preClaim.kind !== 'pause')
|
|
@@ -2588,7 +2613,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2588
2613
|
// A request that skipped the spent preparation's claim and was not bound as
|
|
2589
2614
|
// its retry (the approval ended while it was judged) is refused as that
|
|
2590
2615
|
// claim would refuse it, never asked as a new approval.
|
|
2591
|
-
|
|
2616
|
+
// An EBANX send still out (a confirm that may pay) holds the attachment too: a new card request the page sends
|
|
2617
|
+
// meanwhile, a second tap on the hosted page's Pay among them, would start another payment over it.
|
|
2618
|
+
const followUpOut = lifecycle.followUpsInFlight();
|
|
2619
|
+
if (terminal || lifecycle.isBlocked() || awaitingApproval || followUpOut || preparedRetry || Date.now() < quietUntil) {
|
|
2592
2620
|
let repeatedAuthorizationId = null;
|
|
2593
2621
|
if (lastSubmitted) {
|
|
2594
2622
|
const body = pausedBody(request);
|
|
@@ -2597,12 +2625,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2597
2625
|
}
|
|
2598
2626
|
}
|
|
2599
2627
|
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
2600
|
-
: preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
2628
|
+
: followUpOut ? 'an EBANX send is in flight' : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
2601
2629
|
opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
|
|
2602
2630
|
if (preparation)
|
|
2603
2631
|
preparationGate.retireUnboundClaim();
|
|
2604
2632
|
// A card form navigation never reads that nothing was sent while a payment for this checkout may exist.
|
|
2605
|
-
await refusePaused(requestId, sessionId, request.url, cardEndpoint, heldNavigationRefusal(terminal, awaitingApproval, lifecycle), secureAcceptance);
|
|
2633
|
+
await refusePaused(requestId, sessionId, request.url, cardEndpoint, heldNavigationRefusal(terminal, awaitingApproval || followUpOut, lifecycle), secureAcceptance);
|
|
2606
2634
|
if (repeatedAuthorizationId)
|
|
2607
2635
|
void opts.vault.reportDuplicateGuard?.(repeatedAuthorizationId);
|
|
2608
2636
|
return;
|
|
@@ -2631,6 +2659,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2631
2659
|
// The approval the device gave, and whether it reached a browser request.
|
|
2632
2660
|
let approvedId = null;
|
|
2633
2661
|
let deliveryBegun = false;
|
|
2662
|
+
// Whether this handoff kept an EBANX approval's follow-ups (ebanxFollowUps.remember), which a failed handoff stops.
|
|
2663
|
+
let ebanxRemembered = false;
|
|
2634
2664
|
// The business's pause, so resume resolves once the page has its answer.
|
|
2635
2665
|
let businessPause = null;
|
|
2636
2666
|
// A prepared Amadeus SDK add must be sent within its window (see amadeus.ts).
|
|
@@ -2901,6 +2931,23 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2901
2931
|
// device_encrypt answer (Payrails' 201, which the device obtained) is
|
|
2902
2932
|
// handed over the same way.
|
|
2903
2933
|
const cors = corsDecision(deliverRequest.url, deliverRequest.headers);
|
|
2934
|
+
// A Paddle card request's answer is the stored card's id: the frame's /pay that follows continues only
|
|
2935
|
+
// with the approved card's details, bound to that frame before the id reaches it (paddle-vgs-pay.ts).
|
|
2936
|
+
// An answer that came with no details is not handed over: its /pay could only name the placeholder.
|
|
2937
|
+
try {
|
|
2938
|
+
paddleVgsPay.arm(activeRequest?.frameId || undefined, deliverRequest.url, replay);
|
|
2939
|
+
}
|
|
2940
|
+
catch {
|
|
2941
|
+
throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
|
|
2942
|
+
}
|
|
2943
|
+
// An approved EBANX token request whose read names a window covers the 3-D Secure calls and the confirm the
|
|
2944
|
+
// page sends next (ebanx.ts): kept as the API stored it, the request that raised the approval. Kept before the
|
|
2945
|
+
// page has the answer, since the page may send its first 3-D Secure call the moment the answer lands; a
|
|
2946
|
+
// handoff that then fails stops them (the catch below).
|
|
2947
|
+
if (replay.mode !== 'device_encrypt') {
|
|
2948
|
+
ebanxFollowUps.remember({ url: request.url, body }, replay);
|
|
2949
|
+
ebanxRemembered = true;
|
|
2950
|
+
}
|
|
2904
2951
|
await cdp.send('Fetch.fulfillRequest', {
|
|
2905
2952
|
requestId: deliverTo,
|
|
2906
2953
|
responseCode: replay.status,
|
|
@@ -2926,6 +2973,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
|
|
|
2926
2973
|
catch (err) {
|
|
2927
2974
|
// Ignore the failure event caused by our own safe decline/error abort.
|
|
2928
2975
|
const dead = attempt.target ?? { requestId, sessionId };
|
|
2976
|
+
// An EBANX token answer this handoff kept follow-ups for and did not finish handing over: nothing after it is
|
|
2977
|
+
// claimed (ebanx.ts EbanxFollowUps.stop).
|
|
2978
|
+
if (ebanxRemembered)
|
|
2979
|
+
ebanxFollowUps.stop();
|
|
2929
2980
|
// The business's resume settles once the request is answered: a terminal read is an answer (failed, nothing charged), a lost handoff is not.
|
|
2930
2981
|
const settlePause = () => { if (businessPause) {
|
|
2931
2982
|
if (isApprovalOutcome(err))
|
|
@@ -3070,6 +3121,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3070
3121
|
};
|
|
3071
3122
|
// Juspay's tokenize answer and the synthetic tokens it issued (juspay-gate.ts).
|
|
3072
3123
|
const juspayCheckout = new JuspayCheckoutGate(opts.vault);
|
|
3124
|
+
// The approved EBANX token request on this attachment and the sends claimed under it (ebanx.ts).
|
|
3125
|
+
const ebanxFollowUps = new EbanxFollowUps();
|
|
3073
3126
|
const aci = new AciGate(opts.vault);
|
|
3074
3127
|
let navigationBodyReplaced;
|
|
3075
3128
|
const eprotect = new EprotectGate(opts.vault);
|
|
@@ -3131,6 +3184,9 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3131
3184
|
// a new document (the handlers below).
|
|
3132
3185
|
const shijiCompanions = new ShijiStsCompanions();
|
|
3133
3186
|
lifecycle.abort.signal.addEventListener('abort', () => shijiCompanions.release(), { once: true });
|
|
3187
|
+
// Paddle's /pay after the stored card's id this attachment handed a frame, as attachToCdp binds it.
|
|
3188
|
+
const paddleVgsPay = new PaddleVgsPayGate(() => recognizesPaddleVgs(opts.vault));
|
|
3189
|
+
lifecycle.abort.signal.addEventListener('abort', () => paddleVgsPay.release(), { once: true });
|
|
3134
3190
|
// An answer to a request the facts watch records began to arrive: the watch orders what the page sent after a
|
|
3135
3191
|
// record by this moment, since Playwright reports the answer's end after the page's next request. Listened to only
|
|
3136
3192
|
// once the page sent such a request, so a page that never meets a merchant with facts carries no extra listener.
|
|
@@ -3385,10 +3441,21 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3385
3441
|
// Every request to an NMI host whose entry is served (ekashu.ts), as attachToCdp arms the
|
|
3386
3442
|
// host: the card page's post and its 3-D Secure calls reach the NMI gate and fail.
|
|
3387
3443
|
|| isArmedEkashuUrl(opts.vault, url.toString())
|
|
3444
|
+
// A CreditGuard page's own calls beside its submit (its card lookup and its 3-D Secure calls), for an
|
|
3445
|
+
// org the API served CreditGuard to, as attachToCdp arms its hosts: the CreditGuard gate answers them
|
|
3446
|
+
// under a ready preparation and lets them go on with none (pre-claim.ts isArmedCreditguardCall).
|
|
3447
|
+
|| isArmedCreditguardCall(opts.vault, url.toString())
|
|
3448
|
+
// Every request to a Redsys host once its entry is served (redsys.ts), as attachToCdp arms the
|
|
3449
|
+
// hosts: the card page's post and Redsys's other card requests reach the Redsys gate and fail.
|
|
3450
|
+
|| isArmedRedsysUrl(opts.vault, url.toString())
|
|
3388
3451
|
// PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
|
|
3389
3452
|
|| isPaypalHostedFieldsConfirm(url.toString())
|
|
3390
3453
|
// CyberSource Secure Acceptance's /silent/ paths, for every org, as attachToCdp arms them (cybersource-secure-acceptance.ts).
|
|
3391
|
-
|| isCybersourceSecureAcceptanceUrl(url.toString())
|
|
3454
|
+
|| isCybersourceSecureAcceptanceUrl(url.toString())
|
|
3455
|
+
// Paddle's checkout services, routed only for an org the API served Paddle VGS to (see attachToCdp).
|
|
3456
|
+
|| (isPaddleCheckoutService(url.toString()) && recognizesPaddleVgs(opts.vault))
|
|
3457
|
+
// EBANX's 3-D Secure calls and hosted confirm, for every org, as attachToCdp arms them (ebanx.ts).
|
|
3458
|
+
|| isEbanxResendUrl(url.toString()), async (route) => {
|
|
3392
3459
|
const request = route.request();
|
|
3393
3460
|
// When this request paused, and the page it came from: the merchant's total is read as of now.
|
|
3394
3461
|
const pausedAt = Date.now();
|
|
@@ -3481,10 +3548,12 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3481
3548
|
} },
|
|
3482
3549
|
juspay: juspayCheckout,
|
|
3483
3550
|
datatrans: datatransRepeats,
|
|
3551
|
+
ebanx: ebanxFollowUps,
|
|
3484
3552
|
frame: frameOf(request),
|
|
3485
3553
|
payrails,
|
|
3486
3554
|
shijiSts: { companions: shijiCompanions, page: async () => (await playwrightShijiDocumentPage(request)) ?? `pw:none:${++playwrightShijiSeq}` },
|
|
3487
3555
|
worldlineMyCheckout: worldlineProducts,
|
|
3556
|
+
paddleVgsPay,
|
|
3488
3557
|
});
|
|
3489
3558
|
const preClaim = preClaimResult instanceof Promise ? await preClaimResult : preClaimResult;
|
|
3490
3559
|
if (preClaim.kind !== 'pause')
|
|
@@ -3574,7 +3643,9 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3574
3643
|
// identically whichever adapter is attached. An Amadeus card request is
|
|
3575
3644
|
// 'failed' (ERR_FAILED) in both too, see failsAsNetworkError (refusalFailure).
|
|
3576
3645
|
// As in attachToCdp: an unbound request that skipped the spent preparation's claim is refused.
|
|
3577
|
-
|
|
3646
|
+
// As in attachToCdp: an EBANX send still out holds the attachment too.
|
|
3647
|
+
const followUpOut = lifecycle.followUpsInFlight();
|
|
3648
|
+
if (terminal || lifecycle.isBlocked() || awaitingApproval || followUpOut || preparedRetry || Date.now() < quietUntil) {
|
|
3578
3649
|
let repeatedAuthorizationId = null;
|
|
3579
3650
|
if (lastSubmitted) {
|
|
3580
3651
|
let body = null;
|
|
@@ -3587,12 +3658,12 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3587
3658
|
}
|
|
3588
3659
|
}
|
|
3589
3660
|
const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
|
|
3590
|
-
: preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
3661
|
+
: followUpOut ? 'an EBANX send is in flight' : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
|
|
3591
3662
|
opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
|
|
3592
3663
|
if (preparation)
|
|
3593
3664
|
preparationGate.retireUnboundClaim();
|
|
3594
3665
|
// As in attachToCdp: never that nothing was sent while a payment for this checkout may exist.
|
|
3595
|
-
const refused = await refuseRoute(route, heldNavigationRefusal(terminal, awaitingApproval, lifecycle));
|
|
3666
|
+
const refused = await refuseRoute(route, heldNavigationRefusal(terminal, awaitingApproval || followUpOut, lifecycle));
|
|
3596
3667
|
if (repeatedAuthorizationId)
|
|
3597
3668
|
void opts.vault.reportDuplicateGuard?.(repeatedAuthorizationId);
|
|
3598
3669
|
return refused;
|
|
@@ -3640,6 +3711,8 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3640
3711
|
// The approval the device gave, and whether it reached a browser request.
|
|
3641
3712
|
let approvedId = null;
|
|
3642
3713
|
let deliveryBegun = false;
|
|
3714
|
+
// See the same flag in attachToCdp.
|
|
3715
|
+
let ebanxRemembered = false;
|
|
3643
3716
|
// A prepared Amadeus SDK add must be sent within its window (see amadeus.ts).
|
|
3644
3717
|
let disarmSendWindow = null;
|
|
3645
3718
|
try {
|
|
@@ -3847,6 +3920,18 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3847
3920
|
// none; written here anyway (replacing a stale value) so a
|
|
3848
3921
|
// cross-origin answer is the same whichever adapter ran.
|
|
3849
3922
|
const cors = corsDecision(deliverRequest.url(), deliverRequest.headers());
|
|
3923
|
+
// See the same step in attachToCdp: a Paddle stored card's id reaches its frame only with its /pay bound.
|
|
3924
|
+
try {
|
|
3925
|
+
paddleVgsPay.arm(frameOf(deliverRequest), deliverRequest.url(), replay);
|
|
3926
|
+
}
|
|
3927
|
+
catch {
|
|
3928
|
+
throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
|
|
3929
|
+
}
|
|
3930
|
+
// See the same step in attachToCdp: kept before the page has the answer.
|
|
3931
|
+
if (replay.mode !== 'device_encrypt') {
|
|
3932
|
+
ebanxFollowUps.remember({ url: request.url(), body }, replay);
|
|
3933
|
+
ebanxRemembered = true;
|
|
3934
|
+
}
|
|
3850
3935
|
await deliverTo.fulfill({ status: replay.status, headers: withCorsHeaders(replay.headers, cors.headers), body: replay.body });
|
|
3851
3936
|
datatransRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
|
|
3852
3937
|
assertRequestLive();
|
|
@@ -3862,6 +3947,9 @@ export async function attachToPlaywright(page, opts) {
|
|
|
3862
3947
|
}
|
|
3863
3948
|
catch (err) {
|
|
3864
3949
|
const dead = attempt.target?.route ?? route;
|
|
3950
|
+
// See the same step in attachToCdp.
|
|
3951
|
+
if (ebanxRemembered)
|
|
3952
|
+
ebanxFollowUps.stop();
|
|
3865
3953
|
if (activeRequest?.attempt === attempt)
|
|
3866
3954
|
activeRequest = null;
|
|
3867
3955
|
try {
|
package/dist/client.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type MerchantTotalApproved, type MerchantTotalCapture } from './merchan
|
|
|
5
5
|
import { type PreparationMode, type PreparationProcessor } from './prepared-processor.js';
|
|
6
6
|
import { type VerifoneProfile, type VerifoneSubstitutions } from './verifone.js';
|
|
7
7
|
import { type MerchantSessionCapture } from './merchant-session-probe.js';
|
|
8
|
+
import { type PaddleTokenexServedFacts } from './paddle-tokenex.js';
|
|
8
9
|
import { VoiceModule, type VoiceOptions } from './voice.js';
|
|
9
10
|
import { BuyModule } from './buy.js';
|
|
10
11
|
import { RequestRegistry } from './requests.js';
|
|
@@ -205,6 +206,11 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
205
206
|
* Moneris on for. A gated recognizer never sits in BUILTIN_REGISTRY, so it is paused
|
|
206
207
|
* only after a sync.
|
|
207
208
|
*
|
|
209
|
+
* `redsys_hosted_payment_page`: Redsys's hosted payment page (the TPV Virtual redirect), whose
|
|
210
|
+
* card-free signed entry this build pauses for the cardholder's device and whose card page this build
|
|
211
|
+
* never lets the agent's browser post to (pre-claim.ts, redsys.ts). Served on the same terms as NMI's: only
|
|
212
|
+
* to a build that lists it, for a company Agentcard turned Redsys on for.
|
|
213
|
+
*
|
|
208
214
|
* `paddle_vgs_cards`: the card request Paddle's checkout frame sends to its VGS card
|
|
209
215
|
* vault (vault.paddle.com/cards) on a checkout that names VGS, only through a prepared
|
|
210
216
|
* checkout: the frame aborts the request 10 seconds after Pay and sends it once more,
|
|
@@ -212,6 +218,29 @@ export declare const SUPPORTED_REGISTRY_FEATURES: readonly string[];
|
|
|
212
218
|
* device sends the card itself and the frame gets back the stored card's id alone.
|
|
213
219
|
* Served on the same terms: only to a build that lists it, for a company Agentcard
|
|
214
220
|
* turned Paddle on for.
|
|
221
|
+
*
|
|
222
|
+
* `paddle_tokenex_pay`: the /pay Paddle's checkout frame sends to its checkout service
|
|
223
|
+
* (checkout-service.paddle.com) on a checkout that names TokenEx. The frame encrypts the
|
|
224
|
+
* card in the page under the account's TokenEx key, so /pay carries the placeholder's two
|
|
225
|
+
* ciphertexts and facts; this build pauses it and writes over them the device's own, under
|
|
226
|
+
* the key payment-core pins (paddle-tokenex.ts). Served on the same terms: only to a build
|
|
227
|
+
* that lists it, for a company Agentcard turned Paddle on for.
|
|
228
|
+
*
|
|
229
|
+
* `lyra_krypton_payment`: the card payment Lyra's Krypton form (PayZen, Systempay,
|
|
230
|
+
* Sogecommerce) sends from its hidden checkout frame to ProcessPayment/2 on the brand's API
|
|
231
|
+
* host. This build pauses only the frame's card body under a form token that pays once; the
|
|
232
|
+
* cardholder's device sends the card through the checkout relay, and the frame gets Lyra's
|
|
233
|
+
* answer byte for byte with Lyra's payment id, so it runs any 3-D Secure step itself and its
|
|
234
|
+
* card-free follow-up passes untouched. Served on the same terms: only to a build that lists
|
|
235
|
+
* it, for a company Agentcard turned Lyra on for.
|
|
236
|
+
*
|
|
237
|
+
* `payfast_card_pay`: the card tab's pay on Payfast's hosted payment page (POST
|
|
238
|
+
* /eng/method/CreditCard/<uuid>/pay on payment.payfast.io, www.payfast.co.za or the sandbox),
|
|
239
|
+
* which this build pauses only as the card tab's own form body and answers with Payfast's own
|
|
240
|
+
* answer as the API recorded it: `[true]` once the payment is done, or the 3-D Secure step the
|
|
241
|
+
* card tab then runs in the agent's browser. The cardholder's device sends the card through the
|
|
242
|
+
* checkout relay. Served on the same terms: only to a build that lists it, for a company
|
|
243
|
+
* Agentcard turned Payfast on for.
|
|
215
244
|
*/
|
|
216
245
|
export declare const SUPPORTED_CAPABILITIES: readonly string[];
|
|
217
246
|
/**
|
|
@@ -251,7 +280,10 @@ export interface ExecutionMetadata {
|
|
|
251
280
|
* this is the processor's answer to replay into the paused request.
|
|
252
281
|
*/
|
|
253
282
|
export interface TokenReplay extends ExecutionMetadata {
|
|
254
|
-
/**
|
|
283
|
+
/**
|
|
284
|
+
* Authenticated selected-card metadata; the processor response body stays unchanged. Paddle VGS: the
|
|
285
|
+
* approved card's first six and last four digits, expiry and brand, which the frame's /pay names.
|
|
286
|
+
*/
|
|
255
287
|
processorContext?: unknown;
|
|
256
288
|
/** Present only after validating the native TEST Checkout completion. */
|
|
257
289
|
checkoutSessionId?: string;
|
|
@@ -286,6 +318,12 @@ export interface TokenReplay extends ExecutionMetadata {
|
|
|
286
318
|
chargedKind?: 'captured' | 'authorized' | 'none' | null;
|
|
287
319
|
/** Who named the amount the person approved. */
|
|
288
320
|
amountAuthority?: AmountAuthority;
|
|
321
|
+
/**
|
|
322
|
+
* An EBANX token approval that covers the sends EBANX's page makes after it (its 3-D Secure calls and the hosted
|
|
323
|
+
* page's confirm), for a company Agentcard turned that on for: until when (ISO 8601) the adapters may ask the
|
|
324
|
+
* cardholder's device to send them (sendEbanxFollowUp). Absent on every other approval.
|
|
325
|
+
*/
|
|
326
|
+
followUpsUntil?: string;
|
|
289
327
|
}
|
|
290
328
|
/**
|
|
291
329
|
* The cse flow (Adyen): the cardholder's device encrypted the card under the
|
|
@@ -313,6 +351,13 @@ export interface CseReplay extends ExecutionMetadata {
|
|
|
313
351
|
shijiStsCompanion?: Readonly<{
|
|
314
352
|
keySequenceSha256: readonly string[];
|
|
315
353
|
}>;
|
|
354
|
+
/**
|
|
355
|
+
* Paddle TokenEx only: the five metadata facts the device read from the real card, served beside the two
|
|
356
|
+
* ciphertexts in `substitutions.fields` (`paddle_tokenex_facts`). The adapters write them over the
|
|
357
|
+
* placeholder's at metadata while the ciphertexts go into tokenex_card (paddle-tokenex.ts), because the
|
|
358
|
+
* expiry parts are numbers and cannot ride the string-only `fields`.
|
|
359
|
+
*/
|
|
360
|
+
paddleTokenexFacts?: Readonly<PaddleTokenexServedFacts>;
|
|
316
361
|
amountAuthority?: AmountAuthority;
|
|
317
362
|
}
|
|
318
363
|
/**
|
|
@@ -485,7 +530,12 @@ export interface PrepareCheckoutOptions {
|
|
|
485
530
|
* it to the merchant (https://pps.creditguard.co.il/?txId=<transaction id>;
|
|
486
531
|
* pps2 is production too, ppsuat is the sandbox). Agentcard reads that
|
|
487
532
|
* transaction from CreditGuard before the cardholder is asked, and only that
|
|
488
|
-
* transaction's card submit can use the preparation.
|
|
533
|
+
* transaction's card submit can use the preparation. On a transaction that runs
|
|
534
|
+
* 3-D Secure through Cardinal, this SDK answers the page's own 3-D Secure calls
|
|
535
|
+
* and card lookup itself, so the placeholder never reaches CreditGuard, and the
|
|
536
|
+
* cardholder's device runs 3-D Secure with the real card before it sends the
|
|
537
|
+
* submit: prepare() tells Agentcard so (`three_d_secure: 'device'`), and
|
|
538
|
+
* Agentcard admits such a transaction only for a build that says it.
|
|
489
539
|
*/
|
|
490
540
|
paymentPageUrl?: string;
|
|
491
541
|
/**
|
|
@@ -1042,6 +1092,14 @@ export interface PresetRefusal {
|
|
|
1042
1092
|
}
|
|
1043
1093
|
/** A decline reason only the company presets stamp (see PresetRefusedError). */
|
|
1044
1094
|
export declare const PRESET_REFUSAL_REASONS: ReadonlySet<string>;
|
|
1095
|
+
/**
|
|
1096
|
+
* How often sendEbanxFollowUp reads a send back while the cardholder's device has it: every half second, whatever the
|
|
1097
|
+
* client's own `pollIntervalMs`. One send is out at a time per approval, so this is 120 reads a minute, inside the
|
|
1098
|
+
* API's 240 a minute per caller and authorization (routes/api/v2/checkout-follow-ups.ts).
|
|
1099
|
+
*/
|
|
1100
|
+
export declare const EBANX_FOLLOW_UP_READ_MS = 500;
|
|
1101
|
+
/** The longest sendEbanxFollowUp waits after reads the API refused as too many (429): 1, 2, 4, 8, then 15 seconds. */
|
|
1102
|
+
export declare const EBANX_FOLLOW_UP_BACKOFF_MAX_MS = 15000;
|
|
1045
1103
|
export declare class CheckoutApiError extends Error {
|
|
1046
1104
|
status: number;
|
|
1047
1105
|
path: string;
|
|
@@ -1101,6 +1159,11 @@ export declare class CheckoutAuthError extends Error {
|
|
|
1101
1159
|
* On Rapyd's hosts the hosted page's id reads `{token}`: https://api.rapyd.net/v1/checkout/{token}.
|
|
1102
1160
|
* On api.stripe.com a Checkout Session id reads `{session}`:
|
|
1103
1161
|
* https://api.stripe.com/v1/payment_pages/{session}/confirm.
|
|
1162
|
+
* On Payfast's hosts the payment's uuid reads `{uuid}`:
|
|
1163
|
+
* https://payment.payfast.io/eng/method/CreditCard/{uuid}/pay.
|
|
1164
|
+
* On Redsys's hosts the session id after `;jsessionid=` reads `{session}`:
|
|
1165
|
+
* https://sis.redsys.es/sis/pagoTarjeta;jsessionid={session}.
|
|
1166
|
+
* On Lyra's API hosts the form token's session id reads `{session}`.
|
|
1104
1167
|
* A string that is not a URL keeps the part before any `?` or `#`, with the
|
|
1105
1168
|
* same segments masked.
|
|
1106
1169
|
*/
|
|
@@ -1457,6 +1520,32 @@ export declare class VaultClient {
|
|
|
1457
1520
|
}): Promise<{
|
|
1458
1521
|
setupIntentId: string;
|
|
1459
1522
|
}>;
|
|
1523
|
+
/**
|
|
1524
|
+
* Send one request an approved EBANX token request covers (ebanx.ts says which: EBANX's 3-D Secure calls and the
|
|
1525
|
+
* hosted page's confirm, bound to that approval) from the cardholder's device, and resolve with EBANX's answer for
|
|
1526
|
+
* the paused request: POST /v2/checkout/authorizations/:id/follow_ups with the request as the page sent it, the
|
|
1527
|
+
* agent's placeholder card in it, then the send read back every half second until the device answered
|
|
1528
|
+
* (EBANX_FOLLOW_UP_READ_MS, whatever `pollIntervalMs` says, so the reads stay inside the API's per-approval budget).
|
|
1529
|
+
* A read the API refuses as too many (429) waits longer each time, doubling from a second up to
|
|
1530
|
+
* EBANX_FOLLOW_UP_BACKOFF_MAX_MS, inside the same deadline, and the next answered read goes back to the half second.
|
|
1531
|
+
* No prompt: the cardholder approved the token request, and the Vault page that sent it sends these while it stays
|
|
1532
|
+
* open. Rejects with an EbanxFollowUpError naming why there is no answer and whether the card may have reached
|
|
1533
|
+
* EBANX: the API refused the send (nothing sent), no device started it in time, the device refused it or withheld
|
|
1534
|
+
* an answer that carried the card, or `options.until` (the approval's window, plus the API's own clocks) passed.
|
|
1535
|
+
*/
|
|
1536
|
+
sendEbanxFollowUp(authorizationId: string, request: {
|
|
1537
|
+
url: string;
|
|
1538
|
+
method: 'POST';
|
|
1539
|
+
headers: Record<string, string>;
|
|
1540
|
+
body: string;
|
|
1541
|
+
}, options?: {
|
|
1542
|
+
signal?: AbortSignal;
|
|
1543
|
+
until?: number;
|
|
1544
|
+
}): Promise<{
|
|
1545
|
+
status: number;
|
|
1546
|
+
headers: Record<string, string>;
|
|
1547
|
+
body: string;
|
|
1548
|
+
}>;
|
|
1460
1549
|
/**
|
|
1461
1550
|
* Create a merchant-session probe, the phase-0 read of the deterministic merchant total:
|
|
1462
1551
|
* `user` is the org's connected user, who opens the returned Vault link and reads the
|