@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.
Files changed (42) hide show
  1. package/CHANGELOG.md +38 -1
  2. package/LICENSE +38 -0
  3. package/PREFLIGHT.md +1 -1
  4. package/README.md +53 -14
  5. package/dist/adyen-merchant-hosted.generated.js +2 -2
  6. package/dist/builtin-registry.generated.js +1 -1
  7. package/dist/card-fields.generated.js +843 -3
  8. package/dist/cdp.js +101 -13
  9. package/dist/client.d.ts +91 -2
  10. package/dist/client.js +205 -13
  11. package/dist/creditguard.generated.d.ts +100 -2
  12. package/dist/creditguard.generated.js +333 -3
  13. package/dist/datatrans.generated.d.ts +19 -4
  14. package/dist/datatrans.generated.js +15 -3
  15. package/dist/ebanx.d.ts +107 -0
  16. package/dist/ebanx.generated.d.ts +78 -0
  17. package/dist/ebanx.generated.js +577 -0
  18. package/dist/ebanx.js +159 -0
  19. package/dist/index.d.ts +1 -0
  20. package/dist/index.js +2 -0
  21. package/dist/lifecycle.d.ts +35 -3
  22. package/dist/lifecycle.js +38 -3
  23. package/dist/paddle-tokenex.d.ts +56 -0
  24. package/dist/paddle-tokenex.generated.d.ts +67 -0
  25. package/dist/paddle-tokenex.generated.js +951 -0
  26. package/dist/paddle-tokenex.js +95 -0
  27. package/dist/paddle-vgs-pay.d.ts +55 -0
  28. package/dist/paddle-vgs-pay.js +115 -0
  29. package/dist/paddle-vgs.generated.d.ts +29 -1
  30. package/dist/paddle-vgs.generated.js +124 -5
  31. package/dist/paysafe.generated.d.ts +5 -7
  32. package/dist/paysafe.generated.js +50 -18
  33. package/dist/pre-claim.d.ts +46 -1
  34. package/dist/pre-claim.js +338 -17
  35. package/dist/preparation.d.ts +13 -0
  36. package/dist/preparation.js +22 -0
  37. package/dist/prepared-processor.js +6 -2
  38. package/dist/primer.generated.js +878 -38
  39. package/dist/redsys.d.ts +42 -0
  40. package/dist/redsys.js +68 -0
  41. package/dist/stripe-checkout.generated.js +479 -11
  42. package/package.json +9 -6
