@flopay/js 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -621,6 +621,20 @@ async function buildApiErrorFromResponse(response, fallbackMessage) {
621
621
  statusCode: response.status
622
622
  });
623
623
  }
624
+ var NETWORK_RETRY_ATTEMPTS = 2;
625
+ async function fetchWithNetworkRetry(input, init, attempts = NETWORK_RETRY_ATTEMPTS) {
626
+ let lastErr;
627
+ for (let attempt = 0; ; attempt++) {
628
+ try {
629
+ return await fetch(input, init);
630
+ } catch (err) {
631
+ if (err instanceof Error && err.name === "AbortError") throw err;
632
+ lastErr = err;
633
+ if (attempt >= attempts) throw lastErr;
634
+ await delay(150 * 2 ** attempt);
635
+ }
636
+ }
637
+ }
624
638
  var PaymentAPI = class {
625
639
  constructor(billingApiUrl) {
626
640
  this.baseUrl = billingApiUrl.replace(/\/+$/, "");
@@ -637,7 +651,7 @@ var PaymentAPI = class {
637
651
  * session. Backends that don't yet enforce it ignore the extra header.
638
652
  */
639
653
  async getCheckoutSession(checkoutSessionId, nonce) {
640
- const response = await fetch(
654
+ const response = await fetchWithNetworkRetry(
641
655
  `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,
642
656
  nonce ? { headers: { "x-checkout-session-token": nonce } } : void 0
643
657
  );
@@ -824,7 +838,7 @@ var PaymentAPI = class {
824
838
  if (params.checkoutType) payload["checkoutType"] = params.checkoutType;
825
839
  if (params.checkoutLayout) payload["checkoutLayout"] = params.checkoutLayout;
826
840
  if (params.avsConfig) payload["avsConfig"] = params.avsConfig;
827
- const response = await fetch(
841
+ const response = await fetchWithNetworkRetry(
828
842
  `${this.baseUrl}/v1/checkouts/sessions?expand=true`,
829
843
  {
830
844
  method: "POST",
@@ -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/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 { 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} 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 const { loadStripe } = await import('@stripe/stripe-js');\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 if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\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 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 }): 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 intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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 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';\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 /** 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 }): 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} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\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 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\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 readString(payload, 'message') ??\n readString(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 * 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 — the same token the backend already validates on session-scoped\n * mutations (e.g. the payment-intent-decline endpoint). Sending it on the\n * GET is forward-compatible with TeamFloPay/backend#640, which tightens\n * session reads so the UUID alone is no longer sufficient to fetch a\n * session. 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 const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\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 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 return this.normalizeRawSession(res.data);\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 * @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 const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/process`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, options);\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 async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\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 async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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: 'occurredAt',\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 fetch(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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 };\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 return {\n ...this.normalizeRawSession(merged),\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 ): 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);\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 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 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 },\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 });\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 const productKey = (\n type: SessionDisplayProduct['type'],\n code: string | undefined,\n ): string | undefined => (code && type ? `${type}:${code}` : undefined);\n for (const p of cached?.products ?? []) {\n const key = productKey(p.type, p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.type, 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 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 } } & 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 } } & 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\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\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 }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString() };\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","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/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 { 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} 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 const { loadStripe } = await import('@stripe/stripe-js');\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 if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\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 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 }): 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 intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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 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';\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 /** 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 }): 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} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\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 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\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 readString(payload, 'message') ??\n readString(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 — the same token the backend already validates on session-scoped\n * mutations (e.g. the payment-intent-decline endpoint). Sending it on the\n * GET is forward-compatible with TeamFloPay/backend#640, which tightens\n * session reads so the UUID alone is no longer sufficient to fetch a\n * session. 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 const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\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 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 return this.normalizeRawSession(res.data);\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 * @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 const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/process`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, options);\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 async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\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 async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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: 'occurredAt',\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 headers: { 'Content-Type': 'application/json' },\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 };\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 return {\n ...this.normalizeRawSession(merged),\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 ): 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);\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 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 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 },\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 });\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 const productKey = (\n type: SessionDisplayProduct['type'],\n code: string | undefined,\n ): string | undefined => (code && type ? `${type}:${code}` : undefined);\n for (const p of cached?.products ?? []) {\n const key = productKey(p.type, p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.type, 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 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 } } & 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 } } & 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\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\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 }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString() };\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","readString"]}
package/dist/index.mjs CHANGED
@@ -582,6 +582,20 @@ async function buildApiErrorFromResponse(response, fallbackMessage) {
582
582
  statusCode: response.status
583
583
  });
584
584
  }
