@agent-cards/sdk 0.23.13 → 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/dist/cdp.js CHANGED
@@ -8,14 +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
14
  import { PADDLE_VGS_PAY_PATTERNS, PaddleVgsPayGate, isPaddleCheckoutService, recognizesPaddleVgs } from './paddle-vgs-pay.js';
15
15
  import { DatatransRepeats } from './datatrans.js';
16
16
  import { SHIJI_STS_COMPANION_WINDOW_MS, ShijiStsCompanions, ShijiStsError, isShijiStsHost, shijiStsTokenContinuation, } from './shiji-sts.js';
17
+ import { paddleTokenexTokenContinuation } from './paddle-tokenex.js';
17
18
  import { isArmedEkashuUrl } from './ekashu.js';
19
+ import { isArmedRedsysUrl } from './redsys.js';
18
20
  import { PAYPAL_HOSTED_FIELDS_PATTERNS, isPaypalHostedFieldsConfirm } from './paypal-hosted-fields.js';
21
+ import { EBANX_RESEND_PATTERNS, EbanxFollowUps, isEbanxResendUrl } from './ebanx.js';
19
22
  import { CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS, CYBERSOURCE_SECURE_ACCEPTANCE_RECIPIENT, SecureAcceptanceFollowUpWatch, isCybersourceSecureAcceptanceUrl, secureAcceptanceFollowUp, secureAcceptanceFrameAnswer, secureAcceptanceReplyLast4, } from './cybersource-secure-acceptance.js';
20
23
  import { CheckoutAttachmentError, attachmentDeadline, attachmentFailure, withinAttachmentDeadline } from './attachment.js';
21
24
  import { cseBody } from './cse-body.js';
@@ -339,8 +342,14 @@ function cseAuthorizedDetail(replay, url, shijiCvv = null) {
339
342
  */
340
343
  function cseContinuation(opts, url, body, replay, approved, preparation) {
341
344
  const shiji = shijiStsTokenContinuation(url, body, replay.substitutions, approved, replay.kind === 'merchant_hosted' || replay.kind === 'verifone' ? undefined : replay.shijiStsCompanion);
342
- return shiji ? { body: shiji.body, shijiCvv: shiji.cvv, shijiCvvKeySequenceSha256: shiji.cvvKeySequenceSha256 }
343
- : { body: cseContinuationBody(opts, body, replay, url, preparation), shijiCvv: null, shijiCvvKeySequenceSha256: null };
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 };
344
353
  }
