@flopay/js 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/index.cjs +13 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +13 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -11
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@ Currently backed by Stripe via the `StripeAdapter`. The adapter pattern (`Paymen
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pnpm add @flopay/js
|
|
10
|
+
pnpm add @flopay/js
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
`@stripe/stripe-js`
|
|
13
|
+
`@stripe/stripe-js` ships as a direct dependency of `@flopay/js` — consumers do not need to install it separately.
|
|
14
14
|
|
|
15
15
|
## Quick Start
|
|
16
16
|
|
package/dist/index.cjs
CHANGED
|
@@ -108,7 +108,16 @@ var StripeAdapter = class {
|
|
|
108
108
|
if (typeof window === "undefined") {
|
|
109
109
|
return;
|
|
110
110
|
}
|
|
111
|
-
|
|
111
|
+
let loadStripe;
|
|
112
|
+
try {
|
|
113
|
+
({ loadStripe } = await import("@stripe/stripe-js"));
|
|
114
|
+
} catch {
|
|
115
|
+
throw new import_shared.FloPayError(
|
|
116
|
+
"Failed to load @stripe/stripe-js. Reinstall @flopay/js.",
|
|
117
|
+
"api_error",
|
|
118
|
+
{ code: "StripeJsLoadFailed" }
|
|
119
|
+
);
|
|
120
|
+
}
|
|
112
121
|
const stripe = await loadStripe(config.publishableKey, {
|
|
113
122
|
locale: config.locale ?? "auto"
|
|
114
123
|
});
|
|
@@ -1118,13 +1127,13 @@ var PaymentAPI = class {
|
|
|
1118
1127
|
mergeCachedDisplayData(raw) {
|
|
1119
1128
|
const cached = getSessionDisplayData(raw.uuid);
|
|
1120
1129
|
const cachedProducts = /* @__PURE__ */ new Map();
|
|
1121
|
-
const productKey = (
|
|
1130
|
+
const productKey = (code) => code ? `code:${code}` : void 0;
|
|
1122
1131
|
for (const p of cached?.products ?? []) {
|
|
1123
|
-
const key = productKey(p.
|
|
1132
|
+
const key = productKey(p.code);
|
|
1124
1133
|
if (key) cachedProducts.set(key, p);
|
|
1125
1134
|
}
|
|
1126
1135
|
const mergedProducts = (raw.products ?? []).map((p) => {
|
|
1127
|
-
const key = productKey(p.
|
|
1136
|
+
const key = productKey(p.code);
|
|
1128
1137
|
const fallback = key ? cachedProducts.get(key) : void 0;
|
|
1129
1138
|
return {
|
|
1130
1139
|
...p,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/create-checkout-session.ts"],"sourcesContent":["export { loadFloPay } from './load.js';\nexport { FloPay } from './flopay.js';\nexport { FloPayElements } from './elements.js';\nexport { StripeAdapter } from './stripe-adapter.js';\nexport { PaymentAPI } from './payment-api.js';\nexport { createCheckoutSession, createCheckoutSessionWithRetries } from './create-checkout-session.js';\nexport {\n cacheSessionDisplayData,\n getSessionDisplayData,\n clearSessionDisplayData,\n} from './session-display-cache.js';\nexport type {\n SessionDisplayProduct,\n SessionDisplayCacheData,\n} from './session-display-cache.js';\n","import type { FloPayConfig } from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\nimport { StripeAdapter } from './stripe-adapter.js';\nimport { FloPay } from './flopay.js';\n\n/**\n * Cache of FloPay instances keyed by publishable key. Multiple instances can\n * coexist when a consumer drives more than one Stripe publishable key during a\n * single page-load.\n */\nconst instanceCache = new Map<string, FloPay>();\n\n/**\n * Loads and initializes the FloPay SDK.\n *\n * This is the primary entry point for consumer applications.\n * It initializes the underlying payment provider (currently Stripe)\n * and returns a ready-to-use `FloPay` instance.\n *\n * Instances are cached per `publishableKey`, so calling `loadFloPay` twice\n * with the same key returns the same instance. Different keys produce\n * independent instances that can run side-by-side.\n *\n * @example\n * ```ts\n * import { loadFloPay } from '@flopay/js';\n *\n * const flopay = await loadFloPay('pk_test_...');\n * const elements = flopay.elements();\n * const cardElement = await elements.create('card');\n * cardElement.mount('#card-container');\n * ```\n */\nexport async function loadFloPay(\n publishableKey: string,\n options?: Omit<FloPayConfig, 'publishableKey'>,\n): Promise<FloPay> {\n if (!publishableKey) {\n throw new FloPayError(\n 'A publishable key is required to initialize FloPay.',\n 'validation_error',\n { param: 'publishableKey' },\n );\n }\n\n const cached = instanceCache.get(publishableKey);\n if (cached) return cached;\n\n const config: FloPayConfig = {\n publishableKey,\n ...options,\n };\n\n // Stripe is the only adapter today. Direct PayPal does not flow through\n // this loader — it renders via the official PayPal JS SDK from\n // `DirectPayPalButton`.\n const adapter = new StripeAdapter();\n await adapter.initialize(config);\n\n const instance = new FloPay(adapter, config);\n instanceCache.set(publishableKey, instance);\n\n return instance;\n}\n","import type {Stripe, StripeElements, StripeElementType} from '@stripe/stripe-js';\nimport type {\n BillingDetails,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n ConfirmPaymentParams,\n CreatePaymentMethodResult,\n ElementOptions,\n ElementType,\n FloPayConfig,\n MountedElement,\n PaymentProviderAdapter,\n PaymentResult,\n} from '@flopay/shared';\nimport {FloPayError} from '@flopay/shared';\n\n/**\n * Maps FloPay element types to Stripe element types.\n * FloPay uses camelCase; Stripe uses lowercase with hyphens or specific names.\n */\nfunction toStripeElementType(type: ElementType): StripeElementType {\n const map: Record<ElementType, string> = {\n payment: 'payment',\n card: 'card',\n cardNumber: 'cardNumber',\n cardExpiry: 'cardExpiry',\n cardCvc: 'cardCvc',\n address: 'address',\n };\n return map[type] as StripeElementType;\n}\n\n/**\n * Wraps a Stripe element instance in the FloPay MountedElement interface.\n *\n * TODO: In a future phase, this wrapper will render the Stripe element inside\n * an iframe hosted on the FloPay domain for PCI DSS SAQ-A compliance.\n * For now, it delegates directly to the Stripe element.\n */\n/** Minimal interface shared by all Stripe element types. */\ninterface StripeElementLike {\n mount(domElement: string | HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update(options?: Record<string, unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler?: (...args: unknown[]) => void): void;\n}\n\n/**\n * Maps a `FloPayAppearance.theme` ('default' | 'flat' | 'night' | 'none') to a\n * Stripe Elements Appearance `theme` ('stripe' | 'flat' | 'night'). Stripe's\n * Appearance API only accepts those three; anything else triggers a console\n * warning and silently falls back. We normalize here so the bundles can keep\n * `'default'` as their public token.\n */\nfunction toStripeAppearanceTheme(\n theme: 'default' | 'flat' | 'night' | 'none' | undefined,\n): 'stripe' | 'night' | 'flat' {\n switch (theme) {\n case 'night': return 'night';\n case 'flat': return 'flat';\n // 'default', 'none', undefined, or any unexpected value → Stripe's baseline.\n default: return 'stripe';\n }\n}\n\nfunction wrapStripeElement(\n stripeElement: StripeElementLike,\n): MountedElement {\n const el = stripeElement;\n\n return {\n mount(container: HTMLElement) {\n el.mount(container);\n },\n unmount() {\n el.unmount();\n },\n update(options: Partial<ElementOptions>) {\n el.update(options as Record<string, unknown>);\n },\n on(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['on']?.(event, handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['off']?.(event, handler);\n },\n destroy() {\n el.destroy();\n },\n };\n}\n\n/**\n * Payment provider adapter backed by Stripe.\n *\n * Implements the `PaymentProviderAdapter` interface so that FloPay consumers\n * interact with a stable API regardless of the upstream provider.\n */\nexport class StripeAdapter implements PaymentProviderAdapter {\n readonly name = 'stripe';\n private stripe: Stripe | null = null;\n private elements: StripeElements | null = null;\n // Serialized appearance currently applied to `this.elements`. Used to detect\n // when consumers swap themes mid-session so we can live-update the Stripe\n // Elements group instead of returning a stale-styled cache. `null` while no\n // elements group exists.\n private appliedAppearanceKey: string | null = null;\n\n async initialize(config: FloPayConfig): Promise<void> {\n // loadStripe only works in the browser — skip initialization during SSR/SSG\n if (typeof window === 'undefined') {\n return;\n }\n\n const { loadStripe } = await import('@stripe/stripe-js');\n const stripe = await loadStripe(config.publishableKey, {\n locale: (config.locale as 'auto') ?? 'auto',\n });\n\n if (!stripe) {\n throw new FloPayError(\n 'Failed to initialize Stripe. Check your publishable key.',\n 'authentication_error',\n );\n }\n\n this.stripe = stripe;\n }\n\n /** Lazily creates the Stripe Elements group for the given options. */\n private getElements(options?: ElementOptions): StripeElements {\n if (!this.stripe) {\n throw new FloPayError(\n 'StripeAdapter not initialized. Call initialize() first.',\n 'api_error',\n );\n }\n\n const stripeAppearance = options?.appearance\n ? {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n }\n : undefined;\n const nextAppearanceKey = stripeAppearance ? JSON.stringify(stripeAppearance) : null;\n\n if (!this.elements) {\n let elementsOptions: Record<string, unknown>;\n\n if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n }\n\n if (stripeAppearance) {\n elementsOptions['appearance'] = stripeAppearance;\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n this.appliedAppearanceKey = nextAppearanceKey;\n } else if (nextAppearanceKey !== this.appliedAppearanceKey) {\n // Elements group already exists but the consumer changed (or removed)\n // the appearance. Stripe Elements supports a live `update({ appearance })`\n // call which re-styles mounted elements in place — no re-create, no\n // input-state loss. When the consumer drops `appearance`, fall through\n // with an empty payload so any prior variables/rules are cleared.\n (this.elements as unknown as { update: (opts: Record<string, unknown>) => void }).update({\n appearance: stripeAppearance ?? {},\n });\n this.appliedAppearanceKey = nextAppearanceKey;\n }\n\n return this.elements;\n }\n\n async createElement(\n type: ElementType,\n options: ElementOptions,\n ): Promise<MountedElement> {\n const elements = this.getElements(options);\n const stripeType = toStripeElementType(type);\n\n const elementOptions: Record<string, unknown> = {};\n if (options.layout) {\n elementOptions['layout'] = options.layout;\n }\n if (options.defaultValues) {\n elementOptions['defaultValues'] = options.defaultValues;\n }\n if (options.readOnly) {\n elementOptions['readOnly'] = options.readOnly;\n }\n if (options.mode) {\n elementOptions['mode'] = options.mode;\n }\n if (options.style) {\n elementOptions['style'] = options.style;\n }\n\n const stripeElement = elements.create(stripeType as 'card', elementOptions) as unknown as StripeElementLike;\n return wrapStripeElement(stripeElement);\n }\n\n getElement(type: ElementType): MountedElement | null {\n if (!this.elements) return null;\n const stripeType = toStripeElementType(type);\n const existing = this.elements.getElement(stripeType as 'card') as unknown as StripeElementLike | null;\n if (!existing) return null;\n return wrapStripeElement(existing);\n }\n\n async submitElements(): Promise<{ error?: FloPayError }> {\n if (!this.stripe || !this.elements) {\n return { error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n const { error } = await this.elements.submit();\n if (error) {\n return {\n error: new FloPayError(error.message ?? 'Validation failed', 'validation_error'),\n };\n }\n return {};\n }\n\n async createPaymentMethod(billingDetails?: BillingDetails): Promise<CreatePaymentMethodResult> {\n if (!this.stripe || !this.elements) {\n return {\n paymentMethodId: null,\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Detect split card fields vs unified PaymentElement.\n // With split fields (cardNumber + cardExpiry + cardCvc), Stripe requires\n // passing the specific cardNumber element with type: 'card'.\n // With unified PaymentElement, pass the elements group.\n const cardNumberEl = this.elements.getElement('cardNumber' as 'card');\n\n // Build billing_details for Stripe AVS (Address Verification).\n // When present, Stripe runs postal code and address checks automatically.\n const stripeBilling = billingDetails ? {\n billing_details: {\n ...(billingDetails.email ? { email: billingDetails.email } : {}),\n ...(billingDetails.name ? { name: billingDetails.name } : {}),\n ...(billingDetails.address ? {\n address: {\n ...(billingDetails.address.country ? { country: billingDetails.address.country } : {}),\n ...(billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {}),\n ...(billingDetails.address.city ? { city: billingDetails.address.city } : {}),\n ...(billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {}),\n ...(billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {}),\n ...(billingDetails.address.state ? { state: billingDetails.address.state } : {}),\n },\n } : {}),\n },\n } : {};\n\n const { error, paymentMethod } = cardNumberEl\n ? await this.stripe.createPaymentMethod({\n type: 'card',\n card: cardNumberEl,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0])\n : await this.stripe.createPaymentMethod({\n elements: this.elements,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0]);\n\n if (error) {\n return {\n paymentMethodId: null,\n error: new FloPayError(\n error.message ?? 'Failed to create payment method',\n 'api_error',\n { code: error.code },\n ),\n };\n }\n\n return { paymentMethodId: paymentMethod.id };\n }\n\n async confirmCardPayment(\n params: ConfirmCardPaymentParams,\n ): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return {\n status: 'failed',\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n const { error, paymentIntent } = await this.stripe.confirmCardPayment(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n { code: error.code, declineCode: error.decline_code },\n ),\n };\n }\n\n return {\n status: (paymentIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: paymentIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent?.payment_method),\n };\n }\n\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n if (!this.stripe || !this.elements) {\n throw new FloPayError(\n 'StripeAdapter not initialized or no elements created.',\n 'api_error',\n );\n }\n\n const billing = params.billingDetails;\n const paymentMethodData = billing ? {\n billing_details: {\n ...(billing.email ? { email: billing.email } : {}),\n ...(billing.name ? { name: billing.name } : {}),\n ...(billing.address ? {\n address: {\n ...(billing.address.country ? { country: billing.address.country } : {}),\n ...(billing.address.postal_code ? { postal_code: billing.address.postal_code } : {}),\n ...(billing.address.city ? { city: billing.address.city } : {}),\n ...(billing.address.line1 ? { line1: billing.address.line1 } : {}),\n ...(billing.address.line2 ? { line2: billing.address.line2 } : {}),\n ...(billing.address.state ? { state: billing.address.state } : {}),\n },\n } : {}),\n },\n } : undefined;\n\n const { error, paymentIntent } = await this.stripe.confirmPayment({\n elements: this.elements,\n clientSecret: params.clientSecret,\n confirmParams: {\n return_url: params.returnUrl ?? window.location.href,\n ...(paymentMethodData ? { payment_method_data: paymentMethodData } : {}),\n },\n redirect: 'if_required',\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n {\n code: error.code,\n declineCode: error.decline_code,\n },\n ),\n };\n }\n\n if (!paymentIntent) {\n return { status: 'failed', error: new FloPayError('No payment intent returned', 'api_error') };\n }\n\n const statusMap: Record<string, PaymentResult['status']> = {\n succeeded: 'succeeded',\n processing: 'processing',\n requires_action: 'requires_action',\n requires_payment_method: 'failed',\n canceled: 'failed',\n };\n\n return {\n status: statusMap[paymentIntent.status] ?? 'failed',\n paymentIntentId: paymentIntent.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent.payment_method),\n };\n }\n\n private extractPaymentMethodId(\n paymentMethod: string | { id?: string | null } | null | undefined,\n ): string | undefined {\n if (typeof paymentMethod === 'string' && paymentMethod.startsWith('pm_')) {\n return paymentMethod;\n }\n if (paymentMethod && typeof paymentMethod === 'object' && typeof paymentMethod.id === 'string') {\n return paymentMethod.id;\n }\n return undefined;\n }\n\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return { status: 'failed', error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n\n const baseUrl = params.billingApiUrl.replace(/\\/+$/, '');\n\n if (this.elements) {\n const { error: submitError } = await this.elements.submit();\n if (submitError) {\n return {\n status: 'failed',\n error: new FloPayError(\n submitError.message ?? 'PayPal payment failed',\n 'validation_error',\n { code: submitError.code },\n ),\n };\n }\n }\n\n const intentHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (params.nonce) intentHeaders['x-checkout-session-token'] = params.nonce;\n\n const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: intentHeaders,\n body: JSON.stringify({\n sessionId: params.sessionId,\n email: params.email,\n paymentMethodType: 'paypal',\n isPaypal: 'true',\n }),\n });\n\n if (!intentResponse.ok) {\n return { status: 'failed', error: new FloPayError('Failed to create PayPal payment intent', 'api_error') };\n }\n\n const intentJson = await intentResponse.json() as { data?: { id?: string } };\n const intentClientSecret = intentJson.data?.id;\n if (!intentClientSecret) {\n return { status: 'failed', error: new FloPayError('No client_secret in response', 'api_error') };\n }\n\n const { error } = await this.stripe.confirmPayment({\n clientSecret: intentClientSecret,\n elements: this.elements ?? undefined,\n confirmParams: { return_url: params.returnUrl },\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'PayPal payment failed', 'api_error', { code: error.code }),\n };\n }\n\n return {\n status: 'processing',\n };\n }\n\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n if (!this.stripe || typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n const paymentIntentId = params.get('payment_intent');\n const clientSecret = params.get('payment_intent_client_secret');\n const redirectStatus = params.get('redirect_status');\n\n if (!paymentIntentId || !clientSecret) return null;\n\n if (redirectStatus === 'failed') {\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was declined. Please try again.', 'api_error'),\n };\n }\n\n const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(clientSecret);\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'Failed to retrieve PayPal payment', 'api_error'),\n };\n }\n\n if (paymentIntent && (paymentIntent.status === 'requires_capture' || paymentIntent.status === 'succeeded')) {\n const pmId = typeof paymentIntent.payment_method === 'string'\n ? paymentIntent.payment_method\n : paymentIntent.payment_method?.id;\n\n // Clean up URL params\n const url = new URL(window.location.href);\n url.searchParams.delete('payment_intent');\n url.searchParams.delete('payment_intent_client_secret');\n url.searchParams.delete('redirect_status');\n window.history.replaceState({}, '', url.toString());\n\n return {\n status: paymentIntent.status as ConfirmCardPaymentResult['status'],\n paymentIntentId: paymentIntent.id,\n paymentMethodId: pmId,\n };\n }\n\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was not completed. Please try again.', 'api_error'),\n };\n }\n\n getRawProvider(): unknown {\n return this.stripe;\n }\n\n createPayPalElements(options: ElementOptions): unknown {\n if (!this.stripe) return null;\n\n const elementsOptions: Record<string, unknown> = {\n mode: 'payment' as const,\n amount: options.amount ?? 0,\n currency: (options.currency ?? 'usd').toLowerCase(),\n captureMethod: 'manual' as const,\n };\n\n if (options.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n\n if (options.appearance) {\n elementsOptions['appearance'] = {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n return this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n }\n\n destroy(): void {\n this.elements = null;\n this.appliedAppearanceKey = null;\n this.stripe = null;\n }\n}\n","import type {\n PaymentProviderAdapter,\n FloPayConfig,\n ElementOptions,\n ConfirmPaymentParams,\n PaymentResult,\n 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 /**\n * Session-bound checkout token. Forwarded as `x-checkout-session-token`\n * on the internal `POST /v1/checkouts/payments/intents` call so post-#640\n * backends don't reject the round-trip with a 401.\n */\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmPayPalPayment(params);\n }\n\n /** Resume a PayPal payment after redirect return. Returns null if no PayPal params in URL. */\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n return this.provider.resumePayPalPayment();\n }\n\n /** Confirms a payment using the mounted elements. */\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n return this.provider.confirmPayment(params);\n }\n\n /**\n * Retrieves a checkout session by ID via the billing API.\n *\n * Returns the normalized `CheckoutSession` with amount, currency,\n * customer data, and status.\n *\n * Requires `billingApiUrl` to be set — either via `loadFloPay(key, { billingApiUrl })`\n * or passed directly as the second argument.\n */\n async retrieveSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<CheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required to retrieve a session.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n const unified = await api.getUnifiedCheckoutSession(sessionId);\n if (!unified.data.session) {\n throw new FloPayError('Session not found', 'api_error');\n }\n return unified.data.session;\n }\n\n /**\n * Retrieves and normalizes a checkout session, including provider-specific\n * data (Stripe clientSecret/publishableKey, Chargebee site, etc.).\n *\n * The billing API URL is resolved from: explicit param → `loadFloPay()` config\n * → `NEXT_PUBLIC_FLOPAY_ENV` env var → `configureFlopay()` → staging fallback.\n */\n async retrieveUnifiedSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<NormalizedCheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n return api.getUnifiedCheckoutSession(sessionId);\n }\n\n /**\n * Returns the raw underlying provider instance (e.g. Stripe object).\n * Used internally by components that need direct provider access,\n * such as PayPal which requires its own Elements instance.\n */\n getRawProvider(): unknown {\n return this.provider.getRawProvider();\n }\n\n /** Tears down the SDK instance and releases resources. */\n destroy(): void {\n this.currentElements?.destroy();\n this.currentElements = null;\n this.provider.destroy();\n }\n}\n","import type {\n PaymentProviderAdapter,\n ElementType,\n ElementOptions,\n MountedElement,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/**\n * Manages the creation and lifecycle of payment elements.\n *\n * Each `FloPayElements` instance is bound to a single provider adapter\n * and tracks all created elements for cleanup.\n */\nexport class FloPayElements {\n private readonly provider: PaymentProviderAdapter;\n private readonly elementMap = new Map<ElementType, MountedElement>();\n private readonly baseOptions: ElementOptions;\n\n constructor(provider: PaymentProviderAdapter, options?: ElementOptions) {\n this.provider = provider;\n this.baseOptions = options ?? {};\n }\n\n /**\n * Creates a new element of the given type.\n * If an element of that type already exists, it is destroyed first.\n */\n async create(\n type: ElementType,\n options?: ElementOptions,\n ): Promise<MountedElement> {\n // Check if the provider already has this element (e.g. after React\n // Strict Mode unmount+remount cycle). Reuse it instead of creating\n // a duplicate, which Stripe would reject.\n const providerExisting = this.provider.getElement(type);\n if (providerExisting) {\n this.elementMap.set(type, providerExisting);\n return providerExisting;\n }\n\n const existing = this.elementMap.get(type);\n if (existing) {\n existing.destroy();\n }\n\n const merged: ElementOptions = { ...this.baseOptions, ...options };\n const element = await this.provider.createElement(type, merged);\n this.elementMap.set(type, element);\n return element;\n }\n\n /** Returns a previously created element, or `null`. */\n getElement(type: ElementType): MountedElement | null {\n return this.elementMap.get(type) ?? null;\n }\n\n /**\n * Submits all mounted elements for validation.\n *\n * Returns an object with an optional error if validation fails.\n * This does NOT confirm the payment — call `floPay.confirmPayment()` for that.\n */\n async submit(): Promise<{ error?: FloPayError }> {\n // Walk through all mounted elements and check for incomplete state.\n // The actual validation is handled by the provider's element implementation.\n // For Stripe, the Elements.submit() method is called during confirmPayment.\n // This method exists to match the Stripe Elements API surface.\n return {};\n }\n\n /** Destroys all created elements and clears the internal map. */\n destroy(): void {\n for (const element of this.elementMap.values()) {\n element.destroy();\n }\n this.elementMap.clear();\n }\n}\n","import type {\n CheckoutGateways,\n CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n CheckoutSessionProduct,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayProduct,\n} from './session-display-cache.js';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API. */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n /** Unified products list returned by the billing API (post-#760). */\n products?: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** 'item' or 'subscription'. */\n type: 'item' | 'subscription';\n code?: string;\n name?: string | null;\n description?: string | null;\n quantity: number;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n /**\n * Pre-discount total in cart-currency major units. Populated by billing\n * API ≥ v1.1.2; absent on older backends.\n */\n subtotalAmount?: number;\n /** Total reduction from applied coupons (cart-currency major units). */\n discountAmount?: number;\n /** Final charge amount after coupon discount (cart-currency major units). */\n totalAmount?: number;\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gateways?: CheckoutGateways;\n accountData: {\n userId: string;\n firstName: string;\n lastName: string;\n email: string;\n gender?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n };\n tagsData: {\n googleContainerId?: string | null;\n sessionId?: string | null;\n testEventCode?: string | null;\n };\n}\n\ntype ApiErrorPayload = Record<string, unknown> | null;\n\nconst DEFAULT_PROCESSING_RETRY_AFTER_MS = 1_000;\nconst MIN_PROCESSING_RETRY_AFTER_MS = 500;\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 15_000;\nconst MAX_PROCESSING_RETRY_AFTER_MS = 3_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nfunction readNumber(payload: ApiErrorPayload, key: string): number | undefined {\n const value = payload?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createCheckoutProcessingTimeoutError(): FloPayError {\n return new FloPayError(\n 'Checkout is still processing. Please try again shortly.',\n 'api_error',\n { code: 'checkout_processing_timeout' },\n );\n}\n\nasync function buildApiErrorFromResponse(\n response: Response,\n fallbackMessage: string,\n): Promise<FloPayError> {\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const nestedError = isRecord(payload?.error) ? payload.error : null;\n const message =\n readString(payload, 'message') ??\n readString(nestedError, 'message') ??\n fallbackMessage;\n const code =\n readString(payload, 'code') ??\n readString(payload, 'gatewayErrorCode') ??\n readString(nestedError, 'code') ??\n `http_${response.status}`;\n\n return new FloPayError(message, 'api_error', {\n code,\n statusCode: response.status,\n });\n}\n\n/**\n * Extra attempts (beyond the first) for transient network failures.\n * Three total tries: immediate, +150ms, +300ms.\n */\nconst NETWORK_RETRY_ATTEMPTS = 2;\n\n/**\n * Run `fetch` with a bounded retry on transient network failures.\n *\n * `fetch` rejects — rather than resolving with a non-ok `Response` — only when\n * the request never produced an HTTP response at all: a dropped connection, a\n * DNS hiccup, or a failed CORS preflight, which all surface as\n * `TypeError: Failed to fetch`. Those blips are worth retrying. An `AbortError`\n * (caller-initiated cancellation) is not, and any HTTP-level failure resolves\n * normally and is left for the caller to handle via `response.ok`.\n *\n * Backoff is exponential (150ms, 300ms). Only use this for requests whose\n * replay is harmless — idempotent reads, or session bootstrap where a re-sent\n * create can at worst leave an unused draft session behind.\n */\nasync function fetchWithNetworkRetry(\n input: RequestInfo | URL,\n init?: RequestInit,\n attempts = NETWORK_RETRY_ATTEMPTS,\n): Promise<Response> {\n let lastErr: unknown;\n for (let attempt = 0; ; attempt++) {\n try {\n return await fetch(input, init);\n } catch (err) {\n // Caller-initiated cancellation must propagate immediately, never retry.\n if (err instanceof Error && err.name === 'AbortError') throw err;\n lastErr = err;\n if (attempt >= attempts) throw lastErr;\n await delay(150 * 2 ** attempt);\n }\n }\n}\n\n/**\n * Client-side payment API service.\n *\n * Mirrors the `PaymentAPI` class from the checkout project's\n * `src/service/api.ts`. All methods call the billing API endpoints\n * that the checkout backend exposes.\n */\nexport class PaymentAPI {\n private readonly baseUrl: string;\n\n constructor(billingApiUrl: string) {\n this.baseUrl = billingApiUrl.replace(/\\/+$/, '');\n }\n\n /**\n * Fetch a raw checkout session by ID.\n *\n * `nonce` is the session-bound checkout token returned when the session\n * was created. When supplied it is sent as the `x-checkout-session-token`\n * header that post-#640 backends match against `checkout_session.nonce`\n * before returning the row — the UUID alone is no longer sufficient.\n * Backends that don't yet enforce it ignore the extra header.\n */\n async getCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n const body = await response.json() as BillingResponse<RawCheckoutSession>;\n return { ...body, data: this.mergeCachedDisplayData(body.data) };\n }\n\n /**\n * Stash display-only data for a session so subsequent fetches can fill in\n * fields the backend no longer persists (`overrideAmount`, `totalAmount`,\n * `providerItemName`, `providerPlanName`).\n *\n * Backed by `sessionStorage` in the browser, with an in-memory fallback in\n * Node/SSR contexts. Default TTL: 1 hour.\n *\n * Server-returned values always win — cached values fill in only where the\n * server returned `null` / `undefined`.\n *\n * @example\n * ```ts\n * paymentAPI.cacheSessionDisplayData(sessionId, {\n * currency: 'USD',\n * items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],\n * });\n * ```\n */\n cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n ): void {\n cacheSessionDisplayData(sessionId, data, options);\n }\n\n /**\n * Drop any cached display data for a session. Call after the payment\n * completes; otherwise the TTL handles cleanup.\n */\n clearSessionDisplayData(sessionId: string): void {\n clearSessionDisplayData(sessionId);\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateways` map to enumerate provider-specific data,\n * then wraps the session in a `NormalizedCheckoutSession` for provider-\n * agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId, nonce);\n return this.normalizeRawSession(res.data);\n }\n\n /**\n * Submit a tokenized payment to the billing backend.\n *\n * The backend will either succeed, return `type: '3ds_required'`\n * (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.\n *\n * Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`\n * and forwards `data.nonce` as `x-checkout-session-token`. Backend\n * `TeamFloPay/backend#640` rejects callers without a matching nonce with a\n * 401 — this method throws synchronously when `data.nonce` is missing so the\n * problem surfaces before the network round trip.\n *\n * @param userId Vestigial — backend's GatewayInterceptor routes via session,\n * not headers, so this value is no longer sent on the wire. Kept in the\n * signature for back-compat with existing callers; will be removed in a\n * future major version.\n */\n async processPayment(\n _userId: string,\n data: ProcessPaymentParams,\n options?: {\n pollTimeoutMs?: number;\n },\n ): Promise<Response> {\n if (!data.nonce) {\n throw new FloPayError(\n 'processPayment requires `nonce` — pass the value returned from session creation.',\n 'validation_error',\n { code: 'MissingCheckoutSessionToken', param: 'nonce' },\n );\n }\n\n const { nonce, ...processBody } = data;\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(data.sessionId)}/process`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(processBody),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, { ...options, nonce });\n }\n\n /**\n * Create a PaymentIntent on the backend.\n *\n * Used by the Stripe flow to create a server-side PaymentIntent\n * with the client's payment method attached.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied — the\n * session-bound checkout token returned by session creation. Post-#640\n * backends reject this call with a 401 when the header is missing or does\n * not match the session's stored nonce.\n */\n async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\n isPaypal: options?.isPaypal ?? false,\n }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Create a SetupIntent for saving payment methods.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied —\n * required by post-#640 backends, ignored by earlier versions.\n */\n async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ sessionId, email, paymentMethodType }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Fetch user's prior payments by email.\n * Used to determine if saved card UX should be shown.\n */\n async getPaymentsByEmail(\n email: string,\n options?: { signal?: AbortSignal; page?: number; limit?: number },\n ): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }> {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 1;\n const params = new URLSearchParams({\n email,\n page: String(page),\n limit: String(limit),\n sortField: 'occurredAt',\n sortDirection: 'DESC',\n });\n\n const response = await fetch(\n `${this.baseUrl}/v1/payments?${params.toString()}`,\n {\n method: 'GET',\n signal: options?.signal,\n keepalive: true,\n },\n );\n\n if (!response.ok) {\n throw new FloPayError('Failed to fetch payments', 'api_error');\n }\n\n return response.json() as Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>;\n }\n\n /**\n * Create a checkout session AND return the full session data in one call.\n * Uses `?expand=true` so the backend returns the complete session\n * instead of just a UUID — eliminating the need for a second GET.\n *\n * Falls back to create + GET if the backend doesn't support `expand`.\n */\n async createAndFetchSession(\n params: InlineSessionDraft,\n ): Promise<NormalizedCheckoutSession> {\n const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n wireProducts,\n );\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n checkoutVersion: SDK_VERSION,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: params.account.userId,\n firstName: params.account.firstName ?? null,\n lastName: params.account.lastName ?? null,\n email: params.account.email,\n country: params.account.country ?? null,\n gender: params.account.gender ?? null,\n city: params.account.city ?? null,\n state: params.account.state ?? null,\n zip: params.account.zip ?? null,\n addressLine1: params.account.addressLine1 ?? null,\n addressLine2: params.account.addressLine2 ?? null,\n },\n couponCodes: params.couponCodes ?? [],\n };\n\n if (params.tokenizedData) payload['tokenizedData'] = params.tokenizedData;\n if (params.tagsData) payload['tagsData'] = params.tagsData;\n if (params.utmMetadata?.length) payload['utmMetadata'] = params.utmMetadata;\n\n // Checkout analytics metadata\n if (params.avsCheck !== undefined) payload['avsCheck'] = params.avsCheck;\n if (params.checkoutType) payload['checkoutType'] = params.checkoutType;\n if (params.checkoutLayout) payload['checkoutLayout'] = params.checkoutLayout;\n if (params.avsConfig) payload['avsConfig'] = params.avsConfig;\n\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n );\n\n if (response.status === 204) {\n throw new FloPayError(\n 'Session auto-completed — payment method already on file',\n 'api_error',\n { code: 'session_auto_completed' },\n );\n }\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to create checkout session');\n }\n\n const body = await response.json() as {\n data: RawCheckoutSession & { uuid?: string };\n autoProcessingError?: CheckoutProcessError;\n autoProcessingAttempted?: boolean;\n autoProcessingPending?: CheckoutProcessingPending;\n };\n\n // If backend supports expand=true, it returns full session data.\n // If not, it returns { data: { uuid } } and we fall back to a GET.\n if (body.data && 'gateways' in body.data) {\n // Auto-stash display-only fields the consumer just sent so wallet\n // sheets and the post-redirect fetch see the same totals. Must run\n // BEFORE the merge so the merge picks them up.\n this.autoCacheDisplayData(body.data.uuid, params);\n const merged = this.mergeCachedDisplayData(body.data);\n return {\n ...this.normalizeRawSession(merged),\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n // Fallback: backend returned UUID only — fetch full session\n const uuid = body.data?.uuid;\n if (!uuid) {\n throw new FloPayError('No session ID returned', 'api_error');\n }\n this.autoCacheDisplayData(uuid, params);\n const unifiedSession = await this.getUnifiedCheckoutSession(uuid);\n return {\n ...unifiedSession,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n async waitForCheckoutSessionCompletion(\n checkoutSessionId: string,\n options?: {\n initialDelayMs?: number;\n timeoutMs?: number;\n /**\n * Session-bound checkout token; forwarded on the poll's\n * `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.\n */\n nonce?: string;\n },\n ): Promise<NormalizedCheckoutSession> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_PROCESSING_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n let nextDelayMs = this.clampRetryAfterMs(options?.initialDelayMs ?? DEFAULT_PROCESSING_RETRY_AFTER_MS);\n\n while (true) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n if (nextDelayMs > 0) {\n await delay(Math.min(nextDelayMs, remainingMs));\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n }\n\n const session = await this.getUnifiedCheckoutSession(checkoutSessionId, options?.nonce);\n const status = session.data.session?.status;\n\n if (status === 'complete' || status === 'expired') {\n return session;\n }\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n nextDelayMs = this.clampRetryAfterMs(\n Math.max(nextDelayMs * 2, MIN_PROCESSING_RETRY_AFTER_MS),\n );\n }\n }\n\n /** Normalize a raw session into a provider-agnostic shape. */\n private normalizeRawSession(session: RawCheckoutSession): NormalizedCheckoutSession {\n const gateways: CheckoutGateways = session.gateways ?? {};\n const providers: BillingProvider[] = [];\n const data: NormalizedCheckoutSession['data'] = {\n session: this.toCheckoutSession(session),\n };\n\n const stripeGateway = gateways.stripe;\n if (stripeGateway?.publishableKey) {\n providers.push('stripe');\n const rawSession = session as unknown as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n stripeGateway.stripeClientSecret,\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n data.stripe = {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: stripeGateway.publishableKey ?? undefined,\n paypalPublishableKey: stripeGateway.paypalPublishableKey ?? undefined,\n environment: stripeGateway.environment,\n enabledPaymentMethods: Array.isArray(stripeGateway.enabledPaymentMethods)\n ? stripeGateway.enabledPaymentMethods.filter((m): m is string => typeof m === 'string')\n : undefined,\n };\n }\n\n const paypalGateway = gateways.paypal;\n if (paypalGateway?.publishableKey) {\n providers.push('paypal');\n data.paypal = {\n publishableKey: paypalGateway.publishableKey,\n environment: paypalGateway.environment,\n };\n }\n\n return {\n providers,\n mode: 'tokenize',\n data,\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const rawProducts = raw.products ?? [];\n\n // Billing API ≥ v1.1.2 returns the coupon-adjusted `totalAmount` on the\n // session. Prefer it as the source of truth so wallet sheets render the\n // post-coupon amount; fall back to summing per-line `overrideAmount`.\n const hasBackendTotal =\n typeof raw.totalAmount === 'number' && Number.isFinite(raw.totalAmount);\n const computedTotal = rawProducts.reduce(\n (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),\n 0,\n );\n const totalAmount = hasBackendTotal ? (raw.totalAmount as number) : computedTotal;\n\n // Billing API returns amounts in major units (e.g. 24.95 USD).\n // Stripe expects amounts in smallest currency unit (cents).\n const amountInCents = Math.round(totalAmount * 100);\n\n const currency = raw.currency ?? rawProducts[0]?.currency ?? 'USD';\n\n const mode: CheckoutSession['mode'] =\n rawProducts.some((p) => p.type === 'subscription') ? 'subscription' : 'payment';\n\n return {\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode,\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency,\n customer: {\n id: raw.accountData.userId,\n email: raw.accountData.email,\n firstName: raw.accountData.firstName,\n lastName: raw.accountData.lastName,\n country: raw.accountData.country ?? undefined,\n city: raw.accountData.city ?? undefined,\n state: raw.accountData.state ?? undefined,\n zip: raw.accountData.zip ?? undefined,\n gender: raw.accountData.gender ?? undefined,\n line1: raw.accountData.addressLine1 ?? undefined,\n line2: raw.accountData.addressLine2 ?? undefined,\n },\n metadata: {},\n\n checkoutMode: raw.checkoutMode,\n products: rawProducts.map((p): CheckoutSessionProduct => ({\n ...p,\n totalAmount: typeof p.totalAmount === 'number' ? p.totalAmount : undefined,\n overrideAmount: typeof p.overrideAmount === 'number' ? p.overrideAmount : null,\n currency: typeof p.currency === 'string' ? p.currency : undefined,\n metadata: p.metadata ?? null,\n })),\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n subtotalAmount: raw.subtotalAmount,\n discountAmount: raw.discountAmount,\n totalAmount: raw.totalAmount,\n createdAt: raw.createdAt,\n gateways: raw.gateways,\n accountData: raw.accountData,\n tagsData: raw.tagsData,\n };\n }\n\n private toCheckoutSessionStatus(status: RawCheckoutSession['status']): CheckoutSession['status'] {\n if (status === 'completed') {\n return 'complete';\n }\n\n if (status === 'expired') {\n return 'expired';\n }\n\n return 'open';\n }\n\n private async resolveProcessResponse(\n response: Response,\n checkoutSessionId: string,\n options?: {\n pollTimeoutMs?: number;\n nonce?: string;\n },\n ): Promise<Response> {\n if (response.status !== 202) {\n return response;\n }\n\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const pending = this.toCheckoutProcessingPending(payload, response, checkoutSessionId);\n const session = await this.waitForCheckoutSessionCompletion(pending.sessionId, {\n initialDelayMs: pending.retryAfterMs,\n timeoutMs: options?.pollTimeoutMs,\n nonce: options?.nonce,\n });\n\n if (session.data.session?.status === 'complete') {\n return new Response(null, { status: 204, statusText: 'No Content' });\n }\n\n if (session.data.session?.status === 'expired') {\n throw new FloPayError(\n 'Checkout session has expired.',\n 'api_error',\n { code: 'checkout_session_expired' },\n );\n }\n\n throw createCheckoutProcessingTimeoutError();\n }\n\n private toCheckoutProcessingPending(\n payload: ApiErrorPayload,\n response: Response,\n checkoutSessionId: string,\n ): CheckoutProcessingPending {\n const retryAfterHeader = response.headers.get('Retry-After');\n const headerRetryAfterSeconds = retryAfterHeader === null\n || retryAfterHeader.trim() === ''\n ? undefined\n : Number(retryAfterHeader);\n const headerRetryAfterMs = headerRetryAfterSeconds !== undefined\n && Number.isFinite(headerRetryAfterSeconds)\n ? headerRetryAfterSeconds * 1_000\n : undefined;\n\n return {\n type: 'checkout_processing',\n sessionId: readString(payload, 'sessionId') ?? checkoutSessionId,\n retryAfterMs: this.clampRetryAfterMs(\n readNumber(payload, 'retryAfterMs')\n ?? headerRetryAfterMs\n ?? DEFAULT_PROCESSING_RETRY_AFTER_MS,\n ),\n statusUrl: readString(payload, 'statusUrl'),\n sessionUrl: readString(payload, 'sessionUrl'),\n };\n }\n\n private clampRetryAfterMs(retryAfterMs: number): number {\n return Math.max(0, Math.min(retryAfterMs, MAX_PROCESSING_RETRY_AFTER_MS));\n }\n\n /**\n * Stash the display-only fields the consumer passed into a create-session\n * call. Runs after the backend assigns a UUID so a later GET on the same\n * session (typically after a redirect) can fill in fields the backend no\n * longer persists — `overrideAmount`, `totalAmount`, `name`, etc.\n *\n * No-op when no UUID is available.\n */\n private autoCacheDisplayData(\n sessionId: string | undefined,\n params: InlineSessionDraft,\n ): void {\n if (!sessionId) return;\n const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n if (products.length === 0 && !params.currency) {\n return;\n }\n const usingUnifiedProducts = params.products !== undefined;\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n usingUnifiedProducts ? undefined : params.items,\n usingUnifiedProducts ? undefined : params.subscriptions,\n products,\n );\n cacheSessionDisplayData(sessionId, {\n currency: sessionCurrency ?? undefined,\n products: products.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency ?? undefined,\n })),\n });\n }\n\n /**\n * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response. Server values always win — cache fills in\n * only where the server returned `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedProducts = new Map<string, SessionDisplayProduct>();\n const productKey = (\n type: SessionDisplayProduct['type'],\n code: string | undefined,\n ): string | undefined => (code && type ? `${type}:${code}` : undefined);\n for (const p of cached?.products ?? []) {\n const key = productKey(p.type, p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.type, p.code);\n const fallback = key ? cachedProducts.get(key) : undefined;\n return {\n ...p,\n name: p.name ?? fallback?.name ?? null,\n totalAmount: p.totalAmount ?? fallback?.totalAmount,\n overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,\n currency: p.currency ?? fallback?.currency,\n };\n });\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n products: mergedProducts,\n };\n }\n}\n","/**\n * Client-side cache for display-only checkout fields the backend no longer\n * persists (`overrideAmount`, `totalAmount`, `providerItemName`,\n * `providerPlanName`, per-line `currency`).\n *\n * The cache lives in `sessionStorage` so it survives the navigation from the\n * page that creates the session to the checkout page that fetches it, but\n * dies on tab close. An in-memory fallback keeps the SDK working in Node /\n * SSR contexts where `sessionStorage` is unavailable.\n *\n * Values from the server response always win — cached values fill in only\n * where the server returned `null` or `undefined`.\n */\n\nconst STORAGE_KEY_PREFIX = 'flopay_session_display:';\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/** Display-only fields per product that can be cached and merged back later. */\nexport interface SessionDisplayProduct {\n /** Catalog code (match key). */\n code?: string;\n /** Whether this product is a one-time item or a recurring subscription. */\n type?: 'item' | 'subscription';\n /** Display-only name for the product. */\n name?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only payload that can be stashed for later merge into a session response. */\nexport interface SessionDisplayCacheData {\n /** Session-level currency (falls into the response only when the server omits it). */\n currency?: string;\n products?: SessionDisplayProduct[];\n}\n\ninterface CachedEntry {\n data: SessionDisplayCacheData;\n expiresAt: number;\n}\n\nconst memoryStore = new Map<string, CachedEntry>();\n\nfunction storageKey(sessionId: string): string {\n return `${STORAGE_KEY_PREFIX}${sessionId}`;\n}\n\nfunction getSessionStorage(): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Stash display-only data for a session. Called client-side right after the\n * server returns a session ID, so the values survive the redirect to the\n * checkout page.\n */\nexport function cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n): void {\n if (!sessionId) return;\n\n const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;\n const entry: CachedEntry = { data, expiresAt: Date.now() + ttl };\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.setItem(storageKey(sessionId), JSON.stringify(entry));\n return;\n } catch {\n // Quota / serialization error — fall through to in-memory store\n }\n }\n\n memoryStore.set(sessionId, entry);\n}\n\n/**\n * Read previously-cached display data for a session, or `null` if nothing\n * is cached (or the TTL has elapsed).\n */\nexport function getSessionDisplayData(\n sessionId: string,\n): SessionDisplayCacheData | null {\n if (!sessionId) return null;\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n const raw = storage.getItem(storageKey(sessionId));\n if (raw) {\n const entry = JSON.parse(raw) as CachedEntry;\n if (entry && typeof entry.expiresAt === 'number' && entry.expiresAt > Date.now()) {\n return entry.data;\n }\n storage.removeItem(storageKey(sessionId));\n }\n } catch {\n // Malformed entry — fall through to memory store\n }\n }\n\n const memEntry = memoryStore.get(sessionId);\n if (memEntry) {\n if (memEntry.expiresAt > Date.now()) {\n return memEntry.data;\n }\n memoryStore.delete(sessionId);\n }\n\n return null;\n}\n\n/**\n * Drop any cached display data for a session. Call from the success page\n * after the payment completes; otherwise the TTL handles cleanup.\n */\nexport function clearSessionDisplayData(sessionId: string): void {\n if (!sessionId) return;\n\n memoryStore.delete(sessionId);\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.removeItem(storageKey(sessionId));\n } catch {\n // ignore\n }\n }\n}\n","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport { cacheSessionDisplayData } from './session-display-cache.js';\n\n/**\n * Max number of coupon codes the billing API accepts per session create.\n * Mirrors the backend guard introduced in billing v1.1.2 — exceeding this\n * returns the `CouponLimitExceeded` error.\n */\nconst MAX_COUPON_CODES = 5;\n\ninterface ApiErrorBody {\n code?: unknown;\n message?: unknown;\n error?: { code?: unknown; message?: unknown };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Map a billing API error body to a `FloPayError`. Surfaces the coupon\n * validation codes introduced in billing v1.1.2 (`CouponLimitExceeded`,\n * `CouponCurrencyUnsupported`) as structured errors so callers can branch\n * on `error.code` instead of parsing the HTTP status.\n */\nfunction buildCheckoutSessionError(status: number, payload: ApiErrorBody | undefined): FloPayError {\n const nested = payload?.error;\n const code =\n readString(payload?.code)\n ?? readString(nested?.code)\n ?? `http_${status}`;\n const message =\n readString(payload?.message)\n ?? readString(nested?.message)\n ?? defaultMessageForCode(code, status);\n\n return new FloPayError(message, 'api_error', { code, statusCode: status });\n}\n\nfunction defaultMessageForCode(code: string, status: number): string {\n switch (code) {\n case 'CouponLimitExceeded':\n return `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`;\n case 'CouponCurrencyUnsupported':\n return 'One of the applied coupons has no price configured for the cart currency.';\n default:\n return `Failed to create checkout session (HTTP ${status}).`;\n }\n}\n\n/**\n * Creates a checkout session via the billing API and redirects the user\n * to the hosted checkout page.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n *\n * - On 201: Redirects to `{checkoutBaseUrl}/secure?id={uuid}&...redirectParams`\n * - On 204: Redirects directly to `successUrl` (payment method already on file)\n * - On other: Returns `{ status }` without redirecting\n *\n * @example\n * ```ts\n * import { createCheckoutSession } from '@flopay/js';\n *\n * await createCheckoutSession({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * currency: 'USD',\n * items: [{\n * code: 'initial_charge',\n * quantity: 1,\n * metadata: { source: 'web' },\n * }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * redirectParams: { email: 'user@example.com', bg: 'courses', mode: 'confirm' },\n * });\n * ```\n */\nexport async function createCheckoutSession(\n options: CreateSessionParams,\n): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n products,\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n currency,\n utmMetadata,\n } = options;\n\n if (couponCodes.length > MAX_COUPON_CODES) {\n throw new FloPayError(\n `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`,\n 'validation_error',\n { code: 'CouponLimitExceeded', param: 'couponCodes' },\n );\n }\n\n const wireProducts = products ?? foldIntoProducts(items, subscriptions);\n const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions, wireProducts);\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId,\n checkoutVersion: SDK_VERSION,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n addressLine1: account.addressLine1 ?? null,\n addressLine2: account.addressLine2 ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: ({ data?: { uuid?: string; nonce?: string } } & ApiErrorBody) | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string; nonce?: string } } & ApiErrorBody;\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status >= 400) {\n throw buildCheckoutSessionError(status, body);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n const nonce = body?.data?.nonce;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n if (!nonce) {\n throw new FloPayError(\n 'Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.',\n 'api_error',\n { code: 'MissingCheckoutSessionToken' },\n );\n }\n\n // Auto-stash display-only fields the consumer passed in so the checkout\n // page (post-redirect) renders the right totals/names even though the\n // backend no longer persists them. Runs before the redirect — values\n // survive the navigation via sessionStorage.\n if (wireProducts.length || sessionCurrency) {\n cacheSessionDisplayData(uuid, {\n currency: sessionCurrency,\n products: wireProducts.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency,\n })),\n });\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n if (setCookie && typeof window !== 'undefined' && typeof document !== 'undefined') {\n const checkoutData = JSON.stringify({ origin_url: cancelUrl });\n const domain = window.location.hostname.split('.').slice(-2).join('.');\n document.cookie = `checkout_data=${encodeURIComponent(checkoutData)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n // Carry the session-bound checkout token to the hosted checkout page so\n // it can echo `x-checkout-session-token` on every continuation call\n // (post-#640 backends require this header to match the session nonce).\n // The URL is deliberately not modified — keeping the nonce out of the\n // location bar avoids history / Referer leakage.\n document.cookie = `flopay_checkout_token=${encodeURIComponent(nonce)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString(), nonce };\n }\n\n if (status === 204) {\n if (typeof window !== 'undefined') {\n window.location.href = successUrl;\n }\n return { status: 204 };\n }\n\n return { status };\n}\n\n/**\n * Creates a checkout session with automatic retry on timeout/abort errors.\n *\n * Uses exponential backoff: 100ms, 200ms, 400ms, etc.\n */\nexport async function createCheckoutSessionWithRetries(\n options: CreateSessionParams & { maxRetries?: number },\n): Promise<CheckoutSessionResult> {\n const { maxRetries = 3, ...sessionOptions } = options;\n\n if (!Number.isFinite(maxRetries) || !Number.isInteger(maxRetries) || maxRetries <= 0) {\n throw new Error('Number of retries must be greater than 0');\n }\n\n let lastErr: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await createCheckoutSession(sessionOptions);\n } catch (err) {\n lastErr = err;\n\n if (err instanceof Error && err.name === 'AbortError' && attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastErr ?? new Error('Unknown error during checkout session creation');\n}\n"],"mappings":"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","names":["import_shared","import_shared","import_shared","import_shared","import_shared","readString"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/create-checkout-session.ts"],"sourcesContent":["export { loadFloPay } from './load.js';\nexport { FloPay } from './flopay.js';\nexport { FloPayElements } from './elements.js';\nexport { StripeAdapter } from './stripe-adapter.js';\nexport { PaymentAPI } from './payment-api.js';\nexport { createCheckoutSession, createCheckoutSessionWithRetries } from './create-checkout-session.js';\nexport {\n cacheSessionDisplayData,\n getSessionDisplayData,\n clearSessionDisplayData,\n} from './session-display-cache.js';\nexport type {\n SessionDisplayProduct,\n SessionDisplayCacheData,\n} from './session-display-cache.js';\n","import type { FloPayConfig } from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\nimport { StripeAdapter } from './stripe-adapter.js';\nimport { FloPay } from './flopay.js';\n\n/**\n * Cache of FloPay instances keyed by publishable key. Multiple instances can\n * coexist when a consumer drives more than one Stripe publishable key during a\n * single page-load.\n */\nconst instanceCache = new Map<string, FloPay>();\n\n/**\n * Loads and initializes the FloPay SDK.\n *\n * This is the primary entry point for consumer applications.\n * It initializes the underlying payment provider (currently Stripe)\n * and returns a ready-to-use `FloPay` instance.\n *\n * Instances are cached per `publishableKey`, so calling `loadFloPay` twice\n * with the same key returns the same instance. Different keys produce\n * independent instances that can run side-by-side.\n *\n * @example\n * ```ts\n * import { loadFloPay } from '@flopay/js';\n *\n * const flopay = await loadFloPay('pk_test_...');\n * const elements = flopay.elements();\n * const cardElement = await elements.create('card');\n * cardElement.mount('#card-container');\n * ```\n */\nexport async function loadFloPay(\n publishableKey: string,\n options?: Omit<FloPayConfig, 'publishableKey'>,\n): Promise<FloPay> {\n if (!publishableKey) {\n throw new FloPayError(\n 'A publishable key is required to initialize FloPay.',\n 'validation_error',\n { param: 'publishableKey' },\n );\n }\n\n const cached = instanceCache.get(publishableKey);\n if (cached) return cached;\n\n const config: FloPayConfig = {\n publishableKey,\n ...options,\n };\n\n // Stripe is the only adapter today. Direct PayPal does not flow through\n // this loader — it renders via the official PayPal JS SDK from\n // `DirectPayPalButton`.\n const adapter = new StripeAdapter();\n await adapter.initialize(config);\n\n const instance = new FloPay(adapter, config);\n instanceCache.set(publishableKey, instance);\n\n return instance;\n}\n","import type {Stripe, StripeElements, StripeElementType} from '@stripe/stripe-js';\nimport type {\n BillingDetails,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n ConfirmPaymentParams,\n CreatePaymentMethodResult,\n ElementOptions,\n ElementType,\n FloPayConfig,\n MountedElement,\n PaymentProviderAdapter,\n PaymentResult,\n} from '@flopay/shared';\nimport {FloPayError} from '@flopay/shared';\n\n/**\n * Maps FloPay element types to Stripe element types.\n * FloPay uses camelCase; Stripe uses lowercase with hyphens or specific names.\n */\nfunction toStripeElementType(type: ElementType): StripeElementType {\n const map: Record<ElementType, string> = {\n payment: 'payment',\n card: 'card',\n cardNumber: 'cardNumber',\n cardExpiry: 'cardExpiry',\n cardCvc: 'cardCvc',\n address: 'address',\n };\n return map[type] as StripeElementType;\n}\n\n/**\n * Wraps a Stripe element instance in the FloPay MountedElement interface.\n *\n * TODO: In a future phase, this wrapper will render the Stripe element inside\n * an iframe hosted on the FloPay domain for PCI DSS SAQ-A compliance.\n * For now, it delegates directly to the Stripe element.\n */\n/** Minimal interface shared by all Stripe element types. */\ninterface StripeElementLike {\n mount(domElement: string | HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update(options?: Record<string, unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler?: (...args: unknown[]) => void): void;\n}\n\n/**\n * Maps a `FloPayAppearance.theme` ('default' | 'flat' | 'night' | 'none') to a\n * Stripe Elements Appearance `theme` ('stripe' | 'flat' | 'night'). Stripe's\n * Appearance API only accepts those three; anything else triggers a console\n * warning and silently falls back. We normalize here so the bundles can keep\n * `'default'` as their public token.\n */\nfunction toStripeAppearanceTheme(\n theme: 'default' | 'flat' | 'night' | 'none' | undefined,\n): 'stripe' | 'night' | 'flat' {\n switch (theme) {\n case 'night': return 'night';\n case 'flat': return 'flat';\n // 'default', 'none', undefined, or any unexpected value → Stripe's baseline.\n default: return 'stripe';\n }\n}\n\nfunction wrapStripeElement(\n stripeElement: StripeElementLike,\n): MountedElement {\n const el = stripeElement;\n\n return {\n mount(container: HTMLElement) {\n el.mount(container);\n },\n unmount() {\n el.unmount();\n },\n update(options: Partial<ElementOptions>) {\n el.update(options as Record<string, unknown>);\n },\n on(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['on']?.(event, handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['off']?.(event, handler);\n },\n destroy() {\n el.destroy();\n },\n };\n}\n\n/**\n * Payment provider adapter backed by Stripe.\n *\n * Implements the `PaymentProviderAdapter` interface so that FloPay consumers\n * interact with a stable API regardless of the upstream provider.\n */\nexport class StripeAdapter implements PaymentProviderAdapter {\n readonly name = 'stripe';\n private stripe: Stripe | null = null;\n private elements: StripeElements | null = null;\n // Serialized appearance currently applied to `this.elements`. Used to detect\n // when consumers swap themes mid-session so we can live-update the Stripe\n // Elements group instead of returning a stale-styled cache. `null` while no\n // elements group exists.\n private appliedAppearanceKey: string | null = null;\n\n async initialize(config: FloPayConfig): Promise<void> {\n // loadStripe only works in the browser — skip initialization during SSR/SSG\n if (typeof window === 'undefined') {\n return;\n }\n\n let loadStripe: typeof import('@stripe/stripe-js').loadStripe;\n try {\n ({ loadStripe } = await import('@stripe/stripe-js'));\n } catch {\n // @stripe/stripe-js ships as a direct dependency of @flopay/js, so this\n // should never resolve to a missing module in practice. If it does (e.g.\n // a broken install tree), surface a clear FloPayError instead of an\n // unhandled module-resolution rejection.\n throw new FloPayError(\n 'Failed to load @stripe/stripe-js. Reinstall @flopay/js.',\n 'api_error',\n { code: 'StripeJsLoadFailed' },\n );\n }\n\n const stripe = await loadStripe(config.publishableKey, {\n locale: (config.locale as 'auto') ?? 'auto',\n });\n\n if (!stripe) {\n throw new FloPayError(\n 'Failed to initialize Stripe. Check your publishable key.',\n 'authentication_error',\n );\n }\n\n this.stripe = stripe;\n }\n\n /** Lazily creates the Stripe Elements group for the given options. */\n private getElements(options?: ElementOptions): StripeElements {\n if (!this.stripe) {\n throw new FloPayError(\n 'StripeAdapter not initialized. Call initialize() first.',\n 'api_error',\n );\n }\n\n const stripeAppearance = options?.appearance\n ? {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n }\n : undefined;\n const nextAppearanceKey = stripeAppearance ? JSON.stringify(stripeAppearance) : null;\n\n if (!this.elements) {\n let elementsOptions: Record<string, unknown>;\n\n if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n }\n\n if (stripeAppearance) {\n elementsOptions['appearance'] = stripeAppearance;\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n this.appliedAppearanceKey = nextAppearanceKey;\n } else if (nextAppearanceKey !== this.appliedAppearanceKey) {\n // Elements group already exists but the consumer changed (or removed)\n // the appearance. Stripe Elements supports a live `update({ appearance })`\n // call which re-styles mounted elements in place — no re-create, no\n // input-state loss. When the consumer drops `appearance`, fall through\n // with an empty payload so any prior variables/rules are cleared.\n (this.elements as unknown as { update: (opts: Record<string, unknown>) => void }).update({\n appearance: stripeAppearance ?? {},\n });\n this.appliedAppearanceKey = nextAppearanceKey;\n }\n\n return this.elements;\n }\n\n async createElement(\n type: ElementType,\n options: ElementOptions,\n ): Promise<MountedElement> {\n const elements = this.getElements(options);\n const stripeType = toStripeElementType(type);\n\n const elementOptions: Record<string, unknown> = {};\n if (options.layout) {\n elementOptions['layout'] = options.layout;\n }\n if (options.defaultValues) {\n elementOptions['defaultValues'] = options.defaultValues;\n }\n if (options.readOnly) {\n elementOptions['readOnly'] = options.readOnly;\n }\n if (options.mode) {\n elementOptions['mode'] = options.mode;\n }\n if (options.style) {\n elementOptions['style'] = options.style;\n }\n\n const stripeElement = elements.create(stripeType as 'card', elementOptions) as unknown as StripeElementLike;\n return wrapStripeElement(stripeElement);\n }\n\n getElement(type: ElementType): MountedElement | null {\n if (!this.elements) return null;\n const stripeType = toStripeElementType(type);\n const existing = this.elements.getElement(stripeType as 'card') as unknown as StripeElementLike | null;\n if (!existing) return null;\n return wrapStripeElement(existing);\n }\n\n async submitElements(): Promise<{ error?: FloPayError }> {\n if (!this.stripe || !this.elements) {\n return { error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n const { error } = await this.elements.submit();\n if (error) {\n return {\n error: new FloPayError(error.message ?? 'Validation failed', 'validation_error'),\n };\n }\n return {};\n }\n\n async createPaymentMethod(billingDetails?: BillingDetails): Promise<CreatePaymentMethodResult> {\n if (!this.stripe || !this.elements) {\n return {\n paymentMethodId: null,\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Detect split card fields vs unified PaymentElement.\n // With split fields (cardNumber + cardExpiry + cardCvc), Stripe requires\n // passing the specific cardNumber element with type: 'card'.\n // With unified PaymentElement, pass the elements group.\n const cardNumberEl = this.elements.getElement('cardNumber' as 'card');\n\n // Build billing_details for Stripe AVS (Address Verification).\n // When present, Stripe runs postal code and address checks automatically.\n const stripeBilling = billingDetails ? {\n billing_details: {\n ...(billingDetails.email ? { email: billingDetails.email } : {}),\n ...(billingDetails.name ? { name: billingDetails.name } : {}),\n ...(billingDetails.address ? {\n address: {\n ...(billingDetails.address.country ? { country: billingDetails.address.country } : {}),\n ...(billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {}),\n ...(billingDetails.address.city ? { city: billingDetails.address.city } : {}),\n ...(billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {}),\n ...(billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {}),\n ...(billingDetails.address.state ? { state: billingDetails.address.state } : {}),\n },\n } : {}),\n },\n } : {};\n\n const { error, paymentMethod } = cardNumberEl\n ? await this.stripe.createPaymentMethod({\n type: 'card',\n card: cardNumberEl,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0])\n : await this.stripe.createPaymentMethod({\n elements: this.elements,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0]);\n\n if (error) {\n return {\n paymentMethodId: null,\n error: new FloPayError(\n error.message ?? 'Failed to create payment method',\n 'api_error',\n { code: error.code },\n ),\n };\n }\n\n return { paymentMethodId: paymentMethod.id };\n }\n\n async confirmCardPayment(\n params: ConfirmCardPaymentParams,\n ): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return {\n status: 'failed',\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n const { error, paymentIntent } = await this.stripe.confirmCardPayment(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n { code: error.code, declineCode: error.decline_code },\n ),\n };\n }\n\n return {\n status: (paymentIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: paymentIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent?.payment_method),\n };\n }\n\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n if (!this.stripe || !this.elements) {\n throw new FloPayError(\n 'StripeAdapter not initialized or no elements created.',\n 'api_error',\n );\n }\n\n const billing = params.billingDetails;\n const paymentMethodData = billing ? {\n billing_details: {\n ...(billing.email ? { email: billing.email } : {}),\n ...(billing.name ? { name: billing.name } : {}),\n ...(billing.address ? {\n address: {\n ...(billing.address.country ? { country: billing.address.country } : {}),\n ...(billing.address.postal_code ? { postal_code: billing.address.postal_code } : {}),\n ...(billing.address.city ? { city: billing.address.city } : {}),\n ...(billing.address.line1 ? { line1: billing.address.line1 } : {}),\n ...(billing.address.line2 ? { line2: billing.address.line2 } : {}),\n ...(billing.address.state ? { state: billing.address.state } : {}),\n },\n } : {}),\n },\n } : undefined;\n\n const { error, paymentIntent } = await this.stripe.confirmPayment({\n elements: this.elements,\n clientSecret: params.clientSecret,\n confirmParams: {\n return_url: params.returnUrl ?? window.location.href,\n ...(paymentMethodData ? { payment_method_data: paymentMethodData } : {}),\n },\n redirect: 'if_required',\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n {\n code: error.code,\n declineCode: error.decline_code,\n },\n ),\n };\n }\n\n if (!paymentIntent) {\n return { status: 'failed', error: new FloPayError('No payment intent returned', 'api_error') };\n }\n\n const statusMap: Record<string, PaymentResult['status']> = {\n succeeded: 'succeeded',\n processing: 'processing',\n requires_action: 'requires_action',\n requires_payment_method: 'failed',\n canceled: 'failed',\n };\n\n return {\n status: statusMap[paymentIntent.status] ?? 'failed',\n paymentIntentId: paymentIntent.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent.payment_method),\n };\n }\n\n private extractPaymentMethodId(\n paymentMethod: string | { id?: string | null } | null | undefined,\n ): string | undefined {\n if (typeof paymentMethod === 'string' && paymentMethod.startsWith('pm_')) {\n return paymentMethod;\n }\n if (paymentMethod && typeof paymentMethod === 'object' && typeof paymentMethod.id === 'string') {\n return paymentMethod.id;\n }\n return undefined;\n }\n\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return { status: 'failed', error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n\n const baseUrl = params.billingApiUrl.replace(/\\/+$/, '');\n\n if (this.elements) {\n const { error: submitError } = await this.elements.submit();\n if (submitError) {\n return {\n status: 'failed',\n error: new FloPayError(\n submitError.message ?? 'PayPal payment failed',\n 'validation_error',\n { code: submitError.code },\n ),\n };\n }\n }\n\n const intentHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (params.nonce) intentHeaders['x-checkout-session-token'] = params.nonce;\n\n const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: intentHeaders,\n body: JSON.stringify({\n sessionId: params.sessionId,\n email: params.email,\n paymentMethodType: 'paypal',\n isPaypal: 'true',\n }),\n });\n\n if (!intentResponse.ok) {\n return { status: 'failed', error: new FloPayError('Failed to create PayPal payment intent', 'api_error') };\n }\n\n const intentJson = await intentResponse.json() as { data?: { id?: string } };\n const intentClientSecret = intentJson.data?.id;\n if (!intentClientSecret) {\n return { status: 'failed', error: new FloPayError('No client_secret in response', 'api_error') };\n }\n\n const { error } = await this.stripe.confirmPayment({\n clientSecret: intentClientSecret,\n elements: this.elements ?? undefined,\n confirmParams: { return_url: params.returnUrl },\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'PayPal payment failed', 'api_error', { code: error.code }),\n };\n }\n\n return {\n status: 'processing',\n };\n }\n\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n if (!this.stripe || typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n const paymentIntentId = params.get('payment_intent');\n const clientSecret = params.get('payment_intent_client_secret');\n const redirectStatus = params.get('redirect_status');\n\n if (!paymentIntentId || !clientSecret) return null;\n\n if (redirectStatus === 'failed') {\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was declined. Please try again.', 'api_error'),\n };\n }\n\n const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(clientSecret);\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'Failed to retrieve PayPal payment', 'api_error'),\n };\n }\n\n if (paymentIntent && (paymentIntent.status === 'requires_capture' || paymentIntent.status === 'succeeded')) {\n const pmId = typeof paymentIntent.payment_method === 'string'\n ? paymentIntent.payment_method\n : paymentIntent.payment_method?.id;\n\n // Clean up URL params\n const url = new URL(window.location.href);\n url.searchParams.delete('payment_intent');\n url.searchParams.delete('payment_intent_client_secret');\n url.searchParams.delete('redirect_status');\n window.history.replaceState({}, '', url.toString());\n\n return {\n status: paymentIntent.status as ConfirmCardPaymentResult['status'],\n paymentIntentId: paymentIntent.id,\n paymentMethodId: pmId,\n };\n }\n\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was not completed. Please try again.', 'api_error'),\n };\n }\n\n getRawProvider(): unknown {\n return this.stripe;\n }\n\n createPayPalElements(options: ElementOptions): unknown {\n if (!this.stripe) return null;\n\n const elementsOptions: Record<string, unknown> = {\n mode: 'payment' as const,\n amount: options.amount ?? 0,\n currency: (options.currency ?? 'usd').toLowerCase(),\n captureMethod: 'manual' as const,\n };\n\n if (options.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n\n if (options.appearance) {\n elementsOptions['appearance'] = {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n return this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n }\n\n destroy(): void {\n this.elements = null;\n this.appliedAppearanceKey = null;\n this.stripe = null;\n }\n}\n","import type {\n PaymentProviderAdapter,\n FloPayConfig,\n ElementOptions,\n ConfirmPaymentParams,\n PaymentResult,\n 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 /**\n * Session-bound checkout token. Forwarded as `x-checkout-session-token`\n * on the internal `POST /v1/checkouts/payments/intents` call so post-#640\n * backends don't reject the round-trip with a 401.\n */\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmPayPalPayment(params);\n }\n\n /** Resume a PayPal payment after redirect return. Returns null if no PayPal params in URL. */\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n return this.provider.resumePayPalPayment();\n }\n\n /** Confirms a payment using the mounted elements. */\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n return this.provider.confirmPayment(params);\n }\n\n /**\n * Retrieves a checkout session by ID via the billing API.\n *\n * Returns the normalized `CheckoutSession` with amount, currency,\n * customer data, and status.\n *\n * Requires `billingApiUrl` to be set — either via `loadFloPay(key, { billingApiUrl })`\n * or passed directly as the second argument.\n */\n async retrieveSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<CheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required to retrieve a session.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n const unified = await api.getUnifiedCheckoutSession(sessionId);\n if (!unified.data.session) {\n throw new FloPayError('Session not found', 'api_error');\n }\n return unified.data.session;\n }\n\n /**\n * Retrieves and normalizes a checkout session, including provider-specific\n * data (Stripe clientSecret/publishableKey, Chargebee site, etc.).\n *\n * The billing API URL is resolved from: explicit param → `loadFloPay()` config\n * → `NEXT_PUBLIC_FLOPAY_ENV` env var → `configureFlopay()` → staging fallback.\n */\n async retrieveUnifiedSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<NormalizedCheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n return api.getUnifiedCheckoutSession(sessionId);\n }\n\n /**\n * Returns the raw underlying provider instance (e.g. Stripe object).\n * Used internally by components that need direct provider access,\n * such as PayPal which requires its own Elements instance.\n */\n getRawProvider(): unknown {\n return this.provider.getRawProvider();\n }\n\n /** Tears down the SDK instance and releases resources. */\n destroy(): void {\n this.currentElements?.destroy();\n this.currentElements = null;\n this.provider.destroy();\n }\n}\n","import type {\n PaymentProviderAdapter,\n ElementType,\n ElementOptions,\n MountedElement,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/**\n * Manages the creation and lifecycle of payment elements.\n *\n * Each `FloPayElements` instance is bound to a single provider adapter\n * and tracks all created elements for cleanup.\n */\nexport class FloPayElements {\n private readonly provider: PaymentProviderAdapter;\n private readonly elementMap = new Map<ElementType, MountedElement>();\n private readonly baseOptions: ElementOptions;\n\n constructor(provider: PaymentProviderAdapter, options?: ElementOptions) {\n this.provider = provider;\n this.baseOptions = options ?? {};\n }\n\n /**\n * Creates a new element of the given type.\n * If an element of that type already exists, it is destroyed first.\n */\n async create(\n type: ElementType,\n options?: ElementOptions,\n ): Promise<MountedElement> {\n // Check if the provider already has this element (e.g. after React\n // Strict Mode unmount+remount cycle). Reuse it instead of creating\n // a duplicate, which Stripe would reject.\n const providerExisting = this.provider.getElement(type);\n if (providerExisting) {\n this.elementMap.set(type, providerExisting);\n return providerExisting;\n }\n\n const existing = this.elementMap.get(type);\n if (existing) {\n existing.destroy();\n }\n\n const merged: ElementOptions = { ...this.baseOptions, ...options };\n const element = await this.provider.createElement(type, merged);\n this.elementMap.set(type, element);\n return element;\n }\n\n /** Returns a previously created element, or `null`. */\n getElement(type: ElementType): MountedElement | null {\n return this.elementMap.get(type) ?? null;\n }\n\n /**\n * Submits all mounted elements for validation.\n *\n * Returns an object with an optional error if validation fails.\n * This does NOT confirm the payment — call `floPay.confirmPayment()` for that.\n */\n async submit(): Promise<{ error?: FloPayError }> {\n // Walk through all mounted elements and check for incomplete state.\n // The actual validation is handled by the provider's element implementation.\n // For Stripe, the Elements.submit() method is called during confirmPayment.\n // This method exists to match the Stripe Elements API surface.\n return {};\n }\n\n /** Destroys all created elements and clears the internal map. */\n destroy(): void {\n for (const element of this.elementMap.values()) {\n element.destroy();\n }\n this.elementMap.clear();\n }\n}\n","import type {\n CheckoutGateways,\n CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n CheckoutSessionProduct,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayProduct,\n} from './session-display-cache.js';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API. */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n /** Unified products list returned by the billing API (post-#760). */\n products?: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** 'item' or 'subscription'. */\n type: 'item' | 'subscription';\n code?: string;\n name?: string | null;\n description?: string | null;\n quantity: number;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n /**\n * Pre-discount total in cart-currency major units. Populated by billing\n * API ≥ v1.1.2; absent on older backends.\n */\n subtotalAmount?: number;\n /** Total reduction from applied coupons (cart-currency major units). */\n discountAmount?: number;\n /** Final charge amount after coupon discount (cart-currency major units). */\n totalAmount?: number;\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gateways?: CheckoutGateways;\n accountData: {\n userId: string;\n firstName: string;\n lastName: string;\n email: string;\n gender?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n };\n tagsData: {\n googleContainerId?: string | null;\n sessionId?: string | null;\n testEventCode?: string | null;\n };\n}\n\ntype ApiErrorPayload = Record<string, unknown> | null;\n\nconst DEFAULT_PROCESSING_RETRY_AFTER_MS = 1_000;\nconst MIN_PROCESSING_RETRY_AFTER_MS = 500;\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 15_000;\nconst MAX_PROCESSING_RETRY_AFTER_MS = 3_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nfunction readNumber(payload: ApiErrorPayload, key: string): number | undefined {\n const value = payload?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createCheckoutProcessingTimeoutError(): FloPayError {\n return new FloPayError(\n 'Checkout is still processing. Please try again shortly.',\n 'api_error',\n { code: 'checkout_processing_timeout' },\n );\n}\n\nasync function buildApiErrorFromResponse(\n response: Response,\n fallbackMessage: string,\n): Promise<FloPayError> {\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const nestedError = isRecord(payload?.error) ? payload.error : null;\n const message =\n readString(payload, 'message') ??\n readString(nestedError, 'message') ??\n fallbackMessage;\n const code =\n readString(payload, 'code') ??\n readString(payload, 'gatewayErrorCode') ??\n readString(nestedError, 'code') ??\n `http_${response.status}`;\n\n return new FloPayError(message, 'api_error', {\n code,\n statusCode: response.status,\n });\n}\n\n/**\n * Extra attempts (beyond the first) for transient network failures.\n * Three total tries: immediate, +150ms, +300ms.\n */\nconst NETWORK_RETRY_ATTEMPTS = 2;\n\n/**\n * Run `fetch` with a bounded retry on transient network failures.\n *\n * `fetch` rejects — rather than resolving with a non-ok `Response` — only when\n * the request never produced an HTTP response at all: a dropped connection, a\n * DNS hiccup, or a failed CORS preflight, which all surface as\n * `TypeError: Failed to fetch`. Those blips are worth retrying. An `AbortError`\n * (caller-initiated cancellation) is not, and any HTTP-level failure resolves\n * normally and is left for the caller to handle via `response.ok`.\n *\n * Backoff is exponential (150ms, 300ms). Only use this for requests whose\n * replay is harmless — idempotent reads, or session bootstrap where a re-sent\n * create can at worst leave an unused draft session behind.\n */\nasync function fetchWithNetworkRetry(\n input: RequestInfo | URL,\n init?: RequestInit,\n attempts = NETWORK_RETRY_ATTEMPTS,\n): Promise<Response> {\n let lastErr: unknown;\n for (let attempt = 0; ; attempt++) {\n try {\n return await fetch(input, init);\n } catch (err) {\n // Caller-initiated cancellation must propagate immediately, never retry.\n if (err instanceof Error && err.name === 'AbortError') throw err;\n lastErr = err;\n if (attempt >= attempts) throw lastErr;\n await delay(150 * 2 ** attempt);\n }\n }\n}\n\n/**\n * Client-side payment API service.\n *\n * Mirrors the `PaymentAPI` class from the checkout project's\n * `src/service/api.ts`. All methods call the billing API endpoints\n * that the checkout backend exposes.\n */\nexport class PaymentAPI {\n private readonly baseUrl: string;\n\n constructor(billingApiUrl: string) {\n this.baseUrl = billingApiUrl.replace(/\\/+$/, '');\n }\n\n /**\n * Fetch a raw checkout session by ID.\n *\n * `nonce` is the session-bound checkout token returned when the session\n * was created. When supplied it is sent as the `x-checkout-session-token`\n * header that post-#640 backends match against `checkout_session.nonce`\n * before returning the row — the UUID alone is no longer sufficient.\n * Backends that don't yet enforce it ignore the extra header.\n */\n async getCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n const body = await response.json() as BillingResponse<RawCheckoutSession>;\n return { ...body, data: this.mergeCachedDisplayData(body.data) };\n }\n\n /**\n * Stash display-only data for a session so subsequent fetches can fill in\n * fields the backend no longer persists (`overrideAmount`, `totalAmount`,\n * `providerItemName`, `providerPlanName`).\n *\n * Backed by `sessionStorage` in the browser, with an in-memory fallback in\n * Node/SSR contexts. Default TTL: 1 hour.\n *\n * Server-returned values always win — cached values fill in only where the\n * server returned `null` / `undefined`.\n *\n * @example\n * ```ts\n * paymentAPI.cacheSessionDisplayData(sessionId, {\n * currency: 'USD',\n * items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],\n * });\n * ```\n */\n cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n ): void {\n cacheSessionDisplayData(sessionId, data, options);\n }\n\n /**\n * Drop any cached display data for a session. Call after the payment\n * completes; otherwise the TTL handles cleanup.\n */\n clearSessionDisplayData(sessionId: string): void {\n clearSessionDisplayData(sessionId);\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateways` map to enumerate provider-specific data,\n * then wraps the session in a `NormalizedCheckoutSession` for provider-\n * agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId, nonce);\n return this.normalizeRawSession(res.data);\n }\n\n /**\n * Submit a tokenized payment to the billing backend.\n *\n * The backend will either succeed, return `type: '3ds_required'`\n * (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.\n *\n * Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`\n * and forwards `data.nonce` as `x-checkout-session-token`. Backend\n * `TeamFloPay/backend#640` rejects callers without a matching nonce with a\n * 401 — this method throws synchronously when `data.nonce` is missing so the\n * problem surfaces before the network round trip.\n *\n * @param userId Vestigial — backend's GatewayInterceptor routes via session,\n * not headers, so this value is no longer sent on the wire. Kept in the\n * signature for back-compat with existing callers; will be removed in a\n * future major version.\n */\n async processPayment(\n _userId: string,\n data: ProcessPaymentParams,\n options?: {\n pollTimeoutMs?: number;\n },\n ): Promise<Response> {\n if (!data.nonce) {\n throw new FloPayError(\n 'processPayment requires `nonce` — pass the value returned from session creation.',\n 'validation_error',\n { code: 'MissingCheckoutSessionToken', param: 'nonce' },\n );\n }\n\n const { nonce, ...processBody } = data;\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(data.sessionId)}/process`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(processBody),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, { ...options, nonce });\n }\n\n /**\n * Create a PaymentIntent on the backend.\n *\n * Used by the Stripe flow to create a server-side PaymentIntent\n * with the client's payment method attached.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied — the\n * session-bound checkout token returned by session creation. Post-#640\n * backends reject this call with a 401 when the header is missing or does\n * not match the session's stored nonce.\n */\n async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\n isPaypal: options?.isPaypal ?? false,\n }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Create a SetupIntent for saving payment methods.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied —\n * required by post-#640 backends, ignored by earlier versions.\n */\n async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ sessionId, email, paymentMethodType }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Fetch user's prior payments by email.\n * Used to determine if saved card UX should be shown.\n */\n async getPaymentsByEmail(\n email: string,\n options?: { signal?: AbortSignal; page?: number; limit?: number },\n ): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }> {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 1;\n const params = new URLSearchParams({\n email,\n page: String(page),\n limit: String(limit),\n sortField: 'occurredAt',\n sortDirection: 'DESC',\n });\n\n const response = await fetch(\n `${this.baseUrl}/v1/payments?${params.toString()}`,\n {\n method: 'GET',\n signal: options?.signal,\n keepalive: true,\n },\n );\n\n if (!response.ok) {\n throw new FloPayError('Failed to fetch payments', 'api_error');\n }\n\n return response.json() as Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>;\n }\n\n /**\n * Create a checkout session AND return the full session data in one call.\n * Uses `?expand=true` so the backend returns the complete session\n * instead of just a UUID — eliminating the need for a second GET.\n *\n * Falls back to create + GET if the backend doesn't support `expand`.\n */\n async createAndFetchSession(\n params: InlineSessionDraft,\n ): Promise<NormalizedCheckoutSession> {\n const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n wireProducts,\n );\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n checkoutVersion: SDK_VERSION,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: params.account.userId,\n firstName: params.account.firstName ?? null,\n lastName: params.account.lastName ?? null,\n email: params.account.email,\n country: params.account.country ?? null,\n gender: params.account.gender ?? null,\n city: params.account.city ?? null,\n state: params.account.state ?? null,\n zip: params.account.zip ?? null,\n addressLine1: params.account.addressLine1 ?? null,\n addressLine2: params.account.addressLine2 ?? null,\n },\n couponCodes: params.couponCodes ?? [],\n };\n\n if (params.tokenizedData) payload['tokenizedData'] = params.tokenizedData;\n if (params.tagsData) payload['tagsData'] = params.tagsData;\n if (params.utmMetadata?.length) payload['utmMetadata'] = params.utmMetadata;\n\n // Checkout analytics metadata\n if (params.avsCheck !== undefined) payload['avsCheck'] = params.avsCheck;\n if (params.checkoutType) payload['checkoutType'] = params.checkoutType;\n if (params.checkoutLayout) payload['checkoutLayout'] = params.checkoutLayout;\n if (params.avsConfig) payload['avsConfig'] = params.avsConfig;\n\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n );\n\n if (response.status === 204) {\n throw new FloPayError(\n 'Session auto-completed — payment method already on file',\n 'api_error',\n { code: 'session_auto_completed' },\n );\n }\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to create checkout session');\n }\n\n const body = await response.json() as {\n data: RawCheckoutSession & { uuid?: string };\n autoProcessingError?: CheckoutProcessError;\n autoProcessingAttempted?: boolean;\n autoProcessingPending?: CheckoutProcessingPending;\n };\n\n // If backend supports expand=true, it returns full session data.\n // If not, it returns { data: { uuid } } and we fall back to a GET.\n if (body.data && 'gateways' in body.data) {\n // Auto-stash display-only fields the consumer just sent so wallet\n // sheets and the post-redirect fetch see the same totals. Must run\n // BEFORE the merge so the merge picks them up.\n this.autoCacheDisplayData(body.data.uuid, params);\n const merged = this.mergeCachedDisplayData(body.data);\n return {\n ...this.normalizeRawSession(merged),\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n // Fallback: backend returned UUID only — fetch full session\n const uuid = body.data?.uuid;\n if (!uuid) {\n throw new FloPayError('No session ID returned', 'api_error');\n }\n this.autoCacheDisplayData(uuid, params);\n const unifiedSession = await this.getUnifiedCheckoutSession(uuid);\n return {\n ...unifiedSession,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n async waitForCheckoutSessionCompletion(\n checkoutSessionId: string,\n options?: {\n initialDelayMs?: number;\n timeoutMs?: number;\n /**\n * Session-bound checkout token; forwarded on the poll's\n * `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.\n */\n nonce?: string;\n },\n ): Promise<NormalizedCheckoutSession> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_PROCESSING_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n let nextDelayMs = this.clampRetryAfterMs(options?.initialDelayMs ?? DEFAULT_PROCESSING_RETRY_AFTER_MS);\n\n while (true) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n if (nextDelayMs > 0) {\n await delay(Math.min(nextDelayMs, remainingMs));\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n }\n\n const session = await this.getUnifiedCheckoutSession(checkoutSessionId, options?.nonce);\n const status = session.data.session?.status;\n\n if (status === 'complete' || status === 'expired') {\n return session;\n }\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n nextDelayMs = this.clampRetryAfterMs(\n Math.max(nextDelayMs * 2, MIN_PROCESSING_RETRY_AFTER_MS),\n );\n }\n }\n\n /** Normalize a raw session into a provider-agnostic shape. */\n private normalizeRawSession(session: RawCheckoutSession): NormalizedCheckoutSession {\n const gateways: CheckoutGateways = session.gateways ?? {};\n const providers: BillingProvider[] = [];\n const data: NormalizedCheckoutSession['data'] = {\n session: this.toCheckoutSession(session),\n };\n\n const stripeGateway = gateways.stripe;\n if (stripeGateway?.publishableKey) {\n providers.push('stripe');\n const rawSession = session as unknown as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n stripeGateway.stripeClientSecret,\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n data.stripe = {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: stripeGateway.publishableKey ?? undefined,\n paypalPublishableKey: stripeGateway.paypalPublishableKey ?? undefined,\n environment: stripeGateway.environment,\n enabledPaymentMethods: Array.isArray(stripeGateway.enabledPaymentMethods)\n ? stripeGateway.enabledPaymentMethods.filter((m): m is string => typeof m === 'string')\n : undefined,\n };\n }\n\n const paypalGateway = gateways.paypal;\n if (paypalGateway?.publishableKey) {\n providers.push('paypal');\n data.paypal = {\n publishableKey: paypalGateway.publishableKey,\n environment: paypalGateway.environment,\n };\n }\n\n return {\n providers,\n mode: 'tokenize',\n data,\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const rawProducts = raw.products ?? [];\n\n // Billing API ≥ v1.1.2 returns the coupon-adjusted `totalAmount` on the\n // session. Prefer it as the source of truth so wallet sheets render the\n // post-coupon amount; fall back to summing per-line `overrideAmount`.\n const hasBackendTotal =\n typeof raw.totalAmount === 'number' && Number.isFinite(raw.totalAmount);\n const computedTotal = rawProducts.reduce(\n (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),\n 0,\n );\n const totalAmount = hasBackendTotal ? (raw.totalAmount as number) : computedTotal;\n\n // Billing API returns amounts in major units (e.g. 24.95 USD).\n // Stripe expects amounts in smallest currency unit (cents).\n const amountInCents = Math.round(totalAmount * 100);\n\n const currency = raw.currency ?? rawProducts[0]?.currency ?? 'USD';\n\n const mode: CheckoutSession['mode'] =\n rawProducts.some((p) => p.type === 'subscription') ? 'subscription' : 'payment';\n\n return {\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode,\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency,\n customer: {\n id: raw.accountData.userId,\n email: raw.accountData.email,\n firstName: raw.accountData.firstName,\n lastName: raw.accountData.lastName,\n country: raw.accountData.country ?? undefined,\n city: raw.accountData.city ?? undefined,\n state: raw.accountData.state ?? undefined,\n zip: raw.accountData.zip ?? undefined,\n gender: raw.accountData.gender ?? undefined,\n line1: raw.accountData.addressLine1 ?? undefined,\n line2: raw.accountData.addressLine2 ?? undefined,\n },\n metadata: {},\n\n checkoutMode: raw.checkoutMode,\n products: rawProducts.map((p): CheckoutSessionProduct => ({\n ...p,\n totalAmount: typeof p.totalAmount === 'number' ? p.totalAmount : undefined,\n overrideAmount: typeof p.overrideAmount === 'number' ? p.overrideAmount : null,\n currency: typeof p.currency === 'string' ? p.currency : undefined,\n metadata: p.metadata ?? null,\n })),\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n subtotalAmount: raw.subtotalAmount,\n discountAmount: raw.discountAmount,\n totalAmount: raw.totalAmount,\n createdAt: raw.createdAt,\n gateways: raw.gateways,\n accountData: raw.accountData,\n tagsData: raw.tagsData,\n };\n }\n\n private toCheckoutSessionStatus(status: RawCheckoutSession['status']): CheckoutSession['status'] {\n if (status === 'completed') {\n return 'complete';\n }\n\n if (status === 'expired') {\n return 'expired';\n }\n\n return 'open';\n }\n\n private async resolveProcessResponse(\n response: Response,\n checkoutSessionId: string,\n options?: {\n pollTimeoutMs?: number;\n nonce?: string;\n },\n ): Promise<Response> {\n if (response.status !== 202) {\n return response;\n }\n\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const pending = this.toCheckoutProcessingPending(payload, response, checkoutSessionId);\n const session = await this.waitForCheckoutSessionCompletion(pending.sessionId, {\n initialDelayMs: pending.retryAfterMs,\n timeoutMs: options?.pollTimeoutMs,\n nonce: options?.nonce,\n });\n\n if (session.data.session?.status === 'complete') {\n return new Response(null, { status: 204, statusText: 'No Content' });\n }\n\n if (session.data.session?.status === 'expired') {\n throw new FloPayError(\n 'Checkout session has expired.',\n 'api_error',\n { code: 'checkout_session_expired' },\n );\n }\n\n throw createCheckoutProcessingTimeoutError();\n }\n\n private toCheckoutProcessingPending(\n payload: ApiErrorPayload,\n response: Response,\n checkoutSessionId: string,\n ): CheckoutProcessingPending {\n const retryAfterHeader = response.headers.get('Retry-After');\n const headerRetryAfterSeconds = retryAfterHeader === null\n || retryAfterHeader.trim() === ''\n ? undefined\n : Number(retryAfterHeader);\n const headerRetryAfterMs = headerRetryAfterSeconds !== undefined\n && Number.isFinite(headerRetryAfterSeconds)\n ? headerRetryAfterSeconds * 1_000\n : undefined;\n\n return {\n type: 'checkout_processing',\n sessionId: readString(payload, 'sessionId') ?? checkoutSessionId,\n retryAfterMs: this.clampRetryAfterMs(\n readNumber(payload, 'retryAfterMs')\n ?? headerRetryAfterMs\n ?? DEFAULT_PROCESSING_RETRY_AFTER_MS,\n ),\n statusUrl: readString(payload, 'statusUrl'),\n sessionUrl: readString(payload, 'sessionUrl'),\n };\n }\n\n private clampRetryAfterMs(retryAfterMs: number): number {\n return Math.max(0, Math.min(retryAfterMs, MAX_PROCESSING_RETRY_AFTER_MS));\n }\n\n /**\n * Stash the display-only fields the consumer passed into a create-session\n * call. Runs after the backend assigns a UUID so a later GET on the same\n * session (typically after a redirect) can fill in fields the backend no\n * longer persists — `overrideAmount`, `totalAmount`, `name`, etc.\n *\n * No-op when no UUID is available.\n */\n private autoCacheDisplayData(\n sessionId: string | undefined,\n params: InlineSessionDraft,\n ): void {\n if (!sessionId) return;\n const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n if (products.length === 0 && !params.currency) {\n return;\n }\n const usingUnifiedProducts = params.products !== undefined;\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n usingUnifiedProducts ? undefined : params.items,\n usingUnifiedProducts ? undefined : params.subscriptions,\n products,\n );\n cacheSessionDisplayData(sessionId, {\n currency: sessionCurrency ?? undefined,\n products: products.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency ?? undefined,\n })),\n });\n }\n\n /**\n * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response. Server values always win — cache fills in\n * only where the server returned `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedProducts = new Map<string, SessionDisplayProduct>();\n // Key on `code` alone: the catalog code is the product's identity (the\n // backend resolves item-vs-subscription from it), so cached display data\n // merges back correctly even when the create-side payload omitted the\n // deprecated `type`.\n const productKey = (code: string | undefined): string | undefined =>\n code ? `code:${code}` : undefined;\n for (const p of cached?.products ?? []) {\n const key = productKey(p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.code);\n const fallback = key ? cachedProducts.get(key) : undefined;\n return {\n ...p,\n name: p.name ?? fallback?.name ?? null,\n totalAmount: p.totalAmount ?? fallback?.totalAmount,\n overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,\n currency: p.currency ?? fallback?.currency,\n };\n });\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n products: mergedProducts,\n };\n }\n}\n","/**\n * Client-side cache for display-only checkout fields the backend no longer\n * persists (`overrideAmount`, `totalAmount`, `providerItemName`,\n * `providerPlanName`, per-line `currency`).\n *\n * The cache lives in `sessionStorage` so it survives the navigation from the\n * page that creates the session to the checkout page that fetches it, but\n * dies on tab close. An in-memory fallback keeps the SDK working in Node /\n * SSR contexts where `sessionStorage` is unavailable.\n *\n * Values from the server response always win — cached values fill in only\n * where the server returned `null` or `undefined`.\n */\n\nconst STORAGE_KEY_PREFIX = 'flopay_session_display:';\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/** Display-only fields per product that can be cached and merged back later. */\nexport interface SessionDisplayProduct {\n /** Catalog code (match key). */\n code?: string;\n /** Whether this product is a one-time item or a recurring subscription. */\n type?: 'item' | 'subscription';\n /** Display-only name for the product. */\n name?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only payload that can be stashed for later merge into a session response. */\nexport interface SessionDisplayCacheData {\n /** Session-level currency (falls into the response only when the server omits it). */\n currency?: string;\n products?: SessionDisplayProduct[];\n}\n\ninterface CachedEntry {\n data: SessionDisplayCacheData;\n expiresAt: number;\n}\n\nconst memoryStore = new Map<string, CachedEntry>();\n\nfunction storageKey(sessionId: string): string {\n return `${STORAGE_KEY_PREFIX}${sessionId}`;\n}\n\nfunction getSessionStorage(): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Stash display-only data for a session. Called client-side right after the\n * server returns a session ID, so the values survive the redirect to the\n * checkout page.\n */\nexport function cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n): void {\n if (!sessionId) return;\n\n const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;\n const entry: CachedEntry = { data, expiresAt: Date.now() + ttl };\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.setItem(storageKey(sessionId), JSON.stringify(entry));\n return;\n } catch {\n // Quota / serialization error — fall through to in-memory store\n }\n }\n\n memoryStore.set(sessionId, entry);\n}\n\n/**\n * Read previously-cached display data for a session, or `null` if nothing\n * is cached (or the TTL has elapsed).\n */\nexport function getSessionDisplayData(\n sessionId: string,\n): SessionDisplayCacheData | null {\n if (!sessionId) return null;\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n const raw = storage.getItem(storageKey(sessionId));\n if (raw) {\n const entry = JSON.parse(raw) as CachedEntry;\n if (entry && typeof entry.expiresAt === 'number' && entry.expiresAt > Date.now()) {\n return entry.data;\n }\n storage.removeItem(storageKey(sessionId));\n }\n } catch {\n // Malformed entry — fall through to memory store\n }\n }\n\n const memEntry = memoryStore.get(sessionId);\n if (memEntry) {\n if (memEntry.expiresAt > Date.now()) {\n return memEntry.data;\n }\n memoryStore.delete(sessionId);\n }\n\n return null;\n}\n\n/**\n * Drop any cached display data for a session. Call from the success page\n * after the payment completes; otherwise the TTL handles cleanup.\n */\nexport function clearSessionDisplayData(sessionId: string): void {\n if (!sessionId) return;\n\n memoryStore.delete(sessionId);\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.removeItem(storageKey(sessionId));\n } catch {\n // ignore\n }\n }\n}\n","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport { cacheSessionDisplayData } from './session-display-cache.js';\n\n/**\n * Max number of coupon codes the billing API accepts per session create.\n * Mirrors the backend guard introduced in billing v1.1.2 — exceeding this\n * returns the `CouponLimitExceeded` error.\n */\nconst MAX_COUPON_CODES = 5;\n\ninterface ApiErrorBody {\n code?: unknown;\n message?: unknown;\n error?: { code?: unknown; message?: unknown };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Map a billing API error body to a `FloPayError`. Surfaces the coupon\n * validation codes introduced in billing v1.1.2 (`CouponLimitExceeded`,\n * `CouponCurrencyUnsupported`) as structured errors so callers can branch\n * on `error.code` instead of parsing the HTTP status.\n */\nfunction buildCheckoutSessionError(status: number, payload: ApiErrorBody | undefined): FloPayError {\n const nested = payload?.error;\n const code =\n readString(payload?.code)\n ?? readString(nested?.code)\n ?? `http_${status}`;\n const message =\n readString(payload?.message)\n ?? readString(nested?.message)\n ?? defaultMessageForCode(code, status);\n\n return new FloPayError(message, 'api_error', { code, statusCode: status });\n}\n\nfunction defaultMessageForCode(code: string, status: number): string {\n switch (code) {\n case 'CouponLimitExceeded':\n return `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`;\n case 'CouponCurrencyUnsupported':\n return 'One of the applied coupons has no price configured for the cart currency.';\n default:\n return `Failed to create checkout session (HTTP ${status}).`;\n }\n}\n\n/**\n * Creates a checkout session via the billing API and redirects the user\n * to the hosted checkout page.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n *\n * - On 201: Redirects to `{checkoutBaseUrl}/secure?id={uuid}&...redirectParams`\n * - On 204: Redirects directly to `successUrl` (payment method already on file)\n * - On other: Returns `{ status }` without redirecting\n *\n * @example\n * ```ts\n * import { createCheckoutSession } from '@flopay/js';\n *\n * await createCheckoutSession({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * currency: 'USD',\n * items: [{\n * code: 'initial_charge',\n * quantity: 1,\n * metadata: { source: 'web' },\n * }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * redirectParams: { email: 'user@example.com', bg: 'courses', mode: 'confirm' },\n * });\n * ```\n */\nexport async function createCheckoutSession(\n options: CreateSessionParams,\n): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n products,\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n currency,\n utmMetadata,\n } = options;\n\n if (couponCodes.length > MAX_COUPON_CODES) {\n throw new FloPayError(\n `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`,\n 'validation_error',\n { code: 'CouponLimitExceeded', param: 'couponCodes' },\n );\n }\n\n const wireProducts = products ?? foldIntoProducts(items, subscriptions);\n const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions, wireProducts);\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId,\n checkoutVersion: SDK_VERSION,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n addressLine1: account.addressLine1 ?? null,\n addressLine2: account.addressLine2 ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: ({ data?: { uuid?: string; nonce?: string } } & ApiErrorBody) | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string; nonce?: string } } & ApiErrorBody;\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status >= 400) {\n throw buildCheckoutSessionError(status, body);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n const nonce = body?.data?.nonce;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n if (!nonce) {\n throw new FloPayError(\n 'Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.',\n 'api_error',\n { code: 'MissingCheckoutSessionToken' },\n );\n }\n\n // Auto-stash display-only fields the consumer passed in so the checkout\n // page (post-redirect) renders the right totals/names even though the\n // backend no longer persists them. Runs before the redirect — values\n // survive the navigation via sessionStorage.\n if (wireProducts.length || sessionCurrency) {\n cacheSessionDisplayData(uuid, {\n currency: sessionCurrency,\n products: wireProducts.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency,\n })),\n });\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n if (setCookie && typeof window !== 'undefined' && typeof document !== 'undefined') {\n const checkoutData = JSON.stringify({ origin_url: cancelUrl });\n const domain = window.location.hostname.split('.').slice(-2).join('.');\n document.cookie = `checkout_data=${encodeURIComponent(checkoutData)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n // Carry the session-bound checkout token to the hosted checkout page so\n // it can echo `x-checkout-session-token` on every continuation call\n // (post-#640 backends require this header to match the session nonce).\n // The URL is deliberately not modified — keeping the nonce out of the\n // location bar avoids history / Referer leakage.\n document.cookie = `flopay_checkout_token=${encodeURIComponent(nonce)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString(), nonce };\n }\n\n if (status === 204) {\n if (typeof window !== 'undefined') {\n window.location.href = successUrl;\n }\n return { status: 204 };\n }\n\n return { status };\n}\n\n/**\n * Creates a checkout session with automatic retry on timeout/abort errors.\n *\n * Uses exponential backoff: 100ms, 200ms, 400ms, etc.\n */\nexport async function createCheckoutSessionWithRetries(\n options: CreateSessionParams & { maxRetries?: number },\n): Promise<CheckoutSessionResult> {\n const { maxRetries = 3, ...sessionOptions } = options;\n\n if (!Number.isFinite(maxRetries) || !Number.isInteger(maxRetries) || maxRetries <= 0) {\n throw new Error('Number of retries must be greater than 0');\n }\n\n let lastErr: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await createCheckoutSession(sessionOptions);\n } catch (err) {\n lastErr = err;\n\n if (err instanceof Error && err.name === 'AbortError' && attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastErr ?? new Error('Unknown error during checkout session creation');\n}\n"],"mappings":"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","names":["import_shared","import_shared","import_shared","import_shared","import_shared","readString"]}
|
package/dist/index.mjs
CHANGED
|
@@ -63,7 +63,16 @@ var StripeAdapter = class {
|
|
|
63
63
|
if (typeof window === "undefined") {
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
|
-
|
|
66
|
+
let loadStripe;
|
|
67
|
+
try {
|
|
68
|
+
({ loadStripe } = await import("@stripe/stripe-js"));
|
|
69
|
+
} catch {
|
|
70
|
+
throw new FloPayError(
|
|
71
|
+
"Failed to load @stripe/stripe-js. Reinstall @flopay/js.",
|
|
72
|
+
"api_error",
|
|
73
|
+
{ code: "StripeJsLoadFailed" }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
67
76
|
const stripe = await loadStripe(config.publishableKey, {
|
|
68
77
|
locale: config.locale ?? "auto"
|
|
69
78
|
});
|
|
@@ -1079,13 +1088,13 @@ var PaymentAPI = class {
|
|
|
1079
1088
|
mergeCachedDisplayData(raw) {
|
|
1080
1089
|
const cached = getSessionDisplayData(raw.uuid);
|
|
1081
1090
|
const cachedProducts = /* @__PURE__ */ new Map();
|
|
1082
|
-
const productKey = (
|
|
1091
|
+
const productKey = (code) => code ? `code:${code}` : void 0;
|
|
1083
1092
|
for (const p of cached?.products ?? []) {
|
|
1084
|
-
const key = productKey(p.
|
|
1093
|
+
const key = productKey(p.code);
|
|
1085
1094
|
if (key) cachedProducts.set(key, p);
|
|
1086
1095
|
}
|
|
1087
1096
|
const mergedProducts = (raw.products ?? []).map((p) => {
|
|
1088
|
-
const key = productKey(p.
|
|
1097
|
+
const key = productKey(p.code);
|
|
1089
1098
|
const fallback = key ? cachedProducts.get(key) : void 0;
|
|
1090
1099
|
return {
|
|
1091
1100
|
...p,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/create-checkout-session.ts"],"sourcesContent":["import type { FloPayConfig } from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\nimport { StripeAdapter } from './stripe-adapter.js';\nimport { FloPay } from './flopay.js';\n\n/**\n * Cache of FloPay instances keyed by publishable key. Multiple instances can\n * coexist when a consumer drives more than one Stripe publishable key during a\n * single page-load.\n */\nconst instanceCache = new Map<string, FloPay>();\n\n/**\n * Loads and initializes the FloPay SDK.\n *\n * This is the primary entry point for consumer applications.\n * It initializes the underlying payment provider (currently Stripe)\n * and returns a ready-to-use `FloPay` instance.\n *\n * Instances are cached per `publishableKey`, so calling `loadFloPay` twice\n * with the same key returns the same instance. Different keys produce\n * independent instances that can run side-by-side.\n *\n * @example\n * ```ts\n * import { loadFloPay } from '@flopay/js';\n *\n * const flopay = await loadFloPay('pk_test_...');\n * const elements = flopay.elements();\n * const cardElement = await elements.create('card');\n * cardElement.mount('#card-container');\n * ```\n */\nexport async function loadFloPay(\n publishableKey: string,\n options?: Omit<FloPayConfig, 'publishableKey'>,\n): Promise<FloPay> {\n if (!publishableKey) {\n throw new FloPayError(\n 'A publishable key is required to initialize FloPay.',\n 'validation_error',\n { param: 'publishableKey' },\n );\n }\n\n const cached = instanceCache.get(publishableKey);\n if (cached) return cached;\n\n const config: FloPayConfig = {\n publishableKey,\n ...options,\n };\n\n // Stripe is the only adapter today. Direct PayPal does not flow through\n // this loader — it renders via the official PayPal JS SDK from\n // `DirectPayPalButton`.\n const adapter = new StripeAdapter();\n await adapter.initialize(config);\n\n const instance = new FloPay(adapter, config);\n instanceCache.set(publishableKey, instance);\n\n return instance;\n}\n","import type {Stripe, StripeElements, StripeElementType} from '@stripe/stripe-js';\nimport type {\n BillingDetails,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n ConfirmPaymentParams,\n CreatePaymentMethodResult,\n ElementOptions,\n ElementType,\n FloPayConfig,\n MountedElement,\n PaymentProviderAdapter,\n PaymentResult,\n} from '@flopay/shared';\nimport {FloPayError} from '@flopay/shared';\n\n/**\n * Maps FloPay element types to Stripe element types.\n * FloPay uses camelCase; Stripe uses lowercase with hyphens or specific names.\n */\nfunction toStripeElementType(type: ElementType): StripeElementType {\n const map: Record<ElementType, string> = {\n payment: 'payment',\n card: 'card',\n cardNumber: 'cardNumber',\n cardExpiry: 'cardExpiry',\n cardCvc: 'cardCvc',\n address: 'address',\n };\n return map[type] as StripeElementType;\n}\n\n/**\n * Wraps a Stripe element instance in the FloPay MountedElement interface.\n *\n * TODO: In a future phase, this wrapper will render the Stripe element inside\n * an iframe hosted on the FloPay domain for PCI DSS SAQ-A compliance.\n * For now, it delegates directly to the Stripe element.\n */\n/** Minimal interface shared by all Stripe element types. */\ninterface StripeElementLike {\n mount(domElement: string | HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update(options?: Record<string, unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler?: (...args: unknown[]) => void): void;\n}\n\n/**\n * Maps a `FloPayAppearance.theme` ('default' | 'flat' | 'night' | 'none') to a\n * Stripe Elements Appearance `theme` ('stripe' | 'flat' | 'night'). Stripe's\n * Appearance API only accepts those three; anything else triggers a console\n * warning and silently falls back. We normalize here so the bundles can keep\n * `'default'` as their public token.\n */\nfunction toStripeAppearanceTheme(\n theme: 'default' | 'flat' | 'night' | 'none' | undefined,\n): 'stripe' | 'night' | 'flat' {\n switch (theme) {\n case 'night': return 'night';\n case 'flat': return 'flat';\n // 'default', 'none', undefined, or any unexpected value → Stripe's baseline.\n default: return 'stripe';\n }\n}\n\nfunction wrapStripeElement(\n stripeElement: StripeElementLike,\n): MountedElement {\n const el = stripeElement;\n\n return {\n mount(container: HTMLElement) {\n el.mount(container);\n },\n unmount() {\n el.unmount();\n },\n update(options: Partial<ElementOptions>) {\n el.update(options as Record<string, unknown>);\n },\n on(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['on']?.(event, handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['off']?.(event, handler);\n },\n destroy() {\n el.destroy();\n },\n };\n}\n\n/**\n * Payment provider adapter backed by Stripe.\n *\n * Implements the `PaymentProviderAdapter` interface so that FloPay consumers\n * interact with a stable API regardless of the upstream provider.\n */\nexport class StripeAdapter implements PaymentProviderAdapter {\n readonly name = 'stripe';\n private stripe: Stripe | null = null;\n private elements: StripeElements | null = null;\n // Serialized appearance currently applied to `this.elements`. Used to detect\n // when consumers swap themes mid-session so we can live-update the Stripe\n // Elements group instead of returning a stale-styled cache. `null` while no\n // elements group exists.\n private appliedAppearanceKey: string | null = null;\n\n async initialize(config: FloPayConfig): Promise<void> {\n // loadStripe only works in the browser — skip initialization during SSR/SSG\n if (typeof window === 'undefined') {\n return;\n }\n\n const { loadStripe } = await import('@stripe/stripe-js');\n const stripe = await loadStripe(config.publishableKey, {\n locale: (config.locale as 'auto') ?? 'auto',\n });\n\n if (!stripe) {\n throw new FloPayError(\n 'Failed to initialize Stripe. Check your publishable key.',\n 'authentication_error',\n );\n }\n\n this.stripe = stripe;\n }\n\n /** Lazily creates the Stripe Elements group for the given options. */\n private getElements(options?: ElementOptions): StripeElements {\n if (!this.stripe) {\n throw new FloPayError(\n 'StripeAdapter not initialized. Call initialize() first.',\n 'api_error',\n );\n }\n\n const stripeAppearance = options?.appearance\n ? {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n }\n : undefined;\n const nextAppearanceKey = stripeAppearance ? JSON.stringify(stripeAppearance) : null;\n\n if (!this.elements) {\n let elementsOptions: Record<string, unknown>;\n\n if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n }\n\n if (stripeAppearance) {\n elementsOptions['appearance'] = stripeAppearance;\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n this.appliedAppearanceKey = nextAppearanceKey;\n } else if (nextAppearanceKey !== this.appliedAppearanceKey) {\n // Elements group already exists but the consumer changed (or removed)\n // the appearance. Stripe Elements supports a live `update({ appearance })`\n // call which re-styles mounted elements in place — no re-create, no\n // input-state loss. When the consumer drops `appearance`, fall through\n // with an empty payload so any prior variables/rules are cleared.\n (this.elements as unknown as { update: (opts: Record<string, unknown>) => void }).update({\n appearance: stripeAppearance ?? {},\n });\n this.appliedAppearanceKey = nextAppearanceKey;\n }\n\n return this.elements;\n }\n\n async createElement(\n type: ElementType,\n options: ElementOptions,\n ): Promise<MountedElement> {\n const elements = this.getElements(options);\n const stripeType = toStripeElementType(type);\n\n const elementOptions: Record<string, unknown> = {};\n if (options.layout) {\n elementOptions['layout'] = options.layout;\n }\n if (options.defaultValues) {\n elementOptions['defaultValues'] = options.defaultValues;\n }\n if (options.readOnly) {\n elementOptions['readOnly'] = options.readOnly;\n }\n if (options.mode) {\n elementOptions['mode'] = options.mode;\n }\n if (options.style) {\n elementOptions['style'] = options.style;\n }\n\n const stripeElement = elements.create(stripeType as 'card', elementOptions) as unknown as StripeElementLike;\n return wrapStripeElement(stripeElement);\n }\n\n getElement(type: ElementType): MountedElement | null {\n if (!this.elements) return null;\n const stripeType = toStripeElementType(type);\n const existing = this.elements.getElement(stripeType as 'card') as unknown as StripeElementLike | null;\n if (!existing) return null;\n return wrapStripeElement(existing);\n }\n\n async submitElements(): Promise<{ error?: FloPayError }> {\n if (!this.stripe || !this.elements) {\n return { error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n const { error } = await this.elements.submit();\n if (error) {\n return {\n error: new FloPayError(error.message ?? 'Validation failed', 'validation_error'),\n };\n }\n return {};\n }\n\n async createPaymentMethod(billingDetails?: BillingDetails): Promise<CreatePaymentMethodResult> {\n if (!this.stripe || !this.elements) {\n return {\n paymentMethodId: null,\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Detect split card fields vs unified PaymentElement.\n // With split fields (cardNumber + cardExpiry + cardCvc), Stripe requires\n // passing the specific cardNumber element with type: 'card'.\n // With unified PaymentElement, pass the elements group.\n const cardNumberEl = this.elements.getElement('cardNumber' as 'card');\n\n // Build billing_details for Stripe AVS (Address Verification).\n // When present, Stripe runs postal code and address checks automatically.\n const stripeBilling = billingDetails ? {\n billing_details: {\n ...(billingDetails.email ? { email: billingDetails.email } : {}),\n ...(billingDetails.name ? { name: billingDetails.name } : {}),\n ...(billingDetails.address ? {\n address: {\n ...(billingDetails.address.country ? { country: billingDetails.address.country } : {}),\n ...(billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {}),\n ...(billingDetails.address.city ? { city: billingDetails.address.city } : {}),\n ...(billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {}),\n ...(billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {}),\n ...(billingDetails.address.state ? { state: billingDetails.address.state } : {}),\n },\n } : {}),\n },\n } : {};\n\n const { error, paymentMethod } = cardNumberEl\n ? await this.stripe.createPaymentMethod({\n type: 'card',\n card: cardNumberEl,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0])\n : await this.stripe.createPaymentMethod({\n elements: this.elements,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0]);\n\n if (error) {\n return {\n paymentMethodId: null,\n error: new FloPayError(\n error.message ?? 'Failed to create payment method',\n 'api_error',\n { code: error.code },\n ),\n };\n }\n\n return { paymentMethodId: paymentMethod.id };\n }\n\n async confirmCardPayment(\n params: ConfirmCardPaymentParams,\n ): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return {\n status: 'failed',\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n const { error, paymentIntent } = await this.stripe.confirmCardPayment(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n { code: error.code, declineCode: error.decline_code },\n ),\n };\n }\n\n return {\n status: (paymentIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: paymentIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent?.payment_method),\n };\n }\n\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n if (!this.stripe || !this.elements) {\n throw new FloPayError(\n 'StripeAdapter not initialized or no elements created.',\n 'api_error',\n );\n }\n\n const billing = params.billingDetails;\n const paymentMethodData = billing ? {\n billing_details: {\n ...(billing.email ? { email: billing.email } : {}),\n ...(billing.name ? { name: billing.name } : {}),\n ...(billing.address ? {\n address: {\n ...(billing.address.country ? { country: billing.address.country } : {}),\n ...(billing.address.postal_code ? { postal_code: billing.address.postal_code } : {}),\n ...(billing.address.city ? { city: billing.address.city } : {}),\n ...(billing.address.line1 ? { line1: billing.address.line1 } : {}),\n ...(billing.address.line2 ? { line2: billing.address.line2 } : {}),\n ...(billing.address.state ? { state: billing.address.state } : {}),\n },\n } : {}),\n },\n } : undefined;\n\n const { error, paymentIntent } = await this.stripe.confirmPayment({\n elements: this.elements,\n clientSecret: params.clientSecret,\n confirmParams: {\n return_url: params.returnUrl ?? window.location.href,\n ...(paymentMethodData ? { payment_method_data: paymentMethodData } : {}),\n },\n redirect: 'if_required',\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n {\n code: error.code,\n declineCode: error.decline_code,\n },\n ),\n };\n }\n\n if (!paymentIntent) {\n return { status: 'failed', error: new FloPayError('No payment intent returned', 'api_error') };\n }\n\n const statusMap: Record<string, PaymentResult['status']> = {\n succeeded: 'succeeded',\n processing: 'processing',\n requires_action: 'requires_action',\n requires_payment_method: 'failed',\n canceled: 'failed',\n };\n\n return {\n status: statusMap[paymentIntent.status] ?? 'failed',\n paymentIntentId: paymentIntent.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent.payment_method),\n };\n }\n\n private extractPaymentMethodId(\n paymentMethod: string | { id?: string | null } | null | undefined,\n ): string | undefined {\n if (typeof paymentMethod === 'string' && paymentMethod.startsWith('pm_')) {\n return paymentMethod;\n }\n if (paymentMethod && typeof paymentMethod === 'object' && typeof paymentMethod.id === 'string') {\n return paymentMethod.id;\n }\n return undefined;\n }\n\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return { status: 'failed', error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n\n const baseUrl = params.billingApiUrl.replace(/\\/+$/, '');\n\n if (this.elements) {\n const { error: submitError } = await this.elements.submit();\n if (submitError) {\n return {\n status: 'failed',\n error: new FloPayError(\n submitError.message ?? 'PayPal payment failed',\n 'validation_error',\n { code: submitError.code },\n ),\n };\n }\n }\n\n const intentHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (params.nonce) intentHeaders['x-checkout-session-token'] = params.nonce;\n\n const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: intentHeaders,\n body: JSON.stringify({\n sessionId: params.sessionId,\n email: params.email,\n paymentMethodType: 'paypal',\n isPaypal: 'true',\n }),\n });\n\n if (!intentResponse.ok) {\n return { status: 'failed', error: new FloPayError('Failed to create PayPal payment intent', 'api_error') };\n }\n\n const intentJson = await intentResponse.json() as { data?: { id?: string } };\n const intentClientSecret = intentJson.data?.id;\n if (!intentClientSecret) {\n return { status: 'failed', error: new FloPayError('No client_secret in response', 'api_error') };\n }\n\n const { error } = await this.stripe.confirmPayment({\n clientSecret: intentClientSecret,\n elements: this.elements ?? undefined,\n confirmParams: { return_url: params.returnUrl },\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'PayPal payment failed', 'api_error', { code: error.code }),\n };\n }\n\n return {\n status: 'processing',\n };\n }\n\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n if (!this.stripe || typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n const paymentIntentId = params.get('payment_intent');\n const clientSecret = params.get('payment_intent_client_secret');\n const redirectStatus = params.get('redirect_status');\n\n if (!paymentIntentId || !clientSecret) return null;\n\n if (redirectStatus === 'failed') {\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was declined. Please try again.', 'api_error'),\n };\n }\n\n const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(clientSecret);\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'Failed to retrieve PayPal payment', 'api_error'),\n };\n }\n\n if (paymentIntent && (paymentIntent.status === 'requires_capture' || paymentIntent.status === 'succeeded')) {\n const pmId = typeof paymentIntent.payment_method === 'string'\n ? paymentIntent.payment_method\n : paymentIntent.payment_method?.id;\n\n // Clean up URL params\n const url = new URL(window.location.href);\n url.searchParams.delete('payment_intent');\n url.searchParams.delete('payment_intent_client_secret');\n url.searchParams.delete('redirect_status');\n window.history.replaceState({}, '', url.toString());\n\n return {\n status: paymentIntent.status as ConfirmCardPaymentResult['status'],\n paymentIntentId: paymentIntent.id,\n paymentMethodId: pmId,\n };\n }\n\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was not completed. Please try again.', 'api_error'),\n };\n }\n\n getRawProvider(): unknown {\n return this.stripe;\n }\n\n createPayPalElements(options: ElementOptions): unknown {\n if (!this.stripe) return null;\n\n const elementsOptions: Record<string, unknown> = {\n mode: 'payment' as const,\n amount: options.amount ?? 0,\n currency: (options.currency ?? 'usd').toLowerCase(),\n captureMethod: 'manual' as const,\n };\n\n if (options.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n\n if (options.appearance) {\n elementsOptions['appearance'] = {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n return this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n }\n\n destroy(): void {\n this.elements = null;\n this.appliedAppearanceKey = null;\n this.stripe = null;\n }\n}\n","import type {\n PaymentProviderAdapter,\n FloPayConfig,\n ElementOptions,\n ConfirmPaymentParams,\n PaymentResult,\n 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 /**\n * Session-bound checkout token. Forwarded as `x-checkout-session-token`\n * on the internal `POST /v1/checkouts/payments/intents` call so post-#640\n * backends don't reject the round-trip with a 401.\n */\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmPayPalPayment(params);\n }\n\n /** Resume a PayPal payment after redirect return. Returns null if no PayPal params in URL. */\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n return this.provider.resumePayPalPayment();\n }\n\n /** Confirms a payment using the mounted elements. */\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n return this.provider.confirmPayment(params);\n }\n\n /**\n * Retrieves a checkout session by ID via the billing API.\n *\n * Returns the normalized `CheckoutSession` with amount, currency,\n * customer data, and status.\n *\n * Requires `billingApiUrl` to be set — either via `loadFloPay(key, { billingApiUrl })`\n * or passed directly as the second argument.\n */\n async retrieveSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<CheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required to retrieve a session.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n const unified = await api.getUnifiedCheckoutSession(sessionId);\n if (!unified.data.session) {\n throw new FloPayError('Session not found', 'api_error');\n }\n return unified.data.session;\n }\n\n /**\n * Retrieves and normalizes a checkout session, including provider-specific\n * data (Stripe clientSecret/publishableKey, Chargebee site, etc.).\n *\n * The billing API URL is resolved from: explicit param → `loadFloPay()` config\n * → `NEXT_PUBLIC_FLOPAY_ENV` env var → `configureFlopay()` → staging fallback.\n */\n async retrieveUnifiedSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<NormalizedCheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n return api.getUnifiedCheckoutSession(sessionId);\n }\n\n /**\n * Returns the raw underlying provider instance (e.g. Stripe object).\n * Used internally by components that need direct provider access,\n * such as PayPal which requires its own Elements instance.\n */\n getRawProvider(): unknown {\n return this.provider.getRawProvider();\n }\n\n /** Tears down the SDK instance and releases resources. */\n destroy(): void {\n this.currentElements?.destroy();\n this.currentElements = null;\n this.provider.destroy();\n }\n}\n","import type {\n PaymentProviderAdapter,\n ElementType,\n ElementOptions,\n MountedElement,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/**\n * Manages the creation and lifecycle of payment elements.\n *\n * Each `FloPayElements` instance is bound to a single provider adapter\n * and tracks all created elements for cleanup.\n */\nexport class FloPayElements {\n private readonly provider: PaymentProviderAdapter;\n private readonly elementMap = new Map<ElementType, MountedElement>();\n private readonly baseOptions: ElementOptions;\n\n constructor(provider: PaymentProviderAdapter, options?: ElementOptions) {\n this.provider = provider;\n this.baseOptions = options ?? {};\n }\n\n /**\n * Creates a new element of the given type.\n * If an element of that type already exists, it is destroyed first.\n */\n async create(\n type: ElementType,\n options?: ElementOptions,\n ): Promise<MountedElement> {\n // Check if the provider already has this element (e.g. after React\n // Strict Mode unmount+remount cycle). Reuse it instead of creating\n // a duplicate, which Stripe would reject.\n const providerExisting = this.provider.getElement(type);\n if (providerExisting) {\n this.elementMap.set(type, providerExisting);\n return providerExisting;\n }\n\n const existing = this.elementMap.get(type);\n if (existing) {\n existing.destroy();\n }\n\n const merged: ElementOptions = { ...this.baseOptions, ...options };\n const element = await this.provider.createElement(type, merged);\n this.elementMap.set(type, element);\n return element;\n }\n\n /** Returns a previously created element, or `null`. */\n getElement(type: ElementType): MountedElement | null {\n return this.elementMap.get(type) ?? null;\n }\n\n /**\n * Submits all mounted elements for validation.\n *\n * Returns an object with an optional error if validation fails.\n * This does NOT confirm the payment — call `floPay.confirmPayment()` for that.\n */\n async submit(): Promise<{ error?: FloPayError }> {\n // Walk through all mounted elements and check for incomplete state.\n // The actual validation is handled by the provider's element implementation.\n // For Stripe, the Elements.submit() method is called during confirmPayment.\n // This method exists to match the Stripe Elements API surface.\n return {};\n }\n\n /** Destroys all created elements and clears the internal map. */\n destroy(): void {\n for (const element of this.elementMap.values()) {\n element.destroy();\n }\n this.elementMap.clear();\n }\n}\n","import type {\n CheckoutGateways,\n CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n CheckoutSessionProduct,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayProduct,\n} from './session-display-cache.js';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API. */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n /** Unified products list returned by the billing API (post-#760). */\n products?: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** 'item' or 'subscription'. */\n type: 'item' | 'subscription';\n code?: string;\n name?: string | null;\n description?: string | null;\n quantity: number;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n /**\n * Pre-discount total in cart-currency major units. Populated by billing\n * API ≥ v1.1.2; absent on older backends.\n */\n subtotalAmount?: number;\n /** Total reduction from applied coupons (cart-currency major units). */\n discountAmount?: number;\n /** Final charge amount after coupon discount (cart-currency major units). */\n totalAmount?: number;\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gateways?: CheckoutGateways;\n accountData: {\n userId: string;\n firstName: string;\n lastName: string;\n email: string;\n gender?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n };\n tagsData: {\n googleContainerId?: string | null;\n sessionId?: string | null;\n testEventCode?: string | null;\n };\n}\n\ntype ApiErrorPayload = Record<string, unknown> | null;\n\nconst DEFAULT_PROCESSING_RETRY_AFTER_MS = 1_000;\nconst MIN_PROCESSING_RETRY_AFTER_MS = 500;\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 15_000;\nconst MAX_PROCESSING_RETRY_AFTER_MS = 3_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nfunction readNumber(payload: ApiErrorPayload, key: string): number | undefined {\n const value = payload?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createCheckoutProcessingTimeoutError(): FloPayError {\n return new FloPayError(\n 'Checkout is still processing. Please try again shortly.',\n 'api_error',\n { code: 'checkout_processing_timeout' },\n );\n}\n\nasync function buildApiErrorFromResponse(\n response: Response,\n fallbackMessage: string,\n): Promise<FloPayError> {\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const nestedError = isRecord(payload?.error) ? payload.error : null;\n const message =\n readString(payload, 'message') ??\n readString(nestedError, 'message') ??\n fallbackMessage;\n const code =\n readString(payload, 'code') ??\n readString(payload, 'gatewayErrorCode') ??\n readString(nestedError, 'code') ??\n `http_${response.status}`;\n\n return new FloPayError(message, 'api_error', {\n code,\n statusCode: response.status,\n });\n}\n\n/**\n * Extra attempts (beyond the first) for transient network failures.\n * Three total tries: immediate, +150ms, +300ms.\n */\nconst NETWORK_RETRY_ATTEMPTS = 2;\n\n/**\n * Run `fetch` with a bounded retry on transient network failures.\n *\n * `fetch` rejects — rather than resolving with a non-ok `Response` — only when\n * the request never produced an HTTP response at all: a dropped connection, a\n * DNS hiccup, or a failed CORS preflight, which all surface as\n * `TypeError: Failed to fetch`. Those blips are worth retrying. An `AbortError`\n * (caller-initiated cancellation) is not, and any HTTP-level failure resolves\n * normally and is left for the caller to handle via `response.ok`.\n *\n * Backoff is exponential (150ms, 300ms). Only use this for requests whose\n * replay is harmless — idempotent reads, or session bootstrap where a re-sent\n * create can at worst leave an unused draft session behind.\n */\nasync function fetchWithNetworkRetry(\n input: RequestInfo | URL,\n init?: RequestInit,\n attempts = NETWORK_RETRY_ATTEMPTS,\n): Promise<Response> {\n let lastErr: unknown;\n for (let attempt = 0; ; attempt++) {\n try {\n return await fetch(input, init);\n } catch (err) {\n // Caller-initiated cancellation must propagate immediately, never retry.\n if (err instanceof Error && err.name === 'AbortError') throw err;\n lastErr = err;\n if (attempt >= attempts) throw lastErr;\n await delay(150 * 2 ** attempt);\n }\n }\n}\n\n/**\n * Client-side payment API service.\n *\n * Mirrors the `PaymentAPI` class from the checkout project's\n * `src/service/api.ts`. All methods call the billing API endpoints\n * that the checkout backend exposes.\n */\nexport class PaymentAPI {\n private readonly baseUrl: string;\n\n constructor(billingApiUrl: string) {\n this.baseUrl = billingApiUrl.replace(/\\/+$/, '');\n }\n\n /**\n * Fetch a raw checkout session by ID.\n *\n * `nonce` is the session-bound checkout token returned when the session\n * was created. When supplied it is sent as the `x-checkout-session-token`\n * header that post-#640 backends match against `checkout_session.nonce`\n * before returning the row — the UUID alone is no longer sufficient.\n * Backends that don't yet enforce it ignore the extra header.\n */\n async getCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n const body = await response.json() as BillingResponse<RawCheckoutSession>;\n return { ...body, data: this.mergeCachedDisplayData(body.data) };\n }\n\n /**\n * Stash display-only data for a session so subsequent fetches can fill in\n * fields the backend no longer persists (`overrideAmount`, `totalAmount`,\n * `providerItemName`, `providerPlanName`).\n *\n * Backed by `sessionStorage` in the browser, with an in-memory fallback in\n * Node/SSR contexts. Default TTL: 1 hour.\n *\n * Server-returned values always win — cached values fill in only where the\n * server returned `null` / `undefined`.\n *\n * @example\n * ```ts\n * paymentAPI.cacheSessionDisplayData(sessionId, {\n * currency: 'USD',\n * items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],\n * });\n * ```\n */\n cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n ): void {\n cacheSessionDisplayData(sessionId, data, options);\n }\n\n /**\n * Drop any cached display data for a session. Call after the payment\n * completes; otherwise the TTL handles cleanup.\n */\n clearSessionDisplayData(sessionId: string): void {\n clearSessionDisplayData(sessionId);\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateways` map to enumerate provider-specific data,\n * then wraps the session in a `NormalizedCheckoutSession` for provider-\n * agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId, nonce);\n return this.normalizeRawSession(res.data);\n }\n\n /**\n * Submit a tokenized payment to the billing backend.\n *\n * The backend will either succeed, return `type: '3ds_required'`\n * (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.\n *\n * Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`\n * and forwards `data.nonce` as `x-checkout-session-token`. Backend\n * `TeamFloPay/backend#640` rejects callers without a matching nonce with a\n * 401 — this method throws synchronously when `data.nonce` is missing so the\n * problem surfaces before the network round trip.\n *\n * @param userId Vestigial — backend's GatewayInterceptor routes via session,\n * not headers, so this value is no longer sent on the wire. Kept in the\n * signature for back-compat with existing callers; will be removed in a\n * future major version.\n */\n async processPayment(\n _userId: string,\n data: ProcessPaymentParams,\n options?: {\n pollTimeoutMs?: number;\n },\n ): Promise<Response> {\n if (!data.nonce) {\n throw new FloPayError(\n 'processPayment requires `nonce` — pass the value returned from session creation.',\n 'validation_error',\n { code: 'MissingCheckoutSessionToken', param: 'nonce' },\n );\n }\n\n const { nonce, ...processBody } = data;\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(data.sessionId)}/process`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(processBody),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, { ...options, nonce });\n }\n\n /**\n * Create a PaymentIntent on the backend.\n *\n * Used by the Stripe flow to create a server-side PaymentIntent\n * with the client's payment method attached.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied — the\n * session-bound checkout token returned by session creation. Post-#640\n * backends reject this call with a 401 when the header is missing or does\n * not match the session's stored nonce.\n */\n async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\n isPaypal: options?.isPaypal ?? false,\n }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Create a SetupIntent for saving payment methods.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied —\n * required by post-#640 backends, ignored by earlier versions.\n */\n async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ sessionId, email, paymentMethodType }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Fetch user's prior payments by email.\n * Used to determine if saved card UX should be shown.\n */\n async getPaymentsByEmail(\n email: string,\n options?: { signal?: AbortSignal; page?: number; limit?: number },\n ): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }> {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 1;\n const params = new URLSearchParams({\n email,\n page: String(page),\n limit: String(limit),\n sortField: 'occurredAt',\n sortDirection: 'DESC',\n });\n\n const response = await fetch(\n `${this.baseUrl}/v1/payments?${params.toString()}`,\n {\n method: 'GET',\n signal: options?.signal,\n keepalive: true,\n },\n );\n\n if (!response.ok) {\n throw new FloPayError('Failed to fetch payments', 'api_error');\n }\n\n return response.json() as Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>;\n }\n\n /**\n * Create a checkout session AND return the full session data in one call.\n * Uses `?expand=true` so the backend returns the complete session\n * instead of just a UUID — eliminating the need for a second GET.\n *\n * Falls back to create + GET if the backend doesn't support `expand`.\n */\n async createAndFetchSession(\n params: InlineSessionDraft,\n ): Promise<NormalizedCheckoutSession> {\n const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n wireProducts,\n );\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n checkoutVersion: SDK_VERSION,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: params.account.userId,\n firstName: params.account.firstName ?? null,\n lastName: params.account.lastName ?? null,\n email: params.account.email,\n country: params.account.country ?? null,\n gender: params.account.gender ?? null,\n city: params.account.city ?? null,\n state: params.account.state ?? null,\n zip: params.account.zip ?? null,\n addressLine1: params.account.addressLine1 ?? null,\n addressLine2: params.account.addressLine2 ?? null,\n },\n couponCodes: params.couponCodes ?? [],\n };\n\n if (params.tokenizedData) payload['tokenizedData'] = params.tokenizedData;\n if (params.tagsData) payload['tagsData'] = params.tagsData;\n if (params.utmMetadata?.length) payload['utmMetadata'] = params.utmMetadata;\n\n // Checkout analytics metadata\n if (params.avsCheck !== undefined) payload['avsCheck'] = params.avsCheck;\n if (params.checkoutType) payload['checkoutType'] = params.checkoutType;\n if (params.checkoutLayout) payload['checkoutLayout'] = params.checkoutLayout;\n if (params.avsConfig) payload['avsConfig'] = params.avsConfig;\n\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n );\n\n if (response.status === 204) {\n throw new FloPayError(\n 'Session auto-completed — payment method already on file',\n 'api_error',\n { code: 'session_auto_completed' },\n );\n }\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to create checkout session');\n }\n\n const body = await response.json() as {\n data: RawCheckoutSession & { uuid?: string };\n autoProcessingError?: CheckoutProcessError;\n autoProcessingAttempted?: boolean;\n autoProcessingPending?: CheckoutProcessingPending;\n };\n\n // If backend supports expand=true, it returns full session data.\n // If not, it returns { data: { uuid } } and we fall back to a GET.\n if (body.data && 'gateways' in body.data) {\n // Auto-stash display-only fields the consumer just sent so wallet\n // sheets and the post-redirect fetch see the same totals. Must run\n // BEFORE the merge so the merge picks them up.\n this.autoCacheDisplayData(body.data.uuid, params);\n const merged = this.mergeCachedDisplayData(body.data);\n return {\n ...this.normalizeRawSession(merged),\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n // Fallback: backend returned UUID only — fetch full session\n const uuid = body.data?.uuid;\n if (!uuid) {\n throw new FloPayError('No session ID returned', 'api_error');\n }\n this.autoCacheDisplayData(uuid, params);\n const unifiedSession = await this.getUnifiedCheckoutSession(uuid);\n return {\n ...unifiedSession,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n async waitForCheckoutSessionCompletion(\n checkoutSessionId: string,\n options?: {\n initialDelayMs?: number;\n timeoutMs?: number;\n /**\n * Session-bound checkout token; forwarded on the poll's\n * `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.\n */\n nonce?: string;\n },\n ): Promise<NormalizedCheckoutSession> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_PROCESSING_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n let nextDelayMs = this.clampRetryAfterMs(options?.initialDelayMs ?? DEFAULT_PROCESSING_RETRY_AFTER_MS);\n\n while (true) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n if (nextDelayMs > 0) {\n await delay(Math.min(nextDelayMs, remainingMs));\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n }\n\n const session = await this.getUnifiedCheckoutSession(checkoutSessionId, options?.nonce);\n const status = session.data.session?.status;\n\n if (status === 'complete' || status === 'expired') {\n return session;\n }\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n nextDelayMs = this.clampRetryAfterMs(\n Math.max(nextDelayMs * 2, MIN_PROCESSING_RETRY_AFTER_MS),\n );\n }\n }\n\n /** Normalize a raw session into a provider-agnostic shape. */\n private normalizeRawSession(session: RawCheckoutSession): NormalizedCheckoutSession {\n const gateways: CheckoutGateways = session.gateways ?? {};\n const providers: BillingProvider[] = [];\n const data: NormalizedCheckoutSession['data'] = {\n session: this.toCheckoutSession(session),\n };\n\n const stripeGateway = gateways.stripe;\n if (stripeGateway?.publishableKey) {\n providers.push('stripe');\n const rawSession = session as unknown as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n stripeGateway.stripeClientSecret,\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n data.stripe = {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: stripeGateway.publishableKey ?? undefined,\n paypalPublishableKey: stripeGateway.paypalPublishableKey ?? undefined,\n environment: stripeGateway.environment,\n enabledPaymentMethods: Array.isArray(stripeGateway.enabledPaymentMethods)\n ? stripeGateway.enabledPaymentMethods.filter((m): m is string => typeof m === 'string')\n : undefined,\n };\n }\n\n const paypalGateway = gateways.paypal;\n if (paypalGateway?.publishableKey) {\n providers.push('paypal');\n data.paypal = {\n publishableKey: paypalGateway.publishableKey,\n environment: paypalGateway.environment,\n };\n }\n\n return {\n providers,\n mode: 'tokenize',\n data,\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const rawProducts = raw.products ?? [];\n\n // Billing API ≥ v1.1.2 returns the coupon-adjusted `totalAmount` on the\n // session. Prefer it as the source of truth so wallet sheets render the\n // post-coupon amount; fall back to summing per-line `overrideAmount`.\n const hasBackendTotal =\n typeof raw.totalAmount === 'number' && Number.isFinite(raw.totalAmount);\n const computedTotal = rawProducts.reduce(\n (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),\n 0,\n );\n const totalAmount = hasBackendTotal ? (raw.totalAmount as number) : computedTotal;\n\n // Billing API returns amounts in major units (e.g. 24.95 USD).\n // Stripe expects amounts in smallest currency unit (cents).\n const amountInCents = Math.round(totalAmount * 100);\n\n const currency = raw.currency ?? rawProducts[0]?.currency ?? 'USD';\n\n const mode: CheckoutSession['mode'] =\n rawProducts.some((p) => p.type === 'subscription') ? 'subscription' : 'payment';\n\n return {\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode,\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency,\n customer: {\n id: raw.accountData.userId,\n email: raw.accountData.email,\n firstName: raw.accountData.firstName,\n lastName: raw.accountData.lastName,\n country: raw.accountData.country ?? undefined,\n city: raw.accountData.city ?? undefined,\n state: raw.accountData.state ?? undefined,\n zip: raw.accountData.zip ?? undefined,\n gender: raw.accountData.gender ?? undefined,\n line1: raw.accountData.addressLine1 ?? undefined,\n line2: raw.accountData.addressLine2 ?? undefined,\n },\n metadata: {},\n\n checkoutMode: raw.checkoutMode,\n products: rawProducts.map((p): CheckoutSessionProduct => ({\n ...p,\n totalAmount: typeof p.totalAmount === 'number' ? p.totalAmount : undefined,\n overrideAmount: typeof p.overrideAmount === 'number' ? p.overrideAmount : null,\n currency: typeof p.currency === 'string' ? p.currency : undefined,\n metadata: p.metadata ?? null,\n })),\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n subtotalAmount: raw.subtotalAmount,\n discountAmount: raw.discountAmount,\n totalAmount: raw.totalAmount,\n createdAt: raw.createdAt,\n gateways: raw.gateways,\n accountData: raw.accountData,\n tagsData: raw.tagsData,\n };\n }\n\n private toCheckoutSessionStatus(status: RawCheckoutSession['status']): CheckoutSession['status'] {\n if (status === 'completed') {\n return 'complete';\n }\n\n if (status === 'expired') {\n return 'expired';\n }\n\n return 'open';\n }\n\n private async resolveProcessResponse(\n response: Response,\n checkoutSessionId: string,\n options?: {\n pollTimeoutMs?: number;\n nonce?: string;\n },\n ): Promise<Response> {\n if (response.status !== 202) {\n return response;\n }\n\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const pending = this.toCheckoutProcessingPending(payload, response, checkoutSessionId);\n const session = await this.waitForCheckoutSessionCompletion(pending.sessionId, {\n initialDelayMs: pending.retryAfterMs,\n timeoutMs: options?.pollTimeoutMs,\n nonce: options?.nonce,\n });\n\n if (session.data.session?.status === 'complete') {\n return new Response(null, { status: 204, statusText: 'No Content' });\n }\n\n if (session.data.session?.status === 'expired') {\n throw new FloPayError(\n 'Checkout session has expired.',\n 'api_error',\n { code: 'checkout_session_expired' },\n );\n }\n\n throw createCheckoutProcessingTimeoutError();\n }\n\n private toCheckoutProcessingPending(\n payload: ApiErrorPayload,\n response: Response,\n checkoutSessionId: string,\n ): CheckoutProcessingPending {\n const retryAfterHeader = response.headers.get('Retry-After');\n const headerRetryAfterSeconds = retryAfterHeader === null\n || retryAfterHeader.trim() === ''\n ? undefined\n : Number(retryAfterHeader);\n const headerRetryAfterMs = headerRetryAfterSeconds !== undefined\n && Number.isFinite(headerRetryAfterSeconds)\n ? headerRetryAfterSeconds * 1_000\n : undefined;\n\n return {\n type: 'checkout_processing',\n sessionId: readString(payload, 'sessionId') ?? checkoutSessionId,\n retryAfterMs: this.clampRetryAfterMs(\n readNumber(payload, 'retryAfterMs')\n ?? headerRetryAfterMs\n ?? DEFAULT_PROCESSING_RETRY_AFTER_MS,\n ),\n statusUrl: readString(payload, 'statusUrl'),\n sessionUrl: readString(payload, 'sessionUrl'),\n };\n }\n\n private clampRetryAfterMs(retryAfterMs: number): number {\n return Math.max(0, Math.min(retryAfterMs, MAX_PROCESSING_RETRY_AFTER_MS));\n }\n\n /**\n * Stash the display-only fields the consumer passed into a create-session\n * call. Runs after the backend assigns a UUID so a later GET on the same\n * session (typically after a redirect) can fill in fields the backend no\n * longer persists — `overrideAmount`, `totalAmount`, `name`, etc.\n *\n * No-op when no UUID is available.\n */\n private autoCacheDisplayData(\n sessionId: string | undefined,\n params: InlineSessionDraft,\n ): void {\n if (!sessionId) return;\n const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n if (products.length === 0 && !params.currency) {\n return;\n }\n const usingUnifiedProducts = params.products !== undefined;\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n usingUnifiedProducts ? undefined : params.items,\n usingUnifiedProducts ? undefined : params.subscriptions,\n products,\n );\n cacheSessionDisplayData(sessionId, {\n currency: sessionCurrency ?? undefined,\n products: products.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency ?? undefined,\n })),\n });\n }\n\n /**\n * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response. Server values always win — cache fills in\n * only where the server returned `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedProducts = new Map<string, SessionDisplayProduct>();\n const productKey = (\n type: SessionDisplayProduct['type'],\n code: string | undefined,\n ): string | undefined => (code && type ? `${type}:${code}` : undefined);\n for (const p of cached?.products ?? []) {\n const key = productKey(p.type, p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.type, p.code);\n const fallback = key ? cachedProducts.get(key) : undefined;\n return {\n ...p,\n name: p.name ?? fallback?.name ?? null,\n totalAmount: p.totalAmount ?? fallback?.totalAmount,\n overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,\n currency: p.currency ?? fallback?.currency,\n };\n });\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n products: mergedProducts,\n };\n }\n}\n","/**\n * Client-side cache for display-only checkout fields the backend no longer\n * persists (`overrideAmount`, `totalAmount`, `providerItemName`,\n * `providerPlanName`, per-line `currency`).\n *\n * The cache lives in `sessionStorage` so it survives the navigation from the\n * page that creates the session to the checkout page that fetches it, but\n * dies on tab close. An in-memory fallback keeps the SDK working in Node /\n * SSR contexts where `sessionStorage` is unavailable.\n *\n * Values from the server response always win — cached values fill in only\n * where the server returned `null` or `undefined`.\n */\n\nconst STORAGE_KEY_PREFIX = 'flopay_session_display:';\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/** Display-only fields per product that can be cached and merged back later. */\nexport interface SessionDisplayProduct {\n /** Catalog code (match key). */\n code?: string;\n /** Whether this product is a one-time item or a recurring subscription. */\n type?: 'item' | 'subscription';\n /** Display-only name for the product. */\n name?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only payload that can be stashed for later merge into a session response. */\nexport interface SessionDisplayCacheData {\n /** Session-level currency (falls into the response only when the server omits it). */\n currency?: string;\n products?: SessionDisplayProduct[];\n}\n\ninterface CachedEntry {\n data: SessionDisplayCacheData;\n expiresAt: number;\n}\n\nconst memoryStore = new Map<string, CachedEntry>();\n\nfunction storageKey(sessionId: string): string {\n return `${STORAGE_KEY_PREFIX}${sessionId}`;\n}\n\nfunction getSessionStorage(): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Stash display-only data for a session. Called client-side right after the\n * server returns a session ID, so the values survive the redirect to the\n * checkout page.\n */\nexport function cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n): void {\n if (!sessionId) return;\n\n const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;\n const entry: CachedEntry = { data, expiresAt: Date.now() + ttl };\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.setItem(storageKey(sessionId), JSON.stringify(entry));\n return;\n } catch {\n // Quota / serialization error — fall through to in-memory store\n }\n }\n\n memoryStore.set(sessionId, entry);\n}\n\n/**\n * Read previously-cached display data for a session, or `null` if nothing\n * is cached (or the TTL has elapsed).\n */\nexport function getSessionDisplayData(\n sessionId: string,\n): SessionDisplayCacheData | null {\n if (!sessionId) return null;\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n const raw = storage.getItem(storageKey(sessionId));\n if (raw) {\n const entry = JSON.parse(raw) as CachedEntry;\n if (entry && typeof entry.expiresAt === 'number' && entry.expiresAt > Date.now()) {\n return entry.data;\n }\n storage.removeItem(storageKey(sessionId));\n }\n } catch {\n // Malformed entry — fall through to memory store\n }\n }\n\n const memEntry = memoryStore.get(sessionId);\n if (memEntry) {\n if (memEntry.expiresAt > Date.now()) {\n return memEntry.data;\n }\n memoryStore.delete(sessionId);\n }\n\n return null;\n}\n\n/**\n * Drop any cached display data for a session. Call from the success page\n * after the payment completes; otherwise the TTL handles cleanup.\n */\nexport function clearSessionDisplayData(sessionId: string): void {\n if (!sessionId) return;\n\n memoryStore.delete(sessionId);\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.removeItem(storageKey(sessionId));\n } catch {\n // ignore\n }\n }\n}\n","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport { cacheSessionDisplayData } from './session-display-cache.js';\n\n/**\n * Max number of coupon codes the billing API accepts per session create.\n * Mirrors the backend guard introduced in billing v1.1.2 — exceeding this\n * returns the `CouponLimitExceeded` error.\n */\nconst MAX_COUPON_CODES = 5;\n\ninterface ApiErrorBody {\n code?: unknown;\n message?: unknown;\n error?: { code?: unknown; message?: unknown };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Map a billing API error body to a `FloPayError`. Surfaces the coupon\n * validation codes introduced in billing v1.1.2 (`CouponLimitExceeded`,\n * `CouponCurrencyUnsupported`) as structured errors so callers can branch\n * on `error.code` instead of parsing the HTTP status.\n */\nfunction buildCheckoutSessionError(status: number, payload: ApiErrorBody | undefined): FloPayError {\n const nested = payload?.error;\n const code =\n readString(payload?.code)\n ?? readString(nested?.code)\n ?? `http_${status}`;\n const message =\n readString(payload?.message)\n ?? readString(nested?.message)\n ?? defaultMessageForCode(code, status);\n\n return new FloPayError(message, 'api_error', { code, statusCode: status });\n}\n\nfunction defaultMessageForCode(code: string, status: number): string {\n switch (code) {\n case 'CouponLimitExceeded':\n return `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`;\n case 'CouponCurrencyUnsupported':\n return 'One of the applied coupons has no price configured for the cart currency.';\n default:\n return `Failed to create checkout session (HTTP ${status}).`;\n }\n}\n\n/**\n * Creates a checkout session via the billing API and redirects the user\n * to the hosted checkout page.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n *\n * - On 201: Redirects to `{checkoutBaseUrl}/secure?id={uuid}&...redirectParams`\n * - On 204: Redirects directly to `successUrl` (payment method already on file)\n * - On other: Returns `{ status }` without redirecting\n *\n * @example\n * ```ts\n * import { createCheckoutSession } from '@flopay/js';\n *\n * await createCheckoutSession({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * currency: 'USD',\n * items: [{\n * code: 'initial_charge',\n * quantity: 1,\n * metadata: { source: 'web' },\n * }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * redirectParams: { email: 'user@example.com', bg: 'courses', mode: 'confirm' },\n * });\n * ```\n */\nexport async function createCheckoutSession(\n options: CreateSessionParams,\n): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n products,\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n currency,\n utmMetadata,\n } = options;\n\n if (couponCodes.length > MAX_COUPON_CODES) {\n throw new FloPayError(\n `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`,\n 'validation_error',\n { code: 'CouponLimitExceeded', param: 'couponCodes' },\n );\n }\n\n const wireProducts = products ?? foldIntoProducts(items, subscriptions);\n const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions, wireProducts);\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId,\n checkoutVersion: SDK_VERSION,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n addressLine1: account.addressLine1 ?? null,\n addressLine2: account.addressLine2 ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: ({ data?: { uuid?: string; nonce?: string } } & ApiErrorBody) | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string; nonce?: string } } & ApiErrorBody;\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status >= 400) {\n throw buildCheckoutSessionError(status, body);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n const nonce = body?.data?.nonce;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n if (!nonce) {\n throw new FloPayError(\n 'Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.',\n 'api_error',\n { code: 'MissingCheckoutSessionToken' },\n );\n }\n\n // Auto-stash display-only fields the consumer passed in so the checkout\n // page (post-redirect) renders the right totals/names even though the\n // backend no longer persists them. Runs before the redirect — values\n // survive the navigation via sessionStorage.\n if (wireProducts.length || sessionCurrency) {\n cacheSessionDisplayData(uuid, {\n currency: sessionCurrency,\n products: wireProducts.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency,\n })),\n });\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n if (setCookie && typeof window !== 'undefined' && typeof document !== 'undefined') {\n const checkoutData = JSON.stringify({ origin_url: cancelUrl });\n const domain = window.location.hostname.split('.').slice(-2).join('.');\n document.cookie = `checkout_data=${encodeURIComponent(checkoutData)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n // Carry the session-bound checkout token to the hosted checkout page so\n // it can echo `x-checkout-session-token` on every continuation call\n // (post-#640 backends require this header to match the session nonce).\n // The URL is deliberately not modified — keeping the nonce out of the\n // location bar avoids history / Referer leakage.\n document.cookie = `flopay_checkout_token=${encodeURIComponent(nonce)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString(), nonce };\n }\n\n if (status === 204) {\n if (typeof window !== 'undefined') {\n window.location.href = successUrl;\n }\n return { status: 204 };\n }\n\n return { status };\n}\n\n/**\n * Creates a checkout session with automatic retry on timeout/abort errors.\n *\n * Uses exponential backoff: 100ms, 200ms, 400ms, etc.\n */\nexport async function createCheckoutSessionWithRetries(\n options: CreateSessionParams & { maxRetries?: number },\n): Promise<CheckoutSessionResult> {\n const { maxRetries = 3, ...sessionOptions } = options;\n\n if (!Number.isFinite(maxRetries) || !Number.isInteger(maxRetries) || maxRetries <= 0) {\n throw new Error('Number of retries must be greater than 0');\n }\n\n let lastErr: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await createCheckoutSession(sessionOptions);\n } catch (err) {\n lastErr = err;\n\n if (err instanceof Error && err.name === 'AbortError' && attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastErr ?? new Error('Unknown error during checkout session creation');\n}\n"],"mappings":"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","names":["FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","SDK_VERSION","buildProductPayload","foldIntoProducts","resolveSessionCurrency","readString","FloPayError","foldIntoProducts","resolveSessionCurrency","SDK_VERSION","buildProductPayload"]}
|
|
1
|
+
{"version":3,"sources":["../src/load.ts","../src/stripe-adapter.ts","../src/flopay.ts","../src/elements.ts","../src/payment-api.ts","../src/session-display-cache.ts","../src/create-checkout-session.ts"],"sourcesContent":["import type { FloPayConfig } from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\nimport { StripeAdapter } from './stripe-adapter.js';\nimport { FloPay } from './flopay.js';\n\n/**\n * Cache of FloPay instances keyed by publishable key. Multiple instances can\n * coexist when a consumer drives more than one Stripe publishable key during a\n * single page-load.\n */\nconst instanceCache = new Map<string, FloPay>();\n\n/**\n * Loads and initializes the FloPay SDK.\n *\n * This is the primary entry point for consumer applications.\n * It initializes the underlying payment provider (currently Stripe)\n * and returns a ready-to-use `FloPay` instance.\n *\n * Instances are cached per `publishableKey`, so calling `loadFloPay` twice\n * with the same key returns the same instance. Different keys produce\n * independent instances that can run side-by-side.\n *\n * @example\n * ```ts\n * import { loadFloPay } from '@flopay/js';\n *\n * const flopay = await loadFloPay('pk_test_...');\n * const elements = flopay.elements();\n * const cardElement = await elements.create('card');\n * cardElement.mount('#card-container');\n * ```\n */\nexport async function loadFloPay(\n publishableKey: string,\n options?: Omit<FloPayConfig, 'publishableKey'>,\n): Promise<FloPay> {\n if (!publishableKey) {\n throw new FloPayError(\n 'A publishable key is required to initialize FloPay.',\n 'validation_error',\n { param: 'publishableKey' },\n );\n }\n\n const cached = instanceCache.get(publishableKey);\n if (cached) return cached;\n\n const config: FloPayConfig = {\n publishableKey,\n ...options,\n };\n\n // Stripe is the only adapter today. Direct PayPal does not flow through\n // this loader — it renders via the official PayPal JS SDK from\n // `DirectPayPalButton`.\n const adapter = new StripeAdapter();\n await adapter.initialize(config);\n\n const instance = new FloPay(adapter, config);\n instanceCache.set(publishableKey, instance);\n\n return instance;\n}\n","import type {Stripe, StripeElements, StripeElementType} from '@stripe/stripe-js';\nimport type {\n BillingDetails,\n ConfirmCardPaymentParams,\n ConfirmCardPaymentResult,\n ConfirmPaymentParams,\n CreatePaymentMethodResult,\n ElementOptions,\n ElementType,\n FloPayConfig,\n MountedElement,\n PaymentProviderAdapter,\n PaymentResult,\n} from '@flopay/shared';\nimport {FloPayError} from '@flopay/shared';\n\n/**\n * Maps FloPay element types to Stripe element types.\n * FloPay uses camelCase; Stripe uses lowercase with hyphens or specific names.\n */\nfunction toStripeElementType(type: ElementType): StripeElementType {\n const map: Record<ElementType, string> = {\n payment: 'payment',\n card: 'card',\n cardNumber: 'cardNumber',\n cardExpiry: 'cardExpiry',\n cardCvc: 'cardCvc',\n address: 'address',\n };\n return map[type] as StripeElementType;\n}\n\n/**\n * Wraps a Stripe element instance in the FloPay MountedElement interface.\n *\n * TODO: In a future phase, this wrapper will render the Stripe element inside\n * an iframe hosted on the FloPay domain for PCI DSS SAQ-A compliance.\n * For now, it delegates directly to the Stripe element.\n */\n/** Minimal interface shared by all Stripe element types. */\ninterface StripeElementLike {\n mount(domElement: string | HTMLElement): void;\n unmount(): void;\n destroy(): void;\n update(options?: Record<string, unknown>): void;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler?: (...args: unknown[]) => void): void;\n}\n\n/**\n * Maps a `FloPayAppearance.theme` ('default' | 'flat' | 'night' | 'none') to a\n * Stripe Elements Appearance `theme` ('stripe' | 'flat' | 'night'). Stripe's\n * Appearance API only accepts those three; anything else triggers a console\n * warning and silently falls back. We normalize here so the bundles can keep\n * `'default'` as their public token.\n */\nfunction toStripeAppearanceTheme(\n theme: 'default' | 'flat' | 'night' | 'none' | undefined,\n): 'stripe' | 'night' | 'flat' {\n switch (theme) {\n case 'night': return 'night';\n case 'flat': return 'flat';\n // 'default', 'none', undefined, or any unexpected value → Stripe's baseline.\n default: return 'stripe';\n }\n}\n\nfunction wrapStripeElement(\n stripeElement: StripeElementLike,\n): MountedElement {\n const el = stripeElement;\n\n return {\n mount(container: HTMLElement) {\n el.mount(container);\n },\n unmount() {\n el.unmount();\n },\n update(options: Partial<ElementOptions>) {\n el.update(options as Record<string, unknown>);\n },\n on(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['on']?.(event, handler);\n },\n off(event: string, handler: (...args: unknown[]) => void) {\n (el as unknown as Record<string, (...args: unknown[]) => void>)['off']?.(event, handler);\n },\n destroy() {\n el.destroy();\n },\n };\n}\n\n/**\n * Payment provider adapter backed by Stripe.\n *\n * Implements the `PaymentProviderAdapter` interface so that FloPay consumers\n * interact with a stable API regardless of the upstream provider.\n */\nexport class StripeAdapter implements PaymentProviderAdapter {\n readonly name = 'stripe';\n private stripe: Stripe | null = null;\n private elements: StripeElements | null = null;\n // Serialized appearance currently applied to `this.elements`. Used to detect\n // when consumers swap themes mid-session so we can live-update the Stripe\n // Elements group instead of returning a stale-styled cache. `null` while no\n // elements group exists.\n private appliedAppearanceKey: string | null = null;\n\n async initialize(config: FloPayConfig): Promise<void> {\n // loadStripe only works in the browser — skip initialization during SSR/SSG\n if (typeof window === 'undefined') {\n return;\n }\n\n let loadStripe: typeof import('@stripe/stripe-js').loadStripe;\n try {\n ({ loadStripe } = await import('@stripe/stripe-js'));\n } catch {\n // @stripe/stripe-js ships as a direct dependency of @flopay/js, so this\n // should never resolve to a missing module in practice. If it does (e.g.\n // a broken install tree), surface a clear FloPayError instead of an\n // unhandled module-resolution rejection.\n throw new FloPayError(\n 'Failed to load @stripe/stripe-js. Reinstall @flopay/js.',\n 'api_error',\n { code: 'StripeJsLoadFailed' },\n );\n }\n\n const stripe = await loadStripe(config.publishableKey, {\n locale: (config.locale as 'auto') ?? 'auto',\n });\n\n if (!stripe) {\n throw new FloPayError(\n 'Failed to initialize Stripe. Check your publishable key.',\n 'authentication_error',\n );\n }\n\n this.stripe = stripe;\n }\n\n /** Lazily creates the Stripe Elements group for the given options. */\n private getElements(options?: ElementOptions): StripeElements {\n if (!this.stripe) {\n throw new FloPayError(\n 'StripeAdapter not initialized. Call initialize() first.',\n 'api_error',\n );\n }\n\n const stripeAppearance = options?.appearance\n ? {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n }\n : undefined;\n const nextAppearanceKey = stripeAppearance ? JSON.stringify(stripeAppearance) : null;\n\n if (!this.elements) {\n let elementsOptions: Record<string, unknown>;\n\n if (options?.clientSecret) {\n // Server-side intent already exists — pass its client secret\n elementsOptions = { clientSecret: options.clientSecret };\n } else {\n // No server-side intent yet — use deferred mode with manual payment method creation.\n // This matches checkout/StripeCardForm: mode='payment', paymentMethodCreation='manual'.\n elementsOptions = {\n mode: 'payment' as const,\n amount: options?.amount ?? 0,\n currency: (options?.currency ?? 'usd').toLowerCase(),\n paymentMethodCreation: options?.paymentMethodCreation ?? ('manual' as const),\n };\n\n if (options?.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n }\n\n if (stripeAppearance) {\n elementsOptions['appearance'] = stripeAppearance;\n }\n\n this.elements = this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n this.appliedAppearanceKey = nextAppearanceKey;\n } else if (nextAppearanceKey !== this.appliedAppearanceKey) {\n // Elements group already exists but the consumer changed (or removed)\n // the appearance. Stripe Elements supports a live `update({ appearance })`\n // call which re-styles mounted elements in place — no re-create, no\n // input-state loss. When the consumer drops `appearance`, fall through\n // with an empty payload so any prior variables/rules are cleared.\n (this.elements as unknown as { update: (opts: Record<string, unknown>) => void }).update({\n appearance: stripeAppearance ?? {},\n });\n this.appliedAppearanceKey = nextAppearanceKey;\n }\n\n return this.elements;\n }\n\n async createElement(\n type: ElementType,\n options: ElementOptions,\n ): Promise<MountedElement> {\n const elements = this.getElements(options);\n const stripeType = toStripeElementType(type);\n\n const elementOptions: Record<string, unknown> = {};\n if (options.layout) {\n elementOptions['layout'] = options.layout;\n }\n if (options.defaultValues) {\n elementOptions['defaultValues'] = options.defaultValues;\n }\n if (options.readOnly) {\n elementOptions['readOnly'] = options.readOnly;\n }\n if (options.mode) {\n elementOptions['mode'] = options.mode;\n }\n if (options.style) {\n elementOptions['style'] = options.style;\n }\n\n const stripeElement = elements.create(stripeType as 'card', elementOptions) as unknown as StripeElementLike;\n return wrapStripeElement(stripeElement);\n }\n\n getElement(type: ElementType): MountedElement | null {\n if (!this.elements) return null;\n const stripeType = toStripeElementType(type);\n const existing = this.elements.getElement(stripeType as 'card') as unknown as StripeElementLike | null;\n if (!existing) return null;\n return wrapStripeElement(existing);\n }\n\n async submitElements(): Promise<{ error?: FloPayError }> {\n if (!this.stripe || !this.elements) {\n return { error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n const { error } = await this.elements.submit();\n if (error) {\n return {\n error: new FloPayError(error.message ?? 'Validation failed', 'validation_error'),\n };\n }\n return {};\n }\n\n async createPaymentMethod(billingDetails?: BillingDetails): Promise<CreatePaymentMethodResult> {\n if (!this.stripe || !this.elements) {\n return {\n paymentMethodId: null,\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n // Detect split card fields vs unified PaymentElement.\n // With split fields (cardNumber + cardExpiry + cardCvc), Stripe requires\n // passing the specific cardNumber element with type: 'card'.\n // With unified PaymentElement, pass the elements group.\n const cardNumberEl = this.elements.getElement('cardNumber' as 'card');\n\n // Build billing_details for Stripe AVS (Address Verification).\n // When present, Stripe runs postal code and address checks automatically.\n const stripeBilling = billingDetails ? {\n billing_details: {\n ...(billingDetails.email ? { email: billingDetails.email } : {}),\n ...(billingDetails.name ? { name: billingDetails.name } : {}),\n ...(billingDetails.address ? {\n address: {\n ...(billingDetails.address.country ? { country: billingDetails.address.country } : {}),\n ...(billingDetails.address.postal_code ? { postal_code: billingDetails.address.postal_code } : {}),\n ...(billingDetails.address.city ? { city: billingDetails.address.city } : {}),\n ...(billingDetails.address.line1 ? { line1: billingDetails.address.line1 } : {}),\n ...(billingDetails.address.line2 ? { line2: billingDetails.address.line2 } : {}),\n ...(billingDetails.address.state ? { state: billingDetails.address.state } : {}),\n },\n } : {}),\n },\n } : {};\n\n const { error, paymentMethod } = cardNumberEl\n ? await this.stripe.createPaymentMethod({\n type: 'card',\n card: cardNumberEl,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0])\n : await this.stripe.createPaymentMethod({\n elements: this.elements,\n ...stripeBilling,\n } as unknown as Parameters<typeof this.stripe.createPaymentMethod>[0]);\n\n if (error) {\n return {\n paymentMethodId: null,\n error: new FloPayError(\n error.message ?? 'Failed to create payment method',\n 'api_error',\n { code: error.code },\n ),\n };\n }\n\n return { paymentMethodId: paymentMethod.id };\n }\n\n async confirmCardPayment(\n params: ConfirmCardPaymentParams,\n ): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return {\n status: 'failed',\n error: new FloPayError('Stripe not initialized', 'api_error'),\n };\n }\n\n const { error, paymentIntent } = await this.stripe.confirmCardPayment(\n params.clientSecret,\n { payment_method: params.paymentMethodId },\n );\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n { code: error.code, declineCode: error.decline_code },\n ),\n };\n }\n\n return {\n status: (paymentIntent?.status as ConfirmCardPaymentResult['status']) ?? 'failed',\n paymentIntentId: paymentIntent?.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent?.payment_method),\n };\n }\n\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n if (!this.stripe || !this.elements) {\n throw new FloPayError(\n 'StripeAdapter not initialized or no elements created.',\n 'api_error',\n );\n }\n\n const billing = params.billingDetails;\n const paymentMethodData = billing ? {\n billing_details: {\n ...(billing.email ? { email: billing.email } : {}),\n ...(billing.name ? { name: billing.name } : {}),\n ...(billing.address ? {\n address: {\n ...(billing.address.country ? { country: billing.address.country } : {}),\n ...(billing.address.postal_code ? { postal_code: billing.address.postal_code } : {}),\n ...(billing.address.city ? { city: billing.address.city } : {}),\n ...(billing.address.line1 ? { line1: billing.address.line1 } : {}),\n ...(billing.address.line2 ? { line2: billing.address.line2 } : {}),\n ...(billing.address.state ? { state: billing.address.state } : {}),\n },\n } : {}),\n },\n } : undefined;\n\n const { error, paymentIntent } = await this.stripe.confirmPayment({\n elements: this.elements,\n clientSecret: params.clientSecret,\n confirmParams: {\n return_url: params.returnUrl ?? window.location.href,\n ...(paymentMethodData ? { payment_method_data: paymentMethodData } : {}),\n },\n redirect: 'if_required',\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(\n error.message ?? 'Payment failed',\n 'api_error',\n {\n code: error.code,\n declineCode: error.decline_code,\n },\n ),\n };\n }\n\n if (!paymentIntent) {\n return { status: 'failed', error: new FloPayError('No payment intent returned', 'api_error') };\n }\n\n const statusMap: Record<string, PaymentResult['status']> = {\n succeeded: 'succeeded',\n processing: 'processing',\n requires_action: 'requires_action',\n requires_payment_method: 'failed',\n canceled: 'failed',\n };\n\n return {\n status: statusMap[paymentIntent.status] ?? 'failed',\n paymentIntentId: paymentIntent.id,\n paymentMethodId: this.extractPaymentMethodId(paymentIntent.payment_method),\n };\n }\n\n private extractPaymentMethodId(\n paymentMethod: string | { id?: string | null } | null | undefined,\n ): string | undefined {\n if (typeof paymentMethod === 'string' && paymentMethod.startsWith('pm_')) {\n return paymentMethod;\n }\n if (paymentMethod && typeof paymentMethod === 'object' && typeof paymentMethod.id === 'string') {\n return paymentMethod.id;\n }\n return undefined;\n }\n\n async confirmPayPalPayment(params: {\n billingApiUrl: string;\n sessionId: string;\n email: string;\n returnUrl: string;\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n if (!this.stripe) {\n return { status: 'failed', error: new FloPayError('Stripe not initialized', 'api_error') };\n }\n\n const baseUrl = params.billingApiUrl.replace(/\\/+$/, '');\n\n if (this.elements) {\n const { error: submitError } = await this.elements.submit();\n if (submitError) {\n return {\n status: 'failed',\n error: new FloPayError(\n submitError.message ?? 'PayPal payment failed',\n 'validation_error',\n { code: submitError.code },\n ),\n };\n }\n }\n\n const intentHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (params.nonce) intentHeaders['x-checkout-session-token'] = params.nonce;\n\n const intentResponse = await fetch(`${baseUrl}/v1/checkouts/payments/intents`, {\n method: 'POST',\n headers: intentHeaders,\n body: JSON.stringify({\n sessionId: params.sessionId,\n email: params.email,\n paymentMethodType: 'paypal',\n isPaypal: 'true',\n }),\n });\n\n if (!intentResponse.ok) {\n return { status: 'failed', error: new FloPayError('Failed to create PayPal payment intent', 'api_error') };\n }\n\n const intentJson = await intentResponse.json() as { data?: { id?: string } };\n const intentClientSecret = intentJson.data?.id;\n if (!intentClientSecret) {\n return { status: 'failed', error: new FloPayError('No client_secret in response', 'api_error') };\n }\n\n const { error } = await this.stripe.confirmPayment({\n clientSecret: intentClientSecret,\n elements: this.elements ?? undefined,\n confirmParams: { return_url: params.returnUrl },\n });\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'PayPal payment failed', 'api_error', { code: error.code }),\n };\n }\n\n return {\n status: 'processing',\n };\n }\n\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n if (!this.stripe || typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n const paymentIntentId = params.get('payment_intent');\n const clientSecret = params.get('payment_intent_client_secret');\n const redirectStatus = params.get('redirect_status');\n\n if (!paymentIntentId || !clientSecret) return null;\n\n if (redirectStatus === 'failed') {\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was declined. Please try again.', 'api_error'),\n };\n }\n\n const { paymentIntent, error } = await this.stripe.retrievePaymentIntent(clientSecret);\n\n if (error) {\n return {\n status: 'failed',\n error: new FloPayError(error.message ?? 'Failed to retrieve PayPal payment', 'api_error'),\n };\n }\n\n if (paymentIntent && (paymentIntent.status === 'requires_capture' || paymentIntent.status === 'succeeded')) {\n const pmId = typeof paymentIntent.payment_method === 'string'\n ? paymentIntent.payment_method\n : paymentIntent.payment_method?.id;\n\n // Clean up URL params\n const url = new URL(window.location.href);\n url.searchParams.delete('payment_intent');\n url.searchParams.delete('payment_intent_client_secret');\n url.searchParams.delete('redirect_status');\n window.history.replaceState({}, '', url.toString());\n\n return {\n status: paymentIntent.status as ConfirmCardPaymentResult['status'],\n paymentIntentId: paymentIntent.id,\n paymentMethodId: pmId,\n };\n }\n\n return {\n status: 'failed',\n error: new FloPayError('PayPal payment was not completed. Please try again.', 'api_error'),\n };\n }\n\n getRawProvider(): unknown {\n return this.stripe;\n }\n\n createPayPalElements(options: ElementOptions): unknown {\n if (!this.stripe) return null;\n\n const elementsOptions: Record<string, unknown> = {\n mode: 'payment' as const,\n amount: options.amount ?? 0,\n currency: (options.currency ?? 'usd').toLowerCase(),\n captureMethod: 'manual' as const,\n };\n\n if (options.setupFutureUsage) {\n elementsOptions['setupFutureUsage'] = options.setupFutureUsage;\n }\n\n if (options.appearance) {\n elementsOptions['appearance'] = {\n theme: toStripeAppearanceTheme(options.appearance.theme),\n variables: options.appearance.variables as Record<string, string>,\n rules: options.appearance.rules,\n };\n }\n\n return this.stripe.elements(elementsOptions as unknown as Parameters<Stripe['elements']>[0]);\n }\n\n destroy(): void {\n this.elements = null;\n this.appliedAppearanceKey = null;\n this.stripe = null;\n }\n}\n","import type {\n PaymentProviderAdapter,\n FloPayConfig,\n ElementOptions,\n ConfirmPaymentParams,\n PaymentResult,\n 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 /**\n * Session-bound checkout token. Forwarded as `x-checkout-session-token`\n * on the internal `POST /v1/checkouts/payments/intents` call so post-#640\n * backends don't reject the round-trip with a 401.\n */\n nonce?: string;\n }): Promise<ConfirmCardPaymentResult> {\n return this.provider.confirmPayPalPayment(params);\n }\n\n /** Resume a PayPal payment after redirect return. Returns null if no PayPal params in URL. */\n async resumePayPalPayment(): Promise<ConfirmCardPaymentResult | null> {\n return this.provider.resumePayPalPayment();\n }\n\n /** Confirms a payment using the mounted elements. */\n async confirmPayment(params: ConfirmPaymentParams): Promise<PaymentResult> {\n return this.provider.confirmPayment(params);\n }\n\n /**\n * Retrieves a checkout session by ID via the billing API.\n *\n * Returns the normalized `CheckoutSession` with amount, currency,\n * customer data, and status.\n *\n * Requires `billingApiUrl` to be set — either via `loadFloPay(key, { billingApiUrl })`\n * or passed directly as the second argument.\n */\n async retrieveSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<CheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required to retrieve a session.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n const unified = await api.getUnifiedCheckoutSession(sessionId);\n if (!unified.data.session) {\n throw new FloPayError('Session not found', 'api_error');\n }\n return unified.data.session;\n }\n\n /**\n * Retrieves and normalizes a checkout session, including provider-specific\n * data (Stripe clientSecret/publishableKey, Chargebee site, etc.).\n *\n * The billing API URL is resolved from: explicit param → `loadFloPay()` config\n * → `NEXT_PUBLIC_FLOPAY_ENV` env var → `configureFlopay()` → staging fallback.\n */\n async retrieveUnifiedSession(\n sessionId: string,\n billingApiUrl?: string,\n ): Promise<NormalizedCheckoutSession> {\n if (!sessionId) {\n throw new FloPayError(\n 'sessionId is required.',\n 'validation_error',\n { param: 'sessionId' },\n );\n }\n\n const apiUrl = resolveBillingApiUrl(billingApiUrl ?? this.config.billingApiUrl);\n\n const api = new PaymentAPI(apiUrl);\n return api.getUnifiedCheckoutSession(sessionId);\n }\n\n /**\n * Returns the raw underlying provider instance (e.g. Stripe object).\n * Used internally by components that need direct provider access,\n * such as PayPal which requires its own Elements instance.\n */\n getRawProvider(): unknown {\n return this.provider.getRawProvider();\n }\n\n /** Tears down the SDK instance and releases resources. */\n destroy(): void {\n this.currentElements?.destroy();\n this.currentElements = null;\n this.provider.destroy();\n }\n}\n","import type {\n PaymentProviderAdapter,\n ElementType,\n ElementOptions,\n MountedElement,\n} from '@flopay/shared';\nimport { FloPayError } from '@flopay/shared';\n\n/**\n * Manages the creation and lifecycle of payment elements.\n *\n * Each `FloPayElements` instance is bound to a single provider adapter\n * and tracks all created elements for cleanup.\n */\nexport class FloPayElements {\n private readonly provider: PaymentProviderAdapter;\n private readonly elementMap = new Map<ElementType, MountedElement>();\n private readonly baseOptions: ElementOptions;\n\n constructor(provider: PaymentProviderAdapter, options?: ElementOptions) {\n this.provider = provider;\n this.baseOptions = options ?? {};\n }\n\n /**\n * Creates a new element of the given type.\n * If an element of that type already exists, it is destroyed first.\n */\n async create(\n type: ElementType,\n options?: ElementOptions,\n ): Promise<MountedElement> {\n // Check if the provider already has this element (e.g. after React\n // Strict Mode unmount+remount cycle). Reuse it instead of creating\n // a duplicate, which Stripe would reject.\n const providerExisting = this.provider.getElement(type);\n if (providerExisting) {\n this.elementMap.set(type, providerExisting);\n return providerExisting;\n }\n\n const existing = this.elementMap.get(type);\n if (existing) {\n existing.destroy();\n }\n\n const merged: ElementOptions = { ...this.baseOptions, ...options };\n const element = await this.provider.createElement(type, merged);\n this.elementMap.set(type, element);\n return element;\n }\n\n /** Returns a previously created element, or `null`. */\n getElement(type: ElementType): MountedElement | null {\n return this.elementMap.get(type) ?? null;\n }\n\n /**\n * Submits all mounted elements for validation.\n *\n * Returns an object with an optional error if validation fails.\n * This does NOT confirm the payment — call `floPay.confirmPayment()` for that.\n */\n async submit(): Promise<{ error?: FloPayError }> {\n // Walk through all mounted elements and check for incomplete state.\n // The actual validation is handled by the provider's element implementation.\n // For Stripe, the Elements.submit() method is called during confirmPayment.\n // This method exists to match the Stripe Elements API surface.\n return {};\n }\n\n /** Destroys all created elements and clears the internal map. */\n destroy(): void {\n for (const element of this.elementMap.values()) {\n element.destroy();\n }\n this.elementMap.clear();\n }\n}\n","import type {\n CheckoutGateways,\n CheckoutSession,\n CheckoutProcessError,\n CheckoutProcessingPending,\n CheckoutSessionProduct,\n NormalizedCheckoutSession,\n ProcessPaymentParams,\n TokenizedBody,\n BillingProvider,\n InlineSessionDraft,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport {\n cacheSessionDisplayData,\n clearSessionDisplayData,\n getSessionDisplayData,\n} from './session-display-cache.js';\nimport type {\n SessionDisplayCacheData,\n SessionDisplayProduct,\n} from './session-display-cache.js';\n\n/** Raw billing API response wrapper. */\ninterface BillingResponse<T> {\n data: T;\n}\n\n/** Raw checkout session from the billing API. */\ninterface RawCheckoutSession {\n uuid: string;\n nonce: string;\n status: 'pending' | 'completed' | 'expired';\n successUrl: string;\n cancelUrl: string;\n /** Session-level currency. */\n currency?: string;\n createdAt?: string;\n checkoutUrl?: string;\n /** Unified products list returned by the billing API (post-#760). */\n products?: Array<{\n uuid: string;\n checkoutSessionId: string;\n /** 'item' or 'subscription'. */\n type: 'item' | 'subscription';\n code?: string;\n name?: string | null;\n description?: string | null;\n quantity: number;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n metadata?: Record<string, unknown> | null;\n }>;\n coupons?: string[];\n /**\n * Pre-discount total in cart-currency major units. Populated by billing\n * API ≥ v1.1.2; absent on older backends.\n */\n subtotalAmount?: number;\n /** Total reduction from applied coupons (cart-currency major units). */\n discountAmount?: number;\n /** Final charge amount after coupon discount (cart-currency major units). */\n totalAmount?: number;\n checkoutMode?: 'full' | 'auto' | 'confirm';\n gateways?: CheckoutGateways;\n accountData: {\n userId: string;\n firstName: string;\n lastName: string;\n email: string;\n gender?: string | null;\n city?: string | null;\n state?: string | null;\n country?: string | null;\n zip?: string | null;\n addressLine1?: string | null;\n addressLine2?: string | null;\n };\n tagsData: {\n googleContainerId?: string | null;\n sessionId?: string | null;\n testEventCode?: string | null;\n };\n}\n\ntype ApiErrorPayload = Record<string, unknown> | null;\n\nconst DEFAULT_PROCESSING_RETRY_AFTER_MS = 1_000;\nconst MIN_PROCESSING_RETRY_AFTER_MS = 500;\nconst DEFAULT_PROCESSING_TIMEOUT_MS = 15_000;\nconst MAX_PROCESSING_RETRY_AFTER_MS = 3_000;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction readString(payload: ApiErrorPayload, key: string): string | undefined {\n const value = payload?.[key];\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nfunction readNumber(payload: ApiErrorPayload, key: string): number | undefined {\n const value = payload?.[key];\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined;\n}\n\nfunction delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction createCheckoutProcessingTimeoutError(): FloPayError {\n return new FloPayError(\n 'Checkout is still processing. Please try again shortly.',\n 'api_error',\n { code: 'checkout_processing_timeout' },\n );\n}\n\nasync function buildApiErrorFromResponse(\n response: Response,\n fallbackMessage: string,\n): Promise<FloPayError> {\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const nestedError = isRecord(payload?.error) ? payload.error : null;\n const message =\n readString(payload, 'message') ??\n readString(nestedError, 'message') ??\n fallbackMessage;\n const code =\n readString(payload, 'code') ??\n readString(payload, 'gatewayErrorCode') ??\n readString(nestedError, 'code') ??\n `http_${response.status}`;\n\n return new FloPayError(message, 'api_error', {\n code,\n statusCode: response.status,\n });\n}\n\n/**\n * Extra attempts (beyond the first) for transient network failures.\n * Three total tries: immediate, +150ms, +300ms.\n */\nconst NETWORK_RETRY_ATTEMPTS = 2;\n\n/**\n * Run `fetch` with a bounded retry on transient network failures.\n *\n * `fetch` rejects — rather than resolving with a non-ok `Response` — only when\n * the request never produced an HTTP response at all: a dropped connection, a\n * DNS hiccup, or a failed CORS preflight, which all surface as\n * `TypeError: Failed to fetch`. Those blips are worth retrying. An `AbortError`\n * (caller-initiated cancellation) is not, and any HTTP-level failure resolves\n * normally and is left for the caller to handle via `response.ok`.\n *\n * Backoff is exponential (150ms, 300ms). Only use this for requests whose\n * replay is harmless — idempotent reads, or session bootstrap where a re-sent\n * create can at worst leave an unused draft session behind.\n */\nasync function fetchWithNetworkRetry(\n input: RequestInfo | URL,\n init?: RequestInit,\n attempts = NETWORK_RETRY_ATTEMPTS,\n): Promise<Response> {\n let lastErr: unknown;\n for (let attempt = 0; ; attempt++) {\n try {\n return await fetch(input, init);\n } catch (err) {\n // Caller-initiated cancellation must propagate immediately, never retry.\n if (err instanceof Error && err.name === 'AbortError') throw err;\n lastErr = err;\n if (attempt >= attempts) throw lastErr;\n await delay(150 * 2 ** attempt);\n }\n }\n}\n\n/**\n * Client-side payment API service.\n *\n * Mirrors the `PaymentAPI` class from the checkout project's\n * `src/service/api.ts`. All methods call the billing API endpoints\n * that the checkout backend exposes.\n */\nexport class PaymentAPI {\n private readonly baseUrl: string;\n\n constructor(billingApiUrl: string) {\n this.baseUrl = billingApiUrl.replace(/\\/+$/, '');\n }\n\n /**\n * Fetch a raw checkout session by ID.\n *\n * `nonce` is the session-bound checkout token returned when the session\n * was created. When supplied it is sent as the `x-checkout-session-token`\n * header that post-#640 backends match against `checkout_session.nonce`\n * before returning the row — the UUID alone is no longer sufficient.\n * Backends that don't yet enforce it ignore the extra header.\n */\n async getCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<BillingResponse<RawCheckoutSession>> {\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(checkoutSessionId)}`,\n nonce ? { headers: { 'x-checkout-session-token': nonce } } : undefined,\n );\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to get checkout session');\n }\n\n const body = await response.json() as BillingResponse<RawCheckoutSession>;\n return { ...body, data: this.mergeCachedDisplayData(body.data) };\n }\n\n /**\n * Stash display-only data for a session so subsequent fetches can fill in\n * fields the backend no longer persists (`overrideAmount`, `totalAmount`,\n * `providerItemName`, `providerPlanName`).\n *\n * Backed by `sessionStorage` in the browser, with an in-memory fallback in\n * Node/SSR contexts. Default TTL: 1 hour.\n *\n * Server-returned values always win — cached values fill in only where the\n * server returned `null` / `undefined`.\n *\n * @example\n * ```ts\n * paymentAPI.cacheSessionDisplayData(sessionId, {\n * currency: 'USD',\n * items: [{ code: 'pro_plan', overrideAmount: 24.99, providerItemName: 'Pro' }],\n * });\n * ```\n */\n cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n ): void {\n cacheSessionDisplayData(sessionId, data, options);\n }\n\n /**\n * Drop any cached display data for a session. Call after the payment\n * completes; otherwise the TTL handles cleanup.\n */\n clearSessionDisplayData(sessionId: string): void {\n clearSessionDisplayData(sessionId);\n }\n\n /**\n * Fetch and normalize a checkout session.\n *\n * Reads the backend's `gateways` map to enumerate provider-specific data,\n * then wraps the session in a `NormalizedCheckoutSession` for provider-\n * agnostic consumption.\n */\n async getUnifiedCheckoutSession(\n checkoutSessionId: string,\n nonce?: string,\n ): Promise<NormalizedCheckoutSession> {\n const res = await this.getCheckoutSession(checkoutSessionId, nonce);\n return this.normalizeRawSession(res.data);\n }\n\n /**\n * Submit a tokenized payment to the billing backend.\n *\n * The backend will either succeed, return `type: '3ds_required'`\n * (with a `threeDSecureToken`), or return `type: 'paypal_redirect_required'`.\n *\n * Hits the session-scoped route `POST /v1/checkouts/sessions/:id/process`\n * and forwards `data.nonce` as `x-checkout-session-token`. Backend\n * `TeamFloPay/backend#640` rejects callers without a matching nonce with a\n * 401 — this method throws synchronously when `data.nonce` is missing so the\n * problem surfaces before the network round trip.\n *\n * @param userId Vestigial — backend's GatewayInterceptor routes via session,\n * not headers, so this value is no longer sent on the wire. Kept in the\n * signature for back-compat with existing callers; will be removed in a\n * future major version.\n */\n async processPayment(\n _userId: string,\n data: ProcessPaymentParams,\n options?: {\n pollTimeoutMs?: number;\n },\n ): Promise<Response> {\n if (!data.nonce) {\n throw new FloPayError(\n 'processPayment requires `nonce` — pass the value returned from session creation.',\n 'validation_error',\n { code: 'MissingCheckoutSessionToken', param: 'nonce' },\n );\n }\n\n const { nonce, ...processBody } = data;\n const response = await fetch(\n `${this.baseUrl}/v1/checkouts/sessions/${encodeURIComponent(data.sessionId)}/process`,\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-checkout-session-token': nonce,\n },\n body: JSON.stringify(processBody),\n },\n );\n\n return this.resolveProcessResponse(response, data.sessionId, { ...options, nonce });\n }\n\n /**\n * Create a PaymentIntent on the backend.\n *\n * Used by the Stripe flow to create a server-side PaymentIntent\n * with the client's payment method attached.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied — the\n * session-bound checkout token returned by session creation. Post-#640\n * backends reject this call with a 401 when the header is missing or does\n * not match the session's stored nonce.\n */\n async createPaymentIntent(\n sessionId: string,\n email: string,\n paymentMethodType: string,\n options?: { signal?: AbortSignal; isPaypal?: string; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({\n sessionId,\n email,\n paymentMethodType,\n isPaypal: options?.isPaypal ?? false,\n }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Create a SetupIntent for saving payment methods.\n *\n * Forwards `options.nonce` as `x-checkout-session-token` when supplied —\n * required by post-#640 backends, ignored by earlier versions.\n */\n async createSetupIntent(\n sessionId: string,\n email: string,\n paymentMethodType: 'card' | 'google_pay' | 'apple_pay' | 'paypal_express_checkout',\n options?: { signal?: AbortSignal; nonce?: string },\n ): Promise<Response> {\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options?.nonce) headers['x-checkout-session-token'] = options.nonce;\n return fetch(\n `${this.baseUrl}/v1/checkouts/payments/setup-intents`,\n {\n method: 'POST',\n headers,\n body: JSON.stringify({ sessionId, email, paymentMethodType }),\n signal: options?.signal,\n },\n );\n }\n\n /**\n * Fetch user's prior payments by email.\n * Used to determine if saved card UX should be shown.\n */\n async getPaymentsByEmail(\n email: string,\n options?: { signal?: AbortSignal; page?: number; limit?: number },\n ): Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }> {\n const page = options?.page ?? 1;\n const limit = options?.limit ?? 1;\n const params = new URLSearchParams({\n email,\n page: String(page),\n limit: String(limit),\n sortField: 'occurredAt',\n sortDirection: 'DESC',\n });\n\n const response = await fetch(\n `${this.baseUrl}/v1/payments?${params.toString()}`,\n {\n method: 'GET',\n signal: options?.signal,\n keepalive: true,\n },\n );\n\n if (!response.ok) {\n throw new FloPayError('Failed to fetch payments', 'api_error');\n }\n\n return response.json() as Promise<{ data: Array<{ id: string }>; total: number; page: number; limit: number }>;\n }\n\n /**\n * Create a checkout session AND return the full session data in one call.\n * Uses `?expand=true` so the backend returns the complete session\n * instead of just a UUID — eliminating the need for a second GET.\n *\n * Falls back to create + GET if the backend doesn't support `expand`.\n */\n async createAndFetchSession(\n params: InlineSessionDraft,\n ): Promise<NormalizedCheckoutSession> {\n const wireProducts = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n params.items,\n params.subscriptions,\n wireProducts,\n );\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId: params.clientId,\n checkoutVersion: SDK_VERSION,\n successUrl: params.successUrl,\n cancelUrl: params.cancelUrl,\n currency: sessionCurrency,\n checkoutMode: params.checkoutMode ?? 'full',\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: params.account.userId,\n firstName: params.account.firstName ?? null,\n lastName: params.account.lastName ?? null,\n email: params.account.email,\n country: params.account.country ?? null,\n gender: params.account.gender ?? null,\n city: params.account.city ?? null,\n state: params.account.state ?? null,\n zip: params.account.zip ?? null,\n addressLine1: params.account.addressLine1 ?? null,\n addressLine2: params.account.addressLine2 ?? null,\n },\n couponCodes: params.couponCodes ?? [],\n };\n\n if (params.tokenizedData) payload['tokenizedData'] = params.tokenizedData;\n if (params.tagsData) payload['tagsData'] = params.tagsData;\n if (params.utmMetadata?.length) payload['utmMetadata'] = params.utmMetadata;\n\n // Checkout analytics metadata\n if (params.avsCheck !== undefined) payload['avsCheck'] = params.avsCheck;\n if (params.checkoutType) payload['checkoutType'] = params.checkoutType;\n if (params.checkoutLayout) payload['checkoutLayout'] = params.checkoutLayout;\n if (params.avsConfig) payload['avsConfig'] = params.avsConfig;\n\n const response = await fetchWithNetworkRetry(\n `${this.baseUrl}/v1/checkouts/sessions?expand=true`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n },\n );\n\n if (response.status === 204) {\n throw new FloPayError(\n 'Session auto-completed — payment method already on file',\n 'api_error',\n { code: 'session_auto_completed' },\n );\n }\n\n if (!response.ok) {\n throw await buildApiErrorFromResponse(response, 'Failed to create checkout session');\n }\n\n const body = await response.json() as {\n data: RawCheckoutSession & { uuid?: string };\n autoProcessingError?: CheckoutProcessError;\n autoProcessingAttempted?: boolean;\n autoProcessingPending?: CheckoutProcessingPending;\n };\n\n // If backend supports expand=true, it returns full session data.\n // If not, it returns { data: { uuid } } and we fall back to a GET.\n if (body.data && 'gateways' in body.data) {\n // Auto-stash display-only fields the consumer just sent so wallet\n // sheets and the post-redirect fetch see the same totals. Must run\n // BEFORE the merge so the merge picks them up.\n this.autoCacheDisplayData(body.data.uuid, params);\n const merged = this.mergeCachedDisplayData(body.data);\n return {\n ...this.normalizeRawSession(merged),\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n // Fallback: backend returned UUID only — fetch full session\n const uuid = body.data?.uuid;\n if (!uuid) {\n throw new FloPayError('No session ID returned', 'api_error');\n }\n this.autoCacheDisplayData(uuid, params);\n const unifiedSession = await this.getUnifiedCheckoutSession(uuid);\n return {\n ...unifiedSession,\n autoProcessingError: body.autoProcessingError,\n autoProcessingAttempted: body.autoProcessingAttempted,\n autoProcessingPending: body.autoProcessingPending,\n };\n }\n\n async waitForCheckoutSessionCompletion(\n checkoutSessionId: string,\n options?: {\n initialDelayMs?: number;\n timeoutMs?: number;\n /**\n * Session-bound checkout token; forwarded on the poll's\n * `GET /v1/checkouts/sessions/:id`. Required by post-#640 backends.\n */\n nonce?: string;\n },\n ): Promise<NormalizedCheckoutSession> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_PROCESSING_TIMEOUT_MS;\n const deadline = Date.now() + timeoutMs;\n let nextDelayMs = this.clampRetryAfterMs(options?.initialDelayMs ?? DEFAULT_PROCESSING_RETRY_AFTER_MS);\n\n while (true) {\n const remainingMs = deadline - Date.now();\n if (remainingMs <= 0) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n if (nextDelayMs > 0) {\n await delay(Math.min(nextDelayMs, remainingMs));\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n }\n\n const session = await this.getUnifiedCheckoutSession(checkoutSessionId, options?.nonce);\n const status = session.data.session?.status;\n\n if (status === 'complete' || status === 'expired') {\n return session;\n }\n\n if (Date.now() >= deadline) {\n throw createCheckoutProcessingTimeoutError();\n }\n\n nextDelayMs = this.clampRetryAfterMs(\n Math.max(nextDelayMs * 2, MIN_PROCESSING_RETRY_AFTER_MS),\n );\n }\n }\n\n /** Normalize a raw session into a provider-agnostic shape. */\n private normalizeRawSession(session: RawCheckoutSession): NormalizedCheckoutSession {\n const gateways: CheckoutGateways = session.gateways ?? {};\n const providers: BillingProvider[] = [];\n const data: NormalizedCheckoutSession['data'] = {\n session: this.toCheckoutSession(session),\n };\n\n const stripeGateway = gateways.stripe;\n if (stripeGateway?.publishableKey) {\n providers.push('stripe');\n const rawSession = session as unknown as Record<string, unknown>;\n const stripeClientSecret = [\n rawSession['stripeClientSecret'],\n stripeGateway.stripeClientSecret,\n ].find((value) => typeof value === 'string' && value.length > 0) as string | undefined;\n\n data.stripe = {\n clientSecret: stripeClientSecret ?? '',\n publishableKey: stripeGateway.publishableKey ?? undefined,\n paypalPublishableKey: stripeGateway.paypalPublishableKey ?? undefined,\n environment: stripeGateway.environment,\n enabledPaymentMethods: Array.isArray(stripeGateway.enabledPaymentMethods)\n ? stripeGateway.enabledPaymentMethods.filter((m): m is string => typeof m === 'string')\n : undefined,\n };\n }\n\n const paypalGateway = gateways.paypal;\n if (paypalGateway?.publishableKey) {\n providers.push('paypal');\n data.paypal = {\n publishableKey: paypalGateway.publishableKey,\n environment: paypalGateway.environment,\n };\n }\n\n return {\n providers,\n mode: 'tokenize',\n data,\n raw: { data: session },\n };\n }\n\n /** Convert raw session to the SDK CheckoutSession shape. */\n private toCheckoutSession(raw: RawCheckoutSession): CheckoutSession {\n const rawProducts = raw.products ?? [];\n\n // Billing API ≥ v1.1.2 returns the coupon-adjusted `totalAmount` on the\n // session. Prefer it as the source of truth so wallet sheets render the\n // post-coupon amount; fall back to summing per-line `overrideAmount`.\n const hasBackendTotal =\n typeof raw.totalAmount === 'number' && Number.isFinite(raw.totalAmount);\n const computedTotal = rawProducts.reduce(\n (sum, p) => sum + (p.overrideAmount ?? p.totalAmount ?? 0),\n 0,\n );\n const totalAmount = hasBackendTotal ? (raw.totalAmount as number) : computedTotal;\n\n // Billing API returns amounts in major units (e.g. 24.95 USD).\n // Stripe expects amounts in smallest currency unit (cents).\n const amountInCents = Math.round(totalAmount * 100);\n\n const currency = raw.currency ?? rawProducts[0]?.currency ?? 'USD';\n\n const mode: CheckoutSession['mode'] =\n rawProducts.some((p) => p.type === 'subscription') ? 'subscription' : 'payment';\n\n return {\n id: raw.uuid,\n clientSecret: raw.nonce,\n mode,\n status: this.toCheckoutSessionStatus(raw.status),\n amount: amountInCents,\n currency,\n customer: {\n id: raw.accountData.userId,\n email: raw.accountData.email,\n firstName: raw.accountData.firstName,\n lastName: raw.accountData.lastName,\n country: raw.accountData.country ?? undefined,\n city: raw.accountData.city ?? undefined,\n state: raw.accountData.state ?? undefined,\n zip: raw.accountData.zip ?? undefined,\n gender: raw.accountData.gender ?? undefined,\n line1: raw.accountData.addressLine1 ?? undefined,\n line2: raw.accountData.addressLine2 ?? undefined,\n },\n metadata: {},\n\n checkoutMode: raw.checkoutMode,\n products: rawProducts.map((p): CheckoutSessionProduct => ({\n ...p,\n totalAmount: typeof p.totalAmount === 'number' ? p.totalAmount : undefined,\n overrideAmount: typeof p.overrideAmount === 'number' ? p.overrideAmount : null,\n currency: typeof p.currency === 'string' ? p.currency : undefined,\n metadata: p.metadata ?? null,\n })),\n successUrl: raw.successUrl,\n cancelUrl: raw.cancelUrl,\n coupons: raw.coupons,\n subtotalAmount: raw.subtotalAmount,\n discountAmount: raw.discountAmount,\n totalAmount: raw.totalAmount,\n createdAt: raw.createdAt,\n gateways: raw.gateways,\n accountData: raw.accountData,\n tagsData: raw.tagsData,\n };\n }\n\n private toCheckoutSessionStatus(status: RawCheckoutSession['status']): CheckoutSession['status'] {\n if (status === 'completed') {\n return 'complete';\n }\n\n if (status === 'expired') {\n return 'expired';\n }\n\n return 'open';\n }\n\n private async resolveProcessResponse(\n response: Response,\n checkoutSessionId: string,\n options?: {\n pollTimeoutMs?: number;\n nonce?: string;\n },\n ): Promise<Response> {\n if (response.status !== 202) {\n return response;\n }\n\n const payload = await response.json().catch(() => null) as ApiErrorPayload;\n const pending = this.toCheckoutProcessingPending(payload, response, checkoutSessionId);\n const session = await this.waitForCheckoutSessionCompletion(pending.sessionId, {\n initialDelayMs: pending.retryAfterMs,\n timeoutMs: options?.pollTimeoutMs,\n nonce: options?.nonce,\n });\n\n if (session.data.session?.status === 'complete') {\n return new Response(null, { status: 204, statusText: 'No Content' });\n }\n\n if (session.data.session?.status === 'expired') {\n throw new FloPayError(\n 'Checkout session has expired.',\n 'api_error',\n { code: 'checkout_session_expired' },\n );\n }\n\n throw createCheckoutProcessingTimeoutError();\n }\n\n private toCheckoutProcessingPending(\n payload: ApiErrorPayload,\n response: Response,\n checkoutSessionId: string,\n ): CheckoutProcessingPending {\n const retryAfterHeader = response.headers.get('Retry-After');\n const headerRetryAfterSeconds = retryAfterHeader === null\n || retryAfterHeader.trim() === ''\n ? undefined\n : Number(retryAfterHeader);\n const headerRetryAfterMs = headerRetryAfterSeconds !== undefined\n && Number.isFinite(headerRetryAfterSeconds)\n ? headerRetryAfterSeconds * 1_000\n : undefined;\n\n return {\n type: 'checkout_processing',\n sessionId: readString(payload, 'sessionId') ?? checkoutSessionId,\n retryAfterMs: this.clampRetryAfterMs(\n readNumber(payload, 'retryAfterMs')\n ?? headerRetryAfterMs\n ?? DEFAULT_PROCESSING_RETRY_AFTER_MS,\n ),\n statusUrl: readString(payload, 'statusUrl'),\n sessionUrl: readString(payload, 'sessionUrl'),\n };\n }\n\n private clampRetryAfterMs(retryAfterMs: number): number {\n return Math.max(0, Math.min(retryAfterMs, MAX_PROCESSING_RETRY_AFTER_MS));\n }\n\n /**\n * Stash the display-only fields the consumer passed into a create-session\n * call. Runs after the backend assigns a UUID so a later GET on the same\n * session (typically after a redirect) can fill in fields the backend no\n * longer persists — `overrideAmount`, `totalAmount`, `name`, etc.\n *\n * No-op when no UUID is available.\n */\n private autoCacheDisplayData(\n sessionId: string | undefined,\n params: InlineSessionDraft,\n ): void {\n if (!sessionId) return;\n const products = params.products ?? foldIntoProducts(params.items, params.subscriptions);\n if (products.length === 0 && !params.currency) {\n return;\n }\n const usingUnifiedProducts = params.products !== undefined;\n const sessionCurrency = resolveSessionCurrency(\n params.currency,\n usingUnifiedProducts ? undefined : params.items,\n usingUnifiedProducts ? undefined : params.subscriptions,\n products,\n );\n cacheSessionDisplayData(sessionId, {\n currency: sessionCurrency ?? undefined,\n products: products.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency ?? undefined,\n })),\n });\n }\n\n /**\n * Merge cached display-only fields (set by {@link cacheSessionDisplayData})\n * into a raw session response. Server values always win — cache fills in\n * only where the server returned `null` / `undefined`.\n */\n private mergeCachedDisplayData(raw: RawCheckoutSession): RawCheckoutSession {\n const cached = getSessionDisplayData(raw.uuid);\n\n const cachedProducts = new Map<string, SessionDisplayProduct>();\n // Key on `code` alone: the catalog code is the product's identity (the\n // backend resolves item-vs-subscription from it), so cached display data\n // merges back correctly even when the create-side payload omitted the\n // deprecated `type`.\n const productKey = (code: string | undefined): string | undefined =>\n code ? `code:${code}` : undefined;\n for (const p of cached?.products ?? []) {\n const key = productKey(p.code);\n if (key) cachedProducts.set(key, p);\n }\n\n const mergedProducts = (raw.products ?? []).map((p) => {\n const key = productKey(p.code);\n const fallback = key ? cachedProducts.get(key) : undefined;\n return {\n ...p,\n name: p.name ?? fallback?.name ?? null,\n totalAmount: p.totalAmount ?? fallback?.totalAmount,\n overrideAmount: p.overrideAmount ?? fallback?.overrideAmount,\n currency: p.currency ?? fallback?.currency,\n };\n });\n\n return {\n ...raw,\n currency: raw.currency ?? cached?.currency,\n products: mergedProducts,\n };\n }\n}\n","/**\n * Client-side cache for display-only checkout fields the backend no longer\n * persists (`overrideAmount`, `totalAmount`, `providerItemName`,\n * `providerPlanName`, per-line `currency`).\n *\n * The cache lives in `sessionStorage` so it survives the navigation from the\n * page that creates the session to the checkout page that fetches it, but\n * dies on tab close. An in-memory fallback keeps the SDK working in Node /\n * SSR contexts where `sessionStorage` is unavailable.\n *\n * Values from the server response always win — cached values fill in only\n * where the server returned `null` or `undefined`.\n */\n\nconst STORAGE_KEY_PREFIX = 'flopay_session_display:';\nconst DEFAULT_TTL_MS = 60 * 60 * 1000;\n\n/** Display-only fields per product that can be cached and merged back later. */\nexport interface SessionDisplayProduct {\n /** Catalog code (match key). */\n code?: string;\n /** Whether this product is a one-time item or a recurring subscription. */\n type?: 'item' | 'subscription';\n /** Display-only name for the product. */\n name?: string | null;\n totalAmount?: number;\n overrideAmount?: number | null;\n currency?: string;\n}\n\n/** Display-only payload that can be stashed for later merge into a session response. */\nexport interface SessionDisplayCacheData {\n /** Session-level currency (falls into the response only when the server omits it). */\n currency?: string;\n products?: SessionDisplayProduct[];\n}\n\ninterface CachedEntry {\n data: SessionDisplayCacheData;\n expiresAt: number;\n}\n\nconst memoryStore = new Map<string, CachedEntry>();\n\nfunction storageKey(sessionId: string): string {\n return `${STORAGE_KEY_PREFIX}${sessionId}`;\n}\n\nfunction getSessionStorage(): Storage | null {\n if (typeof window === 'undefined') return null;\n try {\n return window.sessionStorage;\n } catch {\n return null;\n }\n}\n\n/**\n * Stash display-only data for a session. Called client-side right after the\n * server returns a session ID, so the values survive the redirect to the\n * checkout page.\n */\nexport function cacheSessionDisplayData(\n sessionId: string,\n data: SessionDisplayCacheData,\n options?: { ttlMs?: number },\n): void {\n if (!sessionId) return;\n\n const ttl = options?.ttlMs ?? DEFAULT_TTL_MS;\n const entry: CachedEntry = { data, expiresAt: Date.now() + ttl };\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.setItem(storageKey(sessionId), JSON.stringify(entry));\n return;\n } catch {\n // Quota / serialization error — fall through to in-memory store\n }\n }\n\n memoryStore.set(sessionId, entry);\n}\n\n/**\n * Read previously-cached display data for a session, or `null` if nothing\n * is cached (or the TTL has elapsed).\n */\nexport function getSessionDisplayData(\n sessionId: string,\n): SessionDisplayCacheData | null {\n if (!sessionId) return null;\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n const raw = storage.getItem(storageKey(sessionId));\n if (raw) {\n const entry = JSON.parse(raw) as CachedEntry;\n if (entry && typeof entry.expiresAt === 'number' && entry.expiresAt > Date.now()) {\n return entry.data;\n }\n storage.removeItem(storageKey(sessionId));\n }\n } catch {\n // Malformed entry — fall through to memory store\n }\n }\n\n const memEntry = memoryStore.get(sessionId);\n if (memEntry) {\n if (memEntry.expiresAt > Date.now()) {\n return memEntry.data;\n }\n memoryStore.delete(sessionId);\n }\n\n return null;\n}\n\n/**\n * Drop any cached display data for a session. Call from the success page\n * after the payment completes; otherwise the TTL handles cleanup.\n */\nexport function clearSessionDisplayData(sessionId: string): void {\n if (!sessionId) return;\n\n memoryStore.delete(sessionId);\n\n const storage = getSessionStorage();\n if (storage) {\n try {\n storage.removeItem(storageKey(sessionId));\n } catch {\n // ignore\n }\n }\n}\n","import type {\n CreateSessionParams,\n CheckoutSessionResult,\n} from '@flopay/shared';\nimport {\n FloPayError,\n SDK_VERSION,\n buildProductPayload,\n foldIntoProducts,\n resolveSessionCurrency,\n} from '@flopay/shared';\nimport { cacheSessionDisplayData } from './session-display-cache.js';\n\n/**\n * Max number of coupon codes the billing API accepts per session create.\n * Mirrors the backend guard introduced in billing v1.1.2 — exceeding this\n * returns the `CouponLimitExceeded` error.\n */\nconst MAX_COUPON_CODES = 5;\n\ninterface ApiErrorBody {\n code?: unknown;\n message?: unknown;\n error?: { code?: unknown; message?: unknown };\n}\n\nfunction readString(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\n/**\n * Map a billing API error body to a `FloPayError`. Surfaces the coupon\n * validation codes introduced in billing v1.1.2 (`CouponLimitExceeded`,\n * `CouponCurrencyUnsupported`) as structured errors so callers can branch\n * on `error.code` instead of parsing the HTTP status.\n */\nfunction buildCheckoutSessionError(status: number, payload: ApiErrorBody | undefined): FloPayError {\n const nested = payload?.error;\n const code =\n readString(payload?.code)\n ?? readString(nested?.code)\n ?? `http_${status}`;\n const message =\n readString(payload?.message)\n ?? readString(nested?.message)\n ?? defaultMessageForCode(code, status);\n\n return new FloPayError(message, 'api_error', { code, statusCode: status });\n}\n\nfunction defaultMessageForCode(code: string, status: number): string {\n switch (code) {\n case 'CouponLimitExceeded':\n return `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`;\n case 'CouponCurrencyUnsupported':\n return 'One of the applied coupons has no price configured for the cart currency.';\n default:\n return `Failed to create checkout session (HTTP ${status}).`;\n }\n}\n\n/**\n * Creates a checkout session via the billing API and redirects the user\n * to the hosted checkout page.\n *\n * Mirrors `createCheckoutSession` from `clicktech-core-ui/modules`.\n *\n * - On 201: Redirects to `{checkoutBaseUrl}/secure?id={uuid}&...redirectParams`\n * - On 204: Redirects directly to `successUrl` (payment method already on file)\n * - On other: Returns `{ status }` without redirecting\n *\n * @example\n * ```ts\n * import { createCheckoutSession } from '@flopay/js';\n *\n * await createCheckoutSession({\n * billingApiUrl: 'https://billing.example.com',\n * checkoutBaseUrl: 'https://checkout.example.com',\n * clientId: 'client_123',\n * currency: 'USD',\n * items: [{\n * code: 'initial_charge',\n * quantity: 1,\n * metadata: { source: 'web' },\n * }],\n * account: { userId: 'user_1', email: 'user@example.com' },\n * successUrl: '/success',\n * cancelUrl: '/cancel',\n * redirectParams: { email: 'user@example.com', bg: 'courses', mode: 'confirm' },\n * });\n * ```\n */\nexport async function createCheckoutSession(\n options: CreateSessionParams,\n): Promise<CheckoutSessionResult> {\n const {\n billingApiUrl,\n checkoutBaseUrl,\n items = [],\n subscriptions = [],\n products,\n account,\n successUrl,\n cancelUrl,\n checkoutMode = 'confirm',\n couponCodes = [],\n tagsData,\n redirectParams = {},\n setCookie = true,\n timeoutMs = 12000,\n clientId,\n currency,\n utmMetadata,\n } = options;\n\n if (couponCodes.length > MAX_COUPON_CODES) {\n throw new FloPayError(\n `Too many coupon codes — a checkout session accepts at most ${MAX_COUPON_CODES}.`,\n 'validation_error',\n { code: 'CouponLimitExceeded', param: 'couponCodes' },\n );\n }\n\n const wireProducts = products ?? foldIntoProducts(items, subscriptions);\n const sessionCurrency = resolveSessionCurrency(currency, items, subscriptions, wireProducts);\n if (!sessionCurrency) {\n throw new FloPayError(\n 'currency is required: pass `currency` on the session, or include a `currency` on the first item/subscription/product.',\n 'validation_error',\n { code: 'CurrencyRequired', param: 'currency' },\n );\n }\n\n const payload: Record<string, unknown> = {\n clientId,\n checkoutVersion: SDK_VERSION,\n successUrl,\n cancelUrl,\n currency: sessionCurrency,\n checkoutMode,\n products: wireProducts.map((product) => buildProductPayload(product, sessionCurrency)),\n accountData: {\n userId: account.userId,\n firstName: account.firstName ?? null,\n lastName: account.lastName ?? null,\n email: account.email,\n country: account.country ?? null,\n gender: account.gender ?? null,\n city: account.city ?? null,\n state: account.state ?? null,\n zip: account.zip ?? null,\n addressLine1: account.addressLine1 ?? null,\n addressLine2: account.addressLine2 ?? null,\n },\n couponCodes,\n };\n\n if (tagsData) {\n payload['tagsData'] = tagsData;\n }\n\n if (utmMetadata?.length) {\n payload['utmMetadata'] = utmMetadata;\n }\n\n const url = `${billingApiUrl.replace(/\\/+$/, '')}/v1/checkouts/sessions`;\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), timeoutMs);\n\n let status: number;\n let body: ({ data?: { uuid?: string; nonce?: string } } & ApiErrorBody) | undefined;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n status = response.status;\n\n try {\n body = await response.json() as { data?: { uuid?: string; nonce?: string } } & ApiErrorBody;\n } catch {\n // 204 or empty body — leave undefined\n }\n } finally {\n clearTimeout(timer);\n }\n\n if (status >= 400) {\n throw buildCheckoutSessionError(status, body);\n }\n\n if (status === 201) {\n const uuid = body?.data?.uuid;\n const nonce = body?.data?.nonce;\n\n if (!uuid) {\n throw new Error('Checkout session created but no UUID was returned by the billing API');\n }\n\n if (!nonce) {\n throw new FloPayError(\n 'Checkout session created but no `nonce` was returned by the billing API. Upgrade the billing service to TeamFloPay/backend#640 or later.',\n 'api_error',\n { code: 'MissingCheckoutSessionToken' },\n );\n }\n\n // Auto-stash display-only fields the consumer passed in so the checkout\n // page (post-redirect) renders the right totals/names even though the\n // backend no longer persists them. Runs before the redirect — values\n // survive the navigation via sessionStorage.\n if (wireProducts.length || sessionCurrency) {\n cacheSessionDisplayData(uuid, {\n currency: sessionCurrency,\n products: wireProducts.map((p) => ({\n code: p.code ?? p.providerItemId ?? p.providerPlanId,\n type: p.type,\n name: p.name ?? p.itemName ?? p.providerItemName ?? p.subscriptionName ?? p.providerPlanName ?? null,\n totalAmount: p.totalAmount,\n overrideAmount: p.overrideAmount,\n currency: p.currency ?? sessionCurrency,\n })),\n });\n }\n\n const redirectUrl = new URL(`${checkoutBaseUrl.replace(/\\/+$/, '')}/secure`);\n redirectUrl.searchParams.set('id', uuid);\n\n for (const [key, value] of Object.entries(redirectParams)) {\n redirectUrl.searchParams.set(key, value);\n }\n\n if (setCookie && typeof window !== 'undefined' && typeof document !== 'undefined') {\n const checkoutData = JSON.stringify({ origin_url: cancelUrl });\n const domain = window.location.hostname.split('.').slice(-2).join('.');\n document.cookie = `checkout_data=${encodeURIComponent(checkoutData)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n // Carry the session-bound checkout token to the hosted checkout page so\n // it can echo `x-checkout-session-token` on every continuation call\n // (post-#640 backends require this header to match the session nonce).\n // The URL is deliberately not modified — keeping the nonce out of the\n // location bar avoids history / Referer leakage.\n document.cookie = `flopay_checkout_token=${encodeURIComponent(nonce)}; domain=.${domain}; path=/; max-age=3600; SameSite=Lax; Secure;`;\n }\n\n if (typeof window !== 'undefined') {\n window.location.href = redirectUrl.toString();\n }\n\n return { status: 201, redirectUrl: redirectUrl.toString(), nonce };\n }\n\n if (status === 204) {\n if (typeof window !== 'undefined') {\n window.location.href = successUrl;\n }\n return { status: 204 };\n }\n\n return { status };\n}\n\n/**\n * Creates a checkout session with automatic retry on timeout/abort errors.\n *\n * Uses exponential backoff: 100ms, 200ms, 400ms, etc.\n */\nexport async function createCheckoutSessionWithRetries(\n options: CreateSessionParams & { maxRetries?: number },\n): Promise<CheckoutSessionResult> {\n const { maxRetries = 3, ...sessionOptions } = options;\n\n if (!Number.isFinite(maxRetries) || !Number.isInteger(maxRetries) || maxRetries <= 0) {\n throw new Error('Number of retries must be greater than 0');\n }\n\n let lastErr: unknown;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n return await createCheckoutSession(sessionOptions);\n } catch (err) {\n lastErr = err;\n\n if (err instanceof Error && err.name === 'AbortError' && attempt < maxRetries) {\n await new Promise((r) => setTimeout(r, 100 * Math.pow(2, attempt)));\n continue;\n }\n\n throw err;\n }\n }\n\n throw lastErr ?? new Error('Unknown error during checkout session creation');\n}\n"],"mappings":";AACA,SAAS,eAAAA,oBAAmB;;;ACa5B,SAAQ,mBAAkB;AAM1B,SAAS,oBAAoB,MAAsC;AACjE,QAAM,MAAmC;AAAA,IACvC,SAAS;AAAA,IACT,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AACA,SAAO,IAAI,IAAI;AACjB;AA0BA,SAAS,wBACP,OAC6B;AAC7B,UAAQ,OAAO;AAAA,IACb,KAAK;AAAS,aAAO;AAAA,IACrB,KAAK;AAAQ,aAAO;AAAA;AAAA,IAEpB;AAAS,aAAO;AAAA,EAClB;AACF;AAEA,SAAS,kBACP,eACgB;AAChB,QAAM,KAAK;AAEX,SAAO;AAAA,IACL,MAAM,WAAwB;AAC5B,SAAG,MAAM,SAAS;AAAA,IACpB;AAAA,IACA,UAAU;AACR,SAAG,QAAQ;AAAA,IACb;AAAA,IACA,OAAO,SAAkC;AACvC,SAAG,OAAO,OAAkC;AAAA,IAC9C;AAAA,IACA,GAAG,OAAe,SAAuC;AACvD,MAAC,GAA+D,IAAI,IAAI,OAAO,OAAO;AAAA,IACxF;AAAA,IACA,IAAI,OAAe,SAAuC;AACxD,MAAC,GAA+D,KAAK,IAAI,OAAO,OAAO;AAAA,IACzF;AAAA,IACA,UAAU;AACR,SAAG,QAAQ;AAAA,IACb;AAAA,EACF;AACF;AAQO,IAAM,gBAAN,MAAsD;AAAA,EAAtD;AACL,SAAS,OAAO;AAChB,SAAQ,SAAwB;AAChC,SAAQ,WAAkC;AAK1C;AAAA;AAAA;AAAA;AAAA,SAAQ,uBAAsC;AAAA;AAAA,EAE9C,MAAM,WAAW,QAAqC;AAEpD,QAAI,OAAO,WAAW,aAAa;AACjC;AAAA,IACF;AAEA,QAAI;AACJ,QAAI;AACF,OAAC,EAAE,WAAW,IAAI,MAAM,OAAO,mBAAmB;AAAA,IACpD,QAAQ;AAKN,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,MAAM,qBAAqB;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,WAAW,OAAO,gBAAgB;AAAA,MACrD,QAAS,OAAO,UAAqB;AAAA,IACvC,CAAC;AAED,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGQ,YAAY,SAA0C;AAC5D,QAAI,CAAC,KAAK,QAAQ;AAChB,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,mBAAmB,SAAS,aAC9B;AAAA,MACE,OAAO,wBAAwB,QAAQ,WAAW,KAAK;AAAA,MACvD,WAAW,QAAQ,WAAW;AAAA,MAC9B,OAAO,QAAQ,WAAW;AAAA,IAC5B,IACA;AACJ,UAAM,oBAAoB,mBAAmB,KAAK,UAAU,gBAAgB,IAAI;AAEhF,QAAI,CAAC,KAAK,UAAU;AAClB,UAAI;AAEJ,UAAI,SAAS,cAAc;AAEzB,0BAAkB,EAAE,cAAc,QAAQ,aAAa;AAAA,MACzD,OAAO;AAGL,0BAAkB;AAAA,UAChB,MAAM;AAAA,UACN,QAAQ,SAAS,UAAU;AAAA,UAC3B,WAAW,SAAS,YAAY,OAAO,YAAY;AAAA,UACnD,uBAAuB,SAAS,yBAA0B;AAAA,QAC5D;AAEA,YAAI,SAAS,kBAAkB;AAC7B,0BAAgB,kBAAkB,IAAI,QAAQ;AAAA,QAChD;AAAA,MACF;AAEA,UAAI,kBAAkB;AACpB,wBAAgB,YAAY,IAAI;AAAA,MAClC;AAEA,WAAK,WAAW,KAAK,OAAO,SAAS,eAA+D;AACpG,WAAK,uBAAuB;AAAA,IAC9B,WAAW,sBAAsB,KAAK,sBAAsB;AAM1D,MAAC,KAAK,SAA4E,OAAO;AAAA,QACvF,YAAY,oBAAoB,CAAC;AAAA,MACnC,CAAC;AACD,WAAK,uBAAuB;AAAA,IAC9B;AAEA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,cACJ,MACA,SACyB;AACzB,UAAM,WAAW,KAAK,YAAY,OAAO;AACzC,UAAM,aAAa,oBAAoB,IAAI;AAE3C,UAAM,iBAA0C,CAAC;AACjD,QAAI,QAAQ,QAAQ;AAClB,qBAAe,QAAQ,IAAI,QAAQ;AAAA,IACrC;AACA,QAAI,QAAQ,eAAe;AACzB,qBAAe,eAAe,IAAI,QAAQ;AAAA,IAC5C;AACA,QAAI,QAAQ,UAAU;AACpB,qBAAe,UAAU,IAAI,QAAQ;AAAA,IACvC;AACA,QAAI,QAAQ,MAAM;AAChB,qBAAe,MAAM,IAAI,QAAQ;AAAA,IACnC;AACA,QAAI,QAAQ,OAAO;AACjB,qBAAe,OAAO,IAAI,QAAQ;AAAA,IACpC;AAEA,UAAM,gBAAgB,SAAS,OAAO,YAAsB,cAAc;AAC1E,WAAO,kBAAkB,aAAa;AAAA,EACxC;AAAA,EAEA,WAAW,MAA0C;AACnD,QAAI,CAAC,KAAK,SAAU,QAAO;AAC3B,UAAM,aAAa,oBAAoB,IAAI;AAC3C,UAAM,WAAW,KAAK,SAAS,WAAW,UAAoB;AAC9D,QAAI,CAAC,SAAU,QAAO;AACtB,WAAO,kBAAkB,QAAQ;AAAA,EACnC;AAAA,EAEA,MAAM,iBAAmD;AACvD,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;AAClC,aAAO,EAAE,OAAO,IAAI,YAAY,0BAA0B,WAAW,EAAE;AAAA,IACzE;AACA,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,SAAS,OAAO;AAC7C,QAAI,OAAO;AACT,aAAO;AAAA,QACL,OAAO,IAAI,YAAY,MAAM,WAAW,qBAAqB,kBAAkB;AAAA,MACjF;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAAA,EAEA,MAAM,oBAAoB,gBAAqE;AAC7F,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;AAClC,aAAO;AAAA,QACL,iBAAiB;AAAA,QACjB,OAAO,IAAI,YAAY,0BAA0B,WAAW;AAAA,MAC9D;AAAA,IACF;AAMA,UAAM,eAAe,KAAK,SAAS,WAAW,YAAsB;AAIpE,UAAM,gBAAgB,iBAAiB;AAAA,MACrC,iBAAiB;AAAA,QACf,GAAI,eAAe,QAAQ,EAAE,OAAO,eAAe,MAAM,IAAI,CAAC;AAAA,QAC9D,GAAI,eAAe,OAAO,EAAE,MAAM,eAAe,KAAK,IAAI,CAAC;AAAA,QAC3D,GAAI,eAAe,UAAU;AAAA,UAC3B,SAAS;AAAA,YACP,GAAI,eAAe,QAAQ,UAAU,EAAE,SAAS,eAAe,QAAQ,QAAQ,IAAI,CAAC;AAAA,YACpF,GAAI,eAAe,QAAQ,cAAc,EAAE,aAAa,eAAe,QAAQ,YAAY,IAAI,CAAC;AAAA,YAChG,GAAI,eAAe,QAAQ,OAAO,EAAE,MAAM,eAAe,QAAQ,KAAK,IAAI,CAAC;AAAA,YAC3E,GAAI,eAAe,QAAQ,QAAQ,EAAE,OAAO,eAAe,QAAQ,MAAM,IAAI,CAAC;AAAA,YAC9E,GAAI,eAAe,QAAQ,QAAQ,EAAE,OAAO,eAAe,QAAQ,MAAM,IAAI,CAAC;AAAA,YAC9E,GAAI,eAAe,QAAQ,QAAQ,EAAE,OAAO,eAAe,QAAQ,MAAM,IAAI,CAAC;AAAA,UAChF;AAAA,QACF,IAAI,CAAC;AAAA,MACP;AAAA,IACF,IAAI,CAAC;AAEL,UAAM,EAAE,OAAO,cAAc,IAAI,eAC7B,MAAM,KAAK,OAAO,oBAAoB;AAAA,MACpC,MAAM;AAAA,MACN,MAAM;AAAA,MACN,GAAG;AAAA,IACL,CAAqE,IACrE,MAAM,KAAK,OAAO,oBAAoB;AAAA,MACpC,UAAU,KAAK;AAAA,MACf,GAAG;AAAA,IACL,CAAqE;AAEzE,QAAI,OAAO;AACT,aAAO;AAAA,QACL,iBAAiB;AAAA,QACjB,OAAO,IAAI;AAAA,UACT,MAAM,WAAW;AAAA,UACjB;AAAA,UACA,EAAE,MAAM,MAAM,KAAK;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,EAAE,iBAAiB,cAAc,GAAG;AAAA,EAC7C;AAAA,EAEA,MAAM,mBACJ,QACmC;AACnC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,IAAI,YAAY,0BAA0B,WAAW;AAAA,MAC9D;AAAA,IACF;AAEA,UAAM,EAAE,OAAO,cAAc,IAAI,MAAM,KAAK,OAAO;AAAA,MACjD,OAAO;AAAA,MACP,EAAE,gBAAgB,OAAO,gBAAgB;AAAA,IAC3C;AAEA,QAAI,OAAO;AACT,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,IAAI;AAAA,UACT,MAAM,WAAW;AAAA,UACjB;AAAA,UACA,EAAE,MAAM,MAAM,MAAM,aAAa,MAAM,aAAa;AAAA,QACtD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,MACL,QAAS,eAAe,UAAiD;AAAA,MACzE,iBAAiB,eAAe;AAAA,MAChC,iBAAiB,KAAK,uBAAuB,eAAe,cAAc;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,QAAsD;AACzE,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,UAAU;AAClC,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,UAAU,OAAO;AACvB,UAAM,oBAAoB,UAAU;AAAA,MAClC,iBAAiB;AAAA,QACf,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAChD,GAAI,QAAQ,OAAO,EAAE,MAAM,QAAQ,KAAK,IAAI,CAAC;AAAA,QAC7C,GAAI,QAAQ,UAAU;AAAA,UACpB,SAAS;AAAA,YACP,GAAI,QAAQ,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,QAAQ,IAAI,CAAC;AAAA,YACtE,GAAI,QAAQ,QAAQ,cAAc,EAAE,aAAa,QAAQ,QAAQ,YAAY,IAAI,CAAC;AAAA,YAClF,GAAI,QAAQ,QAAQ,OAAO,EAAE,MAAM,QAAQ,QAAQ,KAAK,IAAI,CAAC;AAAA,YAC7D,GAAI,QAAQ,QAAQ,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,YAChE,GAAI,QAAQ,QAAQ,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,YAChE,GAAI,QAAQ,QAAQ,QAAQ,EAAE,OAAO,QAAQ,QAAQ,MAAM,IAAI,CAAC;AAAA,UAClE;AAAA,QACF,IAAI,CAAC;AAAA,MACP;AAAA,IACF,IAAI;AAEJ,UAAM,EAAE,OAAO,cAAc,IAAI,MAAM,KAAK,OAAO,eAAe;AAAA,MAChE,UAAU,KAAK;AAAA,MACf,cAAc,OAAO;AAAA,MACrB,eAAe;AAAA,QACb,YAAY,OAAO,aAAa,OAAO,SAAS;AAAA,QAChD,GAAI,oBAAoB,EAAE,qBAAqB,kBAAkB,IAAI,CAAC;AAAA,MACxE;AAAA,MACA,UAAU;AAAA,IACZ,CAAC;AAED,QAAI,OAAO;AACT,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,IAAI;AAAA,UACT,MAAM,WAAW;AAAA,UACjB;AAAA,UACA;AAAA,YACE,MAAM,MAAM;AAAA,YACZ,aAAa,MAAM;AAAA,UACrB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,eAAe;AAClB,aAAO,EAAE,QAAQ,UAAU,OAAO,IAAI,YAAY,8BAA8B,WAAW,EAAE;AAAA,IAC/F;AAEA,UAAM,YAAqD;AAAA,MACzD,WAAW;AAAA,MACX,YAAY;AAAA,MACZ,iBAAiB;AAAA,MACjB,yBAAyB;AAAA,MACzB,UAAU;AAAA,IACZ;AAEA,WAAO;AAAA,MACL,QAAQ,UAAU,cAAc,MAAM,KAAK;AAAA,MAC3C,iBAAiB,cAAc;AAAA,MAC/B,iBAAiB,KAAK,uBAAuB,cAAc,cAAc;AAAA,IAC3E;AAAA,EACF;AAAA,EAEQ,uBACN,eACoB;AACpB,QAAI,OAAO,kBAAkB,YAAY,cAAc,WAAW,KAAK,GAAG;AACxE,aAAO;AAAA,IACT;AACA,QAAI,iBAAiB,OAAO,kBAAkB,YAAY,OAAO,cAAc,OAAO,UAAU;AAC9F,aAAO,cAAc;AAAA,IACvB;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,qBAAqB,QAMW;AACpC,QAAI,CAAC,KAAK,QAAQ;AAChB,aAAO,EAAE,QAAQ,UAAU,OAAO,IAAI,YAAY,0BAA0B,WAAW,EAAE;AAAA,IAC3F;AAEA,UAAM,UAAU,OAAO,cAAc,QAAQ,QAAQ,EAAE;AAEvD,QAAI,KAAK,UAAU;AACjB,YAAM,EAAE,OAAO,YAAY,IAAI,MAAM,KAAK,SAAS,OAAO;AAC1D,UAAI,aAAa;AACf,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,OAAO,IAAI;AAAA,YACT,YAAY,WAAW;AAAA,YACvB;AAAA,YACA,EAAE,MAAM,YAAY,KAAK;AAAA,UAC3B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,gBAAwC,EAAE,gBAAgB,mBAAmB;AACnF,QAAI,OAAO,MAAO,eAAc,0BAA0B,IAAI,OAAO;AAErE,UAAM,iBAAiB,MAAM,MAAM,GAAG,OAAO,kCAAkC;AAAA,MAC7E,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM,KAAK,UAAU;AAAA,QACnB,WAAW,OAAO;AAAA,QAClB,OAAO,OAAO;AAAA,QACd,mBAAmB;AAAA,QACnB,UAAU;AAAA,MACZ,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,eAAe,IAAI;AACtB,aAAO,EAAE,QAAQ,UAAU,OAAO,IAAI,YAAY,0CAA0C,WAAW,EAAE;AAAA,IAC3G;AAEA,UAAM,aAAa,MAAM,eAAe,KAAK;AAC7C,UAAM,qBAAqB,WAAW,MAAM;AAC5C,QAAI,CAAC,oBAAoB;AACvB,aAAO,EAAE,QAAQ,UAAU,OAAO,IAAI,YAAY,gCAAgC,WAAW,EAAE;AAAA,IACjG;AAEA,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,OAAO,eAAe;AAAA,MACjD,cAAc;AAAA,MACd,UAAU,KAAK,YAAY;AAAA,MAC3B,eAAe,EAAE,YAAY,OAAO,UAAU;AAAA,IAChD,CAAC;AAED,QAAI,OAAO;AACT,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,IAAI,YAAY,MAAM,WAAW,yBAAyB,aAAa,EAAE,MAAM,MAAM,KAAK,CAAC;AAAA,MACpG;AAAA,IACF;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EAEA,MAAM,sBAAgE;AACpE,QAAI,CAAC,KAAK,UAAU,OAAO,WAAW,YAAa,QAAO;AAE1D,UAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,UAAM,kBAAkB,OAAO,IAAI,gBAAgB;AACnD,UAAM,eAAe,OAAO,IAAI,8BAA8B;AAC9D,UAAM,iBAAiB,OAAO,IAAI,iBAAiB;AAEnD,QAAI,CAAC,mBAAmB,CAAC,aAAc,QAAO;AAE9C,QAAI,mBAAmB,UAAU;AAC/B,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,IAAI,YAAY,kDAAkD,WAAW;AAAA,MACtF;AAAA,IACF;AAEA,UAAM,EAAE,eAAe,MAAM,IAAI,MAAM,KAAK,OAAO,sBAAsB,YAAY;AAErF,QAAI,OAAO;AACT,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,OAAO,IAAI,YAAY,MAAM,WAAW,qCAAqC,WAAW;AAAA,MAC1F;AAAA,IACF;AAEA,QAAI,kBAAkB,cAAc,WAAW,sBAAsB,cAAc,WAAW,cAAc;AAC1G,YAAM,OAAO,OAAO,cAAc,mBAAmB,WACjD,cAAc,iBACd,cAAc,gBAAgB;AAGlC,YAAM,MAAM,IAAI,IAAI,OAAO,SAAS,IAAI;AACxC,UAAI,aAAa,OAAO,gBAAgB;AACxC,UAAI,aAAa,OAAO,8BAA8B;AACtD,UAAI,aAAa,OAAO,iBAAiB;AACzC,aAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC;AAElD,aAAO;AAAA,QACL,QAAQ,cAAc;AAAA,QACtB,iBAAiB,cAAc;AAAA,QAC/B,iBAAiB;AAAA,MACnB;AAAA,IACF;AAEA,WAAO;AAAA,MACL,QAAQ;AAAA,MACR,OAAO,IAAI,YAAY,uDAAuD,WAAW;AAAA,IAC3F;AAAA,EACF;AAAA,EAEA,iBAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,qBAAqB,SAAkC;AACrD,QAAI,CAAC,KAAK,OAAQ,QAAO;AAEzB,UAAM,kBAA2C;AAAA,MAC/C,MAAM;AAAA,MACN,QAAQ,QAAQ,UAAU;AAAA,MAC1B,WAAW,QAAQ,YAAY,OAAO,YAAY;AAAA,MAClD,eAAe;AAAA,IACjB;AAEA,QAAI,QAAQ,kBAAkB;AAC5B,sBAAgB,kBAAkB,IAAI,QAAQ;AAAA,IAChD;AAEA,QAAI,QAAQ,YAAY;AACtB,sBAAgB,YAAY,IAAI;AAAA,QAC9B,OAAO,wBAAwB,QAAQ,WAAW,KAAK;AAAA,QACvD,WAAW,QAAQ,WAAW;AAAA,QAC9B,OAAO,QAAQ,WAAW;AAAA,MAC5B;AAAA,IACF;AAEA,WAAO,KAAK,OAAO,SAAS,eAA+D;AAAA,EAC7F;AAAA,EAEA,UAAgB;AACd,SAAK,WAAW;AAChB,SAAK,uBAAuB;AAC5B,SAAK,SAAS;AAAA,EAChB;AACF;;;ACxjBA,SAAS,eAAAC,cAAa,4BAA4B;;;ACNlD,OAA4B;AAQrB,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YAAY,UAAkC,SAA0B;AAHxE,SAAiB,aAAa,oBAAI,IAAiC;AAIjE,SAAK,WAAW;AAChB,SAAK,cAAc,WAAW,CAAC;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OACJ,MACA,SACyB;AAIzB,UAAM,mBAAmB,KAAK,SAAS,WAAW,IAAI;AACtD,QAAI,kBAAkB;AACpB,WAAK,WAAW,IAAI,MAAM,gBAAgB;AAC1C,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,KAAK,WAAW,IAAI,IAAI;AACzC,QAAI,UAAU;AACZ,eAAS,QAAQ;AAAA,IACnB;AAEA,UAAM,SAAyB,EAAE,GAAG,KAAK,aAAa,GAAG,QAAQ;AACjE,UAAM,UAAU,MAAM,KAAK,SAAS,cAAc,MAAM,MAAM;AAC9D,SAAK,WAAW,IAAI,MAAM,OAAO;AACjC,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,WAAW,MAA0C;AACnD,WAAO,KAAK,WAAW,IAAI,IAAI,KAAK;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAA2C;AAK/C,WAAO,CAAC;AAAA,EACV;AAAA;AAAA,EAGA,UAAgB;AACd,eAAW,WAAW,KAAK,WAAW,OAAO,GAAG;AAC9C,cAAQ,QAAQ;AAAA,IAClB;AACA,SAAK,WAAW,MAAM;AAAA,EACxB;AACF;;;AClEA;AAAA,EACE,eAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACJP,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB,KAAK,KAAK;AA2BjC,IAAM,cAAc,oBAAI,IAAyB;AAEjD,SAAS,WAAW,WAA2B;AAC7C,SAAO,GAAG,kBAAkB,GAAG,SAAS;AAC1C;AAEA,SAAS,oBAAoC;AAC3C,MAAI,OAAO,WAAW,YAAa,QAAO;AAC1C,MAAI;AACF,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAOO,SAAS,wBACd,WACA,MACA,SACM;AACN,MAAI,CAAC,UAAW;AAEhB,QAAM,MAAM,SAAS,SAAS;AAC9B,QAAM,QAAqB,EAAE,MAAM,WAAW,KAAK,IAAI,IAAI,IAAI;AAE/D,QAAM,UAAU,kBAAkB;AAClC,MAAI,SAAS;AACX,QAAI;AACF,cAAQ,QAAQ,WAAW,SAAS,GAAG,KAAK,UAAU,KAAK,CAAC;AAC5D;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,cAAY,IAAI,WAAW,KAAK;AAClC;AAMO,SAAS,sBACd,WACgC;AAChC,MAAI,CAAC,UAAW,QAAO;AAEvB,QAAM,UAAU,kBAAkB;AAClC,MAAI,SAAS;AACX,QAAI;AACF,YAAM,MAAM,QAAQ,QAAQ,WAAW,SAAS,CAAC;AACjD,UAAI,KAAK;AACP,cAAM,QAAQ,KAAK,MAAM,GAAG;AAC5B,YAAI,SAAS,OAAO,MAAM,cAAc,YAAY,MAAM,YAAY,KAAK,IAAI,GAAG;AAChF,iBAAO,MAAM;AAAA,QACf;AACA,gBAAQ,WAAW,WAAW,SAAS,CAAC;AAAA,MAC1C;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,WAAW,YAAY,IAAI,SAAS;AAC1C,MAAI,UAAU;AACZ,QAAI,SAAS,YAAY,KAAK,IAAI,GAAG;AACnC,aAAO,SAAS;AAAA,IAClB;AACA,gBAAY,OAAO,SAAS;AAAA,EAC9B;AAEA,SAAO;AACT;AAMO,SAAS,wBAAwB,WAAyB;AAC/D,MAAI,CAAC,UAAW;AAEhB,cAAY,OAAO,SAAS;AAE5B,QAAM,UAAU,kBAAkB;AAClC,MAAI,SAAS;AACX,QAAI;AACF,cAAQ,WAAW,WAAW,SAAS,CAAC;AAAA,IAC1C,QAAQ;AAAA,IAER;AAAA,EACF;AACF;;;AD5CA,IAAM,oCAAoC;AAC1C,IAAM,gCAAgC;AACtC,IAAM,gCAAgC;AACtC,IAAM,gCAAgC;AAEtC,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU;AAChD;AAEA,SAAS,WAAW,SAA0B,KAAiC;AAC7E,QAAM,QAAQ,UAAU,GAAG;AAC3B,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAEA,SAAS,WAAW,SAA0B,KAAiC;AAC7E,QAAM,QAAQ,UAAU,GAAG;AAC3B,SAAO,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK,IAAI,QAAQ;AACvE;AAEA,SAAS,MAAM,IAA2B;AACxC,SAAO,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AACzD;AAEA,SAAS,uCAAoD;AAC3D,SAAO,IAAIC;AAAA,IACT;AAAA,IACA;AAAA,IACA,EAAE,MAAM,8BAA8B;AAAA,EACxC;AACF;AAEA,eAAe,0BACb,UACA,iBACsB;AACtB,QAAM,UAAU,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AACtD,QAAM,cAAc,SAAS,SAAS,KAAK,IAAI,QAAQ,QAAQ;AAC/D,QAAM,UACJ,WAAW,SAAS,SAAS,KAC7B,WAAW,aAAa,SAAS,KACjC;AACF,QAAM,OACJ,WAAW,SAAS,MAAM,KAC1B,WAAW,SAAS,kBAAkB,KACtC,WAAW,aAAa,MAAM,KAC9B,QAAQ,SAAS,MAAM;AAEzB,SAAO,IAAIA,aAAY,SAAS,aAAa;AAAA,IAC3C;AAAA,IACA,YAAY,SAAS;AAAA,EACvB,CAAC;AACH;AAMA,IAAM,yBAAyB;AAgB/B,eAAe,sBACb,OACA,MACA,WAAW,wBACQ;AACnB,MAAI;AACJ,WAAS,UAAU,KAAK,WAAW;AACjC,QAAI;AACF,aAAO,MAAM,MAAM,OAAO,IAAI;AAAA,IAChC,SAAS,KAAK;AAEZ,UAAI,eAAe,SAAS,IAAI,SAAS,aAAc,OAAM;AAC7D,gBAAU;AACV,UAAI,WAAW,SAAU,OAAM;AAC/B,YAAM,MAAM,MAAM,KAAK,OAAO;AAAA,IAChC;AAAA,EACF;AACF;AASO,IAAM,aAAN,MAAiB;AAAA,EAGtB,YAAY,eAAuB;AACjC,SAAK,UAAU,cAAc,QAAQ,QAAQ,EAAE;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,mBACJ,mBACA,OAC8C;AAC9C,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,KAAK,OAAO,0BAA0B,mBAAmB,iBAAiB,CAAC;AAAA,MAC9E,QAAQ,EAAE,SAAS,EAAE,4BAA4B,MAAM,EAAE,IAAI;AAAA,IAC/D;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,MAAM,0BAA0B,UAAU,gCAAgC;AAAA,IAClF;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO,EAAE,GAAG,MAAM,MAAM,KAAK,uBAAuB,KAAK,IAAI,EAAE;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,wBACE,WACA,MACA,SACM;AACN,4BAAwB,WAAW,MAAM,OAAO;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,wBAAwB,WAAyB;AAC/C,4BAAwB,SAAS;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,0BACJ,mBACA,OACoC;AACpC,UAAM,MAAM,MAAM,KAAK,mBAAmB,mBAAmB,KAAK;AAClE,WAAO,KAAK,oBAAoB,IAAI,IAAI;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmBA,MAAM,eACJ,SACA,MACA,SAGmB;AACnB,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,MAAM,+BAA+B,OAAO,QAAQ;AAAA,MACxD;AAAA,IACF;AAEA,UAAM,EAAE,OAAO,GAAG,YAAY,IAAI;AAClC,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,KAAK,OAAO,0BAA0B,mBAAmB,KAAK,SAAS,CAAC;AAAA,MAC3E;AAAA,QACE,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,4BAA4B;AAAA,QAC9B;AAAA,QACA,MAAM,KAAK,UAAU,WAAW;AAAA,MAClC;AAAA,IACF;AAEA,WAAO,KAAK,uBAAuB,UAAU,KAAK,WAAW,EAAE,GAAG,SAAS,MAAM,CAAC;AAAA,EACpF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,oBACJ,WACA,OACA,mBACA,SACmB;AACnB,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,SAAS,MAAO,SAAQ,0BAA0B,IAAI,QAAQ;AAClE,WAAO;AAAA,MACL,GAAG,KAAK,OAAO;AAAA,MACf;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA;AAAA,UACA;AAAA,UACA,UAAU,SAAS,YAAY;AAAA,QACjC,CAAC;AAAA,QACD,QAAQ,SAAS;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBACJ,WACA,OACA,mBACA,SACmB;AACnB,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,SAAS,MAAO,SAAQ,0BAA0B,IAAI,QAAQ;AAClE,WAAO;AAAA,MACL,GAAG,KAAK,OAAO;AAAA,MACf;AAAA,QACE,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,WAAW,OAAO,kBAAkB,CAAC;AAAA,QAC5D,QAAQ,SAAS;AAAA,MACnB;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,mBACJ,OACA,SACsF;AACtF,UAAM,OAAO,SAAS,QAAQ;AAC9B,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,SAAS,IAAI,gBAAgB;AAAA,MACjC;AAAA,MACA,MAAM,OAAO,IAAI;AAAA,MACjB,OAAO,OAAO,KAAK;AAAA,MACnB,WAAW;AAAA,MACX,eAAe;AAAA,IACjB,CAAC;AAED,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,KAAK,OAAO,gBAAgB,OAAO,SAAS,CAAC;AAAA,MAChD;AAAA,QACE,QAAQ;AAAA,QACR,QAAQ,SAAS;AAAA,QACjB,WAAW;AAAA,MACb;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAIA,aAAY,4BAA4B,WAAW;AAAA,IAC/D;AAEA,WAAO,SAAS,KAAK;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,sBACJ,QACoC;AACpC,UAAM,eAAe,OAAO,YAAY,iBAAiB,OAAO,OAAO,OAAO,aAAa;AAC3F,UAAM,kBAAkB;AAAA,MACtB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,MACP;AAAA,IACF;AACA,QAAI,CAAC,iBAAiB;AACpB,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,MAAM,oBAAoB,OAAO,WAAW;AAAA,MAChD;AAAA,IACF;AAEA,UAAM,UAAmC;AAAA,MACvC,UAAU,OAAO;AAAA,MACjB,iBAAiB;AAAA,MACjB,YAAY,OAAO;AAAA,MACnB,WAAW,OAAO;AAAA,MAClB,UAAU;AAAA,MACV,cAAc,OAAO,gBAAgB;AAAA,MACrC,UAAU,aAAa,IAAI,CAAC,YAAY,oBAAoB,SAAS,eAAe,CAAC;AAAA,MACrF,aAAa;AAAA,QACX,QAAQ,OAAO,QAAQ;AAAA,QACvB,WAAW,OAAO,QAAQ,aAAa;AAAA,QACvC,UAAU,OAAO,QAAQ,YAAY;AAAA,QACrC,OAAO,OAAO,QAAQ;AAAA,QACtB,SAAS,OAAO,QAAQ,WAAW;AAAA,QACnC,QAAQ,OAAO,QAAQ,UAAU;AAAA,QACjC,MAAM,OAAO,QAAQ,QAAQ;AAAA,QAC7B,OAAO,OAAO,QAAQ,SAAS;AAAA,QAC/B,KAAK,OAAO,QAAQ,OAAO;AAAA,QAC3B,cAAc,OAAO,QAAQ,gBAAgB;AAAA,QAC7C,cAAc,OAAO,QAAQ,gBAAgB;AAAA,MAC/C;AAAA,MACA,aAAa,OAAO,eAAe,CAAC;AAAA,IACtC;AAEA,QAAI,OAAO,cAAe,SAAQ,eAAe,IAAI,OAAO;AAC5D,QAAI,OAAO,SAAU,SAAQ,UAAU,IAAI,OAAO;AAClD,QAAI,OAAO,aAAa,OAAQ,SAAQ,aAAa,IAAI,OAAO;AAGhE,QAAI,OAAO,aAAa,OAAW,SAAQ,UAAU,IAAI,OAAO;AAChE,QAAI,OAAO,aAAc,SAAQ,cAAc,IAAI,OAAO;AAC1D,QAAI,OAAO,eAAgB,SAAQ,gBAAgB,IAAI,OAAO;AAC9D,QAAI,OAAO,UAAW,SAAQ,WAAW,IAAI,OAAO;AAEpD,UAAM,WAAW,MAAM;AAAA,MACrB,GAAG,KAAK,OAAO;AAAA,MACf;AAAA,QACE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC9B;AAAA,IACF;AAEA,QAAI,SAAS,WAAW,KAAK;AAC3B,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,MAAM,yBAAyB;AAAA,MACnC;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,MAAM,0BAA0B,UAAU,mCAAmC;AAAA,IACrF;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AASjC,QAAI,KAAK,QAAQ,cAAc,KAAK,MAAM;AAIxC,WAAK,qBAAqB,KAAK,KAAK,MAAM,MAAM;AAChD,YAAM,SAAS,KAAK,uBAAuB,KAAK,IAAI;AACpD,aAAO;AAAA,QACL,GAAG,KAAK,oBAAoB,MAAM;AAAA,QAClC,qBAAqB,KAAK;AAAA,QAC1B,yBAAyB,KAAK;AAAA,QAC9B,uBAAuB,KAAK;AAAA,MAC9B;AAAA,IACF;AAGA,UAAM,OAAO,KAAK,MAAM;AACxB,QAAI,CAAC,MAAM;AACT,YAAM,IAAIA,aAAY,0BAA0B,WAAW;AAAA,IAC7D;AACA,SAAK,qBAAqB,MAAM,MAAM;AACtC,UAAM,iBAAiB,MAAM,KAAK,0BAA0B,IAAI;AAChE,WAAO;AAAA,MACL,GAAG;AAAA,MACH,qBAAqB,KAAK;AAAA,MAC1B,yBAAyB,KAAK;AAAA,MAC9B,uBAAuB,KAAK;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,MAAM,iCACJ,mBACA,SASoC;AACpC,UAAM,YAAY,SAAS,aAAa;AACxC,UAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,QAAI,cAAc,KAAK,kBAAkB,SAAS,kBAAkB,iCAAiC;AAErG,WAAO,MAAM;AACX,YAAM,cAAc,WAAW,KAAK,IAAI;AACxC,UAAI,eAAe,GAAG;AACpB,cAAM,qCAAqC;AAAA,MAC7C;AAEA,UAAI,cAAc,GAAG;AACnB,cAAM,MAAM,KAAK,IAAI,aAAa,WAAW,CAAC;AAE9C,YAAI,KAAK,IAAI,KAAK,UAAU;AAC1B,gBAAM,qCAAqC;AAAA,QAC7C;AAAA,MACF;AAEA,YAAM,UAAU,MAAM,KAAK,0BAA0B,mBAAmB,SAAS,KAAK;AACtF,YAAM,SAAS,QAAQ,KAAK,SAAS;AAErC,UAAI,WAAW,cAAc,WAAW,WAAW;AACjD,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,IAAI,KAAK,UAAU;AAC1B,cAAM,qCAAqC;AAAA,MAC7C;AAEA,oBAAc,KAAK;AAAA,QACjB,KAAK,IAAI,cAAc,GAAG,6BAA6B;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGQ,oBAAoB,SAAwD;AAClF,UAAM,WAA6B,QAAQ,YAAY,CAAC;AACxD,UAAM,YAA+B,CAAC;AACtC,UAAM,OAA0C;AAAA,MAC9C,SAAS,KAAK,kBAAkB,OAAO;AAAA,IACzC;AAEA,UAAM,gBAAgB,SAAS;AAC/B,QAAI,eAAe,gBAAgB;AACjC,gBAAU,KAAK,QAAQ;AACvB,YAAM,aAAa;AACnB,YAAM,qBAAqB;AAAA,QACzB,WAAW,oBAAoB;AAAA,QAC/B,cAAc;AAAA,MAChB,EAAE,KAAK,CAAC,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;AAE/D,WAAK,SAAS;AAAA,QACZ,cAAc,sBAAsB;AAAA,QACpC,gBAAgB,cAAc,kBAAkB;AAAA,QAChD,sBAAsB,cAAc,wBAAwB;AAAA,QAC5D,aAAa,cAAc;AAAA,QAC3B,uBAAuB,MAAM,QAAQ,cAAc,qBAAqB,IACpE,cAAc,sBAAsB,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ,IACpF;AAAA,MACN;AAAA,IACF;AAEA,UAAM,gBAAgB,SAAS;AAC/B,QAAI,eAAe,gBAAgB;AACjC,gBAAU,KAAK,QAAQ;AACvB,WAAK,SAAS;AAAA,QACZ,gBAAgB,cAAc;AAAA,QAC9B,aAAa,cAAc;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO;AAAA,MACL;AAAA,MACA,MAAM;AAAA,MACN;AAAA,MACA,KAAK,EAAE,MAAM,QAAQ;AAAA,IACvB;AAAA,EACF;AAAA;AAAA,EAGQ,kBAAkB,KAA0C;AAClE,UAAM,cAAc,IAAI,YAAY,CAAC;AAKrC,UAAM,kBACJ,OAAO,IAAI,gBAAgB,YAAY,OAAO,SAAS,IAAI,WAAW;AACxE,UAAM,gBAAgB,YAAY;AAAA,MAChC,CAAC,KAAK,MAAM,OAAO,EAAE,kBAAkB,EAAE,eAAe;AAAA,MACxD;AAAA,IACF;AACA,UAAM,cAAc,kBAAmB,IAAI,cAAyB;AAIpE,UAAM,gBAAgB,KAAK,MAAM,cAAc,GAAG;AAElD,UAAM,WAAW,IAAI,YAAY,YAAY,CAAC,GAAG,YAAY;AAE7D,UAAM,OACJ,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,cAAc,IAAI,iBAAiB;AAExE,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,cAAc,IAAI;AAAA,MAClB;AAAA,MACA,QAAQ,KAAK,wBAAwB,IAAI,MAAM;AAAA,MAC/C,QAAQ;AAAA,MACR;AAAA,MACA,UAAU;AAAA,QACR,IAAI,IAAI,YAAY;AAAA,QACpB,OAAO,IAAI,YAAY;AAAA,QACvB,WAAW,IAAI,YAAY;AAAA,QAC3B,UAAU,IAAI,YAAY;AAAA,QAC1B,SAAS,IAAI,YAAY,WAAW;AAAA,QACpC,MAAM,IAAI,YAAY,QAAQ;AAAA,QAC9B,OAAO,IAAI,YAAY,SAAS;AAAA,QAChC,KAAK,IAAI,YAAY,OAAO;AAAA,QAC5B,QAAQ,IAAI,YAAY,UAAU;AAAA,QAClC,OAAO,IAAI,YAAY,gBAAgB;AAAA,QACvC,OAAO,IAAI,YAAY,gBAAgB;AAAA,MACzC;AAAA,MACA,UAAU,CAAC;AAAA,MAEX,cAAc,IAAI;AAAA,MAClB,UAAU,YAAY,IAAI,CAAC,OAA+B;AAAA,QACxD,GAAG;AAAA,QACH,aAAa,OAAO,EAAE,gBAAgB,WAAW,EAAE,cAAc;AAAA,QACjE,gBAAgB,OAAO,EAAE,mBAAmB,WAAW,EAAE,iBAAiB;AAAA,QAC1E,UAAU,OAAO,EAAE,aAAa,WAAW,EAAE,WAAW;AAAA,QACxD,UAAU,EAAE,YAAY;AAAA,MAC1B,EAAE;AAAA,MACF,YAAY,IAAI;AAAA,MAChB,WAAW,IAAI;AAAA,MACf,SAAS,IAAI;AAAA,MACb,gBAAgB,IAAI;AAAA,MACpB,gBAAgB,IAAI;AAAA,MACpB,aAAa,IAAI;AAAA,MACjB,WAAW,IAAI;AAAA,MACf,UAAU,IAAI;AAAA,MACd,aAAa,IAAI;AAAA,MACjB,UAAU,IAAI;AAAA,IAChB;AAAA,EACF;AAAA,EAEQ,wBAAwB,QAAiE;AAC/F,QAAI,WAAW,aAAa;AAC1B,aAAO;AAAA,IACT;AAEA,QAAI,WAAW,WAAW;AACxB,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,uBACZ,UACA,mBACA,SAImB;AACnB,QAAI,SAAS,WAAW,KAAK;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,UAAU,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,IAAI;AACtD,UAAM,UAAU,KAAK,4BAA4B,SAAS,UAAU,iBAAiB;AACrF,UAAM,UAAU,MAAM,KAAK,iCAAiC,QAAQ,WAAW;AAAA,MAC7E,gBAAgB,QAAQ;AAAA,MACxB,WAAW,SAAS;AAAA,MACpB,OAAO,SAAS;AAAA,IAClB,CAAC;AAED,QAAI,QAAQ,KAAK,SAAS,WAAW,YAAY;AAC/C,aAAO,IAAI,SAAS,MAAM,EAAE,QAAQ,KAAK,YAAY,aAAa,CAAC;AAAA,IACrE;AAEA,QAAI,QAAQ,KAAK,SAAS,WAAW,WAAW;AAC9C,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,MAAM,2BAA2B;AAAA,MACrC;AAAA,IACF;AAEA,UAAM,qCAAqC;AAAA,EAC7C;AAAA,EAEQ,4BACN,SACA,UACA,mBAC2B;AAC3B,UAAM,mBAAmB,SAAS,QAAQ,IAAI,aAAa;AAC3D,UAAM,0BAA0B,qBAAqB,QAChD,iBAAiB,KAAK,MAAM,KAC7B,SACA,OAAO,gBAAgB;AAC3B,UAAM,qBAAqB,4BAA4B,UAClD,OAAO,SAAS,uBAAuB,IACxC,0BAA0B,MAC1B;AAEJ,WAAO;AAAA,MACL,MAAM;AAAA,MACN,WAAW,WAAW,SAAS,WAAW,KAAK;AAAA,MAC/C,cAAc,KAAK;AAAA,QACjB,WAAW,SAAS,cAAc,KAC7B,sBACA;AAAA,MACP;AAAA,MACA,WAAW,WAAW,SAAS,WAAW;AAAA,MAC1C,YAAY,WAAW,SAAS,YAAY;AAAA,IAC9C;AAAA,EACF;AAAA,EAEQ,kBAAkB,cAA8B;AACtD,WAAO,KAAK,IAAI,GAAG,KAAK,IAAI,cAAc,6BAA6B,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,qBACN,WACA,QACM;AACN,QAAI,CAAC,UAAW;AAChB,UAAM,WAAW,OAAO,YAAY,iBAAiB,OAAO,OAAO,OAAO,aAAa;AACvF,QAAI,SAAS,WAAW,KAAK,CAAC,OAAO,UAAU;AAC7C;AAAA,IACF;AACA,UAAM,uBAAuB,OAAO,aAAa;AACjD,UAAM,kBAAkB;AAAA,MACtB,OAAO;AAAA,MACP,uBAAuB,SAAY,OAAO;AAAA,MAC1C,uBAAuB,SAAY,OAAO;AAAA,MAC1C;AAAA,IACF;AACA,4BAAwB,WAAW;AAAA,MACjC,UAAU,mBAAmB;AAAA,MAC7B,UAAU,SAAS,IAAI,CAAC,OAAO;AAAA,QAC7B,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE;AAAA,QACtC,MAAM,EAAE;AAAA,QACR,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,oBAAoB;AAAA,QAChG,aAAa,EAAE;AAAA,QACf,gBAAgB,EAAE;AAAA,QAClB,UAAU,EAAE,YAAY,mBAAmB;AAAA,MAC7C,EAAE;AAAA,IACJ,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,uBAAuB,KAA6C;AAC1E,UAAM,SAAS,sBAAsB,IAAI,IAAI;AAE7C,UAAM,iBAAiB,oBAAI,IAAmC;AAK9D,UAAM,aAAa,CAAC,SAClB,OAAO,QAAQ,IAAI,KAAK;AAC1B,eAAW,KAAK,QAAQ,YAAY,CAAC,GAAG;AACtC,YAAM,MAAM,WAAW,EAAE,IAAI;AAC7B,UAAI,IAAK,gBAAe,IAAI,KAAK,CAAC;AAAA,IACpC;AAEA,UAAM,kBAAkB,IAAI,YAAY,CAAC,GAAG,IAAI,CAAC,MAAM;AACrD,YAAM,MAAM,WAAW,EAAE,IAAI;AAC7B,YAAM,WAAW,MAAM,eAAe,IAAI,GAAG,IAAI;AACjD,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM,EAAE,QAAQ,UAAU,QAAQ;AAAA,QAClC,aAAa,EAAE,eAAe,UAAU;AAAA,QACxC,gBAAgB,EAAE,kBAAkB,UAAU;AAAA,QAC9C,UAAU,EAAE,YAAY,UAAU;AAAA,MACpC;AAAA,IACF,CAAC;AAED,WAAO;AAAA,MACL,GAAG;AAAA,MACH,UAAU,IAAI,YAAY,QAAQ;AAAA,MAClC,UAAU;AAAA,IACZ;AAAA,EACF;AACF;;;AF3zBO,IAAM,SAAN,MAAa;AAAA,EAKlB,YAAY,UAAkC,QAAsB;AAFpE,SAAQ,kBAAyC;AAG/C,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS,SAA0C;AACjD,QAAI,KAAK,iBAAiB;AACxB,WAAK,gBAAgB,QAAQ;AAAA,IAC/B;AACA,SAAK,kBAAkB,IAAI,eAAe,KAAK,UAAU;AAAA,MACvD,YAAY,KAAK,OAAO;AAAA,MACxB,GAAG;AAAA,IACL,CAAC;AACD,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAM,iBAA4E;AAChF,WAAO,KAAK,SAAS,eAAe;AAAA,EACtC;AAAA;AAAA,EAGA,MAAM,oBAAoB,gBAA8F;AACtH,WAAO,KAAK,SAAS,oBAAoB,cAAc;AAAA,EACzD;AAAA;AAAA,EAGA,MAAM,mBAAmB,QAAqE;AAC5F,WAAO,KAAK,SAAS,mBAAmB,MAAM;AAAA,EAChD;AAAA;AAAA,EAGA,MAAM,qBAAqB,QAWW;AACpC,WAAO,KAAK,SAAS,qBAAqB,MAAM;AAAA,EAClD;AAAA;AAAA,EAGA,MAAM,sBAAgE;AACpE,WAAO,KAAK,SAAS,oBAAoB;AAAA,EAC3C;AAAA;AAAA,EAGA,MAAM,eAAe,QAAsD;AACzE,WAAO,KAAK,SAAS,eAAe,MAAM;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,gBACJ,WACA,eAC0B;AAC1B,QAAI,CAAC,WAAW;AACd,YAAM,IAAIC;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,OAAO,YAAY;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,SAAS,qBAAqB,iBAAiB,KAAK,OAAO,aAAa;AAE9E,UAAM,MAAM,IAAI,WAAW,MAAM;AACjC,UAAM,UAAU,MAAM,IAAI,0BAA0B,SAAS;AAC7D,QAAI,CAAC,QAAQ,KAAK,SAAS;AACzB,YAAM,IAAIA,aAAY,qBAAqB,WAAW;AAAA,IACxD;AACA,WAAO,QAAQ,KAAK;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,uBACJ,WACA,eACoC;AACpC,QAAI,CAAC,WAAW;AACd,YAAM,IAAIA;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,OAAO,YAAY;AAAA,MACvB;AAAA,IACF;AAEA,UAAM,SAAS,qBAAqB,iBAAiB,KAAK,OAAO,aAAa;AAE9E,UAAM,MAAM,IAAI,WAAW,MAAM;AACjC,WAAO,IAAI,0BAA0B,SAAS;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAA0B;AACxB,WAAO,KAAK,SAAS,eAAe;AAAA,EACtC;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,kBAAkB;AACvB,SAAK,SAAS,QAAQ;AAAA,EACxB;AACF;;;AFvJA,IAAM,gBAAgB,oBAAI,IAAoB;AAuB9C,eAAsB,WACpB,gBACA,SACiB;AACjB,MAAI,CAAC,gBAAgB;AACnB,UAAM,IAAIC;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,OAAO,iBAAiB;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,SAAS,cAAc,IAAI,cAAc;AAC/C,MAAI,OAAQ,QAAO;AAEnB,QAAM,SAAuB;AAAA,IAC3B;AAAA,IACA,GAAG;AAAA,EACL;AAKA,QAAM,UAAU,IAAI,cAAc;AAClC,QAAM,QAAQ,WAAW,MAAM;AAE/B,QAAM,WAAW,IAAI,OAAO,SAAS,MAAM;AAC3C,gBAAc,IAAI,gBAAgB,QAAQ;AAE1C,SAAO;AACT;;;AM3DA;AAAA,EACE,eAAAC;AAAA,EACA,eAAAC;AAAA,EACA,uBAAAC;AAAA,EACA,oBAAAC;AAAA,EACA,0BAAAC;AAAA,OACK;AAQP,IAAM,mBAAmB;AAQzB,SAASC,YAAW,OAAoC;AACtD,SAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAC7D;AAQA,SAAS,0BAA0B,QAAgB,SAAgD;AACjG,QAAM,SAAS,SAAS;AACxB,QAAM,OACJA,YAAW,SAAS,IAAI,KACrBA,YAAW,QAAQ,IAAI,KACvB,QAAQ,MAAM;AACnB,QAAM,UACJA,YAAW,SAAS,OAAO,KACxBA,YAAW,QAAQ,OAAO,KAC1B,sBAAsB,MAAM,MAAM;AAEvC,SAAO,IAAIC,aAAY,SAAS,aAAa,EAAE,MAAM,YAAY,OAAO,CAAC;AAC3E;AAEA,SAAS,sBAAsB,MAAc,QAAwB;AACnE,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,mEAA8D,gBAAgB;AAAA,IACvF,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO,2CAA2C,MAAM;AAAA,EAC5D;AACF;AAiCA,eAAsB,sBACpB,SACgC;AAChC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,gBAAgB,CAAC;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,cAAc,CAAC;AAAA,IACf;AAAA,IACA,iBAAiB,CAAC;AAAA,IAClB,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,MAAI,YAAY,SAAS,kBAAkB;AACzC,UAAM,IAAIA;AAAA,MACR,mEAA8D,gBAAgB;AAAA,MAC9E;AAAA,MACA,EAAE,MAAM,uBAAuB,OAAO,cAAc;AAAA,IACtD;AAAA,EACF;AAEA,QAAM,eAAe,YAAYC,kBAAiB,OAAO,aAAa;AACtE,QAAM,kBAAkBC,wBAAuB,UAAU,OAAO,eAAe,YAAY;AAC3F,MAAI,CAAC,iBAAiB;AACpB,UAAM,IAAIF;AAAA,MACR;AAAA,MACA;AAAA,MACA,EAAE,MAAM,oBAAoB,OAAO,WAAW;AAAA,IAChD;AAAA,EACF;AAEA,QAAM,UAAmC;AAAA,IACvC;AAAA,IACA,iBAAiBG;AAAA,IACjB;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA,UAAU,aAAa,IAAI,CAAC,YAAYC,qBAAoB,SAAS,eAAe,CAAC;AAAA,IACrF,aAAa;AAAA,MACX,QAAQ,QAAQ;AAAA,MAChB,WAAW,QAAQ,aAAa;AAAA,MAChC,UAAU,QAAQ,YAAY;AAAA,MAC9B,OAAO,QAAQ;AAAA,MACf,SAAS,QAAQ,WAAW;AAAA,MAC5B,QAAQ,QAAQ,UAAU;AAAA,MAC1B,MAAM,QAAQ,QAAQ;AAAA,MACtB,OAAO,QAAQ,SAAS;AAAA,MACxB,KAAK,QAAQ,OAAO;AAAA,MACpB,cAAc,QAAQ,gBAAgB;AAAA,MACtC,cAAc,QAAQ,gBAAgB;AAAA,IACxC;AAAA,IACA;AAAA,EACF;AAEA,MAAI,UAAU;AACZ,YAAQ,UAAU,IAAI;AAAA,EACxB;AAEA,MAAI,aAAa,QAAQ;AACvB,YAAQ,aAAa,IAAI;AAAA,EAC3B;AAEA,QAAM,MAAM,GAAG,cAAc,QAAQ,QAAQ,EAAE,CAAC;AAChD,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,QAAQ,WAAW,MAAM,WAAW,MAAM,GAAG,SAAS;AAE5D,MAAI;AACJ,MAAI;AAEJ,MAAI;AACF,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B,QAAQ,WAAW;AAAA,IACrB,CAAC;AAED,aAAS,SAAS;AAElB,QAAI;AACF,aAAO,MAAM,SAAS,KAAK;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACF,UAAE;AACA,iBAAa,KAAK;AAAA,EACpB;AAEA,MAAI,UAAU,KAAK;AACjB,UAAM,0BAA0B,QAAQ,IAAI;AAAA,EAC9C;AAEA,MAAI,WAAW,KAAK;AAClB,UAAM,OAAO,MAAM,MAAM;AACzB,UAAM,QAAQ,MAAM,MAAM;AAE1B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF;AAEA,QAAI,CAAC,OAAO;AACV,YAAM,IAAIJ;AAAA,QACR;AAAA,QACA;AAAA,QACA,EAAE,MAAM,8BAA8B;AAAA,MACxC;AAAA,IACF;AAMA,QAAI,aAAa,UAAU,iBAAiB;AAC1C,8BAAwB,MAAM;AAAA,QAC5B,UAAU;AAAA,QACV,UAAU,aAAa,IAAI,CAAC,OAAO;AAAA,UACjC,MAAM,EAAE,QAAQ,EAAE,kBAAkB,EAAE;AAAA,UACtC,MAAM,EAAE;AAAA,UACR,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,oBAAoB;AAAA,UAChG,aAAa,EAAE;AAAA,UACf,gBAAgB,EAAE;AAAA,UAClB,UAAU,EAAE,YAAY;AAAA,QAC1B,EAAE;AAAA,MACJ,CAAC;AAAA,IACH;AAEA,UAAM,cAAc,IAAI,IAAI,GAAG,gBAAgB,QAAQ,QAAQ,EAAE,CAAC,SAAS;AAC3E,gBAAY,aAAa,IAAI,MAAM,IAAI;AAEvC,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,cAAc,GAAG;AACzD,kBAAY,aAAa,IAAI,KAAK,KAAK;AAAA,IACzC;AAEA,QAAI,aAAa,OAAO,WAAW,eAAe,OAAO,aAAa,aAAa;AACjF,YAAM,eAAe,KAAK,UAAU,EAAE,YAAY,UAAU,CAAC;AAC7D,YAAM,SAAS,OAAO,SAAS,SAAS,MAAM,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,GAAG;AACrE,eAAS,SAAS,iBAAiB,mBAAmB,YAAY,CAAC,aAAa,MAAM;AAMtF,eAAS,SAAS,yBAAyB,mBAAmB,KAAK,CAAC,aAAa,MAAM;AAAA,IACzF;AAEA,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,SAAS,OAAO,YAAY,SAAS;AAAA,IAC9C;AAEA,WAAO,EAAE,QAAQ,KAAK,aAAa,YAAY,SAAS,GAAG,MAAM;AAAA,EACnE;AAEA,MAAI,WAAW,KAAK;AAClB,QAAI,OAAO,WAAW,aAAa;AACjC,aAAO,SAAS,OAAO;AAAA,IACzB;AACA,WAAO,EAAE,QAAQ,IAAI;AAAA,EACvB;AAEA,SAAO,EAAE,OAAO;AAClB;AAOA,eAAsB,iCACpB,SACgC;AAChC,QAAM,EAAE,aAAa,GAAG,GAAG,eAAe,IAAI;AAE9C,MAAI,CAAC,OAAO,SAAS,UAAU,KAAK,CAAC,OAAO,UAAU,UAAU,KAAK,cAAc,GAAG;AACpF,UAAM,IAAI,MAAM,0CAA0C;AAAA,EAC5D;AAEA,MAAI;AAEJ,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,QAAI;AACF,aAAO,MAAM,sBAAsB,cAAc;AAAA,IACnD,SAAS,KAAK;AACZ,gBAAU;AAEV,UAAI,eAAe,SAAS,IAAI,SAAS,gBAAgB,UAAU,YAAY;AAC7E,cAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,CAAC;AAClE;AAAA,MACF;AAEA,YAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,MAAM,gDAAgD;AAC7E;","names":["FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","FloPayError","SDK_VERSION","buildProductPayload","foldIntoProducts","resolveSessionCurrency","readString","FloPayError","foldIntoProducts","resolveSessionCurrency","SDK_VERSION","buildProductPayload"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flopay/js",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/",
|
|
@@ -25,18 +25,10 @@
|
|
|
25
25
|
"dist"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@
|
|
29
|
-
|
|
30
|
-
"peerDependencies": {
|
|
31
|
-
"@stripe/stripe-js": ">=2.0.0"
|
|
32
|
-
},
|
|
33
|
-
"peerDependenciesMeta": {
|
|
34
|
-
"@stripe/stripe-js": {
|
|
35
|
-
"optional": true
|
|
36
|
-
}
|
|
28
|
+
"@stripe/stripe-js": "^9.7.0",
|
|
29
|
+
"@flopay/shared": "1.2.6"
|
|
37
30
|
},
|
|
38
31
|
"devDependencies": {
|
|
39
|
-
"@stripe/stripe-js": "^9.7.0",
|
|
40
32
|
"typescript": "^6.0.3",
|
|
41
33
|
"tsup": "^8.3.0",
|
|
42
34
|
"vitest": "^2.1.0"
|