package/dist/ebanx.js ADDED
@@ -0,0 +1,159 @@
1
+ /**
2
+ * EBANX after its card token (payment-core ebanx.js holds the rules; this SDK vendors them as ebanx.generated.ts).
3
+ *
4
+ * The adapters pause EBANX's POST /ws/token, the cardholder's device sends the real card there, and the page
5
+ * receives EBANX's token. EBANX's hosted payment page (payment.ebanx.com, where Nuuvem's Colombia store sends its
6
+ * shoppers) and EBANX.js 1.135.0 then send the card the page holds again, and on a page this SDK is attached to,
7
+ * that card is the agent's placeholder:
8
+ * - 3-D Secure posts paymentInformation.card (number, expiry, holder) to /ws/threedsecureserver-generateToken,
9
+ * -authentications and -authentication-results (up to 48 times while a Netcetera challenge polls) on the host
10
+ * the token request went to;
11
+ * - the hosted page's confirm posts payment.card.card_cvv, the code the agent typed, beside EBANX's token, to
12
+ * /ws/checkout-v2/confirm on the invoice's host.
13
+ * Both adapters arm those requests on every attachment, whatever the synced registry serves, and the pre-claim
14
+ * gate (ebanxPreClaim, pre-claim.ts) judges each POST:
15
+ * - one the approved token request on this attachment covers (EbanxFollowUps.claim: the API said the approval
16
+ * covers sends after it, until when, and payment-core's rule binds the request to that approval): sent from the
17
+ * cardholder's device under that same approval, with no second prompt (VaultClient.sendEbanxFollowUp), and the
18
+ * page receives EBANX's answer. Never after a refused or failed send, a failed handoff of the token's answer, or a
19
+ * final merchant result (completed, failed, declined, timed out, cancelled, unknown): nothing more is covered then;
20
+ * - any other 3-D Secure call, or a confirm whose payment.card names a card code or number: abort, so EBANX never
21
+ * receives the placeholder as a card. The page shows its own payment error and nothing is charged. The checkout
22
+ * reads `unsupported` with reason `unsupported_ebanx_card_resend` and holds, as every unsupported refusal does;
23
+ * after an approval it keeps the approval's state;
24
+ * - a confirm whose body cannot be read: abort that request alone, since nothing proves it carries no card;
25
+ * - any other request (a confirm for a bank transfer, cash or a wallet, -record, a GET, a preflight): continue.
26
+ * A request the synced registry recognizes as a card request belongs to that recognizer instead; no build serves
27
+ * one today.
28
+ */
29
+ import { EBANX_CONFIRM_PATH as CONFIRM_PATH, EBANX_FOLLOW_UP_KINDS, EBANX_RESEND_HOSTS as RESEND_HOSTS, ebanxFollowUpVerdict, ebanxResendKind, ebanxResendRefusal, readEbanxTokenAnswer, readEbanxTokenRequest, } from './ebanx.generated.js';
30
+ export const EBANX_RESEND_REASON = 'unsupported_ebanx_card_resend';
31
+ /** EBANX's hosts for these requests (payment-core EBANX_RESEND_HOSTS). */
32
+ export const EBANX_RESEND_HOSTS = Object.freeze([...RESEND_HOSTS]);
33
+ /** The tenant host only the confirm uses: no card recognizer names it, so its patterns here are the only ones that pause it. */
34
+ const CONFIRM_TENANT_HOST = 'customer-local-latam.ebanx.com';
35
+ /**
36
+ * CDP Fetch patterns for the 3-D Secure calls and the confirm on each of EBANX's hosts, armed on every attachment.
37
+ * The card recognizer already arms every request on the hosts it names; these keep both requests armed for a client
38
+ * whose registry names no EBANX host. The -record call matches the 3-D Secure pattern too and continues untouched.
39
+ * The third pattern arms any address on the host that holds a percent-escape, since CDP matches the address as written
40
+ * and a server may decode /ws/%63heckout-v2/confirm to the confirm: the gate decodes the path the same way
41
+ * (payment-core ebanxResendKind) and continues every one that is not a card send. CDP matches literally and in one
42
+ * letter case, so the tenant host only the confirm uses (CONFIRM_TENANT_HOST) is armed whole, its host text anywhere
43
+ * in the address as the card recognizer arms its own hosts: /WS/CHECKOUT-V2/CONFIRM, //, ;x=1 and a trailing-dot
44
+ * host all pause, and the gate judges each one.
45
+ */
46
+ export const EBANX_RESEND_PATTERNS = Object.freeze([
47
+ ...EBANX_RESEND_HOSTS.flatMap((host) => [`https://${host}/ws/threedsecureserver-*`, `https://${host}${CONFIRM_PATH}*`, `https://${host}/*%*`]),
48
+ `*${CONFIRM_TENANT_HOST}*`,
49
+ ]);
50
+ /**
51
+ * Whether this URL is one of the three 3-D Secure calls that carry the card or the hosted page's confirm on an EBANX
52
+ * host, or an EBANX path still percent-encoded after the last decoding round, which the gate refuses as unreadable.
53
+ */
54
+ export function isEbanxResendUrl(url) {
55
+ return ebanxResendKind(url) !== null;
56
+ }
57
+ /**
58
+ * Why this request must not leave the browser with the card the page holds, or null when it may. The body is read
59
+ * only for a confirm, through `readBody`, and a reader that throws reads as a body nobody could read.
60
+ */
61
+ export function ebanxResendRefusalOf(url, method, readBody) {
62
+ return ebanxResendRefusal({
63
+ url, method,
64
+ get body() { try {
65
+ return readBody();
66
+ }
67
+ catch {
68
+ return null;
69
+ } },
70
+ });
71
+ }
72
+ /**
73
+ * A send the cardholder's device did not answer with EBANX's own answer. `reason` is the API's word for it:
74
+ * `not_picked_up` (no device started it in time: the Vault page was closed or asleep), `not_sent` (the device
75
+ * refused it before the card left), `no_answer`, `answer_withheld` (EBANX's answer carried the card),
76
+ * `answer_refused`, `answer_late`, `response_expired`, or the API's refusal code when it did not take the send at
77
+ * all (`follow_up_refused`, `follow_up_not_covered`, `follow_up_window_closed`). `cardSent` says whether the card
78
+ * may have reached EBANX: false only when the API confirmed it never left the device. Nothing in it names the
79
+ * request, the card or EBANX's answer.
80
+ */
81
+ export class EbanxFollowUpError extends Error {
82
+ reason;
83
+ cardSent;
84
+ authorizationId;
85
+ constructor(reason, cardSent, authorizationId) {
86
+ super(cardSent
87
+ ? `This EBANX request under ${authorizationId} has no answer, and the cardholder's device may have sent it: ${reason}.`
88
+ : `The cardholder's device did not send this EBANX request under ${authorizationId}: ${reason}.`);
89
+ this.reason = reason;
90
+ this.cardSent = cardSent;
91
+ this.authorizationId = authorizationId;
92
+ this.name = 'EbanxFollowUpError';
93
+ }
94
+ }
95
+ /**
96
+ * What one attachment keeps of its approved EBANX token request: the request and EBANX's answer as the page received
97
+ * them, until when the API said the approval covers sends after it, and the sends it claimed since, in order. Only
98
+ * a token approval whose read named that window (`followUpsUntil`, served for a company Agentcard turned it on for)
99
+ * is kept; a later approval replaces it, and a send the window has passed is never claimed. Once the gate refuses a
100
+ * send or a send fails, the attachment stops (stop()): nothing after it is claimed, a later approval included, so
101
+ * a page the controller already reads as stopped cannot finish 3-D Secure and pay through the confirm.
102
+ */
103
+ export class EbanxFollowUps {
104
+ parent = null;
105
+ sent = [];
106
+ stopped = false;
107
+ /** After a refused or failed send: no later request on this attachment is claimed. */
108
+ stop() { this.stopped = true; }
109
+ /**
110
+ * The approval the remembered token request went under, or null when none is remembered. The gate sends nothing
111
+ * unless the checkout's own state names this same approval (pre-claim.ts ebanxMaySend).
112
+ */
113
+ get authorizationId() { return this.parent?.authorizationId ?? null; }
114
+ /**
115
+ * As the adapter hands an approved token request's answer to the page: before the answer lands, since the page may
116
+ * send its first 3-D Secure call at once. A handoff that fails then stops the attachment (stop()).
117
+ */
118
+ remember(request, replay) {
119
+ const until = typeof replay.followUpsUntil === 'string' ? Date.parse(replay.followUpsUntil) : NaN;
120
+ if ((replay.mode ?? 'token') !== 'token' || !Number.isFinite(until))
121
+ return;
122
+ const answer = { status: replay.status, text: replay.body };
123
+ if (!readEbanxTokenRequest({ url: request.url, body: request.body }) || !readEbanxTokenAnswer(answer))
124
+ return;
125
+ this.parent = { authorizationId: replay.authorizationId, url: request.url, body: request.body, answer, until };
126
+ this.sent = [];
127
+ }
128
+ /**
129
+ * The claim for a paused request the remembered approval covers, or null: no approval covers sends here, its
130
+ * window has passed, or payment-core's rule refuses the request against the sends already claimed (the amount
131
+ * is left to the API and the device, which hold the approved figure). Synchronous, and it reserves the send, so
132
+ * two requests can never both be claimed as the next one.
133
+ */
134
+ claim(request, now = Date.now()) {
135
+ const parent = this.parent;
136
+ if (this.stopped || !parent || now >= parent.until || typeof request.body !== 'string')
137
+ return null;
138
+ const verdict = ebanxFollowUpVerdict({ parent, sent: this.sent, request: { url: request.url, method: request.method, body: request.body } }, { skipAmount: true });
139
+ if (!verdict.ok || !EBANX_FOLLOW_UP_KINDS.includes(verdict.kind))
140
+ return null;
141
+ const entry = { kind: verdict.kind, state: 'pending', body: request.body };
142
+ this.sent.push(entry);
143
+ const contentType = Object.entries(request.headers ?? {}).find(([name]) => /^content-type$/i.test(name))?.[1];
144
+ return {
145
+ authorizationId: parent.authorizationId, kind: verdict.kind, until: parent.until,
146
+ request: { url: request.url, method: 'POST', headers: { 'content-type': typeof contentType === 'string' ? contentType : 'text/plain' }, body: request.body },
147
+ settle(answer) {
148
+ if (entry.state !== 'pending')
149
+ return;
150
+ if (answer) {
151
+ entry.state = 'answered';
152
+ entry.answer = answer;
153
+ }
154
+ else
155
+ entry.state = 'failed';
156
+ },
157
+ };
158
+ }
159
+ }
package/dist/index.d.ts CHANGED
@@ -14,6 +14,7 @@ export type { PausedRequest, ReplayResponse, TokenReplay, CseReplay, MerchantHos
14
14
  export type { MicroformField, MicroformSidecar } from './cybersource-flex.js';
15
15
  export { attachToCdp, attachToPlaywright, corsHeadersFor, corsDecision, withCorsHeaders } from './cdp.js';
16
16
  export { CheckoutAttachmentError } from './attachment.js';
17
+ export { EbanxFollowUpError } from './ebanx.js';
17
18
  export { CardEndpointRefusedError, cardEndpointPatterns } from './card-endpoint.js';
18
19
  export { MerchantSessionRefusedError, captureMerchantSession, merchantSessionCapture, merchantSessionReadUrl } from './merchant-session-probe.js';
19
20
  export type { MerchantSessionCapture, MerchantSessionCookie, MerchantSessionCdp, MerchantSessionRefusal } from './merchant-session-probe.js';
package/dist/index.js CHANGED
@@ -5,6 +5,8 @@ export { CardEncryptedError, UnsupportedModeError, UnsupportedCapabilityError, P
5
5
  export { CONVERGE_REFUSAL_REASONS } from './elavon-converge.js';
6
6
  export { attachToCdp, attachToPlaywright, corsHeadersFor, corsDecision, withCorsHeaders } from './cdp.js';
7
7
  export { CheckoutAttachmentError } from './attachment.js';
8
+ // EBANX after its card token: why a send an approved token request covers got no answer from the cardholder's device.
9
+ export { EbanxFollowUpError } from './ebanx.js';
8
10
  export { CardEndpointRefusedError, cardEndpointPatterns } from './card-endpoint.js';
9
11
  // The merchant-session probe's capture (the deterministic merchant total, phase 0): no card, no Pay.
10
12
  export { MerchantSessionRefusedError, captureMerchantSession, merchantSessionCapture, merchantSessionReadUrl } from './merchant-session-probe.js';
@@ -4,6 +4,7 @@ import type { PayrailsUnsupportedReason } from './payrails.js';
4
4
  import type { Substitutions } from './substitute.js';
5
5
  import type { GmoUnsupportedReason } from './gmo.js';
6
6
  import type { ShijiStsUnsupportedReason } from './shiji-sts.js';
7
+ import type { PaddleVgsPayRefusal } from './paddle-vgs-pay.js';
7
8
  /** The application's resolver confirmed the payment for this checkout authorization. */
8
9
  export interface MerchantPaymentConfirmation {
9
10
  kind: 'merchant_payment';
@@ -91,6 +92,11 @@ export interface ResumeResult {
91
92
  headers: Record<string, string>;
92
93
  body: string;
93
94
  };
95
+ /**
96
+ * The read's `processor_context`, as the API serves it. A Paddle VGS read carries the approved card's
97
+ * details here, which the frame's /pay names; without them the stored card's id is never handed to the page.
98
+ */
99
+ processor_context?: unknown;
94
100
  substitutions?: Substitutions;
95
101
  submitted_at?: string;
96
102
  execution_mode?: string;
@@ -152,6 +158,11 @@ export declare class CheckoutLifecycle implements CheckoutController {
152
158
  private ownedShopOrderId;
153
159
  private readonly handoffs;
154
160
  private reconciliation;
161
+ /**
162
+ * EBANX sends this attachment claimed that are still out (pre-claim.ts ebanxFollowUp): the cardholder's device may be
163
+ * sending a confirm that pays, outside `active`, so reconcile() and retryAfterMerchantFailure() wait for them too.
164
+ */
165
+ private followUpsOut;
155
166
  private preparationHandler?;
156
167
  private preparationUsed;
157
168
  private resumeHandler;
@@ -168,6 +179,21 @@ export declare class CheckoutLifecycle implements CheckoutController {
168
179
  prepared(preparation: PreparedCheckout): void;
169
180
  preparationFailed(error: CheckoutPreparationError): void;
170
181
  isBlocked(): boolean;
182
+ /**
183
+ * An EBANX send left for the cardholder's device under this attachment's approval (pre-claim.ts ebanxFollowUp). Until
184
+ * the returned function runs, once the send's answer reached the page or the send failed, reconcile() and
185
+ * retryAfterMerchantFailure() throw, as they do while a card request is active: a confirm may still pay. The returned
186
+ * function settles the send once, however often it runs.
187
+ */
188
+ followUpStarted(): () => void;
189
+ /** Whether a reconcile() is still waiting on the application's merchant resolver; pre-claim.ts claims no EBANX send then. */
190
+ isReconciling(): boolean;
191
+ /**
192
+ * Whether an EBANX send this attachment claimed is still out (followUpStarted). Both adapters refuse a new card
193
+ * request meanwhile, as they do while an approval is outstanding: a confirm that may pay is never hidden behind
194
+ * another payment's state.
195
+ */
196
+ followUpsInFlight(): boolean;
171
197
  isCancelled(): boolean;
172
198
  /** Whether the application gave a merchant resolver, so reconcile() reads the merchant's own record. */
173
199
  hasMerchantResolver(): boolean;
@@ -236,12 +262,18 @@ export declare class CheckoutLifecycle implements CheckoutController {
236
262
  * shiji-sts.ts reasons), a post to a processor's own card page that only
237
263
  * the cardholder's device can fill (processor_card_page: NMI's), a card
238
264
  * sent to PayPal's legacy Hosted Fields confirm, which no Vault mode takes
239
- * over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), or a card
265
+ * over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), a card
240
266
  * a merchant's page posts to CyberSource Secure Acceptance's Silent Order
241
267
  * POST, which no Vault mode takes over either
242
- * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts).
268
+ * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts), a
269
+ * Paddle /pay naming a stored card that its binding does not continue (the
270
+ * paddle-vgs-pay.ts reasons), or the typed card that EBANX's 3-D Secure calls or
271
+ * its hosted page's confirm send again after the token, when no approved token
272
+ * request on this attachment covers that send or the cardholder's device did not
273
+ * send it (unsupported_ebanx_card_resend, ebanx.ts): once an approval exists the
274
+ * state keeps it, and only the hold changes.
243
275
  */
