@flopay/js 1.3.1 → 1.3.3
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/README.md +39 -0
- package/dist/index.cjs +79 -38
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.mjs +85 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/pci-vault-card-capture.ts","../src/create-checkout-session.ts"],"sourcesContent":["export { loadFloPay } from './load.js';\nexport { FloPay } from './flopay.js';\nexport { FloPayElements } from './elements.js';\nexport { StripeAdapter } from './stripe-adapter.js';\nexport { PaymentAPI } from './payment-api.js';\nexport { PciVaultCardCapture } from './pci-vault-card-capture.js';\nexport type { PciVaultCardCaptureConfig } from './pci-vault-card-capture.js';\nexport { createCheckoutSession, createCheckoutSessionWithRetries } from './create-checkout-session.js';\nexport {\n cacheSessionDisplayData,\n getSessionDisplayData,\n clearSessionDisplayData,\n} from './session-display-cache.js';\nexport type {\n SessionDisplayProduct,\n SessionDisplayCacheData,\n} from './session-display-cache.js';\n","import type { FloPayConfig } from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\nimport { StripeAdapter } from './stripe-adapter.js';\nimport { FloPay } from './flopay.js';\n\n/**\n * Cache of FloPay instances keyed by publishable key. Multiple instances can\n * coexist when a consumer drives more than one Stripe publishable key during a\n * single page-load.\n */\nconst instanceCache = new Map<string, FloPay>();\n\n/**\n * Loads and initializes the FloPay SDK.\n *\n * This is the primary entry point for consumer applications.\n * It initializes the underlying payment provider (currently Stripe)\n * and returns a ready-to-use `FloPay` instance.\n *\n * Instances are cached per `publishableKey`, so calling `loadFloPay` twice\n * with the same key returns the same instance. Different keys produce\n * independent instances that can run side-by-side.\n *\n * @example\n * ```ts\n * import { loadFloPay } from '@flopay/js';\n *\n * const flopay = await loadFloPay('pk_test_...');\n * const elements = flopay.elements();\n * const cardElement = await elements.create('card');\n * cardElement.mount('#card-container');\n * ```\n */\nexport async function loadFloPay(\n publishableKey: string,\n options?: Omit<FloPayConfig, 'publishableKey'>,\n): Promise<FloPay> {\n if (!publishableKey) {\n throw new FloPayError(\n 'A publishable key is required to initialize FloPay.',\n 'validation_error',\n { param: 'publishableKey' },\n );\n }\n\n const cached = instanceCache.get(publishableKey);\n if (cached) return cached;\n\n const config: FloPayConfig = {\n publishableKey,\n ...options,\n };\n\n // Stripe is the only adapter today. Direct PayPal does not flow through\n // this loader — it renders via the official PayPal JS SDK from\n // `DirectPayPalButton`.\n const adapter = new StripeAdapter();\n await adapter.initialize(config);\n\n const instance = new FloPay(adapter, config);\n instanceCache.set(publishableKey, instance);\n\n return instance;\n}\n","import type {Stripe, StripeElements, StripeElementType} from '@stripe/stripe-js';\nimport type {\n BillingDetails,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n ConfirmPaymentParams,\n CreatePaymentMethodResult,\n ElementOptions,\n ElementType,\n FloPayConfig,\n MountedElement,\n PaymentProviderAdapter,\n PaymentResult,\n} from '@flopay/shared';\nimport {FloPayError, isSetupIntentClientSecret} from '@flopay/shared';\n\n/**\n * Maps FloPay element types to Stripe element types.\n * FloPay uses camelCase; Stripe uses lowercase with hyphens or specific names.\n */\nfunction toStripeElementType(type: ElementType): StripeElementType {\n const map: Record<ElementType, string> = {\n payment: 'payment',\n card: 'card',\n cardNumber: 'cardNumber',\n cardExpiry: 'cardExpiry',\n cardCvc: 'cardCvc',\n address: 'address',\n };\n return map[type] as StripeElementType;\n}\n\n/**\n * Wraps a Stripe element instance in the FloPay MountedElement interface.\n *\n * TODO: In a future phase, this wrapper will render the Stripe element inside\n * an iframe hosted on the FloPay domain for PCI DSS SAQ-A compliance.\n * For now, it delegates directly to the Stripe element.\n */\n/** Minimal interface shared by all Stripe element types. */\ninterface StripeElementLike {\n mount(domElement: string | HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update(options?: Record<string, unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler?: (...args: unknown[]) => void): void;\n}\n\n/**\n * Maps a `FloPayAppearance.theme` ('default' | 'flat' | 'night' | 'none') to a\n * Stripe Elements Appearance `theme` ('stripe' | 'flat' | 'night'). Stripe's\n * Appearance API only accepts those three; anything else triggers a console\n * warning and silently falls back. We normalize here so the bundles can keep\n * `'default'` as their public token.\n */\nfunction toStripeAppearanceTheme(\n theme: 'default' | 'flat' | 'night' | 'none' | undefined,\n): 'stripe' | 'night' | 'flat' {\n switch (theme) {\n case 'night': return 'night';\n case 'flat': return 'flat';\n // 'default', 'none', undefined, or any unexpected value → Stripe's baseline.\n default: return 'stripe';\n }\n}\n\nfunction wrapStripeElement(\n stripeElement: StripeElementLike,\n): MountedElement {\n const el = stripeElement;\n\n return {\n mount(container: HTMLElement) {\n el.mount(container);\n },\n unmount() {\n el.unmount();\n },\n update(options: Partial<ElementOptions>) {\n el.update(options as Record<string, unknown>);\n },\n on(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['on']?.(event, handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['off']?.(event, handler);\n },\n destroy() {\n el.destroy();\n },\n };\n}\n\n/**\n * Payment provider adapter backed by Stripe.\n *\n * Implements the `PaymentProviderAdapter` interface so that FloPay consumers\n * interact with a stable API regardless of the upstream provider.\n */\nexport class StripeAdapter implements PaymentProviderAdapter {\n readonly name = 'stripe';\n private stripe: Stripe | null = null;\n private elements: StripeElements | null = null;\n // Serialized appearance currently applied to `this.elements`. Used to detect\n // when consumers swap themes mid-session so we can live-update the Stripe\n // Elements group instead of returning a stale-styled cache. `null` while no\n // elements group exists.\n private appliedAppearanceKey: string | null = null;\n\n async initialize(config: FloPayConfig): Promise<void> {\n // loadStripe only works in the browser — skip initialization during SSR/SSG\n if (typeof window === 'undefined') {\n return;\n }\n\n let loadStripe: typeof import('@stripe/stripe-js').loadStripe;\n try {\n ({ loadStripe } = await import('@stripe/stripe-js'));\n } catch {\n // @stripe/stripe-js ships as a direct dependency of @flopay/js, so this\n // should never resolve to a missing module in practice. If it does (e.g.\n // a broken install tree), surface a clear FloPayError instead of an\n // unhandled module-resolution rejection.\n throw new FloPayError(\n 'Failed to load @stripe/stripe-js. Reinstall @flopay/js.',\n 'api_error',\n { code: 'StripeJsLoadFailed' },\n );\n }\n\n const stripe = await loadStripe(config.publishableKey, {\n locale: (config.locale as 'auto') ?? 'auto',\n });\n\n if (!stripe) {\n throw new FloPayError(\n 'Failed to initialize Stripe. Check your publishable key.',\n 'authentication_error',\n );\n }\n\n this.stripe = stripe;\n }\n\n /** Lazily creates the Stripe Elements group for the given options. */\n private getElements(options?: ElementOptions): StripeElements {\n if (!this.stripe) {\n throw new FloPayError(\n 'StripeAdapter not initialized. Call initialize() first.',\n 'api_error',\n );\n }\n\n const stripeAppearance = options?.appearance\n ? {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n }\n : undefined;\n const nextAppearanceKey = stripeAppearance ? JSON.stringify(stripeAppearance) : null;\n\n if (!this.elements) {\n let elementsOptions: Record<string, unknown>;\n\n const deferredAmount = options?.amount ?? 0;\n const deferredCurrency = (options?.currency ?? 'usd').toLowerCase();\n const paymentMethodCreation = options?.paymentMethodCreation ?? ('manual' as const);\n\n if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret. Stripe\n // infers payment vs setup mode from the secret prefix (`pi_` / `seti_`).\n elementsOptions = { clientSecret: options.clientSecret };\n } else if (deferredAmount > 0) {\n // No server-side intent yet — use deferred payment mode with manual\n // payment method creation. Matches checkout/StripeCardForm:\n // mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: deferredAmount,\n currency: deferredCurrency,\n paymentMethodCreation,\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n } else {\n // Trial-only / $0 checkout: there is nothing to charge up front, so a\n // deferred PaymentIntent is invalid — Stripe rejects `elements()` with\n // \"Invalid value for elements(): `amount` must be greater than 0\" and\n // the buyer never reaches the card form. Use deferred *setup* mode\n // instead (no amount); the captured card is attached to the SetupIntent\n // the server creates and charged when the trial ends.\n elementsOptions = {\n mode: 'setup' as const,\n currency: deferredCurrency,\n paymentMethodCreation,\n };\n }\n\n if (stripeAppearance) {\n elementsOptions['appearance'] = stripeAppearance;\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n this.appliedAppearanceKey = nextAppearanceKey;\n } else if (nextAppearanceKey !== this.appliedAppearanceKey) {\n // Elements group already exists but the consumer changed (or removed)\n // the appearance. Stripe Elements supports a live `update({ appearance })`\n // call which re-styles mounted elements in place — no re-create, no\n // input-state loss. When the consumer drops `appearance`, fall through\n // with an empty payload so any prior variables/rules are cleared.\n (this.elements as unknown as { update: (opts: Record<string, unknown>) => void }).update({\n appearance: stripeAppearance ?? {},\n });\n this.appliedAppearanceKey = nextAppearanceKey;\n }\n\n return this.elements;\n }\n\n async createElement(\n type: ElementType,\n options: ElementOptions,\n ): Promise<MountedElement> {\n const elements = this.getElements(options);\n const stripeType = toStripeElementType(type);\n\n const elementOptions: Record<string, unknown> = {};\n if (options.layout) {\n elementOptions['layout'] = options.layout;\n }\n if (options.defaultValues) {\n elementOptions['defaultValues'] = options.defaultValues;\n }\n if (options.readOnly) {\n elementOptions['readOnly'] = options.readOnly;\n }\n if (options.mode) {\n elementOptions['mode'] = options.mode;\n }\n if (options.style) {\n elementOptions['style'] = options.style;\n }\n\n const stripeElement = elements.create(stripeType as 'card', elementOptions) as unknown as StripeElementLike;\n return wrapStripeElement(stripeElement);\n }\n\n getElement(type: ElementType): MountedElement | null {\n if (!this.elements) return null;\n const stripeType = toStripeElementType(type);\n const existing = this.elements.getElement(stripeType as 'card') as unknown as StripeElementLike | null;\n if (!existing) return null;\n return wrapStripeElement(existing);\n }\n\n async submitElements(): Promise<{ error?: FloPayError }> {\n if (!this.stripe || !this.elements) {\n return { error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n const { error } = await this.elements.submit();\n if (error) {\n return {\n error: new FloPayError(error.message ?? 'Validation failed', 'validation_error'),\n };\n }\n return {};\n }\n\n async createPaymentMethod(billingDetails?: BillingDetails): Promise<CreatePaymentMethodResult> {\n if (!this.stripe || !this.elements) {\n return {\n paymentMethodId: null,\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Detect split card fields vs unified PaymentElement.\n // With split fields (cardNumber + cardExpiry + cardCvc), Stripe requires\n // passing the specific cardNumber element with type: 'card'.\n // With unified PaymentElement, pass the elements group.\n const cardNumberEl = this.elements.getElement('cardNumber' as 'card');\n\n // Build billing_details for Stripe AVS (Address Verification).\n // When present, Stripe runs postal code and address checks automatically.\n const stripeBilling = billingDetails ? {\n billing_details: {\n ...(billingDetails.email ? { email: billingDetails.email } : {}),\n ...(billingDetails.name ? { name: billingDetails.name } : {}),\n ...(billingDetails.address ? {\n address: {\n ...(billingDetails.address.country ? { country: billingDetails.address.country } : {}),\n ...(billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {}),\n ...(billingDetails.address.city ? { city: billingDetails.address.city } : {}),\n ...(billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {}),\n ...(billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {}),\n ...(billingDetails.address.state ? { state: billingDetails.address.state } : {}),\n },\n } : {}),\n },\n } : {};\n\n const { error, paymentMethod } = cardNumberEl\n ? await this.stripe.createPaymentMethod({\n type: 'card',\n card: cardNumberEl,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0])\n : await this.stripe.createPaymentMethod({\n elements: this.elements,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0]);\n\n if (error) {\n return {\n paymentMethodId: null,\n error: new FloPayError(\n error.message ?? 'Failed to create payment method',\n 'api_error',\n { code: error.code },\n ),\n };\n }\n\n return { paymentMethodId: paymentMethod.id };\n }\n\n async confirmCardPayment(\n params: ConfirmCardPaymentParams,\n ): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return {\n status: 'failed',\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Trial-only / $0 checkouts get a SetupIntent (`seti_…`) from the server.\n // Stripe's `confirmCardPayment` rejects those with \"value should be a\n // PaymentIntent client secret. You specified: a SetupIntent client\n // secret.\" — route them through `confirmCardSetup`, which captures the\n // card for later off-session billing when the trial ends. The resulting\n // SetupIntent id flows through `paymentIntentId` so downstream /process\n // gets one intent identifier regardless of the intent kind.\n if (isSetupIntentClientSecret(params.clientSecret)) {\n const { error: setupError, setupIntent } = await this.stripe.confirmCardSetup(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (setupError) {\n return {\n status: 'failed',\n error: new FloPayError(\n setupError.message ?? 'Payment failed',\n 'api_error',\n { code: setupError.code, declineCode: setupError.decline_code },\n ),\n };\n }\n\n return {\n status: (setupIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: setupIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(setupIntent?.payment_method),\n };\n }\n\n const { error, paymentIntent } = await this.stripe.confirmCardPayment(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n { code: error.code, declineCode: error.decline_code },\n ),\n };\n }\n\n return {\n status: (paymentIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: paymentIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent?.payment_method),\n };\n }\n\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n if (!this.stripe || !this.elements) {\n throw new FloPayError(\n 'StripeAdapter not initialized or no elements created.',\n 'api_error',\n );\n }\n\n const billing = params.billingDetails;\n const paymentMethodData = billing ? {\n billing_details: {\n ...(billing.email ? { email: billing.email } : {}),\n ...(billing.name ? { name: billing.name } : {}),\n ...(billing.address ? {\n address: {\n ...(billing.address.country ? { country: billing.address.country } : {}),\n ...(billing.address.postal_code ? { postal_code: billing.address.postal_code } : {}),\n ...(billing.address.city ? { city: billing.address.city } : {}),\n ...(billing.address.line1 ? { line1: billing.address.line1 } : {}),\n ...(billing.address.line2 ? { line2: billing.address.line2 } : {}),\n ...(billing.address.state ? { state: billing.address.state } : {}),\n },\n } : {}),\n },\n } : undefined;\n\n const { error, paymentIntent } = await this.stripe.confirmPayment({\n elements: this.elements,\n clientSecret: params.clientSecret,\n confirmParams: {\n return_url: params.returnUrl ?? window.location.href,\n ...(paymentMethodData ? { payment_method_data: paymentMethodData } : {}),\n },\n redirect: 'if_required',\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n {\n code: error.code,\n declineCode: error.decline_code,\n },\n ),\n };\n }\n\n if (!paymentIntent) {\n return { status: 'failed', error: new FloPayError('No payment intent returned', 'api_error') };\n }\n\n const statusMap: Record<string, PaymentResult['status']> = {\n succeeded: 'succeeded',\n processing: 'processing',\n requires_action: 'requires_action',\n requires_payment_method: 'failed',\n canceled: 'failed',\n };\n\n return {\n status: statusMap[paymentIntent.status] ?? 'failed',\n paymentIntentId: paymentIntent.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent.payment_method),\n };\n }\n\n private extractPaymentMethodId(\n paymentMethod: string | { id?: string | null } | null | undefined,\n ): string | undefined {\n if (typeof paymentMethod === 'string' && paymentMethod.startsWith('pm_')) {\n return paymentMethod;\n }\n if (paymentMethod && typeof paymentMethod === 'object' && typeof paymentMethod.id === 'string') {\n return paymentMethod.id;\n }\n return undefined;\n }\n\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return { status: 'failed', error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n\n const baseUrl = params.billingApiUrl.replace(/\\/+$/, '');\n\n if (this.elements) {\n const { error: submitError } = await this.elements.submit();\n if (submitError) {\n return {\n status: 'failed',\n error: new FloPayError(\n submitError.message ?? 'PayPal payment failed',\n 'validation_error',\n { code: submitError.code },\n ),\n };\n }\n }\n\n const intentHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (params.nonce) intentHeaders['x-checkout-session-token'] = params.nonce;\n\n const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: intentHeaders,\n body: JSON.stringify({\n sessionId: params.sessionId,\n email: params.email,\n paymentMethodType: 'paypal',\n isPaypal: 'true',\n }),\n });\n\n if (!intentResponse.ok) {\n return { status: 'failed', error: new FloPayError('Failed to create PayPal payment intent', 'api_error') };\n }\n\n const intentJson = await intentResponse.json() as { data?: { id?: string } };\n const intentClientSecret = intentJson.data?.id;\n if (!intentClientSecret) {\n return { status: 'failed', error: new FloPayError('No client_secret in response', 'api_error') };\n }\n\n const { error } = await this.stripe.confirmPayment({\n clientSecret: intentClientSecret,\n elements: this.elements ?? undefined,\n confirmParams: { return_url: params.returnUrl },\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'PayPal payment failed', 'api_error', { code: error.code }),\n };\n }\n\n return {\n status: 'processing',\n };\n }\n\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n if (!this.stripe || typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n const paymentIntentId = params.get('payment_intent');\n const clientSecret = params.get('payment_intent_client_secret');\n const redirectStatus = params.get('redirect_status');\n\n if (!paymentIntentId || !clientSecret) return null;\n\n if (redirectStatus === 'failed') {\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was declined. Please try again.', 'api_error'),\n };\n }\n\n const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(clientSecret);\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'Failed to retrieve PayPal payment', 'api_error'),\n };\n }\n\n if (paymentIntent && (paymentIntent.status === 'requires_capture' || paymentIntent.status === 'succeeded')) {\n const pmId = typeof paymentIntent.payment_method === 'string'\n ? paymentIntent.payment_method\n : paymentIntent.payment_method?.id;\n\n // Clean up URL params\n const url = new URL(window.location.href);\n url.searchParams.delete('payment_intent');\n url.searchParams.delete('payment_intent_client_secret');\n url.searchParams.delete('redirect_status');\n window.history.replaceState({}, '', url.toString());\n\n return {\n status: paymentIntent.status as ConfirmCardPaymentResult['status'],\n paymentIntentId: paymentIntent.id,\n paymentMethodId: pmId,\n };\n }\n\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was not completed. Please try again.', 'api_error'),\n };\n }\n\n getRawProvider(): unknown {\n return this.stripe;\n }\n\n createPayPalElements(options: ElementOptions): unknown {\n if (!this.stripe) return null;\n\n const elementsOptions: Record<string, unknown> = {\n mode: 'payment' as const,\n amount: options.amount ?? 0,\n currency: (options.currency ?? 'usd').toLowerCase(),\n captureMethod: 'manual' as const,\n };\n\n if (options.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n\n if (options.appearance) {\n elementsOptions['appearance'] = {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n return this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n }\n\n destroy(): void {\n this.elements = null;\n this.appliedAppearanceKey = null;\n this.stripe = null;\n }\n}\n","import type {\n PaymentProviderAdapter,\n FloPayConfig,\n ElementOptions,\n ConfirmPaymentParams,\n PaymentResult,\n CardCaptureAdapter,\n CheckoutSession,\n NormalizedCheckoutSession,\n CreatePaymentMethodResult,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n} from '@flopay/shared';\nimport { FloPayError, resolveBillingApiUrl } from '@flopay/shared';\nimport { FloPayElements } from './elements.js';\nimport { PaymentAPI } from './payment-api.js';\nimport { PciVaultCardCapture } from './pci-vault-card-capture.js';\n\n/**\n * The main FloPay SDK instance.\n *\n * Created via `loadFloPay(publishableKey)`. Provides element management,\n * payment confirmation, and session retrieval.\n */\nexport class FloPay {\n private readonly provider: PaymentProviderAdapter;\n private readonly config: FloPayConfig;\n private currentElements: FloPayElements | null = null;\n\n constructor(provider: PaymentProviderAdapter, config: FloPayConfig) {\n this.provider = provider;\n this.config = config;\n }\n\n /**\n * Creates a new `FloPayElements` group for mounting payment fields.\n *\n * Only one elements group is active at a time. Creating a new one\n * destroys the previous group.\n */\n elements(options?: ElementOptions): FloPayElements {\n if (this.currentElements) {\n this.currentElements.destroy();\n }\n this.currentElements = new FloPayElements(this.provider, {\n appearance: this.config.appearance,\n ...options,\n });\n return this.currentElements;\n }\n\n /** Submit elements for validation. */\n async submitElements(): Promise<{ error?: import('@flopay/shared').FloPayError }> {\n return this.provider.submitElements();\n }\n\n /** Create a payment method from the current elements (tokenize card). */\n async createPaymentMethod(billingDetails?: import('@flopay/shared').BillingDetails): Promise<CreatePaymentMethodResult> {\n return this.provider.createPaymentMethod(billingDetails);\n }\n\n /** Confirm a card payment with a known client secret and payment method ID. */\n async confirmCardPayment(params: ConfirmCardPaymentParams): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmCardPayment(params);\n }\n\n /**\n * Create a {@link CardCaptureAdapter} for collecting card details through the\n * backend-rendered hosted vault PCI widget instead of provider-owned (Stripe)\n * card fields (TeamFloPay/backend#823).\n *\n * The returned adapter injects the server-supplied widget HTML (the session's\n * {@link CheckoutSession.vault} block, or one fetched via\n * `PaymentAPI.getVaultCapture`) and relays the widget's terminal outcome. The\n * backend owns tokenization, the PaymentIntent, 3DS, and fulfilment — no\n * Stripe.js is involved on the card path and PCI-sensitive fields never enter\n * the SDK runtime.\n */\n cardCapture(options?: {\n /** Checkout session id to bind the capture to (outcome correlation). */\n sessionId?: string;\n }): CardCaptureAdapter {\n return new PciVaultCardCapture({\n sessionId: options?.sessionId,\n });\n }\n\n /** Confirm a PayPal payment: create intent via billing API → confirm → redirect if needed. */\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n /**\n * Session-bound checkout token. Forwarded as `x-checkout-session-token`\n * on the internal `POST /v1/checkouts/payments/intents` call so post-#640\n * backends don't reject the round-trip with a 401.\n */\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmPayPalPayment(params);\n }\n\n /** Resume a PayPal payment after redirect return. Returns null if no PayPal params in URL. */\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n return this.provider.resumePayPalPayment();\n }\n\n /** Confirms a payment using the mounted elements. */\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n return this.provider.confirmPayment(params);\n }\n\n /**\n * Retrieves a checkout session by ID via the billing API.\n *\n * Returns the normalized `CheckoutSession` with amount, currency,\n * customer data, and status.\n *\n * Requires `billingApiUrl` to be set — either via `loadFloPay(key, { billingApiUrl })`\n * or passed directly as the second argument.\n */\n async retrieveSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<CheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required to retrieve a session.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n const unified = await api.getUnifiedCheckoutSession(sessionId);\n if (!unified.data.session) {\n throw new FloPayError('Session not found', 'api_error');\n }\n return unified.data.session;\n }\n\n /**\n * Retrieves and normalizes a checkout session, including provider-specific\n * data (Stripe clientSecret/publishableKey, Chargebee site, etc.).\n *\n * The billing API URL is resolved from: explicit param → `loadFloPay()` config\n * → `NEXT_PUBLIC_FLOPAY_ENV` env var → `configureFlopay()` → staging fallback.\n */\n async retrieveUnifiedSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<NormalizedCheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n return api.getUnifiedCheckoutSession(sessionId);\n }\n\n /**\n * Returns the raw underlying provider instance (e.g. Stripe object).\n * Used internally by components that need direct provider access,\n * such as PayPal which requires its own Elements instance.\n */\n getRawProvider(): unknown {\n return this.provider.getRawProvider();\n }\n\n /** Tears down the SDK instance and releases resources. */\n destroy(): void {\n this.currentElements?.destroy();\n this.currentElements = null;\n this.provider.destroy();\n }\n}\n","import type {\n PaymentProviderAdapter,\n ElementType,\n ElementOptions,\n MountedElement,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/**\n * Manages the creation and lifecycle of payment elements.\n *\n * Each `FloPayElements` instance is bound to a single provider adapter\n * and tracks all created elements for cleanup.\n */\nexport class FloPayElements {\n private readonly provider: PaymentProviderAdapter;\n private readonly elementMap = new Map<ElementType, MountedElement>();\n private readonly baseOptions: ElementOptions;\n\n constructor(provider: PaymentProviderAdapter, options?: ElementOptions) {\n this.provider = provider;\n this.baseOptions = options ?? {};\n }\n\n /**\n * Creates a new element of the given type.\n * If an element of that type already exists, it is destroyed first.\n */\n async create(\n type: ElementType,\n options?: ElementOptions,\n ): Promise<MountedElement> {\n // Check if the provider already has this element (e.g. after React\n // Strict Mode unmount+remount cycle). Reuse it instead of creating\n // a duplicate, which Stripe would reject.\n const providerExisting = this.provider.getElement(type);\n if (providerExisting) {\n this.elementMap.set(type, providerExisting);\n return providerExisting;\n }\n\n const existing = this.elementMap.get(type);\n if (existing) {\n existing.destroy();\n }\n\n const merged: ElementOptions = { ...this.baseOptions, ...options };\n const element = await this.provider.createElement(type, merged);\n this.elementMap.set(type, element);\n return element;\n }\n\n /** Returns a previously created element, or `null`. */\n getElement(type: ElementType): MountedElement | null {\n return this.elementMap.get(type) ?? null;\n }\n\n /**\n * Submits all mounted elements for validation.\n *\n * Returns an object with an optional error if validation fails.\n * This does NOT confirm the payment — call `floPay.confirmPayment()` for that.\n */\n async submit(): Promise<{ error?: FloPayError }> {\n // Walk through all mounted elements and check for incomplete state.\n // The actual validation is handled by the provider's element implementation.\n // For Stripe, the Elements.submit() method is called during confirmPayment.\n // This method exists to match the Stripe Elements API surface.\n return {};\n }\n\n /** Destroys all created elements and clears the internal map. */\n destroy(): void {\n for (const element of this.elementMap.values()) {\n element.destroy();\n }\n this.elementMap.clear();\n }\n}\n","import type {\n CheckoutGateways,\n CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n CheckoutSessionProduct,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n VaultCaptureBlock,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n FLO_SDK_VERSION_HEADER,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayProduct,\n} from './session-display-cache.js';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API. */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n /** Unified products list returned by the billing API (post-#760). */\n products?: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** 'item' or 'subscription'. */\n type: 'item' | 'subscription';\n code?: string;\n name?: string | null;\n description?: string | null;\n quantity: number;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n /**\n * Pre-discount total in cart-currency major units. Populated by billing\n * API ≥ v1.1.2; absent on older backends.\n */\n subtotalAmount?: number;\n /** Total reduction from applied coupons (cart-currency major units). */\n discountAmount?: number;\n /** Final charge amount after coupon discount (cart-currency major units). */\n totalAmount?: number;\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gateways?: CheckoutGateways;\n /**\n * Generic downstream card-method id for a card already on file\n * (TeamFloPay/backend#823) — present for returning customers so the SDK can\n * skip the vault widget. Absent for first-time buyers.\n */\n providerPaymentMethodId?: string | null;\n accountData: {\n userId: string;\n firstName: string;\n lastName: string;\n email: string;\n gender?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n };\n tagsData: {\n googleContainerId?: string | null;\n sessionId?: string | null;\n testEventCode?: string | null;\n };\n}\n\ntype ApiErrorPayload = Record<string, unknown> | null;\n\nconst DEFAULT_PROCESSING_RETRY_AFTER_MS = 1_000;\nconst MIN_PROCESSING_RETRY_AFTER_MS = 500;\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 15_000;\nconst MAX_PROCESSING_RETRY_AFTER_MS = 3_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Read an error `message` that may be a `string` or a `string[]`.\n *\n * NestJS class-validator returns `message` as a `string[]` (one entry per\n * failed constraint), e.g. `[\"postcode must be a valid GB postcode\"]`. Read as\n * a plain string that collapses to the generic fallback; here we join the\n * non-empty entries so a country-aware account-snapshot validation error\n * surfaces its real reason instead of \"Failed to persist account snapshot\".\n */\nfunction readMessage(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n if (typeof value === 'string') return value.trim() ? value : undefined;\n if (Array.isArray(value)) {\n const joined = value\n .filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)\n .join('; ');\n return joined || undefined;\n }\n return undefined;\n}\n\nfunction readNumber(payload: ApiErrorPayload, key: string): number | undefined {\n const value = payload?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createCheckoutProcessingTimeoutError(): FloPayError {\n return new FloPayError(\n 'Checkout is still processing. Please try again shortly.',\n 'api_error',\n { code: 'checkout_processing_timeout' },\n );\n}\n\nasync function buildApiErrorFromResponse(\n response: Response,\n fallbackMessage: string,\n): Promise<FloPayError> {\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const nestedError = isRecord(payload?.error) ? payload.error : null;\n const message =\n readMessage(payload, 'message') ??\n readMessage(nestedError, 'message') ??\n fallbackMessage;\n const code =\n readString(payload, 'code') ??\n readString(payload, 'gatewayErrorCode') ??\n readString(nestedError, 'code') ??\n `http_${response.status}`;\n\n return new FloPayError(message, 'api_error', {\n code,\n statusCode: response.status,\n });\n}\n\n/**\n * Extra attempts (beyond the first) for transient network failures.\n * Three total tries: immediate, +150ms, +300ms.\n */\nconst NETWORK_RETRY_ATTEMPTS = 2;\n\n/**\n * Run `fetch` with a bounded retry on transient network failures.\n *\n * `fetch` rejects — rather than resolving with a non-ok `Response` — only when\n * the request never produced an HTTP response at all: a dropped connection, a\n * DNS hiccup, or a failed CORS preflight, which all surface as\n * `TypeError: Failed to fetch`. Those blips are worth retrying. An `AbortError`\n * (caller-initiated cancellation) is not, and any HTTP-level failure resolves\n * normally and is left for the caller to handle via `response.ok`.\n *\n * Backoff is exponential (150ms, 300ms). Only use this for requests whose\n * replay is harmless — idempotent reads, or session bootstrap where a re-sent\n * create can at worst leave an unused draft session behind.\n */\nasync function fetchWithNetworkRetry(\n input: RequestInfo | URL,\n init?: RequestInit,\n attempts = NETWORK_RETRY_ATTEMPTS,\n): Promise<Response> {\n let lastErr: unknown;\n for (let attempt = 0; ; attempt++) {\n try {\n return await fetch(input, init);\n } catch (err) {\n // Caller-initiated cancellation must propagate immediately, never retry.\n if (err instanceof Error && err.name === 'AbortError') throw err;\n lastErr = err;\n if (attempt >= attempts) throw lastErr;\n await delay(150 * 2 ** attempt);\n }\n }\n}\n\n/**\n * Client-side payment API service.\n *\n * Mirrors the `PaymentAPI` class from the checkout project's\n * `src/service/api.ts`. All methods call the billing API endpoints\n * that the checkout backend exposes.\n */\nexport class PaymentAPI {\n private readonly baseUrl: string;\n\n constructor(billingApiUrl: string) {\n this.baseUrl = billingApiUrl.replace(/\\/+$/, '');\n }\n\n /**\n * Fetch a raw checkout session by ID.\n *\n * `nonce` is the session-bound checkout token returned when the session\n * was created. When supplied it is sent as the `x-checkout-session-token`\n * header that post-#640 backends match against `checkout_session.nonce`\n * before returning the row — the UUID alone is no longer sufficient.\n * Backends that don't yet enforce it ignore the extra header.\n */\n async getCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n // Advertise the SDK version on reads too: the backend only embeds the hosted\n // vault block (`session.vault`) for SDKs declaring `>= 1.3.0`. Without this,\n // a reused/resumed session fetched via GET loses the vault signal and the\n // card path falls back to Stripe (revisit/theme-change regression).\n const headers: Record<string, string> = { [FLO_SDK_VERSION_HEADER]: SDK_VERSION };\n if (nonce) headers['x-checkout-session-token'] = nonce;\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}`,\n { headers },\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n const body = await response.json() as BillingResponse<RawCheckoutSession>;\n return { ...body, data: this.mergeCachedDisplayData(body.data) };\n }\n\n /**\n * Stash display-only data for a session so subsequent fetches can fill in\n * fields the backend no longer persists (`overrideAmount`, `totalAmount`,\n * `providerItemName`, `providerPlanName`).\n *\n * Backed by `sessionStorage` in the browser, with an in-memory fallback in\n * Node/SSR contexts. Default TTL: 1 hour.\n *\n * Server-returned values always win — cached values fill in only where the\n * server returned `null` / `undefined`.\n *\n * @example\n * ```ts\n * paymentAPI.cacheSessionDisplayData(sessionId, {\n * currency: 'USD',\n * items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],\n * });\n * ```\n */\n cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n ): void {\n cacheSessionDisplayData(sessionId, data, options);\n }\n\n /**\n * Drop any cached display data for a session. Call after the payment\n * completes; otherwise the TTL handles cleanup.\n */\n clearSessionDisplayData(sessionId: string): void {\n clearSessionDisplayData(sessionId);\n }\n\n /**\n * Fetch (re-mint) the hosted vault capture widget for a session\n * (TeamFloPay/backend#823).\n *\n * `POST /v1/checkouts/sessions/{id}/vault/capture` returns the SDK-ready\n * {@link VaultCaptureBlock} (`html` + `url`, plus `messageToken` /\n * `expectedOrigin` once the backend mints them). The SDK injects `html` as\n * the card-capture widget. This is the fallback path for sessions that did\n * not receive the embedded `vault` block on create (e.g. a session loaded by\n * id via `GET`, or a pre-1.3.0 create); the endpoint is idempotent and reuses\n * session-cached creds when available.\n *\n * Because the endpoint is idempotent, the request is wrapped in\n * `fetchWithNetworkRetry`: a transient network blip (dropped connection, DNS\n * hiccup, failed CORS preflight) would otherwise leave the secure card form\n * unable to load and hard-block checkout.\n *\n * The PCIVault submit *secret* the backend may include in the response is\n * intentionally **not** read or surfaced — it is server-only and never enters\n * the SDK runtime.\n *\n * `nonce` is forwarded as `x-checkout-session-token` (required by post-#640\n * backends, matched against the session's stored nonce).\n */\n async getVaultCapture(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<VaultCaptureBlock> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (nonce) headers['x-checkout-session-token'] = nonce;\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}/vault/capture`,\n { method: 'POST', headers },\n );\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to load the secure card form');\n }\n const block = await response.json() as VaultCaptureBlock;\n return this.toVaultBlock(block);\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateways` map to enumerate provider-specific data,\n * then wraps the session in a `NormalizedCheckoutSession` for provider-\n * agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId, nonce);\n const normalized = this.normalizeRawSession(res.data);\n // The embedded vault block is a top-level sibling of `data` on the read\n // response (mirrors the create response). Thread it onto the session so a\n // reused/resumed session keeps the hosted card form instead of falling back\n // to Stripe (TeamFloPay/backend#823).\n const vault = (res as { vault?: VaultCaptureBlock }).vault;\n if (vault && normalized.data.session) {\n normalized.data.session.vault = this.toVaultBlock(vault);\n }\n return normalized;\n }\n\n /**\n * Submit a tokenized payment to the billing backend.\n *\n * The backend will either succeed, return `type: '3ds_required'`\n * (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.\n *\n * Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`\n * and forwards `data.nonce` as `x-checkout-session-token`. Backend\n * `TeamFloPay/backend#640` rejects callers without a matching nonce with a\n * 401 — this method throws synchronously when `data.nonce` is missing so the\n * problem surfaces before the network round trip.\n *\n * @param userId Vestigial — backend's GatewayInterceptor routes via session,\n * not headers, so this value is no longer sent on the wire. Kept in the\n * signature for back-compat with existing callers; will be removed in a\n * future major version.\n */\n async processPayment(\n _userId: string,\n data: ProcessPaymentParams,\n options?: {\n pollTimeoutMs?: number;\n },\n ): Promise<Response> {\n if (!data.nonce) {\n throw new FloPayError(\n 'processPayment requires `nonce` — pass the value returned from session creation.',\n 'validation_error',\n { code: 'MissingCheckoutSessionToken', param: 'nonce' },\n );\n }\n\n const { nonce, ...processBody } = data;\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(data.sessionId)}/process`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(processBody),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, { ...options, nonce });\n }\n\n /**\n * Patch the buyer's account snapshot (email, name, billing address, AVS\n * intent) onto a checkout session via\n * `PATCH /v1/checkouts/sessions/{id}/account` (TeamFloPay/backend#823).\n *\n * The vault path's hosted form owns the charge end-to-end so the SDK\n * never calls `/process` on this path; the buyer-typed AVS / billing\n * address would otherwise be lost. The SDK calls this just before\n * submitting the vault widget so the downstream listener mints the\n * Stripe PaymentMethod with the right `billing_details.address` and the\n * per-attempt + per-PM address snapshots are populated.\n *\n * Body shape mirrors the relevant subset of `/process`'s\n * `ProcessCheckoutBodyDto` — same keys, same validators. The endpoint\n * is idempotent: empty/undefined fields are not written, addresses are\n * last-writer-wins, AVS analytics are first-writer-wins.\n *\n * Wrapped in `fetchWithNetworkRetry` because a transient blip on this\n * pre-pay PATCH would silently leave AVS unsent and cause an\n * AVS-protected charge to decline downstream.\n */\n async patchAccountSnapshot(\n sessionId: string,\n nonce: string,\n body: {\n accountData: {\n userId: string;\n email: string;\n firstName?: string | null;\n lastName?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n city?: string | null;\n state?: string | null;\n zip?: string | null;\n country?: string | null;\n gender?: string | null;\n };\n avsCheck?: boolean;\n avsConfig?: Record<string, unknown>;\n },\n ): Promise<void> {\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(sessionId)}/account`,\n {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(body),\n },\n );\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to persist account snapshot');\n }\n }\n\n /**\n * Create a PaymentIntent on the backend.\n *\n * Used by the Stripe flow to create a server-side PaymentIntent\n * with the client's payment method attached.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied — the\n * session-bound checkout token returned by session creation. Post-#640\n * backends reject this call with a 401 when the header is missing or does\n * not match the session's stored nonce.\n *\n * `paymentMethodType` is optional for the vault card-capture flow\n * (TeamFloPay/backend#823): the frontend starts checkout *without* an upfront\n * card payment method, so it may be omitted (or `null`). The hosted vault PCI\n * form captures the card afterwards and the backend attaches the resulting\n * payment method to the PaymentIntent it returns here. Legacy callers keep\n * passing the concrete payment method id / type.\n */\n async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType?: string | null,\n options?: { signal?: AbortSignal; isPaypal?: string; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType: paymentMethodType ?? null,\n isPaypal: options?.isPaypal ?? false,\n }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Create a SetupIntent for saving payment methods.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied —\n * required by post-#640 backends, ignored by earlier versions.\n */\n async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ sessionId, email, paymentMethodType }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Fetch user's prior payments by email.\n * Used to determine if saved card UX should be shown.\n */\n async getPaymentsByEmail(\n email: string,\n options?: { signal?: AbortSignal; page?: number; limit?: number },\n ): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }> {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 1;\n const params = new URLSearchParams({\n email,\n page: String(page),\n limit: String(limit),\n sortField: 'createdAt',\n sortDirection: 'DESC',\n });\n\n const response = await fetch(\n `${this.baseUrl}/v1/payments?${params.toString()}`,\n {\n method: 'GET',\n signal: options?.signal,\n keepalive: true,\n },\n );\n\n if (!response.ok) {\n throw new FloPayError('Failed to fetch payments', 'api_error');\n }\n\n return response.json() as Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>;\n }\n\n /**\n * Create a checkout session AND return the full session data in one call.\n * Uses `?expand=true` so the backend returns the complete session\n * instead of just a UUID — eliminating the need for a second GET.\n *\n * Falls back to create + GET if the backend doesn't support `expand`.\n */\n async createAndFetchSession(\n params: InlineSessionDraft,\n ): Promise<NormalizedCheckoutSession> {\n const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n wireProducts,\n );\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n checkoutVersion: SDK_VERSION,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: params.account.userId,\n firstName: params.account.firstName ?? null,\n lastName: params.account.lastName ?? null,\n email: params.account.email,\n country: params.account.country ?? null,\n gender: params.account.gender ?? null,\n city: params.account.city ?? null,\n state: params.account.state ?? null,\n zip: params.account.zip ?? null,\n addressLine1: params.account.addressLine1 ?? null,\n addressLine2: params.account.addressLine2 ?? null,\n },\n couponCodes: params.couponCodes ?? [],\n };\n\n if (params.tokenizedData) payload['tokenizedData'] = params.tokenizedData;\n if (params.tagsData) payload['tagsData'] = params.tagsData;\n if (params.utmMetadata?.length) payload['utmMetadata'] = params.utmMetadata;\n\n // Checkout analytics metadata\n if (params.avsCheck !== undefined) payload['avsCheck'] = params.avsCheck;\n if (params.checkoutType) payload['checkoutType'] = params.checkoutType;\n if (params.checkoutLayout) payload['checkoutLayout'] = params.checkoutLayout;\n if (params.avsConfig) payload['avsConfig'] = params.avsConfig;\n\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n // Declare the SDK version so backends at TeamFloPay/backend#823 embed\n // the hosted vault capture block (`body.vault`) in the response for\n // SDKs ≥ 1.3.0. Older backends ignore the header.\n headers: {\n 'Content-Type': 'application/json',\n [FLO_SDK_VERSION_HEADER]: SDK_VERSION,\n },\n body: JSON.stringify(payload),\n },\n );\n\n if (response.status === 204) {\n throw new FloPayError(\n 'Session auto-completed — payment method already on file',\n 'api_error',\n { code: 'session_auto_completed' },\n );\n }\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to create checkout session');\n }\n\n const body = await response.json() as {\n data: RawCheckoutSession & { uuid?: string };\n autoProcessingError?: CheckoutProcessError;\n autoProcessingAttempted?: boolean;\n autoProcessingPending?: CheckoutProcessingPending;\n vault?: VaultCaptureBlock;\n };\n\n // If backend supports expand=true, it returns full session data.\n // If not, it returns { data: { uuid } } and we fall back to a GET.\n if (body.data && 'gateways' in body.data) {\n // Auto-stash display-only fields the consumer just sent so wallet\n // sheets and the post-redirect fetch see the same totals. Must run\n // BEFORE the merge so the merge picks them up.\n this.autoCacheDisplayData(body.data.uuid, params);\n const merged = this.mergeCachedDisplayData(body.data);\n const normalized = this.normalizeRawSession(merged);\n // Thread the embedded vault capture block (TeamFloPay/backend#823) onto\n // the session so the React card form can inject the hosted widget without\n // a second `/vault/capture` round-trip.\n if (body.vault && normalized.data.session) {\n normalized.data.session.vault = this.toVaultBlock(body.vault);\n }\n return {\n ...normalized,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n // Fallback: backend returned UUID only — fetch full session\n const uuid = body.data?.uuid;\n if (!uuid) {\n throw new FloPayError('No session ID returned', 'api_error');\n }\n this.autoCacheDisplayData(uuid, params);\n const unifiedSession = await this.getUnifiedCheckoutSession(uuid);\n return {\n ...unifiedSession,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n async waitForCheckoutSessionCompletion(\n checkoutSessionId: string,\n options?: {\n initialDelayMs?: number;\n timeoutMs?: number;\n /**\n * Session-bound checkout token; forwarded on the poll's\n * `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.\n */\n nonce?: string;\n },\n ): Promise<NormalizedCheckoutSession> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_PROCESSING_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n let nextDelayMs = this.clampRetryAfterMs(options?.initialDelayMs ?? DEFAULT_PROCESSING_RETRY_AFTER_MS);\n\n while (true) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n if (nextDelayMs > 0) {\n await delay(Math.min(nextDelayMs, remainingMs));\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n }\n\n const session = await this.getUnifiedCheckoutSession(checkoutSessionId, options?.nonce);\n const status = session.data.session?.status;\n\n if (status === 'complete' || status === 'expired') {\n return session;\n }\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n nextDelayMs = this.clampRetryAfterMs(\n Math.max(nextDelayMs * 2, MIN_PROCESSING_RETRY_AFTER_MS),\n );\n }\n }\n\n /** Normalize a raw session into a provider-agnostic shape. */\n private normalizeRawSession(session: RawCheckoutSession): NormalizedCheckoutSession {\n const gateways: CheckoutGateways = session.gateways ?? {};\n const providers: BillingProvider[] = [];\n const data: NormalizedCheckoutSession['data'] = {\n session: this.toCheckoutSession(session),\n };\n\n const stripeGateway = gateways.stripe;\n if (stripeGateway?.publishableKey) {\n providers.push('stripe');\n const rawSession = session as unknown as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n stripeGateway.stripeClientSecret,\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n data.stripe = {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: stripeGateway.publishableKey ?? undefined,\n paypalPublishableKey: stripeGateway.paypalPublishableKey ?? undefined,\n environment: stripeGateway.environment,\n enabledPaymentMethods: Array.isArray(stripeGateway.enabledPaymentMethods)\n ? stripeGateway.enabledPaymentMethods.filter((m): m is string => typeof m === 'string')\n : undefined,\n };\n }\n\n const paypalGateway = gateways.paypal;\n if (paypalGateway?.publishableKey) {\n providers.push('paypal');\n data.paypal = {\n publishableKey: paypalGateway.publishableKey,\n environment: paypalGateway.environment,\n };\n }\n\n return {\n providers,\n mode: 'tokenize',\n data,\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const rawProducts = raw.products ?? [];\n\n // Billing API ≥ v1.1.2 returns the coupon-adjusted `totalAmount` on the\n // session. Prefer it as the source of truth so wallet sheets render the\n // post-coupon amount; fall back to summing per-line `overrideAmount`.\n const hasBackendTotal =\n typeof raw.totalAmount === 'number' && Number.isFinite(raw.totalAmount);\n const computedTotal = rawProducts.reduce(\n (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),\n 0,\n );\n const totalAmount = hasBackendTotal ? (raw.totalAmount as number) : computedTotal;\n\n // Billing API returns amounts in major units (e.g. 24.95 USD).\n // Stripe expects amounts in smallest currency unit (cents).\n const amountInCents = Math.round(totalAmount * 100);\n\n const currency = raw.currency ?? rawProducts[0]?.currency ?? 'USD';\n\n const mode: CheckoutSession['mode'] =\n rawProducts.some((p) => p.type === 'subscription') ? 'subscription' : 'payment';\n\n return {\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode,\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency,\n customer: {\n id: raw.accountData.userId,\n email: raw.accountData.email,\n firstName: raw.accountData.firstName,\n lastName: raw.accountData.lastName,\n country: raw.accountData.country ?? undefined,\n city: raw.accountData.city ?? undefined,\n state: raw.accountData.state ?? undefined,\n zip: raw.accountData.zip ?? undefined,\n gender: raw.accountData.gender ?? undefined,\n line1: raw.accountData.addressLine1 ?? undefined,\n line2: raw.accountData.addressLine2 ?? undefined,\n },\n metadata: {},\n\n checkoutMode: raw.checkoutMode,\n providerPaymentMethodId:\n typeof raw.providerPaymentMethodId === 'string' ? raw.providerPaymentMethodId : null,\n products: rawProducts.map((p): CheckoutSessionProduct => ({\n ...p,\n totalAmount: typeof p.totalAmount === 'number' ? p.totalAmount : undefined,\n overrideAmount: typeof p.overrideAmount === 'number' ? p.overrideAmount : null,\n currency: typeof p.currency === 'string' ? p.currency : undefined,\n metadata: p.metadata ?? null,\n })),\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n subtotalAmount: raw.subtotalAmount,\n discountAmount: raw.discountAmount,\n totalAmount: raw.totalAmount,\n createdAt: raw.createdAt,\n gateways: raw.gateways,\n accountData: raw.accountData,\n tagsData: raw.tagsData,\n };\n }\n\n /**\n * Coerce a raw vault block into a typed {@link VaultCaptureBlock}. The\n * server-only PCIVault submit `secret` is deliberately dropped so it never\n * lands on the public session surface (logs / telemetry / client inspection).\n */\n private toVaultBlock(raw: VaultCaptureBlock): VaultCaptureBlock {\n return {\n html: typeof raw.html === 'string' ? raw.html : undefined,\n url: typeof raw.url === 'string' ? raw.url : undefined,\n messageToken: typeof raw.messageToken === 'string' ? raw.messageToken : undefined,\n expectedOrigin: typeof raw.expectedOrigin === 'string' ? raw.expectedOrigin : undefined,\n };\n }\n\n private toCheckoutSessionStatus(status: RawCheckoutSession['status']): CheckoutSession['status'] {\n if (status === 'completed') {\n return 'complete';\n }\n\n if (status === 'expired') {\n return 'expired';\n }\n\n return 'open';\n }\n\n private async resolveProcessResponse(\n response: Response,\n checkoutSessionId: string,\n options?: {\n pollTimeoutMs?: number;\n nonce?: string;\n },\n ): Promise<Response> {\n if (response.status !== 202) {\n return response;\n }\n\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const pending = this.toCheckoutProcessingPending(payload, response, checkoutSessionId);\n const session = await this.waitForCheckoutSessionCompletion(pending.sessionId, {\n initialDelayMs: pending.retryAfterMs,\n timeoutMs: options?.pollTimeoutMs,\n nonce: options?.nonce,\n });\n\n if (session.data.session?.status === 'complete') {\n return new Response(null, { status: 204, statusText: 'No Content' });\n }\n\n if (session.data.session?.status === 'expired') {\n throw new FloPayError(\n 'Checkout session has expired.',\n 'api_error',\n { code: 'checkout_session_expired' },\n );\n }\n\n throw createCheckoutProcessingTimeoutError();\n }\n\n private toCheckoutProcessingPending(\n payload: ApiErrorPayload,\n response: Response,\n checkoutSessionId: string,\n ): CheckoutProcessingPending {\n const retryAfterHeader = response.headers.get('Retry-After');\n const headerRetryAfterSeconds = retryAfterHeader === null\n || retryAfterHeader.trim() === ''\n ? undefined\n : Number(retryAfterHeader);\n const headerRetryAfterMs = headerRetryAfterSeconds !== undefined\n && Number.isFinite(headerRetryAfterSeconds)\n ? headerRetryAfterSeconds * 1_000\n : undefined;\n\n return {\n type: 'checkout_processing',\n sessionId: readString(payload, 'sessionId') ?? checkoutSessionId,\n retryAfterMs: this.clampRetryAfterMs(\n readNumber(payload, 'retryAfterMs')\n ?? headerRetryAfterMs\n ?? DEFAULT_PROCESSING_RETRY_AFTER_MS,\n ),\n statusUrl: readString(payload, 'statusUrl'),\n sessionUrl: readString(payload, 'sessionUrl'),\n };\n }\n\n private clampRetryAfterMs(retryAfterMs: number): number {\n return Math.max(0, Math.min(retryAfterMs, MAX_PROCESSING_RETRY_AFTER_MS));\n }\n\n /**\n * Stash the display-only fields the consumer passed into a create-session\n * call. Runs after the backend assigns a UUID so a later GET on the same\n * session (typically after a redirect) can fill in fields the backend no\n * longer persists — `overrideAmount`, `totalAmount`, `name`, etc.\n *\n * No-op when no UUID is available.\n */\n private autoCacheDisplayData(\n sessionId: string | undefined,\n params: InlineSessionDraft,\n ): void {\n if (!sessionId) return;\n const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n if (products.length === 0 && !params.currency) {\n return;\n }\n const usingUnifiedProducts = params.products !== undefined;\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n usingUnifiedProducts ? undefined : params.items,\n usingUnifiedProducts ? undefined : params.subscriptions,\n products,\n );\n cacheSessionDisplayData(sessionId, {\n currency: sessionCurrency ?? undefined,\n products: products.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency ?? undefined,\n })),\n });\n }\n\n /**\n * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response. Server values always win — cache fills in\n * only where the server returned `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedProducts = new Map<string, SessionDisplayProduct>();\n // Key on `code` alone: the catalog code is the product's identity (the\n // backend resolves item-vs-subscription from it), so cached display data\n // merges back correctly even when the create-side payload omitted the\n // deprecated `type`.\n const productKey = (code: string | undefined): string | undefined =>\n code ? `code:${code}` : undefined;\n for (const p of cached?.products ?? []) {\n const key = productKey(p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.code);\n const fallback = key ? cachedProducts.get(key) : undefined;\n return {\n ...p,\n name: p.name ?? fallback?.name ?? null,\n totalAmount: p.totalAmount ?? fallback?.totalAmount,\n overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,\n currency: p.currency ?? fallback?.currency,\n };\n });\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n products: mergedProducts,\n };\n }\n}\n","/**\n * Client-side cache for display-only checkout fields the backend no longer\n * persists (`overrideAmount`, `totalAmount`, `providerItemName`,\n * `providerPlanName`, per-line `currency`).\n *\n * The cache lives in `sessionStorage` so it survives the navigation from the\n * page that creates the session to the checkout page that fetches it, but\n * dies on tab close. An in-memory fallback keeps the SDK working in Node /\n * SSR contexts where `sessionStorage` is unavailable.\n *\n * Values from the server response always win — cached values fill in only\n * where the server returned `null` or `undefined`.\n */\n\nconst STORAGE_KEY_PREFIX = 'flopay_session_display:';\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/** Display-only fields per product that can be cached and merged back later. */\nexport interface SessionDisplayProduct {\n /** Catalog code (match key). */\n code?: string;\n /** Whether this product is a one-time item or a recurring subscription. */\n type?: 'item' | 'subscription';\n /** Display-only name for the product. */\n name?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only payload that can be stashed for later merge into a session response. */\nexport interface SessionDisplayCacheData {\n /** Session-level currency (falls into the response only when the server omits it). */\n currency?: string;\n products?: SessionDisplayProduct[];\n}\n\ninterface CachedEntry {\n data: SessionDisplayCacheData;\n expiresAt: number;\n}\n\nconst memoryStore = new Map<string, CachedEntry>();\n\nfunction storageKey(sessionId: string): string {\n return `${STORAGE_KEY_PREFIX}${sessionId}`;\n}\n\nfunction getSessionStorage(): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Stash display-only data for a session. Called client-side right after the\n * server returns a session ID, so the values survive the redirect to the\n * checkout page.\n */\nexport function cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n): void {\n if (!sessionId) return;\n\n const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;\n const entry: CachedEntry = { data, expiresAt: Date.now() + ttl };\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.setItem(storageKey(sessionId), JSON.stringify(entry));\n return;\n } catch {\n // Quota / serialization error — fall through to in-memory store\n }\n }\n\n memoryStore.set(sessionId, entry);\n}\n\n/**\n * Read previously-cached display data for a session, or `null` if nothing\n * is cached (or the TTL has elapsed).\n */\nexport function getSessionDisplayData(\n sessionId: string,\n): SessionDisplayCacheData | null {\n if (!sessionId) return null;\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n const raw = storage.getItem(storageKey(sessionId));\n if (raw) {\n const entry = JSON.parse(raw) as CachedEntry;\n if (entry && typeof entry.expiresAt === 'number' && entry.expiresAt > Date.now()) {\n return entry.data;\n }\n storage.removeItem(storageKey(sessionId));\n }\n } catch {\n // Malformed entry — fall through to memory store\n }\n }\n\n const memEntry = memoryStore.get(sessionId);\n if (memEntry) {\n if (memEntry.expiresAt > Date.now()) {\n return memEntry.data;\n }\n memoryStore.delete(sessionId);\n }\n\n return null;\n}\n\n/**\n * Drop any cached display data for a session. Call from the success page\n * after the payment completes; otherwise the TTL handles cleanup.\n */\nexport function clearSessionDisplayData(sessionId: string): void {\n if (!sessionId) return;\n\n memoryStore.delete(sessionId);\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.removeItem(storageKey(sessionId));\n } catch {\n // ignore\n }\n }\n}\n","import type {\n CardCaptureAdapter,\n CardCaptureEventType,\n CardCaptureMountOptions,\n CardCaptureOutcomeEvent,\n CardCaptureProviderId,\n VaultCaptureResultMessage,\n VaultCardFieldKey,\n VaultCardThemeColors,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n// ---------------------------------------------------------------------------\n// PCIVault hosted-widget capture (Model A — backend-orchestrated)\n//\n// Under TeamFloPay/backend#823 the vault card form is a self-contained,\n// server-rendered widget: the backend returns a `VaultCaptureBlock.html`\n// bundle that owns the PCIVault card fields, its own submit button, card\n// tokenization, PaymentIntent creation + confirmation, 3DS, and decline\n// display. The SDK's only job is to inject that HTML and relay the widget's\n// terminal `postMessage` outcome to the host checkout. No Stripe.js, no\n// PCIVault SDK loading, and no client-side readiness polling happen here —\n// PAN / CVC never enter the SDK runtime.\n//\n// Outcome authenticity: the widget HTML is injected *same-window*, so a genuine\n// outcome's `event.origin` is the merchant page's own origin and the\n// `source: 'flopay-vault'` marker alone is forgeable by any other script on the\n// page (XSS, a rogue dependency, a foreign frame). Before honouring a terminal\n// `complete` / `decline` the listener therefore enforces, when available:\n// • `messageToken` — an unguessable per-session token the backend bakes into\n// the widget bootstrap and echoes back (the only defence against a\n// same-window forger); enforced when the SDK was mounted with one.\n// • `sessionId` — money-affecting outcomes must be bound to the mounted\n// session; an anonymous or mismatched `complete` / `decline` is dropped.\n// • `expectedOrigin` — an optional strict origin gate for widgets that post\n// from a cross-origin frame.\n// Full same-window XSS protection requires the backend to mint+echo\n// `messageToken` (TeamFloPay/backend#823); the enforcement path is in place and\n// activates automatically once the vault block carries the token.\n// ---------------------------------------------------------------------------\n\n/** Configuration for a {@link PciVaultCardCapture} instance. */\nexport interface PciVaultCardCaptureConfig {\n /** Checkout session id bound to the capture (for outcome correlation + trust). */\n sessionId?: string;\n /**\n * Default strict origin for vault `postMessage` outcomes. Overridden by\n * {@link CardCaptureMountOptions.expectedOrigin} when that is supplied at\n * mount. When neither is set the origin gate is skipped (the widget posts\n * same-window in the Model-A flow).\n */\n expectedOrigin?: string;\n}\n\n/** Marker the hosted vault widget stamps on its `postMessage` payloads. */\nconst VAULT_MESSAGE_SOURCE = 'flopay-vault';\n\n/** Best-effort, PII-free Sentry breadcrumb when a global Sentry is present. */\nfunction addBreadcrumb(message: string, data?: Record<string, unknown>): void {\n const sentry = (globalThis as unknown as {\n Sentry?: { addBreadcrumb?: (b: Record<string, unknown>) => void };\n }).Sentry;\n sentry?.addBreadcrumb?.({ category: 'flopay.card-capture', level: 'info', message, data });\n}\n\nfunction isVaultResultMessage(value: unknown): value is VaultCaptureResultMessage {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n record['source'] === VAULT_MESSAGE_SOURCE &&\n (record['type'] === 'ready' ||\n record['type'] === 'submitting' ||\n record['type'] === 'blocked' ||\n record['type'] === 'complete' ||\n record['type'] === 'decline' ||\n record['type'] === 'error' ||\n record['type'] === 'action_required')\n );\n}\n\ninterface VaultResizeMessage {\n source: typeof VAULT_MESSAGE_SOURCE;\n type: 'resize';\n height: number;\n messageToken?: string;\n}\n\ninterface VaultValidationMessage {\n source: typeof VAULT_MESSAGE_SOURCE;\n type: 'validation';\n messages: string[];\n messageToken?: string;\n}\n\nfunction isVaultValidationMessage(value: unknown): value is VaultValidationMessage {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n record['source'] === VAULT_MESSAGE_SOURCE &&\n record['type'] === 'validation' &&\n Array.isArray(record['messages'])\n );\n}\n\nfunction isVaultResizeMessage(value: unknown): value is VaultResizeMessage {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n record['source'] === VAULT_MESSAGE_SOURCE &&\n record['type'] === 'resize' &&\n typeof record['height'] === 'number' &&\n Number.isFinite(record['height'])\n );\n}\n\n/**\n * {@link CardCaptureAdapter} backed by the backend-rendered PCIVault hosted\n * widget.\n *\n * `mount()` injects the server-supplied widget HTML (re-executing its bundled\n * `<script>` so the form bootstraps) and subscribes to the widget's\n * `postMessage` outcome. The backend owns everything else; this adapter never\n * touches the card data, a payment intent, or 3DS.\n */\nexport class PciVaultCardCapture implements CardCaptureAdapter {\n readonly provider: CardCaptureProviderId = 'pcivault';\n\n private readonly config: PciVaultCardCaptureConfig;\n private container: HTMLElement | null = null;\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n /**\n * Parent-page-level overlay rendering the provider's verification challenge\n * (3DS-2 iframe) on `action_required`. Owned by the adapter — not the\n * widget — so it can sit above the host SDK's processing backdrop, which\n * would otherwise visually cover an in-widget challenge iframe.\n */\n private actionOverlay: HTMLElement | null = null;\n /**\n * Listener that catches the `flopay-vault-3ds-return` postMessage from the\n * provider's challenge return page. When the SDK owns the challenge iframe\n * the return page lives inside *that* iframe (not the widget's), so\n * `window.parent` is the host page — the widget's existing message\n * listener can't see it. The SDK forwards completion into the widget via\n * `action_completed` so the widget kicks `/3ds/complete` immediately\n * instead of waiting on the eventual provider webhook.\n */\n private threeDsReturnHandler: ((event: MessageEvent) => void) | null = null;\n /** Per-session integrity token to require on outcomes (from mount options). */\n private messageToken: string | null = null;\n /** Strict origin to require on outcomes, when configured. */\n private expectedOrigin: string | null = null;\n /** Latest merchant theme to push into the (cross-origin) widget. */\n private theme: VaultCardThemeColors | null = null;\n /** Latest host submit-gate state to push into the widget (block its submit). */\n private submitGateBlocked = false;\n /** Latest card-field order + autofocus directive to push into the widget. */\n private cardFieldOrder: VaultCardFieldKey[] | null = null;\n private cardAutoFocus = true;\n private readonly listeners = new Map<\n CardCaptureEventType,\n Set<(event: CardCaptureOutcomeEvent) => void>\n >();\n\n constructor(config: PciVaultCardCaptureConfig = {}) {\n this.config = config;\n }\n\n async mount(container: HTMLElement, options: CardCaptureMountOptions): Promise<void> {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n throw new FloPayError(\n 'The vault card form is only available in the browser.',\n 'api_error',\n { code: 'card_capture_no_window' },\n );\n }\n if (!options?.html || !options.html.trim()) {\n throw new FloPayError(\n 'No vault capture widget HTML was provided to mount the secure card form.',\n 'api_error',\n { code: 'card_capture_no_widget_html' },\n );\n }\n\n this.container = container;\n this.messageToken = options.messageToken ?? null;\n this.expectedOrigin = options.expectedOrigin ?? this.config.expectedOrigin ?? null;\n this.theme = options.theme ?? null;\n this.attachMessageListener();\n this.injectWidget(container, options.html);\n // Push the initial theme + submit-gate + field order once the widget signals\n // ready (its message listener is attached by then); all run on every `ready`.\n this.postTheme();\n this.postSubmitGate();\n this.postCardFieldOrder();\n addBreadcrumb('vault widget mounted', { sessionId: this.config.sessionId });\n // The hosted widget renders its own fields/submit asynchronously after its\n // script boots; surface `ready` once injection succeeds so the surrounding\n // form can drop any loading affordance.\n this.emit('ready', { sessionId: this.config.sessionId });\n }\n\n on(\n event: CardCaptureEventType,\n handler: (event: CardCaptureOutcomeEvent) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(handler);\n return () => {\n this.listeners.get(event)?.delete(handler);\n };\n }\n\n unmount(): void {\n this.hideActionRequiredOverlay();\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler);\n this.messageHandler = null;\n }\n if (this.container) {\n this.container.replaceChildren();\n this.container = null;\n }\n this.messageToken = null;\n this.expectedOrigin = null;\n // NB: do *not* clear `this.listeners` here. Subscribers own their handlers\n // and remove them via the `off()` returned by `on()`. The card form keeps\n // two independent subscription effects on this adapter — VaultCardFields\n // (ready/error/validation) and the parent card form (submitting/complete/\n // decline/error). A widget remount (e.g. the buttons-layout card expand)\n // unmounts/remounts VaultCardFields, but the parent effect's deps are stable\n // so it does not re-subscribe. Clearing here used to silently drop the\n // parent's listeners → no `submitting` (no AVS red-ring, no account PATCH)\n // on the buttons path. The message listener + container are still torn down.\n }\n\n // ── internals ──\n\n /**\n * Inject the server-rendered widget HTML. `innerHTML` does not execute\n * embedded `<script>` tags, so each script node is replaced with a freshly\n * created element that the browser will load and run (this is what boots the\n * PCIVault form bundle against the `data-flopay-config` container).\n */\n private injectWidget(container: HTMLElement, html: string): void {\n container.innerHTML = html;\n const scripts = Array.from(container.querySelectorAll('script'));\n for (const oldScript of scripts) {\n const script = document.createElement('script');\n for (const attr of Array.from(oldScript.attributes)) {\n script.setAttribute(attr.name, attr.value);\n }\n script.text = oldScript.text;\n oldScript.replaceWith(script);\n }\n }\n\n private attachMessageListener(): void {\n if (this.messageHandler) return;\n const handler = (event: MessageEvent): void => {\n // 1. Optional strict origin gate (for cross-origin-posting widgets).\n if (this.expectedOrigin && event.origin !== this.expectedOrigin) return;\n const data = event.data;\n // Auto-resize: the hosted form reports its content height so the\n // cross-origin iframe (which cannot self-size) grows/shrinks to fit.\n // Validate the widget source + integrity token, then size the iframe.\n if (isVaultResizeMessage(data)) {\n if (this.messageToken && data.messageToken !== this.messageToken) return;\n this.applyHeight(data.height);\n return;\n }\n // Inline field-validation surfaced from the hosted form (live). Forwarded\n // so the host can show it in the error box + onError. Empty `messages`\n // clears it.\n if (isVaultValidationMessage(data)) {\n if (this.messageToken && data.messageToken !== this.messageToken) return;\n const text = data.messages.filter((m) => typeof m === 'string' && m.trim()).join(' ');\n this.emit('validation', { sessionId: this.config.sessionId, message: text || undefined });\n return;\n }\n if (!isVaultResultMessage(data)) return;\n // 2. Integrity token: when the SDK was mounted with one, every outcome\n // must echo it. This is the only defence that survives same-window\n // forgery (the token is not present in the DOM/URL).\n if (this.messageToken && data.messageToken !== this.messageToken) return;\n const boundSession = this.config.sessionId;\n const incomingSession =\n typeof data.sessionId === 'string' ? data.sessionId : undefined;\n // 3. Reject any session-tagged outcome that targets a different session.\n if (boundSession && incomingSession && incomingSession !== boundSession) {\n return;\n }\n // 4. Money-affecting outcomes must be bound to the mounted session — a\n // forged `complete` / `decline` that omits the (server-issued) session\n // id cannot falsely drive merchant-side completion logic.\n if (\n (data.type === 'complete' || data.type === 'decline') &&\n boundSession &&\n incomingSession !== boundSession\n ) {\n return;\n }\n const outcome: CardCaptureOutcomeEvent = {\n sessionId: data.sessionId ?? this.config.sessionId,\n intentId: data.intentId,\n declineReason: data.declineReason,\n message: data.message,\n nextActionRedirectUrl: data.nextActionRedirectUrl,\n };\n addBreadcrumb(`vault widget ${data.type}`, {\n sessionId: outcome.sessionId,\n declineReason: outcome.declineReason,\n });\n // The widget's `ready` confirms its message listener is attached — (re)push\n // the theme + submit-gate so a form that mounted before the host posted\n // still gets them.\n if (data.type === 'ready') {\n this.postTheme();\n this.postSubmitGate();\n this.postCardFieldOrder();\n }\n // Provider needs the buyer to complete a verification challenge (3DS-2).\n // Render the challenge iframe at the PARENT page level — the widget can\n // only mount the challenge inside its own iframe, which gets visually\n // covered by the host's processing backdrop, so the adapter does the\n // rendering up here where it can sit above everything.\n if (data.type === 'action_required' && data.nextActionRedirectUrl) {\n this.showActionRequiredOverlay(data.nextActionRedirectUrl);\n }\n // Terminal outcomes always tear down the challenge overlay (success,\n // decline, or error). A `submitting` follow-up (e.g. the buyer retried\n // after a soft decline) also clears it.\n if (\n data.type === 'complete' ||\n data.type === 'decline' ||\n data.type === 'error' ||\n data.type === 'submitting'\n ) {\n this.hideActionRequiredOverlay();\n }\n this.emit(data.type, outcome);\n };\n this.messageHandler = handler;\n window.addEventListener('message', handler);\n }\n\n /**\n * Push merchant theme colors into the hosted widget (live). The host calls\n * this on a runtime theme switch; the widget applies them to its CSS variables\n * without a remount. Stores the latest theme so `ready` can re-push it.\n */\n applyTheme(theme: VaultCardThemeColors): void {\n this.theme = theme;\n this.postTheme();\n }\n\n /** postMessage the current theme to the widget's (cross-origin) document. */\n private postTheme(): void {\n if (!this.theme || !this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage({ source: 'flopay-vault-host', type: 'theme', theme: this.theme }, '*');\n } catch {\n // Best-effort — theming is cosmetic and must never break the form.\n }\n }\n\n /**\n * Gate the widget's submit from the host. When `blocked`, the widget cancels\n * its next submit and emits `'blocked'` instead of `'submitting'` so the host\n * can validate merchant-DOM fields (AVS) first. Stored so `ready` re-pushes it.\n */\n setSubmitGate(blocked: boolean): void {\n this.submitGateBlocked = blocked;\n this.postSubmitGate();\n }\n\n /** postMessage the current submit-gate state to the widget's document. */\n private postSubmitGate(): void {\n if (!this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage(\n { source: 'flopay-vault-host', type: 'gate', blocked: this.submitGateBlocked },\n '*',\n );\n } catch {\n // Best-effort — the host still validates and surfaces errors on its side.\n }\n }\n\n /**\n * Push the card-field order + autofocus directive into the widget (live). The\n * widget re-sequences its rows (DOM order, so tab order follows) and focuses\n * its first field unless `autoFocus` is false. Stored so `ready` re-pushes it.\n */\n setCardFieldOrder(order: VaultCardFieldKey[] | null, autoFocus: boolean): void {\n this.cardFieldOrder = order;\n this.cardAutoFocus = autoFocus;\n this.postCardFieldOrder();\n }\n\n /** postMessage the current field order + autofocus to the widget's document. */\n private postCardFieldOrder(): void {\n if (!this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage(\n {\n source: 'flopay-vault-host',\n type: 'fieldOrder',\n order: this.cardFieldOrder,\n autoFocus: this.cardAutoFocus,\n },\n '*',\n );\n } catch {\n // Best-effort — field order is cosmetic and must never break the form.\n }\n }\n\n private emit(event: CardCaptureEventType, payload: CardCaptureOutcomeEvent): void {\n for (const handler of this.listeners.get(event) ?? []) {\n handler(payload);\n }\n }\n\n /**\n * Render the provider-hosted verification challenge (e.g. Stripe 3DS-2) in a\n * full-page overlay at the PARENT page level. The widget's inline-iframe\n * approach is unusable because the SDK's processing backdrop sits above the\n * vault iframe, hiding any challenge mounted inside it — by lifting the\n * iframe to the host page the adapter can give it a z-index that wins.\n *\n * The overlay tears down on the next terminal outcome\n * (`complete`/`decline`/`error`) or when the buyer closes it via the backdrop\n * close button. Closing manually is a soft abandon — the next `/status` poll\n * either reveals a real outcome (the challenge completed via the issuer's\n * own redirect to `/vault/3ds/return`, which posts back into the widget) or\n * surfaces `requires_action` again so the host can decide what to do.\n */\n private showActionRequiredOverlay(challengeUrl: string): void {\n if (typeof document === 'undefined') return;\n // Idempotent: re-targeting an existing overlay just navigates the iframe.\n if (this.actionOverlay) {\n const existingIframe = this.actionOverlay.querySelector('iframe');\n if (existingIframe instanceof HTMLIFrameElement) {\n existingIframe.src = challengeUrl;\n }\n return;\n }\n\n const backdrop = document.createElement('div');\n backdrop.setAttribute('data-flopay-action-required', '1');\n backdrop.style.cssText = [\n 'position:fixed',\n 'inset:0',\n // Maximum signed 32-bit z-index; the SDK's own processing backdrop sits\n // well below this so the challenge is visible and interactive.\n 'z-index:2147483647',\n 'background:rgba(15,23,42,0.6)',\n 'display:flex',\n 'align-items:center',\n 'justify-content:center',\n 'padding:16px',\n ].join(';');\n\n const frame = document.createElement('iframe');\n frame.setAttribute('title', 'Card authentication');\n frame.setAttribute('allow', 'payment');\n frame.style.cssText = [\n 'width:min(100%,460px)',\n 'height:min(100%,640px)',\n 'border:0',\n 'border-radius:12px',\n 'background:#fff',\n 'box-shadow:0 12px 30px rgba(0,0,0,0.35)',\n ].join(';');\n frame.src = challengeUrl;\n backdrop.appendChild(frame);\n\n // Listen for the `/vault/3ds/return` postMessage. The return page is\n // loaded inside THIS overlay iframe (not the widget's), so its\n // `window.parent` is the host page, not the widget — the widget's own\n // listener won't catch it. Forward completion into the widget so the\n // widget kicks `/3ds/complete` immediately.\n const returnHandler = (event: MessageEvent): void => {\n // Only the challenge iframe we just mounted may dismiss this overlay. The\n // `/vault/3ds/return` page loads inside `frame`, so its postMessage\n // arrives with `event.source === frame.contentWindow`. Gating on the\n // frame's window stops an unrelated frame on the host page from forging\n // the marker string to trigger a premature dismissal. (Origin can't be\n // matched against `challengeUrl` here — the return page is served by the\n // billing API, a different origin from the provider's challenge.)\n if (event.source !== frame.contentWindow) return;\n const data = event.data;\n if (!data || typeof data !== 'object') return;\n const record = data as Record<string, unknown>;\n if (record['source'] !== 'flopay-vault-3ds-return') return;\n this.hideActionRequiredOverlay();\n this.postActionCompleted(record['status']);\n };\n window.addEventListener('message', returnHandler);\n this.threeDsReturnHandler = returnHandler;\n\n document.body.appendChild(backdrop);\n this.actionOverlay = backdrop;\n addBreadcrumb('vault 3ds challenge overlay shown');\n }\n\n /**\n * Tell the vault widget that the buyer has completed (or abandoned) the\n * challenge. The widget responds by POSTing `/3ds/complete` — its\n * sub-300ms sync resolver writes the follow-up attempt row immediately,\n * so the next `/status` poll resolves to a terminal outcome instead of\n * waiting for the eventual provider webhook.\n */\n private postActionCompleted(status: unknown): void {\n if (!this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage(\n {\n source: 'flopay-vault-host',\n type: 'action_completed',\n status: typeof status === 'string' ? status : 'unknown',\n },\n '*',\n );\n } catch {\n // Best-effort — even if the postMessage fails the widget's poll loop\n // will eventually catch the terminal state from the provider webhook.\n }\n }\n\n private hideActionRequiredOverlay(): void {\n if (this.threeDsReturnHandler) {\n window.removeEventListener('message', this.threeDsReturnHandler);\n this.threeDsReturnHandler = null;\n }\n if (!this.actionOverlay) return;\n this.actionOverlay.parentNode?.removeChild(this.actionOverlay);\n this.actionOverlay = null;\n addBreadcrumb('vault 3ds challenge overlay hidden');\n }\n\n /**\n * Size the hosted-widget iframe to the height reported by the form inside it.\n * Cross-origin iframes don't auto-size to their content, so the widget posts\n * its measured height and we apply it here (clamped to a sane range). This is\n * what lets the card form shrink/grow to fit instead of sitting at a fixed\n * height.\n */\n private applyHeight(height: number): void {\n const iframe = this.container?.querySelector('iframe');\n if (!iframe) return;\n const clamped = Math.max(0, Math.min(Math.ceil(height), 2000));\n iframe.style.height = `${clamped}px`;\n }\n}\n","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport { cacheSessionDisplayData } from './session-display-cache.js';\n\n/**\n * Max number of coupon codes the billing API accepts per session create.\n * Mirrors the backend guard introduced in billing v1.1.2 — exceeding this\n * returns the `CouponLimitExceeded` error.\n */\nconst MAX_COUPON_CODES = 5;\n\ninterface ApiErrorBody {\n code?: unknown;\n message?: unknown;\n error?: { code?: unknown; message?: unknown };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Map a billing API error body to a `FloPayError`. Surfaces the coupon\n * validation codes introduced in billing v1.1.2 (`CouponLimitExceeded`,\n * `CouponCurrencyUnsupported`) as structured errors so callers can branch\n * on `error.code` instead of parsing the HTTP status.\n */\nfunction buildCheckoutSessionError(status: number, payload: ApiErrorBody | undefined): FloPayError {\n const nested = payload?.error;\n const code =\n readString(payload?.code)\n ?? readString(nested?.code)\n ?? `http_${status}`;\n const message =\n readString(payload?.message)\n ?? readString(nested?.message)\n ?? defaultMessageForCode(code, status);\n\n return new FloPayError(message, 'api_error', { code, statusCode: status });\n}\n\nfunction defaultMessageForCode(code: string, status: number): string {\n switch (code) {\n case 'CouponLimitExceeded':\n return `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`;\n case 'CouponCurrencyUnsupported':\n return 'One of the applied coupons has no price configured for the cart currency.';\n default:\n return `Failed to create checkout session (HTTP ${status}).`;\n }\n}\n\n/**\n * Creates a checkout session via the billing API and redirects the user\n * to the hosted checkout page.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n *\n * - On 201: Redirects to `{checkoutBaseUrl}/secure?id={uuid}&...redirectParams`\n * - On 204: Redirects directly to `successUrl` (payment method already on file)\n * - On other: Returns `{ status }` without redirecting\n *\n * @example\n * ```ts\n * import { createCheckoutSession } from '@flopay/js';\n *\n * await createCheckoutSession({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * currency: 'USD',\n * items: [{\n * code: 'initial_charge',\n * quantity: 1,\n * metadata: { source: 'web' },\n * }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * redirectParams: { email: 'user@example.com', bg: 'courses', mode: 'confirm' },\n * });\n * ```\n */\nexport async function createCheckoutSession(\n options: CreateSessionParams,\n): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n products,\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n currency,\n utmMetadata,\n } = options;\n\n if (couponCodes.length > MAX_COUPON_CODES) {\n throw new FloPayError(\n `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`,\n 'validation_error',\n { code: 'CouponLimitExceeded', param: 'couponCodes' },\n );\n }\n\n const wireProducts = products ?? foldIntoProducts(items, subscriptions);\n const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions, wireProducts);\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId,\n checkoutVersion: SDK_VERSION,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n addressLine1: account.addressLine1 ?? null,\n addressLine2: account.addressLine2 ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: ({ data?: { uuid?: string; nonce?: string } } & ApiErrorBody) | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string; nonce?: string } } & ApiErrorBody;\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status >= 400) {\n throw buildCheckoutSessionError(status, body);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n const nonce = body?.data?.nonce;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n if (!nonce) {\n throw new FloPayError(\n 'Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.',\n 'api_error',\n { code: 'MissingCheckoutSessionToken' },\n );\n }\n\n // Auto-stash display-only fields the consumer passed in so the checkout\n // page (post-redirect) renders the right totals/names even though the\n // backend no longer persists them. Runs before the redirect — values\n // survive the navigation via sessionStorage.\n if (wireProducts.length || sessionCurrency) {\n cacheSessionDisplayData(uuid, {\n currency: sessionCurrency,\n products: wireProducts.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency,\n })),\n });\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n if (setCookie && typeof window !== 'undefined' && typeof document !== 'undefined') {\n const checkoutData = JSON.stringify({ origin_url: cancelUrl });\n const domain = window.location.hostname.split('.').slice(-2).join('.');\n document.cookie = `checkout_data=${encodeURIComponent(checkoutData)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n // Carry the session-bound checkout token to the hosted checkout page so\n // it can echo `x-checkout-session-token` on every continuation call\n // (post-#640 backends require this header to match the session nonce).\n // The URL is deliberately not modified — keeping the nonce out of the\n // location bar avoids history / Referer leakage.\n document.cookie = `flopay_checkout_token=${encodeURIComponent(nonce)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString(), nonce };\n }\n\n if (status === 204) {\n if (typeof window !== 'undefined') {\n window.location.href = successUrl;\n }\n return { status: 204 };\n }\n\n return { status };\n}\n\n/**\n * Creates a checkout session with automatic retry on timeout/abort errors.\n *\n * Uses exponential backoff: 100ms, 200ms, 400ms, etc.\n */\nexport async function createCheckoutSessionWithRetries(\n options: CreateSessionParams & { maxRetries?: number },\n): Promise<CheckoutSessionResult> {\n const { maxRetries = 3, ...sessionOptions } = options;\n\n if (!Number.isFinite(maxRetries) || !Number.isInteger(maxRetries) || maxRetries <= 0) {\n throw new Error('Number of retries must be greater than 0');\n }\n\n let lastErr: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await createCheckoutSession(sessionOptions);\n } catch (err) {\n lastErr = err;\n\n if (err instanceof Error && err.name === 'AbortError' && attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastErr ?? new Error('Unknown error during checkout session creation');\n}\n"],"mappings":"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","names":["import_shared","import_shared","import_shared","import_shared","import_shared","import_shared","readString"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/pci-vault-card-capture.ts","../src/create-checkout-session.ts"],"sourcesContent":["export { loadFloPay } from './load.js';\nexport { FloPay } from './flopay.js';\nexport { FloPayElements } from './elements.js';\nexport { StripeAdapter } from './stripe-adapter.js';\nexport { PaymentAPI } from './payment-api.js';\nexport { PciVaultCardCapture } from './pci-vault-card-capture.js';\nexport type { PciVaultCardCaptureConfig } from './pci-vault-card-capture.js';\nexport { createCheckoutSession, createCheckoutSessionWithRetries } from './create-checkout-session.js';\nexport {\n cacheSessionDisplayData,\n getSessionDisplayData,\n clearSessionDisplayData,\n} from './session-display-cache.js';\nexport type {\n SessionDisplayProduct,\n SessionDisplayCacheData,\n} from './session-display-cache.js';\n","import type { FloPayConfig } from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\nimport { StripeAdapter } from './stripe-adapter.js';\nimport { FloPay } from './flopay.js';\n\n/**\n * Cache of FloPay instances keyed by publishable key. Multiple instances can\n * coexist when a consumer drives more than one Stripe publishable key during a\n * single page-load.\n */\nconst instanceCache = new Map<string, FloPay>();\n\n/**\n * Loads and initializes the FloPay SDK.\n *\n * This is the primary entry point for consumer applications.\n * It initializes the underlying payment provider (currently Stripe)\n * and returns a ready-to-use `FloPay` instance.\n *\n * Instances are cached per `publishableKey`, so calling `loadFloPay` twice\n * with the same key returns the same instance. Different keys produce\n * independent instances that can run side-by-side.\n *\n * @example\n * ```ts\n * import { loadFloPay } from '@flopay/js';\n *\n * const flopay = await loadFloPay('pk_test_...');\n * const elements = flopay.elements();\n * const cardElement = await elements.create('card');\n * cardElement.mount('#card-container');\n * ```\n */\nexport async function loadFloPay(\n publishableKey: string,\n options?: Omit<FloPayConfig, 'publishableKey'>,\n): Promise<FloPay> {\n if (!publishableKey) {\n throw new FloPayError(\n 'A publishable key is required to initialize FloPay.',\n 'validation_error',\n { param: 'publishableKey' },\n );\n }\n\n const cached = instanceCache.get(publishableKey);\n if (cached) return cached;\n\n const config: FloPayConfig = {\n publishableKey,\n ...options,\n };\n\n // Stripe is the only adapter today. Direct PayPal does not flow through\n // this loader — it renders via the official PayPal JS SDK from\n // `DirectPayPalButton`.\n const adapter = new StripeAdapter();\n await adapter.initialize(config);\n\n const instance = new FloPay(adapter, config);\n instanceCache.set(publishableKey, instance);\n\n return instance;\n}\n","import type {Stripe, StripeElements, StripeElementType} from '@stripe/stripe-js';\nimport type {\n BillingDetails,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n ConfirmPaymentParams,\n CreatePaymentMethodResult,\n ElementOptions,\n ElementType,\n FloPayConfig,\n MountedElement,\n PaymentProviderAdapter,\n PaymentResult,\n} from '@flopay/shared';\nimport {FloPayError, isSetupIntentClientSecret} from '@flopay/shared';\n\n/**\n * Maps FloPay element types to Stripe element types.\n * FloPay uses camelCase; Stripe uses lowercase with hyphens or specific names.\n */\nfunction toStripeElementType(type: ElementType): StripeElementType {\n const map: Record<ElementType, string> = {\n payment: 'payment',\n card: 'card',\n cardNumber: 'cardNumber',\n cardExpiry: 'cardExpiry',\n cardCvc: 'cardCvc',\n address: 'address',\n };\n return map[type] as StripeElementType;\n}\n\n/**\n * Wraps a Stripe element instance in the FloPay MountedElement interface.\n *\n * TODO: In a future phase, this wrapper will render the Stripe element inside\n * an iframe hosted on the FloPay domain for PCI DSS SAQ-A compliance.\n * For now, it delegates directly to the Stripe element.\n */\n/** Minimal interface shared by all Stripe element types. */\ninterface StripeElementLike {\n mount(domElement: string | HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update(options?: Record<string, unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler?: (...args: unknown[]) => void): void;\n}\n\n/**\n * Maps a `FloPayAppearance.theme` ('default' | 'flat' | 'night' | 'none') to a\n * Stripe Elements Appearance `theme` ('stripe' | 'flat' | 'night'). Stripe's\n * Appearance API only accepts those three; anything else triggers a console\n * warning and silently falls back. We normalize here so the bundles can keep\n * `'default'` as their public token.\n */\nfunction toStripeAppearanceTheme(\n theme: 'default' | 'flat' | 'night' | 'none' | undefined,\n): 'stripe' | 'night' | 'flat' {\n switch (theme) {\n case 'night': return 'night';\n case 'flat': return 'flat';\n // 'default', 'none', undefined, or any unexpected value → Stripe's baseline.\n default: return 'stripe';\n }\n}\n\nfunction wrapStripeElement(\n stripeElement: StripeElementLike,\n): MountedElement {\n const el = stripeElement;\n\n return {\n mount(container: HTMLElement) {\n el.mount(container);\n },\n unmount() {\n el.unmount();\n },\n update(options: Partial<ElementOptions>) {\n el.update(options as Record<string, unknown>);\n },\n on(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['on']?.(event, handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['off']?.(event, handler);\n },\n destroy() {\n el.destroy();\n },\n };\n}\n\n/**\n * Payment provider adapter backed by Stripe.\n *\n * Implements the `PaymentProviderAdapter` interface so that FloPay consumers\n * interact with a stable API regardless of the upstream provider.\n */\nexport class StripeAdapter implements PaymentProviderAdapter {\n readonly name = 'stripe';\n private stripe: Stripe | null = null;\n private elements: StripeElements | null = null;\n // Serialized appearance currently applied to `this.elements`. Used to detect\n // when consumers swap themes mid-session so we can live-update the Stripe\n // Elements group instead of returning a stale-styled cache. `null` while no\n // elements group exists.\n private appliedAppearanceKey: string | null = null;\n\n async initialize(config: FloPayConfig): Promise<void> {\n // loadStripe only works in the browser — skip initialization during SSR/SSG\n if (typeof window === 'undefined') {\n return;\n }\n\n let loadStripe: typeof import('@stripe/stripe-js').loadStripe;\n try {\n ({ loadStripe } = await import('@stripe/stripe-js'));\n } catch {\n // @stripe/stripe-js ships as a direct dependency of @flopay/js, so this\n // should never resolve to a missing module in practice. If it does (e.g.\n // a broken install tree), surface a clear FloPayError instead of an\n // unhandled module-resolution rejection.\n throw new FloPayError(\n 'Failed to load @stripe/stripe-js. Reinstall @flopay/js.',\n 'api_error',\n { code: 'StripeJsLoadFailed' },\n );\n }\n\n const stripe = await loadStripe(config.publishableKey, {\n locale: (config.locale as 'auto') ?? 'auto',\n });\n\n if (!stripe) {\n throw new FloPayError(\n 'Failed to initialize Stripe. Check your publishable key.',\n 'authentication_error',\n );\n }\n\n this.stripe = stripe;\n }\n\n /** Lazily creates the Stripe Elements group for the given options. */\n private getElements(options?: ElementOptions): StripeElements {\n if (!this.stripe) {\n throw new FloPayError(\n 'StripeAdapter not initialized. Call initialize() first.',\n 'api_error',\n );\n }\n\n const stripeAppearance = options?.appearance\n ? {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n }\n : undefined;\n const nextAppearanceKey = stripeAppearance ? JSON.stringify(stripeAppearance) : null;\n\n if (!this.elements) {\n let elementsOptions: Record<string, unknown>;\n\n const deferredAmount = options?.amount ?? 0;\n const deferredCurrency = (options?.currency ?? 'usd').toLowerCase();\n const paymentMethodCreation = options?.paymentMethodCreation ?? ('manual' as const);\n\n if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret. Stripe\n // infers payment vs setup mode from the secret prefix (`pi_` / `seti_`).\n elementsOptions = { clientSecret: options.clientSecret };\n } else if (deferredAmount > 0) {\n // No server-side intent yet — use deferred payment mode with manual\n // payment method creation. Matches checkout/StripeCardForm:\n // mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: deferredAmount,\n currency: deferredCurrency,\n paymentMethodCreation,\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n } else {\n // Trial-only / $0 checkout: there is nothing to charge up front, so a\n // deferred PaymentIntent is invalid — Stripe rejects `elements()` with\n // \"Invalid value for elements(): `amount` must be greater than 0\" and\n // the buyer never reaches the card form. Use deferred *setup* mode\n // instead (no amount); the captured card is attached to the SetupIntent\n // the server creates and charged when the trial ends.\n elementsOptions = {\n mode: 'setup' as const,\n currency: deferredCurrency,\n paymentMethodCreation,\n };\n }\n\n if (stripeAppearance) {\n elementsOptions['appearance'] = stripeAppearance;\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n this.appliedAppearanceKey = nextAppearanceKey;\n } else if (nextAppearanceKey !== this.appliedAppearanceKey) {\n // Elements group already exists but the consumer changed (or removed)\n // the appearance. Stripe Elements supports a live `update({ appearance })`\n // call which re-styles mounted elements in place — no re-create, no\n // input-state loss. When the consumer drops `appearance`, fall through\n // with an empty payload so any prior variables/rules are cleared.\n (this.elements as unknown as { update: (opts: Record<string, unknown>) => void }).update({\n appearance: stripeAppearance ?? {},\n });\n this.appliedAppearanceKey = nextAppearanceKey;\n }\n\n return this.elements;\n }\n\n async createElement(\n type: ElementType,\n options: ElementOptions,\n ): Promise<MountedElement> {\n const elements = this.getElements(options);\n const stripeType = toStripeElementType(type);\n\n const elementOptions: Record<string, unknown> = {};\n if (options.layout) {\n elementOptions['layout'] = options.layout;\n }\n if (options.defaultValues) {\n elementOptions['defaultValues'] = options.defaultValues;\n }\n if (options.readOnly) {\n elementOptions['readOnly'] = options.readOnly;\n }\n if (options.mode) {\n elementOptions['mode'] = options.mode;\n }\n if (options.style) {\n elementOptions['style'] = options.style;\n }\n\n const stripeElement = elements.create(stripeType as 'card', elementOptions) as unknown as StripeElementLike;\n return wrapStripeElement(stripeElement);\n }\n\n getElement(type: ElementType): MountedElement | null {\n if (!this.elements) return null;\n const stripeType = toStripeElementType(type);\n const existing = this.elements.getElement(stripeType as 'card') as unknown as StripeElementLike | null;\n if (!existing) return null;\n return wrapStripeElement(existing);\n }\n\n async submitElements(): Promise<{ error?: FloPayError }> {\n if (!this.stripe || !this.elements) {\n return { error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n const { error } = await this.elements.submit();\n if (error) {\n return {\n error: new FloPayError(error.message ?? 'Validation failed', 'validation_error'),\n };\n }\n return {};\n }\n\n async createPaymentMethod(billingDetails?: BillingDetails): Promise<CreatePaymentMethodResult> {\n if (!this.stripe || !this.elements) {\n return {\n paymentMethodId: null,\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Detect split card fields vs unified PaymentElement.\n // With split fields (cardNumber + cardExpiry + cardCvc), Stripe requires\n // passing the specific cardNumber element with type: 'card'.\n // With unified PaymentElement, pass the elements group.\n const cardNumberEl = this.elements.getElement('cardNumber' as 'card');\n\n // Build billing_details for Stripe AVS (Address Verification).\n // When present, Stripe runs postal code and address checks automatically.\n const stripeBilling = billingDetails ? {\n billing_details: {\n ...(billingDetails.email ? { email: billingDetails.email } : {}),\n ...(billingDetails.name ? { name: billingDetails.name } : {}),\n ...(billingDetails.address ? {\n address: {\n ...(billingDetails.address.country ? { country: billingDetails.address.country } : {}),\n ...(billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {}),\n ...(billingDetails.address.city ? { city: billingDetails.address.city } : {}),\n ...(billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {}),\n ...(billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {}),\n ...(billingDetails.address.state ? { state: billingDetails.address.state } : {}),\n },\n } : {}),\n },\n } : {};\n\n const { error, paymentMethod } = cardNumberEl\n ? await this.stripe.createPaymentMethod({\n type: 'card',\n card: cardNumberEl,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0])\n : await this.stripe.createPaymentMethod({\n elements: this.elements,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0]);\n\n if (error) {\n return {\n paymentMethodId: null,\n error: new FloPayError(\n error.message ?? 'Failed to create payment method',\n 'api_error',\n { code: error.code },\n ),\n };\n }\n\n return { paymentMethodId: paymentMethod.id };\n }\n\n async confirmCardPayment(\n params: ConfirmCardPaymentParams,\n ): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return {\n status: 'failed',\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Trial-only / $0 checkouts get a SetupIntent (`seti_…`) from the server.\n // Stripe's `confirmCardPayment` rejects those with \"value should be a\n // PaymentIntent client secret. You specified: a SetupIntent client\n // secret.\" — route them through `confirmCardSetup`, which captures the\n // card for later off-session billing when the trial ends. The resulting\n // SetupIntent id flows through `paymentIntentId` so downstream /process\n // gets one intent identifier regardless of the intent kind.\n if (isSetupIntentClientSecret(params.clientSecret)) {\n const { error: setupError, setupIntent } = await this.stripe.confirmCardSetup(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (setupError) {\n return {\n status: 'failed',\n error: new FloPayError(\n setupError.message ?? 'Payment failed',\n 'api_error',\n { code: setupError.code, declineCode: setupError.decline_code },\n ),\n };\n }\n\n return {\n status: (setupIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: setupIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(setupIntent?.payment_method),\n };\n }\n\n const { error, paymentIntent } = await this.stripe.confirmCardPayment(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n { code: error.code, declineCode: error.decline_code },\n ),\n };\n }\n\n return {\n status: (paymentIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: paymentIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent?.payment_method),\n };\n }\n\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n if (!this.stripe || !this.elements) {\n throw new FloPayError(\n 'StripeAdapter not initialized or no elements created.',\n 'api_error',\n );\n }\n\n const billing = params.billingDetails;\n const paymentMethodData = billing ? {\n billing_details: {\n ...(billing.email ? { email: billing.email } : {}),\n ...(billing.name ? { name: billing.name } : {}),\n ...(billing.address ? {\n address: {\n ...(billing.address.country ? { country: billing.address.country } : {}),\n ...(billing.address.postal_code ? { postal_code: billing.address.postal_code } : {}),\n ...(billing.address.city ? { city: billing.address.city } : {}),\n ...(billing.address.line1 ? { line1: billing.address.line1 } : {}),\n ...(billing.address.line2 ? { line2: billing.address.line2 } : {}),\n ...(billing.address.state ? { state: billing.address.state } : {}),\n },\n } : {}),\n },\n } : undefined;\n\n const { error, paymentIntent } = await this.stripe.confirmPayment({\n elements: this.elements,\n clientSecret: params.clientSecret,\n confirmParams: {\n return_url: params.returnUrl ?? window.location.href,\n ...(paymentMethodData ? { payment_method_data: paymentMethodData } : {}),\n },\n redirect: 'if_required',\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n {\n code: error.code,\n declineCode: error.decline_code,\n },\n ),\n };\n }\n\n if (!paymentIntent) {\n return { status: 'failed', error: new FloPayError('No payment intent returned', 'api_error') };\n }\n\n const statusMap: Record<string, PaymentResult['status']> = {\n succeeded: 'succeeded',\n processing: 'processing',\n requires_action: 'requires_action',\n requires_payment_method: 'failed',\n canceled: 'failed',\n };\n\n return {\n status: statusMap[paymentIntent.status] ?? 'failed',\n paymentIntentId: paymentIntent.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent.payment_method),\n };\n }\n\n private extractPaymentMethodId(\n paymentMethod: string | { id?: string | null } | null | undefined,\n ): string | undefined {\n if (typeof paymentMethod === 'string' && paymentMethod.startsWith('pm_')) {\n return paymentMethod;\n }\n if (paymentMethod && typeof paymentMethod === 'object' && typeof paymentMethod.id === 'string') {\n return paymentMethod.id;\n }\n return undefined;\n }\n\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return { status: 'failed', error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n\n const baseUrl = params.billingApiUrl.replace(/\\/+$/, '');\n\n if (this.elements) {\n const { error: submitError } = await this.elements.submit();\n if (submitError) {\n return {\n status: 'failed',\n error: new FloPayError(\n submitError.message ?? 'PayPal payment failed',\n 'validation_error',\n { code: submitError.code },\n ),\n };\n }\n }\n\n const intentHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (params.nonce) intentHeaders['x-checkout-session-token'] = params.nonce;\n\n const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: intentHeaders,\n body: JSON.stringify({\n sessionId: params.sessionId,\n email: params.email,\n paymentMethodType: 'paypal',\n isPaypal: 'true',\n }),\n });\n\n if (!intentResponse.ok) {\n return { status: 'failed', error: new FloPayError('Failed to create PayPal payment intent', 'api_error') };\n }\n\n const intentJson = await intentResponse.json() as { data?: { id?: string } };\n const intentClientSecret = intentJson.data?.id;\n if (!intentClientSecret) {\n return { status: 'failed', error: new FloPayError('No client_secret in response', 'api_error') };\n }\n\n const { error } = await this.stripe.confirmPayment({\n clientSecret: intentClientSecret,\n elements: this.elements ?? undefined,\n confirmParams: { return_url: params.returnUrl },\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'PayPal payment failed', 'api_error', { code: error.code }),\n };\n }\n\n return {\n status: 'processing',\n };\n }\n\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n if (!this.stripe || typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n const paymentIntentId = params.get('payment_intent');\n const clientSecret = params.get('payment_intent_client_secret');\n const redirectStatus = params.get('redirect_status');\n\n if (!paymentIntentId || !clientSecret) return null;\n\n if (redirectStatus === 'failed') {\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was declined. Please try again.', 'api_error'),\n };\n }\n\n const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(clientSecret);\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'Failed to retrieve PayPal payment', 'api_error'),\n };\n }\n\n if (paymentIntent && (paymentIntent.status === 'requires_capture' || paymentIntent.status === 'succeeded')) {\n const pmId = typeof paymentIntent.payment_method === 'string'\n ? paymentIntent.payment_method\n : paymentIntent.payment_method?.id;\n\n // Clean up URL params\n const url = new URL(window.location.href);\n url.searchParams.delete('payment_intent');\n url.searchParams.delete('payment_intent_client_secret');\n url.searchParams.delete('redirect_status');\n window.history.replaceState({}, '', url.toString());\n\n return {\n status: paymentIntent.status as ConfirmCardPaymentResult['status'],\n paymentIntentId: paymentIntent.id,\n paymentMethodId: pmId,\n };\n }\n\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was not completed. Please try again.', 'api_error'),\n };\n }\n\n getRawProvider(): unknown {\n return this.stripe;\n }\n\n createPayPalElements(options: ElementOptions): unknown {\n if (!this.stripe) return null;\n\n const elementsOptions: Record<string, unknown> = {\n mode: 'payment' as const,\n amount: options.amount ?? 0,\n currency: (options.currency ?? 'usd').toLowerCase(),\n captureMethod: 'manual' as const,\n };\n\n if (options.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n\n if (options.appearance) {\n elementsOptions['appearance'] = {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n return this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n }\n\n destroy(): void {\n this.elements = null;\n this.appliedAppearanceKey = null;\n this.stripe = null;\n }\n}\n","import type {\n PaymentProviderAdapter,\n FloPayConfig,\n ElementOptions,\n ConfirmPaymentParams,\n PaymentResult,\n CardCaptureAdapter,\n CheckoutSession,\n NormalizedCheckoutSession,\n CreatePaymentMethodResult,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n} from '@flopay/shared';\nimport { FloPayError, resolveBillingApiUrl } from '@flopay/shared';\nimport { FloPayElements } from './elements.js';\nimport { PaymentAPI } from './payment-api.js';\nimport { PciVaultCardCapture } from './pci-vault-card-capture.js';\n\n/**\n * The main FloPay SDK instance.\n *\n * Created via `loadFloPay(publishableKey)`. Provides element management,\n * payment confirmation, and session retrieval.\n */\nexport class FloPay {\n private readonly provider: PaymentProviderAdapter;\n private readonly config: FloPayConfig;\n private currentElements: FloPayElements | null = null;\n\n constructor(provider: PaymentProviderAdapter, config: FloPayConfig) {\n this.provider = provider;\n this.config = config;\n }\n\n /**\n * Creates a new `FloPayElements` group for mounting payment fields.\n *\n * Only one elements group is active at a time. Creating a new one\n * destroys the previous group.\n */\n elements(options?: ElementOptions): FloPayElements {\n if (this.currentElements) {\n this.currentElements.destroy();\n }\n this.currentElements = new FloPayElements(this.provider, {\n appearance: this.config.appearance,\n ...options,\n });\n return this.currentElements;\n }\n\n /** Submit elements for validation. */\n async submitElements(): Promise<{ error?: import('@flopay/shared').FloPayError }> {\n return this.provider.submitElements();\n }\n\n /** Create a payment method from the current elements (tokenize card). */\n async createPaymentMethod(billingDetails?: import('@flopay/shared').BillingDetails): Promise<CreatePaymentMethodResult> {\n return this.provider.createPaymentMethod(billingDetails);\n }\n\n /** Confirm a card payment with a known client secret and payment method ID. */\n async confirmCardPayment(params: ConfirmCardPaymentParams): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmCardPayment(params);\n }\n\n /**\n * Create a {@link CardCaptureAdapter} for collecting card details through the\n * backend-rendered hosted vault PCI widget instead of provider-owned (Stripe)\n * card fields (TeamFloPay/backend#823).\n *\n * The returned adapter injects the server-supplied widget HTML (the session's\n * {@link CheckoutSession.vault} block, or one fetched via\n * `PaymentAPI.getVaultCapture`) and relays the widget's terminal outcome. The\n * backend owns tokenization, the PaymentIntent, 3DS, and fulfilment — no\n * Stripe.js is involved on the card path and PCI-sensitive fields never enter\n * the SDK runtime.\n */\n cardCapture(options?: {\n /** Checkout session id to bind the capture to (outcome correlation). */\n sessionId?: string;\n }): CardCaptureAdapter {\n return new PciVaultCardCapture({\n sessionId: options?.sessionId,\n });\n }\n\n /** Confirm a PayPal payment: create intent via billing API → confirm → redirect if needed. */\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n /**\n * Session-bound checkout token. Forwarded as `x-checkout-session-token`\n * on the internal `POST /v1/checkouts/payments/intents` call so post-#640\n * backends don't reject the round-trip with a 401.\n */\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmPayPalPayment(params);\n }\n\n /** Resume a PayPal payment after redirect return. Returns null if no PayPal params in URL. */\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n return this.provider.resumePayPalPayment();\n }\n\n /** Confirms a payment using the mounted elements. */\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n return this.provider.confirmPayment(params);\n }\n\n /**\n * Retrieves a checkout session by ID via the billing API.\n *\n * Returns the normalized `CheckoutSession` with amount, currency,\n * customer data, and status.\n *\n * Requires `billingApiUrl` to be set — either via `loadFloPay(key, { billingApiUrl })`\n * or passed directly as the second argument.\n */\n async retrieveSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<CheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required to retrieve a session.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n const unified = await api.getUnifiedCheckoutSession(sessionId);\n if (!unified.data.session) {\n throw new FloPayError('Session not found', 'api_error');\n }\n return unified.data.session;\n }\n\n /**\n * Retrieves and normalizes a checkout session, including provider-specific\n * data (Stripe clientSecret/publishableKey, Chargebee site, etc.).\n *\n * The billing API URL is resolved from: explicit param → `loadFloPay()` config\n * → `NEXT_PUBLIC_FLOPAY_ENV` env var → `configureFlopay()` → staging fallback.\n */\n async retrieveUnifiedSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<NormalizedCheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n return api.getUnifiedCheckoutSession(sessionId);\n }\n\n /**\n * Returns the raw underlying provider instance (e.g. Stripe object).\n * Used internally by components that need direct provider access,\n * such as PayPal which requires its own Elements instance.\n */\n getRawProvider(): unknown {\n return this.provider.getRawProvider();\n }\n\n /** Tears down the SDK instance and releases resources. */\n destroy(): void {\n this.currentElements?.destroy();\n this.currentElements = null;\n this.provider.destroy();\n }\n}\n","import type {\n PaymentProviderAdapter,\n ElementType,\n ElementOptions,\n MountedElement,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/**\n * Manages the creation and lifecycle of payment elements.\n *\n * Each `FloPayElements` instance is bound to a single provider adapter\n * and tracks all created elements for cleanup.\n */\nexport class FloPayElements {\n private readonly provider: PaymentProviderAdapter;\n private readonly elementMap = new Map<ElementType, MountedElement>();\n private readonly baseOptions: ElementOptions;\n\n constructor(provider: PaymentProviderAdapter, options?: ElementOptions) {\n this.provider = provider;\n this.baseOptions = options ?? {};\n }\n\n /**\n * Creates a new element of the given type.\n * If an element of that type already exists, it is destroyed first.\n */\n async create(\n type: ElementType,\n options?: ElementOptions,\n ): Promise<MountedElement> {\n // Check if the provider already has this element (e.g. after React\n // Strict Mode unmount+remount cycle). Reuse it instead of creating\n // a duplicate, which Stripe would reject.\n const providerExisting = this.provider.getElement(type);\n if (providerExisting) {\n this.elementMap.set(type, providerExisting);\n return providerExisting;\n }\n\n const existing = this.elementMap.get(type);\n if (existing) {\n existing.destroy();\n }\n\n const merged: ElementOptions = { ...this.baseOptions, ...options };\n const element = await this.provider.createElement(type, merged);\n this.elementMap.set(type, element);\n return element;\n }\n\n /** Returns a previously created element, or `null`. */\n getElement(type: ElementType): MountedElement | null {\n return this.elementMap.get(type) ?? null;\n }\n\n /**\n * Submits all mounted elements for validation.\n *\n * Returns an object with an optional error if validation fails.\n * This does NOT confirm the payment — call `floPay.confirmPayment()` for that.\n */\n async submit(): Promise<{ error?: FloPayError }> {\n // Walk through all mounted elements and check for incomplete state.\n // The actual validation is handled by the provider's element implementation.\n // For Stripe, the Elements.submit() method is called during confirmPayment.\n // This method exists to match the Stripe Elements API surface.\n return {};\n }\n\n /** Destroys all created elements and clears the internal map. */\n destroy(): void {\n for (const element of this.elementMap.values()) {\n element.destroy();\n }\n this.elementMap.clear();\n }\n}\n","import type {\n CheckoutGateways,\n CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n CheckoutSessionProduct,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n VaultCaptureBlock,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n FLO_SDK_VERSION_HEADER,\n IDEMPOTENCY_KEY_HEADER,\n IDEMPOTENCY_IN_PROGRESS_CODE,\n buildProductPayload,\n foldIntoProducts,\n resolveIdempotencyKey,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayProduct,\n} from './session-display-cache.js';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API. */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n /** Unified products list returned by the billing API (post-#760). */\n products?: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** 'item' or 'subscription'. */\n type: 'item' | 'subscription';\n code?: string;\n name?: string | null;\n description?: string | null;\n quantity: number;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n /**\n * Pre-discount total in cart-currency major units. Populated by billing\n * API ≥ v1.1.2; absent on older backends.\n */\n subtotalAmount?: number;\n /** Total reduction from applied coupons (cart-currency major units). */\n discountAmount?: number;\n /** Final charge amount after coupon discount (cart-currency major units). */\n totalAmount?: number;\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gateways?: CheckoutGateways;\n /**\n * Generic downstream card-method id for a card already on file\n * (TeamFloPay/backend#823) — present for returning customers so the SDK can\n * skip the vault widget. Absent for first-time buyers.\n */\n providerPaymentMethodId?: string | null;\n accountData: {\n userId: string;\n firstName: string;\n lastName: string;\n email: string;\n gender?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n };\n tagsData: {\n googleContainerId?: string | null;\n sessionId?: string | null;\n testEventCode?: string | null;\n };\n}\n\ntype ApiErrorPayload = Record<string, unknown> | null;\n\nconst DEFAULT_PROCESSING_RETRY_AFTER_MS = 1_000;\nconst MIN_PROCESSING_RETRY_AFTER_MS = 500;\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 15_000;\nconst MAX_PROCESSING_RETRY_AFTER_MS = 3_000;\n// Bounds the pre-pay account-snapshot PATCH so a stalled request can't leave the\n// vault submit gate blocked forever (sdk#124). The whole retry sequence must\n// finish inside this window; on expiry the AbortController fires and the caller\n// falls back to its transient-failure path (release the gate best-effort).\nconst DEFAULT_ACCOUNT_SNAPSHOT_TIMEOUT_MS = 10_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Read an error `message` that may be a `string` or a `string[]`.\n *\n * NestJS class-validator returns `message` as a `string[]` (one entry per\n * failed constraint), e.g. `[\"postcode must be a valid GB postcode\"]`. Read as\n * a plain string that collapses to the generic fallback; here we join the\n * non-empty entries so a country-aware account-snapshot validation error\n * surfaces its real reason instead of \"Failed to persist account snapshot\".\n */\nfunction readMessage(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n if (typeof value === 'string') return value.trim() ? value : undefined;\n if (Array.isArray(value)) {\n const joined = value\n .filter((entry): entry is string => typeof entry === 'string' && entry.trim().length > 0)\n .join('; ');\n return joined || undefined;\n }\n return undefined;\n}\n\nfunction readNumber(payload: ApiErrorPayload, key: string): number | undefined {\n const value = payload?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createCheckoutProcessingTimeoutError(): FloPayError {\n return new FloPayError(\n 'Checkout is still processing. Please try again shortly.',\n 'api_error',\n { code: 'checkout_processing_timeout' },\n );\n}\n\nasync function buildApiErrorFromResponse(\n response: Response,\n fallbackMessage: string,\n): Promise<FloPayError> {\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const nestedError = isRecord(payload?.error) ? payload.error : null;\n const message =\n readMessage(payload, 'message') ??\n readMessage(nestedError, 'message') ??\n fallbackMessage;\n const code =\n readString(payload, 'code') ??\n readString(payload, 'gatewayErrorCode') ??\n readString(nestedError, 'code') ??\n `http_${response.status}`;\n\n return new FloPayError(message, 'api_error', {\n code,\n statusCode: response.status,\n });\n}\n\n/**\n * Extra attempts (beyond the first) for transient network failures.\n * Three total tries: immediate, +150ms, +300ms.\n */\nconst NETWORK_RETRY_ATTEMPTS = 2;\n\n/**\n * Extra attempts (beyond the first) for the backend's documented in-progress\n * replay ({@link IDEMPOTENCY_IN_PROGRESS_CODE}) on checkout-session create.\n * The same stable key is replayed after a short backoff (+150ms, +300ms); a\n * payload-conflict (`409`) and every other error fail fast\n * (TeamFloPay/backend#972).\n */\nconst IDEMPOTENCY_IN_PROGRESS_RETRY_ATTEMPTS = 2;\n\n/**\n * Run `fetch` with a bounded retry on transient network failures.\n *\n * `fetch` rejects — rather than resolving with a non-ok `Response` — only when\n * the request never produced an HTTP response at all: a dropped connection, a\n * DNS hiccup, or a failed CORS preflight, which all surface as\n * `TypeError: Failed to fetch`. Those blips are worth retrying. An `AbortError`\n * (caller-initiated cancellation) is not, and any HTTP-level failure resolves\n * normally and is left for the caller to handle via `response.ok`.\n *\n * Backoff is exponential (150ms, 300ms). Only use this for requests whose\n * replay is harmless — idempotent reads, or session bootstrap where a re-sent\n * create can at worst leave an unused draft session behind.\n */\nasync function fetchWithNetworkRetry(\n input: RequestInfo | URL,\n init?: RequestInit,\n attempts = NETWORK_RETRY_ATTEMPTS,\n): Promise<Response> {\n let lastErr: unknown;\n for (let attempt = 0; ; attempt++) {\n try {\n return await fetch(input, init);\n } catch (err) {\n // Caller-initiated cancellation must propagate immediately, never retry.\n if (err instanceof Error && err.name === 'AbortError') throw err;\n lastErr = err;\n if (attempt >= attempts) throw lastErr;\n await delay(150 * 2 ** attempt);\n }\n }\n}\n\n/**\n * Client-side payment API service.\n *\n * Mirrors the `PaymentAPI` class from the checkout project's\n * `src/service/api.ts`. All methods call the billing API endpoints\n * that the checkout backend exposes.\n */\nexport class PaymentAPI {\n private readonly baseUrl: string;\n\n constructor(billingApiUrl: string) {\n this.baseUrl = billingApiUrl.replace(/\\/+$/, '');\n }\n\n /**\n * Fetch a raw checkout session by ID.\n *\n * `nonce` is the session-bound checkout token returned when the session\n * was created. When supplied it is sent as the `x-checkout-session-token`\n * header that post-#640 backends match against `checkout_session.nonce`\n * before returning the row — the UUID alone is no longer sufficient.\n * Backends that don't yet enforce it ignore the extra header.\n */\n async getCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n // Advertise the SDK version on reads too: the backend only embeds the hosted\n // vault block (`session.vault`) for SDKs declaring `>= 1.3.0`. Without this,\n // a reused/resumed session fetched via GET loses the vault signal and the\n // card path falls back to Stripe (revisit/theme-change regression).\n const headers: Record<string, string> = { [FLO_SDK_VERSION_HEADER]: SDK_VERSION };\n if (nonce) headers['x-checkout-session-token'] = nonce;\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}`,\n { headers },\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n const body = await response.json() as BillingResponse<RawCheckoutSession>;\n return { ...body, data: this.mergeCachedDisplayData(body.data) };\n }\n\n /**\n * Stash display-only data for a session so subsequent fetches can fill in\n * fields the backend no longer persists (`overrideAmount`, `totalAmount`,\n * `providerItemName`, `providerPlanName`).\n *\n * Backed by `sessionStorage` in the browser, with an in-memory fallback in\n * Node/SSR contexts. Default TTL: 1 hour.\n *\n * Server-returned values always win — cached values fill in only where the\n * server returned `null` / `undefined`.\n *\n * @example\n * ```ts\n * paymentAPI.cacheSessionDisplayData(sessionId, {\n * currency: 'USD',\n * items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],\n * });\n * ```\n */\n cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n ): void {\n cacheSessionDisplayData(sessionId, data, options);\n }\n\n /**\n * Drop any cached display data for a session. Call after the payment\n * completes; otherwise the TTL handles cleanup.\n */\n clearSessionDisplayData(sessionId: string): void {\n clearSessionDisplayData(sessionId);\n }\n\n /**\n * Fetch (re-mint) the hosted vault capture widget for a session\n * (TeamFloPay/backend#823).\n *\n * `POST /v1/checkouts/sessions/{id}/vault/capture` returns the SDK-ready\n * {@link VaultCaptureBlock} (`html` + `url`, plus `messageToken` /\n * `expectedOrigin` once the backend mints them). The SDK injects `html` as\n * the card-capture widget. This is the fallback path for sessions that did\n * not receive the embedded `vault` block on create (e.g. a session loaded by\n * id via `GET`, or a pre-1.3.0 create); the endpoint is idempotent and reuses\n * session-cached creds when available.\n *\n * Because the endpoint is idempotent, the request is wrapped in\n * `fetchWithNetworkRetry`: a transient network blip (dropped connection, DNS\n * hiccup, failed CORS preflight) would otherwise leave the secure card form\n * unable to load and hard-block checkout.\n *\n * The PCIVault submit *secret* the backend may include in the response is\n * intentionally **not** read or surfaced — it is server-only and never enters\n * the SDK runtime.\n *\n * `nonce` is forwarded as `x-checkout-session-token` (required by post-#640\n * backends, matched against the session's stored nonce).\n */\n async getVaultCapture(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<VaultCaptureBlock> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (nonce) headers['x-checkout-session-token'] = nonce;\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}/vault/capture`,\n { method: 'POST', headers },\n );\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to load the secure card form');\n }\n const block = await response.json() as VaultCaptureBlock;\n return this.toVaultBlock(block);\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateways` map to enumerate provider-specific data,\n * then wraps the session in a `NormalizedCheckoutSession` for provider-\n * agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId, nonce);\n const normalized = this.normalizeRawSession(res.data);\n // The embedded vault block is a top-level sibling of `data` on the read\n // response (mirrors the create response). Thread it onto the session so a\n // reused/resumed session keeps the hosted card form instead of falling back\n // to Stripe (TeamFloPay/backend#823).\n const vault = (res as { vault?: VaultCaptureBlock }).vault;\n if (vault && normalized.data.session) {\n normalized.data.session.vault = this.toVaultBlock(vault);\n }\n return normalized;\n }\n\n /**\n * Submit a tokenized payment to the billing backend.\n *\n * The backend will either succeed, return `type: '3ds_required'`\n * (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.\n *\n * Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`\n * and forwards `data.nonce` as `x-checkout-session-token`. Backend\n * `TeamFloPay/backend#640` rejects callers without a matching nonce with a\n * 401 — this method throws synchronously when `data.nonce` is missing so the\n * problem surfaces before the network round trip.\n *\n * @param userId Vestigial — backend's GatewayInterceptor routes via session,\n * not headers, so this value is no longer sent on the wire. Kept in the\n * signature for back-compat with existing callers; will be removed in a\n * future major version.\n */\n async processPayment(\n _userId: string,\n data: ProcessPaymentParams,\n options?: {\n pollTimeoutMs?: number;\n },\n ): Promise<Response> {\n if (!data.nonce) {\n throw new FloPayError(\n 'processPayment requires `nonce` — pass the value returned from session creation.',\n 'validation_error',\n { code: 'MissingCheckoutSessionToken', param: 'nonce' },\n );\n }\n\n const { nonce, ...processBody } = data;\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(data.sessionId)}/process`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(processBody),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, { ...options, nonce });\n }\n\n /**\n * Patch the buyer's account snapshot (email, name, billing address, AVS\n * intent) onto a checkout session via\n * `PATCH /v1/checkouts/sessions/{id}/account` (TeamFloPay/backend#823).\n *\n * The vault path's hosted form owns the charge end-to-end so the SDK\n * never calls `/process` on this path; the buyer-typed AVS / billing\n * address would otherwise be lost. The SDK calls this just before\n * submitting the vault widget so the downstream listener mints the\n * Stripe PaymentMethod with the right `billing_details.address` and the\n * per-attempt + per-PM address snapshots are populated.\n *\n * Body shape mirrors the relevant subset of `/process`'s\n * `ProcessCheckoutBodyDto` — same keys, same validators. The endpoint\n * is idempotent: empty/undefined fields are not written, addresses are\n * last-writer-wins, AVS analytics are first-writer-wins.\n *\n * Wrapped in `fetchWithNetworkRetry` because a transient blip on this\n * pre-pay PATCH would silently leave AVS unsent and cause an\n * AVS-protected charge to decline downstream.\n */\n async patchAccountSnapshot(\n sessionId: string,\n nonce: string,\n body: {\n accountData: {\n userId: string;\n email: string;\n firstName?: string | null;\n lastName?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n city?: string | null;\n state?: string | null;\n zip?: string | null;\n country?: string | null;\n gender?: string | null;\n };\n avsCheck?: boolean;\n avsConfig?: Record<string, unknown>;\n },\n options?: { signal?: AbortSignal; timeoutMs?: number },\n ): Promise<void> {\n // Bound the whole retry sequence so a stalled network request can't hang the\n // submit gate indefinitely. Compose the caller's signal (if any) with an\n // internal timeout so either can abort the fetch.\n const timeoutMs = options?.timeoutMs ?? DEFAULT_ACCOUNT_SNAPSHOT_TIMEOUT_MS;\n const controller = new AbortController();\n const onCallerAbort = () => controller.abort();\n if (options?.signal) {\n if (options.signal.aborted) controller.abort();\n else options.signal.addEventListener('abort', onCallerAbort, { once: true });\n }\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n let response: Response;\n try {\n response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(sessionId)}/account`,\n {\n method: 'PATCH',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(body),\n signal: controller.signal,\n },\n );\n } finally {\n clearTimeout(timer);\n options?.signal?.removeEventListener('abort', onCallerAbort);\n }\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to persist account snapshot');\n }\n }\n\n /**\n * Create a PaymentIntent on the backend.\n *\n * Used by the Stripe flow to create a server-side PaymentIntent\n * with the client's payment method attached.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied — the\n * session-bound checkout token returned by session creation. Post-#640\n * backends reject this call with a 401 when the header is missing or does\n * not match the session's stored nonce.\n *\n * `paymentMethodType` is optional for the vault card-capture flow\n * (TeamFloPay/backend#823): the frontend starts checkout *without* an upfront\n * card payment method, so it may be omitted (or `null`). The hosted vault PCI\n * form captures the card afterwards and the backend attaches the resulting\n * payment method to the PaymentIntent it returns here. Legacy callers keep\n * passing the concrete payment method id / type.\n */\n async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType?: string | null,\n options?: { signal?: AbortSignal; isPaypal?: string; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType: paymentMethodType ?? null,\n isPaypal: options?.isPaypal ?? false,\n }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Create a SetupIntent for saving payment methods.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied —\n * required by post-#640 backends, ignored by earlier versions.\n */\n async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ sessionId, email, paymentMethodType }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Fetch user's prior payments by email.\n * Used to determine if saved card UX should be shown.\n */\n async getPaymentsByEmail(\n email: string,\n options?: { signal?: AbortSignal; page?: number; limit?: number },\n ): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }> {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 1;\n const params = new URLSearchParams({\n email,\n page: String(page),\n limit: String(limit),\n sortField: 'createdAt',\n sortDirection: 'DESC',\n });\n\n const response = await fetch(\n `${this.baseUrl}/v1/payments?${params.toString()}`,\n {\n method: 'GET',\n signal: options?.signal,\n keepalive: true,\n },\n );\n\n if (!response.ok) {\n throw new FloPayError('Failed to fetch payments', 'api_error');\n }\n\n return response.json() as Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>;\n }\n\n /**\n * Create a checkout session AND return the full session data in one call.\n * Uses `?expand=true` so the backend returns the complete session\n * instead of just a UUID — eliminating the need for a second GET.\n *\n * Falls back to create + GET if the backend doesn't support `expand`.\n */\n async createAndFetchSession(\n params: InlineSessionDraft,\n ): Promise<NormalizedCheckoutSession> {\n const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n wireProducts,\n );\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n checkoutVersion: SDK_VERSION,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: params.account.userId,\n firstName: params.account.firstName ?? null,\n lastName: params.account.lastName ?? null,\n email: params.account.email,\n country: params.account.country ?? null,\n gender: params.account.gender ?? null,\n city: params.account.city ?? null,\n state: params.account.state ?? null,\n zip: params.account.zip ?? null,\n addressLine1: params.account.addressLine1 ?? null,\n addressLine2: params.account.addressLine2 ?? null,\n },\n couponCodes: params.couponCodes ?? [],\n };\n\n if (params.tokenizedData) payload['tokenizedData'] = params.tokenizedData;\n if (params.tagsData) payload['tagsData'] = params.tagsData;\n if (params.utmMetadata?.length) payload['utmMetadata'] = params.utmMetadata;\n\n // Checkout analytics metadata\n if (params.avsCheck !== undefined) payload['avsCheck'] = params.avsCheck;\n if (params.checkoutType) payload['checkoutType'] = params.checkoutType;\n if (params.checkoutLayout) payload['checkoutLayout'] = params.checkoutLayout;\n if (params.avsConfig) payload['avsConfig'] = params.avsConfig;\n\n // Resolve the stable idempotency key once (an invalid merchant-supplied key\n // throws here, before any request). Because `fetchWithNetworkRetry` reuses\n // this same `headers` object across its transport retries — and the\n // in-progress replay loop below reuses it too — every attempt of this\n // logical create replays the same key and cannot mint a second session\n // (TeamFloPay/backend#972).\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n // Declare the SDK version so backends at TeamFloPay/backend#823 embed\n // the hosted vault capture block (`body.vault`) in the response for\n // SDKs ≥ 1.3.0. Older backends ignore the header.\n [FLO_SDK_VERSION_HEADER]: SDK_VERSION,\n };\n const idempotencyKey = resolveIdempotencyKey(params.idempotencyKey);\n if (idempotencyKey) {\n headers[IDEMPOTENCY_KEY_HEADER] = idempotencyKey;\n }\n\n // On top of `fetchWithNetworkRetry`'s transport retries, retry the backend's\n // documented in-progress replay (a non-OK response carrying\n // `IDEMPOTENCY_IN_PROGRESS_CODE`) with the *same* key after a short backoff.\n // A payload-conflict (`409`) and every other non-OK response fail fast — only\n // the exact same request may safely replay a key (TeamFloPay/backend#972).\n let response: Response;\n for (let attempt = 0; ; attempt++) {\n response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n },\n );\n\n if (response.status === 204) {\n throw new FloPayError(\n 'Session auto-completed — payment method already on file',\n 'api_error',\n { code: 'session_auto_completed' },\n );\n }\n\n if (response.ok) break;\n\n const error = await buildApiErrorFromResponse(response, 'Failed to create checkout session');\n if (\n error.code === IDEMPOTENCY_IN_PROGRESS_CODE &&\n attempt < IDEMPOTENCY_IN_PROGRESS_RETRY_ATTEMPTS\n ) {\n await delay(150 * 2 ** attempt);\n continue;\n }\n throw error;\n }\n\n const body = await response.json() as {\n data: RawCheckoutSession & { uuid?: string };\n autoProcessingError?: CheckoutProcessError;\n autoProcessingAttempted?: boolean;\n autoProcessingPending?: CheckoutProcessingPending;\n vault?: VaultCaptureBlock;\n };\n\n // If backend supports expand=true, it returns full session data.\n // If not, it returns { data: { uuid } } and we fall back to a GET.\n if (body.data && 'gateways' in body.data) {\n // Auto-stash display-only fields the consumer just sent so wallet\n // sheets and the post-redirect fetch see the same totals. Must run\n // BEFORE the merge so the merge picks them up.\n this.autoCacheDisplayData(body.data.uuid, params);\n const merged = this.mergeCachedDisplayData(body.data);\n const normalized = this.normalizeRawSession(merged);\n // Thread the embedded vault capture block (TeamFloPay/backend#823) onto\n // the session so the React card form can inject the hosted widget without\n // a second `/vault/capture` round-trip.\n if (body.vault && normalized.data.session) {\n normalized.data.session.vault = this.toVaultBlock(body.vault);\n }\n return {\n ...normalized,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n // Fallback: backend returned UUID only — fetch full session\n const uuid = body.data?.uuid;\n if (!uuid) {\n throw new FloPayError('No session ID returned', 'api_error');\n }\n this.autoCacheDisplayData(uuid, params);\n const unifiedSession = await this.getUnifiedCheckoutSession(uuid);\n return {\n ...unifiedSession,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n async waitForCheckoutSessionCompletion(\n checkoutSessionId: string,\n options?: {\n initialDelayMs?: number;\n timeoutMs?: number;\n /**\n * Session-bound checkout token; forwarded on the poll's\n * `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.\n */\n nonce?: string;\n },\n ): Promise<NormalizedCheckoutSession> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_PROCESSING_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n let nextDelayMs = this.clampRetryAfterMs(options?.initialDelayMs ?? DEFAULT_PROCESSING_RETRY_AFTER_MS);\n\n while (true) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n if (nextDelayMs > 0) {\n await delay(Math.min(nextDelayMs, remainingMs));\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n }\n\n const session = await this.getUnifiedCheckoutSession(checkoutSessionId, options?.nonce);\n const status = session.data.session?.status;\n\n if (status === 'complete' || status === 'expired') {\n return session;\n }\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n nextDelayMs = this.clampRetryAfterMs(\n Math.max(nextDelayMs * 2, MIN_PROCESSING_RETRY_AFTER_MS),\n );\n }\n }\n\n /** Normalize a raw session into a provider-agnostic shape. */\n private normalizeRawSession(session: RawCheckoutSession): NormalizedCheckoutSession {\n const gateways: CheckoutGateways = session.gateways ?? {};\n const providers: BillingProvider[] = [];\n const data: NormalizedCheckoutSession['data'] = {\n session: this.toCheckoutSession(session),\n };\n\n const stripeGateway = gateways.stripe;\n if (stripeGateway?.publishableKey) {\n providers.push('stripe');\n const rawSession = session as unknown as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n stripeGateway.stripeClientSecret,\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n data.stripe = {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: stripeGateway.publishableKey ?? undefined,\n paypalPublishableKey: stripeGateway.paypalPublishableKey ?? undefined,\n environment: stripeGateway.environment,\n enabledPaymentMethods: Array.isArray(stripeGateway.enabledPaymentMethods)\n ? stripeGateway.enabledPaymentMethods.filter((m): m is string => typeof m === 'string')\n : undefined,\n };\n }\n\n const paypalGateway = gateways.paypal;\n if (paypalGateway?.publishableKey) {\n providers.push('paypal');\n data.paypal = {\n publishableKey: paypalGateway.publishableKey,\n environment: paypalGateway.environment,\n };\n }\n\n return {\n providers,\n mode: 'tokenize',\n data,\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const rawProducts = raw.products ?? [];\n\n // Billing API ≥ v1.1.2 returns the coupon-adjusted `totalAmount` on the\n // session. Prefer it as the source of truth so wallet sheets render the\n // post-coupon amount; fall back to summing per-line `overrideAmount`.\n const hasBackendTotal =\n typeof raw.totalAmount === 'number' && Number.isFinite(raw.totalAmount);\n const computedTotal = rawProducts.reduce(\n (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),\n 0,\n );\n const totalAmount = hasBackendTotal ? (raw.totalAmount as number) : computedTotal;\n\n // Billing API returns amounts in major units (e.g. 24.95 USD).\n // Stripe expects amounts in smallest currency unit (cents).\n const amountInCents = Math.round(totalAmount * 100);\n\n const currency = raw.currency ?? rawProducts[0]?.currency ?? 'USD';\n\n const mode: CheckoutSession['mode'] =\n rawProducts.some((p) => p.type === 'subscription') ? 'subscription' : 'payment';\n\n return {\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode,\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency,\n customer: {\n id: raw.accountData.userId,\n email: raw.accountData.email,\n firstName: raw.accountData.firstName,\n lastName: raw.accountData.lastName,\n country: raw.accountData.country ?? undefined,\n city: raw.accountData.city ?? undefined,\n state: raw.accountData.state ?? undefined,\n zip: raw.accountData.zip ?? undefined,\n gender: raw.accountData.gender ?? undefined,\n line1: raw.accountData.addressLine1 ?? undefined,\n line2: raw.accountData.addressLine2 ?? undefined,\n },\n metadata: {},\n\n checkoutMode: raw.checkoutMode,\n providerPaymentMethodId:\n typeof raw.providerPaymentMethodId === 'string' ? raw.providerPaymentMethodId : null,\n products: rawProducts.map((p): CheckoutSessionProduct => ({\n ...p,\n totalAmount: typeof p.totalAmount === 'number' ? p.totalAmount : undefined,\n overrideAmount: typeof p.overrideAmount === 'number' ? p.overrideAmount : null,\n currency: typeof p.currency === 'string' ? p.currency : undefined,\n metadata: p.metadata ?? null,\n })),\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n subtotalAmount: raw.subtotalAmount,\n discountAmount: raw.discountAmount,\n totalAmount: raw.totalAmount,\n createdAt: raw.createdAt,\n gateways: raw.gateways,\n accountData: raw.accountData,\n tagsData: raw.tagsData,\n };\n }\n\n /**\n * Coerce a raw vault block into a typed {@link VaultCaptureBlock}. The\n * server-only PCIVault submit `secret` is deliberately dropped so it never\n * lands on the public session surface (logs / telemetry / client inspection).\n */\n private toVaultBlock(raw: VaultCaptureBlock): VaultCaptureBlock {\n return {\n html: typeof raw.html === 'string' ? raw.html : undefined,\n url: typeof raw.url === 'string' ? raw.url : undefined,\n messageToken: typeof raw.messageToken === 'string' ? raw.messageToken : undefined,\n expectedOrigin: typeof raw.expectedOrigin === 'string' ? raw.expectedOrigin : undefined,\n };\n }\n\n private toCheckoutSessionStatus(status: RawCheckoutSession['status']): CheckoutSession['status'] {\n if (status === 'completed') {\n return 'complete';\n }\n\n if (status === 'expired') {\n return 'expired';\n }\n\n return 'open';\n }\n\n private async resolveProcessResponse(\n response: Response,\n checkoutSessionId: string,\n options?: {\n pollTimeoutMs?: number;\n nonce?: string;\n },\n ): Promise<Response> {\n if (response.status !== 202) {\n return response;\n }\n\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const pending = this.toCheckoutProcessingPending(payload, response, checkoutSessionId);\n const session = await this.waitForCheckoutSessionCompletion(pending.sessionId, {\n initialDelayMs: pending.retryAfterMs,\n timeoutMs: options?.pollTimeoutMs,\n nonce: options?.nonce,\n });\n\n if (session.data.session?.status === 'complete') {\n return new Response(null, { status: 204, statusText: 'No Content' });\n }\n\n if (session.data.session?.status === 'expired') {\n throw new FloPayError(\n 'Checkout session has expired.',\n 'api_error',\n { code: 'checkout_session_expired' },\n );\n }\n\n throw createCheckoutProcessingTimeoutError();\n }\n\n private toCheckoutProcessingPending(\n payload: ApiErrorPayload,\n response: Response,\n checkoutSessionId: string,\n ): CheckoutProcessingPending {\n const retryAfterHeader = response.headers.get('Retry-After');\n const headerRetryAfterSeconds = retryAfterHeader === null\n || retryAfterHeader.trim() === ''\n ? undefined\n : Number(retryAfterHeader);\n const headerRetryAfterMs = headerRetryAfterSeconds !== undefined\n && Number.isFinite(headerRetryAfterSeconds)\n ? headerRetryAfterSeconds * 1_000\n : undefined;\n\n return {\n type: 'checkout_processing',\n sessionId: readString(payload, 'sessionId') ?? checkoutSessionId,\n retryAfterMs: this.clampRetryAfterMs(\n readNumber(payload, 'retryAfterMs')\n ?? headerRetryAfterMs\n ?? DEFAULT_PROCESSING_RETRY_AFTER_MS,\n ),\n statusUrl: readString(payload, 'statusUrl'),\n sessionUrl: readString(payload, 'sessionUrl'),\n };\n }\n\n private clampRetryAfterMs(retryAfterMs: number): number {\n return Math.max(0, Math.min(retryAfterMs, MAX_PROCESSING_RETRY_AFTER_MS));\n }\n\n /**\n * Stash the display-only fields the consumer passed into a create-session\n * call. Runs after the backend assigns a UUID so a later GET on the same\n * session (typically after a redirect) can fill in fields the backend no\n * longer persists — `overrideAmount`, `totalAmount`, `name`, etc.\n *\n * No-op when no UUID is available.\n */\n private autoCacheDisplayData(\n sessionId: string | undefined,\n params: InlineSessionDraft,\n ): void {\n if (!sessionId) return;\n const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n if (products.length === 0 && !params.currency) {\n return;\n }\n const usingUnifiedProducts = params.products !== undefined;\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n usingUnifiedProducts ? undefined : params.items,\n usingUnifiedProducts ? undefined : params.subscriptions,\n products,\n );\n cacheSessionDisplayData(sessionId, {\n currency: sessionCurrency ?? undefined,\n products: products.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency ?? undefined,\n })),\n });\n }\n\n /**\n * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response. Server values always win — cache fills in\n * only where the server returned `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedProducts = new Map<string, SessionDisplayProduct>();\n // Key on `code` alone: the catalog code is the product's identity (the\n // backend resolves item-vs-subscription from it), so cached display data\n // merges back correctly even when the create-side payload omitted the\n // deprecated `type`.\n const productKey = (code: string | undefined): string | undefined =>\n code ? `code:${code}` : undefined;\n for (const p of cached?.products ?? []) {\n const key = productKey(p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.code);\n const fallback = key ? cachedProducts.get(key) : undefined;\n return {\n ...p,\n name: p.name ?? fallback?.name ?? null,\n totalAmount: p.totalAmount ?? fallback?.totalAmount,\n overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,\n currency: p.currency ?? fallback?.currency,\n };\n });\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n products: mergedProducts,\n };\n }\n}\n","/**\n * Client-side cache for display-only checkout fields the backend no longer\n * persists (`overrideAmount`, `totalAmount`, `providerItemName`,\n * `providerPlanName`, per-line `currency`).\n *\n * The cache lives in `sessionStorage` so it survives the navigation from the\n * page that creates the session to the checkout page that fetches it, but\n * dies on tab close. An in-memory fallback keeps the SDK working in Node /\n * SSR contexts where `sessionStorage` is unavailable.\n *\n * Values from the server response always win — cached values fill in only\n * where the server returned `null` or `undefined`.\n */\n\nconst STORAGE_KEY_PREFIX = 'flopay_session_display:';\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/** Display-only fields per product that can be cached and merged back later. */\nexport interface SessionDisplayProduct {\n /** Catalog code (match key). */\n code?: string;\n /** Whether this product is a one-time item or a recurring subscription. */\n type?: 'item' | 'subscription';\n /** Display-only name for the product. */\n name?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only payload that can be stashed for later merge into a session response. */\nexport interface SessionDisplayCacheData {\n /** Session-level currency (falls into the response only when the server omits it). */\n currency?: string;\n products?: SessionDisplayProduct[];\n}\n\ninterface CachedEntry {\n data: SessionDisplayCacheData;\n expiresAt: number;\n}\n\nconst memoryStore = new Map<string, CachedEntry>();\n\nfunction storageKey(sessionId: string): string {\n return `${STORAGE_KEY_PREFIX}${sessionId}`;\n}\n\nfunction getSessionStorage(): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Stash display-only data for a session. Called client-side right after the\n * server returns a session ID, so the values survive the redirect to the\n * checkout page.\n */\nexport function cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n): void {\n if (!sessionId) return;\n\n const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;\n const entry: CachedEntry = { data, expiresAt: Date.now() + ttl };\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.setItem(storageKey(sessionId), JSON.stringify(entry));\n return;\n } catch {\n // Quota / serialization error — fall through to in-memory store\n }\n }\n\n memoryStore.set(sessionId, entry);\n}\n\n/**\n * Read previously-cached display data for a session, or `null` if nothing\n * is cached (or the TTL has elapsed).\n */\nexport function getSessionDisplayData(\n sessionId: string,\n): SessionDisplayCacheData | null {\n if (!sessionId) return null;\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n const raw = storage.getItem(storageKey(sessionId));\n if (raw) {\n const entry = JSON.parse(raw) as CachedEntry;\n if (entry && typeof entry.expiresAt === 'number' && entry.expiresAt > Date.now()) {\n return entry.data;\n }\n storage.removeItem(storageKey(sessionId));\n }\n } catch {\n // Malformed entry — fall through to memory store\n }\n }\n\n const memEntry = memoryStore.get(sessionId);\n if (memEntry) {\n if (memEntry.expiresAt > Date.now()) {\n return memEntry.data;\n }\n memoryStore.delete(sessionId);\n }\n\n return null;\n}\n\n/**\n * Drop any cached display data for a session. Call from the success page\n * after the payment completes; otherwise the TTL handles cleanup.\n */\nexport function clearSessionDisplayData(sessionId: string): void {\n if (!sessionId) return;\n\n memoryStore.delete(sessionId);\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.removeItem(storageKey(sessionId));\n } catch {\n // ignore\n }\n }\n}\n","import type {\n CardCaptureAdapter,\n CardCaptureEventType,\n CardCaptureMountOptions,\n CardCaptureOutcomeEvent,\n CardCaptureProviderId,\n VaultCaptureResultMessage,\n VaultCardFieldKey,\n VaultCardThemeColors,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n// ---------------------------------------------------------------------------\n// PCIVault hosted-widget capture (Model A — backend-orchestrated)\n//\n// Under TeamFloPay/backend#823 the vault card form is a self-contained,\n// server-rendered widget: the backend returns a `VaultCaptureBlock.html`\n// bundle that owns the PCIVault card fields, its own submit button, card\n// tokenization, PaymentIntent creation + confirmation, 3DS, and decline\n// display. The SDK's only job is to inject that HTML and relay the widget's\n// terminal `postMessage` outcome to the host checkout. No Stripe.js, no\n// PCIVault SDK loading, and no client-side readiness polling happen here —\n// PAN / CVC never enter the SDK runtime.\n//\n// Outcome authenticity: the widget HTML is injected *same-window*, so a genuine\n// outcome's `event.origin` is the merchant page's own origin and the\n// `source: 'flopay-vault'` marker alone is forgeable by any other script on the\n// page (XSS, a rogue dependency, a foreign frame). Before honouring a terminal\n// `complete` / `decline` the listener therefore enforces, when available:\n// • `messageToken` — an unguessable per-session token the backend bakes into\n// the widget bootstrap and echoes back (the only defence against a\n// same-window forger); enforced when the SDK was mounted with one.\n// • `sessionId` — money-affecting outcomes must be bound to the mounted\n// session; an anonymous or mismatched `complete` / `decline` is dropped.\n// • `expectedOrigin` — an optional strict origin gate for widgets that post\n// from a cross-origin frame.\n// Full same-window XSS protection requires the backend to mint+echo\n// `messageToken` (TeamFloPay/backend#823); the enforcement path is in place and\n// activates automatically once the vault block carries the token.\n// ---------------------------------------------------------------------------\n\n/** Configuration for a {@link PciVaultCardCapture} instance. */\nexport interface PciVaultCardCaptureConfig {\n /** Checkout session id bound to the capture (for outcome correlation + trust). */\n sessionId?: string;\n /**\n * Default strict origin for vault `postMessage` outcomes. Overridden by\n * {@link CardCaptureMountOptions.expectedOrigin} when that is supplied at\n * mount. When neither is set the origin gate is skipped (the widget posts\n * same-window in the Model-A flow).\n */\n expectedOrigin?: string;\n}\n\n/** Marker the hosted vault widget stamps on its `postMessage` payloads. */\nconst VAULT_MESSAGE_SOURCE = 'flopay-vault';\n\n/** Best-effort, PII-free Sentry breadcrumb when a global Sentry is present. */\nfunction addBreadcrumb(message: string, data?: Record<string, unknown>): void {\n const sentry = (globalThis as unknown as {\n Sentry?: { addBreadcrumb?: (b: Record<string, unknown>) => void };\n }).Sentry;\n sentry?.addBreadcrumb?.({ category: 'flopay.card-capture', level: 'info', message, data });\n}\n\nfunction isVaultResultMessage(value: unknown): value is VaultCaptureResultMessage {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n record['source'] === VAULT_MESSAGE_SOURCE &&\n (record['type'] === 'ready' ||\n record['type'] === 'submitting' ||\n record['type'] === 'blocked' ||\n record['type'] === 'complete' ||\n record['type'] === 'decline' ||\n record['type'] === 'error' ||\n record['type'] === 'action_required')\n );\n}\n\ninterface VaultResizeMessage {\n source: typeof VAULT_MESSAGE_SOURCE;\n type: 'resize';\n height: number;\n messageToken?: string;\n}\n\ninterface VaultValidationMessage {\n source: typeof VAULT_MESSAGE_SOURCE;\n type: 'validation';\n messages: string[];\n messageToken?: string;\n}\n\nfunction isVaultValidationMessage(value: unknown): value is VaultValidationMessage {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n record['source'] === VAULT_MESSAGE_SOURCE &&\n record['type'] === 'validation' &&\n Array.isArray(record['messages'])\n );\n}\n\nfunction isVaultResizeMessage(value: unknown): value is VaultResizeMessage {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Record<string, unknown>;\n return (\n record['source'] === VAULT_MESSAGE_SOURCE &&\n record['type'] === 'resize' &&\n typeof record['height'] === 'number' &&\n Number.isFinite(record['height'])\n );\n}\n\n/**\n * {@link CardCaptureAdapter} backed by the backend-rendered PCIVault hosted\n * widget.\n *\n * `mount()` injects the server-supplied widget HTML (re-executing its bundled\n * `<script>` so the form bootstraps) and subscribes to the widget's\n * `postMessage` outcome. The backend owns everything else; this adapter never\n * touches the card data, a payment intent, or 3DS.\n */\nexport class PciVaultCardCapture implements CardCaptureAdapter {\n readonly provider: CardCaptureProviderId = 'pcivault';\n\n private readonly config: PciVaultCardCaptureConfig;\n private container: HTMLElement | null = null;\n private messageHandler: ((event: MessageEvent) => void) | null = null;\n /**\n * Parent-page-level overlay rendering the provider's verification challenge\n * (3DS-2 iframe) on `action_required`. Owned by the adapter — not the\n * widget — so it can sit above the host SDK's processing backdrop, which\n * would otherwise visually cover an in-widget challenge iframe.\n */\n private actionOverlay: HTMLElement | null = null;\n /**\n * Listener that catches the `flopay-vault-3ds-return` postMessage from the\n * provider's challenge return page. When the SDK owns the challenge iframe\n * the return page lives inside *that* iframe (not the widget's), so\n * `window.parent` is the host page — the widget's existing message\n * listener can't see it. The SDK forwards completion into the widget via\n * `action_completed` so the widget kicks `/3ds/complete` immediately\n * instead of waiting on the eventual provider webhook.\n */\n private threeDsReturnHandler: ((event: MessageEvent) => void) | null = null;\n /** Per-session integrity token to require on outcomes (from mount options). */\n private messageToken: string | null = null;\n /** Strict origin to require on outcomes, when configured. */\n private expectedOrigin: string | null = null;\n /** Latest merchant theme to push into the (cross-origin) widget. */\n private theme: VaultCardThemeColors | null = null;\n /** Latest host submit-gate state to push into the widget (block its submit). */\n private submitGateBlocked = false;\n /** Latest card-field order + autofocus directive to push into the widget. */\n private cardFieldOrder: VaultCardFieldKey[] | null = null;\n private cardAutoFocus = true;\n private readonly listeners = new Map<\n CardCaptureEventType,\n Set<(event: CardCaptureOutcomeEvent) => void>\n >();\n\n constructor(config: PciVaultCardCaptureConfig = {}) {\n this.config = config;\n }\n\n async mount(container: HTMLElement, options: CardCaptureMountOptions): Promise<void> {\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n throw new FloPayError(\n 'The vault card form is only available in the browser.',\n 'api_error',\n { code: 'card_capture_no_window' },\n );\n }\n if (!options?.html || !options.html.trim()) {\n throw new FloPayError(\n 'No vault capture widget HTML was provided to mount the secure card form.',\n 'api_error',\n { code: 'card_capture_no_widget_html' },\n );\n }\n\n this.container = container;\n this.messageToken = options.messageToken ?? null;\n this.expectedOrigin = options.expectedOrigin ?? this.config.expectedOrigin ?? null;\n this.theme = options.theme ?? null;\n this.attachMessageListener();\n this.injectWidget(container, options.html);\n // Push the initial theme + submit-gate + field order once the widget signals\n // ready (its message listener is attached by then); all run on every `ready`.\n this.postTheme();\n this.postSubmitGate();\n this.postCardFieldOrder();\n addBreadcrumb('vault widget mounted', { sessionId: this.config.sessionId });\n // The hosted widget renders its own fields/submit asynchronously after its\n // script boots; surface `ready` once injection succeeds so the surrounding\n // form can drop any loading affordance.\n this.emit('ready', { sessionId: this.config.sessionId });\n }\n\n on(\n event: CardCaptureEventType,\n handler: (event: CardCaptureOutcomeEvent) => void,\n ): () => void {\n let set = this.listeners.get(event);\n if (!set) {\n set = new Set();\n this.listeners.set(event, set);\n }\n set.add(handler);\n return () => {\n this.listeners.get(event)?.delete(handler);\n };\n }\n\n unmount(): void {\n this.hideActionRequiredOverlay();\n if (this.messageHandler) {\n window.removeEventListener('message', this.messageHandler);\n this.messageHandler = null;\n }\n if (this.container) {\n this.container.replaceChildren();\n this.container = null;\n }\n this.messageToken = null;\n this.expectedOrigin = null;\n // NB: do *not* clear `this.listeners` here. Subscribers own their handlers\n // and remove them via the `off()` returned by `on()`. The card form keeps\n // two independent subscription effects on this adapter — VaultCardFields\n // (ready/error/validation) and the parent card form (submitting/complete/\n // decline/error). A widget remount (e.g. the buttons-layout card expand)\n // unmounts/remounts VaultCardFields, but the parent effect's deps are stable\n // so it does not re-subscribe. Clearing here used to silently drop the\n // parent's listeners → no `submitting` (no AVS red-ring, no account PATCH)\n // on the buttons path. The message listener + container are still torn down.\n }\n\n // ── internals ──\n\n /**\n * Inject the server-rendered widget HTML. `innerHTML` does not execute\n * embedded `<script>` tags, so each script node is replaced with a freshly\n * created element that the browser will load and run (this is what boots the\n * PCIVault form bundle against the `data-flopay-config` container).\n */\n private injectWidget(container: HTMLElement, html: string): void {\n container.innerHTML = html;\n const scripts = Array.from(container.querySelectorAll('script'));\n for (const oldScript of scripts) {\n const script = document.createElement('script');\n for (const attr of Array.from(oldScript.attributes)) {\n script.setAttribute(attr.name, attr.value);\n }\n script.text = oldScript.text;\n oldScript.replaceWith(script);\n }\n }\n\n private attachMessageListener(): void {\n if (this.messageHandler) return;\n const handler = (event: MessageEvent): void => {\n // 1. Optional strict origin gate (for cross-origin-posting widgets).\n if (this.expectedOrigin && event.origin !== this.expectedOrigin) return;\n const data = event.data;\n // Auto-resize: the hosted form reports its content height so the\n // cross-origin iframe (which cannot self-size) grows/shrinks to fit.\n // Validate the widget source + integrity token, then size the iframe.\n if (isVaultResizeMessage(data)) {\n if (this.messageToken && data.messageToken !== this.messageToken) return;\n this.applyHeight(data.height);\n return;\n }\n // Inline field-validation surfaced from the hosted form (live). Forwarded\n // so the host can show it in the error box + onError. Empty `messages`\n // clears it.\n if (isVaultValidationMessage(data)) {\n if (this.messageToken && data.messageToken !== this.messageToken) return;\n const text = data.messages.filter((m) => typeof m === 'string' && m.trim()).join(' ');\n this.emit('validation', { sessionId: this.config.sessionId, message: text || undefined });\n return;\n }\n if (!isVaultResultMessage(data)) return;\n // 2. Integrity token: when the SDK was mounted with one, every outcome\n // must echo it. This is the only defence that survives same-window\n // forgery (the token is not present in the DOM/URL).\n if (this.messageToken && data.messageToken !== this.messageToken) return;\n const boundSession = this.config.sessionId;\n const incomingSession =\n typeof data.sessionId === 'string' ? data.sessionId : undefined;\n // 3. Reject any session-tagged outcome that targets a different session.\n if (boundSession && incomingSession && incomingSession !== boundSession) {\n return;\n }\n // 4. Money-affecting outcomes must be bound to the mounted session — a\n // forged `complete` / `decline` that omits the (server-issued) session\n // id cannot falsely drive merchant-side completion logic.\n if (\n (data.type === 'complete' || data.type === 'decline') &&\n boundSession &&\n incomingSession !== boundSession\n ) {\n return;\n }\n const outcome: CardCaptureOutcomeEvent = {\n sessionId: data.sessionId ?? this.config.sessionId,\n intentId: data.intentId,\n declineReason: data.declineReason,\n message: data.message,\n nextActionRedirectUrl: data.nextActionRedirectUrl,\n };\n addBreadcrumb(`vault widget ${data.type}`, {\n sessionId: outcome.sessionId,\n declineReason: outcome.declineReason,\n });\n // The widget's `ready` confirms its message listener is attached — (re)push\n // the theme + submit-gate so a form that mounted before the host posted\n // still gets them.\n if (data.type === 'ready') {\n this.postTheme();\n this.postSubmitGate();\n this.postCardFieldOrder();\n }\n // Provider needs the buyer to complete a verification challenge (3DS-2).\n // Render the challenge iframe at the PARENT page level — the widget can\n // only mount the challenge inside its own iframe, which gets visually\n // covered by the host's processing backdrop, so the adapter does the\n // rendering up here where it can sit above everything.\n if (data.type === 'action_required' && data.nextActionRedirectUrl) {\n this.showActionRequiredOverlay(data.nextActionRedirectUrl);\n }\n // Terminal outcomes always tear down the challenge overlay (success,\n // decline, or error). A `submitting` follow-up (e.g. the buyer retried\n // after a soft decline) also clears it.\n if (\n data.type === 'complete' ||\n data.type === 'decline' ||\n data.type === 'error' ||\n data.type === 'submitting'\n ) {\n this.hideActionRequiredOverlay();\n }\n this.emit(data.type, outcome);\n };\n this.messageHandler = handler;\n window.addEventListener('message', handler);\n }\n\n /**\n * Push merchant theme colors into the hosted widget (live). The host calls\n * this on a runtime theme switch; the widget applies them to its CSS variables\n * without a remount. Stores the latest theme so `ready` can re-push it.\n */\n applyTheme(theme: VaultCardThemeColors): void {\n this.theme = theme;\n this.postTheme();\n }\n\n /** postMessage the current theme to the widget's (cross-origin) document. */\n private postTheme(): void {\n if (!this.theme || !this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage({ source: 'flopay-vault-host', type: 'theme', theme: this.theme }, '*');\n } catch {\n // Best-effort — theming is cosmetic and must never break the form.\n }\n }\n\n /**\n * Gate the widget's submit from the host. When `blocked`, the widget cancels\n * its next submit and emits `'blocked'` instead of `'submitting'` so the host\n * can validate merchant-DOM fields (AVS) first. Stored so `ready` re-pushes it.\n */\n setSubmitGate(blocked: boolean): void {\n this.submitGateBlocked = blocked;\n this.postSubmitGate();\n }\n\n /** postMessage the current submit-gate state to the widget's document. */\n private postSubmitGate(): void {\n if (!this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage(\n { source: 'flopay-vault-host', type: 'gate', blocked: this.submitGateBlocked },\n '*',\n );\n } catch {\n // Best-effort — the host still validates and surfaces errors on its side.\n }\n }\n\n /**\n * Push the card-field order + autofocus directive into the widget (live). The\n * widget re-sequences its rows (DOM order, so tab order follows) and focuses\n * its first field unless `autoFocus` is false. Stored so `ready` re-pushes it.\n */\n setCardFieldOrder(order: VaultCardFieldKey[] | null, autoFocus: boolean): void {\n this.cardFieldOrder = order;\n this.cardAutoFocus = autoFocus;\n this.postCardFieldOrder();\n }\n\n /** postMessage the current field order + autofocus to the widget's document. */\n private postCardFieldOrder(): void {\n if (!this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage(\n {\n source: 'flopay-vault-host',\n type: 'fieldOrder',\n order: this.cardFieldOrder,\n autoFocus: this.cardAutoFocus,\n },\n '*',\n );\n } catch {\n // Best-effort — field order is cosmetic and must never break the form.\n }\n }\n\n private emit(event: CardCaptureEventType, payload: CardCaptureOutcomeEvent): void {\n for (const handler of this.listeners.get(event) ?? []) {\n handler(payload);\n }\n }\n\n /**\n * Render the provider-hosted verification challenge (e.g. Stripe 3DS-2) in a\n * full-page overlay at the PARENT page level. The widget's inline-iframe\n * approach is unusable because the SDK's processing backdrop sits above the\n * vault iframe, hiding any challenge mounted inside it — by lifting the\n * iframe to the host page the adapter can give it a z-index that wins.\n *\n * The overlay tears down on the next terminal outcome\n * (`complete`/`decline`/`error`) or when the buyer closes it via the backdrop\n * close button. Closing manually is a soft abandon — the next `/status` poll\n * either reveals a real outcome (the challenge completed via the issuer's\n * own redirect to `/vault/3ds/return`, which posts back into the widget) or\n * surfaces `requires_action` again so the host can decide what to do.\n */\n private showActionRequiredOverlay(challengeUrl: string): void {\n if (typeof document === 'undefined') return;\n // Idempotent: re-targeting an existing overlay just navigates the iframe.\n if (this.actionOverlay) {\n const existingIframe = this.actionOverlay.querySelector('iframe');\n if (existingIframe instanceof HTMLIFrameElement) {\n existingIframe.src = challengeUrl;\n }\n return;\n }\n\n const backdrop = document.createElement('div');\n backdrop.setAttribute('data-flopay-action-required', '1');\n backdrop.style.cssText = [\n 'position:fixed',\n 'inset:0',\n // Maximum signed 32-bit z-index; the SDK's own processing backdrop sits\n // well below this so the challenge is visible and interactive.\n 'z-index:2147483647',\n 'background:rgba(15,23,42,0.6)',\n 'display:flex',\n 'align-items:center',\n 'justify-content:center',\n 'padding:16px',\n ].join(';');\n\n const frame = document.createElement('iframe');\n frame.setAttribute('title', 'Card authentication');\n frame.setAttribute('allow', 'payment');\n frame.style.cssText = [\n 'width:min(100%,460px)',\n 'height:min(100%,640px)',\n 'border:0',\n 'border-radius:12px',\n 'background:#fff',\n 'box-shadow:0 12px 30px rgba(0,0,0,0.35)',\n ].join(';');\n frame.src = challengeUrl;\n backdrop.appendChild(frame);\n\n // Listen for the `/vault/3ds/return` postMessage. The return page is\n // loaded inside THIS overlay iframe (not the widget's), so its\n // `window.parent` is the host page, not the widget — the widget's own\n // listener won't catch it. Forward completion into the widget so the\n // widget kicks `/3ds/complete` immediately.\n const returnHandler = (event: MessageEvent): void => {\n // Only the challenge iframe we just mounted may dismiss this overlay. The\n // `/vault/3ds/return` page loads inside `frame`, so its postMessage\n // arrives with `event.source === frame.contentWindow`. Gating on the\n // frame's window stops an unrelated frame on the host page from forging\n // the marker string to trigger a premature dismissal. (Origin can't be\n // matched against `challengeUrl` here — the return page is served by the\n // billing API, a different origin from the provider's challenge.)\n if (event.source !== frame.contentWindow) return;\n const data = event.data;\n if (!data || typeof data !== 'object') return;\n const record = data as Record<string, unknown>;\n if (record['source'] !== 'flopay-vault-3ds-return') return;\n this.hideActionRequiredOverlay();\n this.postActionCompleted(record['status']);\n };\n window.addEventListener('message', returnHandler);\n this.threeDsReturnHandler = returnHandler;\n\n document.body.appendChild(backdrop);\n this.actionOverlay = backdrop;\n addBreadcrumb('vault 3ds challenge overlay shown');\n }\n\n /**\n * Tell the vault widget that the buyer has completed (or abandoned) the\n * challenge. The widget responds by POSTing `/3ds/complete` — its\n * sub-300ms sync resolver writes the follow-up attempt row immediately,\n * so the next `/status` poll resolves to a terminal outcome instead of\n * waiting for the eventual provider webhook.\n */\n private postActionCompleted(status: unknown): void {\n if (!this.container) return;\n const iframe = this.container.querySelector('iframe');\n const target = iframe?.contentWindow;\n if (!target) return;\n try {\n target.postMessage(\n {\n source: 'flopay-vault-host',\n type: 'action_completed',\n status: typeof status === 'string' ? status : 'unknown',\n },\n '*',\n );\n } catch {\n // Best-effort — even if the postMessage fails the widget's poll loop\n // will eventually catch the terminal state from the provider webhook.\n }\n }\n\n private hideActionRequiredOverlay(): void {\n if (this.threeDsReturnHandler) {\n window.removeEventListener('message', this.threeDsReturnHandler);\n this.threeDsReturnHandler = null;\n }\n if (!this.actionOverlay) return;\n this.actionOverlay.parentNode?.removeChild(this.actionOverlay);\n this.actionOverlay = null;\n addBreadcrumb('vault 3ds challenge overlay hidden');\n }\n\n /**\n * Size the hosted-widget iframe to the height reported by the form inside it.\n * Cross-origin iframes don't auto-size to their content, so the widget posts\n * its measured height and we apply it here (clamped to a sane range). This is\n * what lets the card form shrink/grow to fit instead of sitting at a fixed\n * height.\n */\n private applyHeight(height: number): void {\n const iframe = this.container?.querySelector('iframe');\n if (!iframe) return;\n const clamped = Math.max(0, Math.min(Math.ceil(height), 2000));\n iframe.style.height = `${clamped}px`;\n }\n}\n","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\nimport {\n FloPayError,\n IDEMPOTENCY_IN_PROGRESS_CODE,\n IDEMPOTENCY_KEY_HEADER,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveIdempotencyKey,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport { cacheSessionDisplayData } from './session-display-cache.js';\n\n/**\n * Max number of coupon codes the billing API accepts per session create.\n * Mirrors the backend guard introduced in billing v1.1.2 — exceeding this\n * returns the `CouponLimitExceeded` error.\n */\nconst MAX_COUPON_CODES = 5;\n\ninterface ApiErrorBody {\n code?: unknown;\n message?: unknown;\n error?: { code?: unknown; message?: unknown };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Map a billing API error body to a `FloPayError`. Surfaces the coupon\n * validation codes introduced in billing v1.1.2 (`CouponLimitExceeded`,\n * `CouponCurrencyUnsupported`) as structured errors so callers can branch\n * on `error.code` instead of parsing the HTTP status.\n */\nfunction buildCheckoutSessionError(status: number, payload: ApiErrorBody | undefined): FloPayError {\n const nested = payload?.error;\n const code =\n readString(payload?.code)\n ?? readString(nested?.code)\n ?? `http_${status}`;\n const message =\n readString(payload?.message)\n ?? readString(nested?.message)\n ?? defaultMessageForCode(code, status);\n\n return new FloPayError(message, 'api_error', { code, statusCode: status });\n}\n\nfunction defaultMessageForCode(code: string, status: number): string {\n switch (code) {\n case 'CouponLimitExceeded':\n return `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`;\n case 'CouponCurrencyUnsupported':\n return 'One of the applied coupons has no price configured for the cart currency.';\n default:\n return `Failed to create checkout session (HTTP ${status}).`;\n }\n}\n\n/**\n * Creates a checkout session via the billing API and redirects the user\n * to the hosted checkout page.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n *\n * - On 201: Redirects to `{checkoutBaseUrl}/secure?id={uuid}&...redirectParams`\n * - On 204: Redirects directly to `successUrl` (payment method already on file)\n * - On other: Returns `{ status }` without redirecting\n *\n * @example\n * ```ts\n * import { createCheckoutSession } from '@flopay/js';\n *\n * await createCheckoutSession({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * currency: 'USD',\n * items: [{\n * code: 'initial_charge',\n * quantity: 1,\n * metadata: { source: 'web' },\n * }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * redirectParams: { email: 'user@example.com', bg: 'courses', mode: 'confirm' },\n * });\n * ```\n */\nexport async function createCheckoutSession(\n options: CreateSessionParams,\n): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n products,\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n currency,\n utmMetadata,\n idempotencyKey,\n } = options;\n\n // Resolve the stable idempotency key up front — before any HTTP work — so an\n // invalid merchant-supplied key surfaces without issuing a request. A\n // generated key is high-entropy and per-call, so two independent creates with\n // identical bodies remain distinct logical operations (TeamFloPay/backend#972).\n const resolvedIdempotencyKey = resolveIdempotencyKey(idempotencyKey);\n\n if (couponCodes.length > MAX_COUPON_CODES) {\n throw new FloPayError(\n `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`,\n 'validation_error',\n { code: 'CouponLimitExceeded', param: 'couponCodes' },\n );\n }\n\n const wireProducts = products ?? foldIntoProducts(items, subscriptions);\n const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions, wireProducts);\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId,\n checkoutVersion: SDK_VERSION,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n addressLine1: account.addressLine1 ?? null,\n addressLine2: account.addressLine2 ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (resolvedIdempotencyKey) {\n headers[IDEMPOTENCY_KEY_HEADER] = resolvedIdempotencyKey;\n }\n\n let status: number;\n let body: ({ data?: { uuid?: string; nonce?: string } } & ApiErrorBody) | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string; nonce?: string } } & ApiErrorBody;\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status >= 400) {\n throw buildCheckoutSessionError(status, body);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n const nonce = body?.data?.nonce;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n if (!nonce) {\n throw new FloPayError(\n 'Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.',\n 'api_error',\n { code: 'MissingCheckoutSessionToken' },\n );\n }\n\n // Auto-stash display-only fields the consumer passed in so the checkout\n // page (post-redirect) renders the right totals/names even though the\n // backend no longer persists them. Runs before the redirect — values\n // survive the navigation via sessionStorage.\n if (wireProducts.length || sessionCurrency) {\n cacheSessionDisplayData(uuid, {\n currency: sessionCurrency,\n products: wireProducts.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency,\n })),\n });\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n if (setCookie && typeof window !== 'undefined' && typeof document !== 'undefined') {\n const checkoutData = JSON.stringify({ origin_url: cancelUrl });\n const domain = window.location.hostname.split('.').slice(-2).join('.');\n document.cookie = `checkout_data=${encodeURIComponent(checkoutData)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n // Carry the session-bound checkout token to the hosted checkout page so\n // it can echo `x-checkout-session-token` on every continuation call\n // (post-#640 backends require this header to match the session nonce).\n // The URL is deliberately not modified — keeping the nonce out of the\n // location bar avoids history / Referer leakage.\n document.cookie = `flopay_checkout_token=${encodeURIComponent(nonce)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString(), nonce };\n }\n\n if (status === 204) {\n if (typeof window !== 'undefined') {\n window.location.href = successUrl;\n }\n return { status: 204 };\n }\n\n return { status };\n}\n\n/**\n * Creates a checkout session with automatic retry on timeout/abort errors.\n *\n * Uses exponential backoff: 100ms, 200ms, 400ms, etc.\n *\n * The idempotency key (supplied or generated) is resolved **once**, before the\n * retry loop, and reused for every attempt — so a timeout, a lost response, or\n * a documented in-progress reply all replay the *same* key and cannot mint a\n * second checkout session (TeamFloPay/backend#972). A later independent call\n * resolves its own fresh key. A payload-conflict (`409`) is surfaced without\n * retrying, since only the exact same request may safely replay a key.\n */\nexport async function createCheckoutSessionWithRetries(\n options: CreateSessionParams & { maxRetries?: number },\n): Promise<CheckoutSessionResult> {\n const { maxRetries = 3, ...sessionOptions } = options;\n\n if (!Number.isFinite(maxRetries) || !Number.isInteger(maxRetries) || maxRetries <= 0) {\n throw new Error('Number of retries must be greater than 0');\n }\n\n // Resolve the key up front so every attempt below reuses this exact value\n // instead of `createCheckoutSession` generating a new one per attempt.\n const attemptOptions: CreateSessionParams = {\n ...sessionOptions,\n idempotencyKey: resolveIdempotencyKey(sessionOptions.idempotencyKey),\n };\n\n let lastErr: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await createCheckoutSession(attemptOptions);\n } catch (err) {\n lastErr = err;\n\n const isTransportAbort = err instanceof Error && err.name === 'AbortError';\n const isInProgressReplay =\n err instanceof FloPayError && err.code === IDEMPOTENCY_IN_PROGRESS_CODE;\n\n if ((isTransportAbort || isInProgressReplay) && attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastErr ?? new Error('Unknown error during checkout session creation');\n}\n"],"mappings":"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","names":["import_shared","import_shared","import_shared","import_shared","import_shared","import_shared","readString"]}
|