585
+ var NETWORK_RETRY_ATTEMPTS = 2;
586
+ async function fetchWithNetworkRetry(input, init, attempts = NETWORK_RETRY_ATTEMPTS) {
587
+ let lastErr;
588
+ for (let attempt = 0; ; attempt++) {
589
+ try {
590
+ return await fetch(input, init);
591
+ } catch (err) {
592
+ if (err instanceof Error && err.name === "AbortError") throw err;
593
+ lastErr = err;
594
+ if (attempt >= attempts) throw lastErr;
595
+ await delay(150 * 2 ** attempt);
596
+ }
597
+ }
598
+ }
585
599
  var PaymentAPI = class {
586
600
  constructor(billingApiUrl) {
587
601
  this.baseUrl = billingApiUrl.replace(/\/+$/, "");
@@ -598,7 +612,7 @@ var PaymentAPI = class {
598
612
  * session. Backends that don't yet enforce it ignore the extra header.
599
613
  */
600
614
  async getCheckoutSession(checkoutSessionId, nonce) {
601
- const response = await fetch(
615
+ const response = await fetchWithNetworkRetry(
602
616
  `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,
603
617
  nonce ? { headers: { "x-checkout-session-token": nonce } } : void 0
604
618
  );
@@ -785,7 +799,7 @@ var PaymentAPI = class {
785
799
  if (params.checkoutType) payload["checkoutType"] = params.checkoutType;
786
800
  if (params.checkoutLayout) payload["checkoutLayout"] = params.checkoutLayout;
787
801
  if (params.avsConfig) payload["avsConfig"] = params.avsConfig;
788
- const response = await fetch(
802
+ const response = await fetchWithNetworkRetry(
789
803
  `${this.baseUrl}/v1/checkouts/sessions?expand=true`,
790
804
  {
791
805
  method: "POST",
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/create-checkout-session.ts"],"sourcesContent":["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} 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 const { loadStripe } = await import('@stripe/stripe-js');\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 if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\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 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 }): 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 intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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 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';\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 /** 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 }): 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} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\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 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\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 readString(payload, 'message') ??\n readString(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 * 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 — the same token the backend already validates on session-scoped\n * mutations (e.g. the payment-intent-decline endpoint). Sending it on the\n * GET is forward-compatible with TeamFloPay/backend#640, which tightens\n * session reads so the UUID alone is no longer sufficient to fetch a\n * session. 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 const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\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 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 return this.normalizeRawSession(res.data);\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 * @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 const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/process`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, options);\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 async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\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 async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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: 'occurredAt',\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 fetch(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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 };\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 return {\n ...this.normalizeRawSession(merged),\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 ): 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);\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 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 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 },\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 });\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 const productKey = (\n type: SessionDisplayProduct['type'],\n code: string | undefined,\n ): string | undefined => (code && type ? `${type}:${code}` : undefined);\n for (const p of cached?.products ?? []) {\n const key = productKey(p.type, p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.type, 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 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 } } & 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 } } & 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\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\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 }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString() };\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":["FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","SDK_VERSION","buildProductPayload","foldIntoProducts","resolveSessionCurrency","readString","FloPayError","foldIntoProducts","resolveSessionCurrency","SDK_VERSION","buildProductPayload"]}
1
+ {"version":3,"sources":["../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/create-checkout-session.ts"],"sourcesContent":["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} 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 const { loadStripe } = await import('@stripe/stripe-js');\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 if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\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 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 }): 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 intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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 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';\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 /** 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 }): 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} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\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 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\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 readString(payload, 'message') ??\n readString(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 — the same token the backend already validates on session-scoped\n * mutations (e.g. the payment-intent-decline endpoint). Sending it on the\n * GET is forward-compatible with TeamFloPay/backend#640, which tightens\n * session reads so the UUID alone is no longer sufficient to fetch a\n * session. 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 const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\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 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 return this.normalizeRawSession(res.data);\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 * @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 const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/process`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, options);\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 async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\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 async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal },\n ): Promise<Response> {\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\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: 'occurredAt',\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 headers: { 'Content-Type': 'application/json' },\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 };\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 return {\n ...this.normalizeRawSession(merged),\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 ): 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);\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 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 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 },\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 });\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 const productKey = (\n type: SessionDisplayProduct['type'],\n code: string | undefined,\n ): string | undefined => (code && type ? `${type}:${code}` : undefined);\n for (const p of cached?.products ?? []) {\n const key = productKey(p.type, p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.type, 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 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 } } & 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 } } & 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\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\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 }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString() };\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":["FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","SDK_VERSION","buildProductPayload","foldIntoProducts","resolveSessionCurrency","readString","FloPayError","foldIntoProducts","resolveSessionCurrency","SDK_VERSION","buildProductPayload"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flopay/js",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/",
@@ -25,7 +25,7 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@flopay/shared": "1.2.0"
28
+ "@flopay/shared": "1.2.1"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@stripe/stripe-js": ">=2.0.0"
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  "devDependencies": {
39
- "@stripe/stripe-js": "^9.5.0",
39
+ "@stripe/stripe-js": "^9.7.0",
40
40
  "typescript": "^6.0.3",
41
41
  "tsup": "^8.3.0",
42
42
  "vitest": "^2.1.0"