@flopay/js 0.5.19 → 1.0.2
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 +176 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +141 -15
- package/dist/index.d.ts +141 -15
- package/dist/index.mjs +183 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/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';\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.\n *\n * Multiple instances can coexist — e.g. the card account and a dedicated\n * PayPal account (`gatewayData.paypalPublishableKey`) during the same\n * checkout session.\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 (used for the dedicated\n * PayPal Stripe account).\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 // Currently only Stripe is supported. The adapter pattern allows\n // adding Chargebee, Recurly, or other providers in the future.\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\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\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 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 (options?.appearance) {\n elementsOptions['appearance'] = {\n theme: (options.appearance.theme as 'stripe') ?? 'stripe',\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\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: (options.appearance.theme as 'stripe') ?? 'stripe',\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.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 CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API (includes gateway field). */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n gateway?: BillingProvider;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n createdAt?: string;\n checkoutUrl?: string;\n items: Array<{\n uuid: string;\n checkoutSessionId: string;\n providerItemId: string;\n providerItemName: string;\n providerItemDescription?: string | null;\n quantity: number;\n totalAmount: number;\n overrideAmount: number | null;\n currency: string;\n metadata?: Record<string, unknown> | null;\n }>;\n subscriptions: Array<{\n uuid: string;\n checkoutSessionId: string;\n providerPlanId: string;\n providerPlanName: string;\n providerPlanDescription?: string | null;\n quantity: number;\n totalAmount: number;\n overrideAmount: number | null;\n currency: string;\n isUpdate?: boolean;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gatewayData: {\n publishableKey?: string | null;\n stripeClientSecret?: string | null;\n paypalPublishableKey?: string | null;\n };\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 /** Fetch a raw checkout session by ID. */\n async getCheckoutSession(\n checkoutSessionId: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n return response.json() as Promise<BillingResponse<RawCheckoutSession>>;\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateway` field to determine the provider,\n * then wraps the session in a `NormalizedCheckoutSession` for\n * provider-agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId);\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 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: {\n 'Content-Type': 'application/json',\n 'x-user-id': userId,\n },\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 payload: Record<string, unknown> = {\n clientId: params.clientId,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n checkoutMode: params.checkoutMode ?? 'full',\n items: (params.items ?? []).map((item) => ({\n providerItemId: item.providerItemId,\n providerItemName: item.providerItemName ?? null,\n quantity: item.quantity ?? 1,\n totalAmount: item.totalAmount,\n overrideAmount: item.overrideAmount ?? null,\n currency: item.currency ?? 'USD',\n })),\n subscriptions: (params.subscriptions ?? []).map((sub) => ({\n providerPlanId: sub.providerPlanId,\n providerPlanName: sub.providerPlanName ?? null,\n quantity: sub.quantity ?? 1,\n totalAmount: sub.totalAmount,\n overrideAmount: sub.overrideAmount ?? null,\n currency: sub.currency ?? 'USD',\n })),\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 && 'gateway' in body.data) {\n // Full session returned — normalize directly\n return {\n ...this.normalizeRawSession(body.data),\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 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 gateway = session.gateway;\n\n if (gateway === 'chargebee') {\n return {\n provider: 'chargebee',\n mode: 'tokenize',\n data: { session: this.toCheckoutSession(session) },\n raw: { data: session },\n };\n }\n\n if (gateway === 'stripe') {\n const rawSession = session as unknown as Record<string, unknown>;\n const gatewayDataRecord = (session.gatewayData ?? {}) as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n gatewayDataRecord['stripeClientSecret'],\n gatewayDataRecord['clientSecret'],\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n return {\n provider: 'stripe',\n mode: 'tokenize',\n data: {\n session: this.toCheckoutSession(session),\n stripe: {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: session.gatewayData?.publishableKey ?? undefined,\n paypalPublishableKey: session.gatewayData?.paypalPublishableKey ?? undefined,\n },\n },\n raw: { data: session },\n };\n }\n\n return {\n provider: 'recurly',\n mode: 'tokenize',\n data: { session: this.toCheckoutSession(session) },\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const totalAmount = [\n ...raw.subscriptions.map((s) => s.overrideAmount || s.totalAmount),\n ...raw.items.map((i) => i.overrideAmount || i.totalAmount),\n ].reduce((sum, val) => sum + val, 0);\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 return {\n // Core fields (backward compat)\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode: raw.subscriptions.length > 0 ? 'subscription' : 'payment',\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency: raw.subscriptions[0]?.currency ?? raw.items[0]?.currency ?? 'USD',\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 // Full session data from billing API\n checkoutMode: raw.checkoutMode,\n items: raw.items,\n subscriptions: raw.subscriptions,\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n createdAt: raw.createdAt,\n gateway: raw.gateway,\n gatewayData: raw.gatewayData,\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","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\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 * items: [{\n * providerItemId: 'prod_abc',\n * providerItemName: 'Pro Plan',\n * totalAmount: 49.99,\n * overrideAmount: 24.99,\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 account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n utmMetadata,\n } = options;\n\n const payload: Record<string, unknown> = {\n clientId,\n successUrl,\n cancelUrl,\n checkoutMode,\n items: items.map((item) => ({\n providerItemId: item.providerItemId,\n providerItemName: item.providerItemName ?? null,\n quantity: item.quantity ?? 1,\n totalAmount: item.totalAmount,\n overrideAmount: item.overrideAmount ?? null,\n currency: item.currency ?? 'USD',\n })),\n subscriptions: subscriptions.map((sub) => ({\n providerPlanId: sub.providerPlanId,\n providerPlanName: sub.providerPlanName ?? null,\n quantity: sub.quantity ?? 1,\n totalAmount: sub.totalAmount,\n overrideAmount: sub.overrideAmount ?? null,\n currency: sub.currency ?? 'USD',\n })),\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 } } | 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 } };\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\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 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"]}
|
|
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 SessionDisplayItem,\n SessionDisplaySubscription,\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.\n *\n * Multiple instances can coexist — e.g. the card account and a dedicated\n * PayPal account (`gatewayData.paypalPublishableKey`) during the same\n * checkout session.\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 (used for the dedicated\n * PayPal Stripe account).\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 // Currently only Stripe is supported. The adapter pattern allows\n // adding Chargebee, Recurly, or other providers in the future.\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\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\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 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 (options?.appearance) {\n elementsOptions['appearance'] = {\n theme: (options.appearance.theme as 'stripe') ?? 'stripe',\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\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: (options.appearance.theme as 'stripe') ?? 'stripe',\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.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 CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n} from '@flopay/shared';\nimport {\n FloPayError,\n buildItemPayload,\n buildSubscriptionPayload,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayItem,\n SessionDisplaySubscription,\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 (includes gateway field). */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n gateway?: BillingProvider;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. Takes precedence over per-item/per-subscription currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n items: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** Preferred catalog code. Falls back to the deprecated `providerItemId`. */\n code?: string;\n /** @deprecated Use `code`. */\n providerItemId?: string;\n /** Display-only name. Preferred over `providerItemName`. */\n itemName?: string | null;\n /** @deprecated Use `itemName`. Mirrored for backward compatibility. */\n providerItemName?: string | null;\n /** Display-only description from the catalog. */\n providerItemDescription?: string | null;\n quantity: number;\n /** @deprecated Removed from the backend — resolved from the catalog. */\n totalAmount?: number;\n /** @deprecated Removed from the backend entirely. */\n overrideAmount?: number | null;\n /** @deprecated Use the session-level `currency`. */\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n subscriptions: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** Preferred catalog code. Falls back to the deprecated `providerPlanId`. */\n code?: string;\n /** @deprecated Use `code`. */\n providerPlanId?: string;\n /** Display-only name. Preferred over `providerPlanName`. */\n subscriptionName?: string | null;\n /** @deprecated Use `subscriptionName`. Mirrored for backward compatibility. */\n providerPlanName?: string | null;\n /** Display-only description from the catalog. */\n providerPlanDescription?: string | null;\n quantity: number;\n /** @deprecated Removed from the backend — resolved from the catalog. */\n totalAmount?: number;\n /** @deprecated Removed from the backend entirely. */\n overrideAmount?: number | null;\n /** @deprecated Use the session-level `currency`. */\n currency?: string;\n /** @deprecated Removed from the backend. Checkouts only create new subscriptions. */\n isUpdate?: boolean;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gatewayData: {\n publishableKey?: string | null;\n stripeClientSecret?: string | null;\n paypalPublishableKey?: string | null;\n };\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 /** Fetch a raw checkout session by ID. */\n async getCheckoutSession(\n checkoutSessionId: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${checkoutSessionId}`,\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 `gateway` field to determine the provider,\n * then wraps the session in a `NormalizedCheckoutSession` for\n * provider-agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId);\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 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: {\n 'Content-Type': 'application/json',\n 'x-user-id': userId,\n },\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 sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n );\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n items: (params.items ?? []).map((item) => buildItemPayload(item, sessionCurrency)),\n subscriptions: (params.subscriptions ?? []).map((sub) => buildSubscriptionPayload(sub, 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 && 'gateway' in body.data) {\n // Full session returned — merge any cached display fields, then normalize\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 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 gateway = session.gateway;\n\n if (gateway === 'chargebee') {\n return {\n provider: 'chargebee',\n mode: 'tokenize',\n data: { session: this.toCheckoutSession(session) },\n raw: { data: session },\n };\n }\n\n if (gateway === 'stripe') {\n const rawSession = session as unknown as Record<string, unknown>;\n const gatewayDataRecord = (session.gatewayData ?? {}) as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n gatewayDataRecord['stripeClientSecret'],\n gatewayDataRecord['clientSecret'],\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n return {\n provider: 'stripe',\n mode: 'tokenize',\n data: {\n session: this.toCheckoutSession(session),\n stripe: {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: session.gatewayData?.publishableKey ?? undefined,\n paypalPublishableKey: session.gatewayData?.paypalPublishableKey ?? undefined,\n },\n },\n raw: { data: session },\n };\n }\n\n return {\n provider: 'recurly',\n mode: 'tokenize',\n data: { session: this.toCheckoutSession(session) },\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const totalAmount = [\n ...raw.subscriptions.map((s) => s.overrideAmount ?? s.totalAmount ?? 0),\n ...raw.items.map((i) => i.overrideAmount ?? i.totalAmount ?? 0),\n ].reduce((sum, val) => sum + val, 0);\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 // Session-level currency takes precedence; fall back to the first\n // per-line-item currency for backward compatibility.\n const currency =\n raw.currency\n ?? raw.subscriptions[0]?.currency\n ?? raw.items[0]?.currency\n ?? 'USD';\n\n return {\n // Core fields (backward compat)\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode: raw.subscriptions.length > 0 ? 'subscription' : 'payment',\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 // Full session data from billing API\n checkoutMode: raw.checkoutMode,\n items: raw.items,\n subscriptions: raw.subscriptions,\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n createdAt: raw.createdAt,\n gateway: raw.gateway,\n gatewayData: raw.gatewayData,\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 * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response and mirror the new/legacy name aliases so\n * readers using either field always get a value when one exists.\n *\n * Server values always win — cache fills in only where the server returned\n * `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedItems = new Map<string, SessionDisplayItem>();\n for (const item of cached?.items ?? []) {\n const key = item.code ?? item.providerItemId;\n if (key) cachedItems.set(key, item);\n }\n\n const cachedSubs = new Map<string, SessionDisplaySubscription>();\n for (const sub of cached?.subscriptions ?? []) {\n const key = sub.code ?? sub.providerPlanId;\n if (key) cachedSubs.set(key, sub);\n }\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n items: raw.items.map((item) => {\n const key = item.code ?? item.providerItemId;\n const fallback = key ? cachedItems.get(key) : undefined;\n // `itemName` and `providerItemName` are aliases — server values win,\n // then cache. Both output fields are mirrored to the resolved value\n // so consumers reading either path always get the same result.\n const resolvedName =\n item.itemName\n ?? item.providerItemName\n ?? fallback?.itemName\n ?? fallback?.providerItemName;\n return {\n ...item,\n itemName: resolvedName,\n providerItemName: resolvedName,\n totalAmount: item.totalAmount ?? fallback?.totalAmount,\n overrideAmount: item.overrideAmount ?? fallback?.overrideAmount,\n currency: item.currency ?? fallback?.currency,\n };\n }),\n subscriptions: raw.subscriptions.map((sub) => {\n const key = sub.code ?? sub.providerPlanId;\n const fallback = key ? cachedSubs.get(key) : undefined;\n const resolvedName =\n sub.subscriptionName\n ?? sub.providerPlanName\n ?? fallback?.subscriptionName\n ?? fallback?.providerPlanName;\n return {\n ...sub,\n subscriptionName: resolvedName,\n providerPlanName: resolvedName,\n totalAmount: sub.totalAmount ?? fallback?.totalAmount,\n overrideAmount: sub.overrideAmount ?? fallback?.overrideAmount,\n currency: sub.currency ?? fallback?.currency,\n };\n }),\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 item that can be cached and merged back later. */\nexport interface SessionDisplayItem {\n /** Catalog code (preferred match key). */\n code?: string;\n /** @deprecated Match key fallback when `code` is not provided. */\n providerItemId?: string;\n /** Display-only name for the item. Takes priority over `providerItemName`. */\n itemName?: string | null;\n /** @deprecated Use `itemName`. */\n providerItemName?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only fields per subscription that can be cached and merged back later. */\nexport interface SessionDisplaySubscription {\n /** Catalog code (preferred match key). */\n code?: string;\n /** @deprecated Match key fallback when `code` is not provided. */\n providerPlanId?: string;\n /** Display-only name for the subscription plan. Takes priority over `providerPlanName`. */\n subscriptionName?: string | null;\n /** @deprecated Use `subscriptionName`. */\n providerPlanName?: 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 items?: SessionDisplayItem[];\n subscriptions?: SessionDisplaySubscription[];\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 buildItemPayload,\n buildSubscriptionPayload,\n resolveSessionCurrency,\n} from '@flopay/shared';\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 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 const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions);\n\n const payload: Record<string, unknown> = {\n clientId,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n items: items.map((item) => buildItemPayload(item, sessionCurrency)),\n subscriptions: subscriptions.map((sub) => buildSubscriptionPayload(sub, 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 } } | 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 } };\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\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 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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -155,6 +155,73 @@ declare class StripeAdapter implements PaymentProviderAdapter {
|
|
|
155
155
|
destroy(): void;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Client-side cache for display-only checkout fields the backend no longer
|
|
160
|
+
* persists (`overrideAmount`, `totalAmount`, `providerItemName`,
|
|
161
|
+
* `providerPlanName`, per-line `currency`).
|
|
162
|
+
*
|
|
163
|
+
* The cache lives in `sessionStorage` so it survives the navigation from the
|
|
164
|
+
* page that creates the session to the checkout page that fetches it, but
|
|
165
|
+
* dies on tab close. An in-memory fallback keeps the SDK working in Node /
|
|
166
|
+
* SSR contexts where `sessionStorage` is unavailable.
|
|
167
|
+
*
|
|
168
|
+
* Values from the server response always win — cached values fill in only
|
|
169
|
+
* where the server returned `null` or `undefined`.
|
|
170
|
+
*/
|
|
171
|
+
/** Display-only fields per item that can be cached and merged back later. */
|
|
172
|
+
interface SessionDisplayItem {
|
|
173
|
+
/** Catalog code (preferred match key). */
|
|
174
|
+
code?: string;
|
|
175
|
+
/** @deprecated Match key fallback when `code` is not provided. */
|
|
176
|
+
providerItemId?: string;
|
|
177
|
+
/** Display-only name for the item. Takes priority over `providerItemName`. */
|
|
178
|
+
itemName?: string | null;
|
|
179
|
+
/** @deprecated Use `itemName`. */
|
|
180
|
+
providerItemName?: string | null;
|
|
181
|
+
totalAmount?: number;
|
|
182
|
+
overrideAmount?: number | null;
|
|
183
|
+
currency?: string;
|
|
184
|
+
}
|
|
185
|
+
/** Display-only fields per subscription that can be cached and merged back later. */
|
|
186
|
+
interface SessionDisplaySubscription {
|
|
187
|
+
/** Catalog code (preferred match key). */
|
|
188
|
+
code?: string;
|
|
189
|
+
/** @deprecated Match key fallback when `code` is not provided. */
|
|
190
|
+
providerPlanId?: string;
|
|
191
|
+
/** Display-only name for the subscription plan. Takes priority over `providerPlanName`. */
|
|
192
|
+
subscriptionName?: string | null;
|
|
193
|
+
/** @deprecated Use `subscriptionName`. */
|
|
194
|
+
providerPlanName?: string | null;
|
|
195
|
+
totalAmount?: number;
|
|
196
|
+
overrideAmount?: number | null;
|
|
197
|
+
currency?: string;
|
|
198
|
+
}
|
|
199
|
+
/** Display-only payload that can be stashed for later merge into a session response. */
|
|
200
|
+
interface SessionDisplayCacheData {
|
|
201
|
+
/** Session-level currency (falls into the response only when the server omits it). */
|
|
202
|
+
currency?: string;
|
|
203
|
+
items?: SessionDisplayItem[];
|
|
204
|
+
subscriptions?: SessionDisplaySubscription[];
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Stash display-only data for a session. Called client-side right after the
|
|
208
|
+
* server returns a session ID, so the values survive the redirect to the
|
|
209
|
+
* checkout page.
|
|
210
|
+
*/
|
|
211
|
+
declare function cacheSessionDisplayData(sessionId: string, data: SessionDisplayCacheData, options?: {
|
|
212
|
+
ttlMs?: number;
|
|
213
|
+
}): void;
|
|
214
|
+
/**
|
|
215
|
+
* Read previously-cached display data for a session, or `null` if nothing
|
|
216
|
+
* is cached (or the TTL has elapsed).
|
|
217
|
+
*/
|
|
218
|
+
declare function getSessionDisplayData(sessionId: string): SessionDisplayCacheData | null;
|
|
219
|
+
/**
|
|
220
|
+
* Drop any cached display data for a session. Call from the success page
|
|
221
|
+
* after the payment completes; otherwise the TTL handles cleanup.
|
|
222
|
+
*/
|
|
223
|
+
declare function clearSessionDisplayData(sessionId: string): void;
|
|
224
|
+
|
|
158
225
|
/** Raw billing API response wrapper. */
|
|
159
226
|
interface BillingResponse<T> {
|
|
160
227
|
data: T;
|
|
@@ -167,30 +234,53 @@ interface RawCheckoutSession {
|
|
|
167
234
|
status: 'pending' | 'completed' | 'expired';
|
|
168
235
|
successUrl: string;
|
|
169
236
|
cancelUrl: string;
|
|
237
|
+
/** Session-level currency. Takes precedence over per-item/per-subscription currency. */
|
|
238
|
+
currency?: string;
|
|
170
239
|
createdAt?: string;
|
|
171
240
|
checkoutUrl?: string;
|
|
172
241
|
items: Array<{
|
|
173
242
|
uuid: string;
|
|
174
243
|
checkoutSessionId: string;
|
|
175
|
-
providerItemId
|
|
176
|
-
|
|
244
|
+
/** Preferred catalog code. Falls back to the deprecated `providerItemId`. */
|
|
245
|
+
code?: string;
|
|
246
|
+
/** @deprecated Use `code`. */
|
|
247
|
+
providerItemId?: string;
|
|
248
|
+
/** Display-only name. Preferred over `providerItemName`. */
|
|
249
|
+
itemName?: string | null;
|
|
250
|
+
/** @deprecated Use `itemName`. Mirrored for backward compatibility. */
|
|
251
|
+
providerItemName?: string | null;
|
|
252
|
+
/** Display-only description from the catalog. */
|
|
177
253
|
providerItemDescription?: string | null;
|
|
178
254
|
quantity: number;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
255
|
+
/** @deprecated Removed from the backend — resolved from the catalog. */
|
|
256
|
+
totalAmount?: number;
|
|
257
|
+
/** @deprecated Removed from the backend entirely. */
|
|
258
|
+
overrideAmount?: number | null;
|
|
259
|
+
/** @deprecated Use the session-level `currency`. */
|
|
260
|
+
currency?: string;
|
|
182
261
|
metadata?: Record<string, unknown> | null;
|
|
183
262
|
}>;
|
|
184
263
|
subscriptions: Array<{
|
|
185
264
|
uuid: string;
|
|
186
265
|
checkoutSessionId: string;
|
|
187
|
-
providerPlanId
|
|
188
|
-
|
|
266
|
+
/** Preferred catalog code. Falls back to the deprecated `providerPlanId`. */
|
|
267
|
+
code?: string;
|
|
268
|
+
/** @deprecated Use `code`. */
|
|
269
|
+
providerPlanId?: string;
|
|
270
|
+
/** Display-only name. Preferred over `providerPlanName`. */
|
|
271
|
+
subscriptionName?: string | null;
|
|
272
|
+
/** @deprecated Use `subscriptionName`. Mirrored for backward compatibility. */
|
|
273
|
+
providerPlanName?: string | null;
|
|
274
|
+
/** Display-only description from the catalog. */
|
|
189
275
|
providerPlanDescription?: string | null;
|
|
190
276
|
quantity: number;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
277
|
+
/** @deprecated Removed from the backend — resolved from the catalog. */
|
|
278
|
+
totalAmount?: number;
|
|
279
|
+
/** @deprecated Removed from the backend entirely. */
|
|
280
|
+
overrideAmount?: number | null;
|
|
281
|
+
/** @deprecated Use the session-level `currency`. */
|
|
282
|
+
currency?: string;
|
|
283
|
+
/** @deprecated Removed from the backend. Checkouts only create new subscriptions. */
|
|
194
284
|
isUpdate?: boolean;
|
|
195
285
|
metadata?: Record<string, unknown> | null;
|
|
196
286
|
}>;
|
|
@@ -232,6 +322,33 @@ declare class PaymentAPI {
|
|
|
232
322
|
constructor(billingApiUrl: string);
|
|
233
323
|
/** Fetch a raw checkout session by ID. */
|
|
234
324
|
getCheckoutSession(checkoutSessionId: string): Promise<BillingResponse<RawCheckoutSession>>;
|
|
325
|
+
/**
|
|
326
|
+
* Stash display-only data for a session so subsequent fetches can fill in
|
|
327
|
+
* fields the backend no longer persists (`overrideAmount`, `totalAmount`,
|
|
328
|
+
* `providerItemName`, `providerPlanName`).
|
|
329
|
+
*
|
|
330
|
+
* Backed by `sessionStorage` in the browser, with an in-memory fallback in
|
|
331
|
+
* Node/SSR contexts. Default TTL: 1 hour.
|
|
332
|
+
*
|
|
333
|
+
* Server-returned values always win — cached values fill in only where the
|
|
334
|
+
* server returned `null` / `undefined`.
|
|
335
|
+
*
|
|
336
|
+
* @example
|
|
337
|
+
* ```ts
|
|
338
|
+
* paymentAPI.cacheSessionDisplayData(sessionId, {
|
|
339
|
+
* currency: 'USD',
|
|
340
|
+
* items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],
|
|
341
|
+
* });
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
cacheSessionDisplayData(sessionId: string, data: SessionDisplayCacheData, options?: {
|
|
345
|
+
ttlMs?: number;
|
|
346
|
+
}): void;
|
|
347
|
+
/**
|
|
348
|
+
* Drop any cached display data for a session. Call after the payment
|
|
349
|
+
* completes; otherwise the TTL handles cleanup.
|
|
350
|
+
*/
|
|
351
|
+
clearSessionDisplayData(sessionId: string): void;
|
|
235
352
|
/**
|
|
236
353
|
* Fetch and normalize a checkout session.
|
|
237
354
|
*
|
|
@@ -301,6 +418,15 @@ declare class PaymentAPI {
|
|
|
301
418
|
private resolveProcessResponse;
|
|
302
419
|
private toCheckoutProcessingPending;
|
|
303
420
|
private clampRetryAfterMs;
|
|
421
|
+
/**
|
|
422
|
+
* Merge cached display-only fields (set by {@link cacheSessionDisplayData})
|
|
423
|
+
* into a raw session response and mirror the new/legacy name aliases so
|
|
424
|
+
* readers using either field always get a value when one exists.
|
|
425
|
+
*
|
|
426
|
+
* Server values always win — cache fills in only where the server returned
|
|
427
|
+
* `null` / `undefined`.
|
|
428
|
+
*/
|
|
429
|
+
private mergeCachedDisplayData;
|
|
304
430
|
}
|
|
305
431
|
|
|
306
432
|
/**
|
|
@@ -321,11 +447,11 @@ declare class PaymentAPI {
|
|
|
321
447
|
* billingApiUrl: 'https://billing.example.com',
|
|
322
448
|
* checkoutBaseUrl: 'https://checkout.example.com',
|
|
323
449
|
* clientId: 'client_123',
|
|
450
|
+
* currency: 'USD',
|
|
324
451
|
* items: [{
|
|
325
|
-
*
|
|
326
|
-
*
|
|
327
|
-
*
|
|
328
|
-
* overrideAmount: 24.99,
|
|
452
|
+
* code: 'initial_charge',
|
|
453
|
+
* quantity: 1,
|
|
454
|
+
* metadata: { source: 'web' },
|
|
329
455
|
* }],
|
|
330
456
|
* account: { userId: 'user_1', email: 'user@example.com' },
|
|
331
457
|
* successUrl: '/success',
|
|
@@ -344,4 +470,4 @@ declare function createCheckoutSessionWithRetries(options: CreateSessionParams &
|
|
|
344
470
|
maxRetries?: number;
|
|
345
471
|
}): Promise<CheckoutSessionResult>;
|
|
346
472
|
|
|
347
|
-
export { FloPay, FloPayElements, PaymentAPI, StripeAdapter, createCheckoutSession, createCheckoutSessionWithRetries, loadFloPay };
|
|
473
|
+
export { FloPay, FloPayElements, PaymentAPI, type SessionDisplayCacheData, type SessionDisplayItem, type SessionDisplaySubscription, StripeAdapter, cacheSessionDisplayData, clearSessionDisplayData, createCheckoutSession, createCheckoutSessionWithRetries, getSessionDisplayData, loadFloPay };
|