244
- unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'merchant_card_endpoint_resend_refused' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason | 'processor_card_page' | 'unsupported_paypal_hosted_fields' | 'unsupported_cybersource_secure_acceptance'): void;
276
+ unsupported(reason?: 'unrecognized_payment_endpoint' | 'merchant_profile_observe' | 'merchant_request_unsupported' | GmoUnsupportedReason | 'microform_unsupported' | 'juspay_request_unsupported' | 'merchant_card_endpoint_not_enabled' | 'merchant_card_endpoint_resend_refused' | 'creditguard_three_d_secure' | 'unsupported_primer_sdk' | 'unsupported_primer_request' | PayrailsUnsupportedReason | ShijiStsUnsupportedReason | 'processor_card_page' | 'unsupported_paypal_hosted_fields' | 'unsupported_cybersource_secure_acceptance' | PaddleVgsPayRefusal | 'unsupported_ebanx_card_resend'): void;
245
277
  /**
246
278
  * A card request that only a prepared checkout pays (an Adyen merchant
247
279
  * profile's, or a Fiserv card capture) arrived with no preparation on this
package/dist/lifecycle.js CHANGED
@@ -64,6 +64,11 @@ export class CheckoutLifecycle {
64
64
  // Every request this attachment handed the card to: see cardHandoffs().
65
65
  handoffs = [];
66
66
  reconciliation = null;
67
+ /**
68
+ * EBANX sends this attachment claimed that are still out (pre-claim.ts ebanxFollowUp): the cardholder's device may be
69
+ * sending a confirm that pays, outside `active`, so reconcile() and retryAfterMerchantFailure() wait for them too.
70
+ */
71
+ followUpsOut = 0;
67
72
  preparationHandler;