345
354
  /**
346
355
  * The opaque page id a Shiji STS companion is bound to: the CDP session and
@@ -1758,6 +1767,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
1758
1767
  };
1759
1768
  // Juspay's tokenize answer and the synthetic tokens it issued (juspay-gate.ts).
1760
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();
1761
1772
  const aci = new AciGate(opts.vault);
1762
1773
  const eprotect = new EprotectGate(opts.vault);
1763
1774
  // The merchant's follow-up request after a delivered Secure Acceptance reply (cybersource-secure-acceptance.ts).
@@ -2050,12 +2061,15 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2050
2061
  // registry serves: one that carries the typed card is paused only to be
2051
2062
  // refused, and one with no card continues (see paypal-hosted-fields.ts).
2052
2063
  // CyberSource Secure Acceptance's /silent/ paths are armed the same way, form
2053
- // 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).
2054
2068
  const urlPatterns = [...new Set([...(derived.length > 0 ? derived : FALLBACK_CARD_PATTERNS), ...guards.patterns,
2055
2069
  ...(stripeCheckout.isEnabled() ? ['https://api.stripe.com/*'] : []), MERCADO_CHECKOUT_PATTERN, ...merchantPatterns,
2056
2070
  ...(recognizesGmo(opts.vault) ? GMO_TOKEN_V1_PATTERNS : []), ...juspayCheckout.urlPatterns(), ...cardEndpointPatterns(),
2057
2071
  ...(payrails ? PAYRAILS_GUARD_PATTERNS : []), ...PAYPAL_HOSTED_FIELDS_PATTERNS, ...CYBERSOURCE_SECURE_ACCEPTANCE_PATTERNS,
2058
- ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : [])])];
2072
+ ...(recognizesPaddleVgs(opts.vault) ? PADDLE_VGS_PAY_PATTERNS : []), ...EBANX_RESEND_PATTERNS])];
2059
2073
  const arm = async (sessionId, resume = false, worker = false) => {
2060
2074
  const key = sessionId ?? '__root__';
2061
2075
  if (armed.has(key))
@@ -2485,6 +2499,7 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2485
2499
  cybersourceFlex: { judge: (flexRequest) => microforms.judge(flexRequest, frameId, () => rereadMicroforms(sessionId, frameId)) },
2486
2500
  juspay: juspayCheckout,
2487
2501
  datatrans: datatransRepeats,
2502
+ ebanx: ebanxFollowUps,
2488
2503
  frame: typeof frameId === 'string' && frameId ? frameId : undefined,
2489
2504
  payrails,
2490
2505
  shijiSts: { companions: shijiCompanions, page: () => cdpShijiPage(sessionId, frameId) },
@@ -2598,7 +2613,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2598
2613
  // A request that skipped the spent preparation's claim and was not bound as
2599
2614
  // its retry (the approval ended while it was judged) is refused as that
2600
2615
  // claim would refuse it, never asked as a new approval.
2601
- if (terminal || lifecycle.isBlocked() || awaitingApproval || preparedRetry || Date.now() < quietUntil) {
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) {
2602
2620
  let repeatedAuthorizationId = null;
2603
2621
  if (lastSubmitted) {
2604
2622
  const body = pausedBody(request);
@@ -2607,12 +2625,12 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2607
2625
  }
2608
2626
  }
2609
2627
  const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
2610
- : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
2628
+ : followUpOut ? 'an EBANX send is in flight' : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
2611
2629
  opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
2612
2630
  if (preparation)
2613
2631
  preparationGate.retireUnboundClaim();
2614
2632
  // A card form navigation never reads that nothing was sent while a payment for this checkout may exist.
2615
- 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);
2616
2634
  if (repeatedAuthorizationId)
2617
2635
  void opts.vault.reportDuplicateGuard?.(repeatedAuthorizationId);
2618
2636
  return;
@@ -2641,6 +2659,8 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2641
2659
  // The approval the device gave, and whether it reached a browser request.
2642
2660
  let approvedId = null;
2643
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;
2644
2664
  // The business's pause, so resume resolves once the page has its answer.
2645
2665
  let businessPause = null;
2646
2666
  // A prepared Amadeus SDK add must be sent within its window (see amadeus.ts).
@@ -2920,6 +2940,14 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2920
2940
  catch {
2921
2941
  throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
2922
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
+ }
2923
2951
  await cdp.send('Fetch.fulfillRequest', {
2924
2952
  requestId: deliverTo,
2925
2953
  responseCode: replay.status,
@@ -2945,6 +2973,10 @@ export async function attachToCdp(cdp, pageSessionId, opts) {
2945
2973
  catch (err) {
2946
2974
  // Ignore the failure event caused by our own safe decline/error abort.
2947
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();
2948
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.
2949
2981
  const settlePause = () => { if (businessPause) {
2950
2982
  if (isApprovalOutcome(err))
@@ -3089,6 +3121,8 @@ export async function attachToPlaywright(page, opts) {
3089
3121
  };
3090
3122
  // Juspay's tokenize answer and the synthetic tokens it issued (juspay-gate.ts).
3091
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();
3092
3126
  const aci = new AciGate(opts.vault);
3093
3127
  let navigationBodyReplaced;
3094
3128
  const eprotect = new EprotectGate(opts.vault);
@@ -3407,12 +3441,21 @@ export async function attachToPlaywright(page, opts) {
3407
3441
  // Every request to an NMI host whose entry is served (ekashu.ts), as attachToCdp arms the
3408
3442
  // host: the card page's post and its 3-D Secure calls reach the NMI gate and fail.
3409
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())
3410
3451
  // PayPal's legacy Hosted Fields confirm, for every org, as attachToCdp arms it (paypal-hosted-fields.ts).
3411
3452
  || isPaypalHostedFieldsConfirm(url.toString())
3412
3453
  // CyberSource Secure Acceptance's /silent/ paths, for every org, as attachToCdp arms them (cybersource-secure-acceptance.ts).
3413
3454
  || isCybersourceSecureAcceptanceUrl(url.toString())
3414
3455
  // Paddle's checkout services, routed only for an org the API served Paddle VGS to (see attachToCdp).
3415
- || (isPaddleCheckoutService(url.toString()) && recognizesPaddleVgs(opts.vault)), async (route) => {
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) => {
3416
3459
  const request = route.request();
3417
3460
  // When this request paused, and the page it came from: the merchant's total is read as of now.
3418
3461
  const pausedAt = Date.now();
@@ -3505,6 +3548,7 @@ export async function attachToPlaywright(page, opts) {
3505
3548
  } },
3506
3549
  juspay: juspayCheckout,
3507
3550
  datatrans: datatransRepeats,
3551
+ ebanx: ebanxFollowUps,
3508
3552
  frame: frameOf(request),
3509
3553
  payrails,
3510
3554
  shijiSts: { companions: shijiCompanions, page: async () => (await playwrightShijiDocumentPage(request)) ?? `pw:none:${++playwrightShijiSeq}` },
@@ -3599,7 +3643,9 @@ export async function attachToPlaywright(page, opts) {
3599
3643
  // identically whichever adapter is attached. An Amadeus card request is
3600
3644
  // 'failed' (ERR_FAILED) in both too, see failsAsNetworkError (refusalFailure).
3601
3645
  // As in attachToCdp: an unbound request that skipped the spent preparation's claim is refused.
3602
- if (terminal || lifecycle.isBlocked() || awaitingApproval || preparedRetry || Date.now() < quietUntil) {
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) {
3603
3649
  let repeatedAuthorizationId = null;
3604
3650
  if (lastSubmitted) {
3605
3651
  let body = null;
@@ -3612,12 +3658,12 @@ export async function attachToPlaywright(page, opts) {
3612
3658
  }
3613
3659
  }
3614
3660
  const why = terminal ?? (lifecycle.isBlocked() ? lifecycle.getState().status : awaitingApproval ? 'an approval is already outstanding'
3615
- : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
3661
+ : followUpOut ? 'an EBANX send is in flight' : preparedRetry ? 'CheckoutPreparationError' : 'awaiting approval cooldown');
3616
3662
  opts.onEvent?.({ type: 'blocked', detail: why instanceof Error ? failureSummary(why) : String(why) });
3617
3663
  if (preparation)
3618
3664
  preparationGate.retireUnboundClaim();
3619
3665
  // As in attachToCdp: never that nothing was sent while a payment for this checkout may exist.
3620
- const refused = await refuseRoute(route, heldNavigationRefusal(terminal, awaitingApproval, lifecycle));
3666
+ const refused = await refuseRoute(route, heldNavigationRefusal(terminal, awaitingApproval || followUpOut, lifecycle));
3621
3667
  if (repeatedAuthorizationId)
3622
3668
  void opts.vault.reportDuplicateGuard?.(repeatedAuthorizationId);
3623
3669
  return refused;
@@ -3665,6 +3711,8 @@ export async function attachToPlaywright(page, opts) {
3665
3711
  // The approval the device gave, and whether it reached a browser request.
3666
3712
  let approvedId = null;
3667
3713
  let deliveryBegun = false;
3714
+ // See the same flag in attachToCdp.
3715
+ let ebanxRemembered = false;
3668
3716
  // A prepared Amadeus SDK add must be sent within its window (see amadeus.ts).
3669
3717
  let disarmSendWindow = null;
3670
3718
  try {
@@ -3879,6 +3927,11 @@ export async function attachToPlaywright(page, opts) {
3879
3927
  catch {
3880
3928
  throw new PaymentOutcomeUnknownError(replay.authorizationId, 'paddle_vgs_card_facts_unavailable');
3881
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
+ }
3882
3935
  await deliverTo.fulfill({ status: replay.status, headers: withCorsHeaders(replay.headers, cors.headers), body: replay.body });
3883
3936
  datatransRepeats.remember(frameOf(deliverRequest), deliverRequest.url(), deliverBody, replay);
3884
3937
  assertRequestLive();
@@ -3894,6 +3947,9 @@ export async function attachToPlaywright(page, opts) {
3894
3947
  }
3895
3948
  catch (err) {
3896
3949
  const dead = attempt.target?.route ?? route;
3950
+ // See the same step in attachToCdp.
3951
+ if (ebanxRemembered)
3952
+ ebanxFollowUps.stop();
3897
3953
  if (activeRequest?.attempt === attempt)
3898
3954
  activeRequest = null;
3899
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
  /**
@@ -289,6 +318,12 @@ export interface TokenReplay extends ExecutionMetadata {
289
318
  chargedKind?: 'captured' | 'authorized' | 'none' | null;
290
319
  /** Who named the amount the person approved. */
