@appfunnel-dev/sdk 2.0.0-canary.3 → 2.0.0-canary.5

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.
Files changed (53) hide show
  1. package/dist/{chunk-AKO6XKXP.js → chunk-AXXG3ODP.js} +10 -3
  2. package/dist/chunk-AXXG3ODP.js.map +1 -0
  3. package/dist/{chunk-M6U3FNRW.js → chunk-CFFMZYPE.js} +3 -3
  4. package/dist/{chunk-M6U3FNRW.js.map → chunk-CFFMZYPE.js.map} +1 -1
  5. package/dist/chunk-EMMSS5I5.cjs +37 -0
  6. package/dist/chunk-EMMSS5I5.cjs.map +1 -0
  7. package/dist/chunk-G3PMV62Z.js +33 -0
  8. package/dist/chunk-G3PMV62Z.js.map +1 -0
  9. package/dist/{chunk-JSRKA375.cjs → chunk-G44BUG6G.cjs} +10 -3
  10. package/dist/chunk-G44BUG6G.cjs.map +1 -0
  11. package/dist/{chunk-LJYLGLFS.cjs → chunk-QMAZGLGV.cjs} +2 -35
  12. package/dist/chunk-QMAZGLGV.cjs.map +1 -0
  13. package/dist/{chunk-YY375F2B.js → chunk-TNLRQPVY.js} +3 -33
  14. package/dist/chunk-TNLRQPVY.js.map +1 -0
  15. package/dist/{chunk-CY4VBSMX.cjs → chunk-VAOR77NW.cjs} +7 -7
  16. package/dist/{chunk-CY4VBSMX.cjs.map → chunk-VAOR77NW.cjs.map} +1 -1
  17. package/dist/chunk-WYUDL4FI.cjs +8 -0
  18. package/dist/chunk-WYUDL4FI.cjs.map +1 -0
  19. package/dist/chunk-ZZJG4EYL.js +6 -0
  20. package/dist/chunk-ZZJG4EYL.js.map +1 -0
  21. package/dist/driver-paddle.cjs +23 -22
  22. package/dist/driver-paddle.cjs.map +1 -1
  23. package/dist/driver-paddle.js +3 -2
  24. package/dist/driver-paddle.js.map +1 -1
  25. package/dist/driver-stripe.cjs +37 -36
  26. package/dist/driver-stripe.cjs.map +1 -1
  27. package/dist/driver-stripe.js +3 -2
  28. package/dist/driver-stripe.js.map +1 -1
  29. package/dist/index.cjs +541 -91
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +151 -75
  32. package/dist/index.d.ts +151 -75
  33. package/dist/index.js +484 -34
  34. package/dist/index.js.map +1 -1
  35. package/dist/{manifest-Cr2y1op6.d.cts → manifest-Dl7faaKe.d.cts} +34 -0
  36. package/dist/{manifest-Cr2y1op6.d.ts → manifest-Dl7faaKe.d.ts} +34 -0
  37. package/dist/manifest-entry.cjs +45 -30
  38. package/dist/manifest-entry.cjs.map +1 -1
  39. package/dist/manifest-entry.d.cts +1 -1
  40. package/dist/manifest-entry.d.ts +1 -1
  41. package/dist/manifest-entry.js +10 -3
  42. package/dist/manifest-entry.js.map +1 -1
  43. package/dist/protocol.cjs +13 -0
  44. package/dist/protocol.cjs.map +1 -0
  45. package/dist/protocol.d.cts +182 -0
  46. package/dist/protocol.d.ts +182 -0
  47. package/dist/protocol.js +4 -0
  48. package/dist/protocol.js.map +1 -0
  49. package/package.json +14 -2
  50. package/dist/chunk-AKO6XKXP.js.map +0 -1
  51. package/dist/chunk-JSRKA375.cjs.map +0 -1
  52. package/dist/chunk-LJYLGLFS.cjs.map +0 -1
  53. package/dist/chunk-YY375F2B.js.map +0 -1
@@ -1,35 +1,5 @@
1
1
  'use strict';
2
2
 
3
- var __create = Object.create;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getProtoOf = Object.getPrototypeOf;
8
- var __hasOwnProp = Object.prototype.hasOwnProperty;
9
- var __commonJS = (cb, mod) => function __require() {
10
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
11
- };
12
- var __export = (target, all) => {
13
- for (var name in all)
14
- __defProp(target, name, { get: all[name], enumerable: true });
15
- };
16
- var __copyProps = (to, from, except, desc) => {
17
- if (from && typeof from === "object" || typeof from === "function") {
18
- for (let key of __getOwnPropNames(from))
19
- if (!__hasOwnProp.call(to, key) && key !== except)
20
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
21
- }
22
- return to;
23
- };
24
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
25
- // If the importer is in node compatibility mode or this is not an ESM
26
- // file that has been converted to a CommonJS file using a Babel-
27
- // compatible transform (i.e. "__esModule" has not been set), then set
28
- // "default" to the CommonJS "module.exports" for node compatibility.
29
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
30
- mod
31
- ));
32
-
33
3
  // src/commerce/capabilities.ts
