@usethrottle/checkout-react 1.5.0 → 2.1.0

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 CHANGED
@@ -185,7 +185,7 @@ over signed HTTP).
185
185
  | ----------------------- | ------------------------------- | ----------------------------------- |
186
186
  | `throttle.ready` | iframe mounted | `onReady()` |
187
187
  | `throttle.processing` | user clicked Pay; awaiting auth | `onProcessing()` |
188
- | `throttle.completed` | payment captured | `onSucceeded({orderId, paymentId})` |
188
+ | `throttle.completed` | payment captured | `onSucceeded({orderId, paymentId, subscriptionId?, subscriptionIds?})` |
189
189
  | `throttle.error` | card declined / network failure | `onFailed({code, message})` |
190
190
  | `throttle.cancelled` | user dismissed | `onCanceled()` |
191
191
  | `throttle.step.changed` | (CheckoutEmbed) step transition | `onStepChanged({step})` |
package/dist/index.cjs CHANGED
@@ -121,6 +121,7 @@ function CheckoutEmbed({
121
121
  orderId: msg.orderId,
122
122
  paymentId: msg.paymentId,
123
123
  ...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
124
+ ...msg.subscriptionIds !== void 0 ? { subscriptionIds: msg.subscriptionIds } : {},
124
125
  // v1.4 analytics enrichment — forwarded verbatim so the parent
125
126
  // can fire purchase events without an extra fetch.
126
127
  ...msg.total !== void 0 ? { total: msg.total } : {},
@@ -208,6 +209,7 @@ function PaymentEmbed({
208
209
  orderId: msg.orderId,
209
210
  paymentId: msg.paymentId,
210
211
  ...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
212
+ ...msg.subscriptionIds !== void 0 ? { subscriptionIds: msg.subscriptionIds } : {},
211
213
  // v1.4 analytics enrichment — forwarded verbatim so the parent
212
214
  // can fire purchase events without an extra fetch.
213
215
  ...msg.total !== void 0 ? { total: msg.total } : {},
@@ -709,11 +711,12 @@ function SecureCardProvider({
709
711
  const threeDSElement = (0, import_react7.useRef)(null);
710
712
  const threeDSOptions = (0, import_react7.useRef)(void 0);
711
713
  const challengeWaiter = (0, import_react7.useRef)(null);
712
- const { gr4vyId, environment, sessionId } = session;
714
+ const { environment, sessionId } = session;
715
+ const providerInstanceId = session.providerId ?? session.gr4vyId ?? "";
713
716
  const [instance, setInstance] = (0, import_react7.useState)(null);
714
717
  const [threeDSMount, setThreeDSMount] = (0, import_react7.useState)(null);
715
718
  (0, import_react7.useEffect)(() => {
716
- const created = new import_secure_fields2.SecureFields({ gr4vyId, environment, sessionId });
719
+ const created = new import_secure_fields2.SecureFields({ gr4vyId: providerInstanceId, environment, sessionId });
717
720
  setInstance(created);
718
721
  setMountedCount(0);
719
722
  setStatus((current) => current === "idle" || current === "ready" ? "mounting" : current);
@@ -721,7 +724,7 @@ function SecureCardProvider({
721
724
  created.controller?.remove();
722
725
  setInstance(null);
723
726
  };
724
- }, [gr4vyId, environment, sessionId]);
727
+ }, [providerInstanceId, environment, sessionId]);
725
728
  (0, import_react7.useEffect)(() => {
726
729
  if (!instance) return;
727
730
  const handleSuccess = () => {
@@ -735,6 +738,7 @@ function SecureCardProvider({
735
738
  };
736
739
  const handleThreeDSStart = () => setStatus("challenging");
737
740
  const handleThreeDSFinish = () => {
741
+ if (!challengeWaiter.current) return;
738
742
  setStatus((current) => current === "challenging" ? "vaulting" : current);
739
743
  };
740
744
  instance.addEventListener(import_secure_fields2.SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);
@@ -906,7 +910,14 @@ function SecureCardProvider({
906
910
  cancelChallenge,
907
911
  status,
908
912
  error,
909
- ready: status === "ready",
913
+ // "The fields are mounted and nothing is in flight" — NOT `status ===
914
+ // 'ready'`. Deriving it from the status alone made a decline terminal: the
915
+ // only thing that ever sets `ready` is the mount-count effect, which does
916
+ // not re-run after `setStatus('error')`, so `ready` stayed false and the
917
+ // merchant's pay button stayed disabled. A declined buyer could not try
918
+ // another card without reloading the page — and a decline is the single
919
+ // most common failure at a checkout.
920
+ ready: mountedCount >= FIELDS_REQUIRED_TO_BE_READY && status !== "vaulting" && status !== "authorizing" && status !== "challenging" && status !== "complete",
910
921
  pay,
911
922
  vault,
912
923
  complete
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx","../src/useSecureCard.tsx","../src/threeDSecure.ts","../src/SecureCardFields.tsx"],"sourcesContent":["export { CheckoutEmbed } from './CheckoutEmbed.js';\nexport { PaymentEmbed } from './PaymentEmbed.js';\nexport { useThrottleEvents } from './useThrottleEvents.js';\nexport { useCartSession } from './useCartSession.js';\nexport type { UseCartSessionOptions, UseCartSessionResult } from './useCartSession.js';\nexport { useThrottleCheckout } from './useThrottleCheckout.js';\nexport type {\n UseThrottleCheckoutOptions,\n UseThrottleCheckoutResult,\n ThrottleCheckoutStatus,\n CheckoutTotals,\n} from './useThrottleCheckout.js';\nexport type {\n ThrottleEvent,\n ThrottleMessage,\n ThrottleEnvelope,\n ThrottleCompletedLineItem,\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n RecurringIntent,\n ThrottleParentCommand,\n ThrottleParentEnvelope,\n ThrottleParentMessage,\n} from './types.js';\n\n// v0.1.x compat alias. v0.1's <ThrottleCheckout> shape (positional\n// onCompleted/onError args, optional baseUrl, no parentOrigin) is\n// gone in v0.2 — the alias resolves to <CheckoutEmbed> which now\n// requires `parentOrigin`. Update call sites accordingly.\nexport { CheckoutEmbed as ThrottleCheckout } from './CheckoutEmbed.js';\n\n// Secure Fields surface (Tasks 4-6)\nexport {\n SecureCardProvider,\n useSecureCard,\n type SecureCardSession,\n type SecureCardStatus,\n type SecureCardError,\n type SecureCardProviderProps,\n type SecureCardCompleteResult,\n // Required to type what the public surface already hands out:\n // `SecureCardContextValue` is `useSecureCard()`'s return, `SecureCardFieldKind`\n // is `registerField`'s first argument, and `SecureFieldsInstance` is the\n // `instance` on both.\n type SecureCardContextValue,\n type SecureCardFieldKind,\n type SecureFieldsInstance,\n} from './useSecureCard.js';\n// `resolveChallengeFn` is a public prop, so a merchant writing one needs its\n// argument type — and the instance type that argument carries.\nexport type { ResolveChallengeArgs, ThreeDSecureInstance } from './threeDSecure.js';\nexport {\n CardNumberField, ExpiryField, SecurityCodeField, PostalCodeField,\n type SecureCardFieldProps,\n} from './SecureCardFields.js';\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport type { ThrottleParentMessage } from './types.js';\n\ninterface UseParentCommandsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /** The Throttle origin to address messages to. */\n targetOrigin: string;\n /**\n * The current value of `submitDisabled` from the embed props. Posts a\n * `set-submit-disabled` parent command whenever it changes — and once\n * after the iframe reports `throttle.ready` so the iframe sees the\n * intent even if it boots after the prop is already set.\n *\n * `undefined` means the parent never explicitly enabled/disabled — we\n * never post in that case. Backward-compat with embeds that don't\n * pass the prop.\n */\n submitDisabled?: boolean;\n /**\n * Set to `true` once the iframe posts `throttle.ready`. Until then,\n * commands are buffered (we still try to post but the iframe may not\n * have its listener attached yet — and we re-post on ready as a\n * belt-and-braces guard).\n */\n iframeReady: boolean;\n}\n\n/**\n * Post parent → iframe commands when controlled props change. v1.2 adds\n * `set-submit-disabled`; future commands extend the same channel.\n *\n * postMessage is fire-and-forget — we don't wait for an ack from the\n * iframe. The iframe's listener is idempotent (setting the same value\n * twice is a no-op).\n */\nexport function useParentCommands({\n iframeRef,\n targetOrigin,\n submitDisabled,\n iframeReady,\n}: UseParentCommandsOptions) {\n const [lastSent, setLastSent] = useState<boolean | undefined>(undefined);\n\n // Post when the prop value changes OR when the iframe transitions to\n // ready (so the first state lands).\n useEffect(() => {\n if (submitDisabled === undefined) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // Skip if we already sent this value AND the iframe was already ready\n // — avoids spam on unrelated re-renders.\n if (lastSent === submitDisabled && iframeReady) return;\n if (!iframeReady) return; // Defer until ready\n\n const message: ThrottleParentMessage = {\n source: 'throttle-parent',\n version: 1,\n type: 'set-submit-disabled',\n disabled: submitDisabled,\n };\n win.postMessage(message, targetOrigin);\n setLastSent(submitDisabled);\n }, [iframeRef, targetOrigin, submitDisabled, iframeReady, lastSent]);\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n CartSessionClient,\n type CartSessionCart,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\nexport interface UseCartSessionOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the session id across page loads. Default\n * `'throttle.cartSessionId'`. Pass `null` to disable persistence (in-memory\n * only).\n */\n storageKey?: string | null;\n}\n\nexport interface UseCartSessionResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current opaque session id, or null before the first item. */\n cartSessionId: string | null;\n /** True while a request is in flight. */\n loading: boolean;\n /** Last error, or null. */\n error: Error | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n selectShipping: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /** Hand off to checkout. Returns the hand-off (redirect the buyer to `checkoutUrl`). */\n checkout: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\n/**\n * React hook for backend-less cart ownership. Wraps {@link CartSessionClient}:\n * creates the Throttle cart lazily on the first `addItem` (so empty carts\n * aren't created on every page load), persists the session id to localStorage,\n * and resumes it on the next visit. Mutations update reactive `cart` state.\n *\n * ```tsx\n * const cart = useCartSession({ applicationId, environmentId, quoteToken });\n * <button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>Add</button>\n * <button onClick={async () => {\n * const { checkoutUrl } = await cart.checkout({ returnUrl, cancelUrl });\n * window.location.href = checkoutUrl;\n * }}>Checkout</button>\n * ```\n */\nexport function useCartSession(options: UseCartSessionOptions): UseCartSessionResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Stable client across renders for a given config; construction validates the\n // token, so capture any constructor error instead of throwing during render.\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) setError(initError);\n }, [initError]);\n\n // Resume a persisted session on mount (and whenever the client identity changes).\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setLoading(true);\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n })\n .catch(() => {\n // Expired / unknown / origin-rejected → drop the stale id and start fresh.\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /** Run a mutation that returns the updated cart, managing loading/error + creating the session first. */\n const run = useCallback(\n async (fn: () => Promise<CartSessionCart>, opts: { create?: boolean } = {}): Promise<void> => {\n if (!client) {\n setError(initError ?? new Error('CartSessionClient unavailable'));\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const next = await fn();\n setCart(next);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const addItem = useCallback(\n (input: AddLineItemInput) => run(() => client!.addItem(input), { create: true }),\n [client, run],\n );\n const updateItem = useCallback(\n (itemId: string, input: UpdateLineItemInput) => run(() => client!.updateItem(itemId, input)),\n [client, run],\n );\n const removeItem = useCallback((itemId: string) => run(() => client!.removeItem(itemId)), [client, run]);\n const selectShipping = useCallback(\n (input: SelectShippingInput) => run(() => client!.selectShipping(input), { create: true }),\n [client, run],\n );\n const clearShipping = useCallback(() => run(() => client!.clearShipping()), [client, run]);\n const applyDiscount = useCallback(\n (code: string) => run(() => client!.applyDiscount(code), { create: true }),\n [client, run],\n );\n const removeDiscount = useCallback(() => run(() => client!.removeDiscount()), [client, run]);\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n await run(async () => (await client.get()).cart);\n }, [client, run]);\n\n const checkout = useCallback(\n async (input: CheckoutHandoffInput): Promise<CheckoutHandoff> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n setLoading(true);\n setError(null);\n try {\n await ensureSession();\n return await client.checkout(input);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n loading,\n error,\n addItem,\n updateItem,\n removeItem,\n selectShipping,\n clearShipping,\n applyDiscount,\n removeDiscount,\n checkout,\n refresh,\n reset,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CartSessionClient,\n CartNotOpenError,\n CartNotFoundError,\n type CartSessionCart,\n type SelectedShipping,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\n/**\n * Lifecycle status for the checkout orchestration.\n * - `idle` — no work in flight, no cart yet\n * - `loading` — a request is in flight\n * - `recovering` — the cart went stale (converted/abandoned); rebuilding from the\n * last-known line items into a fresh session\n * - `ready` — a cart is present and no request is in flight\n * - `error` — the last operation failed (see `error`)\n */\nexport type ThrottleCheckoutStatus = 'idle' | 'loading' | 'recovering' | 'ready' | 'error';\n\nexport interface CheckoutTotals {\n currency: string;\n subtotal: number;\n discountTotal: number;\n shippingTotal: number;\n taxTotal: number;\n total: number;\n}\n\nexport interface UseThrottleCheckoutOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the cart session id across page loads.\n * Default `'throttle.cartSessionId'`. Pass `null` to disable persistence.\n */\n storageKey?: string | null;\n /**\n * Auto-recover from a stale cart. When a mutation fails with\n * {@link CartNotOpenError} (the cart was converted by a completed order, or\n * abandoned), the hook starts a fresh session, re-adds the last-known line\n * items and re-selects the shipping method, then retries the operation once.\n * Default `true`. Set `false` to surface {@link CartNotOpenError} instead.\n */\n autoRecover?: boolean;\n}\n\nexport interface UseThrottleCheckoutResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current cart session id, or null before the first item. */\n cartSessionId: string | null;\n /** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */\n status: ThrottleCheckoutStatus;\n /** Last error, or null. */\n error: Error | null;\n /** The selected shipping method, bound to the cart (never a shadow copy). */\n selectedMethod: SelectedShipping | null;\n /** Live totals, bound to the cart. Null before a cart exists. */\n totals: CheckoutTotals | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n /** Lock a shipping method. One call — the returned cart carries recomputed totals. */\n selectMethod: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /**\n * Hand off to checkout. Returns the hand-off — use `checkoutSessionId` with\n * `<PaymentEmbed sessionId={…}>` (or redirect the buyer to `checkoutUrl`).\n */\n createSession: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\nfunction totalsOf(cart: CartSessionCart | null): CheckoutTotals | null {\n if (!cart) return null;\n return {\n currency: cart.currency,\n subtotal: cart.subtotal,\n discountTotal: cart.discountTotal,\n shippingTotal: cart.shippingTotal,\n taxTotal: cart.taxTotal,\n total: cart.total,\n };\n}\n\n/**\n * Orchestrates a backend-less storefront checkout on top of\n * {@link CartSessionClient}, bundling the pieces every storefront otherwise\n * re-implements:\n *\n * - **Cart as source of truth** — `selectedMethod` and `totals` are read straight\n * off the cart; there is no shadow copy to drift.\n * - **One-call shipping select** — `selectMethod` locks the method and returns the\n * cart with recomputed totals in a single round trip.\n * - **Stale-cart recovery** — a {@link CartNotOpenError} (cart converted by a\n * completed order, or abandoned) transparently rebuilds a fresh session from\n * the last-known line items + shipping and retries (`status === 'recovering'`).\n * - **Session handoff** — `createSession` returns the `checkoutSessionId` for\n * `<PaymentEmbed>`.\n *\n * Address collection is handled inside the checkout embed (the hosted/full\n * checkout collects shipping/billing), so this hook intentionally does not write\n * addresses to the cart.\n *\n * ```tsx\n * const { addItem, selectMethod, totals, createSession, status } =\n * useThrottleCheckout({ applicationId, environmentId, quoteToken });\n * ```\n */\nexport function useThrottleCheckout(\n options: UseThrottleCheckoutOptions,\n): UseThrottleCheckoutResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const autoRecover = options.autoRecover ?? true;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [status, setStatus] = useState<ThrottleCheckoutStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Keep the latest cart in a ref so recovery can read the last-known line items\n // without threading it through every callback's dependency list.\n const cartRef = useRef<CartSessionCart | null>(null);\n cartRef.current = cart;\n\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) {\n setError(initError);\n setStatus('error');\n }\n }, [initError]);\n\n // Resume a persisted session on mount.\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setStatus('loading');\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n setStatus('ready');\n })\n .catch(() => {\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n setStatus('idle');\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /**\n * Rebuild a fresh session from the last-known cart so a stale cart doesn't\n * strand the buyer. Re-adds line items and re-selects shipping, then returns\n * the rebuilt cart.\n */\n const rebuild = useCallback(async (): Promise<CartSessionCart> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n const prev = cartRef.current;\n // Drop the stale session and create a new one.\n client.resume('');\n setCartSessionId(null);\n const session = await client.create({\n currency: prev?.currency,\n netN: prev?.netN ?? undefined,\n });\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n let next = session.cart;\n for (const item of prev?.lineItems ?? []) {\n next = await client.addItem({\n type: item.type,\n referenceId: item.referenceId ?? undefined,\n name: item.name,\n description: item.description ?? undefined,\n unitPrice: item.unitPrice,\n quantity: item.quantity,\n imageUrl: item.imageUrl ?? undefined,\n metadata: item.metadata,\n });\n }\n const ship = prev?.selectedShipping;\n if (ship) {\n next = await client.selectShipping({\n methodId: ship.methodId,\n displayName: ship.displayName,\n rateAmount: ship.rateAmount,\n currency: ship.currency,\n carrier: ship.carrier ?? undefined,\n serviceCode: ship.serviceCode ?? undefined,\n estimatedDeliveryDays: ship.estimatedDeliveryDays ?? undefined,\n });\n }\n setCart(next);\n return next;\n }, [client, initError, storageKey]);\n\n /**\n * Run a cart op with loading/error bookkeeping. On a stale-cart error, rebuild\n * (when `autoRecover`) and — for forward-progress ops (`retryAfterRecover`) —\n * retry once against the rebuilt session. Item-id-specific ops set\n * `retryAfterRecover: false` because the id no longer exists after rebuild.\n */\n const run = useCallback(\n async <T,>(\n fn: () => Promise<T>,\n opts: { create?: boolean; retryAfterRecover?: boolean } = {},\n ): Promise<T> => {\n if (!client) {\n const e = initError ?? new Error('CartSessionClient unavailable');\n setError(e);\n setStatus('error');\n throw e;\n }\n setStatus('loading');\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const result = await fn();\n setStatus('ready');\n return result;\n } catch (e) {\n const stale = e instanceof CartNotOpenError || e instanceof CartNotFoundError;\n if (stale && autoRecover) {\n try {\n setStatus('recovering');\n await rebuild();\n if (opts.retryAfterRecover) {\n const retried = await fn();\n setStatus('ready');\n return retried;\n }\n // Rebuild-only: the original op targeted a now-gone id. Surface the\n // rebuilt cart and let the caller reconcile against fresh state.\n setStatus('ready');\n const stale2 = new CartNotOpenError({\n message: 'Cart was rebuilt after going stale; retry your action against the new cart.',\n });\n setError(stale2);\n throw stale2;\n } catch (recoverErr) {\n setError(recoverErr as Error);\n setStatus('error');\n throw recoverErr;\n }\n }\n setError(e as Error);\n setStatus('error');\n throw e;\n }\n },\n [client, ensureSession, initError, autoRecover, rebuild],\n );\n\n const addItem = useCallback(\n async (input: AddLineItemInput) => {\n const next = await run(() => client!.addItem(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const updateItem = useCallback(\n async (itemId: string, input: UpdateLineItemInput) => {\n const next = await run(() => client!.updateItem(itemId, input), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const removeItem = useCallback(\n async (itemId: string) => {\n const next = await run(() => client!.removeItem(itemId), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const selectMethod = useCallback(\n async (input: SelectShippingInput) => {\n const next = await run(() => client!.selectShipping(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const clearShipping = useCallback(async () => {\n const next = await run(() => client!.clearShipping(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n const applyDiscount = useCallback(\n async (code: string) => {\n const next = await run(() => client!.applyDiscount(code), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const removeDiscount = useCallback(async () => {\n const next = await run(() => client!.removeDiscount(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n const next = await run(async () => (await client.get()).cart);\n setCart(next);\n }, [client, run]);\n\n const createSession = useCallback(\n (input: CheckoutHandoffInput): Promise<CheckoutHandoff> =>\n run(() => client!.checkout(input), { create: true, retryAfterRecover: true }),\n [client, run],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n setStatus('idle');\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n status,\n error,\n selectedMethod: cart?.selectedShipping ?? null,\n totals: totalsOf(cart),\n addItem,\n updateItem,\n removeItem,\n selectMethod,\n clearShipping,\n applyDiscount,\n removeDiscount,\n createSession,\n refresh,\n reset,\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { SecureFields } from '@gr4vy/secure-fields';\nimport { resolveChallenge, type ResolveChallengeArgs } from './threeDSecure.js';\n\n/**\n * Lifecycle status for the headless Secure Fields state machine.\n * - `idle` — provider mounted, fields not yet registered\n * - `mounting` — Secure Fields instance constructed, fields registering\n * - `ready` — all required fields registered, submit() may be called\n * - `vaulting` — submit() called, waiting on CARD_VAULT_SUCCESS/FAILURE\n * - `authorizing` — card vaulted, awaiting the merchant's complete() call\n * - `challenging` — complete() returned `requires_action`; Gr4vy's own 3DS\n * iframe (addThreeDSecure) is mounted and running\n * - `complete` — order created\n * - `error` — vault, challenge, or completion failed (see `error`)\n */\nexport type SecureCardStatus =\n | 'idle'\n | 'mounting'\n | 'ready'\n | 'vaulting'\n | 'authorizing'\n | 'challenging'\n | 'complete'\n | 'error';\n\n/** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */\nexport interface SecureCardSession {\n sessionId: string;\n gr4vyId: string;\n environment: 'sandbox' | 'production';\n}\n\nexport interface SecureCardError {\n code: string;\n message: string;\n}\n\nexport type SecureCardFieldKind = 'number' | 'expiry' | 'cvv' | 'postalCode';\n\n/**\n * Everything this package uses off a `SecureFields` instance, declared\n * structurally instead of re-exporting the SDK's own class type.\n *\n * `@gr4vy/secure-fields`'s `exports` map has no `types` condition, so naming\n * its type in an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts` — which is a hard TS7016 for any consumer on\n * `moduleResolution: bundler` with `skipLibCheck: false`, and a silent\n * degradation of `instance` to `any` with `skipLibCheck: true` (the Next\n * default). That hits every installer of this package, not only Secure Fields\n * users. The `paths` override in this package's tsconfig repairs our own\n * compile; it is monorepo-only and does not ship. Same approach as\n * `ThreeDSecureInstance` in `threeDSecure.ts`.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower option and event types stay\n * assignable to the looser ones declared here.\n */\nexport interface SecureFieldsInstance {\n /** Gr4vy's hidden controller iframe, removed when the provider unmounts. */\n controller?: HTMLIFrameElement;\n addCardNumberField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addExpiryDateField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addSecurityCodeField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addField(element: string | HTMLElement, options: Record<string, unknown>): unknown;\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n submit(): void;\n}\n\nexport interface SecureCardContextValue {\n /**\n * The underlying Secure Fields instance — Task 5's field primitives register\n * against it.\n *\n * `null` until the provider's mount effect has constructed it. It cannot be\n * built during render: `new SecureFields()` mutates the document (see the\n * effect in `SecureCardProvider`), and React does not promise a render-phase\n * factory runs exactly once. Consumers that reach for it directly must\n * null-check; the field primitives already wait for it.\n */\n instance: SecureFieldsInstance | null;\n registerField: (kind: SecureCardFieldKind, element: HTMLElement, options?: Record<string, unknown>) => void;\n /**\n * Mount point for Gr4vy's own 3DS challenge iframe (`addThreeDSecure`).\n * Register this once, alongside the card fields; it is only actually\n * mounted if `complete()` gets `requires_action`.\n */\n registerThreeDSecure: (\n element: HTMLElement | string,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ) => void;\n /**\n * Abandons an in-progress challenge (e.g. the buyer closed the challenge\n * UI). Only meaningful while `status === 'challenging'`; settles\n * `complete()`'s promise with `error.code === 'three_ds_abandoned'`, kept\n * distinct from a processor decline code.\n */\n cancelChallenge: () => void;\n status: SecureCardStatus;\n error: SecureCardError | null;\n /** True once all required fields are registered and submit() may be called. */\n ready: boolean;\n /** vault() then complete(). */\n pay: () => Promise<unknown>;\n vault: () => Promise<void>;\n complete: () => Promise<unknown>;\n}\n\nconst SecureCardContext = createContext<SecureCardContextValue | null>(null);\n\n// number, expiry, cvv are required; postalCode is optional.\nconst FIELDS_REQUIRED_TO_BE_READY = 3;\n\nexport interface SecureCardProviderProps {\n session: SecureCardSession;\n /**\n * Throttle's own checkout session id — the `:id` already used to mint\n * `session` via `POST /checkout-sessions/:id/card-session`. NOT the same\n * value as `session.sessionId`: that field is Gr4vy's own checkout session\n * id (required to construct the `SecureFields` instance — see\n * `packages/platform-api/src/routes/card-session.ts`, which returns\n * `{ sessionId: checkoutSessionId, ... }` from `adapter.createCardSession()`,\n * a Gr4vy-minted id). The public `POST /checkout-sessions/:id/complete`\n * route looks `:id` up directly against Throttle's own `checkoutSessions`\n * table (`checkout-sessions.ts`), so `defaultComplete` needs this id, not\n * `session.sessionId`, to call the right URL.\n */\n checkoutSessionId: string;\n children: ReactNode;\n /** Injected for tests / custom completion flows; defaults to a real POST /complete call. */\n completeFn?: (args: { sessionId: string; gr4vySessionId: string }) => Promise<any>;\n /** Injected for tests / custom 3DS UI; defaults to mounting Gr4vy's own 3DS iframe via addThreeDSecure. */\n resolveChallengeFn?: (args: ResolveChallengeArgs) => Promise<void>;\n /**\n * Bounds how long `complete()` waits on an unfinished challenge before\n * settling to a terminal `error` (code `three_ds_timeout`) on its own — the\n * only other exits are Gr4vy's own `THREE_DS_FINISH` and an explicit\n * `cancelChallenge()` call, neither of which is guaranteed to ever fire if\n * the buyer just abandons the tab. Not sourced from Gr4vy documentation\n * (no challenge-duration limit is published in the SDK); 5 minutes is a\n * conservative, overridable default.\n */\n challengeTimeoutMs?: number;\n /**\n * Base URL of the Throttle API (e.g. `https://api.usethrottle.dev`).\n * Required — not optional-with-a-same-origin-default. Secure Fields is the\n * headless surface for merchants who mount the card form on THEIR OWN\n * origin, which by definition is not same-origin with the Throttle API, so\n * `defaultComplete`'s `fetch()` can never resolve a relative path (it 404s\n * against the merchant's own domain at payment time instead). Making this\n * required turns that into a compile error instead of a silent runtime\n * failure. Joined onto the request path regardless of a trailing slash.\n */\n apiBaseUrl: string;\n onSuccess?: (result: SecureCardCompleteResult) => void;\n}\n\n/**\n * The real `POST /checkout-sessions/:id/complete` success body for the\n * Secure Fields flow (`checkout-session-service.ts` `complete()`,\n * proxy-mode-embedded return, ~line 2338 and its idempotent-retry twin at\n * ~line 1849). There is no `order` key.\n */\nexport interface SecureCardCompleteResult {\n orderId: string;\n orderNumber: string;\n paymentId: string;\n /** Minor units (cents). */\n total: number;\n currency: string;\n status: string;\n paymentStatus: string;\n}\n\nconst DEFAULT_CHALLENGE_TIMEOUT_MS = 5 * 60 * 1000;\n\nexport function SecureCardProvider({\n session,\n checkoutSessionId,\n children,\n completeFn,\n resolveChallengeFn,\n challengeTimeoutMs = DEFAULT_CHALLENGE_TIMEOUT_MS,\n apiBaseUrl,\n onSuccess,\n}: SecureCardProviderProps) {\n const [status, setStatus] = useState<SecureCardStatus>('idle');\n const [error, setError] = useState<SecureCardError | null>(null);\n const [mountedCount, setMountedCount] = useState(0);\n const vaultWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n const threeDSElement = useRef<HTMLElement | string | null>(null);\n const threeDSOptions = useRef<{ challengeWindowSize?: '01' | '02' | '03' | '04' | '05' } | undefined>(undefined);\n const challengeWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n\n const { gr4vyId, environment, sessionId } = session;\n const [instance, setInstance] = useState<SecureFieldsInstance | null>(null);\n /** Mount point for Gr4vy's challenge, as state so arming can be an effect. */\n const [threeDSMount, setThreeDSMount] = useState<HTMLElement | string | null>(null);\n\n /**\n * `new SecureFields()` is a side effect on the document, not a computation.\n * Its constructor removes whatever `iframe#controller[src*=secure-fields]`\n * is already on the page, creates its own, appends it to `document.body`,\n * and resets the SDK's module-level event bus — so it must run exactly once\n * per mounted provider.\n *\n * It used to sit in a `useMemo`. React invokes a memo factory twice under\n * Strict Mode and commits the FIRST value, so the second construction tore\n * the committed instance's own controller iframe off the page. That\n * instance's internal `controllerReady` promise then never resolved, Gr4vy\n * never inserted a single field iframe, and yet `status` still reported\n * `ready` — because `ready` counts our own `registerField` calls, not the\n * SDK's DOM. Every \"verified in a browser\" pass before Task 9a ran against\n * that false positive (Task 9a, Finding 1).\n *\n * Task 4 had already moved `setStatus('mounting')` out of this same memo for\n * exactly this reason and left the SDK construction behind. `useMemo` is not\n * a lifecycle hook; an effect is the only hook React pairs with a cleanup,\n * so a Strict-Mode double-invoke settles on one live, attached instance.\n */\n useEffect(() => {\n const created = new SecureFields({ gr4vyId, environment, sessionId });\n setInstance(created);\n // A rebuilt instance has no registered fields. Both the counter and the\n // status derived from it must go back, or a `session` prop change leaves\n // `ready === true` while every field iframe is still being re-created and\n // `submit()` would fire against nothing.\n setMountedCount(0);\n setStatus((current) => (current === 'idle' || current === 'ready' ? 'mounting' : current));\n return () => {\n // The SDK exposes no teardown at all, so do the two things that matter:\n // take its controller iframe off the page, and stop handing the instance\n // out so no field can ever register against a discarded one.\n created.controller?.remove();\n setInstance(null);\n };\n }, [gr4vyId, environment, sessionId]);\n\n useEffect(() => {\n if (!instance) return;\n\n const handleSuccess = () => {\n vaultWaiter.current?.resolve();\n };\n const handleFailure = (data?: { code?: string; message?: string }) => {\n vaultWaiter.current?.reject({\n code: data?.code ?? 'card_vault_failed',\n message: data?.message ?? 'The card could not be saved.',\n });\n };\n\n // The challenge now happens INSIDE submit(), between `vaulting` and the\n // vault result, so its status is driven by Gr4vy's own events rather than\n // by anything the server says. Without these the UI sits on `vaulting`\n // while a challenge is visibly on screen.\n const handleThreeDSStart = () => setStatus('challenging');\n const handleThreeDSFinish = () => {\n // Back to waiting on CARD_VAULT_SUCCESS/FAILURE, which follows the\n // challenge. The authentication outcome is not read from here: the\n // server is the authority on whether the money moved.\n setStatus((current) => (current === 'challenging' ? 'vaulting' : current));\n };\n\n instance.addEventListener(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.addEventListener(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.addEventListener(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n\n return () => {\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n };\n }, [instance]);\n\n const registerField = useCallback(\n (kind: SecureCardFieldKind, element: HTMLElement, options: Record<string, unknown> = {}) => {\n // Fields render before the provider's mount effect has constructed the\n // instance (React runs child effects before parent effects). They\n // re-register themselves when `instance` appears — see SecureCardFields.\n if (!instance) return;\n if (kind === 'number') instance.addCardNumberField(element, options);\n else if (kind === 'expiry') instance.addExpiryDateField(element, options);\n else if (kind === 'cvv') instance.addSecurityCodeField(element, options);\n else instance.addField(element, { type: 'postalCode', ...options } as any);\n setMountedCount((n) => n + 1);\n },\n [instance],\n );\n\n useEffect(() => {\n if (mountedCount >= FIELDS_REQUIRED_TO_BE_READY) setStatus('ready');\n }, [mountedCount]);\n\n const registerThreeDSecure = useCallback(\n (element: HTMLElement | string, options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' }) => {\n threeDSElement.current = element;\n threeDSOptions.current = options;\n // State as well as the ref: arming 3DS is an effect that has to re-run\n // when the mount point appears, and a ref write does not re-render.\n setThreeDSMount(element);\n },\n [],\n );\n\n /**\n * Arm 3-D Secure as SETUP, before `submit()` — not as a reaction to the\n * server.\n *\n * Gr4vy runs the enrolment check \"at the time of submitting Secure Fields\",\n * so `addThreeDSecure` has to be registered before `submit()` or the\n * challenge can never be raised. The original build called it only after\n * `/complete` answered `requires_action`, which is after `submit()` has\n * already finished — so it never ran, and every sandbox transaction came\n * back `threeDSecure: undefined`.\n *\n * The SDK has no un-register, so this arms once per instance; a changed\n * mount element arrives with a new instance or not at all.\n */\n useEffect(() => {\n if (!instance || !threeDSMount) return;\n try {\n instance.addThreeDSecure(\n threeDSMount,\n threeDSOptions.current?.challengeWindowSize\n ? { challengeWindowSize: threeDSOptions.current.challengeWindowSize }\n : undefined,\n );\n } catch (err) {\n // Arming is best-effort: a card that never needs a challenge must still\n // be chargeable. A card that DOES need one will fail visibly at submit.\n setError({\n code: 'three_ds_arm_failed',\n message: (err as Error)?.message ?? 'Could not arm 3-D Secure.',\n });\n }\n }, [instance, threeDSMount]);\n\n const cancelChallenge = useCallback(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_abandoned',\n message: 'The 3-D Secure challenge was abandoned.',\n });\n }, []);\n\n const vault = useCallback(() => {\n // Nothing to submit to yet. Previously impossible (the instance was built\n // during render); now the provider's first commit has none, and calling\n // `submit()` on nothing would hang `vaulting` forever with no rejection.\n if (!instance) {\n return Promise.reject<void>({\n code: 'not_ready',\n message: 'The card fields are not ready yet.',\n } as SecureCardError);\n }\n\n // A second vault() while one is already in flight would otherwise silently\n // overwrite vaultWaiter.current, orphaning the first caller's promise forever\n // (no resolve, no reject). Reject the second call instead; the first keeps\n // waiting on its own vault event.\n if (vaultWaiter.current) {\n return Promise.reject<void>({\n code: 'vault_in_progress',\n message: 'A card vault is already in progress.',\n } as SecureCardError);\n }\n\n setError(null);\n setStatus('vaulting');\n return new Promise<void>((resolve, reject) => {\n vaultWaiter.current = {\n resolve: () => {\n vaultWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n vaultWaiter.current = null;\n setError(e);\n setStatus('error');\n reject(e);\n },\n };\n instance.submit();\n });\n }, [instance]);\n\n const complete = useCallback(async () => {\n setStatus('authorizing');\n const doComplete = completeFn ?? ((args: { sessionId: string; gr4vySessionId: string }) => defaultComplete({ ...args, apiBaseUrl }));\n const doResolveChallenge = resolveChallengeFn ?? resolveChallenge;\n try {\n let res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n\n if (res?.status === 'requires_action') {\n if (!threeDSElement.current) {\n throw Object.assign(new Error('No 3-D Secure mount element registered (call registerThreeDSecure).'), {\n code: 'three_ds_element_missing',\n });\n }\n if (!instance) {\n throw Object.assign(new Error('The card fields are not ready yet.'), { code: 'not_ready' });\n }\n setStatus('challenging');\n // Gr4vy's own 3DS iframe runs the challenge; this resolves once it\n // reports THREE_DS_FINISH, or rejects if cancelChallenge() is called\n // or the resolver itself fails, or rejects on its own after\n // challengeTimeoutMs so a buyer who just\n // abandons the tab (no finish, no cancel) can never strand complete()\n // forever. It does not tell us success/failure — re-query /complete\n // for that.\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_timeout',\n message: 'The 3-D Secure challenge timed out.',\n });\n }, challengeTimeoutMs);\n\n challengeWaiter.current = {\n resolve: () => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n reject(Object.assign(new Error(e.message), { code: e.code }));\n },\n };\n doResolveChallenge({\n instance,\n element: threeDSElement.current!,\n ...(threeDSOptions.current ?? {}),\n }).then(\n () => challengeWaiter.current?.resolve(),\n // `resolveChallenge` calls `instance.addThreeDSecure()` synchronously\n // inside its Promise executor, so an SDK throw rejects here; a\n // merchant-supplied `resolveChallengeFn` can reject too. Without this\n // arm the only exit was `challengeTimeoutMs` — five minutes stuck at\n // `status: 'challenging'`, plus an unhandled rejection. Same\n // code-passthrough convention as `complete()`'s catch: keep a `code`\n // the resolver supplied, otherwise report `three_ds_failed`.\n (err: unknown) =>\n challengeWaiter.current?.reject({\n code: (err as { code?: string })?.code ?? 'three_ds_failed',\n message: (err as Error)?.message ?? 'The 3-D Secure challenge could not be completed.',\n }),\n );\n });\n\n setStatus('authorizing');\n res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n if (res?.status === 'requires_action') {\n throw Object.assign(new Error('The payment still requires authentication after the 3-D Secure challenge.'), {\n code: 'three_ds_unresolved',\n });\n }\n }\n\n setStatus('complete');\n onSuccess?.(res as SecureCardCompleteResult);\n return res;\n } catch (err) {\n setError({\n code: (err as { code?: string })?.code ?? 'complete_failed',\n message: (err as Error)?.message ?? 'Payment could not be completed.',\n });\n setStatus('error');\n throw err;\n }\n }, [completeFn, resolveChallengeFn, challengeTimeoutMs, apiBaseUrl, instance, checkoutSessionId, session.sessionId, onSuccess]);\n\n const pay = useCallback(async () => {\n await vault();\n return complete();\n }, [vault, complete]);\n\n const value: SecureCardContextValue = {\n instance,\n registerField,\n registerThreeDSecure,\n cancelChallenge,\n status,\n error,\n ready: status === 'ready',\n pay,\n vault,\n complete,\n };\n\n return <SecureCardContext.Provider value={value}>{children}</SecureCardContext.Provider>;\n}\n\n/**\n * Default completion call — POSTs to the public, buyer-facing complete route.\n *\n * `completeSessionSchema` (`packages/platform-api/src/schemas/checkout-sessions.ts`)\n * is `.strict()` and requires `paymentMethod`. The handler\n * (`checkout-session-service.ts` `complete()`) rejects `paymentMethod: 'card'`\n * outright (\"Use 'embedded' ...\"), and for non-proxy-mode `'embedded'`\n * requires `processorCheckoutSessionId` or `processorTransactionId`. Secure\n * Fields vaults the card straight to the Gr4vy checkout session — no embed\n * transaction exists yet — so this is the server-authorize sub-branch: send\n * `processorCheckoutSessionId` = that same Gr4vy session id (`gr4vySessionId`,\n * i.e. `SecureCardSession.sessionId`), which the handler forwards into\n * `adapter.authorize({ token: JSON.stringify({ checkoutSessionId }) })`.\n *\n * `apiBaseUrl` is joined onto the path with `joinApiUrl` so a merchant-supplied\n * value with or without a trailing slash both resolve to the same URL —\n * without it, `https://api.example.com/` + `/api/v1/...` would double the slash.\n */\nasync function defaultComplete({\n sessionId,\n gr4vySessionId,\n apiBaseUrl,\n}: {\n sessionId: string;\n gr4vySessionId: string;\n apiBaseUrl: string;\n}) {\n const res = await fetch(joinApiUrl(apiBaseUrl, `/api/v1/checkout-sessions/${sessionId}/complete`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paymentMethod: 'embedded', processorCheckoutSessionId: gr4vySessionId }),\n });\n const body = await res.json();\n if (!res.ok) {\n throw Object.assign(new Error(body?.error?.message ?? 'Payment could not be completed.'), {\n code: body?.error?.code ?? 'payment_failed',\n });\n }\n return body?.data ?? body;\n}\n\n/** Joins a base URL and a path safely regardless of a trailing slash on `base`. */\nfunction joinApiUrl(base: string, path: string): string {\n return `${base.replace(/\\/+$/, '')}${path}`;\n}\n\nexport function useSecureCard(): SecureCardContextValue {\n const ctx = useContext(SecureCardContext);\n if (!ctx) throw new Error('useSecureCard must be used inside <SecureCardProvider>');\n return ctx;\n}\n\nexport { SecureCardContext };\n","import { SecureFields } from '@gr4vy/secure-fields';\n\n/**\n * Subset of the Secure Fields instance this module needs — real or mocked.\n *\n * Declared structurally rather than as `Pick<SecureFields, …>`: any reference\n * to the SDK's own type from an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts`, and that package's `exports` map carries no `types`\n * condition — so a consumer on `moduleResolution: bundler` gets TS7016 under\n * `skipLibCheck: false`, or silently degrades the member to `any` under\n * `skipLibCheck: true` (the Next default). The `paths` override in this\n * package's tsconfig repairs our own compile only; it does not ship.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower `CombinedEvents` event parameter\n * stays assignable to the `string` declared here.\n */\nexport interface ThreeDSecureInstance {\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n}\n\nexport interface ResolveChallengeArgs {\n /** The `SecureFields` instance already constructed for this session. */\n instance: ThreeDSecureInstance;\n /** Mount point for Gr4vy's own 3DS challenge iframe. */\n element: HTMLElement | string;\n challengeWindowSize?: '01' | '02' | '03' | '04' | '05';\n}\n\n/**\n * Drives a Gr4vy-hosted 3-D Secure challenge to completion using Secure\n * Fields' own `addThreeDSecure`, instead of hand-rolling a modal around\n * `transaction.paymentMethod.approvalUrl`.\n *\n * Why this instead of a modal + approvalUrl + redirect fallback: `addThreeDSecure(element, options)`\n * takes no transaction id and no approval URL — its only inputs are a mount\n * element and an optional `challengeWindowSize`\n * (node_modules/@gr4vy/secure-fields lib/index.d.ts, lib/types.d.ts —\n * `ThreeDSecureOptions = { challengeWindowSize?: ... }`). The bundled\n * implementation (lib/index.js, `class P` / `ThreeDSecure.init`) shows the\n * iframe it creates is `${frameUrl}/threeds.html` with query params\n * `{sessionId, parentOrigin, gr4vyId, environment}` — the exact identifiers\n * `SecureFields` was already constructed with. The challenge is scoped to the\n * Secure Fields *session*, not to anything passed in per-call. Our server\n * creates the transaction against that same session\n * (`paymentMethod: { method: 'checkout-session', id: checkoutSessionId }` —\n * packages/commerce-payment-orchestrator/src/adapters/gr4vy.ts `authorize()`),\n * so this element renders the challenge for that transaction without ever\n * touching `approvalUrl`. `approvalUrl` does not appear anywhere in the SDK\n * bundle (checked via `grep -c approvalUrl lib/index.js` → 0).\n *\n * `THREE_DS_FINISH`'s payload shape is undocumented (absent from the SDK's\n * README; only the bare event name appears in the CHANGELOG and\n * lib/constants.d.ts), so this does not try to read a success/fail flag off\n * it — it only signals \"the challenge UI is done.\" The caller re-queries\n * `/complete` afterwards for the authoritative outcome, which is also the\n * documented re-entry path for this endpoint (`checkout-session-service.ts`'s\n * `complete()` already tolerates re-invocation on the same session — see its\n * \"processing lease\" / pending-order-reuse comments).\n */\nexport function resolveChallenge({ instance, element, challengeWindowSize }: ResolveChallengeArgs): Promise<void> {\n return new Promise((resolve) => {\n const handleFinish = () => {\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n resolve();\n };\n // Registered before addThreeDSecure() mounts the frame, so a finish that\n // arrives immediately after mount is never missed.\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n instance.addThreeDSecure(element, challengeWindowSize ? { challengeWindowSize } : undefined);\n });\n}\n","import { useContext, useEffect, useRef, type CSSProperties } from 'react';\nimport { SecureCardContext, type SecureCardFieldKind } from './useSecureCard.js';\n\nexport interface SecureCardFieldProps {\n className?: string;\n style?: CSSProperties;\n /** Gr4vy field options (styles, autoFocus, maskInput, pattern…) passed through unchanged. */\n options?: Record<string, unknown>;\n}\n\n/**\n * Each field is an empty element Gr4vy replaces with its own iframe. We render\n * the container and nothing else: the merchant chose Secure Fields precisely so\n * that the styling is theirs.\n *\n * The element we hand Gr4vy is created with `document.createElement`, NOT the\n * one React renders. Gr4vy's SDK mounts a field with\n * `element.parentNode.replaceChild(itsOwnWrapper, element)` — it *replaces* the\n * node it is given. Handing it the React-managed node left React holding a\n * reference to a node that was no longer in the tree, so unmounting the field\n * threw an uncaught `NotFoundError: Failed to execute 'removeChild'` and killed\n * the page on the first click (Task 9a, Finding 3 — 100% reproducible in a\n * production build). Giving Gr4vy a throwaway child instead means React only\n * ever removes the host `<div>` it rendered itself, and everything Gr4vy put\n * inside goes with it.\n *\n * The merchant's `className`/`style` stay on the host element, and the\n * throwaway child carries `height: 100%` so Gr4vy's wrapper (which copies the\n * `style` attribute off the node it replaces) still fills the merchant's box —\n * the field iframe is `height: 100%` and would otherwise collapse to an\n * iframe's 150px default.\n *\n * Known limitation: Gr4vy's SDK has no unregister (only `removeEventListener`),\n * so a field that unmounts stays registered on the instance. Unmounting an\n * optional field (postal code) and remounting it is safe — re-registering\n * replaces the SDK's own per-type listener and builds a fresh iframe.\n * Unmounting a REQUIRED field (number, expiry, cvv) and then calling `pay()`\n * without remounting it leaves the SDK submitting against a field whose iframe\n * is gone; remount it first.\n */\nfunction useField(kind: SecureCardFieldKind, options?: Record<string, unknown>) {\n const ctx = useContext(SecureCardContext);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const instance = ctx?.instance ?? null;\n\n useEffect(() => {\n const host = hostRef.current;\n // React runs child effects before parent effects, so on the very first\n // commit the provider has not constructed the instance yet. This effect\n // re-runs when it appears.\n if (!host || !ctx || !instance) return;\n\n const target = document.createElement('div');\n target.style.height = '100%';\n host.appendChild(target);\n ctx.registerField(kind, target, options ?? {});\n\n return () => {\n // Drop whatever Gr4vy put in the host (its wrapper replaced `target`),\n // so a remount starts from an empty container instead of stacking a\n // second, dead iframe under the same field.\n while (host.firstChild) host.removeChild(host.firstChild);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [instance]);\n\n return hostRef;\n}\n\nexport function CardNumberField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('number', options)} className={className} style={style} />;\n}\n\nexport function ExpiryField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('expiry', options)} className={className} style={style} />;\n}\n\nexport function SecurityCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('cvv', options)} className={className} style={style} />;\n}\n\nexport function PostalCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('postalCode', options)} className={className} style={style} />;\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","import_jsx_runtime","DEFAULT_BASE_URL","import_react","import_react","import_cart","DEFAULT_STORAGE_KEY","readStored","writeStored","import_react","import_secure_fields","import_jsx_runtime","import_react","import_jsx_runtime"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx","../src/useSecureCard.tsx","../src/threeDSecure.ts","../src/SecureCardFields.tsx"],"sourcesContent":["export { CheckoutEmbed } from './CheckoutEmbed.js';\nexport { PaymentEmbed } from './PaymentEmbed.js';\nexport { useThrottleEvents } from './useThrottleEvents.js';\nexport { useCartSession } from './useCartSession.js';\nexport type { UseCartSessionOptions, UseCartSessionResult } from './useCartSession.js';\nexport { useThrottleCheckout } from './useThrottleCheckout.js';\nexport type {\n UseThrottleCheckoutOptions,\n UseThrottleCheckoutResult,\n ThrottleCheckoutStatus,\n CheckoutTotals,\n} from './useThrottleCheckout.js';\nexport type {\n ThrottleEvent,\n ThrottleMessage,\n ThrottleEnvelope,\n ThrottleCompletedLineItem,\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n RecurringIntent,\n ThrottleParentCommand,\n ThrottleParentEnvelope,\n ThrottleParentMessage,\n} from './types.js';\n\n// v0.1.x compat alias. v0.1's <ThrottleCheckout> shape (positional\n// onCompleted/onError args, optional baseUrl, no parentOrigin) is\n// gone in v0.2 — the alias resolves to <CheckoutEmbed> which now\n// requires `parentOrigin`. Update call sites accordingly.\nexport { CheckoutEmbed as ThrottleCheckout } from './CheckoutEmbed.js';\n\n// Secure Fields surface (Tasks 4-6)\nexport {\n SecureCardProvider,\n useSecureCard,\n type SecureCardSession,\n type SecureCardStatus,\n type SecureCardError,\n type SecureCardProviderProps,\n type SecureCardCompleteResult,\n // Required to type what the public surface already hands out:\n // `SecureCardContextValue` is `useSecureCard()`'s return, `SecureCardFieldKind`\n // is `registerField`'s first argument, and `SecureFieldsInstance` is the\n // `instance` on both.\n type SecureCardContextValue,\n type SecureCardFieldKind,\n type SecureFieldsInstance,\n} from './useSecureCard.js';\n// `resolveChallengeFn` is a public prop, so a merchant writing one needs its\n// argument type — and the instance type that argument carries.\nexport type { ResolveChallengeArgs, ThreeDSecureInstance } from './threeDSecure.js';\nexport {\n CardNumberField, ExpiryField, SecurityCodeField, PostalCodeField,\n type SecureCardFieldProps,\n} from './SecureCardFields.js';\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n ...(msg.subscriptionIds !== undefined ? { subscriptionIds: msg.subscriptionIds } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport type { ThrottleParentMessage } from './types.js';\n\ninterface UseParentCommandsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /** The Throttle origin to address messages to. */\n targetOrigin: string;\n /**\n * The current value of `submitDisabled` from the embed props. Posts a\n * `set-submit-disabled` parent command whenever it changes — and once\n * after the iframe reports `throttle.ready` so the iframe sees the\n * intent even if it boots after the prop is already set.\n *\n * `undefined` means the parent never explicitly enabled/disabled — we\n * never post in that case. Backward-compat with embeds that don't\n * pass the prop.\n */\n submitDisabled?: boolean;\n /**\n * Set to `true` once the iframe posts `throttle.ready`. Until then,\n * commands are buffered (we still try to post but the iframe may not\n * have its listener attached yet — and we re-post on ready as a\n * belt-and-braces guard).\n */\n iframeReady: boolean;\n}\n\n/**\n * Post parent → iframe commands when controlled props change. v1.2 adds\n * `set-submit-disabled`; future commands extend the same channel.\n *\n * postMessage is fire-and-forget — we don't wait for an ack from the\n * iframe. The iframe's listener is idempotent (setting the same value\n * twice is a no-op).\n */\nexport function useParentCommands({\n iframeRef,\n targetOrigin,\n submitDisabled,\n iframeReady,\n}: UseParentCommandsOptions) {\n const [lastSent, setLastSent] = useState<boolean | undefined>(undefined);\n\n // Post when the prop value changes OR when the iframe transitions to\n // ready (so the first state lands).\n useEffect(() => {\n if (submitDisabled === undefined) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // Skip if we already sent this value AND the iframe was already ready\n // — avoids spam on unrelated re-renders.\n if (lastSent === submitDisabled && iframeReady) return;\n if (!iframeReady) return; // Defer until ready\n\n const message: ThrottleParentMessage = {\n source: 'throttle-parent',\n version: 1,\n type: 'set-submit-disabled',\n disabled: submitDisabled,\n };\n win.postMessage(message, targetOrigin);\n setLastSent(submitDisabled);\n }, [iframeRef, targetOrigin, submitDisabled, iframeReady, lastSent]);\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n ...(msg.subscriptionIds !== undefined ? { subscriptionIds: msg.subscriptionIds } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n CartSessionClient,\n type CartSessionCart,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\nexport interface UseCartSessionOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the session id across page loads. Default\n * `'throttle.cartSessionId'`. Pass `null` to disable persistence (in-memory\n * only).\n */\n storageKey?: string | null;\n}\n\nexport interface UseCartSessionResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current opaque session id, or null before the first item. */\n cartSessionId: string | null;\n /** True while a request is in flight. */\n loading: boolean;\n /** Last error, or null. */\n error: Error | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n selectShipping: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /** Hand off to checkout. Returns the hand-off (redirect the buyer to `checkoutUrl`). */\n checkout: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\n/**\n * React hook for backend-less cart ownership. Wraps {@link CartSessionClient}:\n * creates the Throttle cart lazily on the first `addItem` (so empty carts\n * aren't created on every page load), persists the session id to localStorage,\n * and resumes it on the next visit. Mutations update reactive `cart` state.\n *\n * ```tsx\n * const cart = useCartSession({ applicationId, environmentId, quoteToken });\n * <button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>Add</button>\n * <button onClick={async () => {\n * const { checkoutUrl } = await cart.checkout({ returnUrl, cancelUrl });\n * window.location.href = checkoutUrl;\n * }}>Checkout</button>\n * ```\n */\nexport function useCartSession(options: UseCartSessionOptions): UseCartSessionResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Stable client across renders for a given config; construction validates the\n // token, so capture any constructor error instead of throwing during render.\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) setError(initError);\n }, [initError]);\n\n // Resume a persisted session on mount (and whenever the client identity changes).\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setLoading(true);\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n })\n .catch(() => {\n // Expired / unknown / origin-rejected → drop the stale id and start fresh.\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /** Run a mutation that returns the updated cart, managing loading/error + creating the session first. */\n const run = useCallback(\n async (fn: () => Promise<CartSessionCart>, opts: { create?: boolean } = {}): Promise<void> => {\n if (!client) {\n setError(initError ?? new Error('CartSessionClient unavailable'));\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const next = await fn();\n setCart(next);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const addItem = useCallback(\n (input: AddLineItemInput) => run(() => client!.addItem(input), { create: true }),\n [client, run],\n );\n const updateItem = useCallback(\n (itemId: string, input: UpdateLineItemInput) => run(() => client!.updateItem(itemId, input)),\n [client, run],\n );\n const removeItem = useCallback((itemId: string) => run(() => client!.removeItem(itemId)), [client, run]);\n const selectShipping = useCallback(\n (input: SelectShippingInput) => run(() => client!.selectShipping(input), { create: true }),\n [client, run],\n );\n const clearShipping = useCallback(() => run(() => client!.clearShipping()), [client, run]);\n const applyDiscount = useCallback(\n (code: string) => run(() => client!.applyDiscount(code), { create: true }),\n [client, run],\n );\n const removeDiscount = useCallback(() => run(() => client!.removeDiscount()), [client, run]);\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n await run(async () => (await client.get()).cart);\n }, [client, run]);\n\n const checkout = useCallback(\n async (input: CheckoutHandoffInput): Promise<CheckoutHandoff> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n setLoading(true);\n setError(null);\n try {\n await ensureSession();\n return await client.checkout(input);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n loading,\n error,\n addItem,\n updateItem,\n removeItem,\n selectShipping,\n clearShipping,\n applyDiscount,\n removeDiscount,\n checkout,\n refresh,\n reset,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CartSessionClient,\n CartNotOpenError,\n CartNotFoundError,\n type CartSessionCart,\n type SelectedShipping,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\n/**\n * Lifecycle status for the checkout orchestration.\n * - `idle` — no work in flight, no cart yet\n * - `loading` — a request is in flight\n * - `recovering` — the cart went stale (converted/abandoned); rebuilding from the\n * last-known line items into a fresh session\n * - `ready` — a cart is present and no request is in flight\n * - `error` — the last operation failed (see `error`)\n */\nexport type ThrottleCheckoutStatus = 'idle' | 'loading' | 'recovering' | 'ready' | 'error';\n\nexport interface CheckoutTotals {\n currency: string;\n subtotal: number;\n discountTotal: number;\n shippingTotal: number;\n taxTotal: number;\n total: number;\n}\n\nexport interface UseThrottleCheckoutOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the cart session id across page loads.\n * Default `'throttle.cartSessionId'`. Pass `null` to disable persistence.\n */\n storageKey?: string | null;\n /**\n * Auto-recover from a stale cart. When a mutation fails with\n * {@link CartNotOpenError} (the cart was converted by a completed order, or\n * abandoned), the hook starts a fresh session, re-adds the last-known line\n * items and re-selects the shipping method, then retries the operation once.\n * Default `true`. Set `false` to surface {@link CartNotOpenError} instead.\n */\n autoRecover?: boolean;\n}\n\nexport interface UseThrottleCheckoutResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current cart session id, or null before the first item. */\n cartSessionId: string | null;\n /** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */\n status: ThrottleCheckoutStatus;\n /** Last error, or null. */\n error: Error | null;\n /** The selected shipping method, bound to the cart (never a shadow copy). */\n selectedMethod: SelectedShipping | null;\n /** Live totals, bound to the cart. Null before a cart exists. */\n totals: CheckoutTotals | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n /** Lock a shipping method. One call — the returned cart carries recomputed totals. */\n selectMethod: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /**\n * Hand off to checkout. Returns the hand-off — use `checkoutSessionId` with\n * `<PaymentEmbed sessionId={…}>` (or redirect the buyer to `checkoutUrl`).\n */\n createSession: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\nfunction totalsOf(cart: CartSessionCart | null): CheckoutTotals | null {\n if (!cart) return null;\n return {\n currency: cart.currency,\n subtotal: cart.subtotal,\n discountTotal: cart.discountTotal,\n shippingTotal: cart.shippingTotal,\n taxTotal: cart.taxTotal,\n total: cart.total,\n };\n}\n\n/**\n * Orchestrates a backend-less storefront checkout on top of\n * {@link CartSessionClient}, bundling the pieces every storefront otherwise\n * re-implements:\n *\n * - **Cart as source of truth** — `selectedMethod` and `totals` are read straight\n * off the cart; there is no shadow copy to drift.\n * - **One-call shipping select** — `selectMethod` locks the method and returns the\n * cart with recomputed totals in a single round trip.\n * - **Stale-cart recovery** — a {@link CartNotOpenError} (cart converted by a\n * completed order, or abandoned) transparently rebuilds a fresh session from\n * the last-known line items + shipping and retries (`status === 'recovering'`).\n * - **Session handoff** — `createSession` returns the `checkoutSessionId` for\n * `<PaymentEmbed>`.\n *\n * Address collection is handled inside the checkout embed (the hosted/full\n * checkout collects shipping/billing), so this hook intentionally does not write\n * addresses to the cart.\n *\n * ```tsx\n * const { addItem, selectMethod, totals, createSession, status } =\n * useThrottleCheckout({ applicationId, environmentId, quoteToken });\n * ```\n */\nexport function useThrottleCheckout(\n options: UseThrottleCheckoutOptions,\n): UseThrottleCheckoutResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const autoRecover = options.autoRecover ?? true;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [status, setStatus] = useState<ThrottleCheckoutStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Keep the latest cart in a ref so recovery can read the last-known line items\n // without threading it through every callback's dependency list.\n const cartRef = useRef<CartSessionCart | null>(null);\n cartRef.current = cart;\n\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) {\n setError(initError);\n setStatus('error');\n }\n }, [initError]);\n\n // Resume a persisted session on mount.\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setStatus('loading');\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n setStatus('ready');\n })\n .catch(() => {\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n setStatus('idle');\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /**\n * Rebuild a fresh session from the last-known cart so a stale cart doesn't\n * strand the buyer. Re-adds line items and re-selects shipping, then returns\n * the rebuilt cart.\n */\n const rebuild = useCallback(async (): Promise<CartSessionCart> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n const prev = cartRef.current;\n // Drop the stale session and create a new one.\n client.resume('');\n setCartSessionId(null);\n const session = await client.create({\n currency: prev?.currency,\n netN: prev?.netN ?? undefined,\n });\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n let next = session.cart;\n for (const item of prev?.lineItems ?? []) {\n next = await client.addItem({\n type: item.type,\n referenceId: item.referenceId ?? undefined,\n name: item.name,\n description: item.description ?? undefined,\n unitPrice: item.unitPrice,\n quantity: item.quantity,\n imageUrl: item.imageUrl ?? undefined,\n metadata: item.metadata,\n });\n }\n const ship = prev?.selectedShipping;\n if (ship) {\n next = await client.selectShipping({\n methodId: ship.methodId,\n displayName: ship.displayName,\n rateAmount: ship.rateAmount,\n currency: ship.currency,\n carrier: ship.carrier ?? undefined,\n serviceCode: ship.serviceCode ?? undefined,\n estimatedDeliveryDays: ship.estimatedDeliveryDays ?? undefined,\n });\n }\n setCart(next);\n return next;\n }, [client, initError, storageKey]);\n\n /**\n * Run a cart op with loading/error bookkeeping. On a stale-cart error, rebuild\n * (when `autoRecover`) and — for forward-progress ops (`retryAfterRecover`) —\n * retry once against the rebuilt session. Item-id-specific ops set\n * `retryAfterRecover: false` because the id no longer exists after rebuild.\n */\n const run = useCallback(\n async <T,>(\n fn: () => Promise<T>,\n opts: { create?: boolean; retryAfterRecover?: boolean } = {},\n ): Promise<T> => {\n if (!client) {\n const e = initError ?? new Error('CartSessionClient unavailable');\n setError(e);\n setStatus('error');\n throw e;\n }\n setStatus('loading');\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const result = await fn();\n setStatus('ready');\n return result;\n } catch (e) {\n const stale = e instanceof CartNotOpenError || e instanceof CartNotFoundError;\n if (stale && autoRecover) {\n try {\n setStatus('recovering');\n await rebuild();\n if (opts.retryAfterRecover) {\n const retried = await fn();\n setStatus('ready');\n return retried;\n }\n // Rebuild-only: the original op targeted a now-gone id. Surface the\n // rebuilt cart and let the caller reconcile against fresh state.\n setStatus('ready');\n const stale2 = new CartNotOpenError({\n message: 'Cart was rebuilt after going stale; retry your action against the new cart.',\n });\n setError(stale2);\n throw stale2;\n } catch (recoverErr) {\n setError(recoverErr as Error);\n setStatus('error');\n throw recoverErr;\n }\n }\n setError(e as Error);\n setStatus('error');\n throw e;\n }\n },\n [client, ensureSession, initError, autoRecover, rebuild],\n );\n\n const addItem = useCallback(\n async (input: AddLineItemInput) => {\n const next = await run(() => client!.addItem(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const updateItem = useCallback(\n async (itemId: string, input: UpdateLineItemInput) => {\n const next = await run(() => client!.updateItem(itemId, input), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const removeItem = useCallback(\n async (itemId: string) => {\n const next = await run(() => client!.removeItem(itemId), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const selectMethod = useCallback(\n async (input: SelectShippingInput) => {\n const next = await run(() => client!.selectShipping(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const clearShipping = useCallback(async () => {\n const next = await run(() => client!.clearShipping(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n const applyDiscount = useCallback(\n async (code: string) => {\n const next = await run(() => client!.applyDiscount(code), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const removeDiscount = useCallback(async () => {\n const next = await run(() => client!.removeDiscount(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n const next = await run(async () => (await client.get()).cart);\n setCart(next);\n }, [client, run]);\n\n const createSession = useCallback(\n (input: CheckoutHandoffInput): Promise<CheckoutHandoff> =>\n run(() => client!.checkout(input), { create: true, retryAfterRecover: true }),\n [client, run],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n setStatus('idle');\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n status,\n error,\n selectedMethod: cart?.selectedShipping ?? null,\n totals: totalsOf(cart),\n addItem,\n updateItem,\n removeItem,\n selectMethod,\n clearShipping,\n applyDiscount,\n removeDiscount,\n createSession,\n refresh,\n reset,\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { SecureFields } from '@gr4vy/secure-fields';\nimport { resolveChallenge, type ResolveChallengeArgs } from './threeDSecure.js';\n\n/**\n * Lifecycle status for the headless Secure Fields state machine.\n * - `idle` — provider mounted, fields not yet registered\n * - `mounting` — Secure Fields instance constructed, fields registering\n * - `ready` — all required fields registered, submit() may be called\n * - `vaulting` — submit() called, waiting on CARD_VAULT_SUCCESS/FAILURE\n * - `authorizing` — card vaulted, awaiting the merchant's complete() call\n * - `challenging` — complete() returned `requires_action`; Gr4vy's own 3DS\n * iframe (addThreeDSecure) is mounted and running\n * - `complete` — order created\n * - `error` — vault, challenge, or completion failed (see `error`)\n */\nexport type SecureCardStatus =\n | 'idle'\n | 'mounting'\n | 'ready'\n | 'vaulting'\n | 'authorizing'\n | 'challenging'\n | 'complete'\n | 'error';\n\n/** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */\nexport interface SecureCardSession {\n sessionId: string;\n /**\n * The payment provider instance this session belongs to, straight from\n * `POST /checkout-sessions/:id/card-session`.\n *\n * Either name works. `providerId` is the documented one; `gr4vyId` is the\n * original and is still accepted, because the package shipped with it and\n * dropping it would break consumers already passing the response through.\n */\n providerId?: string;\n /** @deprecated Use `providerId`. Still honoured. */\n gr4vyId?: string;\n environment: 'sandbox' | 'production';\n}\n\nexport interface SecureCardError {\n code: string;\n message: string;\n}\n\nexport type SecureCardFieldKind = 'number' | 'expiry' | 'cvv' | 'postalCode';\n\n/**\n * Everything this package uses off a `SecureFields` instance, declared\n * structurally instead of re-exporting the SDK's own class type.\n *\n * `@gr4vy/secure-fields`'s `exports` map has no `types` condition, so naming\n * its type in an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts` — which is a hard TS7016 for any consumer on\n * `moduleResolution: bundler` with `skipLibCheck: false`, and a silent\n * degradation of `instance` to `any` with `skipLibCheck: true` (the Next\n * default). That hits every installer of this package, not only Secure Fields\n * users. The `paths` override in this package's tsconfig repairs our own\n * compile; it is monorepo-only and does not ship. Same approach as\n * `ThreeDSecureInstance` in `threeDSecure.ts`.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower option and event types stay\n * assignable to the looser ones declared here.\n */\nexport interface SecureFieldsInstance {\n /** Gr4vy's hidden controller iframe, removed when the provider unmounts. */\n controller?: HTMLIFrameElement;\n addCardNumberField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addExpiryDateField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addSecurityCodeField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addField(element: string | HTMLElement, options: Record<string, unknown>): unknown;\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n submit(): void;\n}\n\nexport interface SecureCardContextValue {\n /**\n * The underlying Secure Fields instance — Task 5's field primitives register\n * against it.\n *\n * `null` until the provider's mount effect has constructed it. It cannot be\n * built during render: `new SecureFields()` mutates the document (see the\n * effect in `SecureCardProvider`), and React does not promise a render-phase\n * factory runs exactly once. Consumers that reach for it directly must\n * null-check; the field primitives already wait for it.\n */\n instance: SecureFieldsInstance | null;\n registerField: (kind: SecureCardFieldKind, element: HTMLElement, options?: Record<string, unknown>) => void;\n /**\n * Mount point for Gr4vy's own 3DS challenge iframe (`addThreeDSecure`).\n * Register this once, alongside the card fields; it is only actually\n * mounted if `complete()` gets `requires_action`.\n */\n registerThreeDSecure: (\n element: HTMLElement | string,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ) => void;\n /**\n * Abandons an in-progress challenge (e.g. the buyer closed the challenge\n * UI). Only meaningful while `status === 'challenging'`; settles\n * `complete()`'s promise with `error.code === 'three_ds_abandoned'`, kept\n * distinct from a processor decline code.\n */\n cancelChallenge: () => void;\n status: SecureCardStatus;\n error: SecureCardError | null;\n /** True once all required fields are registered and submit() may be called. */\n ready: boolean;\n /** vault() then complete(). */\n pay: () => Promise<unknown>;\n vault: () => Promise<void>;\n complete: () => Promise<unknown>;\n}\n\nconst SecureCardContext = createContext<SecureCardContextValue | null>(null);\n\n// number, expiry, cvv are required; postalCode is optional.\nconst FIELDS_REQUIRED_TO_BE_READY = 3;\n\nexport interface SecureCardProviderProps {\n session: SecureCardSession;\n /**\n * Throttle's own checkout session id — the `:id` already used to mint\n * `session` via `POST /checkout-sessions/:id/card-session`. NOT the same\n * value as `session.sessionId`: that field is Gr4vy's own checkout session\n * id (required to construct the `SecureFields` instance — see\n * `packages/platform-api/src/routes/card-session.ts`, which returns\n * `{ sessionId: checkoutSessionId, ... }` from `adapter.createCardSession()`,\n * a Gr4vy-minted id). The public `POST /checkout-sessions/:id/complete`\n * route looks `:id` up directly against Throttle's own `checkoutSessions`\n * table (`checkout-sessions.ts`), so `defaultComplete` needs this id, not\n * `session.sessionId`, to call the right URL.\n */\n checkoutSessionId: string;\n children: ReactNode;\n /** Injected for tests / custom completion flows; defaults to a real POST /complete call. */\n completeFn?: (args: { sessionId: string; gr4vySessionId: string }) => Promise<any>;\n /** Injected for tests / custom 3DS UI; defaults to mounting Gr4vy's own 3DS iframe via addThreeDSecure. */\n resolveChallengeFn?: (args: ResolveChallengeArgs) => Promise<void>;\n /**\n * Bounds how long `complete()` waits on an unfinished challenge before\n * settling to a terminal `error` (code `three_ds_timeout`) on its own — the\n * only other exits are Gr4vy's own `THREE_DS_FINISH` and an explicit\n * `cancelChallenge()` call, neither of which is guaranteed to ever fire if\n * the buyer just abandons the tab. Not sourced from Gr4vy documentation\n * (no challenge-duration limit is published in the SDK); 5 minutes is a\n * conservative, overridable default.\n */\n challengeTimeoutMs?: number;\n /**\n * Base URL of the Throttle API (e.g. `https://api.usethrottle.dev`).\n * Required — not optional-with-a-same-origin-default. Secure Fields is the\n * headless surface for merchants who mount the card form on THEIR OWN\n * origin, which by definition is not same-origin with the Throttle API, so\n * `defaultComplete`'s `fetch()` can never resolve a relative path (it 404s\n * against the merchant's own domain at payment time instead). Making this\n * required turns that into a compile error instead of a silent runtime\n * failure. Joined onto the request path regardless of a trailing slash.\n */\n apiBaseUrl: string;\n onSuccess?: (result: SecureCardCompleteResult) => void;\n}\n\n/**\n * The real `POST /checkout-sessions/:id/complete` success body for the\n * Secure Fields flow (`checkout-session-service.ts` `complete()`,\n * proxy-mode-embedded return, ~line 2338 and its idempotent-retry twin at\n * ~line 1849). There is no `order` key.\n */\nexport interface SecureCardCompleteResult {\n orderId: string;\n orderNumber: string;\n paymentId: string;\n /** Minor units (cents). */\n total: number;\n currency: string;\n status: string;\n paymentStatus: string;\n}\n\nconst DEFAULT_CHALLENGE_TIMEOUT_MS = 5 * 60 * 1000;\n\nexport function SecureCardProvider({\n session,\n checkoutSessionId,\n children,\n completeFn,\n resolveChallengeFn,\n challengeTimeoutMs = DEFAULT_CHALLENGE_TIMEOUT_MS,\n apiBaseUrl,\n onSuccess,\n}: SecureCardProviderProps) {\n const [status, setStatus] = useState<SecureCardStatus>('idle');\n const [error, setError] = useState<SecureCardError | null>(null);\n const [mountedCount, setMountedCount] = useState(0);\n const vaultWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n const threeDSElement = useRef<HTMLElement | string | null>(null);\n const threeDSOptions = useRef<{ challengeWindowSize?: '01' | '02' | '03' | '04' | '05' } | undefined>(undefined);\n const challengeWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n\n const { environment, sessionId } = session;\n // Either name is accepted: `providerId` is documented, `gr4vyId` is the\n // original the package first shipped with. Resolving here means the rest of\n // the hook never has to care which one the caller passed through.\n const providerInstanceId = session.providerId ?? session.gr4vyId ?? '';\n const [instance, setInstance] = useState<SecureFieldsInstance | null>(null);\n /** Mount point for Gr4vy's challenge, as state so arming can be an effect. */\n const [threeDSMount, setThreeDSMount] = useState<HTMLElement | string | null>(null);\n\n /**\n * `new SecureFields()` is a side effect on the document, not a computation.\n * Its constructor removes whatever `iframe#controller[src*=secure-fields]`\n * is already on the page, creates its own, appends it to `document.body`,\n * and resets the SDK's module-level event bus — so it must run exactly once\n * per mounted provider.\n *\n * It used to sit in a `useMemo`. React invokes a memo factory twice under\n * Strict Mode and commits the FIRST value, so the second construction tore\n * the committed instance's own controller iframe off the page. That\n * instance's internal `controllerReady` promise then never resolved, Gr4vy\n * never inserted a single field iframe, and yet `status` still reported\n * `ready` — because `ready` counts our own `registerField` calls, not the\n * SDK's DOM. Every \"verified in a browser\" pass before Task 9a ran against\n * that false positive (Task 9a, Finding 1).\n *\n * Task 4 had already moved `setStatus('mounting')` out of this same memo for\n * exactly this reason and left the SDK construction behind. `useMemo` is not\n * a lifecycle hook; an effect is the only hook React pairs with a cleanup,\n * so a Strict-Mode double-invoke settles on one live, attached instance.\n */\n useEffect(() => {\n const created = new SecureFields({ gr4vyId: providerInstanceId, environment, sessionId });\n setInstance(created);\n // A rebuilt instance has no registered fields. Both the counter and the\n // status derived from it must go back, or a `session` prop change leaves\n // `ready === true` while every field iframe is still being re-created and\n // `submit()` would fire against nothing.\n setMountedCount(0);\n setStatus((current) => (current === 'idle' || current === 'ready' ? 'mounting' : current));\n return () => {\n // The SDK exposes no teardown at all, so do the two things that matter:\n // take its controller iframe off the page, and stop handing the instance\n // out so no field can ever register against a discarded one.\n created.controller?.remove();\n setInstance(null);\n };\n }, [providerInstanceId, environment, sessionId]);\n\n useEffect(() => {\n if (!instance) return;\n\n const handleSuccess = () => {\n vaultWaiter.current?.resolve();\n };\n const handleFailure = (data?: { code?: string; message?: string }) => {\n vaultWaiter.current?.reject({\n code: data?.code ?? 'card_vault_failed',\n message: data?.message ?? 'The card could not be saved.',\n });\n };\n\n // The challenge now happens INSIDE submit(), between `vaulting` and the\n // vault result, so its status is driven by Gr4vy's own events rather than\n // by anything the server says. Without these the UI sits on `vaulting`\n // while a challenge is visibly on screen.\n const handleThreeDSStart = () => setStatus('challenging');\n const handleThreeDSFinish = () => {\n // Only meaningful while a challenge is genuinely outstanding.\n //\n // The provider fires FINISH even for a challenge that was cancelled or\n // failed, and it arrives AFTER the cancellation has settled the flow.\n // Without this guard that late event moved the status to `vaulting` — a\n // state nothing can leave, because the vault already resolved before the\n // challenge began. A buyer who pressed \"cancel\" was left on a spinner\n // forever, with no error and no way back. Observed in a browser; the\n // state machine reads fine without running it.\n if (!challengeWaiter.current) return;\n // Back to waiting on CARD_VAULT_SUCCESS/FAILURE, which follows the\n // challenge. The authentication outcome is not read from here: the\n // server is the authority on whether the money moved.\n setStatus((current) => (current === 'challenging' ? 'vaulting' : current));\n };\n\n instance.addEventListener(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.addEventListener(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.addEventListener(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n\n return () => {\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n };\n }, [instance]);\n\n const registerField = useCallback(\n (kind: SecureCardFieldKind, element: HTMLElement, options: Record<string, unknown> = {}) => {\n // Fields render before the provider's mount effect has constructed the\n // instance (React runs child effects before parent effects). They\n // re-register themselves when `instance` appears — see SecureCardFields.\n if (!instance) return;\n if (kind === 'number') instance.addCardNumberField(element, options);\n else if (kind === 'expiry') instance.addExpiryDateField(element, options);\n else if (kind === 'cvv') instance.addSecurityCodeField(element, options);\n else instance.addField(element, { type: 'postalCode', ...options } as any);\n setMountedCount((n) => n + 1);\n },\n [instance],\n );\n\n useEffect(() => {\n if (mountedCount >= FIELDS_REQUIRED_TO_BE_READY) setStatus('ready');\n }, [mountedCount]);\n\n const registerThreeDSecure = useCallback(\n (element: HTMLElement | string, options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' }) => {\n threeDSElement.current = element;\n threeDSOptions.current = options;\n // State as well as the ref: arming 3DS is an effect that has to re-run\n // when the mount point appears, and a ref write does not re-render.\n setThreeDSMount(element);\n },\n [],\n );\n\n /**\n * Arm 3-D Secure as SETUP, before `submit()` — not as a reaction to the\n * server.\n *\n * Gr4vy runs the enrolment check \"at the time of submitting Secure Fields\",\n * so `addThreeDSecure` has to be registered before `submit()` or the\n * challenge can never be raised. The original build called it only after\n * `/complete` answered `requires_action`, which is after `submit()` has\n * already finished — so it never ran, and every sandbox transaction came\n * back `threeDSecure: undefined`.\n *\n * The SDK has no un-register, so this arms once per instance; a changed\n * mount element arrives with a new instance or not at all.\n */\n useEffect(() => {\n if (!instance || !threeDSMount) return;\n try {\n instance.addThreeDSecure(\n threeDSMount,\n threeDSOptions.current?.challengeWindowSize\n ? { challengeWindowSize: threeDSOptions.current.challengeWindowSize }\n : undefined,\n );\n } catch (err) {\n // Arming is best-effort: a card that never needs a challenge must still\n // be chargeable. A card that DOES need one will fail visibly at submit.\n setError({\n code: 'three_ds_arm_failed',\n message: (err as Error)?.message ?? 'Could not arm 3-D Secure.',\n });\n }\n }, [instance, threeDSMount]);\n\n const cancelChallenge = useCallback(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_abandoned',\n message: 'The 3-D Secure challenge was abandoned.',\n });\n }, []);\n\n const vault = useCallback(() => {\n // Nothing to submit to yet. Previously impossible (the instance was built\n // during render); now the provider's first commit has none, and calling\n // `submit()` on nothing would hang `vaulting` forever with no rejection.\n if (!instance) {\n return Promise.reject<void>({\n code: 'not_ready',\n message: 'The card fields are not ready yet.',\n } as SecureCardError);\n }\n\n // A second vault() while one is already in flight would otherwise silently\n // overwrite vaultWaiter.current, orphaning the first caller's promise forever\n // (no resolve, no reject). Reject the second call instead; the first keeps\n // waiting on its own vault event.\n if (vaultWaiter.current) {\n return Promise.reject<void>({\n code: 'vault_in_progress',\n message: 'A card vault is already in progress.',\n } as SecureCardError);\n }\n\n setError(null);\n setStatus('vaulting');\n return new Promise<void>((resolve, reject) => {\n vaultWaiter.current = {\n resolve: () => {\n vaultWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n vaultWaiter.current = null;\n setError(e);\n setStatus('error');\n reject(e);\n },\n };\n instance.submit();\n });\n }, [instance]);\n\n const complete = useCallback(async () => {\n setStatus('authorizing');\n const doComplete = completeFn ?? ((args: { sessionId: string; gr4vySessionId: string }) => defaultComplete({ ...args, apiBaseUrl }));\n const doResolveChallenge = resolveChallengeFn ?? resolveChallenge;\n try {\n let res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n\n if (res?.status === 'requires_action') {\n if (!threeDSElement.current) {\n throw Object.assign(new Error('No 3-D Secure mount element registered (call registerThreeDSecure).'), {\n code: 'three_ds_element_missing',\n });\n }\n if (!instance) {\n throw Object.assign(new Error('The card fields are not ready yet.'), { code: 'not_ready' });\n }\n setStatus('challenging');\n // Gr4vy's own 3DS iframe runs the challenge; this resolves once it\n // reports THREE_DS_FINISH, or rejects if cancelChallenge() is called\n // or the resolver itself fails, or rejects on its own after\n // challengeTimeoutMs so a buyer who just\n // abandons the tab (no finish, no cancel) can never strand complete()\n // forever. It does not tell us success/failure — re-query /complete\n // for that.\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_timeout',\n message: 'The 3-D Secure challenge timed out.',\n });\n }, challengeTimeoutMs);\n\n challengeWaiter.current = {\n resolve: () => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n reject(Object.assign(new Error(e.message), { code: e.code }));\n },\n };\n doResolveChallenge({\n instance,\n element: threeDSElement.current!,\n ...(threeDSOptions.current ?? {}),\n }).then(\n () => challengeWaiter.current?.resolve(),\n // `resolveChallenge` calls `instance.addThreeDSecure()` synchronously\n // inside its Promise executor, so an SDK throw rejects here; a\n // merchant-supplied `resolveChallengeFn` can reject too. Without this\n // arm the only exit was `challengeTimeoutMs` — five minutes stuck at\n // `status: 'challenging'`, plus an unhandled rejection. Same\n // code-passthrough convention as `complete()`'s catch: keep a `code`\n // the resolver supplied, otherwise report `three_ds_failed`.\n (err: unknown) =>\n challengeWaiter.current?.reject({\n code: (err as { code?: string })?.code ?? 'three_ds_failed',\n message: (err as Error)?.message ?? 'The 3-D Secure challenge could not be completed.',\n }),\n );\n });\n\n setStatus('authorizing');\n res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n if (res?.status === 'requires_action') {\n throw Object.assign(new Error('The payment still requires authentication after the 3-D Secure challenge.'), {\n code: 'three_ds_unresolved',\n });\n }\n }\n\n setStatus('complete');\n onSuccess?.(res as SecureCardCompleteResult);\n return res;\n } catch (err) {\n setError({\n code: (err as { code?: string })?.code ?? 'complete_failed',\n message: (err as Error)?.message ?? 'Payment could not be completed.',\n });\n setStatus('error');\n throw err;\n }\n }, [completeFn, resolveChallengeFn, challengeTimeoutMs, apiBaseUrl, instance, checkoutSessionId, session.sessionId, onSuccess]);\n\n const pay = useCallback(async () => {\n await vault();\n return complete();\n }, [vault, complete]);\n\n const value: SecureCardContextValue = {\n instance,\n registerField,\n registerThreeDSecure,\n cancelChallenge,\n status,\n error,\n // \"The fields are mounted and nothing is in flight\" — NOT `status ===\n // 'ready'`. Deriving it from the status alone made a decline terminal: the\n // only thing that ever sets `ready` is the mount-count effect, which does\n // not re-run after `setStatus('error')`, so `ready` stayed false and the\n // merchant's pay button stayed disabled. A declined buyer could not try\n // another card without reloading the page — and a decline is the single\n // most common failure at a checkout.\n ready:\n mountedCount >= FIELDS_REQUIRED_TO_BE_READY &&\n status !== 'vaulting' &&\n status !== 'authorizing' &&\n status !== 'challenging' &&\n status !== 'complete',\n pay,\n vault,\n complete,\n };\n\n return <SecureCardContext.Provider value={value}>{children}</SecureCardContext.Provider>;\n}\n\n/**\n * Default completion call — POSTs to the public, buyer-facing complete route.\n *\n * `completeSessionSchema` (`packages/platform-api/src/schemas/checkout-sessions.ts`)\n * is `.strict()` and requires `paymentMethod`. The handler\n * (`checkout-session-service.ts` `complete()`) rejects `paymentMethod: 'card'`\n * outright (\"Use 'embedded' ...\"), and for non-proxy-mode `'embedded'`\n * requires `processorCheckoutSessionId` or `processorTransactionId`. Secure\n * Fields vaults the card straight to the Gr4vy checkout session — no embed\n * transaction exists yet — so this is the server-authorize sub-branch: send\n * `processorCheckoutSessionId` = that same Gr4vy session id (`gr4vySessionId`,\n * i.e. `SecureCardSession.sessionId`), which the handler forwards into\n * `adapter.authorize({ token: JSON.stringify({ checkoutSessionId }) })`.\n *\n * `apiBaseUrl` is joined onto the path with `joinApiUrl` so a merchant-supplied\n * value with or without a trailing slash both resolve to the same URL —\n * without it, `https://api.example.com/` + `/api/v1/...` would double the slash.\n */\nasync function defaultComplete({\n sessionId,\n gr4vySessionId,\n apiBaseUrl,\n}: {\n sessionId: string;\n gr4vySessionId: string;\n apiBaseUrl: string;\n}) {\n const res = await fetch(joinApiUrl(apiBaseUrl, `/api/v1/checkout-sessions/${sessionId}/complete`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paymentMethod: 'embedded', processorCheckoutSessionId: gr4vySessionId }),\n });\n const body = await res.json();\n if (!res.ok) {\n throw Object.assign(new Error(body?.error?.message ?? 'Payment could not be completed.'), {\n code: body?.error?.code ?? 'payment_failed',\n });\n }\n return body?.data ?? body;\n}\n\n/** Joins a base URL and a path safely regardless of a trailing slash on `base`. */\nfunction joinApiUrl(base: string, path: string): string {\n return `${base.replace(/\\/+$/, '')}${path}`;\n}\n\nexport function useSecureCard(): SecureCardContextValue {\n const ctx = useContext(SecureCardContext);\n if (!ctx) throw new Error('useSecureCard must be used inside <SecureCardProvider>');\n return ctx;\n}\n\nexport { SecureCardContext };\n","import { SecureFields } from '@gr4vy/secure-fields';\n\n/**\n * Subset of the Secure Fields instance this module needs — real or mocked.\n *\n * Declared structurally rather than as `Pick<SecureFields, …>`: any reference\n * to the SDK's own type from an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts`, and that package's `exports` map carries no `types`\n * condition — so a consumer on `moduleResolution: bundler` gets TS7016 under\n * `skipLibCheck: false`, or silently degrades the member to `any` under\n * `skipLibCheck: true` (the Next default). The `paths` override in this\n * package's tsconfig repairs our own compile only; it does not ship.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower `CombinedEvents` event parameter\n * stays assignable to the `string` declared here.\n */\nexport interface ThreeDSecureInstance {\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n}\n\nexport interface ResolveChallengeArgs {\n /** The `SecureFields` instance already constructed for this session. */\n instance: ThreeDSecureInstance;\n /** Mount point for Gr4vy's own 3DS challenge iframe. */\n element: HTMLElement | string;\n challengeWindowSize?: '01' | '02' | '03' | '04' | '05';\n}\n\n/**\n * Drives a Gr4vy-hosted 3-D Secure challenge to completion using Secure\n * Fields' own `addThreeDSecure`, instead of hand-rolling a modal around\n * `transaction.paymentMethod.approvalUrl`.\n *\n * Why this instead of a modal + approvalUrl + redirect fallback: `addThreeDSecure(element, options)`\n * takes no transaction id and no approval URL — its only inputs are a mount\n * element and an optional `challengeWindowSize`\n * (node_modules/@gr4vy/secure-fields lib/index.d.ts, lib/types.d.ts —\n * `ThreeDSecureOptions = { challengeWindowSize?: ... }`). The bundled\n * implementation (lib/index.js, `class P` / `ThreeDSecure.init`) shows the\n * iframe it creates is `${frameUrl}/threeds.html` with query params\n * `{sessionId, parentOrigin, gr4vyId, environment}` — the exact identifiers\n * `SecureFields` was already constructed with. The challenge is scoped to the\n * Secure Fields *session*, not to anything passed in per-call. Our server\n * creates the transaction against that same session\n * (`paymentMethod: { method: 'checkout-session', id: checkoutSessionId }` —\n * packages/commerce-payment-orchestrator/src/adapters/gr4vy.ts `authorize()`),\n * so this element renders the challenge for that transaction without ever\n * touching `approvalUrl`. `approvalUrl` does not appear anywhere in the SDK\n * bundle (checked via `grep -c approvalUrl lib/index.js` → 0).\n *\n * `THREE_DS_FINISH`'s payload shape is undocumented (absent from the SDK's\n * README; only the bare event name appears in the CHANGELOG and\n * lib/constants.d.ts), so this does not try to read a success/fail flag off\n * it — it only signals \"the challenge UI is done.\" The caller re-queries\n * `/complete` afterwards for the authoritative outcome, which is also the\n * documented re-entry path for this endpoint (`checkout-session-service.ts`'s\n * `complete()` already tolerates re-invocation on the same session — see its\n * \"processing lease\" / pending-order-reuse comments).\n */\nexport function resolveChallenge({ instance, element, challengeWindowSize }: ResolveChallengeArgs): Promise<void> {\n return new Promise((resolve) => {\n const handleFinish = () => {\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n resolve();\n };\n // Registered before addThreeDSecure() mounts the frame, so a finish that\n // arrives immediately after mount is never missed.\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n instance.addThreeDSecure(element, challengeWindowSize ? { challengeWindowSize } : undefined);\n });\n}\n","import { useContext, useEffect, useRef, type CSSProperties } from 'react';\nimport { SecureCardContext, type SecureCardFieldKind } from './useSecureCard.js';\n\nexport interface SecureCardFieldProps {\n className?: string;\n style?: CSSProperties;\n /** Gr4vy field options (styles, autoFocus, maskInput, pattern…) passed through unchanged. */\n options?: Record<string, unknown>;\n}\n\n/**\n * Each field is an empty element Gr4vy replaces with its own iframe. We render\n * the container and nothing else: the merchant chose Secure Fields precisely so\n * that the styling is theirs.\n *\n * The element we hand Gr4vy is created with `document.createElement`, NOT the\n * one React renders. Gr4vy's SDK mounts a field with\n * `element.parentNode.replaceChild(itsOwnWrapper, element)` — it *replaces* the\n * node it is given. Handing it the React-managed node left React holding a\n * reference to a node that was no longer in the tree, so unmounting the field\n * threw an uncaught `NotFoundError: Failed to execute 'removeChild'` and killed\n * the page on the first click (Task 9a, Finding 3 — 100% reproducible in a\n * production build). Giving Gr4vy a throwaway child instead means React only\n * ever removes the host `<div>` it rendered itself, and everything Gr4vy put\n * inside goes with it.\n *\n * The merchant's `className`/`style` stay on the host element, and the\n * throwaway child carries `height: 100%` so Gr4vy's wrapper (which copies the\n * `style` attribute off the node it replaces) still fills the merchant's box —\n * the field iframe is `height: 100%` and would otherwise collapse to an\n * iframe's 150px default.\n *\n * Known limitation: Gr4vy's SDK has no unregister (only `removeEventListener`),\n * so a field that unmounts stays registered on the instance. Unmounting an\n * optional field (postal code) and remounting it is safe — re-registering\n * replaces the SDK's own per-type listener and builds a fresh iframe.\n * Unmounting a REQUIRED field (number, expiry, cvv) and then calling `pay()`\n * without remounting it leaves the SDK submitting against a field whose iframe\n * is gone; remount it first.\n */\nfunction useField(kind: SecureCardFieldKind, options?: Record<string, unknown>) {\n const ctx = useContext(SecureCardContext);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const instance = ctx?.instance ?? null;\n\n useEffect(() => {\n const host = hostRef.current;\n // React runs child effects before parent effects, so on the very first\n // commit the provider has not constructed the instance yet. This effect\n // re-runs when it appears.\n if (!host || !ctx || !instance) return;\n\n const target = document.createElement('div');\n target.style.height = '100%';\n host.appendChild(target);\n ctx.registerField(kind, target, options ?? {});\n\n return () => {\n // Drop whatever Gr4vy put in the host (its wrapper replaced `target`),\n // so a remount starts from an empty container instead of stacking a\n // second, dead iframe under the same field.\n while (host.firstChild) host.removeChild(host.firstChild);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [instance]);\n\n return hostRef;\n}\n\nexport function CardNumberField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('number', options)} className={className} style={style} />;\n}\n\nexport function ExpiryField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('expiry', options)} className={className} style={style} />;\n}\n\nexport function SecurityCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('cvv', options)} className={className} style={style} />;\n}\n\nexport function PostalCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('postalCode', options)} className={className} style={style} />;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAA8C;;;ACA9C,mBAA0C;AA4BnC,SAAS,kBAAkB,EAAE,WAAW,gBAAgB,UAAU,GAA6B;AACpG,8BAAU,MAAM;AACd,aAAS,OAAO,IAAkB;AAChC,UAAI,GAAG,WAAW,eAAgB;AAClC,UAAI,UAAU,WAAW,GAAG,WAAW,UAAU,QAAQ,cAAe;AACxE,YAAM,OAAO,GAAG;AAChB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,UAAI,KAAK,WAAW,cAAc,KAAK,YAAY,EAAG;AACtD,UAAI,OAAO,KAAK,SAAS,YAAY,CAAC,KAAK,KAAK,WAAW,WAAW,EAAG;AACzE,gBAAU,IAAuB;AAAA,IACnC;AACA,WAAO,iBAAiB,WAAW,MAAM;AACzC,WAAO,MAAM,OAAO,oBAAoB,WAAW,MAAM;AAAA,EAC3D,GAAG,CAAC,WAAW,gBAAgB,SAAS,CAAC;AAC3C;;;AC1CA,IAAAC,gBAAoD;AAmC7C,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA6B;AAC3B,QAAM,CAAC,UAAU,WAAW,QAAI,wBAA8B,MAAS;AAIvE,+BAAU,MAAM;AACd,QAAI,mBAAmB,OAAW;AAClC,UAAM,MAAM,UAAU,SAAS;AAC/B,QAAI,CAAC,IAAK;AAGV,QAAI,aAAa,kBAAkB,YAAa;AAChD,QAAI,CAAC,YAAa;AAElB,UAAM,UAAiC;AAAA,MACrC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AACA,QAAI,YAAY,SAAS,YAAY;AACrC,gBAAY,cAAc;AAAA,EAC5B,GAAG,CAAC,WAAW,cAAc,gBAAgB,aAAa,QAAQ,CAAC;AACrE;;;AFmCI;AAzFJ,IAAM,mBAAmB;AAYlB,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAiD;AAC/C,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AAEpD,QAAM,eAAW;AAAA,IACf,CAAC,QAAyB;AACxB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,yBAAe,IAAI;AACnB,oBAAU;AACV;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,wBAAc;AAAA,YACZ,SAAS,IAAI;AAAA,YACb,WAAW,IAAI;AAAA,YACf,GAAI,IAAI,mBAAmB,SAAY,EAAE,gBAAgB,IAAI,eAAe,IAAI,CAAC;AAAA,YACjF,GAAI,IAAI,oBAAoB,SAAY,EAAE,iBAAiB,IAAI,gBAAgB,IAAI,CAAC;AAAA;AAAA;AAAA,YAGpF,GAAI,IAAI,UAAU,SAAY,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,YACtD,GAAI,IAAI,aAAa,SAAY,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,YAC/D,GAAI,IAAI,cAAc,SAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,UACpE,CAAC;AACD;AAAA,QACF,KAAK;AACH,qBAAW,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,QAAQ,CAAC;AACnD;AAAA,QACF,KAAK;AACH,uBAAa;AACb;AAAA,QACF,KAAK;AACH,0BAAgB,EAAE,MAAM,IAAI,KAAK,CAAC;AAClC;AAAA,QACF,KAAK;AACH,cAAI,UAAU,QAAS,WAAU,QAAQ,MAAM,SAAS,GAAG,IAAI,MAAM;AACrE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,SAAS,cAAc,aAAa,UAAU,YAAY,aAAa;AAAA,EAC1E;AAEA,oBAAkB,EAAE,WAAW,gBAAgB,WAAW,SAAS,CAAC;AACpE,oBAAkB;AAAA,IAChB;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAS,IAAI,gBAAgB,EAAE,OAAO,KAAK,MAAM,iBAAiB,aAAa,CAAC;AACtF,MAAI,QAAS,QAAO,IAAI,WAAW,OAAO;AAC1C,MAAI,KAAM,QAAO,IAAI,QAAQ,IAAI;AACjC,QAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAM,MAAM,GAAG,SAAS,IAAI,KAAK,IAAI,mBAAmB,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAEvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,WAAW,eAAe,GAAG,MAAM;AAAA,MACtE,OAAM;AAAA,MACN,OAAM;AAAA;AAAA,EACR;AAEJ;;;AG3GA,IAAAC,gBAA8C;AA0F1C,IAAAC,sBAAA;AArFJ,IAAMC,oBAAmB;AAYlB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,UAAUA;AAAA,EACV,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,gBAAY,sBAA0B,IAAI;AAChD,QAAM,iBAAiB,IAAI,IAAI,OAAO,EAAE;AACxC,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,KAAK;AAEpD,QAAM,eAAW;AAAA,IACf,CAAC,QAAyB;AACxB,cAAQ,IAAI,MAAM;AAAA,QAChB,KAAK;AACH,yBAAe,IAAI;AACnB,oBAAU;AACV;AAAA,QACF,KAAK;AACH,yBAAe;AACf;AAAA,QACF,KAAK;AACH,wBAAc;AAAA,YACZ,SAAS,IAAI;AAAA,YACb,WAAW,IAAI;AAAA,YACf,GAAI,IAAI,mBAAmB,SAAY,EAAE,gBAAgB,IAAI,eAAe,IAAI,CAAC;AAAA,YACjF,GAAI,IAAI,oBAAoB,SAAY,EAAE,iBAAiB,IAAI,gBAAgB,IAAI,CAAC;AAAA;AAAA;AAAA,YAGpF,GAAI,IAAI,UAAU,SAAY,EAAE,OAAO,IAAI,MAAM,IAAI,CAAC;AAAA,YACtD,GAAI,IAAI,aAAa,SAAY,EAAE,UAAU,IAAI,SAAS,IAAI,CAAC;AAAA,YAC/D,GAAI,IAAI,cAAc,SAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,UACpE,CAAC;AACD;AAAA,QACF,KAAK;AACH,qBAAW,EAAE,MAAM,IAAI,MAAM,SAAS,IAAI,QAAQ,CAAC;AACnD;AAAA,QACF,KAAK;AACH,uBAAa;AACb;AAAA,QACF,KAAK;AACH,cAAI,UAAU,QAAS,WAAU,QAAQ,MAAM,SAAS,GAAG,IAAI,MAAM;AACrE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,CAAC,SAAS,cAAc,aAAa,UAAU,UAAU;AAAA,EAC3D;AAEA,oBAAkB,EAAE,WAAW,gBAAgB,WAAW,SAAS,CAAC;AACpE,oBAAkB;AAAA,IAChB;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,SAAS,IAAI,gBAAgB,EAAE,OAAO,KAAK,MAAM,gBAAgB,aAAa,CAAC;AACrF,MAAI,QAAS,QAAO,IAAI,WAAW,OAAO;AAC1C,MAAI,KAAM,QAAO,IAAI,QAAQ,IAAI;AACjC,QAAM,YAAY,QAAQ,QAAQ,OAAO,EAAE;AAC3C,QAAM,MAAM,GAAG,SAAS,IAAI,KAAK,IAAI,mBAAmB,SAAS,CAAC,IAAI,OAAO,SAAS,CAAC;AAEvF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,KAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,OAAO,EAAE,QAAQ,GAAG,OAAO,QAAQ,WAAW,eAAe,GAAG,MAAM;AAAA,MACtE,OAAM;AAAA,MACN,OAAM;AAAA;AAAA,EACR;AAEJ;;;ACnGA,IAAAC,gBAA0D;AAC1D,kBASO;AAEP,IAAM,sBAAsB;AA0C5B,SAAS,WAAW,KAA+C;AACjE,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa,QAAO;AAClD,MAAI;AACF,WAAO,OAAO,aAAa,QAAQ,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,YAAY,KAAgC,OAA4B;AAC/E,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,QAAI,UAAU,KAAM,QAAO,aAAa,WAAW,GAAG;AAAA,QACjD,QAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAiBO,SAAS,eAAe,SAAsD;AACnF,QAAM,EAAE,eAAe,eAAe,YAAY,QAAQ,IAAI;AAC9D,QAAM,aAAa,QAAQ,eAAe,SAAY,sBAAsB,QAAQ;AAEpF,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAiC,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAIrD,QAAM,EAAE,QAAQ,UAAU,QAAI,uBAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,8BAAkB,EAAE,eAAe,eAAe,YAAY,QAAQ,CAAC;AAAA,QACnF,WAAW;AAAA,MACb;AAAA,IACF,SAAS,GAAG;AACV,aAAO,EAAE,QAAQ,MAAkC,WAAW,EAAW;AAAA,IAC3E;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,YAAY,OAAO,CAAC;AAEtD,+BAAU,MAAM;AACd,QAAI,UAAW,UAAS,SAAS;AAAA,EACnC,GAAG,CAAC,SAAS,CAAC;AAGd,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQ,WAAW,UAAU;AACnC,QAAI,CAAC,MAAO;AACZ,QAAI,YAAY;AAChB,WAAO,OAAO,KAAK;AACnB,qBAAiB,KAAK;AACtB,eAAW,IAAI;AACf,WACG,IAAI,EACJ,KAAK,CAAC,MAAM;AACX,UAAI,UAAW;AACf,cAAQ,EAAE,IAAI;AAAA,IAChB,CAAC,EACA,MAAM,MAAM;AAEX,UAAI,UAAW;AACf,kBAAY,YAAY,IAAI;AAC5B,aAAO,OAAO,EAAE;AAChB,uBAAiB,IAAI;AACrB,cAAQ,IAAI;AAAA,IACd,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,UAAW,YAAW,KAAK;AAAA,IAClC,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,QAAM,oBAAgB;AAAA,IACpB,OAAO,UAAkD;AACvD,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAI,OAAO,cAAe;AAC1B,YAAM,UAAU,MAAM,OAAO,OAAO,SAAS,CAAC,CAAC;AAC/C,kBAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAGA,QAAM,UAAM;AAAA,IACV,OAAO,IAAoC,OAA6B,CAAC,MAAqB;AAC5F,UAAI,CAAC,QAAQ;AACX,iBAAS,aAAa,IAAI,MAAM,+BAA+B,CAAC;AAChE;AAAA,MACF;AACA,iBAAW,IAAI;AACf,eAAS,IAAI;AACb,UAAI;AACF,YAAI,KAAK,OAAQ,OAAM,cAAc;AACrC,cAAM,OAAO,MAAM,GAAG;AACtB,gBAAQ,IAAI;AAAA,MACd,SAAS,GAAG;AACV,iBAAS,CAAU;AACnB,cAAM;AAAA,MACR,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,SAAS;AAAA,EACnC;AAEA,QAAM,cAAU;AAAA,IACd,CAAC,UAA4B,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IAC/E,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,CAAC,QAAgB,UAA+B,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,CAAC;AAAA,IAC3F,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa,2BAAY,CAAC,WAAmB,IAAI,MAAM,OAAQ,WAAW,MAAM,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACvG,QAAM,qBAAiB;AAAA,IACrB,CAAC,UAA+B,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzF,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,oBAAgB,2BAAY,MAAM,IAAI,MAAM,OAAQ,cAAc,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AACzF,QAAM,oBAAgB;AAAA,IACpB,CAAC,SAAiB,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC;AAAA,IACzE,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,qBAAiB,2BAAY,MAAM,IAAI,MAAM,OAAQ,eAAe,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC;AAC3F,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAAA,EACjD,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,eAAW;AAAA,IACf,OAAO,UAA0D;AAC/D,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,iBAAW,IAAI;AACf,eAAS,IAAI;AACb,UAAI;AACF,cAAM,cAAc;AACpB,eAAO,MAAM,OAAO,SAAS,KAAK;AAAA,MACpC,SAAS,GAAG;AACV,iBAAS,CAAU;AACnB,cAAM;AAAA,MACR,UAAE;AACA,mBAAW,KAAK;AAAA,MAClB;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,SAAS;AAAA,EACnC;AAEA,QAAM,YAAQ,2BAAY,MAAM;AAC9B,gBAAY,YAAY,IAAI;AAC5B,YAAQ,OAAO,EAAE;AACjB,qBAAiB,IAAI;AACrB,YAAQ,IAAI;AACZ,aAAS,IAAI;AAAA,EACf,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACtPA,IAAAC,gBAAkE;AAClE,IAAAC,eAYO;AAEP,IAAMC,uBAAsB;AA6E5B,SAASC,YAAW,KAA+C;AACjE,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa,QAAO;AAClD,MAAI;AACF,WAAO,OAAO,aAAa,QAAQ,GAAG;AAAA,EACxC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAASC,aAAY,KAAgC,OAA4B;AAC/E,MAAI,CAAC,OAAO,OAAO,WAAW,YAAa;AAC3C,MAAI;AACF,QAAI,UAAU,KAAM,QAAO,aAAa,WAAW,GAAG;AAAA,QACjD,QAAO,aAAa,QAAQ,KAAK,KAAK;AAAA,EAC7C,QAAQ;AAAA,EAER;AACF;AAEA,SAAS,SAAS,MAAqD;AACrE,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,UAAU,KAAK;AAAA,IACf,eAAe,KAAK;AAAA,IACpB,eAAe,KAAK;AAAA,IACpB,UAAU,KAAK;AAAA,IACf,OAAO,KAAK;AAAA,EACd;AACF;AA0BO,SAAS,oBACd,SAC2B;AAC3B,QAAM,EAAE,eAAe,eAAe,YAAY,QAAQ,IAAI;AAC9D,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,aAAa,QAAQ,eAAe,SAAYF,uBAAsB,QAAQ;AAEpF,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAiC,IAAI;AAC7D,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAAiC,MAAM;AACnE,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAIrD,QAAM,cAAU,sBAA+B,IAAI;AACnD,UAAQ,UAAU;AAElB,QAAM,EAAE,QAAQ,UAAU,QAAI,uBAAQ,MAAM;AAC1C,QAAI;AACF,aAAO;AAAA,QACL,QAAQ,IAAI,+BAAkB,EAAE,eAAe,eAAe,YAAY,QAAQ,CAAC;AAAA,QACnF,WAAW;AAAA,MACb;AAAA,IACF,SAAS,GAAG;AACV,aAAO,EAAE,QAAQ,MAAkC,WAAW,EAAW;AAAA,IAC3E;AAAA,EACF,GAAG,CAAC,eAAe,eAAe,YAAY,OAAO,CAAC;AAEtD,+BAAU,MAAM;AACd,QAAI,WAAW;AACb,eAAS,SAAS;AAClB,gBAAU,OAAO;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,SAAS,CAAC;AAGd,+BAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AACb,UAAM,QAAQC,YAAW,UAAU;AACnC,QAAI,CAAC,MAAO;AACZ,QAAI,YAAY;AAChB,WAAO,OAAO,KAAK;AACnB,qBAAiB,KAAK;AACtB,cAAU,SAAS;AACnB,WACG,IAAI,EACJ,KAAK,CAAC,MAAM;AACX,UAAI,UAAW;AACf,cAAQ,EAAE,IAAI;AACd,gBAAU,OAAO;AAAA,IACnB,CAAC,EACA,MAAM,MAAM;AACX,UAAI,UAAW;AACf,MAAAC,aAAY,YAAY,IAAI;AAC5B,aAAO,OAAO,EAAE;AAChB,uBAAiB,IAAI;AACrB,cAAQ,IAAI;AACZ,gBAAU,MAAM;AAAA,IAClB,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,QAAM,oBAAgB;AAAA,IACpB,OAAO,UAAkD;AACvD,UAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAI,OAAO,cAAe;AAC1B,YAAM,UAAU,MAAM,OAAO,OAAO,SAAS,CAAC,CAAC;AAC/C,MAAAA,aAAY,YAAY,QAAQ,aAAa;AAC7C,uBAAiB,QAAQ,aAAa;AACtC,cAAQ,QAAQ,IAAI;AAAA,IACtB;AAAA,IACA,CAAC,QAAQ,WAAW,UAAU;AAAA,EAChC;AAOA,QAAM,cAAU,2BAAY,YAAsC;AAChE,QAAI,CAAC,OAAQ,OAAM,aAAa,IAAI,MAAM,+BAA+B;AACzE,UAAM,OAAO,QAAQ;AAErB,WAAO,OAAO,EAAE;AAChB,qBAAiB,IAAI;AACrB,UAAM,UAAU,MAAM,OAAO,OAAO;AAAA,MAClC,UAAU,MAAM;AAAA,MAChB,MAAM,MAAM,QAAQ;AAAA,IACtB,CAAC;AACD,IAAAA,aAAY,YAAY,QAAQ,aAAa;AAC7C,qBAAiB,QAAQ,aAAa;AACtC,QAAI,OAAO,QAAQ;AACnB,eAAW,QAAQ,MAAM,aAAa,CAAC,GAAG;AACxC,aAAO,MAAM,OAAO,QAAQ;AAAA,QAC1B,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,MAAM,KAAK;AAAA,QACX,aAAa,KAAK,eAAe;AAAA,QACjC,WAAW,KAAK;AAAA,QAChB,UAAU,KAAK;AAAA,QACf,UAAU,KAAK,YAAY;AAAA,QAC3B,UAAU,KAAK;AAAA,MACjB,CAAC;AAAA,IACH;AACA,UAAM,OAAO,MAAM;AACnB,QAAI,MAAM;AACR,aAAO,MAAM,OAAO,eAAe;AAAA,QACjC,UAAU,KAAK;AAAA,QACf,aAAa,KAAK;AAAA,QAClB,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK;AAAA,QACf,SAAS,KAAK,WAAW;AAAA,QACzB,aAAa,KAAK,eAAe;AAAA,QACjC,uBAAuB,KAAK,yBAAyB;AAAA,MACvD,CAAC;AAAA,IACH;AACA,YAAQ,IAAI;AACZ,WAAO;AAAA,EACT,GAAG,CAAC,QAAQ,WAAW,UAAU,CAAC;AAQlC,QAAM,UAAM;AAAA,IACV,OACE,IACA,OAA0D,CAAC,MAC5C;AACf,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,aAAa,IAAI,MAAM,+BAA+B;AAChE,iBAAS,CAAC;AACV,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AACA,gBAAU,SAAS;AACnB,eAAS,IAAI;AACb,UAAI;AACF,YAAI,KAAK,OAAQ,OAAM,cAAc;AACrC,cAAM,SAAS,MAAM,GAAG;AACxB,kBAAU,OAAO;AACjB,eAAO;AAAA,MACT,SAAS,GAAG;AACV,cAAM,QAAQ,aAAa,iCAAoB,aAAa;AAC5D,YAAI,SAAS,aAAa;AACxB,cAAI;AACF,sBAAU,YAAY;AACtB,kBAAM,QAAQ;AACd,gBAAI,KAAK,mBAAmB;AAC1B,oBAAM,UAAU,MAAM,GAAG;AACzB,wBAAU,OAAO;AACjB,qBAAO;AAAA,YACT;AAGA,sBAAU,OAAO;AACjB,kBAAM,SAAS,IAAI,8BAAiB;AAAA,cAClC,SAAS;AAAA,YACX,CAAC;AACD,qBAAS,MAAM;AACf,kBAAM;AAAA,UACR,SAAS,YAAY;AACnB,qBAAS,UAAmB;AAC5B,sBAAU,OAAO;AACjB,kBAAM;AAAA,UACR;AAAA,QACF;AACA,iBAAS,CAAU;AACnB,kBAAU,OAAO;AACjB,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,eAAe,WAAW,aAAa,OAAO;AAAA,EACzD;AAEA,QAAM,cAAU;AAAA,IACd,OAAO,UAA4B;AACjC,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,QAAQ,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAC9F,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,OAAO,QAAgB,UAA+B;AACpD,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,WAAW,QAAQ,KAAK,GAAG,EAAE,mBAAmB,MAAM,CAAC;AAC5F,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,iBAAa;AAAA,IACjB,OAAO,WAAmB;AACxB,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,WAAW,MAAM,GAAG,EAAE,mBAAmB,MAAM,CAAC;AACrF,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,mBAAe;AAAA,IACnB,OAAO,UAA+B;AACpC,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,eAAe,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AACrG,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,oBAAgB,2BAAY,YAAY;AAC5C,UAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,cAAc,GAAG,EAAE,mBAAmB,KAAK,CAAC;AACjF,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAChB,QAAM,oBAAgB;AAAA,IACpB,OAAO,SAAiB;AACtB,YAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,cAAc,IAAI,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AACnG,cAAQ,IAAI;AAAA,IACd;AAAA,IACA,CAAC,QAAQ,GAAG;AAAA,EACd;AACA,QAAM,qBAAiB,2BAAY,YAAY;AAC7C,UAAM,OAAO,MAAM,IAAI,MAAM,OAAQ,eAAe,GAAG,EAAE,mBAAmB,KAAK,CAAC;AAClF,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAQ,cAAe;AAC5B,UAAM,OAAO,MAAM,IAAI,aAAa,MAAM,OAAO,IAAI,GAAG,IAAI;AAC5D,YAAQ,IAAI;AAAA,EACd,GAAG,CAAC,QAAQ,GAAG,CAAC;AAEhB,QAAM,oBAAgB;AAAA,IACpB,CAAC,UACC,IAAI,MAAM,OAAQ,SAAS,KAAK,GAAG,EAAE,QAAQ,MAAM,mBAAmB,KAAK,CAAC;AAAA,IAC9E,CAAC,QAAQ,GAAG;AAAA,EACd;AAEA,QAAM,YAAQ,2BAAY,MAAM;AAC9B,IAAAA,aAAY,YAAY,IAAI;AAC5B,YAAQ,OAAO,EAAE;AACjB,qBAAiB,IAAI;AACrB,YAAQ,IAAI;AACZ,aAAS,IAAI;AACb,cAAU,MAAM;AAAA,EAClB,GAAG,CAAC,QAAQ,UAAU,CAAC;AAEvB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB,MAAM,oBAAoB;AAAA,IAC1C,QAAQ,SAAS,IAAI;AAAA,IACrB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACzZA,IAAAC,gBAQO;AACP,IAAAC,wBAA6B;;;ACT7B,2BAA6B;AAkEtB,SAAS,iBAAiB,EAAE,UAAU,SAAS,oBAAoB,GAAwC;AAChH,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,UAAM,eAAe,MAAM;AACzB,eAAS,sBAAsB,kCAAa,OAAO,iBAAiB,YAAY;AAChF,cAAQ;AAAA,IACV;AAGA,aAAS,iBAAiB,kCAAa,OAAO,iBAAiB,YAAY;AAC3E,aAAS,gBAAgB,SAAS,sBAAsB,EAAE,oBAAoB,IAAI,MAAS;AAAA,EAC7F,CAAC;AACH;;;ADidS,IAAAC,sBAAA;AA1ZT,IAAM,wBAAoB,6BAA6C,IAAI;AAG3E,IAAM,8BAA8B;AA+DpC,IAAM,+BAA+B,IAAI,KAAK;AAEvC,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,qBAAqB;AAAA,EACrB;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,CAAC,QAAQ,SAAS,QAAI,wBAA2B,MAAM;AAC7D,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAiC,IAAI;AAC/D,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAS,CAAC;AAClD,QAAM,kBAAc,sBAA6E,IAAI;AACrG,QAAM,qBAAiB,sBAAoC,IAAI;AAC/D,QAAM,qBAAiB,sBAA+E,MAAS;AAC/G,QAAM,sBAAkB,sBAA6E,IAAI;AAEzG,QAAM,EAAE,aAAa,UAAU,IAAI;AAInC,QAAM,qBAAqB,QAAQ,cAAc,QAAQ,WAAW;AACpE,QAAM,CAAC,UAAU,WAAW,QAAI,wBAAsC,IAAI;AAE1E,QAAM,CAAC,cAAc,eAAe,QAAI,wBAAsC,IAAI;AAuBlF,+BAAU,MAAM;AACd,UAAM,UAAU,IAAI,mCAAa,EAAE,SAAS,oBAAoB,aAAa,UAAU,CAAC;AACxF,gBAAY,OAAO;AAKnB,oBAAgB,CAAC;AACjB,cAAU,CAAC,YAAa,YAAY,UAAU,YAAY,UAAU,aAAa,OAAQ;AACzF,WAAO,MAAM;AAIX,cAAQ,YAAY,OAAO;AAC3B,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,oBAAoB,aAAa,SAAS,CAAC;AAE/C,+BAAU,MAAM;AACd,QAAI,CAAC,SAAU;AAEf,UAAM,gBAAgB,MAAM;AAC1B,kBAAY,SAAS,QAAQ;AAAA,IAC/B;AACA,UAAM,gBAAgB,CAAC,SAA+C;AACpE,kBAAY,SAAS,OAAO;AAAA,QAC1B,MAAM,MAAM,QAAQ;AAAA,QACpB,SAAS,MAAM,WAAW;AAAA,MAC5B,CAAC;AAAA,IACH;AAMA,UAAM,qBAAqB,MAAM,UAAU,aAAa;AACxD,UAAM,sBAAsB,MAAM;AAUhC,UAAI,CAAC,gBAAgB,QAAS;AAI9B,gBAAU,CAAC,YAAa,YAAY,gBAAgB,aAAa,OAAQ;AAAA,IAC3E;AAEA,aAAS,iBAAiB,mCAAa,OAAO,oBAAoB,aAAa;AAC/E,aAAS,iBAAiB,mCAAa,OAAO,oBAAoB,aAAa;AAC/E,aAAS,iBAAiB,mCAAa,OAAO,gBAAgB,kBAAkB;AAChF,aAAS,iBAAiB,mCAAa,OAAO,iBAAiB,mBAAmB;AAElF,WAAO,MAAM;AACX,eAAS,sBAAsB,mCAAa,OAAO,oBAAoB,aAAa;AACpF,eAAS,sBAAsB,mCAAa,OAAO,oBAAoB,aAAa;AACpF,eAAS,sBAAsB,mCAAa,OAAO,gBAAgB,kBAAkB;AACrF,eAAS,sBAAsB,mCAAa,OAAO,iBAAiB,mBAAmB;AAAA,IACzF;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,oBAAgB;AAAA,IACpB,CAAC,MAA2B,SAAsB,UAAmC,CAAC,MAAM;AAI1F,UAAI,CAAC,SAAU;AACf,UAAI,SAAS,SAAU,UAAS,mBAAmB,SAAS,OAAO;AAAA,eAC1D,SAAS,SAAU,UAAS,mBAAmB,SAAS,OAAO;AAAA,eAC/D,SAAS,MAAO,UAAS,qBAAqB,SAAS,OAAO;AAAA,UAClE,UAAS,SAAS,SAAS,EAAE,MAAM,cAAc,GAAG,QAAQ,CAAQ;AACzE,sBAAgB,CAAC,MAAM,IAAI,CAAC;AAAA,IAC9B;AAAA,IACA,CAAC,QAAQ;AAAA,EACX;AAEA,+BAAU,MAAM;AACd,QAAI,gBAAgB,4BAA6B,WAAU,OAAO;AAAA,EACpE,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,2BAAuB;AAAA,IAC3B,CAAC,SAA+B,YAAyE;AACvG,qBAAe,UAAU;AACzB,qBAAe,UAAU;AAGzB,sBAAgB,OAAO;AAAA,IACzB;AAAA,IACA,CAAC;AAAA,EACH;AAgBA,+BAAU,MAAM;AACd,QAAI,CAAC,YAAY,CAAC,aAAc;AAChC,QAAI;AACF,eAAS;AAAA,QACP;AAAA,QACA,eAAe,SAAS,sBACpB,EAAE,qBAAqB,eAAe,QAAQ,oBAAoB,IAClE;AAAA,MACN;AAAA,IACF,SAAS,KAAK;AAGZ,eAAS;AAAA,QACP,MAAM;AAAA,QACN,SAAU,KAAe,WAAW;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,UAAU,YAAY,CAAC;AAE3B,QAAM,sBAAkB,2BAAY,MAAM;AACxC,oBAAgB,SAAS,OAAO;AAAA,MAC9B,MAAM;AAAA,MACN,SAAS;AAAA,IACX,CAAC;AAAA,EACH,GAAG,CAAC,CAAC;AAEL,QAAM,YAAQ,2BAAY,MAAM;AAI9B,QAAI,CAAC,UAAU;AACb,aAAO,QAAQ,OAAa;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAoB;AAAA,IACtB;AAMA,QAAI,YAAY,SAAS;AACvB,aAAO,QAAQ,OAAa;AAAA,QAC1B,MAAM;AAAA,QACN,SAAS;AAAA,MACX,CAAoB;AAAA,IACtB;AAEA,aAAS,IAAI;AACb,cAAU,UAAU;AACpB,WAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,kBAAY,UAAU;AAAA,QACpB,SAAS,MAAM;AACb,sBAAY,UAAU;AACtB,kBAAQ;AAAA,QACV;AAAA,QACA,QAAQ,CAAC,MAAM;AACb,sBAAY,UAAU;AACtB,mBAAS,CAAC;AACV,oBAAU,OAAO;AACjB,iBAAO,CAAC;AAAA,QACV;AAAA,MACF;AACA,eAAS,OAAO;AAAA,IAClB,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,eAAW,2BAAY,YAAY;AACvC,cAAU,aAAa;AACvB,UAAM,aAAa,eAAe,CAAC,SAAwD,gBAAgB,EAAE,GAAG,MAAM,WAAW,CAAC;AAClI,UAAM,qBAAqB,sBAAsB;AACjD,QAAI;AACF,UAAI,MAAM,MAAM,WAAW,EAAE,WAAW,mBAAmB,gBAAgB,QAAQ,UAAU,CAAC;AAE9F,UAAI,KAAK,WAAW,mBAAmB;AACrC,YAAI,CAAC,eAAe,SAAS;AAC3B,gBAAM,OAAO,OAAO,IAAI,MAAM,qEAAqE,GAAG;AAAA,YACpG,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AACA,YAAI,CAAC,UAAU;AACb,gBAAM,OAAO,OAAO,IAAI,MAAM,oCAAoC,GAAG,EAAE,MAAM,YAAY,CAAC;AAAA,QAC5F;AACA,kBAAU,aAAa;AAQvB,cAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,gBAAM,YAAY,WAAW,MAAM;AACjC,4BAAgB,SAAS,OAAO;AAAA,cAC9B,MAAM;AAAA,cACN,SAAS;AAAA,YACX,CAAC;AAAA,UACH,GAAG,kBAAkB;AAErB,0BAAgB,UAAU;AAAA,YACxB,SAAS,MAAM;AACb,2BAAa,SAAS;AACtB,8BAAgB,UAAU;AAC1B,sBAAQ;AAAA,YACV;AAAA,YACA,QAAQ,CAAC,MAAM;AACb,2BAAa,SAAS;AACtB,8BAAgB,UAAU;AAC1B,qBAAO,OAAO,OAAO,IAAI,MAAM,EAAE,OAAO,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC;AAAA,YAC9D;AAAA,UACF;AACA,6BAAmB;AAAA,YACjB;AAAA,YACA,SAAS,eAAe;AAAA,YACxB,GAAI,eAAe,WAAW,CAAC;AAAA,UACjC,CAAC,EAAE;AAAA,YACD,MAAM,gBAAgB,SAAS,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAQvC,CAAC,QACC,gBAAgB,SAAS,OAAO;AAAA,cAC9B,MAAO,KAA2B,QAAQ;AAAA,cAC1C,SAAU,KAAe,WAAW;AAAA,YACtC,CAAC;AAAA,UACL;AAAA,QACF,CAAC;AAED,kBAAU,aAAa;AACvB,cAAM,MAAM,WAAW,EAAE,WAAW,mBAAmB,gBAAgB,QAAQ,UAAU,CAAC;AAC1F,YAAI,KAAK,WAAW,mBAAmB;AACrC,gBAAM,OAAO,OAAO,IAAI,MAAM,2EAA2E,GAAG;AAAA,YAC1G,MAAM;AAAA,UACR,CAAC;AAAA,QACH;AAAA,MACF;AAEA,gBAAU,UAAU;AACpB,kBAAY,GAA+B;AAC3C,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,eAAS;AAAA,QACP,MAAO,KAA2B,QAAQ;AAAA,QAC1C,SAAU,KAAe,WAAW;AAAA,MACtC,CAAC;AACD,gBAAU,OAAO;AACjB,YAAM;AAAA,IACR;AAAA,EACF,GAAG,CAAC,YAAY,oBAAoB,oBAAoB,YAAY,UAAU,mBAAmB,QAAQ,WAAW,SAAS,CAAC;AAE9H,QAAM,UAAM,2BAAY,YAAY;AAClC,UAAM,MAAM;AACZ,WAAO,SAAS;AAAA,EAClB,GAAG,CAAC,OAAO,QAAQ,CAAC;AAEpB,QAAM,QAAgC;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,OACE,gBAAgB,+BAChB,WAAW,cACX,WAAW,iBACX,WAAW,iBACX,WAAW;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,6CAAC,kBAAkB,UAAlB,EAA2B,OAAe,UAAS;AAC7D;AAoBA,eAAe,gBAAgB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,MAAM,MAAM,MAAM,WAAW,YAAY,6BAA6B,SAAS,WAAW,GAAG;AAAA,IACjG,QAAQ;AAAA,IACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,IAC9C,MAAM,KAAK,UAAU,EAAE,eAAe,YAAY,4BAA4B,eAAe,CAAC;AAAA,EAChG,CAAC;AACD,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,OAAO,OAAO,IAAI,MAAM,MAAM,OAAO,WAAW,iCAAiC,GAAG;AAAA,MACxF,MAAM,MAAM,OAAO,QAAQ;AAAA,IAC7B,CAAC;AAAA,EACH;AACA,SAAO,MAAM,QAAQ;AACvB;AAGA,SAAS,WAAW,MAAc,MAAsB;AACtD,SAAO,GAAG,KAAK,QAAQ,QAAQ,EAAE,CAAC,GAAG,IAAI;AAC3C;AAEO,SAAS,gBAAwC;AACtD,QAAM,UAAM,0BAAW,iBAAiB;AACxC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,wDAAwD;AAClF,SAAO;AACT;;;AEnlBA,IAAAC,gBAAkE;AAsEzD,IAAAC,sBAAA;AA9BT,SAAS,SAAS,MAA2B,SAAmC;AAC9E,QAAM,UAAM,0BAAW,iBAAiB;AACxC,QAAM,cAAU,sBAA8B,IAAI;AAClD,QAAM,WAAW,KAAK,YAAY;AAElC,+BAAU,MAAM;AACd,UAAM,OAAO,QAAQ;AAIrB,QAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAU;AAEhC,UAAM,SAAS,SAAS,cAAc,KAAK;AAC3C,WAAO,MAAM,SAAS;AACtB,SAAK,YAAY,MAAM;AACvB,QAAI,cAAc,MAAM,QAAQ,WAAW,CAAC,CAAC;AAE7C,WAAO,MAAM;AAIX,aAAO,KAAK,WAAY,MAAK,YAAY,KAAK,UAAU;AAAA,IAC1D;AAAA,EAEF,GAAG,CAAC,QAAQ,CAAC;AAEb,SAAO;AACT;AAEO,SAAS,gBAAgB,EAAE,WAAW,OAAO,QAAQ,GAAyB;AACnF,SAAO,6CAAC,SAAI,KAAK,SAAS,UAAU,OAAO,GAAG,WAAsB,OAAc;AACpF;AAEO,SAAS,YAAY,EAAE,WAAW,OAAO,QAAQ,GAAyB;AAC/E,SAAO,6CAAC,SAAI,KAAK,SAAS,UAAU,OAAO,GAAG,WAAsB,OAAc;AACpF;AAEO,SAAS,kBAAkB,EAAE,WAAW,OAAO,QAAQ,GAAyB;AACrF,SAAO,6CAAC,SAAI,KAAK,SAAS,OAAO,OAAO,GAAG,WAAsB,OAAc;AACjF;AAEO,SAAS,gBAAgB,EAAE,WAAW,OAAO,QAAQ,GAAyB;AACnF,SAAO,6CAAC,SAAI,KAAK,SAAS,cAAc,OAAO,GAAG,WAAsB,OAAc;AACxF;","names":["import_react","import_react","import_react","import_jsx_runtime","DEFAULT_BASE_URL","import_react","import_react","import_cart","DEFAULT_STORAGE_KEY","readStored","writeStored","import_react","import_secure_fields","import_jsx_runtime","import_react","import_jsx_runtime"]}
package/dist/index.d.cts CHANGED
@@ -38,6 +38,8 @@ type ThrottleEvent = {
38
38
  paymentId: string;
39
39
  paymentStatus?: string;
40
40
  subscriptionId?: string;
41
+ /** v1.5 additive — every subscription the checkout created, in line order. `subscriptionId` is set only when there is exactly one. */
42
+ subscriptionIds?: string[];
41
43
  /** v1.4 additive — order total in minor units, for analytics purchase events. */
42
44
  total?: number;
43
45
  currency?: string;
@@ -77,14 +79,19 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
77
79
  * requiring a runtime read of the session.
78
80
  *
79
81
  * - `create: 'auto'` (default) — server creates the subscription on
80
- * vault success. `onSucceeded` payload includes `subscriptionId`.
82
+ * vault success. `onSucceeded` payload includes `subscriptionIds` (and `subscriptionId` when exactly one was created).
81
83
  * - `create: 'manual'` — server only vaults; merchant calls
82
84
  * `subscriptions.create` from their backend after `payment.vaulted`
83
85
  * webhook or `onSucceeded` callback.
84
86
  */
85
87
  interface RecurringIntent {
86
88
  plan: string;
87
- interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
89
+ /** A named interval. Or send intervalUnit + intervalCount for any cadence from 1 hour to 1 year. */
90
+ interval?: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly' | 'custom';
91
+ intervalUnit?: 'hour' | 'day' | 'week' | 'month';
92
+ intervalCount?: number;
93
+ /** Makes the subscription an installment plan of exactly this many payments (2–60). Not combinable with trialDays. */
94
+ totalPayments?: number;
88
95
  amount?: number;
89
96
  trialDays?: number;
90
97
  planName?: string;
@@ -142,6 +149,8 @@ interface ThrottleEmbedProps {
142
149
  orderId: string;
143
150
  paymentId: string;
144
151
  subscriptionId?: string;
152
+ /** Every subscription the checkout created, in line order. `subscriptionId` is set only when there is exactly one. */
153
+ subscriptionIds?: string[];
145
154
  /** Order total in minor units — fire `gtag`/`fbq` purchase events with value = total / 100. */
146
155
  total?: number;
147
156
  currency?: string;
@@ -408,7 +417,17 @@ type SecureCardStatus = 'idle' | 'mounting' | 'ready' | 'vaulting' | 'authorizin
408
417
  /** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */
409
418
  interface SecureCardSession {
410
419
  sessionId: string;
411
- gr4vyId: string;
420
+ /**
421
+ * The payment provider instance this session belongs to, straight from
422
+ * `POST /checkout-sessions/:id/card-session`.
423
+ *
424
+ * Either name works. `providerId` is the documented one; `gr4vyId` is the
425
+ * original and is still accepted, because the package shipped with it and
426
+ * dropping it would break consumers already passing the response through.
427
+ */
428
+ providerId?: string;
429
+ /** @deprecated Use `providerId`. Still honoured. */
430
+ gr4vyId?: string;
412
431
  environment: 'sandbox' | 'production';
413
432
  }
414
433
  interface SecureCardError {
package/dist/index.d.ts CHANGED
@@ -38,6 +38,8 @@ type ThrottleEvent = {
38
38
  paymentId: string;
39
39
  paymentStatus?: string;
40
40
  subscriptionId?: string;
41
+ /** v1.5 additive — every subscription the checkout created, in line order. `subscriptionId` is set only when there is exactly one. */
42
+ subscriptionIds?: string[];
41
43
  /** v1.4 additive — order total in minor units, for analytics purchase events. */
42
44
  total?: number;
43
45
  currency?: string;
@@ -77,14 +79,19 @@ type ThrottleParentMessage = ThrottleParentCommand & ThrottleParentEnvelope;
77
79
  * requiring a runtime read of the session.
78
80
  *
79
81
  * - `create: 'auto'` (default) — server creates the subscription on
80
- * vault success. `onSucceeded` payload includes `subscriptionId`.
82
+ * vault success. `onSucceeded` payload includes `subscriptionIds` (and `subscriptionId` when exactly one was created).
81
83
  * - `create: 'manual'` — server only vaults; merchant calls
82
84
  * `subscriptions.create` from their backend after `payment.vaulted`
83
85
  * webhook or `onSucceeded` callback.
84
86
  */
85
87
  interface RecurringIntent {
86
88
  plan: string;
87
- interval: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly';
89
+ /** A named interval. Or send intervalUnit + intervalCount for any cadence from 1 hour to 1 year. */
90
+ interval?: 'weekly' | 'biweekly' | 'monthly' | 'quarterly' | 'yearly' | 'custom';
91
+ intervalUnit?: 'hour' | 'day' | 'week' | 'month';
92
+ intervalCount?: number;
93
+ /** Makes the subscription an installment plan of exactly this many payments (2–60). Not combinable with trialDays. */
94
+ totalPayments?: number;
88
95
  amount?: number;
89
96
  trialDays?: number;
90
97
  planName?: string;
@@ -142,6 +149,8 @@ interface ThrottleEmbedProps {
142
149
  orderId: string;
143
150
  paymentId: string;
144
151
  subscriptionId?: string;
152
+ /** Every subscription the checkout created, in line order. `subscriptionId` is set only when there is exactly one. */
153
+ subscriptionIds?: string[];
145
154
  /** Order total in minor units — fire `gtag`/`fbq` purchase events with value = total / 100. */
146
155
  total?: number;
147
156
  currency?: string;
@@ -408,7 +417,17 @@ type SecureCardStatus = 'idle' | 'mounting' | 'ready' | 'vaulting' | 'authorizin
408
417
  /** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */
409
418
  interface SecureCardSession {
410
419
  sessionId: string;
411
- gr4vyId: string;
420
+ /**
421
+ * The payment provider instance this session belongs to, straight from
422
+ * `POST /checkout-sessions/:id/card-session`.
423
+ *
424
+ * Either name works. `providerId` is the documented one; `gr4vyId` is the
425
+ * original and is still accepted, because the package shipped with it and
426
+ * dropping it would break consumers already passing the response through.
427
+ */
428
+ providerId?: string;
429
+ /** @deprecated Use `providerId`. Still honoured. */
430
+ gr4vyId?: string;
412
431
  environment: 'sandbox' | 'production';
413
432
  }
414
433
  interface SecureCardError {
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ function CheckoutEmbed({
84
84
  orderId: msg.orderId,
85
85
  paymentId: msg.paymentId,
86
86
  ...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
87
+ ...msg.subscriptionIds !== void 0 ? { subscriptionIds: msg.subscriptionIds } : {},
87
88
  // v1.4 analytics enrichment — forwarded verbatim so the parent
88
89
  // can fire purchase events without an extra fetch.
89
90
  ...msg.total !== void 0 ? { total: msg.total } : {},
@@ -171,6 +172,7 @@ function PaymentEmbed({
171
172
  orderId: msg.orderId,
172
173
  paymentId: msg.paymentId,
173
174
  ...msg.subscriptionId !== void 0 ? { subscriptionId: msg.subscriptionId } : {},
175
+ ...msg.subscriptionIds !== void 0 ? { subscriptionIds: msg.subscriptionIds } : {},
174
176
  // v1.4 analytics enrichment — forwarded verbatim so the parent
175
177
  // can fire purchase events without an extra fetch.
176
178
  ...msg.total !== void 0 ? { total: msg.total } : {},
@@ -685,11 +687,12 @@ function SecureCardProvider({
685
687
  const threeDSElement = useRef4(null);
686
688
  const threeDSOptions = useRef4(void 0);
687
689
  const challengeWaiter = useRef4(null);
688
- const { gr4vyId, environment, sessionId } = session;
690
+ const { environment, sessionId } = session;
691
+ const providerInstanceId = session.providerId ?? session.gr4vyId ?? "";
689
692
  const [instance, setInstance] = useState6(null);
690
693
  const [threeDSMount, setThreeDSMount] = useState6(null);
691
694
  useEffect5(() => {
692
- const created = new SecureFields2({ gr4vyId, environment, sessionId });
695
+ const created = new SecureFields2({ gr4vyId: providerInstanceId, environment, sessionId });
693
696
  setInstance(created);
694
697
  setMountedCount(0);
695
698
  setStatus((current) => current === "idle" || current === "ready" ? "mounting" : current);
@@ -697,7 +700,7 @@ function SecureCardProvider({
697
700
  created.controller?.remove();
698
701
  setInstance(null);
699
702
  };
700
- }, [gr4vyId, environment, sessionId]);
703
+ }, [providerInstanceId, environment, sessionId]);
701
704
  useEffect5(() => {
702
705
  if (!instance) return;
703
706
  const handleSuccess = () => {
@@ -711,6 +714,7 @@ function SecureCardProvider({
711
714
  };
712
715
  const handleThreeDSStart = () => setStatus("challenging");
713
716
  const handleThreeDSFinish = () => {
717
+ if (!challengeWaiter.current) return;
714
718
  setStatus((current) => current === "challenging" ? "vaulting" : current);
715
719
  };
716
720
  instance.addEventListener(SecureFields2.Events.CARD_VAULT_SUCCESS, handleSuccess);
@@ -882,7 +886,14 @@ function SecureCardProvider({
882
886
  cancelChallenge,
883
887
  status,
884
888
  error,
885
- ready: status === "ready",
889
+ // "The fields are mounted and nothing is in flight" — NOT `status ===
890
+ // 'ready'`. Deriving it from the status alone made a decline terminal: the
891
+ // only thing that ever sets `ready` is the mount-count effect, which does
892
+ // not re-run after `setStatus('error')`, so `ready` stayed false and the
893
+ // merchant's pay button stayed disabled. A declined buyer could not try
894
+ // another card without reloading the page — and a decline is the single
895
+ // most common failure at a checkout.
896
+ ready: mountedCount >= FIELDS_REQUIRED_TO_BE_READY && status !== "vaulting" && status !== "authorizing" && status !== "challenging" && status !== "complete",
886
897
  pay,
887
898
  vault,
888
899
  complete
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx","../src/useSecureCard.tsx","../src/threeDSecure.ts","../src/SecureCardFields.tsx"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport type { ThrottleParentMessage } from './types.js';\n\ninterface UseParentCommandsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /** The Throttle origin to address messages to. */\n targetOrigin: string;\n /**\n * The current value of `submitDisabled` from the embed props. Posts a\n * `set-submit-disabled` parent command whenever it changes — and once\n * after the iframe reports `throttle.ready` so the iframe sees the\n * intent even if it boots after the prop is already set.\n *\n * `undefined` means the parent never explicitly enabled/disabled — we\n * never post in that case. Backward-compat with embeds that don't\n * pass the prop.\n */\n submitDisabled?: boolean;\n /**\n * Set to `true` once the iframe posts `throttle.ready`. Until then,\n * commands are buffered (we still try to post but the iframe may not\n * have its listener attached yet — and we re-post on ready as a\n * belt-and-braces guard).\n */\n iframeReady: boolean;\n}\n\n/**\n * Post parent → iframe commands when controlled props change. v1.2 adds\n * `set-submit-disabled`; future commands extend the same channel.\n *\n * postMessage is fire-and-forget — we don't wait for an ack from the\n * iframe. The iframe's listener is idempotent (setting the same value\n * twice is a no-op).\n */\nexport function useParentCommands({\n iframeRef,\n targetOrigin,\n submitDisabled,\n iframeReady,\n}: UseParentCommandsOptions) {\n const [lastSent, setLastSent] = useState<boolean | undefined>(undefined);\n\n // Post when the prop value changes OR when the iframe transitions to\n // ready (so the first state lands).\n useEffect(() => {\n if (submitDisabled === undefined) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // Skip if we already sent this value AND the iframe was already ready\n // — avoids spam on unrelated re-renders.\n if (lastSent === submitDisabled && iframeReady) return;\n if (!iframeReady) return; // Defer until ready\n\n const message: ThrottleParentMessage = {\n source: 'throttle-parent',\n version: 1,\n type: 'set-submit-disabled',\n disabled: submitDisabled,\n };\n win.postMessage(message, targetOrigin);\n setLastSent(submitDisabled);\n }, [iframeRef, targetOrigin, submitDisabled, iframeReady, lastSent]);\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n CartSessionClient,\n type CartSessionCart,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\nexport interface UseCartSessionOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the session id across page loads. Default\n * `'throttle.cartSessionId'`. Pass `null` to disable persistence (in-memory\n * only).\n */\n storageKey?: string | null;\n}\n\nexport interface UseCartSessionResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current opaque session id, or null before the first item. */\n cartSessionId: string | null;\n /** True while a request is in flight. */\n loading: boolean;\n /** Last error, or null. */\n error: Error | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n selectShipping: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /** Hand off to checkout. Returns the hand-off (redirect the buyer to `checkoutUrl`). */\n checkout: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\n/**\n * React hook for backend-less cart ownership. Wraps {@link CartSessionClient}:\n * creates the Throttle cart lazily on the first `addItem` (so empty carts\n * aren't created on every page load), persists the session id to localStorage,\n * and resumes it on the next visit. Mutations update reactive `cart` state.\n *\n * ```tsx\n * const cart = useCartSession({ applicationId, environmentId, quoteToken });\n * <button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>Add</button>\n * <button onClick={async () => {\n * const { checkoutUrl } = await cart.checkout({ returnUrl, cancelUrl });\n * window.location.href = checkoutUrl;\n * }}>Checkout</button>\n * ```\n */\nexport function useCartSession(options: UseCartSessionOptions): UseCartSessionResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Stable client across renders for a given config; construction validates the\n // token, so capture any constructor error instead of throwing during render.\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) setError(initError);\n }, [initError]);\n\n // Resume a persisted session on mount (and whenever the client identity changes).\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setLoading(true);\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n })\n .catch(() => {\n // Expired / unknown / origin-rejected → drop the stale id and start fresh.\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /** Run a mutation that returns the updated cart, managing loading/error + creating the session first. */\n const run = useCallback(\n async (fn: () => Promise<CartSessionCart>, opts: { create?: boolean } = {}): Promise<void> => {\n if (!client) {\n setError(initError ?? new Error('CartSessionClient unavailable'));\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const next = await fn();\n setCart(next);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const addItem = useCallback(\n (input: AddLineItemInput) => run(() => client!.addItem(input), { create: true }),\n [client, run],\n );\n const updateItem = useCallback(\n (itemId: string, input: UpdateLineItemInput) => run(() => client!.updateItem(itemId, input)),\n [client, run],\n );\n const removeItem = useCallback((itemId: string) => run(() => client!.removeItem(itemId)), [client, run]);\n const selectShipping = useCallback(\n (input: SelectShippingInput) => run(() => client!.selectShipping(input), { create: true }),\n [client, run],\n );\n const clearShipping = useCallback(() => run(() => client!.clearShipping()), [client, run]);\n const applyDiscount = useCallback(\n (code: string) => run(() => client!.applyDiscount(code), { create: true }),\n [client, run],\n );\n const removeDiscount = useCallback(() => run(() => client!.removeDiscount()), [client, run]);\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n await run(async () => (await client.get()).cart);\n }, [client, run]);\n\n const checkout = useCallback(\n async (input: CheckoutHandoffInput): Promise<CheckoutHandoff> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n setLoading(true);\n setError(null);\n try {\n await ensureSession();\n return await client.checkout(input);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n loading,\n error,\n addItem,\n updateItem,\n removeItem,\n selectShipping,\n clearShipping,\n applyDiscount,\n removeDiscount,\n checkout,\n refresh,\n reset,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CartSessionClient,\n CartNotOpenError,\n CartNotFoundError,\n type CartSessionCart,\n type SelectedShipping,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\n/**\n * Lifecycle status for the checkout orchestration.\n * - `idle` — no work in flight, no cart yet\n * - `loading` — a request is in flight\n * - `recovering` — the cart went stale (converted/abandoned); rebuilding from the\n * last-known line items into a fresh session\n * - `ready` — a cart is present and no request is in flight\n * - `error` — the last operation failed (see `error`)\n */\nexport type ThrottleCheckoutStatus = 'idle' | 'loading' | 'recovering' | 'ready' | 'error';\n\nexport interface CheckoutTotals {\n currency: string;\n subtotal: number;\n discountTotal: number;\n shippingTotal: number;\n taxTotal: number;\n total: number;\n}\n\nexport interface UseThrottleCheckoutOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the cart session id across page loads.\n * Default `'throttle.cartSessionId'`. Pass `null` to disable persistence.\n */\n storageKey?: string | null;\n /**\n * Auto-recover from a stale cart. When a mutation fails with\n * {@link CartNotOpenError} (the cart was converted by a completed order, or\n * abandoned), the hook starts a fresh session, re-adds the last-known line\n * items and re-selects the shipping method, then retries the operation once.\n * Default `true`. Set `false` to surface {@link CartNotOpenError} instead.\n */\n autoRecover?: boolean;\n}\n\nexport interface UseThrottleCheckoutResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current cart session id, or null before the first item. */\n cartSessionId: string | null;\n /** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */\n status: ThrottleCheckoutStatus;\n /** Last error, or null. */\n error: Error | null;\n /** The selected shipping method, bound to the cart (never a shadow copy). */\n selectedMethod: SelectedShipping | null;\n /** Live totals, bound to the cart. Null before a cart exists. */\n totals: CheckoutTotals | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n /** Lock a shipping method. One call — the returned cart carries recomputed totals. */\n selectMethod: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /**\n * Hand off to checkout. Returns the hand-off — use `checkoutSessionId` with\n * `<PaymentEmbed sessionId={…}>` (or redirect the buyer to `checkoutUrl`).\n */\n createSession: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\nfunction totalsOf(cart: CartSessionCart | null): CheckoutTotals | null {\n if (!cart) return null;\n return {\n currency: cart.currency,\n subtotal: cart.subtotal,\n discountTotal: cart.discountTotal,\n shippingTotal: cart.shippingTotal,\n taxTotal: cart.taxTotal,\n total: cart.total,\n };\n}\n\n/**\n * Orchestrates a backend-less storefront checkout on top of\n * {@link CartSessionClient}, bundling the pieces every storefront otherwise\n * re-implements:\n *\n * - **Cart as source of truth** — `selectedMethod` and `totals` are read straight\n * off the cart; there is no shadow copy to drift.\n * - **One-call shipping select** — `selectMethod` locks the method and returns the\n * cart with recomputed totals in a single round trip.\n * - **Stale-cart recovery** — a {@link CartNotOpenError} (cart converted by a\n * completed order, or abandoned) transparently rebuilds a fresh session from\n * the last-known line items + shipping and retries (`status === 'recovering'`).\n * - **Session handoff** — `createSession` returns the `checkoutSessionId` for\n * `<PaymentEmbed>`.\n *\n * Address collection is handled inside the checkout embed (the hosted/full\n * checkout collects shipping/billing), so this hook intentionally does not write\n * addresses to the cart.\n *\n * ```tsx\n * const { addItem, selectMethod, totals, createSession, status } =\n * useThrottleCheckout({ applicationId, environmentId, quoteToken });\n * ```\n */\nexport function useThrottleCheckout(\n options: UseThrottleCheckoutOptions,\n): UseThrottleCheckoutResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const autoRecover = options.autoRecover ?? true;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [status, setStatus] = useState<ThrottleCheckoutStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Keep the latest cart in a ref so recovery can read the last-known line items\n // without threading it through every callback's dependency list.\n const cartRef = useRef<CartSessionCart | null>(null);\n cartRef.current = cart;\n\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) {\n setError(initError);\n setStatus('error');\n }\n }, [initError]);\n\n // Resume a persisted session on mount.\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setStatus('loading');\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n setStatus('ready');\n })\n .catch(() => {\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n setStatus('idle');\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /**\n * Rebuild a fresh session from the last-known cart so a stale cart doesn't\n * strand the buyer. Re-adds line items and re-selects shipping, then returns\n * the rebuilt cart.\n */\n const rebuild = useCallback(async (): Promise<CartSessionCart> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n const prev = cartRef.current;\n // Drop the stale session and create a new one.\n client.resume('');\n setCartSessionId(null);\n const session = await client.create({\n currency: prev?.currency,\n netN: prev?.netN ?? undefined,\n });\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n let next = session.cart;\n for (const item of prev?.lineItems ?? []) {\n next = await client.addItem({\n type: item.type,\n referenceId: item.referenceId ?? undefined,\n name: item.name,\n description: item.description ?? undefined,\n unitPrice: item.unitPrice,\n quantity: item.quantity,\n imageUrl: item.imageUrl ?? undefined,\n metadata: item.metadata,\n });\n }\n const ship = prev?.selectedShipping;\n if (ship) {\n next = await client.selectShipping({\n methodId: ship.methodId,\n displayName: ship.displayName,\n rateAmount: ship.rateAmount,\n currency: ship.currency,\n carrier: ship.carrier ?? undefined,\n serviceCode: ship.serviceCode ?? undefined,\n estimatedDeliveryDays: ship.estimatedDeliveryDays ?? undefined,\n });\n }\n setCart(next);\n return next;\n }, [client, initError, storageKey]);\n\n /**\n * Run a cart op with loading/error bookkeeping. On a stale-cart error, rebuild\n * (when `autoRecover`) and — for forward-progress ops (`retryAfterRecover`) —\n * retry once against the rebuilt session. Item-id-specific ops set\n * `retryAfterRecover: false` because the id no longer exists after rebuild.\n */\n const run = useCallback(\n async <T,>(\n fn: () => Promise<T>,\n opts: { create?: boolean; retryAfterRecover?: boolean } = {},\n ): Promise<T> => {\n if (!client) {\n const e = initError ?? new Error('CartSessionClient unavailable');\n setError(e);\n setStatus('error');\n throw e;\n }\n setStatus('loading');\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const result = await fn();\n setStatus('ready');\n return result;\n } catch (e) {\n const stale = e instanceof CartNotOpenError || e instanceof CartNotFoundError;\n if (stale && autoRecover) {\n try {\n setStatus('recovering');\n await rebuild();\n if (opts.retryAfterRecover) {\n const retried = await fn();\n setStatus('ready');\n return retried;\n }\n // Rebuild-only: the original op targeted a now-gone id. Surface the\n // rebuilt cart and let the caller reconcile against fresh state.\n setStatus('ready');\n const stale2 = new CartNotOpenError({\n message: 'Cart was rebuilt after going stale; retry your action against the new cart.',\n });\n setError(stale2);\n throw stale2;\n } catch (recoverErr) {\n setError(recoverErr as Error);\n setStatus('error');\n throw recoverErr;\n }\n }\n setError(e as Error);\n setStatus('error');\n throw e;\n }\n },\n [client, ensureSession, initError, autoRecover, rebuild],\n );\n\n const addItem = useCallback(\n async (input: AddLineItemInput) => {\n const next = await run(() => client!.addItem(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const updateItem = useCallback(\n async (itemId: string, input: UpdateLineItemInput) => {\n const next = await run(() => client!.updateItem(itemId, input), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const removeItem = useCallback(\n async (itemId: string) => {\n const next = await run(() => client!.removeItem(itemId), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const selectMethod = useCallback(\n async (input: SelectShippingInput) => {\n const next = await run(() => client!.selectShipping(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const clearShipping = useCallback(async () => {\n const next = await run(() => client!.clearShipping(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n const applyDiscount = useCallback(\n async (code: string) => {\n const next = await run(() => client!.applyDiscount(code), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const removeDiscount = useCallback(async () => {\n const next = await run(() => client!.removeDiscount(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n const next = await run(async () => (await client.get()).cart);\n setCart(next);\n }, [client, run]);\n\n const createSession = useCallback(\n (input: CheckoutHandoffInput): Promise<CheckoutHandoff> =>\n run(() => client!.checkout(input), { create: true, retryAfterRecover: true }),\n [client, run],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n setStatus('idle');\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n status,\n error,\n selectedMethod: cart?.selectedShipping ?? null,\n totals: totalsOf(cart),\n addItem,\n updateItem,\n removeItem,\n selectMethod,\n clearShipping,\n applyDiscount,\n removeDiscount,\n createSession,\n refresh,\n reset,\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { SecureFields } from '@gr4vy/secure-fields';\nimport { resolveChallenge, type ResolveChallengeArgs } from './threeDSecure.js';\n\n/**\n * Lifecycle status for the headless Secure Fields state machine.\n * - `idle` — provider mounted, fields not yet registered\n * - `mounting` — Secure Fields instance constructed, fields registering\n * - `ready` — all required fields registered, submit() may be called\n * - `vaulting` — submit() called, waiting on CARD_VAULT_SUCCESS/FAILURE\n * - `authorizing` — card vaulted, awaiting the merchant's complete() call\n * - `challenging` — complete() returned `requires_action`; Gr4vy's own 3DS\n * iframe (addThreeDSecure) is mounted and running\n * - `complete` — order created\n * - `error` — vault, challenge, or completion failed (see `error`)\n */\nexport type SecureCardStatus =\n | 'idle'\n | 'mounting'\n | 'ready'\n | 'vaulting'\n | 'authorizing'\n | 'challenging'\n | 'complete'\n | 'error';\n\n/** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */\nexport interface SecureCardSession {\n sessionId: string;\n gr4vyId: string;\n environment: 'sandbox' | 'production';\n}\n\nexport interface SecureCardError {\n code: string;\n message: string;\n}\n\nexport type SecureCardFieldKind = 'number' | 'expiry' | 'cvv' | 'postalCode';\n\n/**\n * Everything this package uses off a `SecureFields` instance, declared\n * structurally instead of re-exporting the SDK's own class type.\n *\n * `@gr4vy/secure-fields`'s `exports` map has no `types` condition, so naming\n * its type in an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts` — which is a hard TS7016 for any consumer on\n * `moduleResolution: bundler` with `skipLibCheck: false`, and a silent\n * degradation of `instance` to `any` with `skipLibCheck: true` (the Next\n * default). That hits every installer of this package, not only Secure Fields\n * users. The `paths` override in this package's tsconfig repairs our own\n * compile; it is monorepo-only and does not ship. Same approach as\n * `ThreeDSecureInstance` in `threeDSecure.ts`.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower option and event types stay\n * assignable to the looser ones declared here.\n */\nexport interface SecureFieldsInstance {\n /** Gr4vy's hidden controller iframe, removed when the provider unmounts. */\n controller?: HTMLIFrameElement;\n addCardNumberField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addExpiryDateField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addSecurityCodeField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addField(element: string | HTMLElement, options: Record<string, unknown>): unknown;\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n submit(): void;\n}\n\nexport interface SecureCardContextValue {\n /**\n * The underlying Secure Fields instance — Task 5's field primitives register\n * against it.\n *\n * `null` until the provider's mount effect has constructed it. It cannot be\n * built during render: `new SecureFields()` mutates the document (see the\n * effect in `SecureCardProvider`), and React does not promise a render-phase\n * factory runs exactly once. Consumers that reach for it directly must\n * null-check; the field primitives already wait for it.\n */\n instance: SecureFieldsInstance | null;\n registerField: (kind: SecureCardFieldKind, element: HTMLElement, options?: Record<string, unknown>) => void;\n /**\n * Mount point for Gr4vy's own 3DS challenge iframe (`addThreeDSecure`).\n * Register this once, alongside the card fields; it is only actually\n * mounted if `complete()` gets `requires_action`.\n */\n registerThreeDSecure: (\n element: HTMLElement | string,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ) => void;\n /**\n * Abandons an in-progress challenge (e.g. the buyer closed the challenge\n * UI). Only meaningful while `status === 'challenging'`; settles\n * `complete()`'s promise with `error.code === 'three_ds_abandoned'`, kept\n * distinct from a processor decline code.\n */\n cancelChallenge: () => void;\n status: SecureCardStatus;\n error: SecureCardError | null;\n /** True once all required fields are registered and submit() may be called. */\n ready: boolean;\n /** vault() then complete(). */\n pay: () => Promise<unknown>;\n vault: () => Promise<void>;\n complete: () => Promise<unknown>;\n}\n\nconst SecureCardContext = createContext<SecureCardContextValue | null>(null);\n\n// number, expiry, cvv are required; postalCode is optional.\nconst FIELDS_REQUIRED_TO_BE_READY = 3;\n\nexport interface SecureCardProviderProps {\n session: SecureCardSession;\n /**\n * Throttle's own checkout session id — the `:id` already used to mint\n * `session` via `POST /checkout-sessions/:id/card-session`. NOT the same\n * value as `session.sessionId`: that field is Gr4vy's own checkout session\n * id (required to construct the `SecureFields` instance — see\n * `packages/platform-api/src/routes/card-session.ts`, which returns\n * `{ sessionId: checkoutSessionId, ... }` from `adapter.createCardSession()`,\n * a Gr4vy-minted id). The public `POST /checkout-sessions/:id/complete`\n * route looks `:id` up directly against Throttle's own `checkoutSessions`\n * table (`checkout-sessions.ts`), so `defaultComplete` needs this id, not\n * `session.sessionId`, to call the right URL.\n */\n checkoutSessionId: string;\n children: ReactNode;\n /** Injected for tests / custom completion flows; defaults to a real POST /complete call. */\n completeFn?: (args: { sessionId: string; gr4vySessionId: string }) => Promise<any>;\n /** Injected for tests / custom 3DS UI; defaults to mounting Gr4vy's own 3DS iframe via addThreeDSecure. */\n resolveChallengeFn?: (args: ResolveChallengeArgs) => Promise<void>;\n /**\n * Bounds how long `complete()` waits on an unfinished challenge before\n * settling to a terminal `error` (code `three_ds_timeout`) on its own — the\n * only other exits are Gr4vy's own `THREE_DS_FINISH` and an explicit\n * `cancelChallenge()` call, neither of which is guaranteed to ever fire if\n * the buyer just abandons the tab. Not sourced from Gr4vy documentation\n * (no challenge-duration limit is published in the SDK); 5 minutes is a\n * conservative, overridable default.\n */\n challengeTimeoutMs?: number;\n /**\n * Base URL of the Throttle API (e.g. `https://api.usethrottle.dev`).\n * Required — not optional-with-a-same-origin-default. Secure Fields is the\n * headless surface for merchants who mount the card form on THEIR OWN\n * origin, which by definition is not same-origin with the Throttle API, so\n * `defaultComplete`'s `fetch()` can never resolve a relative path (it 404s\n * against the merchant's own domain at payment time instead). Making this\n * required turns that into a compile error instead of a silent runtime\n * failure. Joined onto the request path regardless of a trailing slash.\n */\n apiBaseUrl: string;\n onSuccess?: (result: SecureCardCompleteResult) => void;\n}\n\n/**\n * The real `POST /checkout-sessions/:id/complete` success body for the\n * Secure Fields flow (`checkout-session-service.ts` `complete()`,\n * proxy-mode-embedded return, ~line 2338 and its idempotent-retry twin at\n * ~line 1849). There is no `order` key.\n */\nexport interface SecureCardCompleteResult {\n orderId: string;\n orderNumber: string;\n paymentId: string;\n /** Minor units (cents). */\n total: number;\n currency: string;\n status: string;\n paymentStatus: string;\n}\n\nconst DEFAULT_CHALLENGE_TIMEOUT_MS = 5 * 60 * 1000;\n\nexport function SecureCardProvider({\n session,\n checkoutSessionId,\n children,\n completeFn,\n resolveChallengeFn,\n challengeTimeoutMs = DEFAULT_CHALLENGE_TIMEOUT_MS,\n apiBaseUrl,\n onSuccess,\n}: SecureCardProviderProps) {\n const [status, setStatus] = useState<SecureCardStatus>('idle');\n const [error, setError] = useState<SecureCardError | null>(null);\n const [mountedCount, setMountedCount] = useState(0);\n const vaultWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n const threeDSElement = useRef<HTMLElement | string | null>(null);\n const threeDSOptions = useRef<{ challengeWindowSize?: '01' | '02' | '03' | '04' | '05' } | undefined>(undefined);\n const challengeWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n\n const { gr4vyId, environment, sessionId } = session;\n const [instance, setInstance] = useState<SecureFieldsInstance | null>(null);\n /** Mount point for Gr4vy's challenge, as state so arming can be an effect. */\n const [threeDSMount, setThreeDSMount] = useState<HTMLElement | string | null>(null);\n\n /**\n * `new SecureFields()` is a side effect on the document, not a computation.\n * Its constructor removes whatever `iframe#controller[src*=secure-fields]`\n * is already on the page, creates its own, appends it to `document.body`,\n * and resets the SDK's module-level event bus — so it must run exactly once\n * per mounted provider.\n *\n * It used to sit in a `useMemo`. React invokes a memo factory twice under\n * Strict Mode and commits the FIRST value, so the second construction tore\n * the committed instance's own controller iframe off the page. That\n * instance's internal `controllerReady` promise then never resolved, Gr4vy\n * never inserted a single field iframe, and yet `status` still reported\n * `ready` — because `ready` counts our own `registerField` calls, not the\n * SDK's DOM. Every \"verified in a browser\" pass before Task 9a ran against\n * that false positive (Task 9a, Finding 1).\n *\n * Task 4 had already moved `setStatus('mounting')` out of this same memo for\n * exactly this reason and left the SDK construction behind. `useMemo` is not\n * a lifecycle hook; an effect is the only hook React pairs with a cleanup,\n * so a Strict-Mode double-invoke settles on one live, attached instance.\n */\n useEffect(() => {\n const created = new SecureFields({ gr4vyId, environment, sessionId });\n setInstance(created);\n // A rebuilt instance has no registered fields. Both the counter and the\n // status derived from it must go back, or a `session` prop change leaves\n // `ready === true` while every field iframe is still being re-created and\n // `submit()` would fire against nothing.\n setMountedCount(0);\n setStatus((current) => (current === 'idle' || current === 'ready' ? 'mounting' : current));\n return () => {\n // The SDK exposes no teardown at all, so do the two things that matter:\n // take its controller iframe off the page, and stop handing the instance\n // out so no field can ever register against a discarded one.\n created.controller?.remove();\n setInstance(null);\n };\n }, [gr4vyId, environment, sessionId]);\n\n useEffect(() => {\n if (!instance) return;\n\n const handleSuccess = () => {\n vaultWaiter.current?.resolve();\n };\n const handleFailure = (data?: { code?: string; message?: string }) => {\n vaultWaiter.current?.reject({\n code: data?.code ?? 'card_vault_failed',\n message: data?.message ?? 'The card could not be saved.',\n });\n };\n\n // The challenge now happens INSIDE submit(), between `vaulting` and the\n // vault result, so its status is driven by Gr4vy's own events rather than\n // by anything the server says. Without these the UI sits on `vaulting`\n // while a challenge is visibly on screen.\n const handleThreeDSStart = () => setStatus('challenging');\n const handleThreeDSFinish = () => {\n // Back to waiting on CARD_VAULT_SUCCESS/FAILURE, which follows the\n // challenge. The authentication outcome is not read from here: the\n // server is the authority on whether the money moved.\n setStatus((current) => (current === 'challenging' ? 'vaulting' : current));\n };\n\n instance.addEventListener(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.addEventListener(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.addEventListener(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n\n return () => {\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n };\n }, [instance]);\n\n const registerField = useCallback(\n (kind: SecureCardFieldKind, element: HTMLElement, options: Record<string, unknown> = {}) => {\n // Fields render before the provider's mount effect has constructed the\n // instance (React runs child effects before parent effects). They\n // re-register themselves when `instance` appears — see SecureCardFields.\n if (!instance) return;\n if (kind === 'number') instance.addCardNumberField(element, options);\n else if (kind === 'expiry') instance.addExpiryDateField(element, options);\n else if (kind === 'cvv') instance.addSecurityCodeField(element, options);\n else instance.addField(element, { type: 'postalCode', ...options } as any);\n setMountedCount((n) => n + 1);\n },\n [instance],\n );\n\n useEffect(() => {\n if (mountedCount >= FIELDS_REQUIRED_TO_BE_READY) setStatus('ready');\n }, [mountedCount]);\n\n const registerThreeDSecure = useCallback(\n (element: HTMLElement | string, options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' }) => {\n threeDSElement.current = element;\n threeDSOptions.current = options;\n // State as well as the ref: arming 3DS is an effect that has to re-run\n // when the mount point appears, and a ref write does not re-render.\n setThreeDSMount(element);\n },\n [],\n );\n\n /**\n * Arm 3-D Secure as SETUP, before `submit()` — not as a reaction to the\n * server.\n *\n * Gr4vy runs the enrolment check \"at the time of submitting Secure Fields\",\n * so `addThreeDSecure` has to be registered before `submit()` or the\n * challenge can never be raised. The original build called it only after\n * `/complete` answered `requires_action`, which is after `submit()` has\n * already finished — so it never ran, and every sandbox transaction came\n * back `threeDSecure: undefined`.\n *\n * The SDK has no un-register, so this arms once per instance; a changed\n * mount element arrives with a new instance or not at all.\n */\n useEffect(() => {\n if (!instance || !threeDSMount) return;\n try {\n instance.addThreeDSecure(\n threeDSMount,\n threeDSOptions.current?.challengeWindowSize\n ? { challengeWindowSize: threeDSOptions.current.challengeWindowSize }\n : undefined,\n );\n } catch (err) {\n // Arming is best-effort: a card that never needs a challenge must still\n // be chargeable. A card that DOES need one will fail visibly at submit.\n setError({\n code: 'three_ds_arm_failed',\n message: (err as Error)?.message ?? 'Could not arm 3-D Secure.',\n });\n }\n }, [instance, threeDSMount]);\n\n const cancelChallenge = useCallback(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_abandoned',\n message: 'The 3-D Secure challenge was abandoned.',\n });\n }, []);\n\n const vault = useCallback(() => {\n // Nothing to submit to yet. Previously impossible (the instance was built\n // during render); now the provider's first commit has none, and calling\n // `submit()` on nothing would hang `vaulting` forever with no rejection.\n if (!instance) {\n return Promise.reject<void>({\n code: 'not_ready',\n message: 'The card fields are not ready yet.',\n } as SecureCardError);\n }\n\n // A second vault() while one is already in flight would otherwise silently\n // overwrite vaultWaiter.current, orphaning the first caller's promise forever\n // (no resolve, no reject). Reject the second call instead; the first keeps\n // waiting on its own vault event.\n if (vaultWaiter.current) {\n return Promise.reject<void>({\n code: 'vault_in_progress',\n message: 'A card vault is already in progress.',\n } as SecureCardError);\n }\n\n setError(null);\n setStatus('vaulting');\n return new Promise<void>((resolve, reject) => {\n vaultWaiter.current = {\n resolve: () => {\n vaultWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n vaultWaiter.current = null;\n setError(e);\n setStatus('error');\n reject(e);\n },\n };\n instance.submit();\n });\n }, [instance]);\n\n const complete = useCallback(async () => {\n setStatus('authorizing');\n const doComplete = completeFn ?? ((args: { sessionId: string; gr4vySessionId: string }) => defaultComplete({ ...args, apiBaseUrl }));\n const doResolveChallenge = resolveChallengeFn ?? resolveChallenge;\n try {\n let res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n\n if (res?.status === 'requires_action') {\n if (!threeDSElement.current) {\n throw Object.assign(new Error('No 3-D Secure mount element registered (call registerThreeDSecure).'), {\n code: 'three_ds_element_missing',\n });\n }\n if (!instance) {\n throw Object.assign(new Error('The card fields are not ready yet.'), { code: 'not_ready' });\n }\n setStatus('challenging');\n // Gr4vy's own 3DS iframe runs the challenge; this resolves once it\n // reports THREE_DS_FINISH, or rejects if cancelChallenge() is called\n // or the resolver itself fails, or rejects on its own after\n // challengeTimeoutMs so a buyer who just\n // abandons the tab (no finish, no cancel) can never strand complete()\n // forever. It does not tell us success/failure — re-query /complete\n // for that.\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_timeout',\n message: 'The 3-D Secure challenge timed out.',\n });\n }, challengeTimeoutMs);\n\n challengeWaiter.current = {\n resolve: () => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n reject(Object.assign(new Error(e.message), { code: e.code }));\n },\n };\n doResolveChallenge({\n instance,\n element: threeDSElement.current!,\n ...(threeDSOptions.current ?? {}),\n }).then(\n () => challengeWaiter.current?.resolve(),\n // `resolveChallenge` calls `instance.addThreeDSecure()` synchronously\n // inside its Promise executor, so an SDK throw rejects here; a\n // merchant-supplied `resolveChallengeFn` can reject too. Without this\n // arm the only exit was `challengeTimeoutMs` — five minutes stuck at\n // `status: 'challenging'`, plus an unhandled rejection. Same\n // code-passthrough convention as `complete()`'s catch: keep a `code`\n // the resolver supplied, otherwise report `three_ds_failed`.\n (err: unknown) =>\n challengeWaiter.current?.reject({\n code: (err as { code?: string })?.code ?? 'three_ds_failed',\n message: (err as Error)?.message ?? 'The 3-D Secure challenge could not be completed.',\n }),\n );\n });\n\n setStatus('authorizing');\n res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n if (res?.status === 'requires_action') {\n throw Object.assign(new Error('The payment still requires authentication after the 3-D Secure challenge.'), {\n code: 'three_ds_unresolved',\n });\n }\n }\n\n setStatus('complete');\n onSuccess?.(res as SecureCardCompleteResult);\n return res;\n } catch (err) {\n setError({\n code: (err as { code?: string })?.code ?? 'complete_failed',\n message: (err as Error)?.message ?? 'Payment could not be completed.',\n });\n setStatus('error');\n throw err;\n }\n }, [completeFn, resolveChallengeFn, challengeTimeoutMs, apiBaseUrl, instance, checkoutSessionId, session.sessionId, onSuccess]);\n\n const pay = useCallback(async () => {\n await vault();\n return complete();\n }, [vault, complete]);\n\n const value: SecureCardContextValue = {\n instance,\n registerField,\n registerThreeDSecure,\n cancelChallenge,\n status,\n error,\n ready: status === 'ready',\n pay,\n vault,\n complete,\n };\n\n return <SecureCardContext.Provider value={value}>{children}</SecureCardContext.Provider>;\n}\n\n/**\n * Default completion call — POSTs to the public, buyer-facing complete route.\n *\n * `completeSessionSchema` (`packages/platform-api/src/schemas/checkout-sessions.ts`)\n * is `.strict()` and requires `paymentMethod`. The handler\n * (`checkout-session-service.ts` `complete()`) rejects `paymentMethod: 'card'`\n * outright (\"Use 'embedded' ...\"), and for non-proxy-mode `'embedded'`\n * requires `processorCheckoutSessionId` or `processorTransactionId`. Secure\n * Fields vaults the card straight to the Gr4vy checkout session — no embed\n * transaction exists yet — so this is the server-authorize sub-branch: send\n * `processorCheckoutSessionId` = that same Gr4vy session id (`gr4vySessionId`,\n * i.e. `SecureCardSession.sessionId`), which the handler forwards into\n * `adapter.authorize({ token: JSON.stringify({ checkoutSessionId }) })`.\n *\n * `apiBaseUrl` is joined onto the path with `joinApiUrl` so a merchant-supplied\n * value with or without a trailing slash both resolve to the same URL —\n * without it, `https://api.example.com/` + `/api/v1/...` would double the slash.\n */\nasync function defaultComplete({\n sessionId,\n gr4vySessionId,\n apiBaseUrl,\n}: {\n sessionId: string;\n gr4vySessionId: string;\n apiBaseUrl: string;\n}) {\n const res = await fetch(joinApiUrl(apiBaseUrl, `/api/v1/checkout-sessions/${sessionId}/complete`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paymentMethod: 'embedded', processorCheckoutSessionId: gr4vySessionId }),\n });\n const body = await res.json();\n if (!res.ok) {\n throw Object.assign(new Error(body?.error?.message ?? 'Payment could not be completed.'), {\n code: body?.error?.code ?? 'payment_failed',\n });\n }\n return body?.data ?? body;\n}\n\n/** Joins a base URL and a path safely regardless of a trailing slash on `base`. */\nfunction joinApiUrl(base: string, path: string): string {\n return `${base.replace(/\\/+$/, '')}${path}`;\n}\n\nexport function useSecureCard(): SecureCardContextValue {\n const ctx = useContext(SecureCardContext);\n if (!ctx) throw new Error('useSecureCard must be used inside <SecureCardProvider>');\n return ctx;\n}\n\nexport { SecureCardContext };\n","import { SecureFields } from '@gr4vy/secure-fields';\n\n/**\n * Subset of the Secure Fields instance this module needs — real or mocked.\n *\n * Declared structurally rather than as `Pick<SecureFields, …>`: any reference\n * to the SDK's own type from an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts`, and that package's `exports` map carries no `types`\n * condition — so a consumer on `moduleResolution: bundler` gets TS7016 under\n * `skipLibCheck: false`, or silently degrades the member to `any` under\n * `skipLibCheck: true` (the Next default). The `paths` override in this\n * package's tsconfig repairs our own compile only; it does not ship.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower `CombinedEvents` event parameter\n * stays assignable to the `string` declared here.\n */\nexport interface ThreeDSecureInstance {\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n}\n\nexport interface ResolveChallengeArgs {\n /** The `SecureFields` instance already constructed for this session. */\n instance: ThreeDSecureInstance;\n /** Mount point for Gr4vy's own 3DS challenge iframe. */\n element: HTMLElement | string;\n challengeWindowSize?: '01' | '02' | '03' | '04' | '05';\n}\n\n/**\n * Drives a Gr4vy-hosted 3-D Secure challenge to completion using Secure\n * Fields' own `addThreeDSecure`, instead of hand-rolling a modal around\n * `transaction.paymentMethod.approvalUrl`.\n *\n * Why this instead of a modal + approvalUrl + redirect fallback: `addThreeDSecure(element, options)`\n * takes no transaction id and no approval URL — its only inputs are a mount\n * element and an optional `challengeWindowSize`\n * (node_modules/@gr4vy/secure-fields lib/index.d.ts, lib/types.d.ts —\n * `ThreeDSecureOptions = { challengeWindowSize?: ... }`). The bundled\n * implementation (lib/index.js, `class P` / `ThreeDSecure.init`) shows the\n * iframe it creates is `${frameUrl}/threeds.html` with query params\n * `{sessionId, parentOrigin, gr4vyId, environment}` — the exact identifiers\n * `SecureFields` was already constructed with. The challenge is scoped to the\n * Secure Fields *session*, not to anything passed in per-call. Our server\n * creates the transaction against that same session\n * (`paymentMethod: { method: 'checkout-session', id: checkoutSessionId }` —\n * packages/commerce-payment-orchestrator/src/adapters/gr4vy.ts `authorize()`),\n * so this element renders the challenge for that transaction without ever\n * touching `approvalUrl`. `approvalUrl` does not appear anywhere in the SDK\n * bundle (checked via `grep -c approvalUrl lib/index.js` → 0).\n *\n * `THREE_DS_FINISH`'s payload shape is undocumented (absent from the SDK's\n * README; only the bare event name appears in the CHANGELOG and\n * lib/constants.d.ts), so this does not try to read a success/fail flag off\n * it — it only signals \"the challenge UI is done.\" The caller re-queries\n * `/complete` afterwards for the authoritative outcome, which is also the\n * documented re-entry path for this endpoint (`checkout-session-service.ts`'s\n * `complete()` already tolerates re-invocation on the same session — see its\n * \"processing lease\" / pending-order-reuse comments).\n */\nexport function resolveChallenge({ instance, element, challengeWindowSize }: ResolveChallengeArgs): Promise<void> {\n return new Promise((resolve) => {\n const handleFinish = () => {\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n resolve();\n };\n // Registered before addThreeDSecure() mounts the frame, so a finish that\n // arrives immediately after mount is never missed.\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n instance.addThreeDSecure(element, challengeWindowSize ? { challengeWindowSize } : undefined);\n });\n}\n","import { useContext, useEffect, useRef, type CSSProperties } from 'react';\nimport { SecureCardContext, type SecureCardFieldKind } from './useSecureCard.js';\n\nexport interface SecureCardFieldProps {\n className?: string;\n style?: CSSProperties;\n /** Gr4vy field options (styles, autoFocus, maskInput, pattern…) passed through unchanged. */\n options?: Record<string, unknown>;\n}\n\n/**\n * Each field is an empty element Gr4vy replaces with its own iframe. We render\n * the container and nothing else: the merchant chose Secure Fields precisely so\n * that the styling is theirs.\n *\n * The element we hand Gr4vy is created with `document.createElement`, NOT the\n * one React renders. Gr4vy's SDK mounts a field with\n * `element.parentNode.replaceChild(itsOwnWrapper, element)` — it *replaces* the\n * node it is given. Handing it the React-managed node left React holding a\n * reference to a node that was no longer in the tree, so unmounting the field\n * threw an uncaught `NotFoundError: Failed to execute 'removeChild'` and killed\n * the page on the first click (Task 9a, Finding 3 — 100% reproducible in a\n * production build). Giving Gr4vy a throwaway child instead means React only\n * ever removes the host `<div>` it rendered itself, and everything Gr4vy put\n * inside goes with it.\n *\n * The merchant's `className`/`style` stay on the host element, and the\n * throwaway child carries `height: 100%` so Gr4vy's wrapper (which copies the\n * `style` attribute off the node it replaces) still fills the merchant's box —\n * the field iframe is `height: 100%` and would otherwise collapse to an\n * iframe's 150px default.\n *\n * Known limitation: Gr4vy's SDK has no unregister (only `removeEventListener`),\n * so a field that unmounts stays registered on the instance. Unmounting an\n * optional field (postal code) and remounting it is safe — re-registering\n * replaces the SDK's own per-type listener and builds a fresh iframe.\n * Unmounting a REQUIRED field (number, expiry, cvv) and then calling `pay()`\n * without remounting it leaves the SDK submitting against a field whose iframe\n * is gone; remount it first.\n */\nfunction useField(kind: SecureCardFieldKind, options?: Record<string, unknown>) {\n const ctx = useContext(SecureCardContext);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const instance = ctx?.instance ?? null;\n\n useEffect(() => {\n const host = hostRef.current;\n // React runs child effects before parent effects, so on the very first\n // commit the provider has not constructed the instance yet. This effect\n // re-runs when it appears.\n if (!host || !ctx || !instance) return;\n\n const target = document.createElement('div');\n target.style.height = '100%';\n host.appendChild(target);\n ctx.registerField(kind, target, options ?? {});\n\n return () => {\n // Drop whatever Gr4vy put in the host (its wrapper replaced `target`),\n // so a remount starts from an empty container instead of stacking a\n // second, dead iframe under the same field.\n while (host.firstChild) host.removeChild(host.firstChild);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [instance]);\n\n return hostRef;\n}\n\nexport function CardNumberField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('number', options)} className={className} style={style} />;\n}\n\nexport function ExpiryField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('expiry', options)} className={className} style={style} />;\n}\n\nexport function SecurityCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('cvv', options)} className={className} style={style} />;\n}\n\nexport function PostalCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('postalCode', options)} className={className} style={style} />;\n}\n"],"mappings":"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","names":["useState","useEffect","useState","useCallback","useRef","useState","jsx","DEFAULT_BASE_URL","useRef","useState","useCallback","useCallback","useEffect","useState","useCallback","useEffect","useMemo","useRef","useState","CartSessionClient","DEFAULT_STORAGE_KEY","readStored","writeStored","useCallback","useEffect","useRef","useState","SecureFields","jsx","useState","useRef","useEffect","SecureFields","useCallback","useContext","useEffect","useRef","jsx","useContext","useRef","useEffect"]}
1
+ {"version":3,"sources":["../src/CheckoutEmbed.tsx","../src/useThrottleEvents.ts","../src/useParentCommands.ts","../src/PaymentEmbed.tsx","../src/useCartSession.tsx","../src/useThrottleCheckout.tsx","../src/useSecureCard.tsx","../src/threeDSecure.ts","../src/SecureCardFields.tsx"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type {\n ThrottleEmbedProps,\n CheckoutEmbedExtraProps,\n ThrottleMessage,\n} from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Full-checkout iframe embed. Renders the entire Throttle hosted\n * checkout (address / shipping / payment) inside an iframe and\n * surfaces lifecycle + terminal events through callbacks. The\n * iframe auto-resizes on `throttle.resize` events.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list\n * (`throttle embed-config set --origins ...`). Mismatches render\n * an in-iframe \"Embed not authorized\" panel.\n */\nexport function CheckoutEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 600,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n onStepChanged,\n}: ThrottleEmbedProps & CheckoutEmbedExtraProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n ...(msg.subscriptionIds !== undefined ? { subscriptionIds: msg.subscriptionIds } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.step.changed':\n onStepChanged?.({ step: msg.step });\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled, onStepChanged],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'checkout-full', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle checkout\"\n />\n );\n}\n","import { useEffect, type RefObject } from 'react';\nimport type { ThrottleMessage } from './types.js';\n\nexport interface UseThrottleEventsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /**\n * The Throttle origin (e.g. https://checkout.usethrottle.dev).\n * Strict-equals check against `MessageEvent.origin`. Anything else\n * is silently dropped.\n */\n expectedOrigin: string;\n onMessage: (msg: ThrottleMessage) => void;\n}\n\n/**\n * Subscribe to validated postMessage events from a Throttle iframe.\n *\n * Validation:\n * - event.origin === expectedOrigin (strict string match)\n * - event.source === iframeRef.current.contentWindow (defends against\n * other iframes on the same origin spoofing events)\n * - event.data.source === 'throttle' && event.data.version === 1\n * - event.data.type starts with 'throttle.'\n *\n * Anything failing validation is silently dropped — the parent page\n * may receive postMessages from analytics SDKs, browser extensions,\n * other iframes, etc. We must never throw on those.\n */\nexport function useThrottleEvents({ iframeRef, expectedOrigin, onMessage }: UseThrottleEventsOptions) {\n useEffect(() => {\n function handle(ev: MessageEvent) {\n if (ev.origin !== expectedOrigin) return;\n if (iframeRef.current && ev.source !== iframeRef.current.contentWindow) return;\n const data = ev.data as Partial<ThrottleMessage> | null | undefined;\n if (!data || typeof data !== 'object') return;\n if (data.source !== 'throttle' || data.version !== 1) return;\n if (typeof data.type !== 'string' || !data.type.startsWith('throttle.')) return;\n onMessage(data as ThrottleMessage);\n }\n window.addEventListener('message', handle);\n return () => window.removeEventListener('message', handle);\n }, [iframeRef, expectedOrigin, onMessage]);\n}\n","import { useEffect, useState, type RefObject } from 'react';\nimport type { ThrottleParentMessage } from './types.js';\n\ninterface UseParentCommandsOptions {\n iframeRef: RefObject<HTMLIFrameElement | null>;\n /** The Throttle origin to address messages to. */\n targetOrigin: string;\n /**\n * The current value of `submitDisabled` from the embed props. Posts a\n * `set-submit-disabled` parent command whenever it changes — and once\n * after the iframe reports `throttle.ready` so the iframe sees the\n * intent even if it boots after the prop is already set.\n *\n * `undefined` means the parent never explicitly enabled/disabled — we\n * never post in that case. Backward-compat with embeds that don't\n * pass the prop.\n */\n submitDisabled?: boolean;\n /**\n * Set to `true` once the iframe posts `throttle.ready`. Until then,\n * commands are buffered (we still try to post but the iframe may not\n * have its listener attached yet — and we re-post on ready as a\n * belt-and-braces guard).\n */\n iframeReady: boolean;\n}\n\n/**\n * Post parent → iframe commands when controlled props change. v1.2 adds\n * `set-submit-disabled`; future commands extend the same channel.\n *\n * postMessage is fire-and-forget — we don't wait for an ack from the\n * iframe. The iframe's listener is idempotent (setting the same value\n * twice is a no-op).\n */\nexport function useParentCommands({\n iframeRef,\n targetOrigin,\n submitDisabled,\n iframeReady,\n}: UseParentCommandsOptions) {\n const [lastSent, setLastSent] = useState<boolean | undefined>(undefined);\n\n // Post when the prop value changes OR when the iframe transitions to\n // ready (so the first state lands).\n useEffect(() => {\n if (submitDisabled === undefined) return;\n const win = iframeRef.current?.contentWindow;\n if (!win) return;\n // Skip if we already sent this value AND the iframe was already ready\n // — avoids spam on unrelated re-renders.\n if (lastSent === submitDisabled && iframeReady) return;\n if (!iframeReady) return; // Defer until ready\n\n const message: ThrottleParentMessage = {\n source: 'throttle-parent',\n version: 1,\n type: 'set-submit-disabled',\n disabled: submitDisabled,\n };\n win.postMessage(message, targetOrigin);\n setLastSent(submitDisabled);\n }, [iframeRef, targetOrigin, submitDisabled, iframeReady, lastSent]);\n}\n","import { useCallback, useRef, useState } from 'react';\nimport { useThrottleEvents } from './useThrottleEvents.js';\nimport { useParentCommands } from './useParentCommands.js';\nimport type { ThrottleEmbedProps, ThrottleMessage } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://checkout.usethrottle.dev';\n\n/**\n * Payment-only iframe embed. Renders just the Gr4vy provider iframe\n * (no address / shipping / cart UI). Email is collected by the Gr4vy\n * iframe in proxy-mode sessions. Used when the merchant has built\n * their own checkout UI and only needs Throttle to handle payment\n * authorization + capture.\n *\n * `parentOrigin` must match an entry in the merchant's allow-list.\n * The iframe auto-resizes on `throttle.resize` events.\n */\nexport function PaymentEmbed({\n sessionId,\n parentOrigin,\n baseUrl = DEFAULT_BASE_URL,\n route = 'c',\n primary,\n logo,\n initialHeight = 480,\n className,\n style,\n submitDisabled,\n onReady,\n onProcessing,\n onSucceeded,\n onFailed,\n onCanceled,\n}: ThrottleEmbedProps) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const expectedOrigin = new URL(baseUrl).origin;\n const [iframeReady, setIframeReady] = useState(false);\n\n const dispatch = useCallback(\n (msg: ThrottleMessage) => {\n switch (msg.type) {\n case 'throttle.ready':\n setIframeReady(true);\n onReady?.();\n break;\n case 'throttle.processing':\n onProcessing?.();\n break;\n case 'throttle.completed':\n onSucceeded?.({\n orderId: msg.orderId,\n paymentId: msg.paymentId,\n ...(msg.subscriptionId !== undefined ? { subscriptionId: msg.subscriptionId } : {}),\n ...(msg.subscriptionIds !== undefined ? { subscriptionIds: msg.subscriptionIds } : {}),\n // v1.4 analytics enrichment — forwarded verbatim so the parent\n // can fire purchase events without an extra fetch.\n ...(msg.total !== undefined ? { total: msg.total } : {}),\n ...(msg.currency !== undefined ? { currency: msg.currency } : {}),\n ...(msg.lineItems !== undefined ? { lineItems: msg.lineItems } : {}),\n });\n break;\n case 'throttle.error':\n onFailed?.({ code: msg.code, message: msg.message });\n break;\n case 'throttle.cancelled':\n onCanceled?.();\n break;\n case 'throttle.resize':\n if (iframeRef.current) iframeRef.current.style.height = `${msg.height}px`;\n break;\n }\n },\n [onReady, onProcessing, onSucceeded, onFailed, onCanceled],\n );\n\n useThrottleEvents({ iframeRef, expectedOrigin, onMessage: dispatch });\n useParentCommands({\n iframeRef,\n targetOrigin: expectedOrigin,\n submitDisabled,\n iframeReady,\n });\n\n const params = new URLSearchParams({ embed: '1', mode: 'payment-only', parentOrigin });\n if (primary) params.set('primary', primary);\n if (logo) params.set('logo', logo);\n const cleanBase = baseUrl.replace(/\\/$/, '');\n const src = `${cleanBase}/${route}/${encodeURIComponent(sessionId)}?${params.toString()}`;\n\n return (\n <iframe\n ref={iframeRef}\n src={src}\n className={className}\n style={{ border: 0, width: '100%', minHeight: initialHeight, ...style }}\n allow=\"payment *\"\n title=\"Throttle payment\"\n />\n );\n}\n","import { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n CartSessionClient,\n type CartSessionCart,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\nexport interface UseCartSessionOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the session id across page loads. Default\n * `'throttle.cartSessionId'`. Pass `null` to disable persistence (in-memory\n * only).\n */\n storageKey?: string | null;\n}\n\nexport interface UseCartSessionResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current opaque session id, or null before the first item. */\n cartSessionId: string | null;\n /** True while a request is in flight. */\n loading: boolean;\n /** Last error, or null. */\n error: Error | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n selectShipping: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /** Hand off to checkout. Returns the hand-off (redirect the buyer to `checkoutUrl`). */\n checkout: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\n/**\n * React hook for backend-less cart ownership. Wraps {@link CartSessionClient}:\n * creates the Throttle cart lazily on the first `addItem` (so empty carts\n * aren't created on every page load), persists the session id to localStorage,\n * and resumes it on the next visit. Mutations update reactive `cart` state.\n *\n * ```tsx\n * const cart = useCartSession({ applicationId, environmentId, quoteToken });\n * <button onClick={() => cart.addItem({ name: 'Widget', unitPrice: 2999, quantity: 1 })}>Add</button>\n * <button onClick={async () => {\n * const { checkoutUrl } = await cart.checkout({ returnUrl, cancelUrl });\n * window.location.href = checkoutUrl;\n * }}>Checkout</button>\n * ```\n */\nexport function useCartSession(options: UseCartSessionOptions): UseCartSessionResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Stable client across renders for a given config; construction validates the\n // token, so capture any constructor error instead of throwing during render.\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) setError(initError);\n }, [initError]);\n\n // Resume a persisted session on mount (and whenever the client identity changes).\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setLoading(true);\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n })\n .catch(() => {\n // Expired / unknown / origin-rejected → drop the stale id and start fresh.\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n })\n .finally(() => {\n if (!cancelled) setLoading(false);\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /** Run a mutation that returns the updated cart, managing loading/error + creating the session first. */\n const run = useCallback(\n async (fn: () => Promise<CartSessionCart>, opts: { create?: boolean } = {}): Promise<void> => {\n if (!client) {\n setError(initError ?? new Error('CartSessionClient unavailable'));\n return;\n }\n setLoading(true);\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const next = await fn();\n setCart(next);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const addItem = useCallback(\n (input: AddLineItemInput) => run(() => client!.addItem(input), { create: true }),\n [client, run],\n );\n const updateItem = useCallback(\n (itemId: string, input: UpdateLineItemInput) => run(() => client!.updateItem(itemId, input)),\n [client, run],\n );\n const removeItem = useCallback((itemId: string) => run(() => client!.removeItem(itemId)), [client, run]);\n const selectShipping = useCallback(\n (input: SelectShippingInput) => run(() => client!.selectShipping(input), { create: true }),\n [client, run],\n );\n const clearShipping = useCallback(() => run(() => client!.clearShipping()), [client, run]);\n const applyDiscount = useCallback(\n (code: string) => run(() => client!.applyDiscount(code), { create: true }),\n [client, run],\n );\n const removeDiscount = useCallback(() => run(() => client!.removeDiscount()), [client, run]);\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n await run(async () => (await client.get()).cart);\n }, [client, run]);\n\n const checkout = useCallback(\n async (input: CheckoutHandoffInput): Promise<CheckoutHandoff> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n setLoading(true);\n setError(null);\n try {\n await ensureSession();\n return await client.checkout(input);\n } catch (e) {\n setError(e as Error);\n throw e;\n } finally {\n setLoading(false);\n }\n },\n [client, ensureSession, initError],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n loading,\n error,\n addItem,\n updateItem,\n removeItem,\n selectShipping,\n clearShipping,\n applyDiscount,\n removeDiscount,\n checkout,\n refresh,\n reset,\n };\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n CartSessionClient,\n CartNotOpenError,\n CartNotFoundError,\n type CartSessionCart,\n type SelectedShipping,\n type AddLineItemInput,\n type UpdateLineItemInput,\n type SelectShippingInput,\n type CreateCartSessionInput,\n type CheckoutHandoffInput,\n type CheckoutHandoff,\n} from '@usethrottle/cart';\n\nconst DEFAULT_STORAGE_KEY = 'throttle.cartSessionId';\n\n/**\n * Lifecycle status for the checkout orchestration.\n * - `idle` — no work in flight, no cart yet\n * - `loading` — a request is in flight\n * - `recovering` — the cart went stale (converted/abandoned); rebuilding from the\n * last-known line items into a fresh session\n * - `ready` — a cart is present and no request is in flight\n * - `error` — the last operation failed (see `error`)\n */\nexport type ThrottleCheckoutStatus = 'idle' | 'loading' | 'recovering' | 'ready' | 'error';\n\nexport interface CheckoutTotals {\n currency: string;\n subtotal: number;\n discountTotal: number;\n shippingTotal: number;\n taxTotal: number;\n total: number;\n}\n\nexport interface UseThrottleCheckoutOptions {\n /** Application UUID. */\n applicationId: string;\n /** Workspace environment UUID the publishable token was minted for. */\n environmentId: string;\n /** Publishable storefront quote token (`pk_…`). */\n quoteToken: string;\n baseUrl?: string;\n /**\n * localStorage key for persisting the cart session id across page loads.\n * Default `'throttle.cartSessionId'`. Pass `null` to disable persistence.\n */\n storageKey?: string | null;\n /**\n * Auto-recover from a stale cart. When a mutation fails with\n * {@link CartNotOpenError} (the cart was converted by a completed order, or\n * abandoned), the hook starts a fresh session, re-adds the last-known line\n * items and re-selects the shipping method, then retries the operation once.\n * Default `true`. Set `false` to surface {@link CartNotOpenError} instead.\n */\n autoRecover?: boolean;\n}\n\nexport interface UseThrottleCheckoutResult {\n /** Current cart snapshot, or null before the first item / resume. */\n cart: CartSessionCart | null;\n /** Current cart session id, or null before the first item. */\n cartSessionId: string | null;\n /** Coarse lifecycle status — drive your UI off this rather than `cart === null`. */\n status: ThrottleCheckoutStatus;\n /** Last error, or null. */\n error: Error | null;\n /** The selected shipping method, bound to the cart (never a shadow copy). */\n selectedMethod: SelectedShipping | null;\n /** Live totals, bound to the cart. Null before a cart exists. */\n totals: CheckoutTotals | null;\n addItem: (input: AddLineItemInput) => Promise<void>;\n updateItem: (itemId: string, input: UpdateLineItemInput) => Promise<void>;\n removeItem: (itemId: string) => Promise<void>;\n /** Lock a shipping method. One call — the returned cart carries recomputed totals. */\n selectMethod: (input: SelectShippingInput) => Promise<void>;\n clearShipping: () => Promise<void>;\n applyDiscount: (code: string) => Promise<void>;\n removeDiscount: () => Promise<void>;\n /**\n * Hand off to checkout. Returns the hand-off — use `checkoutSessionId` with\n * `<PaymentEmbed sessionId={…}>` (or redirect the buyer to `checkoutUrl`).\n */\n createSession: (input: CheckoutHandoffInput) => Promise<CheckoutHandoff>;\n /** Re-fetch the current cart. */\n refresh: () => Promise<void>;\n /** Forget the current session (a new cart is created on the next item). */\n reset: () => void;\n}\n\nfunction readStored(key: string | null | undefined): string | null {\n if (!key || typeof window === 'undefined') return null;\n try {\n return window.localStorage.getItem(key);\n } catch {\n return null;\n }\n}\n\nfunction writeStored(key: string | null | undefined, value: string | null): void {\n if (!key || typeof window === 'undefined') return;\n try {\n if (value === null) window.localStorage.removeItem(key);\n else window.localStorage.setItem(key, value);\n } catch {\n /* ignore quota / privacy-mode errors */\n }\n}\n\nfunction totalsOf(cart: CartSessionCart | null): CheckoutTotals | null {\n if (!cart) return null;\n return {\n currency: cart.currency,\n subtotal: cart.subtotal,\n discountTotal: cart.discountTotal,\n shippingTotal: cart.shippingTotal,\n taxTotal: cart.taxTotal,\n total: cart.total,\n };\n}\n\n/**\n * Orchestrates a backend-less storefront checkout on top of\n * {@link CartSessionClient}, bundling the pieces every storefront otherwise\n * re-implements:\n *\n * - **Cart as source of truth** — `selectedMethod` and `totals` are read straight\n * off the cart; there is no shadow copy to drift.\n * - **One-call shipping select** — `selectMethod` locks the method and returns the\n * cart with recomputed totals in a single round trip.\n * - **Stale-cart recovery** — a {@link CartNotOpenError} (cart converted by a\n * completed order, or abandoned) transparently rebuilds a fresh session from\n * the last-known line items + shipping and retries (`status === 'recovering'`).\n * - **Session handoff** — `createSession` returns the `checkoutSessionId` for\n * `<PaymentEmbed>`.\n *\n * Address collection is handled inside the checkout embed (the hosted/full\n * checkout collects shipping/billing), so this hook intentionally does not write\n * addresses to the cart.\n *\n * ```tsx\n * const { addItem, selectMethod, totals, createSession, status } =\n * useThrottleCheckout({ applicationId, environmentId, quoteToken });\n * ```\n */\nexport function useThrottleCheckout(\n options: UseThrottleCheckoutOptions,\n): UseThrottleCheckoutResult {\n const { applicationId, environmentId, quoteToken, baseUrl } = options;\n const autoRecover = options.autoRecover ?? true;\n const storageKey = options.storageKey === undefined ? DEFAULT_STORAGE_KEY : options.storageKey;\n\n const [cart, setCart] = useState<CartSessionCart | null>(null);\n const [cartSessionId, setCartSessionId] = useState<string | null>(null);\n const [status, setStatus] = useState<ThrottleCheckoutStatus>('idle');\n const [error, setError] = useState<Error | null>(null);\n\n // Keep the latest cart in a ref so recovery can read the last-known line items\n // without threading it through every callback's dependency list.\n const cartRef = useRef<CartSessionCart | null>(null);\n cartRef.current = cart;\n\n const { client, initError } = useMemo(() => {\n try {\n return {\n client: new CartSessionClient({ applicationId, environmentId, quoteToken, baseUrl }),\n initError: null as Error | null,\n };\n } catch (e) {\n return { client: null as CartSessionClient | null, initError: e as Error };\n }\n }, [applicationId, environmentId, quoteToken, baseUrl]);\n\n useEffect(() => {\n if (initError) {\n setError(initError);\n setStatus('error');\n }\n }, [initError]);\n\n // Resume a persisted session on mount.\n useEffect(() => {\n if (!client) return;\n const saved = readStored(storageKey);\n if (!saved) return;\n let cancelled = false;\n client.resume(saved);\n setCartSessionId(saved);\n setStatus('loading');\n client\n .get()\n .then((s) => {\n if (cancelled) return;\n setCart(s.cart);\n setStatus('ready');\n })\n .catch(() => {\n if (cancelled) return;\n writeStored(storageKey, null);\n client.resume('');\n setCartSessionId(null);\n setCart(null);\n setStatus('idle');\n });\n return () => {\n cancelled = true;\n };\n }, [client, storageKey]);\n\n const ensureSession = useCallback(\n async (input?: CreateCartSessionInput): Promise<void> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n if (client.cartSessionId) return;\n const session = await client.create(input ?? {});\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n setCart(session.cart);\n },\n [client, initError, storageKey],\n );\n\n /**\n * Rebuild a fresh session from the last-known cart so a stale cart doesn't\n * strand the buyer. Re-adds line items and re-selects shipping, then returns\n * the rebuilt cart.\n */\n const rebuild = useCallback(async (): Promise<CartSessionCart> => {\n if (!client) throw initError ?? new Error('CartSessionClient unavailable');\n const prev = cartRef.current;\n // Drop the stale session and create a new one.\n client.resume('');\n setCartSessionId(null);\n const session = await client.create({\n currency: prev?.currency,\n netN: prev?.netN ?? undefined,\n });\n writeStored(storageKey, session.cartSessionId);\n setCartSessionId(session.cartSessionId);\n let next = session.cart;\n for (const item of prev?.lineItems ?? []) {\n next = await client.addItem({\n type: item.type,\n referenceId: item.referenceId ?? undefined,\n name: item.name,\n description: item.description ?? undefined,\n unitPrice: item.unitPrice,\n quantity: item.quantity,\n imageUrl: item.imageUrl ?? undefined,\n metadata: item.metadata,\n });\n }\n const ship = prev?.selectedShipping;\n if (ship) {\n next = await client.selectShipping({\n methodId: ship.methodId,\n displayName: ship.displayName,\n rateAmount: ship.rateAmount,\n currency: ship.currency,\n carrier: ship.carrier ?? undefined,\n serviceCode: ship.serviceCode ?? undefined,\n estimatedDeliveryDays: ship.estimatedDeliveryDays ?? undefined,\n });\n }\n setCart(next);\n return next;\n }, [client, initError, storageKey]);\n\n /**\n * Run a cart op with loading/error bookkeeping. On a stale-cart error, rebuild\n * (when `autoRecover`) and — for forward-progress ops (`retryAfterRecover`) —\n * retry once against the rebuilt session. Item-id-specific ops set\n * `retryAfterRecover: false` because the id no longer exists after rebuild.\n */\n const run = useCallback(\n async <T,>(\n fn: () => Promise<T>,\n opts: { create?: boolean; retryAfterRecover?: boolean } = {},\n ): Promise<T> => {\n if (!client) {\n const e = initError ?? new Error('CartSessionClient unavailable');\n setError(e);\n setStatus('error');\n throw e;\n }\n setStatus('loading');\n setError(null);\n try {\n if (opts.create) await ensureSession();\n const result = await fn();\n setStatus('ready');\n return result;\n } catch (e) {\n const stale = e instanceof CartNotOpenError || e instanceof CartNotFoundError;\n if (stale && autoRecover) {\n try {\n setStatus('recovering');\n await rebuild();\n if (opts.retryAfterRecover) {\n const retried = await fn();\n setStatus('ready');\n return retried;\n }\n // Rebuild-only: the original op targeted a now-gone id. Surface the\n // rebuilt cart and let the caller reconcile against fresh state.\n setStatus('ready');\n const stale2 = new CartNotOpenError({\n message: 'Cart was rebuilt after going stale; retry your action against the new cart.',\n });\n setError(stale2);\n throw stale2;\n } catch (recoverErr) {\n setError(recoverErr as Error);\n setStatus('error');\n throw recoverErr;\n }\n }\n setError(e as Error);\n setStatus('error');\n throw e;\n }\n },\n [client, ensureSession, initError, autoRecover, rebuild],\n );\n\n const addItem = useCallback(\n async (input: AddLineItemInput) => {\n const next = await run(() => client!.addItem(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const updateItem = useCallback(\n async (itemId: string, input: UpdateLineItemInput) => {\n const next = await run(() => client!.updateItem(itemId, input), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const removeItem = useCallback(\n async (itemId: string) => {\n const next = await run(() => client!.removeItem(itemId), { retryAfterRecover: false });\n setCart(next);\n },\n [client, run],\n );\n const selectMethod = useCallback(\n async (input: SelectShippingInput) => {\n const next = await run(() => client!.selectShipping(input), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const clearShipping = useCallback(async () => {\n const next = await run(() => client!.clearShipping(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n const applyDiscount = useCallback(\n async (code: string) => {\n const next = await run(() => client!.applyDiscount(code), { create: true, retryAfterRecover: true });\n setCart(next);\n },\n [client, run],\n );\n const removeDiscount = useCallback(async () => {\n const next = await run(() => client!.removeDiscount(), { retryAfterRecover: true });\n setCart(next);\n }, [client, run]);\n\n const refresh = useCallback(async () => {\n if (!client?.cartSessionId) return;\n const next = await run(async () => (await client.get()).cart);\n setCart(next);\n }, [client, run]);\n\n const createSession = useCallback(\n (input: CheckoutHandoffInput): Promise<CheckoutHandoff> =>\n run(() => client!.checkout(input), { create: true, retryAfterRecover: true }),\n [client, run],\n );\n\n const reset = useCallback(() => {\n writeStored(storageKey, null);\n client?.resume('');\n setCartSessionId(null);\n setCart(null);\n setError(null);\n setStatus('idle');\n }, [client, storageKey]);\n\n return {\n cart,\n cartSessionId,\n status,\n error,\n selectedMethod: cart?.selectedShipping ?? null,\n totals: totalsOf(cart),\n addItem,\n updateItem,\n removeItem,\n selectMethod,\n clearShipping,\n applyDiscount,\n removeDiscount,\n createSession,\n refresh,\n reset,\n };\n}\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useRef,\n useState,\n type ReactNode,\n} from 'react';\nimport { SecureFields } from '@gr4vy/secure-fields';\nimport { resolveChallenge, type ResolveChallengeArgs } from './threeDSecure.js';\n\n/**\n * Lifecycle status for the headless Secure Fields state machine.\n * - `idle` — provider mounted, fields not yet registered\n * - `mounting` — Secure Fields instance constructed, fields registering\n * - `ready` — all required fields registered, submit() may be called\n * - `vaulting` — submit() called, waiting on CARD_VAULT_SUCCESS/FAILURE\n * - `authorizing` — card vaulted, awaiting the merchant's complete() call\n * - `challenging` — complete() returned `requires_action`; Gr4vy's own 3DS\n * iframe (addThreeDSecure) is mounted and running\n * - `complete` — order created\n * - `error` — vault, challenge, or completion failed (see `error`)\n */\nexport type SecureCardStatus =\n | 'idle'\n | 'mounting'\n | 'ready'\n | 'vaulting'\n | 'authorizing'\n | 'challenging'\n | 'complete'\n | 'error';\n\n/** The Task 2 `POST /checkout-sessions/:id/card-session` response shape. */\nexport interface SecureCardSession {\n sessionId: string;\n /**\n * The payment provider instance this session belongs to, straight from\n * `POST /checkout-sessions/:id/card-session`.\n *\n * Either name works. `providerId` is the documented one; `gr4vyId` is the\n * original and is still accepted, because the package shipped with it and\n * dropping it would break consumers already passing the response through.\n */\n providerId?: string;\n /** @deprecated Use `providerId`. Still honoured. */\n gr4vyId?: string;\n environment: 'sandbox' | 'production';\n}\n\nexport interface SecureCardError {\n code: string;\n message: string;\n}\n\nexport type SecureCardFieldKind = 'number' | 'expiry' | 'cvv' | 'postalCode';\n\n/**\n * Everything this package uses off a `SecureFields` instance, declared\n * structurally instead of re-exporting the SDK's own class type.\n *\n * `@gr4vy/secure-fields`'s `exports` map has no `types` condition, so naming\n * its type in an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts` — which is a hard TS7016 for any consumer on\n * `moduleResolution: bundler` with `skipLibCheck: false`, and a silent\n * degradation of `instance` to `any` with `skipLibCheck: true` (the Next\n * default). That hits every installer of this package, not only Secure Fields\n * users. The `paths` override in this package's tsconfig repairs our own\n * compile; it is monorepo-only and does not ship. Same approach as\n * `ThreeDSecureInstance` in `threeDSecure.ts`.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower option and event types stay\n * assignable to the looser ones declared here.\n */\nexport interface SecureFieldsInstance {\n /** Gr4vy's hidden controller iframe, removed when the provider unmounts. */\n controller?: HTMLIFrameElement;\n addCardNumberField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addExpiryDateField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addSecurityCodeField(element: string | HTMLElement, options?: Record<string, unknown>): unknown;\n addField(element: string | HTMLElement, options: Record<string, unknown>): unknown;\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n submit(): void;\n}\n\nexport interface SecureCardContextValue {\n /**\n * The underlying Secure Fields instance — Task 5's field primitives register\n * against it.\n *\n * `null` until the provider's mount effect has constructed it. It cannot be\n * built during render: `new SecureFields()` mutates the document (see the\n * effect in `SecureCardProvider`), and React does not promise a render-phase\n * factory runs exactly once. Consumers that reach for it directly must\n * null-check; the field primitives already wait for it.\n */\n instance: SecureFieldsInstance | null;\n registerField: (kind: SecureCardFieldKind, element: HTMLElement, options?: Record<string, unknown>) => void;\n /**\n * Mount point for Gr4vy's own 3DS challenge iframe (`addThreeDSecure`).\n * Register this once, alongside the card fields; it is only actually\n * mounted if `complete()` gets `requires_action`.\n */\n registerThreeDSecure: (\n element: HTMLElement | string,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ) => void;\n /**\n * Abandons an in-progress challenge (e.g. the buyer closed the challenge\n * UI). Only meaningful while `status === 'challenging'`; settles\n * `complete()`'s promise with `error.code === 'three_ds_abandoned'`, kept\n * distinct from a processor decline code.\n */\n cancelChallenge: () => void;\n status: SecureCardStatus;\n error: SecureCardError | null;\n /** True once all required fields are registered and submit() may be called. */\n ready: boolean;\n /** vault() then complete(). */\n pay: () => Promise<unknown>;\n vault: () => Promise<void>;\n complete: () => Promise<unknown>;\n}\n\nconst SecureCardContext = createContext<SecureCardContextValue | null>(null);\n\n// number, expiry, cvv are required; postalCode is optional.\nconst FIELDS_REQUIRED_TO_BE_READY = 3;\n\nexport interface SecureCardProviderProps {\n session: SecureCardSession;\n /**\n * Throttle's own checkout session id — the `:id` already used to mint\n * `session` via `POST /checkout-sessions/:id/card-session`. NOT the same\n * value as `session.sessionId`: that field is Gr4vy's own checkout session\n * id (required to construct the `SecureFields` instance — see\n * `packages/platform-api/src/routes/card-session.ts`, which returns\n * `{ sessionId: checkoutSessionId, ... }` from `adapter.createCardSession()`,\n * a Gr4vy-minted id). The public `POST /checkout-sessions/:id/complete`\n * route looks `:id` up directly against Throttle's own `checkoutSessions`\n * table (`checkout-sessions.ts`), so `defaultComplete` needs this id, not\n * `session.sessionId`, to call the right URL.\n */\n checkoutSessionId: string;\n children: ReactNode;\n /** Injected for tests / custom completion flows; defaults to a real POST /complete call. */\n completeFn?: (args: { sessionId: string; gr4vySessionId: string }) => Promise<any>;\n /** Injected for tests / custom 3DS UI; defaults to mounting Gr4vy's own 3DS iframe via addThreeDSecure. */\n resolveChallengeFn?: (args: ResolveChallengeArgs) => Promise<void>;\n /**\n * Bounds how long `complete()` waits on an unfinished challenge before\n * settling to a terminal `error` (code `three_ds_timeout`) on its own — the\n * only other exits are Gr4vy's own `THREE_DS_FINISH` and an explicit\n * `cancelChallenge()` call, neither of which is guaranteed to ever fire if\n * the buyer just abandons the tab. Not sourced from Gr4vy documentation\n * (no challenge-duration limit is published in the SDK); 5 minutes is a\n * conservative, overridable default.\n */\n challengeTimeoutMs?: number;\n /**\n * Base URL of the Throttle API (e.g. `https://api.usethrottle.dev`).\n * Required — not optional-with-a-same-origin-default. Secure Fields is the\n * headless surface for merchants who mount the card form on THEIR OWN\n * origin, which by definition is not same-origin with the Throttle API, so\n * `defaultComplete`'s `fetch()` can never resolve a relative path (it 404s\n * against the merchant's own domain at payment time instead). Making this\n * required turns that into a compile error instead of a silent runtime\n * failure. Joined onto the request path regardless of a trailing slash.\n */\n apiBaseUrl: string;\n onSuccess?: (result: SecureCardCompleteResult) => void;\n}\n\n/**\n * The real `POST /checkout-sessions/:id/complete` success body for the\n * Secure Fields flow (`checkout-session-service.ts` `complete()`,\n * proxy-mode-embedded return, ~line 2338 and its idempotent-retry twin at\n * ~line 1849). There is no `order` key.\n */\nexport interface SecureCardCompleteResult {\n orderId: string;\n orderNumber: string;\n paymentId: string;\n /** Minor units (cents). */\n total: number;\n currency: string;\n status: string;\n paymentStatus: string;\n}\n\nconst DEFAULT_CHALLENGE_TIMEOUT_MS = 5 * 60 * 1000;\n\nexport function SecureCardProvider({\n session,\n checkoutSessionId,\n children,\n completeFn,\n resolveChallengeFn,\n challengeTimeoutMs = DEFAULT_CHALLENGE_TIMEOUT_MS,\n apiBaseUrl,\n onSuccess,\n}: SecureCardProviderProps) {\n const [status, setStatus] = useState<SecureCardStatus>('idle');\n const [error, setError] = useState<SecureCardError | null>(null);\n const [mountedCount, setMountedCount] = useState(0);\n const vaultWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n const threeDSElement = useRef<HTMLElement | string | null>(null);\n const threeDSOptions = useRef<{ challengeWindowSize?: '01' | '02' | '03' | '04' | '05' } | undefined>(undefined);\n const challengeWaiter = useRef<{ resolve: () => void; reject: (e: SecureCardError) => void } | null>(null);\n\n const { environment, sessionId } = session;\n // Either name is accepted: `providerId` is documented, `gr4vyId` is the\n // original the package first shipped with. Resolving here means the rest of\n // the hook never has to care which one the caller passed through.\n const providerInstanceId = session.providerId ?? session.gr4vyId ?? '';\n const [instance, setInstance] = useState<SecureFieldsInstance | null>(null);\n /** Mount point for Gr4vy's challenge, as state so arming can be an effect. */\n const [threeDSMount, setThreeDSMount] = useState<HTMLElement | string | null>(null);\n\n /**\n * `new SecureFields()` is a side effect on the document, not a computation.\n * Its constructor removes whatever `iframe#controller[src*=secure-fields]`\n * is already on the page, creates its own, appends it to `document.body`,\n * and resets the SDK's module-level event bus — so it must run exactly once\n * per mounted provider.\n *\n * It used to sit in a `useMemo`. React invokes a memo factory twice under\n * Strict Mode and commits the FIRST value, so the second construction tore\n * the committed instance's own controller iframe off the page. That\n * instance's internal `controllerReady` promise then never resolved, Gr4vy\n * never inserted a single field iframe, and yet `status` still reported\n * `ready` — because `ready` counts our own `registerField` calls, not the\n * SDK's DOM. Every \"verified in a browser\" pass before Task 9a ran against\n * that false positive (Task 9a, Finding 1).\n *\n * Task 4 had already moved `setStatus('mounting')` out of this same memo for\n * exactly this reason and left the SDK construction behind. `useMemo` is not\n * a lifecycle hook; an effect is the only hook React pairs with a cleanup,\n * so a Strict-Mode double-invoke settles on one live, attached instance.\n */\n useEffect(() => {\n const created = new SecureFields({ gr4vyId: providerInstanceId, environment, sessionId });\n setInstance(created);\n // A rebuilt instance has no registered fields. Both the counter and the\n // status derived from it must go back, or a `session` prop change leaves\n // `ready === true` while every field iframe is still being re-created and\n // `submit()` would fire against nothing.\n setMountedCount(0);\n setStatus((current) => (current === 'idle' || current === 'ready' ? 'mounting' : current));\n return () => {\n // The SDK exposes no teardown at all, so do the two things that matter:\n // take its controller iframe off the page, and stop handing the instance\n // out so no field can ever register against a discarded one.\n created.controller?.remove();\n setInstance(null);\n };\n }, [providerInstanceId, environment, sessionId]);\n\n useEffect(() => {\n if (!instance) return;\n\n const handleSuccess = () => {\n vaultWaiter.current?.resolve();\n };\n const handleFailure = (data?: { code?: string; message?: string }) => {\n vaultWaiter.current?.reject({\n code: data?.code ?? 'card_vault_failed',\n message: data?.message ?? 'The card could not be saved.',\n });\n };\n\n // The challenge now happens INSIDE submit(), between `vaulting` and the\n // vault result, so its status is driven by Gr4vy's own events rather than\n // by anything the server says. Without these the UI sits on `vaulting`\n // while a challenge is visibly on screen.\n const handleThreeDSStart = () => setStatus('challenging');\n const handleThreeDSFinish = () => {\n // Only meaningful while a challenge is genuinely outstanding.\n //\n // The provider fires FINISH even for a challenge that was cancelled or\n // failed, and it arrives AFTER the cancellation has settled the flow.\n // Without this guard that late event moved the status to `vaulting` — a\n // state nothing can leave, because the vault already resolved before the\n // challenge began. A buyer who pressed \"cancel\" was left on a spinner\n // forever, with no error and no way back. Observed in a browser; the\n // state machine reads fine without running it.\n if (!challengeWaiter.current) return;\n // Back to waiting on CARD_VAULT_SUCCESS/FAILURE, which follows the\n // challenge. The authentication outcome is not read from here: the\n // server is the authority on whether the money moved.\n setStatus((current) => (current === 'challenging' ? 'vaulting' : current));\n };\n\n instance.addEventListener(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.addEventListener(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.addEventListener(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n\n return () => {\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_SUCCESS, handleSuccess);\n instance.removeEventListener?.(SecureFields.Events.CARD_VAULT_FAILURE, handleFailure);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_START, handleThreeDSStart);\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleThreeDSFinish);\n };\n }, [instance]);\n\n const registerField = useCallback(\n (kind: SecureCardFieldKind, element: HTMLElement, options: Record<string, unknown> = {}) => {\n // Fields render before the provider's mount effect has constructed the\n // instance (React runs child effects before parent effects). They\n // re-register themselves when `instance` appears — see SecureCardFields.\n if (!instance) return;\n if (kind === 'number') instance.addCardNumberField(element, options);\n else if (kind === 'expiry') instance.addExpiryDateField(element, options);\n else if (kind === 'cvv') instance.addSecurityCodeField(element, options);\n else instance.addField(element, { type: 'postalCode', ...options } as any);\n setMountedCount((n) => n + 1);\n },\n [instance],\n );\n\n useEffect(() => {\n if (mountedCount >= FIELDS_REQUIRED_TO_BE_READY) setStatus('ready');\n }, [mountedCount]);\n\n const registerThreeDSecure = useCallback(\n (element: HTMLElement | string, options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' }) => {\n threeDSElement.current = element;\n threeDSOptions.current = options;\n // State as well as the ref: arming 3DS is an effect that has to re-run\n // when the mount point appears, and a ref write does not re-render.\n setThreeDSMount(element);\n },\n [],\n );\n\n /**\n * Arm 3-D Secure as SETUP, before `submit()` — not as a reaction to the\n * server.\n *\n * Gr4vy runs the enrolment check \"at the time of submitting Secure Fields\",\n * so `addThreeDSecure` has to be registered before `submit()` or the\n * challenge can never be raised. The original build called it only after\n * `/complete` answered `requires_action`, which is after `submit()` has\n * already finished — so it never ran, and every sandbox transaction came\n * back `threeDSecure: undefined`.\n *\n * The SDK has no un-register, so this arms once per instance; a changed\n * mount element arrives with a new instance or not at all.\n */\n useEffect(() => {\n if (!instance || !threeDSMount) return;\n try {\n instance.addThreeDSecure(\n threeDSMount,\n threeDSOptions.current?.challengeWindowSize\n ? { challengeWindowSize: threeDSOptions.current.challengeWindowSize }\n : undefined,\n );\n } catch (err) {\n // Arming is best-effort: a card that never needs a challenge must still\n // be chargeable. A card that DOES need one will fail visibly at submit.\n setError({\n code: 'three_ds_arm_failed',\n message: (err as Error)?.message ?? 'Could not arm 3-D Secure.',\n });\n }\n }, [instance, threeDSMount]);\n\n const cancelChallenge = useCallback(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_abandoned',\n message: 'The 3-D Secure challenge was abandoned.',\n });\n }, []);\n\n const vault = useCallback(() => {\n // Nothing to submit to yet. Previously impossible (the instance was built\n // during render); now the provider's first commit has none, and calling\n // `submit()` on nothing would hang `vaulting` forever with no rejection.\n if (!instance) {\n return Promise.reject<void>({\n code: 'not_ready',\n message: 'The card fields are not ready yet.',\n } as SecureCardError);\n }\n\n // A second vault() while one is already in flight would otherwise silently\n // overwrite vaultWaiter.current, orphaning the first caller's promise forever\n // (no resolve, no reject). Reject the second call instead; the first keeps\n // waiting on its own vault event.\n if (vaultWaiter.current) {\n return Promise.reject<void>({\n code: 'vault_in_progress',\n message: 'A card vault is already in progress.',\n } as SecureCardError);\n }\n\n setError(null);\n setStatus('vaulting');\n return new Promise<void>((resolve, reject) => {\n vaultWaiter.current = {\n resolve: () => {\n vaultWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n vaultWaiter.current = null;\n setError(e);\n setStatus('error');\n reject(e);\n },\n };\n instance.submit();\n });\n }, [instance]);\n\n const complete = useCallback(async () => {\n setStatus('authorizing');\n const doComplete = completeFn ?? ((args: { sessionId: string; gr4vySessionId: string }) => defaultComplete({ ...args, apiBaseUrl }));\n const doResolveChallenge = resolveChallengeFn ?? resolveChallenge;\n try {\n let res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n\n if (res?.status === 'requires_action') {\n if (!threeDSElement.current) {\n throw Object.assign(new Error('No 3-D Secure mount element registered (call registerThreeDSecure).'), {\n code: 'three_ds_element_missing',\n });\n }\n if (!instance) {\n throw Object.assign(new Error('The card fields are not ready yet.'), { code: 'not_ready' });\n }\n setStatus('challenging');\n // Gr4vy's own 3DS iframe runs the challenge; this resolves once it\n // reports THREE_DS_FINISH, or rejects if cancelChallenge() is called\n // or the resolver itself fails, or rejects on its own after\n // challengeTimeoutMs so a buyer who just\n // abandons the tab (no finish, no cancel) can never strand complete()\n // forever. It does not tell us success/failure — re-query /complete\n // for that.\n await new Promise<void>((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n challengeWaiter.current?.reject({\n code: 'three_ds_timeout',\n message: 'The 3-D Secure challenge timed out.',\n });\n }, challengeTimeoutMs);\n\n challengeWaiter.current = {\n resolve: () => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n resolve();\n },\n reject: (e) => {\n clearTimeout(timeoutId);\n challengeWaiter.current = null;\n reject(Object.assign(new Error(e.message), { code: e.code }));\n },\n };\n doResolveChallenge({\n instance,\n element: threeDSElement.current!,\n ...(threeDSOptions.current ?? {}),\n }).then(\n () => challengeWaiter.current?.resolve(),\n // `resolveChallenge` calls `instance.addThreeDSecure()` synchronously\n // inside its Promise executor, so an SDK throw rejects here; a\n // merchant-supplied `resolveChallengeFn` can reject too. Without this\n // arm the only exit was `challengeTimeoutMs` — five minutes stuck at\n // `status: 'challenging'`, plus an unhandled rejection. Same\n // code-passthrough convention as `complete()`'s catch: keep a `code`\n // the resolver supplied, otherwise report `three_ds_failed`.\n (err: unknown) =>\n challengeWaiter.current?.reject({\n code: (err as { code?: string })?.code ?? 'three_ds_failed',\n message: (err as Error)?.message ?? 'The 3-D Secure challenge could not be completed.',\n }),\n );\n });\n\n setStatus('authorizing');\n res = await doComplete({ sessionId: checkoutSessionId, gr4vySessionId: session.sessionId });\n if (res?.status === 'requires_action') {\n throw Object.assign(new Error('The payment still requires authentication after the 3-D Secure challenge.'), {\n code: 'three_ds_unresolved',\n });\n }\n }\n\n setStatus('complete');\n onSuccess?.(res as SecureCardCompleteResult);\n return res;\n } catch (err) {\n setError({\n code: (err as { code?: string })?.code ?? 'complete_failed',\n message: (err as Error)?.message ?? 'Payment could not be completed.',\n });\n setStatus('error');\n throw err;\n }\n }, [completeFn, resolveChallengeFn, challengeTimeoutMs, apiBaseUrl, instance, checkoutSessionId, session.sessionId, onSuccess]);\n\n const pay = useCallback(async () => {\n await vault();\n return complete();\n }, [vault, complete]);\n\n const value: SecureCardContextValue = {\n instance,\n registerField,\n registerThreeDSecure,\n cancelChallenge,\n status,\n error,\n // \"The fields are mounted and nothing is in flight\" — NOT `status ===\n // 'ready'`. Deriving it from the status alone made a decline terminal: the\n // only thing that ever sets `ready` is the mount-count effect, which does\n // not re-run after `setStatus('error')`, so `ready` stayed false and the\n // merchant's pay button stayed disabled. A declined buyer could not try\n // another card without reloading the page — and a decline is the single\n // most common failure at a checkout.\n ready:\n mountedCount >= FIELDS_REQUIRED_TO_BE_READY &&\n status !== 'vaulting' &&\n status !== 'authorizing' &&\n status !== 'challenging' &&\n status !== 'complete',\n pay,\n vault,\n complete,\n };\n\n return <SecureCardContext.Provider value={value}>{children}</SecureCardContext.Provider>;\n}\n\n/**\n * Default completion call — POSTs to the public, buyer-facing complete route.\n *\n * `completeSessionSchema` (`packages/platform-api/src/schemas/checkout-sessions.ts`)\n * is `.strict()` and requires `paymentMethod`. The handler\n * (`checkout-session-service.ts` `complete()`) rejects `paymentMethod: 'card'`\n * outright (\"Use 'embedded' ...\"), and for non-proxy-mode `'embedded'`\n * requires `processorCheckoutSessionId` or `processorTransactionId`. Secure\n * Fields vaults the card straight to the Gr4vy checkout session — no embed\n * transaction exists yet — so this is the server-authorize sub-branch: send\n * `processorCheckoutSessionId` = that same Gr4vy session id (`gr4vySessionId`,\n * i.e. `SecureCardSession.sessionId`), which the handler forwards into\n * `adapter.authorize({ token: JSON.stringify({ checkoutSessionId }) })`.\n *\n * `apiBaseUrl` is joined onto the path with `joinApiUrl` so a merchant-supplied\n * value with or without a trailing slash both resolve to the same URL —\n * without it, `https://api.example.com/` + `/api/v1/...` would double the slash.\n */\nasync function defaultComplete({\n sessionId,\n gr4vySessionId,\n apiBaseUrl,\n}: {\n sessionId: string;\n gr4vySessionId: string;\n apiBaseUrl: string;\n}) {\n const res = await fetch(joinApiUrl(apiBaseUrl, `/api/v1/checkout-sessions/${sessionId}/complete`), {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ paymentMethod: 'embedded', processorCheckoutSessionId: gr4vySessionId }),\n });\n const body = await res.json();\n if (!res.ok) {\n throw Object.assign(new Error(body?.error?.message ?? 'Payment could not be completed.'), {\n code: body?.error?.code ?? 'payment_failed',\n });\n }\n return body?.data ?? body;\n}\n\n/** Joins a base URL and a path safely regardless of a trailing slash on `base`. */\nfunction joinApiUrl(base: string, path: string): string {\n return `${base.replace(/\\/+$/, '')}${path}`;\n}\n\nexport function useSecureCard(): SecureCardContextValue {\n const ctx = useContext(SecureCardContext);\n if (!ctx) throw new Error('useSecureCard must be used inside <SecureCardProvider>');\n return ctx;\n}\n\nexport { SecureCardContext };\n","import { SecureFields } from '@gr4vy/secure-fields';\n\n/**\n * Subset of the Secure Fields instance this module needs — real or mocked.\n *\n * Declared structurally rather than as `Pick<SecureFields, …>`: any reference\n * to the SDK's own type from an exported declaration makes tsup emit\n * `import { SecureFields } from '@gr4vy/secure-fields'` into\n * `dist/index.d.ts`, and that package's `exports` map carries no `types`\n * condition — so a consumer on `moduleResolution: bundler` gets TS7016 under\n * `skipLibCheck: false`, or silently degrades the member to `any` under\n * `skipLibCheck: true` (the Next default). The `paths` override in this\n * package's tsconfig repairs our own compile only; it does not ship.\n *\n * Members use method syntax deliberately: TypeScript checks method parameters\n * bivariantly, so the real class's narrower `CombinedEvents` event parameter\n * stays assignable to the `string` declared here.\n */\nexport interface ThreeDSecureInstance {\n addThreeDSecure(\n element: string | HTMLElement,\n options?: { challengeWindowSize?: '01' | '02' | '03' | '04' | '05' },\n ): void;\n addEventListener(event: string, callback: (...args: any[]) => void): void;\n removeEventListener?(event: string, callback: (...args: any[]) => void): void;\n}\n\nexport interface ResolveChallengeArgs {\n /** The `SecureFields` instance already constructed for this session. */\n instance: ThreeDSecureInstance;\n /** Mount point for Gr4vy's own 3DS challenge iframe. */\n element: HTMLElement | string;\n challengeWindowSize?: '01' | '02' | '03' | '04' | '05';\n}\n\n/**\n * Drives a Gr4vy-hosted 3-D Secure challenge to completion using Secure\n * Fields' own `addThreeDSecure`, instead of hand-rolling a modal around\n * `transaction.paymentMethod.approvalUrl`.\n *\n * Why this instead of a modal + approvalUrl + redirect fallback: `addThreeDSecure(element, options)`\n * takes no transaction id and no approval URL — its only inputs are a mount\n * element and an optional `challengeWindowSize`\n * (node_modules/@gr4vy/secure-fields lib/index.d.ts, lib/types.d.ts —\n * `ThreeDSecureOptions = { challengeWindowSize?: ... }`). The bundled\n * implementation (lib/index.js, `class P` / `ThreeDSecure.init`) shows the\n * iframe it creates is `${frameUrl}/threeds.html` with query params\n * `{sessionId, parentOrigin, gr4vyId, environment}` — the exact identifiers\n * `SecureFields` was already constructed with. The challenge is scoped to the\n * Secure Fields *session*, not to anything passed in per-call. Our server\n * creates the transaction against that same session\n * (`paymentMethod: { method: 'checkout-session', id: checkoutSessionId }` —\n * packages/commerce-payment-orchestrator/src/adapters/gr4vy.ts `authorize()`),\n * so this element renders the challenge for that transaction without ever\n * touching `approvalUrl`. `approvalUrl` does not appear anywhere in the SDK\n * bundle (checked via `grep -c approvalUrl lib/index.js` → 0).\n *\n * `THREE_DS_FINISH`'s payload shape is undocumented (absent from the SDK's\n * README; only the bare event name appears in the CHANGELOG and\n * lib/constants.d.ts), so this does not try to read a success/fail flag off\n * it — it only signals \"the challenge UI is done.\" The caller re-queries\n * `/complete` afterwards for the authoritative outcome, which is also the\n * documented re-entry path for this endpoint (`checkout-session-service.ts`'s\n * `complete()` already tolerates re-invocation on the same session — see its\n * \"processing lease\" / pending-order-reuse comments).\n */\nexport function resolveChallenge({ instance, element, challengeWindowSize }: ResolveChallengeArgs): Promise<void> {\n return new Promise((resolve) => {\n const handleFinish = () => {\n instance.removeEventListener?.(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n resolve();\n };\n // Registered before addThreeDSecure() mounts the frame, so a finish that\n // arrives immediately after mount is never missed.\n instance.addEventListener(SecureFields.Events.THREE_DS_FINISH, handleFinish);\n instance.addThreeDSecure(element, challengeWindowSize ? { challengeWindowSize } : undefined);\n });\n}\n","import { useContext, useEffect, useRef, type CSSProperties } from 'react';\nimport { SecureCardContext, type SecureCardFieldKind } from './useSecureCard.js';\n\nexport interface SecureCardFieldProps {\n className?: string;\n style?: CSSProperties;\n /** Gr4vy field options (styles, autoFocus, maskInput, pattern…) passed through unchanged. */\n options?: Record<string, unknown>;\n}\n\n/**\n * Each field is an empty element Gr4vy replaces with its own iframe. We render\n * the container and nothing else: the merchant chose Secure Fields precisely so\n * that the styling is theirs.\n *\n * The element we hand Gr4vy is created with `document.createElement`, NOT the\n * one React renders. Gr4vy's SDK mounts a field with\n * `element.parentNode.replaceChild(itsOwnWrapper, element)` — it *replaces* the\n * node it is given. Handing it the React-managed node left React holding a\n * reference to a node that was no longer in the tree, so unmounting the field\n * threw an uncaught `NotFoundError: Failed to execute 'removeChild'` and killed\n * the page on the first click (Task 9a, Finding 3 — 100% reproducible in a\n * production build). Giving Gr4vy a throwaway child instead means React only\n * ever removes the host `<div>` it rendered itself, and everything Gr4vy put\n * inside goes with it.\n *\n * The merchant's `className`/`style` stay on the host element, and the\n * throwaway child carries `height: 100%` so Gr4vy's wrapper (which copies the\n * `style` attribute off the node it replaces) still fills the merchant's box —\n * the field iframe is `height: 100%` and would otherwise collapse to an\n * iframe's 150px default.\n *\n * Known limitation: Gr4vy's SDK has no unregister (only `removeEventListener`),\n * so a field that unmounts stays registered on the instance. Unmounting an\n * optional field (postal code) and remounting it is safe — re-registering\n * replaces the SDK's own per-type listener and builds a fresh iframe.\n * Unmounting a REQUIRED field (number, expiry, cvv) and then calling `pay()`\n * without remounting it leaves the SDK submitting against a field whose iframe\n * is gone; remount it first.\n */\nfunction useField(kind: SecureCardFieldKind, options?: Record<string, unknown>) {\n const ctx = useContext(SecureCardContext);\n const hostRef = useRef<HTMLDivElement | null>(null);\n const instance = ctx?.instance ?? null;\n\n useEffect(() => {\n const host = hostRef.current;\n // React runs child effects before parent effects, so on the very first\n // commit the provider has not constructed the instance yet. This effect\n // re-runs when it appears.\n if (!host || !ctx || !instance) return;\n\n const target = document.createElement('div');\n target.style.height = '100%';\n host.appendChild(target);\n ctx.registerField(kind, target, options ?? {});\n\n return () => {\n // Drop whatever Gr4vy put in the host (its wrapper replaced `target`),\n // so a remount starts from an empty container instead of stacking a\n // second, dead iframe under the same field.\n while (host.firstChild) host.removeChild(host.firstChild);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [instance]);\n\n return hostRef;\n}\n\nexport function CardNumberField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('number', options)} className={className} style={style} />;\n}\n\nexport function ExpiryField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('expiry', options)} className={className} style={style} />;\n}\n\nexport function SecurityCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('cvv', options)} className={className} style={style} />;\n}\n\nexport function PostalCodeField({ className, style, options }: SecureCardFieldProps) {\n return <div ref={useField('postalCode', options)} className={className} style={style} />;\n}\n"],"mappings":"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","names":["useState","useEffect","useState","useCallback","useRef","useState","jsx","DEFAULT_BASE_URL","useRef","useState","useCallback","useCallback","useEffect","useState","useCallback","useEffect","useMemo","useRef","useState","CartSessionClient","DEFAULT_STORAGE_KEY","readStored","writeStored","useCallback","useEffect","useRef","useState","SecureFields","jsx","useState","useRef","useEffect","SecureFields","useCallback","useContext","useEffect","useRef","jsx","useContext","useRef","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethrottle/checkout-react",
3
- "version": "1.5.0",
3
+ "version": "2.1.0",
4
4
  "description": "React component for embedded Throttle checkout",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@gr4vy/secure-fields": "^2.11.0",
39
- "@usethrottle/cart": "^3.9.1"
39
+ "@usethrottle/cart": "^3.11.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "pnpm run typecheck && tsup",