291
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;
292
327
  }
293
328
  /**
294
329
  * The cse flow (Adyen): the cardholder's device encrypted the card under the
@@ -316,6 +351,13 @@ export interface CseReplay extends ExecutionMetadata {
316
351
  shijiStsCompanion?: Readonly<{
317
352
  keySequenceSha256: readonly string[];
318
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>;
319
361
  amountAuthority?: AmountAuthority;
320
362
  }
321
363
  /**
@@ -488,7 +530,12 @@ export interface PrepareCheckoutOptions {
488
530
  * it to the merchant (https://pps.creditguard.co.il/?txId=<transaction id>;
489
531
  * pps2 is production too, ppsuat is the sandbox). Agentcard reads that
490
532
  * transaction from CreditGuard before the cardholder is asked, and only that
491
- * 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.
492
539
  */
493
540
  paymentPageUrl?: string;
494
541
  /**
@@ -1045,6 +1092,14 @@ export interface PresetRefusal {
1045
1092
  }
1046
1093
  /** A decline reason only the company presets stamp (see PresetRefusedError). */
1047
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;
1048
1103
  export declare class CheckoutApiError extends Error {
1049
1104
  status: number;
1050
1105
  path: string;
@@ -1104,6 +1159,11 @@ export declare class CheckoutAuthError extends Error {
1104
1159
  * On Rapyd's hosts the hosted page's id reads `{token}`: https://api.rapyd.net/v1/checkout/{token}.
1105
1160
  * On api.stripe.com a Checkout Session id reads `{session}`:
1106
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}`.
1107
1167
  * A string that is not a URL keeps the part before any `?` or `#`, with the
1108
1168
  * same segments masked.
1109
1169
  */
@@ -1460,6 +1520,32 @@ export declare class VaultClient {
1460
1520
  }): Promise<{
1461
1521
  setupIntentId: string;
1462
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
+ }>;
1463
1549
  /**
1464
1550
  * Create a merchant-session probe, the phase-0 read of the deterministic merchant total:
1465
1551
  * `user` is the org's connected user, who opens the returned Vault link and reads the