34
4
  var INLINE_SURFACES = /* @__PURE__ */ new Set([
35
5
  "express",
@@ -139,9 +109,6 @@ function checkoutError(category, message, extra = {}) {
139
109
 
140
110
  exports.INLINE_SURFACES = INLINE_SURFACES;
141
111
  exports.PROVIDER_PROFILES = PROVIDER_PROFILES;
142
- exports.__commonJS = __commonJS;
143
- exports.__export = __export;
144
- exports.__toESM = __toESM;
145
112
  exports.checkoutError = checkoutError;
146
113
  exports.isInlineSurface = isInlineSurface;
147
114
  exports.isMerchantOfRecord = isMerchantOfRecord;
@@ -149,5 +116,5 @@ exports.isOrchestrator = isOrchestrator;
149
116
  exports.surfacesFor = surfacesFor;
150
117
  exports.validateCheckout = validateCheckout;
151
118
  exports.validateUpsell = validateUpsell;
152
- //# sourceMappingURL=chunk-LJYLGLFS.cjs.map
153
- //# sourceMappingURL=chunk-LJYLGLFS.cjs.map
119
+ //# sourceMappingURL=chunk-QMAZGLGV.cjs.map
120
+ //# sourceMappingURL=chunk-QMAZGLGV.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commerce/capabilities.ts"],"names":[],"mappings":"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","file":"chunk-QMAZGLGV.cjs","sourcesContent":["/**\n * The provider capability matrix + checkout error taxonomy — the PURE half of the\n * checkout seam, extracted from checkout.tsx so it ships through the **manifest\n * entry** (no React): the API's publish validation, the editor, AI authoring, and\n * local dev all run the SAME checks the runtime primitives use (phase-3 §3.1b —\n * \"the SDK ships this as data\"). The React half (driver seam, `useCheckout`,\n * `<Checkout>`/`<Upsell>`) stays in ./checkout, which re-exports everything here.\n */\n\nexport type CheckoutSurface =\n | 'express' // wallet quick-buttons (Apple/Google Pay) — inline\n | 'element' // inline card form — inline\n | 'embedded' // provider-managed inline checkout — inline\n | 'sheet' // bottom-sheet popup — opens on click\n | 'popup' // provider-hosted overlay — opens on click\n | 'redirect' // provider-hosted page — opens on click\n\nexport type CheckoutProvider = 'stripe' | 'paddle' | 'whop' | 'primer' | 'solidgate'\nexport type CheckoutIntent = 'purchase' | 'upsell'\n\n/** Surfaces that render inline (vs. those that open on click). */\nexport const INLINE_SURFACES: ReadonlySet<CheckoutSurface> = new Set<CheckoutSurface>([\n 'express',\n 'element',\n 'embedded',\n])\n\nexport function isInlineSurface(surface: CheckoutSurface): boolean {\n return INLINE_SURFACES.has(surface)\n}\n\n/**\n * The kind of off-session upsell (researched against Funnelfox's changelog, which\n * ships these as distinct features). The discriminating real-world constraint is\n * **stacking a 2nd concurrent subscription** — Paddle can't, SolidGate/Stripe can.\n */\nexport type UpsellKind =\n | 'subscription' // charge a NEW, 2nd concurrent subscription off-session (stacking)\n | 'one-time' // charge a one-time add-on off-session\n | 'upgrade' // change the existing subscription's plan\n\n/** Reliability of an off-session upsell charge AFTER a purchase on a surface. */\nexport type OffSessionReliability =\n | 'reliable' // we hold the saved method (Stripe Elements / SolidGate) or the provider charges it (Paddle / Whop API)\n | 'conditional' // possible but may decline — Stripe **managed** checkout (vault charge, SCA not guaranteed) or an orchestrator's PSP-dependent routing (Primer)\n | 'none'\n\nexport interface SurfaceCapability {\n purchase: boolean\n /** Apple/Google Pay available on this surface. */\n wallets: boolean\n /** Reliability of an off-session upsell after a purchase on this surface. */\n offSession: OffSessionReliability\n}\n\nexport interface ProviderProfile {\n /** Merchant-of-Record (handles tax/compliance) vs. a PSP you settle through. */\n isMoR: boolean\n /** Upsell kinds this provider can charge off-session (one-click, no re-entry). */\n offSessionUpsells: UpsellKind[]\n /** Who charges the saved method off-session: you (PSP) or the provider (MoR/managed). */\n tokenModel: 'merchant' | 'provider'\n /** Routes across multiple processors (Primer; SolidGate also offers this) vs. a single acquirer. */\n orchestrator: boolean\n /**\n * Driver tier (Wave-4 decision, Simon 2026-07-02): `full` = a real driver ships\n * (Stripe, Paddle); `planned` = profile only — authoring/publish validate against\n * it, but no charge path exists yet (Whop, Primer, SolidGate). A new provider\n * becomes buildable by writing a driver; its profile already gates what authors\n * can express, so funnels written today stay valid when the driver lands.\n */\n status: 'full' | 'planned'\n /** Supported surfaces → per-surface capabilities. **Absent = not supported.** */\n surfaces: Partial<Record<CheckoutSurface, SurfaceCapability>>\n}\n\n/** Reliable off-session after this surface (we/the provider hold a chargeable method). */\nconst ON: SurfaceCapability = { purchase: true, wallets: true, offSession: 'reliable' }\n/** Stripe-managed checkout: a purchase works, but the off-session upsell may decline. */\nconst MANAGED: SurfaceCapability = { purchase: true, wallets: true, offSession: 'conditional' }\n/** Orchestrator-routed (Primer): off-session depends on which PSP vaulted the method. */\nconst ROUTED: SurfaceCapability = { purchase: true, wallets: true, offSession: 'conditional' }\nconst ALL_UPSELLS: UpsellKind[] = ['subscription', 'one-time', 'upgrade']\n\n/**\n * Provider capabilities (researched 2026-06-23 against provider docs + Funnelfox;\n * trial mechanics re-verified 2026-07-02, doc 09 §4.4).\n *\n * Three real-world corrections to the naive model:\n * 1. **Off-session upsell support is per-provider by *kind*** (`offSessionUpsells`):\n * Paddle can't stack a 2nd concurrent subscription (upgrade + one-time only);\n * Stripe/SolidGate/Whop/Primer can do all kinds.\n * 2. **Off-session *reliability* varies by the paywall surface** (`SurfaceCapability.\n * offSession`): Stripe **managed** checkout (`embedded`/`redirect`) upsells as a\n * direct charge against Stripe's vault → `conditional` (may decline); Elements\n * surfaces are `reliable`. Primer is `conditional` everywhere (PSP-dependent).\n * 3. **Surface availability is a hard constraint.** MoR/managed providers can't\n * expose a raw card field to your own UI: Paddle has no `express`/`element`/your\n * `sheet`; Whop has no raw `element`/`express`; Primer has no hosted `redirect`;\n * Stripe/SolidGate have no `popup`. A missing entry means \"not possible.\"\n */\nexport const PROVIDER_PROFILES: Record<CheckoutProvider, ProviderProfile> = {\n // PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on\n // the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)\n // upsells too, but as a direct charge against Stripe's vaulted card → conditional.\n // No provider \"popup\" surface.\n stripe: {\n isMoR: false, offSessionUpsells: ALL_UPSELLS, tokenModel: 'merchant', orchestrator: false, status: 'full',\n surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED },\n },\n // Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/\n // express. Off-session via subscription one-time charge / collection_mode, but\n // **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.\n paddle: {\n isMoR: true, offSessionUpsells: ['one-time', 'upgrade'], tokenModel: 'provider', orchestrator: false, status: 'full',\n surfaces: { embedded: ON, popup: ON, redirect: ON },\n },\n // Merchant-of-Record; iframe-only (no raw card field). Off-session works\n // (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.\n whop: {\n isMoR: true, offSessionUpsells: ALL_UPSELLS, tokenModel: 'provider', orchestrator: false, status: 'planned',\n surfaces: { embedded: ON, redirect: ON, sheet: ON },\n },\n // Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.\n // No hosted redirect page, no popup checkout. Off-session is PSP-conditional —\n // the vault lives with whichever processor Primer routed to (data matches this\n // comment as of 2026-07-02; it previously said conditional but coded reliable).\n primer: {\n isMoR: false, offSessionUpsells: ALL_UPSELLS, tokenModel: 'merchant', orchestrator: true, status: 'planned',\n surfaces: { express: ROUTED, element: ROUTED, sheet: ROUTED, embedded: ROUTED },\n },\n // PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/\n // PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.\n // Strong native off-session (Recurring API). No popup.\n solidgate: {\n isMoR: false, offSessionUpsells: ALL_UPSELLS, tokenModel: 'merchant', orchestrator: true, status: 'planned',\n surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON },\n },\n}\n\nexport interface ValidationResult {\n ok: boolean\n reason?: string\n /** Non-fatal caveat (e.g. Paddle upgrade-only). */\n note?: string\n}\n\n/** Surfaces a provider supports, for the editor/AI to steer authors. */\nexport function surfacesFor(provider: CheckoutProvider): CheckoutSurface[] {\n return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {}) as CheckoutSurface[]\n}\n\n/** Whether a provider is a Merchant of Record (it owns tax/compliance). */\nexport function isMerchantOfRecord(provider: CheckoutProvider): boolean {\n return PROVIDER_PROFILES[provider]?.isMoR ?? false\n}\n\n/** Whether a provider routes across multiple processors (Primer, SolidGate). */\nexport function isOrchestrator(provider: CheckoutProvider): boolean {\n return PROVIDER_PROFILES[provider]?.orchestrator ?? false\n}\n\n/**\n * Validate a (provider, surface) for a **purchase**: the surface must exist for\n * the provider and accept a payment. Surface availability is the real constraint\n * (e.g. Paddle has no `element`).\n */\nexport function validateCheckout(provider: CheckoutProvider, surface: CheckoutSurface): ValidationResult {\n const cap = PROVIDER_PROFILES[provider]?.surfaces[surface]\n if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` }\n return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` }\n}\n\n/**\n * Validate an **off-session upsell** of `kind` after a purchase made on\n * `paywallSurface`. Combines provider support for the kind (Paddle can't stack a\n * 2nd subscription) with the paywall surface's off-session reliability (Stripe\n * managed checkout is `conditional` — returned as a `note`, not a hard block).\n */\nexport function validateUpsell(\n provider: CheckoutProvider,\n paywallSurface: CheckoutSurface,\n kind: UpsellKind = 'subscription',\n): ValidationResult {\n const profile = PROVIDER_PROFILES[provider]\n if (!profile) return { ok: false, reason: `unknown provider '${provider}'` }\n if (!profile.offSessionUpsells.includes(kind)) {\n const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(', ')})` : ''\n return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` }\n }\n const cap = profile.surfaces[paywallSurface]\n if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` }\n if (cap.offSession === 'none') {\n return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` }\n }\n if (cap.offSession === 'conditional') {\n return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' may decline — prefer a reliable surface or wire an on-session fallback` }\n }\n return { ok: true }\n}\n\n// ── Checkout error taxonomy (pure — drivers + server both normalize into it) ──\n\n/**\n * Normalized failure category — the driver/platform maps each provider's decline\n * codes into one of these so funnels can route/recover by reason. `authentication_\n * required` (3DS/SCA) and `requires_payment_method` are the cases an **off-session**\n * upsell can't satisfy → recover on-session (re-collect a card).\n */\nexport type CheckoutErrorCategory =\n | 'card_declined'\n | 'insufficient_funds'\n | 'expired_card'\n | 'prepaid_card' // card funding is prepaid — trials/subscriptions commonly decline; route to a real-card recapture page\n | 'authentication_required' // SCA/3DS needed — off-session can't challenge\n | 'requires_payment_method' // no usable saved method\n | 'processing_error'\n | 'canceled'\n | 'unknown'\n\nexport interface CheckoutError {\n category: CheckoutErrorCategory\n message: string\n /** Raw provider decline code, if any (e.g. Stripe `card_declined`/`insufficient_funds`). */\n declineCode?: string\n /** Whether re-attempting (e.g. an on-session re-collection) may succeed. */\n retryable: boolean\n}\n\n/** Build a {@link CheckoutError} (driver/test helper). */\nexport function checkoutError(\n category: CheckoutErrorCategory,\n message: string,\n extra: { declineCode?: string; retryable?: boolean } = {},\n): CheckoutError {\n const retryable = extra.retryable ?? (category === 'authentication_required' || category === 'requires_payment_method' || category === 'processing_error')\n return { category, message, declineCode: extra.declineCode, retryable }\n}\n"]}
@@ -1,33 +1,3 @@
1
- var __create = Object.create;
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __getProtoOf = Object.getPrototypeOf;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __commonJS = (cb, mod) => function __require() {
8
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
9
- };
10
- var __export = (target, all) => {
11
- for (var name in all)
12
- __defProp(target, name, { get: all[name], enumerable: true });
13
- };
14
- var __copyProps = (to, from, except, desc) => {
15
- if (from && typeof from === "object" || typeof from === "function") {
16
- for (let key of __getOwnPropNames(from))
17
- if (!__hasOwnProp.call(to, key) && key !== except)
18
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
- }
20
- return to;
21
- };
22
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
- // If the importer is in node compatibility mode or this is not an ESM
24
- // file that has been converted to a CommonJS file using a Babel-
25
- // compatible transform (i.e. "__esModule" has not been set), then set
26
- // "default" to the CommonJS "module.exports" for node compatibility.
27
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
- mod
29
- ));
30
-
31
1
  // src/commerce/capabilities.ts
32
2
  var INLINE_SURFACES = /* @__PURE__ */ new Set([
33
3
  "express",
@@ -135,6 +105,6 @@ function checkoutError(category, message, extra = {}) {
135
105
  return { category, message, declineCode: extra.declineCode, retryable };
136
106
  }
137
107
 
138
- export { INLINE_SURFACES, PROVIDER_PROFILES, __commonJS, __export, __toESM, checkoutError, isInlineSurface, isMerchantOfRecord, isOrchestrator, surfacesFor, validateCheckout, validateUpsell };
139
- //# sourceMappingURL=chunk-YY375F2B.js.map
140
- //# sourceMappingURL=chunk-YY375F2B.js.map
108
+ export { INLINE_SURFACES, PROVIDER_PROFILES, checkoutError, isInlineSurface, isMerchantOfRecord, isOrchestrator, surfacesFor, validateCheckout, validateUpsell };
109
+ //# sourceMappingURL=chunk-TNLRQPVY.js.map
110
+ //# sourceMappingURL=chunk-TNLRQPVY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commerce/capabilities.ts"],"names":[],"mappings":"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","file":"chunk-TNLRQPVY.js","sourcesContent":["/**\n * The provider capability matrix + checkout error taxonomy — the PURE half of the\n * checkout seam, extracted from checkout.tsx so it ships through the **manifest\n * entry** (no React): the API's publish validation, the editor, AI authoring, and\n * local dev all run the SAME checks the runtime primitives use (phase-3 §3.1b —\n * \"the SDK ships this as data\"). The React half (driver seam, `useCheckout`,\n * `<Checkout>`/`<Upsell>`) stays in ./checkout, which re-exports everything here.\n */\n\nexport type CheckoutSurface =\n | 'express' // wallet quick-buttons (Apple/Google Pay) — inline\n | 'element' // inline card form — inline\n | 'embedded' // provider-managed inline checkout — inline\n | 'sheet' // bottom-sheet popup — opens on click\n | 'popup' // provider-hosted overlay — opens on click\n | 'redirect' // provider-hosted page — opens on click\n\nexport type CheckoutProvider = 'stripe' | 'paddle' | 'whop' | 'primer' | 'solidgate'\nexport type CheckoutIntent = 'purchase' | 'upsell'\n\n/** Surfaces that render inline (vs. those that open on click). */\nexport const INLINE_SURFACES: ReadonlySet<CheckoutSurface> = new Set<CheckoutSurface>([\n 'express',\n 'element',\n 'embedded',\n])\n\nexport function isInlineSurface(surface: CheckoutSurface): boolean {\n return INLINE_SURFACES.has(surface)\n}\n\n/**\n * The kind of off-session upsell (researched against Funnelfox's changelog, which\n * ships these as distinct features). The discriminating real-world constraint is\n * **stacking a 2nd concurrent subscription** — Paddle can't, SolidGate/Stripe can.\n */\nexport type UpsellKind =\n | 'subscription' // charge a NEW, 2nd concurrent subscription off-session (stacking)\n | 'one-time' // charge a one-time add-on off-session\n | 'upgrade' // change the existing subscription's plan\n\n/** Reliability of an off-session upsell charge AFTER a purchase on a surface. */\nexport type OffSessionReliability =\n | 'reliable' // we hold the saved method (Stripe Elements / SolidGate) or the provider charges it (Paddle / Whop API)\n | 'conditional' // possible but may decline — Stripe **managed** checkout (vault charge, SCA not guaranteed) or an orchestrator's PSP-dependent routing (Primer)\n | 'none'\n\nexport interface SurfaceCapability {\n purchase: boolean\n /** Apple/Google Pay available on this surface. */\n wallets: boolean\n /** Reliability of an off-session upsell after a purchase on this surface. */\n offSession: OffSessionReliability\n}\n\nexport interface ProviderProfile {\n /** Merchant-of-Record (handles tax/compliance) vs. a PSP you settle through. */\n isMoR: boolean\n /** Upsell kinds this provider can charge off-session (one-click, no re-entry). */\n offSessionUpsells: UpsellKind[]\n /** Who charges the saved method off-session: you (PSP) or the provider (MoR/managed). */\n tokenModel: 'merchant' | 'provider'\n /** Routes across multiple processors (Primer; SolidGate also offers this) vs. a single acquirer. */\n orchestrator: boolean\n /**\n * Driver tier (Wave-4 decision, Simon 2026-07-02): `full` = a real driver ships\n * (Stripe, Paddle); `planned` = profile only — authoring/publish validate against\n * it, but no charge path exists yet (Whop, Primer, SolidGate). A new provider\n * becomes buildable by writing a driver; its profile already gates what authors\n * can express, so funnels written today stay valid when the driver lands.\n */\n status: 'full' | 'planned'\n /** Supported surfaces → per-surface capabilities. **Absent = not supported.** */\n surfaces: Partial<Record<CheckoutSurface, SurfaceCapability>>\n}\n\n/** Reliable off-session after this surface (we/the provider hold a chargeable method). */\nconst ON: SurfaceCapability = { purchase: true, wallets: true, offSession: 'reliable' }\n/** Stripe-managed checkout: a purchase works, but the off-session upsell may decline. */\nconst MANAGED: SurfaceCapability = { purchase: true, wallets: true, offSession: 'conditional' }\n/** Orchestrator-routed (Primer): off-session depends on which PSP vaulted the method. */\nconst ROUTED: SurfaceCapability = { purchase: true, wallets: true, offSession: 'conditional' }\nconst ALL_UPSELLS: UpsellKind[] = ['subscription', 'one-time', 'upgrade']\n\n/**\n * Provider capabilities (researched 2026-06-23 against provider docs + Funnelfox;\n * trial mechanics re-verified 2026-07-02, doc 09 §4.4).\n *\n * Three real-world corrections to the naive model:\n * 1. **Off-session upsell support is per-provider by *kind*** (`offSessionUpsells`):\n * Paddle can't stack a 2nd concurrent subscription (upgrade + one-time only);\n * Stripe/SolidGate/Whop/Primer can do all kinds.\n * 2. **Off-session *reliability* varies by the paywall surface** (`SurfaceCapability.\n * offSession`): Stripe **managed** checkout (`embedded`/`redirect`) upsells as a\n * direct charge against Stripe's vault → `conditional` (may decline); Elements\n * surfaces are `reliable`. Primer is `conditional` everywhere (PSP-dependent).\n * 3. **Surface availability is a hard constraint.** MoR/managed providers can't\n * expose a raw card field to your own UI: Paddle has no `express`/`element`/your\n * `sheet`; Whop has no raw `element`/`express`; Primer has no hosted `redirect`;\n * Stripe/SolidGate have no `popup`. A missing entry means \"not possible.\"\n */\nexport const PROVIDER_PROFILES: Record<CheckoutProvider, ProviderProfile> = {\n // PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on\n // the Elements surfaces (reliable). **Managed** Checkout (`embedded`/`redirect`)\n // upsells too, but as a direct charge against Stripe's vaulted card → conditional.\n // No provider \"popup\" surface.\n stripe: {\n isMoR: false, offSessionUpsells: ALL_UPSELLS, tokenModel: 'merchant', orchestrator: false, status: 'full',\n surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED },\n },\n // Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet/\n // express. Off-session via subscription one-time charge / collection_mode, but\n // **no 2nd concurrent subscription** (upgrade + one-time only), provider-initiated.\n paddle: {\n isMoR: true, offSessionUpsells: ['one-time', 'upgrade'], tokenModel: 'provider', orchestrator: false, status: 'full',\n surfaces: { embedded: ON, popup: ON, redirect: ON },\n },\n // Merchant-of-Record; iframe-only (no raw card field). Off-session works\n // (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.\n whop: {\n isMoR: true, offSessionUpsells: ALL_UPSELLS, tokenModel: 'provider', orchestrator: false, status: 'planned',\n surfaces: { embedded: ON, redirect: ON, sheet: ON },\n },\n // Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.\n // No hosted redirect page, no popup checkout. Off-session is PSP-conditional —\n // the vault lives with whichever processor Primer routed to (data matches this\n // comment as of 2026-07-02; it previously said conditional but coded reliable).\n primer: {\n isMoR: false, offSessionUpsells: ALL_UPSELLS, tokenModel: 'merchant', orchestrator: true, status: 'planned',\n surfaces: { express: ROUTED, element: ROUTED, sheet: ROUTED, embedded: ROUTED },\n },\n // PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/\n // PayPal). One Payment Form (element/sheet/embedded) + hosted page + wallet button.\n // Strong native off-session (Recurring API). No popup.\n solidgate: {\n isMoR: false, offSessionUpsells: ALL_UPSELLS, tokenModel: 'merchant', orchestrator: true, status: 'planned',\n surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON },\n },\n}\n\nexport interface ValidationResult {\n ok: boolean\n reason?: string\n /** Non-fatal caveat (e.g. Paddle upgrade-only). */\n note?: string\n}\n\n/** Surfaces a provider supports, for the editor/AI to steer authors. */\nexport function surfacesFor(provider: CheckoutProvider): CheckoutSurface[] {\n return Object.keys(PROVIDER_PROFILES[provider]?.surfaces ?? {}) as CheckoutSurface[]\n}\n\n/** Whether a provider is a Merchant of Record (it owns tax/compliance). */\nexport function isMerchantOfRecord(provider: CheckoutProvider): boolean {\n return PROVIDER_PROFILES[provider]?.isMoR ?? false\n}\n\n/** Whether a provider routes across multiple processors (Primer, SolidGate). */\nexport function isOrchestrator(provider: CheckoutProvider): boolean {\n return PROVIDER_PROFILES[provider]?.orchestrator ?? false\n}\n\n/**\n * Validate a (provider, surface) for a **purchase**: the surface must exist for\n * the provider and accept a payment. Surface availability is the real constraint\n * (e.g. Paddle has no `element`).\n */\nexport function validateCheckout(provider: CheckoutProvider, surface: CheckoutSurface): ValidationResult {\n const cap = PROVIDER_PROFILES[provider]?.surfaces[surface]\n if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` }\n return cap.purchase ? { ok: true } : { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` }\n}\n\n/**\n * Validate an **off-session upsell** of `kind` after a purchase made on\n * `paywallSurface`. Combines provider support for the kind (Paddle can't stack a\n * 2nd subscription) with the paywall surface's off-session reliability (Stripe\n * managed checkout is `conditional` — returned as a `note`, not a hard block).\n */\nexport function validateUpsell(\n provider: CheckoutProvider,\n paywallSurface: CheckoutSurface,\n kind: UpsellKind = 'subscription',\n): ValidationResult {\n const profile = PROVIDER_PROFILES[provider]\n if (!profile) return { ok: false, reason: `unknown provider '${provider}'` }\n if (!profile.offSessionUpsells.includes(kind)) {\n const can = profile.offSessionUpsells.length ? ` (it supports: ${profile.offSessionUpsells.join(', ')})` : ''\n return { ok: false, reason: `${provider} can't charge a '${kind}' upsell off-session${can}` }\n }\n const cap = profile.surfaces[paywallSurface]\n if (!cap) return { ok: false, reason: `${provider} has no '${paywallSurface}' surface to upsell after` }\n if (cap.offSession === 'none') {\n return { ok: false, reason: `a purchase on '${paywallSurface}' retains no chargeable method for an upsell` }\n }\n if (cap.offSession === 'conditional') {\n return { ok: true, note: `off-session upsell after ${provider} '${paywallSurface}' may decline — prefer a reliable surface or wire an on-session fallback` }\n }\n return { ok: true }\n}\n\n// ── Checkout error taxonomy (pure — drivers + server both normalize into it) ──\n\n/**\n * Normalized failure category — the driver/platform maps each provider's decline\n * codes into one of these so funnels can route/recover by reason. `authentication_\n * required` (3DS/SCA) and `requires_payment_method` are the cases an **off-session**\n * upsell can't satisfy → recover on-session (re-collect a card).\n */\nexport type CheckoutErrorCategory =\n | 'card_declined'\n | 'insufficient_funds'\n | 'expired_card'\n | 'prepaid_card' // card funding is prepaid — trials/subscriptions commonly decline; route to a real-card recapture page\n | 'authentication_required' // SCA/3DS needed — off-session can't challenge\n | 'requires_payment_method' // no usable saved method\n | 'processing_error'\n | 'canceled'\n | 'unknown'\n\nexport interface CheckoutError {\n category: CheckoutErrorCategory\n message: string\n /** Raw provider decline code, if any (e.g. Stripe `card_declined`/`insufficient_funds`). */\n declineCode?: string\n /** Whether re-attempting (e.g. an on-session re-collection) may succeed. */\n retryable: boolean\n}\n\n/** Build a {@link CheckoutError} (driver/test helper). */\nexport function checkoutError(\n category: CheckoutErrorCategory,\n message: string,\n extra: { declineCode?: string; retryable?: boolean } = {},\n): CheckoutError {\n const retryable = extra.retryable ?? (category === 'authentication_required' || category === 'requires_payment_method' || category === 'processing_error')\n return { category, message, declineCode: extra.declineCode, retryable }\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkLJYLGLFS_cjs = require('./chunk-LJYLGLFS.cjs');
3
+ var chunkQMAZGLGV_cjs = require('./chunk-QMAZGLGV.cjs');
4
4
 
5
5
  // src/drivers/wire.ts
6
6
  function endpoint(ctx, path = "") {
@@ -13,14 +13,14 @@ async function postJson(url, body) {
13
13
  headers: { "content-type": "application/json" },
14
14
  body: JSON.stringify(body)
15
15
  });
16
- if (!res.ok) throw chunkLJYLGLFS_cjs.checkoutError("processing_error", `checkout request failed (HTTP ${res.status})`);
16
+ if (!res.ok) throw chunkQMAZGLGV_cjs.checkoutError("processing_error", `checkout request failed (HTTP ${res.status})`);
17
17
  return await res.json();
18
18
  }
19
19
  function toCheckoutError(e) {
20
20
  if (e && typeof e === "object" && "category" in e && "message" in e && "retryable" in e) {
21
21
  return e;
22
22
  }
23
- return chunkLJYLGLFS_cjs.checkoutError("processing_error", e instanceof Error ? e.message : "Checkout request failed");
23
+ return chunkQMAZGLGV_cjs.checkoutError("processing_error", e instanceof Error ? e.message : "Checkout request failed");
24
24
  }
25
25
  function postSession(ctx, req, correlationId) {
26
26
  const body = {
@@ -69,7 +69,7 @@ async function completeWithPoll(ctx, correlationId, opts = {}) {
69
69
  }
70
70
  return {
71
71
  ok: false,
72
- error: chunkLJYLGLFS_cjs.checkoutError(
72
+ error: chunkQMAZGLGV_cjs.checkoutError(
73
73
  "processing_error",
74
74
  opts.timeoutMessage ?? "The payment has not been confirmed yet \u2014 please check back shortly.",
75
75
  // `completion_timeout` marks "the poll ran out while the attempt was still
@@ -87,7 +87,7 @@ function leaveForRedirect(url) {
87
87
  window.removeEventListener("pageshow", onPageshow);
88
88
  resolve({
89
89
  ok: false,
90
- error: chunkLJYLGLFS_cjs.checkoutError("canceled", "Returned from the hosted checkout without completing")
90
+ error: chunkQMAZGLGV_cjs.checkoutError("canceled", "Returned from the hosted checkout without completing")
91
91
  });
92
92
  };
93
93
  window.addEventListener("pageshow", onPageshow);
@@ -102,5 +102,5 @@ exports.postComplete = postComplete;
102
102
  exports.postSession = postSession;
103
103
  exports.settlementToResult = settlementToResult;
104
104
  exports.toCheckoutError = toCheckoutError;
105
- //# sourceMappingURL=chunk-CY4VBSMX.cjs.map
106
- //# sourceMappingURL=chunk-CY4VBSMX.cjs.map
105
+ //# sourceMappingURL=chunk-VAOR77NW.cjs.map
106
+ //# sourceMappingURL=chunk-VAOR77NW.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/drivers/wire.ts"],"names":["checkoutError"],"mappings":"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","file":"chunk-CY4VBSMX.cjs","sourcesContent":["/**\n * Shared wire plumbing for the checkout drivers — the CLIENT half of the v2\n * checkout contract (commerce/checkout-api.ts). Lives under drivers/ (not\n * commerce/) on purpose: it ships only inside the `driver-*` subpath entries,\n * so the core funnel entry never bundles a checkout fetch — the core SDK talks\n * to a {@link CheckoutDriver}, never to the wire.\n *\n * Only `import type` from commerce/checkout(.tsx) here — a runtime import would\n * drag the React checkout primitives into every driver chunk. The one runtime\n * dependency is the pure error taxonomy (capabilities.ts).\n */\nimport type {\n CheckoutCompleteRequest,\n CheckoutCompleteResponse,\n CheckoutSessionRequest,\n CheckoutSessionResponse,\n CheckoutSettlement,\n} from '../commerce/checkout-api'\nimport type { CheckoutDriverContext, CheckoutRequest, CheckoutResult } from '../commerce/checkout'\nimport { checkoutError, type CheckoutError } from '../commerce/capabilities'\n\nfunction endpoint(ctx: CheckoutDriverContext, path = ''): string {\n const base = ctx.apiBase.replace(/\\/+$/, '')\n return `${base}/campaign/${encodeURIComponent(ctx.campaignId)}/v2/checkout${path}`\n}\n\nasync function postJson<T>(url: string, body: unknown): Promise<T> {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n // The contract speaks in `step` responses, not HTTP errors — a non-2xx means the\n // request never reached the checkout service properly (proxy, deploy, outage).\n if (!res.ok) throw checkoutError('processing_error', `checkout request failed (HTTP ${res.status})`)\n return (await res.json()) as T\n}\n\n/** Normalize any thrown value into the taxonomy (network drops → `processing_error`). */\nexport function toCheckoutError(e: unknown): CheckoutError {\n if (e && typeof e === 'object' && 'category' in e && 'message' in e && 'retryable' in e) {\n return e as CheckoutError\n }\n return checkoutError('processing_error', e instanceof Error ? e.message : 'Checkout request failed')\n}\n\n/**\n * Open a checkout attempt: `POST {apiBase}/campaign/{campaignId}/v2/checkout`.\n * The correlationId is client-minted per ATTEMPT (idempotency key + the id the\n * server stamps into provider metadata) — mint it once, reuse it on retries.\n */\nexport function postSession(\n ctx: CheckoutDriverContext,\n req: CheckoutRequest,\n correlationId: string,\n): Promise<CheckoutSessionResponse> {\n const body: CheckoutSessionRequest = {\n funnelId: ctx.funnelId,\n mode: ctx.mode,\n // The CHARGE identity is the catalog key (resolved from the slot by driverWithCatalog); fall back\n // to `product` for legacy builds where the page named the catalog key directly.\n productKey: req.catalogKey ?? req.product,\n intent: req.intent,\n kind: req.kind,\n // null (not absent) = explicit off-session — the server validates surfaces\n // against the capability matrix, so never let one default in transit.\n surface: req.surface ?? null,\n sessionId: ctx.sessionId ?? undefined,\n visitorId: ctx.visitorId ?? undefined,\n // Buyer email collected by the funnel (user.email) — Stripe Customer creation,\n // receipts, Paddle prefill. Injected at attempt time by the FunnelProvider seam.\n email: req.email,\n correlationId,\n }\n return postJson<CheckoutSessionResponse>(endpoint(ctx), body)\n}\n\n/** Ask the server to VERIFY with the provider and write the money facts (idempotent). */\nexport function postComplete(\n ctx: CheckoutDriverContext,\n correlationId: string,\n providerRef?: string,\n): Promise<CheckoutCompleteResponse> {\n const body: CheckoutCompleteRequest = { providerRef }\n return postJson<CheckoutCompleteResponse>(\n endpoint(ctx, `/${encodeURIComponent(correlationId)}/complete`),\n body,\n )\n}\n\n/** Server settlement → the SDK's CheckoutResult (eventId passes through for pixel dedup). */\nexport function settlementToResult(s: CheckoutSettlement): CheckoutResult {\n return { ok: true, amountMinor: s.amountMinor, currency: s.currency, eventId: s.eventId }\n}\n\nexport interface CompletePollOptions {\n providerRef?: string\n /** Total complete attempts before giving up (default 5). */\n attempts?: number\n /** Delay between attempts (default 2000ms). */\n intervalMs?: number\n /** Error message when the provider hasn't confirmed inside the window (MoR/async). */\n timeoutMessage?: string\n}\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))\n\n/**\n * Complete with a bounded poll. Sync providers (Stripe) settle on the first\n * attempt — the server verifies against the provider inline. MoR/async providers\n * (Paddle) settle via webhook, so `pending` is normal: re-poll a few times, then\n * stop holding the UI hostage — the charge may still land server-side, which is\n * why the timeout error is `retryable` and the message should say the receipt\n * can still arrive (the server's money facts don't depend on this browser).\n */\nexport async function completeWithPoll(\n ctx: CheckoutDriverContext,\n correlationId: string,\n opts: CompletePollOptions = {},\n): Promise<CheckoutResult> {\n const attempts = opts.attempts ?? 5\n const intervalMs = opts.intervalMs ?? 2000\n try {\n for (let i = 0; i < attempts; i++) {\n const res = await postComplete(ctx, correlationId, opts.providerRef)\n if (res.step === 'settled') return settlementToResult(res.result)\n if (res.step === 'failed') return { ok: false, error: res.error }\n if (i < attempts - 1) await sleep(intervalMs)\n }\n } catch (e) {\n return { ok: false, error: toCheckoutError(e) }\n }\n return {\n ok: false,\n error: checkoutError(\n 'processing_error',\n opts.timeoutMessage ?? 'The payment has not been confirmed yet — please check back shortly.',\n // `completion_timeout` marks \"the poll ran out while the attempt was still\n // pending\" — drivers key double-charge guards on it (the attempt may still\n // settle server-side; it was never refused).\n { retryable: true, declineCode: 'completion_timeout' },\n ),\n }\n}\n\n/**\n * The `redirect` step hand-off: navigate to the provider-hosted page and return a\n * promise that settles ONLY if the buyer comes back to this very page. The page\n * is leaving — resolving early would fire `purchase.complete`/navigation for a\n * charge that hasn't happened, and rejecting would flash an error during the\n * (successful) hand-off — so while the navigation proceeds the promise stays\n * pending and `useCheckout` stays in `loading`, the truthful UI for a page\n * mid-navigation. The hosted page's SUCCESS return URL re-enters the funnel with\n * `af_checkout={correlationId}` (a fresh load — this promise is gone; the SDK's\n * resume leg settles the attempt).\n *\n * The one way THIS page comes back to life is a bfcache restore (browser Back\n * from the hosted page): `pageshow` with `persisted: true`. That is a buyer who\n * abandoned the hosted checkout — resolve `canceled` so the trigger re-enables\n * and inline redirect placeholders recover instead of spinning forever.\n */\nexport function leaveForRedirect(url: string): Promise<CheckoutResult> {\n return new Promise<CheckoutResult>((resolve) => {\n if (typeof window !== 'undefined') {\n const onPageshow = (event: PageTransitionEvent) => {\n if (!event.persisted) return\n window.removeEventListener('pageshow', onPageshow)\n resolve({\n ok: false,\n error: checkoutError('canceled', 'Returned from the hosted checkout without completing'),\n })\n }\n window.addEventListener('pageshow', onPageshow)\n }\n window.location.assign(url)\n })\n}\n"]}
1
+ {"version":3,"sources":["../src/drivers/wire.ts"],"names":["checkoutError"],"mappings":"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","file":"chunk-VAOR77NW.cjs","sourcesContent":["/**\n * Shared wire plumbing for the checkout drivers — the CLIENT half of the v2\n * checkout contract (commerce/checkout-api.ts). Lives under drivers/ (not\n * commerce/) on purpose: it ships only inside the `driver-*` subpath entries,\n * so the core funnel entry never bundles a checkout fetch — the core SDK talks\n * to a {@link CheckoutDriver}, never to the wire.\n *\n * Only `import type` from commerce/checkout(.tsx) here — a runtime import would\n * drag the React checkout primitives into every driver chunk. The one runtime\n * dependency is the pure error taxonomy (capabilities.ts).\n */\nimport type {\n CheckoutCompleteRequest,\n CheckoutCompleteResponse,\n CheckoutSessionRequest,\n CheckoutSessionResponse,\n CheckoutSettlement,\n} from '../commerce/checkout-api'\nimport type { CheckoutDriverContext, CheckoutRequest, CheckoutResult } from '../commerce/checkout'\nimport { checkoutError, type CheckoutError } from '../commerce/capabilities'\n\nfunction endpoint(ctx: CheckoutDriverContext, path = ''): string {\n const base = ctx.apiBase.replace(/\\/+$/, '')\n return `${base}/campaign/${encodeURIComponent(ctx.campaignId)}/v2/checkout${path}`\n}\n\nasync function postJson<T>(url: string, body: unknown): Promise<T> {\n const res = await fetch(url, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n })\n // The contract speaks in `step` responses, not HTTP errors — a non-2xx means the\n // request never reached the checkout service properly (proxy, deploy, outage).\n if (!res.ok) throw checkoutError('processing_error', `checkout request failed (HTTP ${res.status})`)\n return (await res.json()) as T\n}\n\n/** Normalize any thrown value into the taxonomy (network drops → `processing_error`). */\nexport function toCheckoutError(e: unknown): CheckoutError {\n if (e && typeof e === 'object' && 'category' in e && 'message' in e && 'retryable' in e) {\n return e as CheckoutError\n }\n return checkoutError('processing_error', e instanceof Error ? e.message : 'Checkout request failed')\n}\n\n/**\n * Open a checkout attempt: `POST {apiBase}/campaign/{campaignId}/v2/checkout`.\n * The correlationId is client-minted per ATTEMPT (idempotency key + the id the\n * server stamps into provider metadata) — mint it once, reuse it on retries.\n */\nexport function postSession(\n ctx: CheckoutDriverContext,\n req: CheckoutRequest,\n correlationId: string,\n): Promise<CheckoutSessionResponse> {\n const body: CheckoutSessionRequest = {\n funnelId: ctx.funnelId,\n mode: ctx.mode,\n // The CHARGE identity is the catalog key (resolved from the slot by driverWithCatalog); fall back\n // to `product` for legacy builds where the page named the catalog key directly.\n productKey: req.catalogKey ?? req.product,\n intent: req.intent,\n kind: req.kind,\n // null (not absent) = explicit off-session — the server validates surfaces\n // against the capability matrix, so never let one default in transit.\n surface: req.surface ?? null,\n sessionId: ctx.sessionId ?? undefined,\n visitorId: ctx.visitorId ?? undefined,\n // Buyer email collected by the funnel (user.email) — Stripe Customer creation,\n // receipts, Paddle prefill. Injected at attempt time by the FunnelProvider seam.\n email: req.email,\n correlationId,\n }\n return postJson<CheckoutSessionResponse>(endpoint(ctx), body)\n}\n\n/** Ask the server to VERIFY with the provider and write the money facts (idempotent). */\nexport function postComplete(\n ctx: CheckoutDriverContext,\n correlationId: string,\n providerRef?: string,\n): Promise<CheckoutCompleteResponse> {\n const body: CheckoutCompleteRequest = { providerRef }\n return postJson<CheckoutCompleteResponse>(\n endpoint(ctx, `/${encodeURIComponent(correlationId)}/complete`),\n body,\n )\n}\n\n/** Server settlement → the SDK's CheckoutResult (eventId passes through for pixel dedup). */\nexport function settlementToResult(s: CheckoutSettlement): CheckoutResult {\n return { ok: true, amountMinor: s.amountMinor, currency: s.currency, eventId: s.eventId }\n}\n\nexport interface CompletePollOptions {\n providerRef?: string\n /** Total complete attempts before giving up (default 5). */\n attempts?: number\n /** Delay between attempts (default 2000ms). */\n intervalMs?: number\n /** Error message when the provider hasn't confirmed inside the window (MoR/async). */\n timeoutMessage?: string\n}\n\nconst sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms))\n\n/**\n * Complete with a bounded poll. Sync providers (Stripe) settle on the first\n * attempt — the server verifies against the provider inline. MoR/async providers\n * (Paddle) settle via webhook, so `pending` is normal: re-poll a few times, then\n * stop holding the UI hostage — the charge may still land server-side, which is\n * why the timeout error is `retryable` and the message should say the receipt\n * can still arrive (the server's money facts don't depend on this browser).\n */\nexport async function completeWithPoll(\n ctx: CheckoutDriverContext,\n correlationId: string,\n opts: CompletePollOptions = {},\n): Promise<CheckoutResult> {\n const attempts = opts.attempts ?? 5\n const intervalMs = opts.intervalMs ?? 2000\n try {\n for (let i = 0; i < attempts; i++) {\n const res = await postComplete(ctx, correlationId, opts.providerRef)\n if (res.step === 'settled') return settlementToResult(res.result)\n if (res.step === 'failed') return { ok: false, error: res.error }\n if (i < attempts - 1) await sleep(intervalMs)\n }\n } catch (e) {\n return { ok: false, error: toCheckoutError(e) }\n }\n return {\n ok: false,\n error: checkoutError(\n 'processing_error',\n opts.timeoutMessage ?? 'The payment has not been confirmed yet — please check back shortly.',\n // `completion_timeout` marks \"the poll ran out while the attempt was still\n // pending\" — drivers key double-charge guards on it (the attempt may still\n // settle server-side; it was never refused).\n { retryable: true, declineCode: 'completion_timeout' },\n ),\n }\n}\n\n/**\n * The `redirect` step hand-off: navigate to the provider-hosted page and return a\n * promise that settles ONLY if the buyer comes back to this very page. The page\n * is leaving — resolving early would fire `purchase.complete`/navigation for a\n * charge that hasn't happened, and rejecting would flash an error during the\n * (successful) hand-off — so while the navigation proceeds the promise stays\n * pending and `useCheckout` stays in `loading`, the truthful UI for a page\n * mid-navigation. The hosted page's SUCCESS return URL re-enters the funnel with\n * `af_checkout={correlationId}` (a fresh load — this promise is gone; the SDK's\n * resume leg settles the attempt).\n *\n * The one way THIS page comes back to life is a bfcache restore (browser Back\n * from the hosted page): `pageshow` with `persisted: true`. That is a buyer who\n * abandoned the hosted checkout — resolve `canceled` so the trigger re-enables\n * and inline redirect placeholders recover instead of spinning forever.\n */\nexport function leaveForRedirect(url: string): Promise<CheckoutResult> {\n return new Promise<CheckoutResult>((resolve) => {\n if (typeof window !== 'undefined') {\n const onPageshow = (event: PageTransitionEvent) => {\n if (!event.persisted) return\n window.removeEventListener('pageshow', onPageshow)\n resolve({\n ok: false,\n error: checkoutError('canceled', 'Returned from the hosted checkout without completing'),\n })\n }\n window.addEventListener('pageshow', onPageshow)\n }\n window.location.assign(url)\n })\n}\n"]}
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // src/protocol/index.ts
4
+ var AF_PROTOCOL = 1;
5
+
6
+ exports.AF_PROTOCOL = AF_PROTOCOL;
7
+ //# sourceMappingURL=chunk-WYUDL4FI.cjs.map
8
+ //# sourceMappingURL=chunk-WYUDL4FI.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protocol/index.ts"],"names":[],"mappings":";;;AAsBO,IAAM,WAAA,GAAc","file":"chunk-WYUDL4FI.cjs","sourcesContent":["/**\n * The **Funnel Embed API** wire protocol — the versioned contract for the cross-origin channel\n * between a host (the editor today; 3rd-party embedders later) and a funnel running in a frame.\n *\n * Side-effect-free and (almost) type-only: import it from `@appfunnel-dev/sdk/protocol` on BOTH\n * ends — the funnel side (SDK `EmbedBridge`) and the host side (`@appfunnel/embed` / the dashboard)\n * — so the two can never drift. The runtime that carries these types (the RPC dispatcher, the\n * bridge, the host client) lives elsewhere; this file is just the shared shapes.\n *\n * Two message families ride one envelope:\n * - **methods** — request/response, `id`-correlated (needed the moment anything returns data, e.g.\n * `screenshot`). Host → funnel.\n * - **events** — fire-and-forget notifications. Funnel → host (e.g. `navigation`).\n * A `hello` handshake advertises the protocol major + capability set so an older funnel (pinned to\n * an older SDK via esm.sh) degrades gracefully instead of hanging on an unknown method.\n */\n\n/**\n * Wire-format major. **Frozen** — bump ONLY on a breaking envelope change, which the design tries to\n * make never happen (new capabilities are new `method`/`event` names, not new envelope shapes). A\n * receiver drops any message whose `af` major it doesn't speak.\n */\nexport const AF_PROTOCOL = 1 as const\n\n/**\n * Trust-direction tag — the ONLY vocabulary the preview-host relay gates on (besides origin). **Frozen**:\n * these strings are compiled into every sealed funnel build, so they are renamed NEVER; the protocol\n * evolves via `af`/`kind`/`method`, not by re-tagging. `af-host` = from the host/parent (dashboard/\n * embedder); `af-preview` = from the funnel/embed (the child frame).\n */\nexport type EnvelopeSource = 'af-host' | 'af-preview'\n\ninterface BaseEnvelope {\n /** Wire-format major — see {@link AF_PROTOCOL}. */\n af: typeof AF_PROTOCOL\n /** Who sent it (trust direction). */\n source: EnvelopeSource\n}\n\n/** Host → funnel: invoke a method. Correlate the reply by `id`. */\nexport interface RequestMessage<M extends MethodName = MethodName> extends BaseEnvelope {\n kind: 'request'\n id: string\n method: M\n params: MethodParams<M>\n}\n\n/** Funnel → host: the reply to a {@link RequestMessage}, echoing its `id`. */\nexport interface ResponseMessage extends BaseEnvelope {\n kind: 'response'\n id: string\n ok: boolean\n result?: unknown\n error?: EmbedError\n}\n\n/** Funnel → host: a fire-and-forget notification (no `id`, lossy-tolerant). */\nexport interface EventMessage<E extends EventName = EventName> extends BaseEnvelope {\n kind: 'event'\n event: E\n data: EventData<E>\n}\n\n/** Either side on connect: announce protocol major + capabilities (see the handshake note above). */\nexport interface HelloMessage extends BaseEnvelope {\n kind: 'hello'\n role: 'funnel' | 'host'\n /** Wire major this end speaks (== `af`). Negotiated dialect = min of both ends. */\n protocol: number\n /** Diagnostics only (e.g. '2.0.0-canary.5'). Never gate behavior on this — use `capabilities`. */\n sdkVersion?: string\n capabilities: Capabilities\n}\n\nexport type EmbedMessage = RequestMessage | ResponseMessage | EventMessage | HelloMessage\n\nexport interface EmbedError {\n code: 'unknown_method' | 'bad_params' | 'not_allowed' | 'failed' | 'timeout'\n message: string\n}\n\n// ── Capability registries ────────────────────────────────\n// Adding a capability = one entry here (+ its handler). No envelope change, no relay change.\n\n/** Host → funnel request/response methods, namespaced by domain. */\nexport interface Methods {\n /** Current flow position (replaces the old fire-and-forget `nav-request`). */\n 'nav.snapshot': { params: void; result: NavSnapshot }\n /** Jump to a page key. Resolves when accepted (unknown key → `bad_params` error); the resulting\n * position arrives as a `navigation` event, since the funnel navigates asynchronously. */\n 'nav.goTo': { params: { key: string }; result: void }\n 'nav.goNext': { params: void; result: void }\n 'nav.goPrevious': { params: void; result: void }\n /** Capture the running funnel to an image (see {@link ScreenshotParams}). */\n screenshot: { params: ScreenshotParams; result: ScreenshotResult }\n}\n\n/** Funnel → host fire-and-forget events. */\nexport interface Events {\n /** The flow moved — pushed on every page change (incl. the initial page). Always on. */\n navigation: NavSnapshot\n}\n\nexport type MethodName = keyof Methods\nexport type MethodParams<M extends MethodName> = Methods[M]['params']\nexport type MethodResult<M extends MethodName> = Methods[M]['result']\nexport type EventName = keyof Events\nexport type EventData<E extends EventName> = Events[E]\n\n/** What an end implements — a SET of tokens (a funnel can be on any historical SDK), not a version int. */\nexport interface Capabilities {\n methods: MethodName[]\n events: EventName[]\n}\n\n// ── Shared data shapes ───────────────────────────────────\n\n/** A live snapshot of the funnel's flow position — the single source of truth for both surfaces. */\nexport interface NavSnapshot {\n /** Current page key. */\n key: string\n /** Zero-based position in the visitor's traversed path. */\n index: number\n /** Expected total steps on the branch the current answers imply. */\n total: number\n /** 0..100 progress along that expected path (matches PageContext.progressPercentage). */\n progressPercentage: number\n /** Whether `nav.goPrevious` would move. */\n canGoBack: boolean\n /** Page keys the visitor could advance to next. */\n nextCandidates: string[]\n}\n\n/** What region of the funnel to capture. Default `{ kind: 'page' }` (the funnel root, full height). */\nexport type ScreenshotTarget =\n | { kind: 'page' }\n | { kind: 'viewport' }\n | { kind: 'selector'; selector: string }\n\nexport interface ScreenshotParams {\n target?: ScreenshotTarget\n /** Encoded image format. Default 'png'. */\n format?: 'png' | 'jpeg' | 'webp'\n /** Lossy quality 0..1 for jpeg/webp (ignored for png). Default 0.92. */\n quality?: number\n /** How the bytes cross postMessage. Default 'blob' (no base64 inflation across the two-hop relay). */\n encoding?: 'blob' | 'dataURL'\n /** Device-pixel scale. Default 2 (deterministic, NOT the live devicePixelRatio). Clamped to `maxScale`. */\n scale?: number\n /** Upper bound on `scale` (bounds payload size). Default 3. */\n maxScale?: number\n /** Explicit output box in CSS px (pre-scale). Default = the target's rendered box. */\n width?: number\n height?: number\n /** Painted under transparent regions. Default '#ffffff'. `null` → transparent (png/webp only). */\n backgroundColor?: string | null\n /** await document.fonts.ready before capture. Default true. */\n waitForFonts?: boolean\n /** await in-tree images decoding + inline them. Default true. */\n waitForImages?: boolean\n /** Scroll the target through its height first to trigger lazy/IO images (restores scroll). Default false. */\n triggerLazyLoad?: boolean\n /** Selectors hidden during capture (dev overlays, the \"compiling…\" chip, cursors). Default []. */\n hideSelectors?: string[]\n /** Hard cap (ms) on the whole capture incl. asset waits. Default 10000. */\n timeout?: number\n}\n\nexport interface ScreenshotResult {\n mime: string\n /** Produced pixel dimensions (post-scale). */\n width: number\n height: number\n /** The scale actually used (after the maxScale clamp). */\n scale: number\n /** Payload size, for the caller to guard / for telemetry. */\n bytesApprox: number\n /** Exactly one of these is set, per `params.encoding`. */\n blob?: Blob\n dataURL?: string\n}\n"]}
@@ -0,0 +1,6 @@
1
+ // src/protocol/index.ts
2
+ var AF_PROTOCOL = 1;
3
+
4
+ export { AF_PROTOCOL };
5
+ //# sourceMappingURL=chunk-ZZJG4EYL.js.map
6
+ //# sourceMappingURL=chunk-ZZJG4EYL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/protocol/index.ts"],"names":[],"mappings":";AAsBO,IAAM,WAAA,GAAc","file":"chunk-ZZJG4EYL.js","sourcesContent":["/**\n * The **Funnel Embed API** wire protocol — the versioned contract for the cross-origin channel\n * between a host (the editor today; 3rd-party embedders later) and a funnel running in a frame.\n *\n * Side-effect-free and (almost) type-only: import it from `@appfunnel-dev/sdk/protocol` on BOTH\n * ends — the funnel side (SDK `EmbedBridge`) and the host side (`@appfunnel/embed` / the dashboard)\n * — so the two can never drift. The runtime that carries these types (the RPC dispatcher, the\n * bridge, the host client) lives elsewhere; this file is just the shared shapes.\n *\n * Two message families ride one envelope:\n * - **methods** — request/response, `id`-correlated (needed the moment anything returns data, e.g.\n * `screenshot`). Host → funnel.\n * - **events** — fire-and-forget notifications. Funnel → host (e.g. `navigation`).\n * A `hello` handshake advertises the protocol major + capability set so an older funnel (pinned to\n * an older SDK via esm.sh) degrades gracefully instead of hanging on an unknown method.\n */\n\n/**\n * Wire-format major. **Frozen** — bump ONLY on a breaking envelope change, which the design tries to\n * make never happen (new capabilities are new `method`/`event` names, not new envelope shapes). A\n * receiver drops any message whose `af` major it doesn't speak.\n */\nexport const AF_PROTOCOL = 1 as const\n\n/**\n * Trust-direction tag — the ONLY vocabulary the preview-host relay gates on (besides origin). **Frozen**:\n * these strings are compiled into every sealed funnel build, so they are renamed NEVER; the protocol\n * evolves via `af`/`kind`/`method`, not by re-tagging. `af-host` = from the host/parent (dashboard/\n * embedder); `af-preview` = from the funnel/embed (the child frame).\n */\nexport type EnvelopeSource = 'af-host' | 'af-preview'\n\ninterface BaseEnvelope {\n /** Wire-format major — see {@link AF_PROTOCOL}. */\n af: typeof AF_PROTOCOL\n /** Who sent it (trust direction). */\n source: EnvelopeSource\n}\n\n/** Host → funnel: invoke a method. Correlate the reply by `id`. */\nexport interface RequestMessage<M extends MethodName = MethodName> extends BaseEnvelope {\n kind: 'request'\n id: string\n method: M\n params: MethodParams<M>\n}\n\n/** Funnel → host: the reply to a {@link RequestMessage}, echoing its `id`. */\nexport interface ResponseMessage extends BaseEnvelope {\n kind: 'response'\n id: string\n ok: boolean\n result?: unknown\n error?: EmbedError\n}\n\n/** Funnel → host: a fire-and-forget notification (no `id`, lossy-tolerant). */\nexport interface EventMessage<E extends EventName = EventName> extends BaseEnvelope {\n kind: 'event'\n event: E\n data: EventData<E>\n}\n\n/** Either side on connect: announce protocol major + capabilities (see the handshake note above). */\nexport interface HelloMessage extends BaseEnvelope {\n kind: 'hello'\n role: 'funnel' | 'host'\n /** Wire major this end speaks (== `af`). Negotiated dialect = min of both ends. */\n protocol: number\n /** Diagnostics only (e.g. '2.0.0-canary.5'). Never gate behavior on this — use `capabilities`. */\n sdkVersion?: string\n capabilities: Capabilities\n}\n\nexport type EmbedMessage = RequestMessage | ResponseMessage | EventMessage | HelloMessage\n\nexport interface EmbedError {\n code: 'unknown_method' | 'bad_params' | 'not_allowed' | 'failed' | 'timeout'\n message: string\n}\n\n// ── Capability registries ────────────────────────────────\n// Adding a capability = one entry here (+ its handler). No envelope change, no relay change.\n\n/** Host → funnel request/response methods, namespaced by domain. */\nexport interface Methods {\n /** Current flow position (replaces the old fire-and-forget `nav-request`). */\n 'nav.snapshot': { params: void; result: NavSnapshot }\n /** Jump to a page key. Resolves when accepted (unknown key → `bad_params` error); the resulting\n * position arrives as a `navigation` event, since the funnel navigates asynchronously. */\n 'nav.goTo': { params: { key: string }; result: void }\n 'nav.goNext': { params: void; result: void }\n 'nav.goPrevious': { params: void; result: void }\n /** Capture the running funnel to an image (see {@link ScreenshotParams}). */\n screenshot: { params: ScreenshotParams; result: ScreenshotResult }\n}\n\n/** Funnel → host fire-and-forget events. */\nexport interface Events {\n /** The flow moved — pushed on every page change (incl. the initial page). Always on. */\n navigation: NavSnapshot\n}\n\nexport type MethodName = keyof Methods\nexport type MethodParams<M extends MethodName> = Methods[M]['params']\nexport type MethodResult<M extends MethodName> = Methods[M]['result']\nexport type EventName = keyof Events\nexport type EventData<E extends EventName> = Events[E]\n\n/** What an end implements — a SET of tokens (a funnel can be on any historical SDK), not a version int. */\nexport interface Capabilities {\n methods: MethodName[]\n events: EventName[]\n}\n\n// ── Shared data shapes ───────────────────────────────────\n\n/** A live snapshot of the funnel's flow position — the single source of truth for both surfaces. */\nexport interface NavSnapshot {\n /** Current page key. */\n key: string\n /** Zero-based position in the visitor's traversed path. */\n index: number\n /** Expected total steps on the branch the current answers imply. */\n total: number\n /** 0..100 progress along that expected path (matches PageContext.progressPercentage). */\n progressPercentage: number\n /** Whether `nav.goPrevious` would move. */\n canGoBack: boolean\n /** Page keys the visitor could advance to next. */\n nextCandidates: string[]\n}\n\n/** What region of the funnel to capture. Default `{ kind: 'page' }` (the funnel root, full height). */\nexport type ScreenshotTarget =\n | { kind: 'page' }\n | { kind: 'viewport' }\n | { kind: 'selector'; selector: string }\n\nexport interface ScreenshotParams {\n target?: ScreenshotTarget\n /** Encoded image format. Default 'png'. */\n format?: 'png' | 'jpeg' | 'webp'\n /** Lossy quality 0..1 for jpeg/webp (ignored for png). Default 0.92. */\n quality?: number\n /** How the bytes cross postMessage. Default 'blob' (no base64 inflation across the two-hop relay). */\n encoding?: 'blob' | 'dataURL'\n /** Device-pixel scale. Default 2 (deterministic, NOT the live devicePixelRatio). Clamped to `maxScale`. */\n scale?: number\n /** Upper bound on `scale` (bounds payload size). Default 3. */\n maxScale?: number\n /** Explicit output box in CSS px (pre-scale). Default = the target's rendered box. */\n width?: number\n height?: number\n /** Painted under transparent regions. Default '#ffffff'. `null` → transparent (png/webp only). */\n backgroundColor?: string | null\n /** await document.fonts.ready before capture. Default true. */\n waitForFonts?: boolean\n /** await in-tree images decoding + inline them. Default true. */\n waitForImages?: boolean\n /** Scroll the target through its height first to trigger lazy/IO images (restores scroll). Default false. */\n triggerLazyLoad?: boolean\n /** Selectors hidden during capture (dev overlays, the \"compiling…\" chip, cursors). Default []. */\n hideSelectors?: string[]\n /** Hard cap (ms) on the whole capture incl. asset waits. Default 10000. */\n timeout?: number\n}\n\nexport interface ScreenshotResult {\n mime: string\n /** Produced pixel dimensions (post-scale). */\n width: number\n height: number\n /** The scale actually used (after the maxScale clamp). */\n scale: number\n /** Payload size, for the caller to guard / for telemetry. */\n bytesApprox: number\n /** Exactly one of these is set, per `params.encoding`. */\n blob?: Blob\n dataURL?: string\n}\n"]}
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- var chunkCY4VBSMX_cjs = require('./chunk-CY4VBSMX.cjs');
4
- var chunkLJYLGLFS_cjs = require('./chunk-LJYLGLFS.cjs');
3
+ var chunkVAOR77NW_cjs = require('./chunk-VAOR77NW.cjs');
4
+ var chunkQMAZGLGV_cjs = require('./chunk-QMAZGLGV.cjs');
5
+ require('./chunk-EMMSS5I5.cjs');
5
6
  var react = require('react');
6
7
 
7
8
  // ../../node_modules/@paddle/paddle-js/dist/index.esm.js
@@ -637,12 +638,12 @@ async function openCheckout(ctx, session, correlationId, display) {
637
638
  if (!session.transactionId && !session.items) {
638
639
  return {
639
640
  ok: false,
640
- error: chunkLJYLGLFS_cjs.checkoutError("processing_error", "provider_checkout carried neither items nor a transactionId")
641
+ error: chunkQMAZGLGV_cjs.checkoutError("processing_error", "provider_checkout carried neither items nor a transactionId")
641
642
  };
642
643
  }
643
644
  const paddle = await getPaddle(session.clientToken, session.providerEnvironment);
644
645
  if (!paddle) {
645
- return { ok: false, error: chunkLJYLGLFS_cjs.checkoutError("processing_error", "Paddle.js failed to initialize") };
646
+ return { ok: false, error: chunkQMAZGLGV_cjs.checkoutError("processing_error", "Paddle.js failed to initialize") };
646
647
  }
647
648
  return await new Promise((resolve) => {
648
649
  let done = false;
@@ -652,7 +653,7 @@ async function openCheckout(ctx, session, correlationId, display) {
652
653
  if (event.name === "checkout.completed") {
653
654
  done = true;
654
655
  listeners.delete(listener);
655
- void chunkCY4VBSMX_cjs.completeWithPoll(ctx, correlationId, {
656
+ void chunkVAOR77NW_cjs.completeWithPoll(ctx, correlationId, {
656
657
  attempts: 10,
657
658
  intervalMs: 3e3,
658
659
  timeoutMessage: PADDLE_TIMEOUT_MESSAGE
@@ -660,7 +661,7 @@ async function openCheckout(ctx, session, correlationId, display) {
660
661
  } else if (event.name === "checkout.closed") {
661
662
  done = true;
662
663
  listeners.delete(listener);
663
- resolve({ ok: false, error: chunkLJYLGLFS_cjs.checkoutError("canceled", "Checkout was closed before completing") });
664
+ resolve({ ok: false, error: chunkQMAZGLGV_cjs.checkoutError("canceled", "Checkout was closed before completing") });
664
665
  }
665
666
  };
666
667
  listeners.add(listener);
@@ -688,29 +689,29 @@ function paddleCheckoutDriver(ctx) {
688
689
  if (prior) {
689
690
  pendingTimeouts.delete(priorKey);
690
691
  try {
691
- const check = await chunkCY4VBSMX_cjs.postComplete(ctx, prior);
692
- if (check.step === "settled") return chunkCY4VBSMX_cjs.settlementToResult(check.result);
692
+ const check = await chunkVAOR77NW_cjs.postComplete(ctx, prior);
693
+ if (check.step === "settled") return chunkVAOR77NW_cjs.settlementToResult(check.result);
693
694
  } catch {
694
695
  }
695
696
  }
696
697
  const correlationId = crypto.randomUUID();
697
698
  try {
698
- const session = await chunkCY4VBSMX_cjs.postSession(ctx, req, correlationId);
699
+ const session = await chunkVAOR77NW_cjs.postSession(ctx, req, correlationId);
699
700
  switch (session.step) {
700
701
  case "settled":
701
- return chunkCY4VBSMX_cjs.settlementToResult(session.result);
702
+ return chunkVAOR77NW_cjs.settlementToResult(session.result);
702
703
  case "pending":
703
704
  return rememberIfStillPending(
704
705
  req,
705
706
  correlationId,
706
- await chunkCY4VBSMX_cjs.completeWithPoll(ctx, correlationId, {
707
+ await chunkVAOR77NW_cjs.completeWithPoll(ctx, correlationId, {
707
708
  timeoutMessage: PADDLE_PENDING_TIMEOUT_MESSAGE
708
709
  })
709
710
  );
710
711
  case "failed":
711
712
  return { ok: false, error: session.error };
712
713
  case "redirect":
713
- return chunkCY4VBSMX_cjs.leaveForRedirect(session.url);
714
+ return chunkVAOR77NW_cjs.leaveForRedirect(session.url);
714
715
  case "provider_checkout":
715
716
  return rememberIfStillPending(
716
717
  req,
@@ -720,14 +721,14 @@ function paddleCheckoutDriver(ctx) {
720
721
  default:
721
722
  return {
722
723
  ok: false,
723
- error: chunkLJYLGLFS_cjs.checkoutError(
724
+ error: chunkQMAZGLGV_cjs.checkoutError(
724
725
  "processing_error",
725
726
  `Paddle returned '${session.step}' for a trigger start \u2014 Paddle has no in-page confirm step`
726
727
  )
727
728
  };
728
729
  }
729
730
  } catch (e) {
730
- return { ok: false, error: chunkCY4VBSMX_cjs.toCheckoutError(e) };
731
+ return { ok: false, error: chunkVAOR77NW_cjs.toCheckoutError(e) };
731
732
  }
732
733
  },
733
734
  renderInline(req, cb) {
@@ -736,7 +737,7 @@ function paddleCheckoutDriver(ctx) {
736
737
  // Redirect-return resume: verify an existing attempt. Webhook-dependent, so it
737
738
  // gets the same ~10×3s window the overlay completion poll uses.
738
739
  resume(correlationId) {
739
- return chunkCY4VBSMX_cjs.completeWithPoll(ctx, correlationId, {
740
+ return chunkVAOR77NW_cjs.completeWithPoll(ctx, correlationId, {
740
741
  attempts: 10,
741
742
  intervalMs: 3e3,
742
743
  timeoutMessage: PADDLE_TIMEOUT_MESSAGE
@@ -758,7 +759,7 @@ function PaddleInline({
758
759
  let disposed = false;
759
760
  void (async () => {
760
761
  try {
761
- const session = await chunkCY4VBSMX_cjs.postSession(ctx, req, correlationId);
762
+ const session = await chunkVAOR77NW_cjs.postSession(ctx, req, correlationId);
762
763
  if (disposed) return;
763
764
  switch (session.step) {
764
765
  case "provider_checkout": {
@@ -772,29 +773,29 @@ function PaddleInline({
772
773
  });
773
774
  if (disposed) return;
774
775
  if (result.ok) cbRef.current.onSuccess(result);
775
- else cbRef.current.onError(result.error ?? chunkLJYLGLFS_cjs.checkoutError("unknown", "Checkout failed"));
776
+ else cbRef.current.onError(result.error ?? chunkQMAZGLGV_cjs.checkoutError("unknown", "Checkout failed"));
776
777
  return;
777
778
  }
778
779
  case "settled":
779
- cbRef.current.onSuccess(chunkCY4VBSMX_cjs.settlementToResult(session.result));
780
+ cbRef.current.onSuccess(chunkVAOR77NW_cjs.settlementToResult(session.result));
780
781
  return;
781
782
  case "failed":
782
783
  cbRef.current.onError(session.error);
783
784
  return;
784
785
  case "redirect": {
785
- const result = await chunkCY4VBSMX_cjs.leaveForRedirect(session.url);
786
+ const result = await chunkVAOR77NW_cjs.leaveForRedirect(session.url);
786
787
  if (!disposed && !result.ok) {
787
- cbRef.current.onError(result.error ?? chunkLJYLGLFS_cjs.checkoutError("canceled", "Checkout was canceled"));
788
+ cbRef.current.onError(result.error ?? chunkQMAZGLGV_cjs.checkoutError("canceled", "Checkout was canceled"));
788
789
  }
789
790
  return;
790
791
  }
791
792
  default:
792
793
  cbRef.current.onError(
793
- chunkLJYLGLFS_cjs.checkoutError("processing_error", "unexpected step for a Paddle inline surface")
794
+ chunkQMAZGLGV_cjs.checkoutError("processing_error", "unexpected step for a Paddle inline surface")
794
795
  );
795
796
  }
796
797
  } catch (e) {
797
- if (!disposed) cbRef.current.onError(chunkCY4VBSMX_cjs.toCheckoutError(e));
798
+ if (!disposed) cbRef.current.onError(chunkVAOR77NW_cjs.toCheckoutError(e));
798
799
  }
799
800
  })();
800
801
  return () => {