68
73
  preparationUsed = false;
69
74
  resumeHandler = null;
@@ -104,6 +109,28 @@ export class CheckoutLifecycle {
104
109
  this.set({ ...this.state, status: error.reason === 'expired' ? 'timed_out' : error.reason === 'cancelled' ? 'cancelled' : 'failed', reason: error.reason });
105
110
  }
106
111
  isBlocked() { return this.held || this.cancelled; }
112
+ /**
113
+ * An EBANX send left for the cardholder's device under this attachment's approval (pre-claim.ts ebanxFollowUp). Until
114
+ * the returned function runs, once the send's answer reached the page or the send failed, reconcile() and
115
+ * retryAfterMerchantFailure() throw, as they do while a card request is active: a confirm may still pay. The returned
116
+ * function settles the send once, however often it runs.
117
+ */
118
+ followUpStarted() {
119
+ this.followUpsOut += 1;
120
+ let settled = false;
121
+ return () => { if (!settled) {
122
+ settled = true;
123
+ this.followUpsOut -= 1;
124
+ } };
125
+ }
126
+ /** Whether a reconcile() is still waiting on the application's merchant resolver; pre-claim.ts claims no EBANX send then. */
127
+ isReconciling() { return this.reconciliation !== null; }
128
+ /**
129
+ * Whether an EBANX send this attachment claimed is still out (followUpStarted). Both adapters refuse a new card
130
+ * request meanwhile, as they do while an approval is outstanding: a confirm that may pay is never hidden behind
131
+ * another payment's state.
132
+ */
133
+ followUpsInFlight() { return this.followUpsOut > 0; }
107
134
  isCancelled() { return this.cancelled; }
