@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/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';
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -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),
|
|
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),
|
|
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 };
|