@appfunnel-dev/sdk 2.0.0-canary.5 → 2.0.0-canary.8
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 +1 -1
- package/dist/{capabilities-7_hy5f5G.d.cts → capabilities-CsnkVwna.d.cts} +34 -1
- package/dist/{capabilities-7_hy5f5G.d.ts → capabilities-CsnkVwna.d.ts} +34 -1
- package/dist/{checkout-7Dy6IedP.d.ts → checkout-3PkKwPd7.d.ts} +14 -14
- package/dist/{checkout-Dz8cGkB_.d.cts → checkout-Cg20l53c.d.cts} +14 -14
- package/dist/{chunk-VAOR77NW.cjs → chunk-FY3TMSRU.cjs} +7 -7
- package/dist/{chunk-VAOR77NW.cjs.map → chunk-FY3TMSRU.cjs.map} +1 -1
- package/dist/{chunk-CFFMZYPE.js → chunk-PWXMQGO6.js} +3 -3
- package/dist/{chunk-CFFMZYPE.js.map → chunk-PWXMQGO6.js.map} +1 -1
- package/dist/{chunk-TNLRQPVY.js → chunk-RVJLR4RS.js} +36 -8
- package/dist/chunk-RVJLR4RS.js.map +1 -0
- package/dist/{chunk-QMAZGLGV.cjs → chunk-RY3LOSVZ.cjs} +37 -7
- package/dist/chunk-RY3LOSVZ.cjs.map +1 -0
- package/dist/{chunk-AXXG3ODP.js → chunk-VW2HVPR4.js} +63 -90
- package/dist/chunk-VW2HVPR4.js.map +1 -0
- package/dist/{chunk-G44BUG6G.cjs → chunk-Y4YNJ2EX.cjs} +65 -93
- package/dist/chunk-Y4YNJ2EX.cjs.map +1 -0
- package/dist/driver-paddle.cjs +22 -22
- package/dist/driver-paddle.d.cts +2 -2
- package/dist/driver-paddle.d.ts +2 -2
- package/dist/driver-paddle.js +2 -2
- package/dist/driver-stripe.cjs +264 -212
- package/dist/driver-stripe.cjs.map +1 -1
- package/dist/driver-stripe.d.cts +19 -3
- package/dist/driver-stripe.d.ts +19 -3
- package/dist/driver-stripe.js +244 -193
- package/dist/driver-stripe.js.map +1 -1
- package/dist/index.cjs +310 -134
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -25
- package/dist/index.d.ts +25 -25
- package/dist/index.js +262 -82
- package/dist/index.js.map +1 -1
- package/dist/{manifest-Dl7faaKe.d.cts → manifest-B3Tdab0M.d.cts} +157 -85
- package/dist/{manifest-Dl7faaKe.d.ts → manifest-B3Tdab0M.d.ts} +157 -85
- package/dist/manifest-entry.cjs +118 -49
- package/dist/manifest-entry.cjs.map +1 -1
- package/dist/manifest-entry.d.cts +12 -29
- package/dist/manifest-entry.d.ts +12 -29
- package/dist/manifest-entry.js +79 -14
- package/dist/manifest-entry.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-AXXG3ODP.js.map +0 -1
- package/dist/chunk-G44BUG6G.cjs.map +0 -1
- package/dist/chunk-QMAZGLGV.cjs.map +0 -1
- package/dist/chunk-TNLRQPVY.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @appfunnel-dev/sdk
|
|
2
2
|
|
|
3
|
-
Headless SDK for building
|
|
3
|
+
Headless SDK for building Appfunnel pages with React. Write funnel pages as React components while the SDK handles payments, tracking, navigation, and variable state.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -26,7 +26,26 @@ interface SurfaceCapability {
|
|
|
26
26
|
wallets: boolean;
|
|
27
27
|
/** Reliability of an off-session upsell after a purchase on this surface. */
|
|
28
28
|
offSession: OffSessionReliability;
|
|
29
|
+
/**
|
|
30
|
+
* Per-surface DRIVER readiness. Absent = renderable now (the provider's driver mounts it).
|
|
31
|
+
* `'planned'` = the provider genuinely ships this surface (cite it in the profile) but OUR
|
|
32
|
+
* driver doesn't render it yet — publish validation FAILS CLOSED on it so we never promise a
|
|
33
|
+
* surface the runtime can't produce. Mirrors the provider-level {@link ProviderProfile.status}
|
|
34
|
+
* seam at surface granularity: implement the driver, drop the flag, and it flips on cleanly.
|
|
35
|
+
*/
|
|
36
|
+
driver?: 'planned';
|
|
29
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* How a provider composes a TRIAL — the scattered provider `if`s made a matrix dimension
|
|
40
|
+
* (§7 item 4). Stripe trials are **composed** (a separate fee/vault step precedes the sub);
|
|
41
|
+
* Paddle trials are **native** (the price itself carries the trial, MoR collects up front):
|
|
42
|
+
* - `paid_composed` — Stripe: trial-fee PaymentIntent → subscription with `trial_period_days`.
|
|
43
|
+
* - `free_vault` — Stripe: SetupIntent vaults the card (no charge) → trial subscription.
|
|
44
|
+
* - `paid_native` — Paddle: the price carries the paid trial; one item, collected up front.
|
|
45
|
+
* - `free_native` — Paddle: native free trial on the price.
|
|
46
|
+
* A provider with an EMPTY `trialShapes` can't run a trial offer (planned providers → fail closed).
|
|
47
|
+
*/
|
|
48
|
+
type TrialShape = 'paid_composed' | 'free_vault' | 'paid_native' | 'free_native';
|
|
30
49
|
interface ProviderProfile {
|
|
31
50
|
/** Merchant-of-Record (handles tax/compliance) vs. a PSP you settle through. */
|
|
32
51
|
isMoR: boolean;
|
|
@@ -44,6 +63,11 @@ interface ProviderProfile {
|
|
|
44
63
|
* can express, so funnels written today stay valid when the driver lands.
|
|
45
64
|
*/
|
|
46
65
|
status: 'full' | 'planned';
|
|
66
|
+
/**
|
|
67
|
+
* TRIAL shapes this provider can run (§7 item 4). Empty = no trial path (fail closed at
|
|
68
|
+
* publish — a paid/free-trial funnel on this provider can't be produced yet).
|
|
69
|
+
*/
|
|
70
|
+
trialShapes: TrialShape[];
|
|
47
71
|
/** Supported surfaces → per-surface capabilities. **Absent = not supported.** */
|
|
48
72
|
surfaces: Partial<Record<CheckoutSurface, SurfaceCapability>>;
|
|
49
73
|
}
|
|
@@ -83,6 +107,15 @@ declare function isOrchestrator(provider: CheckoutProvider): boolean;
|
|
|
83
107
|
* (e.g. Paddle has no `element`).
|
|
84
108
|
*/
|
|
85
109
|
declare function validateCheckout(provider: CheckoutProvider, surface: CheckoutSurface): ValidationResult;
|
|
110
|
+
/** Trial shapes a provider can compose (empty = it can't run a trial offer yet). */
|
|
111
|
+
declare function trialShapesFor(provider: CheckoutProvider): TrialShape[];
|
|
112
|
+
/**
|
|
113
|
+
* Validate that a provider can run a TRIAL (§7 item 4). Pass a specific {@link TrialShape} to
|
|
114
|
+
* check that exact shape; omit it to assert the provider supports *any* trial (the cheap gate
|
|
115
|
+
* check when only the offering's TRIAL-role binding existence is known). A provider with no
|
|
116
|
+
* trial shapes (every planned provider) fails closed.
|
|
117
|
+
*/
|
|
118
|
+
declare function validateTrial(provider: CheckoutProvider, shape?: TrialShape): ValidationResult;
|
|
86
119
|
/**
|
|
87
120
|
* Validate an **off-session upsell** of `kind` after a purchase made on
|
|
88
121
|
* `paywallSurface`. Combines provider support for the kind (Paddle can't stack a
|
|
@@ -111,4 +144,4 @@ declare function checkoutError(category: CheckoutErrorCategory, message: string,
|
|
|
111
144
|
retryable?: boolean;
|
|
112
145
|
}): CheckoutError;
|
|
113
146
|
|
|
114
|
-
export { type CheckoutError as C, INLINE_SURFACES as I, type OffSessionReliability as O, PROVIDER_PROFILES as P, type SurfaceCapability as S, type UpsellKind as U, type ValidationResult as V, type CheckoutErrorCategory as a, type CheckoutIntent as b, type CheckoutProvider as c, type CheckoutSurface as d, type ProviderProfile as e, checkoutError as f, isMerchantOfRecord as g, isOrchestrator as h, isInlineSurface as i, validateUpsell as j, surfacesFor as s, validateCheckout as v };
|
|
147
|
+
export { type CheckoutError as C, INLINE_SURFACES as I, type OffSessionReliability as O, PROVIDER_PROFILES as P, type SurfaceCapability as S, type TrialShape as T, type UpsellKind as U, type ValidationResult as V, type CheckoutErrorCategory as a, type CheckoutIntent as b, type CheckoutProvider as c, type CheckoutSurface as d, type ProviderProfile as e, checkoutError as f, isMerchantOfRecord as g, isOrchestrator as h, isInlineSurface as i, validateUpsell as j, validateTrial as k, surfacesFor as s, trialShapesFor as t, validateCheckout as v };
|
|
@@ -26,7 +26,26 @@ interface SurfaceCapability {
|
|
|
26
26
|
wallets: boolean;
|
|
27
27
|
/** Reliability of an off-session upsell after a purchase on this surface. */
|
|
28
28
|
offSession: OffSessionReliability;
|
|
29
|
+
/**
|
|
30
|
+
* Per-surface DRIVER readiness. Absent = renderable now (the provider's driver mounts it).
|
|
31
|
+
* `'planned'` = the provider genuinely ships this surface (cite it in the profile) but OUR
|
|
32
|
+
* driver doesn't render it yet — publish validation FAILS CLOSED on it so we never promise a
|
|
33
|
+
* surface the runtime can't produce. Mirrors the provider-level {@link ProviderProfile.status}
|
|
34
|
+
* seam at surface granularity: implement the driver, drop the flag, and it flips on cleanly.
|
|
35
|
+
*/
|
|
36
|
+
driver?: 'planned';
|
|
29
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* How a provider composes a TRIAL — the scattered provider `if`s made a matrix dimension
|
|
40
|
+
* (§7 item 4). Stripe trials are **composed** (a separate fee/vault step precedes the sub);
|
|
41
|
+
* Paddle trials are **native** (the price itself carries the trial, MoR collects up front):
|
|
42
|
+
* - `paid_composed` — Stripe: trial-fee PaymentIntent → subscription with `trial_period_days`.
|
|
43
|
+
* - `free_vault` — Stripe: SetupIntent vaults the card (no charge) → trial subscription.
|
|
44
|
+
* - `paid_native` — Paddle: the price carries the paid trial; one item, collected up front.
|
|
45
|
+
* - `free_native` — Paddle: native free trial on the price.
|
|
46
|
+
* A provider with an EMPTY `trialShapes` can't run a trial offer (planned providers → fail closed).
|
|
47
|
+
*/
|
|
48
|
+
type TrialShape = 'paid_composed' | 'free_vault' | 'paid_native' | 'free_native';
|
|
30
49
|
interface ProviderProfile {
|
|
31
50
|
/** Merchant-of-Record (handles tax/compliance) vs. a PSP you settle through. */
|
|
32
51
|
isMoR: boolean;
|
|
@@ -44,6 +63,11 @@ interface ProviderProfile {
|
|
|
44
63
|
* can express, so funnels written today stay valid when the driver lands.
|
|
45
64
|
*/
|
|
46
65
|
status: 'full' | 'planned';
|
|
66
|
+
/**
|
|
67
|
+
* TRIAL shapes this provider can run (§7 item 4). Empty = no trial path (fail closed at
|
|
68
|
+
* publish — a paid/free-trial funnel on this provider can't be produced yet).
|
|
69
|
+
*/
|
|
70
|
+
trialShapes: TrialShape[];
|
|
47
71
|
/** Supported surfaces → per-surface capabilities. **Absent = not supported.** */
|
|
48
72
|
surfaces: Partial<Record<CheckoutSurface, SurfaceCapability>>;
|
|
49
73
|
}
|
|
@@ -83,6 +107,15 @@ declare function isOrchestrator(provider: CheckoutProvider): boolean;
|
|
|
83
107
|
* (e.g. Paddle has no `element`).
|
|
84
108
|
*/
|
|
85
109
|
declare function validateCheckout(provider: CheckoutProvider, surface: CheckoutSurface): ValidationResult;
|
|
110
|
+
/** Trial shapes a provider can compose (empty = it can't run a trial offer yet). */
|
|
111
|
+
declare function trialShapesFor(provider: CheckoutProvider): TrialShape[];
|
|
112
|
+
/**
|
|
113
|
+
* Validate that a provider can run a TRIAL (§7 item 4). Pass a specific {@link TrialShape} to
|
|
114
|
+
* check that exact shape; omit it to assert the provider supports *any* trial (the cheap gate
|
|
115
|
+
* check when only the offering's TRIAL-role binding existence is known). A provider with no
|
|
116
|
+
* trial shapes (every planned provider) fails closed.
|
|
117
|
+
*/
|
|
118
|
+
declare function validateTrial(provider: CheckoutProvider, shape?: TrialShape): ValidationResult;
|
|
86
119
|
/**
|
|
87
120
|
* Validate an **off-session upsell** of `kind` after a purchase made on
|
|
88
121
|
* `paywallSurface`. Combines provider support for the kind (Paddle can't stack a
|
|
@@ -111,4 +144,4 @@ declare function checkoutError(category: CheckoutErrorCategory, message: string,
|
|
|
111
144
|
retryable?: boolean;
|
|
112
145
|
}): CheckoutError;
|
|
113
146
|
|
|
114
|
-
export { type CheckoutError as C, INLINE_SURFACES as I, type OffSessionReliability as O, PROVIDER_PROFILES as P, type SurfaceCapability as S, type UpsellKind as U, type ValidationResult as V, type CheckoutErrorCategory as a, type CheckoutIntent as b, type CheckoutProvider as c, type CheckoutSurface as d, type ProviderProfile as e, checkoutError as f, isMerchantOfRecord as g, isOrchestrator as h, isInlineSurface as i, validateUpsell as j, surfacesFor as s, validateCheckout as v };
|
|
147
|
+
export { type CheckoutError as C, INLINE_SURFACES as I, type OffSessionReliability as O, PROVIDER_PROFILES as P, type SurfaceCapability as S, type TrialShape as T, type UpsellKind as U, type ValidationResult as V, type CheckoutErrorCategory as a, type CheckoutIntent as b, type CheckoutProvider as c, type CheckoutSurface as d, type ProviderProfile as e, checkoutError as f, isMerchantOfRecord as g, isOrchestrator as h, isInlineSurface as i, validateUpsell as j, validateTrial as k, surfacesFor as s, trialShapesFor as t, validateCheckout as v };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { ReactNode, ComponentType } from 'react';
|
|
2
|
-
import { c as CheckoutProvider, b as CheckoutIntent, d as CheckoutSurface, U as UpsellKind, C as CheckoutError, a as CheckoutErrorCategory } from './capabilities-
|
|
2
|
+
import { c as CheckoutProvider, b as CheckoutIntent, d as CheckoutSurface, U as UpsellKind, C as CheckoutError, a as CheckoutErrorCategory } from './capabilities-CsnkVwna.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* v2 commerce primitives — the two intents from phase-3, made trivial.
|
|
6
6
|
*
|
|
7
|
-
* - `<Checkout
|
|
7
|
+
* - `<Checkout offering surface>` — the **purchase** primitive. The `surface`
|
|
8
8
|
* prop picks presentation; some surfaces render **inline** (express wallets,
|
|
9
9
|
* inline card, provider iframe), some **open on click** (sheet/popup/redirect).
|
|
10
10
|
* `<Checkout>` covers both: it renders inline surfaces and renders a trigger
|
|
11
11
|
* button for the rest (`asChild` to use your own button).
|
|
12
|
-
* - `<Upsell
|
|
12
|
+
* - `<Upsell offering>` — the **upsell** primitive. Off-session charge to the
|
|
13
13
|
* saved method, no card re-entry, one tap.
|
|
14
14
|
* - `useCheckout()` — the headless hook under both, always available for a
|
|
15
15
|
* fully-custom CTA.
|
|
@@ -19,15 +19,15 @@ import { c as CheckoutProvider, b as CheckoutIntent, d as CheckoutSurface, U as
|
|
|
19
19
|
* delegates the actual charge/render to an injected {@link CheckoutDriver}
|
|
20
20
|
* (the real one is platform-side; tests use {@link createMockDriver}).
|
|
21
21
|
*
|
|
22
|
-
* Which
|
|
22
|
+
* Which offering is charged is the author's bound variable, captured at render
|
|
23
23
|
* (inline surfaces) or at `open()` time (trigger surfaces).
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
type CheckoutStatus = 'idle' | 'loading' | 'requires_action' | 'success' | 'error';
|
|
27
27
|
interface CheckoutRequest {
|
|
28
|
-
/** The
|
|
28
|
+
/** The offering SLOT the page named (`<Checkout offering="primary">`) — the display/analytics identity. Kept as the wire field `product` at the provider boundary. */
|
|
29
29
|
product: string;
|
|
30
|
-
/** The catalog
|
|
30
|
+
/** The catalog charge KEY charged — the CHARGE identity, resolved from `product` by {@link driverWithCatalog}. */
|
|
31
31
|
catalogKey?: string;
|
|
32
32
|
intent: CheckoutIntent;
|
|
33
33
|
surface?: CheckoutSurface;
|
|
@@ -170,7 +170,7 @@ type FailureRoute = {
|
|
|
170
170
|
* ))
|
|
171
171
|
* ```
|
|
172
172
|
*
|
|
173
|
-
* A modal can also host a whole `<Checkout
|
|
173
|
+
* A modal can also host a whole `<Checkout offering="…" surface="element" />` for
|
|
174
174
|
* an inline downsell — the driver context is app-level, so checkout works inside
|
|
175
175
|
* modals as-is.
|
|
176
176
|
*/
|
|
@@ -200,7 +200,7 @@ interface FailureModalProps {
|
|
|
200
200
|
* next, instead of hand-wiring `onError`:
|
|
201
201
|
*
|
|
202
202
|
* ```tsx
|
|
203
|
-
* <Checkout
|
|
203
|
+
* <Checkout offering="monthly" surface="element" onFailed={{
|
|
204
204
|
* prepaid_card: { go: 'real-card' },
|
|
205
205
|
* insufficient_funds: { modal: DownsellModal },
|
|
206
206
|
* default: { modal: TryAgainModal },
|
|
@@ -257,8 +257,8 @@ interface CheckoutHandle {
|
|
|
257
257
|
*/
|
|
258
258
|
declare function useCheckout(opts?: UseCheckoutOptions): CheckoutHandle;
|
|
259
259
|
interface CheckoutProps extends UseCheckoutOptions {
|
|
260
|
-
/** Logical
|
|
261
|
-
|
|
260
|
+
/** Logical offering slot (the author's bound selection). */
|
|
261
|
+
offering: string;
|
|
262
262
|
surface: CheckoutSurface;
|
|
263
263
|
/** Render your own trigger element instead of the default button (trigger surfaces). */
|
|
264
264
|
asChild?: boolean;
|
|
@@ -270,14 +270,14 @@ interface CheckoutProps extends UseCheckoutOptions {
|
|
|
270
270
|
* The purchase primitive. Three presentation modes by `surface`:
|
|
271
271
|
* - **inline** (`express`/`element`/`embedded`) — the driver renders the
|
|
272
272
|
* provider UI in place (keyed by product so a selection change remounts it);
|
|
273
|
-
* - **SDK sheet** (`sheet`) — a trigger opens the
|
|
273
|
+
* - **SDK sheet** (`sheet`) — a trigger opens the Appfunnel-owned {@link Sheet}
|
|
274
274
|
* and the driver renders the provider element inside it;
|
|
275
275
|
* - **provider-hosted** (`popup`/`redirect`) — a trigger hands off to the
|
|
276
276
|
* provider's own overlay/page.
|
|
277
277
|
*/
|
|
278
|
-
declare function Checkout({
|
|
278
|
+
declare function Checkout({ offering, surface, asChild, children, className, ...options }: CheckoutProps): ReactNode;
|
|
279
279
|
interface UpsellProps extends Omit<UseCheckoutOptions, 'intent'> {
|
|
280
|
-
|
|
280
|
+
offering: string;
|
|
281
281
|
/**
|
|
282
282
|
* If the off-session charge fails, present this surface **on-session** to
|
|
283
283
|
* re-collect a card and charge again — e.g. `fallback="sheet"` opens the
|
|
@@ -296,7 +296,7 @@ interface UpsellProps extends Omit<UseCheckoutOptions, 'intent'> {
|
|
|
296
296
|
* so the retry's own failure routes through `onFailed` (e.g. prepaid_card → a
|
|
297
297
|
* recapture page) instead of dead-ending.
|
|
298
298
|
*/
|
|
299
|
-
declare function Upsell({
|
|
299
|
+
declare function Upsell({ offering, fallback, asChild, children, className, ...options }: UpsellProps): ReactNode;
|
|
300
300
|
/**
|
|
301
301
|
* The dangling settle leg of the `redirect` surface. The server appends
|
|
302
302
|
* `af_checkout={correlationId}` to hosted-checkout success URLs; when the buyer
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { ReactNode, ComponentType } from 'react';
|
|
2
|
-
import { c as CheckoutProvider, b as CheckoutIntent, d as CheckoutSurface, U as UpsellKind, C as CheckoutError, a as CheckoutErrorCategory } from './capabilities-
|
|
2
|
+
import { c as CheckoutProvider, b as CheckoutIntent, d as CheckoutSurface, U as UpsellKind, C as CheckoutError, a as CheckoutErrorCategory } from './capabilities-CsnkVwna.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* v2 commerce primitives — the two intents from phase-3, made trivial.
|
|
6
6
|
*
|
|
7
|
-
* - `<Checkout
|
|
7
|
+
* - `<Checkout offering surface>` — the **purchase** primitive. The `surface`
|
|
8
8
|
* prop picks presentation; some surfaces render **inline** (express wallets,
|
|
9
9
|
* inline card, provider iframe), some **open on click** (sheet/popup/redirect).
|
|
10
10
|
* `<Checkout>` covers both: it renders inline surfaces and renders a trigger
|
|
11
11
|
* button for the rest (`asChild` to use your own button).
|
|
12
|
-
* - `<Upsell
|
|
12
|
+
* - `<Upsell offering>` — the **upsell** primitive. Off-session charge to the
|
|
13
13
|
* saved method, no card re-entry, one tap.
|
|
14
14
|
* - `useCheckout()` — the headless hook under both, always available for a
|
|
15
15
|
* fully-custom CTA.
|
|
@@ -19,15 +19,15 @@ import { c as CheckoutProvider, b as CheckoutIntent, d as CheckoutSurface, U as
|
|
|
19
19
|
* delegates the actual charge/render to an injected {@link CheckoutDriver}
|
|
20
20
|
* (the real one is platform-side; tests use {@link createMockDriver}).
|
|
21
21
|
*
|
|
22
|
-
* Which
|
|
22
|
+
* Which offering is charged is the author's bound variable, captured at render
|
|
23
23
|
* (inline surfaces) or at `open()` time (trigger surfaces).
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
type CheckoutStatus = 'idle' | 'loading' | 'requires_action' | 'success' | 'error';
|
|
27
27
|
interface CheckoutRequest {
|
|
28
|
-
/** The
|
|
28
|
+
/** The offering SLOT the page named (`<Checkout offering="primary">`) — the display/analytics identity. Kept as the wire field `product` at the provider boundary. */
|
|
29
29
|
product: string;
|
|
30
|
-
/** The catalog
|
|
30
|
+
/** The catalog charge KEY charged — the CHARGE identity, resolved from `product` by {@link driverWithCatalog}. */
|
|
31
31
|
catalogKey?: string;
|
|
32
32
|
intent: CheckoutIntent;
|
|
33
33
|
surface?: CheckoutSurface;
|
|
@@ -170,7 +170,7 @@ type FailureRoute = {
|
|
|
170
170
|
* ))
|
|
171
171
|
* ```
|
|
172
172
|
*
|
|
173
|
-
* A modal can also host a whole `<Checkout
|
|
173
|
+
* A modal can also host a whole `<Checkout offering="…" surface="element" />` for
|
|
174
174
|
* an inline downsell — the driver context is app-level, so checkout works inside
|
|
175
175
|
* modals as-is.
|
|
176
176
|
*/
|
|
@@ -200,7 +200,7 @@ interface FailureModalProps {
|
|
|
200
200
|
* next, instead of hand-wiring `onError`:
|
|
201
201
|
*
|
|
202
202
|
* ```tsx
|
|
203
|
-
* <Checkout
|
|
203
|
+
* <Checkout offering="monthly" surface="element" onFailed={{
|
|
204
204
|
* prepaid_card: { go: 'real-card' },
|
|
205
205
|
* insufficient_funds: { modal: DownsellModal },
|
|
206
206
|
* default: { modal: TryAgainModal },
|
|
@@ -257,8 +257,8 @@ interface CheckoutHandle {
|
|
|
257
257
|
*/
|
|
258
258
|
declare function useCheckout(opts?: UseCheckoutOptions): CheckoutHandle;
|
|
259
259
|
interface CheckoutProps extends UseCheckoutOptions {
|
|
260
|
-
/** Logical
|
|
261
|
-
|
|
260
|
+
/** Logical offering slot (the author's bound selection). */
|
|
261
|
+
offering: string;
|
|
262
262
|
surface: CheckoutSurface;
|
|
263
263
|
/** Render your own trigger element instead of the default button (trigger surfaces). */
|
|
264
264
|
asChild?: boolean;
|
|
@@ -270,14 +270,14 @@ interface CheckoutProps extends UseCheckoutOptions {
|
|
|
270
270
|
* The purchase primitive. Three presentation modes by `surface`:
|
|
271
271
|
* - **inline** (`express`/`element`/`embedded`) — the driver renders the
|
|
272
272
|
* provider UI in place (keyed by product so a selection change remounts it);
|
|
273
|
-
* - **SDK sheet** (`sheet`) — a trigger opens the
|
|
273
|
+
* - **SDK sheet** (`sheet`) — a trigger opens the Appfunnel-owned {@link Sheet}
|
|
274
274
|
* and the driver renders the provider element inside it;
|
|
275
275
|
* - **provider-hosted** (`popup`/`redirect`) — a trigger hands off to the
|
|
276
276
|
* provider's own overlay/page.
|
|
277
277
|
*/
|
|
278
|
-
declare function Checkout({
|
|
278
|
+
declare function Checkout({ offering, surface, asChild, children, className, ...options }: CheckoutProps): ReactNode;
|
|
279
279
|
interface UpsellProps extends Omit<UseCheckoutOptions, 'intent'> {
|
|
280
|
-
|
|
280
|
+
offering: string;
|
|
281
281
|
/**
|
|
282
282
|
* If the off-session charge fails, present this surface **on-session** to
|
|
283
283
|
* re-collect a card and charge again — e.g. `fallback="sheet"` opens the
|
|
@@ -296,7 +296,7 @@ interface UpsellProps extends Omit<UseCheckoutOptions, 'intent'> {
|
|
|
296
296
|
* so the retry's own failure routes through `onFailed` (e.g. prepaid_card → a
|
|
297
297
|
* recapture page) instead of dead-ending.
|
|
298
298
|
*/
|
|
299
|
-
declare function Upsell({
|
|
299
|
+
declare function Upsell({ offering, fallback, asChild, children, className, ...options }: UpsellProps): ReactNode;
|
|
300
300
|
/**
|
|
301
301
|
* The dangling settle leg of the `redirect` surface. The server appends
|
|
302
302
|
* `af_checkout={correlationId}` to hosted-checkout success URLs; when the buyer
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkRY3LOSVZ_cjs = require('./chunk-RY3LOSVZ.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
|
|
16
|
+
if (!res.ok) throw chunkRY3LOSVZ_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
|
|
23
|
+
return chunkRY3LOSVZ_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:
|
|
72
|
+
error: chunkRY3LOSVZ_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:
|
|
90
|
+
error: chunkRY3LOSVZ_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-
|
|
106
|
-
//# sourceMappingURL=chunk-
|
|
105
|
+
//# sourceMappingURL=chunk-FY3TMSRU.cjs.map
|
|
106
|
+
//# sourceMappingURL=chunk-FY3TMSRU.cjs.map
|
|
@@ -1 +1 @@
|
|
|
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"]}
|
|
1
|
+
{"version":3,"sources":["../src/drivers/wire.ts"],"names":["checkoutError"],"mappings":"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","file":"chunk-FY3TMSRU.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,4 +1,4 @@
|
|
|
1
|
-
import { checkoutError } from './chunk-
|
|
1
|
+
import { checkoutError } from './chunk-RVJLR4RS.js';
|
|
2
2
|
|
|
3
3
|
// src/drivers/wire.ts
|
|
4
4
|
function endpoint(ctx, path = "") {
|
|
@@ -95,5 +95,5 @@ function leaveForRedirect(url) {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
export { completeWithPoll, leaveForRedirect, postComplete, postSession, settlementToResult, toCheckoutError };
|
|
98
|
-
//# sourceMappingURL=chunk-
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
98
|
+
//# sourceMappingURL=chunk-PWXMQGO6.js.map
|
|
99
|
+
//# sourceMappingURL=chunk-PWXMQGO6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/drivers/wire.ts"],"names":[],"mappings":"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","file":"chunk-CFFMZYPE.js","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":[],"mappings":"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","file":"chunk-PWXMQGO6.js","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"]}
|
|
@@ -10,6 +10,7 @@ function isInlineSurface(surface) {
|
|
|
10
10
|
var ON = { purchase: true, wallets: true, offSession: "reliable" };
|
|
11
11
|
var MANAGED = { purchase: true, wallets: true, offSession: "conditional" };
|
|
12
12
|
var ROUTED = { purchase: true, wallets: true, offSession: "conditional" };
|
|
13
|
+
var PADDLE_EXPRESS = { purchase: true, wallets: true, offSession: "reliable", driver: "planned" };
|
|
13
14
|
var ALL_UPSELLS = ["subscription", "one-time", "upgrade"];
|
|
14
15
|
var PROVIDER_PROFILES = {
|
|
15
16
|
// PSP; you hold the PaymentMethod and fire your own off-session PaymentIntent on
|
|
@@ -22,27 +23,37 @@ var PROVIDER_PROFILES = {
|
|
|
22
23
|
tokenModel: "merchant",
|
|
23
24
|
orchestrator: false,
|
|
24
25
|
status: "full",
|
|
26
|
+
// Composed trials: paid = fee PaymentIntent + sub (trial_period_days); free = SetupIntent vault
|
|
27
|
+
// + trial sub (services/checkout/stripe-trial.ts).
|
|
28
|
+
trialShapes: ["paid_composed", "free_vault"],
|
|
25
29
|
surfaces: { express: ON, element: ON, sheet: ON, embedded: MANAGED, redirect: MANAGED }
|
|
26
30
|
},
|
|
27
|
-
// Merchant-of-Record; card entry is always Paddle's frame → no own element/sheet
|
|
28
|
-
// express
|
|
29
|
-
//
|
|
31
|
+
// Merchant-of-Record; raw card entry is always Paddle's frame → no own element/sheet.
|
|
32
|
+
// `express` IS now present (Apple-Pay-first Express Checkout, shipped 2026-04-27) but is
|
|
33
|
+
// driver-planned (PADDLE_EXPRESS) → publish fails it closed until the driver renders it.
|
|
34
|
+
// Off-session via subscription one-time charge / collection_mode, but **no 2nd concurrent
|
|
35
|
+
// subscription** (upgrade + one-time only), provider-initiated.
|
|
30
36
|
paddle: {
|
|
31
37
|
isMoR: true,
|
|
32
38
|
offSessionUpsells: ["one-time", "upgrade"],
|
|
33
39
|
tokenModel: "provider",
|
|
34
40
|
orchestrator: false,
|
|
35
41
|
status: "full",
|
|
36
|
-
|
|
42
|
+
// Native trials: the price itself carries the trial, Paddle collects up front / vaults
|
|
43
|
+
// (services/checkout/paddle-checkout.ts — "Paddle paid trials are NATIVE").
|
|
44
|
+
trialShapes: ["paid_native", "free_native"],
|
|
45
|
+
surfaces: { embedded: ON, popup: ON, redirect: ON, express: PADDLE_EXPRESS }
|
|
37
46
|
},
|
|
38
47
|
// Merchant-of-Record; iframe-only (no raw card field). Off-session works
|
|
39
48
|
// (provider-initiated, async via webhooks). `sheet` = our chrome hosting their embed.
|
|
49
|
+
// Trial shape not yet researched → empty (fail closed until the driver + profile land).
|
|
40
50
|
whop: {
|
|
41
51
|
isMoR: true,
|
|
42
52
|
offSessionUpsells: ALL_UPSELLS,
|
|
43
53
|
tokenModel: "provider",
|
|
44
54
|
orchestrator: false,
|
|
45
55
|
status: "planned",
|
|
56
|
+
trialShapes: [],
|
|
46
57
|
surfaces: { embedded: ON, redirect: ON, sheet: ON }
|
|
47
58
|
},
|
|
48
59
|
// Pure orchestrator (PSP-side); Headless renders express/element/sheet/embedded.
|
|
@@ -55,6 +66,7 @@ var PROVIDER_PROFILES = {
|
|
|
55
66
|
tokenModel: "merchant",
|
|
56
67
|
orchestrator: true,
|
|
57
68
|
status: "planned",
|
|
69
|
+
trialShapes: [],
|
|
58
70
|
surfaces: { express: ROUTED, element: ROUTED, sheet: ROUTED, embedded: ROUTED }
|
|
59
71
|
},
|
|
60
72
|
// PSP **and** orchestrator (own acquiring + connectors routing across Stripe/Adyen/
|
|
@@ -66,6 +78,7 @@ var PROVIDER_PROFILES = {
|
|
|
66
78
|
tokenModel: "merchant",
|
|
67
79
|
orchestrator: true,
|
|
68
80
|
status: "planned",
|
|
81
|
+
trialShapes: [],
|
|
69
82
|
surfaces: { express: ON, element: ON, sheet: ON, embedded: ON, redirect: ON }
|
|
70
83
|
}
|
|
71
84
|
};
|
|
@@ -81,7 +94,22 @@ function isOrchestrator(provider) {
|
|
|
81
94
|
function validateCheckout(provider, surface) {
|
|
82
95
|
const cap = PROVIDER_PROFILES[provider]?.surfaces[surface];
|
|
83
96
|
if (!cap) return { ok: false, reason: `${provider} has no '${surface}' surface` };
|
|
84
|
-
|
|
97
|
+
if (!cap.purchase) return { ok: false, reason: `'${surface}' can't take a purchase on ${provider}` };
|
|
98
|
+
if (cap.driver === "planned") return { ok: false, reason: `${provider} '${surface}' checkout isn't renderable yet (the provider ships it, but our driver doesn't render it)` };
|
|
99
|
+
return { ok: true };
|
|
100
|
+
}
|
|
101
|
+
function trialShapesFor(provider) {
|
|
102
|
+
return PROVIDER_PROFILES[provider]?.trialShapes ?? [];
|
|
103
|
+
}
|
|
104
|
+
function validateTrial(provider, shape) {
|
|
105
|
+
const profile = PROVIDER_PROFILES[provider];
|
|
106
|
+
if (!profile) return { ok: false, reason: `unknown provider '${provider}'` };
|
|
107
|
+
const shapes = profile.trialShapes;
|
|
108
|
+
if (shapes.length === 0) return { ok: false, reason: `${provider} can't run a trial offer yet` };
|
|
109
|
+
if (shape && !shapes.includes(shape)) {
|
|
110
|
+
return { ok: false, reason: `${provider} doesn't support a '${shape}' trial (it supports: ${shapes.join(", ")})` };
|
|
111
|
+
}
|
|
112
|
+
return { ok: true };
|
|
85
113
|
}
|
|
86
114
|
function validateUpsell(provider, paywallSurface, kind = "subscription") {
|
|
87
115
|
const profile = PROVIDER_PROFILES[provider];
|
|
@@ -105,6 +133,6 @@ function checkoutError(category, message, extra = {}) {
|
|
|
105
133
|
return { category, message, declineCode: extra.declineCode, retryable };
|
|
106
134
|
}
|
|
107
135
|
|
|
108
|
-
export { INLINE_SURFACES, PROVIDER_PROFILES, checkoutError, isInlineSurface, isMerchantOfRecord, isOrchestrator, surfacesFor, validateCheckout, validateUpsell };
|
|
109
|
-
//# sourceMappingURL=chunk-
|
|
110
|
-
//# sourceMappingURL=chunk-
|
|
136
|
+
export { INLINE_SURFACES, PROVIDER_PROFILES, checkoutError, isInlineSurface, isMerchantOfRecord, isOrchestrator, surfacesFor, trialShapesFor, validateCheckout, validateTrial, validateUpsell };
|
|
137
|
+
//# sourceMappingURL=chunk-RVJLR4RS.js.map
|
|
138
|
+
//# sourceMappingURL=chunk-RVJLR4RS.js.map
|