108
135
  /** Whether the application gave a merchant resolver, so reconcile() reads the merchant's own record. */
109
136
  hasMerchantResolver() { return typeof this.options.resolveMerchantResult === 'function'; }
@@ -235,10 +262,16 @@ export class CheckoutLifecycle {
235
262
  * shiji-sts.ts reasons), a post to a processor's own card page that only
236
263
  * the cardholder's device can fill (processor_card_page: NMI's), a card
237
264
  * sent to PayPal's legacy Hosted Fields confirm, which no Vault mode takes
238
- * over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), or a card
265
+ * over (unsupported_paypal_hosted_fields, paypal-hosted-fields.ts), a card
239
266
  * a merchant's page posts to CyberSource Secure Acceptance's Silent Order
240
267
  * POST, which no Vault mode takes over either
241
- * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts).
268
+ * (unsupported_cybersource_secure_acceptance, cybersource-secure-acceptance.ts), a
269
+ * Paddle /pay naming a stored card that its binding does not continue (the
270
+ * paddle-vgs-pay.ts reasons), or the typed card that EBANX's 3-D Secure calls or
271
+ * its hosted page's confirm send again after the token, when no approved token
272
+ * request on this attachment covers that send or the cardholder's device did not
273
+ * send it (unsupported_ebanx_card_resend, ebanx.ts): once an approval exists the
274
+ * state keeps it, and only the hold changes.
242
275
  */
