PaymentAPI
Client-side payment API service for interacting with the billing API endpoints.
PaymentAPI
Client-side payment API service. All methods call the billing API endpoints that the checkout backend exposes.
class PaymentAPI {
constructor(billingApiUrl: string)
}| Parameter | Type | Required | Description |
|---|---|---|---|
billingApiUrl | string | Yes | Base URL of the billing API. Trailing slashes are stripped. |
createAndFetchSession
Create a checkout session and resolve with the complete session data.
async createAndFetchSession(
params: InlineSessionDraft
): Promise<NormalizedCheckoutSession>| Parameter | Type | Required | Description |
|---|---|---|---|
params | InlineSessionDraft | Yes | Session draft — client, currency, products, account, URLs. |
Returns: Promise<NormalizedCheckoutSession> — one fully-populated session, with buyer identity, cart, coupons and totals attached.
Eligible sessions are created through the two-phase shell + claim flow (see createDetachedSession), and this method awaits the claim — so it resolves with the same single session either way. The win is server-side: the create no longer contends on the buyer-identity advisory locks that serialise concurrent checkouts for the same customer.
Call createDetachedSession instead when you want to render from the shell before the claim lands. Pass deferDataAttachment: false to force the original one-shot create.
Session-level currency is required. The SDK throws FloPayError({ type: 'validation_error', code: 'CurrencyRequired' }) before any network call when nothing can be resolved from the session-level field or a per-line currency.
createDetachedSession
Create a checkout session detached from its buyer and catalog data, resolving as soon as the lightweight shell exists.
async createDetachedSession(
params: InlineSessionDraft
): Promise<DetachedCheckoutSession>| Parameter | Type | Required | Description |
|---|---|---|---|
params | InlineSessionDraft | Yes | Session draft. currency and account.country are sent on the shell create as gateway-routing inputs; everything else goes on the claim. |
Returns: DetachedCheckoutSession — { shell, sessionId, nonce, claimed }.
The shell create runs no catalog validation and takes no buyer-identity advisory locks, and the backend routes a gateway for it — so shell already carries the session id, nonce, gateways and the hosted vault block. The card form can mount from it immediately.
Buyer identity, address, products and coupons are attached by the claimed promise, which is already in flight when this resolves.
const { shell, sessionId, nonce, claimed } = await api.createDetachedSession(params);
mountCardForm(shell.data.session?.vault); // interactive immediately
const session = await claimed; // cart, coupons, totals, buyerNothing may be charged until claimed settles. The billing API rejects process / intent / decline calls on an unclaimed session with 409 checkout_session_data_attachment_required, and holds an unclaimed vault charge with a retryable 503. In @flopay/react, SplitCardForm gates the card submit for exactly this window.
Throws: FloPayError with code InvalidCheckoutSessionResponse when the billing API returns no session shell. There is no fallback to the one-shot create — this method requires a backend that exposes PATCH /v1/checkouts/sessions/{id}/claim.
Endpoint: POST /v1/checkouts/sessions with deferDataAttachment: true
See the Session Creation guide for the full two-phase contract.
claimCheckoutSession
Attach buyer identity, address, products and coupons to a detached session shell.
async claimCheckoutSession(
checkoutSessionId: string,
nonce: string,
payload: Record<string, unknown>
): Promise<NormalizedCheckoutSession>| Parameter | Type | Required | Description |
|---|---|---|---|
checkoutSessionId | string | Yes | The shell's session UUID. |
nonce | string | Yes | Session-bound checkout token from the shell create. |
payload | Record<string, unknown> | Yes | Claim body — currency, products, couponCodes, accountData. |
Returns: Promise<NormalizedCheckoutSession> — the fully-attached session.
Endpoint: PATCH /v1/checkouts/sessions/{checkoutSessionId}/claim
The backend fingerprints the payload, so a transport retry replaying the identical body returns the same claimed session rather than conflicting. The SDK makes up to 3 attempts against a 12s deadline (override with timeoutMs on the draft) and replays a byte-identical body each time. A materially different claim for an already-claimed session returns 409 and is surfaced without retrying.
Invalid catalog data surfaces here as the same 422 the one-shot create would have returned — later in the flow, but with identical semantics.
A fourth, optional argument carries internal telemetry plumbing when the SDK calls this itself. Integrations should pass the three arguments above.
getCheckoutSession
Fetch a raw checkout session by ID.
async getCheckoutSession(
checkoutSessionId: string
): Promise<BillingResponse<RawCheckoutSession>>| Parameter | Type | Required | Description |
|---|---|---|---|
checkoutSessionId | string | Yes | The checkout session UUID. |
Returns: Promise<BillingResponse<RawCheckoutSession>> -- the raw billing API response.
Throws: FloPayError with type 'api_error' if the request fails.
Endpoint: GET /v1/checkouts/sessions/{checkoutSessionId}
Requires the x-checkout-session-token header — the session nonce returned by the create call. See Checkout session token.
getUnifiedCheckoutSession
Fetch and normalize a checkout session. Reads the backend's gateways map (see CheckoutGatewaysDto) and wraps the session in a NormalizedCheckoutSession. gateways.stripe populates data.stripe, and gateways.paypal populates data.paypal for the direct PayPal flow.
async getUnifiedCheckoutSession(
checkoutSessionId: string
): Promise<NormalizedCheckoutSession>| Parameter | Type | Required | Description |
|---|---|---|---|
checkoutSessionId | string | Yes | The checkout session UUID. |
Returns: Promise<NormalizedCheckoutSession>
Display-only fields are merged from the cache. The backend resolves product name and totalAmount from the catalog and may omit them on the response. If you stashed these values via cacheSessionDisplayData right after creating the session, the SDK merges them into the returned session's products[] only where the server returned null / undefined -- server values always win. The same merge runs inside getCheckoutSession. (overrideAmount is computed by the backend, not sourced from your input; the cache only backfills it in the rare case the server omits it.)
cacheSessionDisplayData
Stash display-only fields (name, totalAmount, currency) for a session ID so the SDK can merge them back into subsequent getCheckoutSession / getUnifiedCheckoutSession calls. (overrideAmount is computed by the backend, so you don't need to stash it.)
Call this on the client immediately after the server returns a session ID, before redirecting to the checkout page. Values are written to sessionStorage (browser) with an in-memory fallback for non-browser environments.
cacheSessionDisplayData(
sessionId: string,
data: SessionDisplayCacheData,
options?: { ttlMs?: number }
): void| Parameter | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | The checkout session UUID. |
data | SessionDisplayCacheData | Yes | Display-only payload to stash. |
options.ttlMs | number | No | Time-to-live in milliseconds. Defaults to a reasonable cache window; entries are pruned when the TTL elapses. |
import { PaymentAPI } from '@flopay/js';
const paymentAPI = new PaymentAPI(billingApiUrl);
// After your server returns the session ID:
paymentAPI.cacheSessionDisplayData(sessionId, {
currency: 'EUR',
products: [
{
code: 'initial_charge',
name: '7 Day Dollar Trial',
totalAmount: 1,
},
{
code: '4_week_subscription',
name: 'Hub Membership',
totalAmount: 24.95,
},
],
});
window.location.href = redirectUrl;The standalone function cacheSessionDisplayData is also exported from @flopay/js for use without a PaymentAPI instance.
SessionDisplayCacheData
| Field | Type | Description |
|---|---|---|
currency | string | Session-level currency. Falls into the response only when the server omits it. |
products | SessionDisplayProduct[] | Display-only fields per product, unified across items and subscriptions. |
SessionDisplayProduct
| Field | Type | Description |
|---|---|---|
code | string | Catalog code -- preferred match key. |
type | 'item' | 'subscription' | Optional, deprecated. Not used for matching — products merge on code. The backend resolves item vs subscription from the catalog. |
name | string | null | Display-only name for the product. |
totalAmount | number | Original/base price (major units). |
overrideAmount | number | null | Optional. Backend-computed override (server-authoritative); the server normally supplies it. Kept here only as a cache fallback for the rare case the server omits it. |
currency | string | Per-line currency, used only as a fallback. |
Merge rules:
- Products match on
code. - Cache only fills fields the server returned as
null/undefined. If the backend starts returning a field again, the server value wins.
clearSessionDisplayData
Drop any cached display data for a session. Call from your success/cancel page after the payment lifecycle completes; otherwise the TTL handles cleanup.
clearSessionDisplayData(sessionId: string): void| Parameter | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | The checkout session UUID. |
import { PaymentAPI } from '@flopay/js';
const paymentAPI = new PaymentAPI(billingApiUrl);
paymentAPI.clearSessionDisplayData(sessionId);The standalone function clearSessionDisplayData is also exported from @flopay/js. A companion getSessionDisplayData(sessionId) is available for reading the current cache entry (returns null if nothing is cached or the TTL has elapsed).
processPayment
Submit a tokenized payment to the billing backend. The backend will either succeed, return type: '3ds_required', or return type: 'paypal_redirect_required'.
async processPayment(
userId: string,
data: ProcessPaymentParams
): Promise<Response>| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | User ID, sent as x-user-id header. |
data | ProcessPaymentParams | Yes | Payment data including sessionId, tokenizedData, accountData, and optional chv. |
Returns: Promise<Response> -- the raw fetch response.
Endpoint: POST /v1/checkouts/sessions/{sessionId}/process
Requires the x-checkout-session-token header — the session nonce returned by the create call. See Checkout session token.
createPaymentIntent
Create a PaymentIntent on the backend with the client's payment method attached.
async createPaymentIntent(
sessionId: string,
email: string,
paymentMethodType: string,
options?: { signal?: AbortSignal; isPaypal?: string }
): Promise<Response>| Parameter | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | Checkout session ID. |
email | string | Yes | User's email. |
paymentMethodType | string | Yes | Payment method ID or type string. |
options.signal | AbortSignal | No | Abort signal for cancellation. |
options.isPaypal | string | No | Set to 'true' for PayPal payments. |
Returns: Promise<Response>
Endpoint: POST /v1/checkouts/payments/intents
Requires the x-checkout-session-token header — the session nonce returned by the create call. See Checkout session token.
createSetupIntent
Create a SetupIntent for saving payment methods without an immediate charge.
async createSetupIntent(
sessionId: string,
email: string,
paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',
options?: { signal?: AbortSignal }
): Promise<Response>| Parameter | Type | Required | Description |
|---|---|---|---|
sessionId | string | Yes | Checkout session ID. |
email | string | Yes | User's email. |
paymentMethodType | 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout' | Yes | Type of payment method to set up. |
options.signal | AbortSignal | No | Abort signal for cancellation. |
Returns: Promise<Response>
Endpoint: POST /v1/checkouts/payments/setup-intents
Requires the x-checkout-session-token header — the session nonce returned by the create call. See Checkout session token.
getPaymentsByEmail
Fetch a user's prior payments by email. Used to determine if saved card UX should be shown.
async getPaymentsByEmail(
email: string,
options?: { signal?: AbortSignal; page?: number; limit?: number }
): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | Yes | User's email. |
options.signal | AbortSignal | No | Abort signal for cancellation. |
options.page | number | No | Page number. Defaults to 1. |
options.limit | number | No | Results per page. Defaults to 1. |
Returns: Paginated list of payment records.
Throws: FloPayError with type 'api_error' if the request fails.
Endpoint: GET /v1/payments?email=...&page=...&limit=...&sortField=createdAt&sortDirection=DESC