@flopay/js 1.4.23 → 1.4.24
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/card-setup-B8II-Etg.d.cts +589 -0
- package/dist/card-setup-B8II-Etg.d.ts +589 -0
- package/dist/card-setup.cjs +1 -0
- package/dist/card-setup.d.cts +2 -0
- package/dist/card-setup.d.ts +2 -0
- package/dist/card-setup.mjs +1 -0
- package/dist/chunk-G6M32WZN.mjs +1 -0
- package/dist/index.d.cts +3 -588
- package/dist/index.d.ts +3 -588
- package/dist/index.mjs +1 -1
- package/package.json +7 -2
|
@@ -0,0 +1,589 @@
|
|
|
1
|
+
import { CheckoutSessionMode, CheckoutGateways, VaultCaptureBlock, NormalizedCheckoutSession, ProcessPaymentParams, CreateSessionIntentRequest, SessionIntent, SessionIntentDeclineRequest, InlineSessionDraft, DetachedCheckoutSession, CardCaptureAdapter, CardCaptureProviderId, CaptureMethod, CardCaptureMountOptions, CardCaptureEventType, CardCaptureOutcomeEvent, VaultCardThemeColors, VaultCardFieldKey } from '@flopay/shared';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Client-side cache for display-only checkout fields the backend no longer
|
|
5
|
+
* persists (`overrideAmount`, `totalAmount`, `providerItemName`,
|
|
6
|
+
* `providerPlanName`, per-line `currency`).
|
|
7
|
+
*
|
|
8
|
+
* The cache lives in `sessionStorage` so it survives the navigation from the
|
|
9
|
+
* page that creates the session to the checkout page that fetches it, but
|
|
10
|
+
* dies on tab close. An in-memory fallback keeps the SDK working in Node /
|
|
11
|
+
* SSR contexts where `sessionStorage` is unavailable.
|
|
12
|
+
*
|
|
13
|
+
* Values from the server response always win — cached values fill in only
|
|
14
|
+
* where the server returned `null` or `undefined`.
|
|
15
|
+
*/
|
|
16
|
+
/** Display-only fields per product that can be cached and merged back later. */
|
|
17
|
+
interface SessionDisplayProduct {
|
|
18
|
+
/** Catalog code (match key). */
|
|
19
|
+
code?: string;
|
|
20
|
+
/** Whether this product is a one-time item or a recurring subscription. */
|
|
21
|
+
type?: 'item' | 'subscription';
|
|
22
|
+
/** Display-only name for the product. */
|
|
23
|
+
name?: string | null;
|
|
24
|
+
totalAmount?: number;
|
|
25
|
+
overrideAmount?: number | null;
|
|
26
|
+
currency?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Display-only payload that can be stashed for later merge into a session response. */
|
|
29
|
+
interface SessionDisplayCacheData {
|
|
30
|
+
/** Session-level currency (falls into the response only when the server omits it). */
|
|
31
|
+
currency?: string;
|
|
32
|
+
products?: SessionDisplayProduct[];
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Stash display-only data for a session. Called client-side right after the
|
|
36
|
+
* server returns a session ID, so the values survive the redirect to the
|
|
37
|
+
* checkout page.
|
|
38
|
+
*/
|
|
39
|
+
declare function cacheSessionDisplayData(sessionId: string, data: SessionDisplayCacheData, options?: {
|
|
40
|
+
ttlMs?: number;
|
|
41
|
+
}): void;
|
|
42
|
+
/**
|
|
43
|
+
* Read previously-cached display data for a session, or `null` if nothing
|
|
44
|
+
* is cached (or the TTL has elapsed).
|
|
45
|
+
*/
|
|
46
|
+
declare function getSessionDisplayData(sessionId: string): SessionDisplayCacheData | null;
|
|
47
|
+
/**
|
|
48
|
+
* Drop any cached display data for a session. Call from the success page
|
|
49
|
+
* after the payment completes; otherwise the TTL handles cleanup.
|
|
50
|
+
*/
|
|
51
|
+
declare function clearSessionDisplayData(sessionId: string): void;
|
|
52
|
+
|
|
53
|
+
/** Raw billing API response wrapper. */
|
|
54
|
+
interface BillingResponse<T> {
|
|
55
|
+
data: T;
|
|
56
|
+
}
|
|
57
|
+
/** Raw checkout session from the billing API. */
|
|
58
|
+
interface RawCheckoutSession {
|
|
59
|
+
uuid: string;
|
|
60
|
+
nonce: string;
|
|
61
|
+
status: 'pending' | 'authorized' | 'completed' | 'expired';
|
|
62
|
+
successUrl: string;
|
|
63
|
+
cancelUrl: string;
|
|
64
|
+
/** Session-level currency. */
|
|
65
|
+
currency?: string;
|
|
66
|
+
createdAt?: string;
|
|
67
|
+
checkoutUrl?: string;
|
|
68
|
+
/** Unified products list returned by the billing API (post-#760). */
|
|
69
|
+
products?: Array<{
|
|
70
|
+
uuid: string;
|
|
71
|
+
checkoutSessionId: string;
|
|
72
|
+
/** 'item' or 'subscription'. */
|
|
73
|
+
type: 'item' | 'subscription';
|
|
74
|
+
code?: string;
|
|
75
|
+
name?: string | null;
|
|
76
|
+
description?: string | null;
|
|
77
|
+
quantity: number;
|
|
78
|
+
totalAmount?: number;
|
|
79
|
+
overrideAmount?: number | null;
|
|
80
|
+
currency?: string;
|
|
81
|
+
metadata?: Record<string, unknown> | null;
|
|
82
|
+
}>;
|
|
83
|
+
coupons?: string[];
|
|
84
|
+
/**
|
|
85
|
+
* Pre-discount total in cart-currency major units. Populated by billing
|
|
86
|
+
* API ≥ v1.1.2; absent on older backends.
|
|
87
|
+
*/
|
|
88
|
+
subtotalAmount?: number;
|
|
89
|
+
/** Total reduction from applied coupons (cart-currency major units). */
|
|
90
|
+
discountAmount?: number;
|
|
91
|
+
/** Final charge amount after coupon discount (cart-currency major units). */
|
|
92
|
+
totalAmount?: number;
|
|
93
|
+
checkoutMode?: CheckoutSessionMode;
|
|
94
|
+
captureMethod?: 'automatic' | 'manual';
|
|
95
|
+
paymentId?: string;
|
|
96
|
+
authorizationExpiresAt?: string;
|
|
97
|
+
/** Backend authorization/capture lifecycle reason, normalized before exposure. */
|
|
98
|
+
failureReason?: string;
|
|
99
|
+
outcome?: string;
|
|
100
|
+
gateways?: CheckoutGateways;
|
|
101
|
+
/**
|
|
102
|
+
* Generic downstream card-method id for a card already on file
|
|
103
|
+
* (TeamFloPay/backend#823) — present for returning customers so the SDK can
|
|
104
|
+
* skip the vault widget. Absent for first-time buyers.
|
|
105
|
+
*/
|
|
106
|
+
providerPaymentMethodId?: string | null;
|
|
107
|
+
accountData: {
|
|
108
|
+
userId: string;
|
|
109
|
+
firstName: string;
|
|
110
|
+
lastName: string;
|
|
111
|
+
email: string;
|
|
112
|
+
gender?: string | null;
|
|
113
|
+
city?: string | null;
|
|
114
|
+
state?: string | null;
|
|
115
|
+
country?: string | null;
|
|
116
|
+
zip?: string | null;
|
|
117
|
+
addressLine1?: string | null;
|
|
118
|
+
addressLine2?: string | null;
|
|
119
|
+
};
|
|
120
|
+
tagsData: {
|
|
121
|
+
googleContainerId?: string | null;
|
|
122
|
+
sessionId?: string | null;
|
|
123
|
+
testEventCode?: string | null;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
type AccountSnapshotTelemetryMode = 'blocking' | 'best_effort';
|
|
127
|
+
interface PaymentAPIOptions {
|
|
128
|
+
/** Flo-owned privacy-safe telemetry is enabled by default; set false to opt out. */
|
|
129
|
+
telemetry?: boolean;
|
|
130
|
+
}
|
|
131
|
+
declare const SESSION_CREATE_TELEMETRY: unique symbol;
|
|
132
|
+
/**
|
|
133
|
+
* Client-side payment API service.
|
|
134
|
+
*
|
|
135
|
+
* Mirrors the `PaymentAPI` class from the checkout project's
|
|
136
|
+
* `src/service/api.ts`. All methods call the billing API endpoints
|
|
137
|
+
* that the checkout backend exposes.
|
|
138
|
+
*/
|
|
139
|
+
declare class PaymentAPI {
|
|
140
|
+
private static readonly activeVaultCaptureRequests;
|
|
141
|
+
private readonly baseUrl;
|
|
142
|
+
private readonly directTelemetry?;
|
|
143
|
+
private readonly telemetryHooks?;
|
|
144
|
+
private directTelemetryCheckoutId?;
|
|
145
|
+
constructor(billingApiUrl: string, options?: PaymentAPIOptions);
|
|
146
|
+
/** Dispose the reporter owned by direct public usage. Internal hooks are never disposed here. */
|
|
147
|
+
destroy(): void;
|
|
148
|
+
private reportDirectFailure;
|
|
149
|
+
/**
|
|
150
|
+
* A hosted-vault snapshot timeout is an observed fallback, not a checkout
|
|
151
|
+
* failure: the widget charge is already proceeding with the session baseline.
|
|
152
|
+
* Every other snapshot failure and every default/direct timeout remains an
|
|
153
|
+
* alert-level technical error.
|
|
154
|
+
*/
|
|
155
|
+
private reportAccountSnapshotFailure;
|
|
156
|
+
private telemetryTimestamp;
|
|
157
|
+
private beginDirectTelemetryCheckout;
|
|
158
|
+
private beginDirectTelemetryOperation;
|
|
159
|
+
private adoptDirectTelemetryCheckout;
|
|
160
|
+
/**
|
|
161
|
+
* Fetch a raw checkout session by ID.
|
|
162
|
+
*
|
|
163
|
+
* `nonce` is the session-bound checkout token returned when the session
|
|
164
|
+
* was created. When supplied it is sent as the `x-checkout-session-token`
|
|
165
|
+
* header that post-#640 backends match against `checkout_session.nonce`
|
|
166
|
+
* before returning the row — the UUID alone is no longer sufficient.
|
|
167
|
+
* Backends that don't yet enforce it ignore the extra header.
|
|
168
|
+
*/
|
|
169
|
+
getCheckoutSession(checkoutSessionId: string, nonce?: string): Promise<BillingResponse<RawCheckoutSession>>;
|
|
170
|
+
/**
|
|
171
|
+
* Stash display-only data for a session so subsequent fetches can fill in
|
|
172
|
+
* fields the backend no longer persists (`overrideAmount`, `totalAmount`,
|
|
173
|
+
* `providerItemName`, `providerPlanName`).
|
|
174
|
+
*
|
|
175
|
+
* Backed by `sessionStorage` in the browser, with an in-memory fallback in
|
|
176
|
+
* Node/SSR contexts. Default TTL: 1 hour.
|
|
177
|
+
*
|
|
178
|
+
* Server-returned values always win — cached values fill in only where the
|
|
179
|
+
* server returned `null` / `undefined`.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* paymentAPI.cacheSessionDisplayData(sessionId, {
|
|
184
|
+
* currency: 'USD',
|
|
185
|
+
* items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],
|
|
186
|
+
* });
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
cacheSessionDisplayData(sessionId: string, data: SessionDisplayCacheData, options?: {
|
|
190
|
+
ttlMs?: number;
|
|
191
|
+
}): void;
|
|
192
|
+
/**
|
|
193
|
+
* Drop any cached display data for a session. Call after the payment
|
|
194
|
+
* completes; otherwise the TTL handles cleanup.
|
|
195
|
+
*/
|
|
196
|
+
clearSessionDisplayData(sessionId: string): void;
|
|
197
|
+
/**
|
|
198
|
+
* Fetch (re-mint) the hosted vault capture widget for a session
|
|
199
|
+
* (TeamFloPay/backend#823).
|
|
200
|
+
*
|
|
201
|
+
* `POST /v1/checkouts/sessions/{id}/vault/capture` returns the SDK-ready
|
|
202
|
+
* {@link VaultCaptureBlock} (`html` + `url`, plus `messageToken` /
|
|
203
|
+
* `expectedOrigin` once the backend mints them). The SDK injects `html` as
|
|
204
|
+
* the card-capture widget. This is the lazy path for explicitly card-capable
|
|
205
|
+
* sessions that do not receive an embedded `vault` block; the endpoint is
|
|
206
|
+
* idempotent and reuses session-cached creds when available.
|
|
207
|
+
*
|
|
208
|
+
* Because the endpoint is idempotent, transient network failures receive a
|
|
209
|
+
* bounded retry and a stalled request aborts after ten seconds. Concurrent
|
|
210
|
+
* callers for the same base URL, session, and nonce share the active promise,
|
|
211
|
+
* preventing React renders/remounts from racing competing recovery POSTs.
|
|
212
|
+
*
|
|
213
|
+
* The PCIVault submit *secret* the backend may include in the response is
|
|
214
|
+
* intentionally **not** read or surfaced — it is server-only and never enters
|
|
215
|
+
* the SDK runtime.
|
|
216
|
+
*
|
|
217
|
+
* `nonce` is forwarded as `x-checkout-session-token` (required by post-#640
|
|
218
|
+
* backends, matched against the session's stored nonce).
|
|
219
|
+
*/
|
|
220
|
+
getVaultCapture(checkoutSessionId: string, nonce?: string): Promise<VaultCaptureBlock>;
|
|
221
|
+
private requestVaultCapture;
|
|
222
|
+
/**
|
|
223
|
+
* Fetch and normalize a checkout session.
|
|
224
|
+
*
|
|
225
|
+
* Reads the backend's `gateways` map to enumerate provider-specific data,
|
|
226
|
+
* then wraps the session in a `NormalizedCheckoutSession` for provider-
|
|
227
|
+
* agnostic consumption.
|
|
228
|
+
*/
|
|
229
|
+
getUnifiedCheckoutSession(checkoutSessionId: string, nonce?: string): Promise<NormalizedCheckoutSession>;
|
|
230
|
+
/**
|
|
231
|
+
* Submit a tokenized payment to the billing backend.
|
|
232
|
+
*
|
|
233
|
+
* The backend will either succeed, return `type: '3ds_required'`
|
|
234
|
+
* (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.
|
|
235
|
+
*
|
|
236
|
+
* Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`
|
|
237
|
+
* and forwards `data.nonce` as `x-checkout-session-token`. Backend
|
|
238
|
+
* `TeamFloPay/backend#640` rejects callers without a matching nonce with a
|
|
239
|
+
* 401 — this method throws synchronously when `data.nonce` is missing so the
|
|
240
|
+
* problem surfaces before the network round trip.
|
|
241
|
+
*
|
|
242
|
+
* @param userId Vestigial — backend's GatewayInterceptor routes via session,
|
|
243
|
+
* not headers, so this value is no longer sent on the wire. Kept in the
|
|
244
|
+
* signature for back-compat with existing callers; will be removed in a
|
|
245
|
+
* future major version.
|
|
246
|
+
*/
|
|
247
|
+
processPayment(_userId: string, data: ProcessPaymentParams, options?: {
|
|
248
|
+
pollTimeoutMs?: number;
|
|
249
|
+
}): Promise<Response>;
|
|
250
|
+
/**
|
|
251
|
+
* Patch the buyer's account snapshot (email, name, billing address, AVS
|
|
252
|
+
* intent) onto a checkout session via
|
|
253
|
+
* `PATCH /v1/checkouts/sessions/{id}/account` (TeamFloPay/backend#823).
|
|
254
|
+
*
|
|
255
|
+
* The vault path's hosted form owns the charge end-to-end so the SDK
|
|
256
|
+
* never calls `/process` on this path; the buyer-typed AVS / billing
|
|
257
|
+
* address would otherwise be lost. The SDK calls this just before
|
|
258
|
+
* submitting the vault widget so the downstream listener mints the
|
|
259
|
+
* Stripe PaymentMethod with the right `billing_details.address` and the
|
|
260
|
+
* per-attempt + per-PM address snapshots are populated.
|
|
261
|
+
*
|
|
262
|
+
* Body shape mirrors the relevant subset of `/process`'s
|
|
263
|
+
* `ProcessCheckoutBodyDto` — same keys, same validators. The endpoint
|
|
264
|
+
* is idempotent: empty/undefined fields are not written, addresses are
|
|
265
|
+
* last-writer-wins, AVS analytics are first-writer-wins.
|
|
266
|
+
*
|
|
267
|
+
* Wrapped in `fetchWithNetworkRetry` because a transient blip on this
|
|
268
|
+
* pre-pay PATCH would silently leave AVS unsent and cause an
|
|
269
|
+
* AVS-protected charge to decline downstream.
|
|
270
|
+
*/
|
|
271
|
+
patchAccountSnapshot(sessionId: string, nonce: string, body: {
|
|
272
|
+
accountData: {
|
|
273
|
+
userId: string;
|
|
274
|
+
email: string;
|
|
275
|
+
firstName?: string | null;
|
|
276
|
+
lastName?: string | null;
|
|
277
|
+
addressLine1?: string | null;
|
|
278
|
+
addressLine2?: string | null;
|
|
279
|
+
city?: string | null;
|
|
280
|
+
state?: string | null;
|
|
281
|
+
zip?: string | null;
|
|
282
|
+
country?: string | null;
|
|
283
|
+
gender?: string | null;
|
|
284
|
+
};
|
|
285
|
+
avsCheck?: boolean;
|
|
286
|
+
avsConfig?: Record<string, unknown>;
|
|
287
|
+
}, options?: {
|
|
288
|
+
signal?: AbortSignal;
|
|
289
|
+
timeoutMs?: number;
|
|
290
|
+
/**
|
|
291
|
+
* Downgrade only a timeout to `operation.fallback` when the caller can
|
|
292
|
+
* continue safely. The promise still rejects and all other failures stay
|
|
293
|
+
* technical errors. Defaults to `blocking`.
|
|
294
|
+
*/
|
|
295
|
+
telemetryMode?: AccountSnapshotTelemetryMode;
|
|
296
|
+
}): Promise<void>;
|
|
297
|
+
/** Create a wallet/APM/PayPal intent through the session-scoped contract. */
|
|
298
|
+
createSessionIntent(sessionId: string, nonce: string, request: CreateSessionIntentRequest, options?: {
|
|
299
|
+
signal?: AbortSignal;
|
|
300
|
+
idempotencyKey?: string;
|
|
301
|
+
}): Promise<SessionIntent>;
|
|
302
|
+
/** Record a provider-neutral non-card decline without sensitive identifiers. */
|
|
303
|
+
reportSessionIntentDecline(sessionId: string, nonce: string, request: SessionIntentDeclineRequest, options?: {
|
|
304
|
+
signal?: AbortSignal;
|
|
305
|
+
}): Promise<void>;
|
|
306
|
+
/**
|
|
307
|
+
* Fetch user's prior payments by email.
|
|
308
|
+
* Used to determine if saved card UX should be shown.
|
|
309
|
+
*/
|
|
310
|
+
getPaymentsByEmail(email: string, options?: {
|
|
311
|
+
signal?: AbortSignal;
|
|
312
|
+
page?: number;
|
|
313
|
+
limit?: number;
|
|
314
|
+
}): Promise<{
|
|
315
|
+
data: Array<{
|
|
316
|
+
id: string;
|
|
317
|
+
}>;
|
|
318
|
+
total: number;
|
|
319
|
+
page: number;
|
|
320
|
+
limit: number;
|
|
321
|
+
}>;
|
|
322
|
+
/**
|
|
323
|
+
* Create a checkout session AND return the full session data in one call.
|
|
324
|
+
* Uses `?expand=true` so the backend returns the complete session
|
|
325
|
+
* instead of just a UUID — eliminating the need for a second GET.
|
|
326
|
+
*
|
|
327
|
+
* Falls back to create + GET if the backend doesn't support `expand`.
|
|
328
|
+
*
|
|
329
|
+
* Eligible sessions are created through the **detached** shell + claim flow
|
|
330
|
+
* (see {@link PaymentAPI.createDetachedSession}) and this method awaits the
|
|
331
|
+
* claim, so its resolved value is unchanged — callers still receive one
|
|
332
|
+
* fully-populated session. The win here is server-side: the create no longer
|
|
333
|
+
* contends on buyer-identity advisory locks. Callers that want to render from
|
|
334
|
+
* the shell *before* the claim lands — mounting the card form early — should
|
|
335
|
+
* call {@link PaymentAPI.createDetachedSession} directly. Pass
|
|
336
|
+
* `deferDataAttachment: false` to force the original one-shot create.
|
|
337
|
+
*/
|
|
338
|
+
createAndFetchSession(params: InlineSessionDraft): Promise<NormalizedCheckoutSession>;
|
|
339
|
+
private createAndFetchSessionRequest;
|
|
340
|
+
/**
|
|
341
|
+
* `POST /v1/checkouts/sessions` with the SDK's bounded retry budget.
|
|
342
|
+
*
|
|
343
|
+
* Network failures and the backend's documented in-progress replay share one
|
|
344
|
+
* attempt budget and one operation-wide abort signal, so the two retry modes
|
|
345
|
+
* cannot multiply into nine POSTs during an outage. The idempotency key is
|
|
346
|
+
* resolved once, before the loop — an invalid merchant-supplied key throws
|
|
347
|
+
* before any request, and every attempt of this logical create replays the
|
|
348
|
+
* same key so a timeout cannot mint a second session
|
|
349
|
+
* (TeamFloPay/backend#972).
|
|
350
|
+
*/
|
|
351
|
+
private postCheckoutSessionCreate;
|
|
352
|
+
/**
|
|
353
|
+
* Create a checkout session **detached** from its buyer and catalog data
|
|
354
|
+
* (TeamFloPay/backend#1099).
|
|
355
|
+
*
|
|
356
|
+
* Resolves as soon as the lightweight *shell* exists. That create runs no
|
|
357
|
+
* catalog validation and takes none of the buyer-identity advisory locks that
|
|
358
|
+
* serialise concurrent checkouts for the same customer, and the backend routes
|
|
359
|
+
* a gateway for it from `currency` + the buyer's country — so the shell
|
|
360
|
+
* already carries the session id, nonce, `gateways` and the hosted `vault`
|
|
361
|
+
* block. The card form can mount from it immediately.
|
|
362
|
+
*
|
|
363
|
+
* Buyer identity, address, products and coupons are attached by the returned
|
|
364
|
+
* {@link DetachedCheckoutSession.claimed} promise, which is already in flight
|
|
365
|
+
* when this resolves. **Nothing may be charged until it settles** — the
|
|
366
|
+
* billing API rejects process / intent / decline calls on an unclaimed
|
|
367
|
+
* session with `409 checkout_session_data_attachment_required`, and holds an
|
|
368
|
+
* unclaimed vault charge with a retryable `503`.
|
|
369
|
+
*
|
|
370
|
+
* Requires a billing API with `PATCH /v1/checkouts/sessions/{id}/claim`; there
|
|
371
|
+
* is no fallback to the one-shot create. Callers that want the original
|
|
372
|
+
* single-request behaviour should pass `deferDataAttachment: false` and use
|
|
373
|
+
* {@link PaymentAPI.createAndFetchSession}.
|
|
374
|
+
*/
|
|
375
|
+
createDetachedSession(params: InlineSessionDraft): Promise<DetachedCheckoutSession>;
|
|
376
|
+
/**
|
|
377
|
+
* `PATCH /v1/checkouts/sessions/{id}/claim` — attach buyer identity, address,
|
|
378
|
+
* products and coupons to a detached session shell.
|
|
379
|
+
*
|
|
380
|
+
* The backend fingerprints the payload, so a transport retry replaying the
|
|
381
|
+
* identical body returns the same claimed session rather than conflicting; a
|
|
382
|
+
* *materially different* claim for an already-claimed session returns `409`,
|
|
383
|
+
* which is surfaced without retrying. Invalid catalog data surfaces here as
|
|
384
|
+
* the same `422` the one-shot create would have returned — later in the flow,
|
|
385
|
+
* but with identical semantics.
|
|
386
|
+
*/
|
|
387
|
+
claimCheckoutSession(checkoutSessionId: string, nonce: string, payload: Record<string, unknown>, internal?: {
|
|
388
|
+
params: InlineSessionDraft;
|
|
389
|
+
startedAt: number;
|
|
390
|
+
deadline: {
|
|
391
|
+
signal: AbortSignal;
|
|
392
|
+
clear(): void;
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* Attempts the shell create needed. The `session_create` rollup below
|
|
396
|
+
* reports this rather than the claim's own attempt count, so an
|
|
397
|
+
* end-to-end span keeps meaning "attempts to create this session".
|
|
398
|
+
*/
|
|
399
|
+
createAttempt: number;
|
|
400
|
+
}): Promise<NormalizedCheckoutSession>;
|
|
401
|
+
/** Shared create/claim failure reporting so both phases classify identically. */
|
|
402
|
+
private reportSessionCreateFailure;
|
|
403
|
+
waitForCheckoutSessionCompletion(checkoutSessionId: string, options?: {
|
|
404
|
+
initialDelayMs?: number;
|
|
405
|
+
timeoutMs?: number;
|
|
406
|
+
/**
|
|
407
|
+
* Session-bound checkout token; forwarded on the poll's
|
|
408
|
+
* `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.
|
|
409
|
+
*/
|
|
410
|
+
nonce?: string;
|
|
411
|
+
}): Promise<NormalizedCheckoutSession>;
|
|
412
|
+
/** Normalize a raw session into a provider-agnostic shape. */
|
|
413
|
+
private normalizeRawSession;
|
|
414
|
+
/** Convert raw session to the SDK CheckoutSession shape. */
|
|
415
|
+
private toCheckoutSession;
|
|
416
|
+
/**
|
|
417
|
+
* Coerce a raw vault block into a typed {@link VaultCaptureBlock}. The
|
|
418
|
+
* server-only PCIVault submit `secret` is deliberately dropped so it never
|
|
419
|
+
* lands on the public session surface (logs / telemetry / client inspection).
|
|
420
|
+
*/
|
|
421
|
+
private toVaultBlock;
|
|
422
|
+
private toCheckoutSessionStatus;
|
|
423
|
+
private resolveProcessResponse;
|
|
424
|
+
private toCheckoutProcessingPending;
|
|
425
|
+
private clampRetryAfterMs;
|
|
426
|
+
/**
|
|
427
|
+
* Stash the display-only fields the consumer passed into a create-session
|
|
428
|
+
* call. Runs after the backend assigns a UUID so a later GET on the same
|
|
429
|
+
* session (typically after a redirect) can fill in fields the backend no
|
|
430
|
+
* longer persists — `overrideAmount`, `totalAmount`, `name`, etc.
|
|
431
|
+
*
|
|
432
|
+
* No-op when no UUID is available.
|
|
433
|
+
*/
|
|
434
|
+
private autoCacheDisplayData;
|
|
435
|
+
/**
|
|
436
|
+
* Merge cached display-only fields (set by {@link cacheSessionDisplayData})
|
|
437
|
+
* into a raw session response. Server values always win — cache fills in
|
|
438
|
+
* only where the server returned `null` / `undefined`.
|
|
439
|
+
*/
|
|
440
|
+
private mergeCachedDisplayData;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/** Browser operation hosted by the PCIVault card-capture adapter. */
|
|
444
|
+
type CardCaptureOperation = 'checkout' | 'card_setup';
|
|
445
|
+
/** Configuration for a {@link PciVaultCardCapture} instance. */
|
|
446
|
+
interface PciVaultCardCaptureConfig {
|
|
447
|
+
/** Checkout session id bound to the capture (for outcome correlation + trust). */
|
|
448
|
+
sessionId?: string;
|
|
449
|
+
/** Capture behavior of the checkout session, used to classify legacy outcomes safely. */
|
|
450
|
+
captureMethod?: CaptureMethod;
|
|
451
|
+
/** Distinguishes no-charge card verification telemetry from checkout payment telemetry. */
|
|
452
|
+
operation?: CardCaptureOperation;
|
|
453
|
+
/**
|
|
454
|
+
* Default strict origin for vault `postMessage` outcomes. Overridden by
|
|
455
|
+
* {@link CardCaptureMountOptions.expectedOrigin} when that is supplied at
|
|
456
|
+
* mount. When neither is set the origin gate is skipped (the widget posts
|
|
457
|
+
* same-window in the Model-A flow).
|
|
458
|
+
*/
|
|
459
|
+
expectedOrigin?: string;
|
|
460
|
+
/** Flo-owned privacy-safe telemetry is enabled by default; set false to opt out. */
|
|
461
|
+
telemetry?: boolean;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* {@link CardCaptureAdapter} backed by the backend-rendered PCIVault hosted
|
|
465
|
+
* widget.
|
|
466
|
+
*
|
|
467
|
+
* `mount()` injects the server-supplied widget HTML (re-executing its bundled
|
|
468
|
+
* `<script>` so the form bootstraps) and subscribes to the widget's
|
|
469
|
+
* `postMessage` outcome. The backend owns everything else; this adapter never
|
|
470
|
+
* touches the card data, a payment intent, or 3DS.
|
|
471
|
+
*/
|
|
472
|
+
declare class PciVaultCardCapture implements CardCaptureAdapter {
|
|
473
|
+
readonly provider: CardCaptureProviderId;
|
|
474
|
+
private readonly config;
|
|
475
|
+
private telemetryReporter?;
|
|
476
|
+
/** Reporter already initialized for this adapter's setup operation. */
|
|
477
|
+
private setupTelemetryReporter?;
|
|
478
|
+
private readonly ownsTelemetryReporter;
|
|
479
|
+
private container;
|
|
480
|
+
private messageHandler;
|
|
481
|
+
/**
|
|
482
|
+
* Parent-page-level overlay rendering the provider's verification challenge
|
|
483
|
+
* (3DS-2 iframe) on `action_required`. Owned by the adapter — not the
|
|
484
|
+
* widget — so it can sit above the host SDK's processing backdrop, which
|
|
485
|
+
* would otherwise visually cover an in-widget challenge iframe.
|
|
486
|
+
*/
|
|
487
|
+
private actionOverlay;
|
|
488
|
+
/**
|
|
489
|
+
* Listener that catches the `flopay-vault-3ds-return` postMessage from the
|
|
490
|
+
* provider's challenge return page. When the SDK owns the challenge iframe
|
|
491
|
+
* the return page lives inside *that* iframe (not the widget's), so
|
|
492
|
+
* `window.parent` is the host page — the widget's existing message
|
|
493
|
+
* listener can't see it. The SDK forwards completion into the widget via
|
|
494
|
+
* `action_completed` so the widget kicks `/3ds/complete` immediately
|
|
495
|
+
* instead of waiting on the eventual provider webhook.
|
|
496
|
+
*/
|
|
497
|
+
private threeDsReturnHandler;
|
|
498
|
+
/** Per-session integrity token to require on outcomes (from mount options). */
|
|
499
|
+
private messageToken;
|
|
500
|
+
/** Strict origin to require on outcomes, when configured. */
|
|
501
|
+
private expectedOrigin;
|
|
502
|
+
/** Latest merchant theme to push into the (cross-origin) widget. */
|
|
503
|
+
private theme;
|
|
504
|
+
/** Latest host submit-gate state to push into the widget (block its submit). */
|
|
505
|
+
private submitGateBlocked;
|
|
506
|
+
/** Latest card-field order + autofocus directive to push into the widget. */
|
|
507
|
+
private cardFieldOrder;
|
|
508
|
+
private cardAutoFocus;
|
|
509
|
+
/** Monotonic start of the current widget mount-to-ready machine interval. */
|
|
510
|
+
private mountStartedAt;
|
|
511
|
+
private vaultReadyReported;
|
|
512
|
+
private submissionStarted;
|
|
513
|
+
private submissionStartedAt;
|
|
514
|
+
private readonly listeners;
|
|
515
|
+
constructor(config?: PciVaultCardCaptureConfig);
|
|
516
|
+
mount(container: HTMLElement, options: CardCaptureMountOptions): Promise<void>;
|
|
517
|
+
private reportVaultLoadFailure;
|
|
518
|
+
on(event: CardCaptureEventType, handler: (event: CardCaptureOutcomeEvent) => void): () => void;
|
|
519
|
+
unmount(): void;
|
|
520
|
+
/**
|
|
521
|
+
* Inject the server-rendered widget HTML. `innerHTML` does not execute
|
|
522
|
+
* embedded `<script>` tags, so each script node is replaced with a freshly
|
|
523
|
+
* created element that the browser will load and run (this is what boots the
|
|
524
|
+
* PCIVault form bundle against the `data-flopay-config` container).
|
|
525
|
+
*/
|
|
526
|
+
private injectWidget;
|
|
527
|
+
private attachMessageListener;
|
|
528
|
+
private reportOutcome;
|
|
529
|
+
/**
|
|
530
|
+
* Push merchant theme colors into the hosted widget (live). The host calls
|
|
531
|
+
* this on a runtime theme switch; the widget applies them to its CSS variables
|
|
532
|
+
* without a remount. Stores the latest theme so `ready` can re-push it.
|
|
533
|
+
*/
|
|
534
|
+
applyTheme(theme: VaultCardThemeColors): void;
|
|
535
|
+
/** postMessage the current theme to the widget's (cross-origin) document. */
|
|
536
|
+
private postTheme;
|
|
537
|
+
/**
|
|
538
|
+
* Gate the widget's submit from the host. When `blocked`, the widget cancels
|
|
539
|
+
* its next submit and emits `'blocked'` instead of `'submitting'` so the host
|
|
540
|
+
* can validate merchant-DOM fields (AVS) first. Stored so `ready` re-pushes it.
|
|
541
|
+
*/
|
|
542
|
+
setSubmitGate(blocked: boolean): void;
|
|
543
|
+
/** postMessage the current submit-gate state to the widget's document. */
|
|
544
|
+
private postSubmitGate;
|
|
545
|
+
/**
|
|
546
|
+
* Push the card-field order + autofocus directive into the widget (live). The
|
|
547
|
+
* widget re-sequences its rows (DOM order, so tab order follows) and focuses
|
|
548
|
+
* its first field unless `autoFocus` is false. Stored so `ready` re-pushes it.
|
|
549
|
+
*/
|
|
550
|
+
setCardFieldOrder(order: VaultCardFieldKey[] | null, autoFocus: boolean): void;
|
|
551
|
+
/** postMessage the current field order + autofocus to the widget's document. */
|
|
552
|
+
private postCardFieldOrder;
|
|
553
|
+
private emit;
|
|
554
|
+
/**
|
|
555
|
+
* Render the provider-hosted verification challenge (e.g. Stripe 3DS-2) in a
|
|
556
|
+
* full-page overlay at the PARENT page level. The widget's inline-iframe
|
|
557
|
+
* approach is unusable because the SDK's processing backdrop sits above the
|
|
558
|
+
* vault iframe, hiding any challenge mounted inside it — by lifting the
|
|
559
|
+
* iframe to the host page the adapter can give it a z-index that wins.
|
|
560
|
+
*
|
|
561
|
+
* The overlay tears down on the next terminal outcome
|
|
562
|
+
* (`complete`/`decline`/`error`) or when the buyer closes it via the backdrop
|
|
563
|
+
* close button. Closing manually is a soft abandon — the next `/status` poll
|
|
564
|
+
* either reveals a real outcome (the challenge completed via the issuer's
|
|
565
|
+
* own redirect to `/vault/3ds/return`, which posts back into the widget) or
|
|
566
|
+
* surfaces `requires_action` again so the host can decide what to do.
|
|
567
|
+
*/
|
|
568
|
+
private showActionRequiredOverlay;
|
|
569
|
+
/**
|
|
570
|
+
* Tell the vault widget that the buyer has completed (or abandoned) the
|
|
571
|
+
* challenge. The widget responds by POSTing `/3ds/complete` — its
|
|
572
|
+
* sub-300ms sync resolver writes the follow-up attempt row immediately,
|
|
573
|
+
* so the next `/status` poll resolves to a terminal outcome instead of
|
|
574
|
+
* waiting for the eventual provider webhook.
|
|
575
|
+
*/
|
|
576
|
+
private postActionCompleted;
|
|
577
|
+
private abandonActionRequiredOverlay;
|
|
578
|
+
private hideActionRequiredOverlay;
|
|
579
|
+
/**
|
|
580
|
+
* Size the hosted-widget iframe to the height reported by the form inside it.
|
|
581
|
+
* Cross-origin iframes don't auto-size to their content, so the widget posts
|
|
582
|
+
* its measured height and we apply it here (clamped to a sane range). This is
|
|
583
|
+
* what lets the card form shrink/grow to fit instead of sitting at a fixed
|
|
584
|
+
* height.
|
|
585
|
+
*/
|
|
586
|
+
private applyHeight;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export { type CardCaptureOperation as C, PaymentAPI as P, SESSION_CREATE_TELEMETRY as S, PciVaultCardCapture as a, type PciVaultCardCaptureConfig as b, type SessionDisplayCacheData as c, type SessionDisplayProduct as d, cacheSessionDisplayData as e, clearSessionDisplayData as f, getSessionDisplayData as g };
|