243
276
  unsupported(reason = 'unrecognized_payment_endpoint') {
244
277
  this.held = true;
@@ -464,6 +497,8 @@ export class CheckoutLifecycle {
464
497
  return Promise.resolve(this.getState());
465
498
  if (this.active)
466
499
  throw new Error('Wait for the paused request to finish before reconciling the merchant order.');
500
+ if (this.followUpsOut > 0)
501
+ throw new Error('Wait for the EBANX send in flight to finish before reconciling the merchant order.');
467
502
  this.reconciliation = this.resolve().finally(() => { this.reconciliation = null; });
468
503
  return this.reconciliation;
469
504
  }
@@ -517,7 +552,7 @@ export class CheckoutLifecycle {
517
552
  throw new Error('This shop payment already has a receipt; another payment is not permitted.');
518
553
  if (this.preparationUsed)
519
554
  throw new Error('Prepared checkouts are single use. Reconcile this attempt and create a new attachment.');
520
- if (this.active || this.reconciliation)
555
+ if (this.active || this.reconciliation || this.followUpsOut > 0)
521
556
  throw new Error('Cannot start another attempt while payment or reconciliation is in progress.');
522
557
  if (this.cancelled)
523
558
  throw new Error('This attachment was cancelled; attach again after reconciling the merchant order.');
@@ -0,0 +1,56 @@
1
+ /** The PSP id the API and the recognizer name this lane, re-exported so the client dispatches by it. */
2
+ export { PADDLE_TOKENEX_PSP } from './paddle-tokenex.generated.js';
3
+ /** The dark-launch capability a build declares when it fills a Paddle TokenEx /pay with the device's card. */
4
+ export declare const PADDLE_TOKENEX_CAPABILITY = "paddle_tokenex_pay";
5
+ /** A cse substitution as the API serves it (substitute.ts Substitutions): here the two ciphertexts only. */
6
+ interface PaddleTokenexServedSubstitution {
7
+ encoding: string;
8
+ at: string;
9
+ fields: Record<string, string>;
10
+ remove?: string[];
11
+ }
12
+ /**
13
+ * Whether an approved Paddle TokenEx authorization's served substitution is one this SDK can continue: JSON
14
+ * at the frame's tokenex_card holder, no sibling to remove, and exactly the two ciphertext fields, each a
15
+ * non-empty string and distinct from the other. substitutePaddleTokenex checks each against the pinned key
16
+ * for the /pay's environment; this is the shape gate the client runs before it acts.
17
+ */
18
+ export declare function isPaddleTokenexServedSubstitution(sub: unknown): sub is PaddleTokenexServedSubstitution;
19
+ /** The five metadata facts the device read from the real card, served beside the ciphertexts. */
20
+ export interface PaddleTokenexServedFacts {
21
+ card_first_six: string;
22
+ card_last_four: string;
23
+ expiry_month: number;
24
+ expiry_year: number;
25
+ card_brand: string;
26
+ }
27
+ /**
28
+ * Whether the API's `paddle_tokenex_facts` beside an approved set is the five facts this SDK can write over
29
+ * the placeholder's: exactly the five keys, the three strings as strings and the two expiry parts as
30
+ * numbers. paddleTokenexSubstitutionOf checks the digits, range and network at the swap; this is the shape
31
+ * gate the client runs before it acts, so a malformed approval is refused before anything is written.
32
+ */
33
+ export declare function isPaddleTokenexServedFacts(value: unknown): value is PaddleTokenexServedFacts;
34
+ /** The paused request an approval was created from: the URL and body the API reviewed at create. */
35
+ export interface PaddleTokenexApprovedRequest {
36
+ url: string;
37
+ body: string;
38
+ }
39
+ /**
40
+ * The continuation of an approved Paddle TokenEx /pay: the paused body with the device's two ciphertexts
41
+ * written over the placeholder's at tokenex_card and the five facts over the placeholder's at metadata
42
+ * (payment-core substitutePaddleTokenex, which refuses a ciphertext that is not one under the pinned key
43
+ * for the /pay's environment, or one already in the paused body).
44
+ *
45
+ * Only into `approved`, the paused /pay the approval was created from: `url` and `body` must equal it byte
46
+ * for byte. The account-level TokenEx key is the same for every checkout of that seller, so the device's
47
+ * ciphertext could be decrypted in any later /pay of the same shape; binding it to the one request keeps
48
+ * this lane from ever submitting one checkout's ciphertext to another.
49
+ *
50
+ * Null when neither URL is a TokenEx /pay, so every other processor keeps the generic swap. Throws for any
51
+ * other body or URL, and for a substitution or facts the shape gates refuse, in each case before anything
52
+ * continues.
53
+ */
54
+ export declare function paddleTokenexTokenContinuation(url: string, body: string, sub: unknown, facts: unknown, approved: PaddleTokenexApprovedRequest): {
55
+ body: string;
56
+ } | null;
@@ -0,0 +1,67 @@
1
+ declare function paddleVgsCardFactsAgree(facts: any, stored: any): boolean;
2
+ declare var PADDLE_TOKENEX_PSP: string;
3
+ declare var PADDLE_TOKENEX_CAPABILITY: string;
4
+ declare var PADDLE_TOKENEX_OPERATION: string;
5
+ declare var PADDLE_TOKENEX_SCHEME: string;
6
+ declare var PADDLE_TOKENEX_FIELDS: readonly string[];
7
+ declare var PADDLE_TOKENEX_FACT_KEYS: readonly string[];
8
+ declare var PADDLE_TOKENEX_CARD_NETWORKS: readonly string[];
9
+ declare var PADDLE_TOKENEX_REFUSAL_CODES: readonly string[];
10
+ declare function paddleTokenexPayTarget(url: any): Readonly<{
11
+ environment: "production" | "sandbox";
12
+ line: "classic" | "billing";
13
+ }> | null;
14
+ declare function paddleTokenexPayCarriesCard(body: any): boolean;
15
+ declare function isPaddleTokenexCiphertextUnderKey(value: any, environment: any): boolean;
16
+ declare function isPaddleTokenexCiphertext(value: any, environment: any): boolean;
17
+ declare function paddleTokenexRequestRefusal(request: any, environment: any): "paddle_tokenex_pay_unsupported" | null;
18
+ declare function isPaddleTokenexCardRequest(request: any): boolean;
19
+ declare function paddleTokenexBodyVerdict(body: any, line: any): Readonly<{
20
+ verdict: "continue";
21
+ }> | Readonly<{
22
+ verdict: "card";
23
+ }> | Readonly<{
24
+ verdict: "refuse";
25
+ code: any;
26
+ }>;
27
+ declare function paddleTokenexRefusalMessage(code: any): any;
28
+ declare function paddleTokenexStoredCardRefusal(brand: any): "paddle_tokenex_card_unsupported" | null;
29
+ declare function encryptPaddleTokenexCard(card: any, environment: any, facilities?: {}): Readonly<{
30
+ card_first_six: string;
31
+ card_last_four: string;
32
+ expiry_month: number;
33
+ expiry_year: number;
34
+ card_brand: "visa" | "master" | "american_express";
35
+ encrypted_card_number: string;
36
+ encrypted_cvv: string;
37
+ }>;
38
+ declare function paddleTokenexSubstitutionOf(value: any, environment: any): Readonly<{
39
+ card_first_six: any;
40
+ card_last_four: string;
41
+ expiry_month: any;
42
+ expiry_year: any;
43
+ card_brand: any;
44
+ encrypted_card_number: any;
45
+ encrypted_cvv: any;
46
+ }> | null;
47
+ declare function substitutePaddleTokenex(url: any, body: any, substitution: any): string;
48
+ declare function paddleTokenexKeyUsable(authorization: any): boolean;
49
+ declare function paddleTokenexSubstitutionsRefusal(template: any, substitution: any): {
50
+ code: string;
51
+ message: any;
52
+ } | null;
53
+ declare function paddleTokenexRefusalCode(text: any): string | null;
54
+ declare function paddleTokenexAnswerOutcome(status: any, text: any): {
55
+ outcome: string;
56
+ reason: string;
57
+ code?: undefined;
58
+ } | {
59
+ outcome: string;
60
+ code: string;
61
+ reason?: undefined;
62
+ } | {
63
+ outcome: string;
64
+ reason?: undefined;
65
+ code?: undefined;
66
+ };
67
+ export { PADDLE_TOKENEX_CAPABILITY, PADDLE_TOKENEX_CARD_NETWORKS, PADDLE_TOKENEX_FACT_KEYS, PADDLE_TOKENEX_FIELDS, PADDLE_TOKENEX_OPERATION, PADDLE_TOKENEX_PSP, PADDLE_TOKENEX_REFUSAL_CODES, PADDLE_TOKENEX_SCHEME, encryptPaddleTokenexCard, isPaddleTokenexCardRequest, isPaddleTokenexCiphertext, isPaddleTokenexCiphertextUnderKey, paddleTokenexAnswerOutcome, paddleTokenexBodyVerdict, paddleVgsCardFactsAgree as paddleTokenexCardFactsAgree, paddleTokenexKeyUsable, paddleTokenexPayCarriesCard, paddleTokenexPayTarget, paddleTokenexRefusalCode, paddleTokenexRefusalMessage, paddleTokenexRequestRefusal, paddleTokenexStoredCardRefusal, paddleTokenexSubstitutionOf, paddleTokenexSubstitutionsRefusal, substitutePaddleTokenex };