@funnelsgrove/payments 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -13,7 +13,7 @@ Read this file before editing checkout code. Deeper implementation docs:
13
13
 
14
14
  ## Current Checkout Path
15
15
 
16
- Render every visible wallet placement with `StripeSubscriptionWalletSurface`. It is the normal current path for Apple Pay and Google Pay:
16
+ Render every visible wallet placement with `StripeSubscriptionWalletSurface` or `StripeOneTimeWalletSurface`, matching the resolved plan's payment mode. They are the normal current path for Apple Pay and Google Pay:
17
17
 
18
18
  - Each surface owns one private Checkout Session per simultaneously visible wallet placement, isolated by its own `surfaceId`.
19
19
  - Simultaneously visible placements may use the same plan and coupon; nothing is shared because each surface keeps its own session.
@@ -29,6 +29,8 @@ Use these for new subscription paywalls:
29
29
 
30
30
  Use these for new one-time paywall plans:
31
31
 
32
+ - `StripeOneTimeWalletSurface`
33
+ - `useStripeOneTimeCheckoutSession`
32
34
  - `createStripeOneTimeCheckout`
33
35
  - `SharedStripeCheckoutV2Dialog`
34
36
 
@@ -46,7 +48,7 @@ provider-neutral status endpoint finds canonical shared-ledger payment evidence.
46
48
 
47
49
  ## Advanced Wallet Primitives
48
50
 
49
- `ApplePaySubscriptionCheckoutSlot`, `GooglePaySubscriptionCheckoutSlot`, and `WalletSubscriptionCheckoutSlot` stay exported and runtime-compatible. They are backward-compatible advanced low-level primitives: reach for them only when a placement needs wiring `StripeSubscriptionWalletSurface` does not cover. Never share one `useStripeSubscriptionCheckoutSession` result across placements; give each visible placement its own session.
51
+ `ApplePaySubscriptionCheckoutSlot`, `GooglePaySubscriptionCheckoutSlot`, and `WalletSubscriptionCheckoutSlot` stay exported and runtime-compatible. They are backward-compatible advanced low-level primitives: reach for them only when a placement needs wiring the shared wallet surfaces do not cover. Never share one checkout hook result across placements; give each visible placement its own session.
50
52
 
51
53
  ## Compatibility-Only Surfaces
52
54
 
@@ -106,11 +108,11 @@ Funnels should call `resolvePublishedBillingRuntime` once at their runtime-provi
106
108
  - Stripe owns real card fields and wallet controls. Local wallet buttons are placeholders until Stripe confirms availability.
107
109
  - Keep `planId` as the funnel plan key and send Stripe's price id only as `providerPlanId`; the API uses both fields to resolve the active test/live offer mapping.
108
110
  - Wallet unavailable state must fall back to card/manual checkout or disappear.
109
- - Count `checkout_started` only at shared UI boundaries: when a shared card dialog opens or when Stripe reports an Apple Pay / Google Pay button click.
111
+ - Count `checkout_started` only at shared UI boundaries: when a shared card dialog opens or when Stripe reports Apple Pay / Google Pay intent. Checkout Sessions use confirmation as a fallback because Stripe omits their click callback.
110
112
  - Pass `checkoutAnalytics` to every current shared dialog and wallet surface. The package then owns `checkout_started`, `add_payment_info`, and verified `checkout_completed`, all deduplicated by the actual Checkout Session.
111
113
  - Call funnel lifecycle `completeStep` only from the shared checkout `onSuccess` callback. Payment-info submission is not step completion.
112
114
  - Wallet slots may create render-only Checkout Sessions on mount so Apple Pay and Google Pay render before the visitor taps. Session creation is technical preparation and must never emit `checkout_started`.
113
- - Shared checkout analytics deduplicates `checkout_started` by Checkout Session id. Card submit and wallet confirmation must not emit it again.
115
+ - Shared checkout analytics deduplicates `checkout_started` by Checkout Session id. Wallet confirmation may retry the start signal when the click callback is unavailable, without creating a duplicate.
114
116
  - Do not call `publicAnalyticsSdk.trackCheckoutStarted`, `trackPaymentInfoSubmitted`, or `trackCheckoutCompleted` directly from funnel checkout components.
115
117
  - Funnel paywalls should style wallet buttons through the shared slot API: `className` for the Stripe button/placeholder, `slotClassName` for the outer slot, `appearance` for Stripe Elements appearance, and `options` for Stripe Express Checkout options.
116
118
  - Keep discount math reusable here, but keep offer activation timing in funnel code.
@@ -5,8 +5,8 @@ export type ApplePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionCheck
5
5
  placeholderLabel?: string;
6
6
  };
7
7
  /**
8
- * @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
9
- * exported and runtime-compatible as an advanced low-level primitive; its checkout session must
10
- * never be shared across placements.
8
+ * @deprecated Prefer StripeSubscriptionWalletSurface or StripeOneTimeWalletSurface for visible
9
+ * wallet placements. This slot stays exported and runtime-compatible as an advanced low-level
10
+ * primitive; its checkout session must never be shared across placements.
11
11
  */
12
12
  export declare function ApplePaySubscriptionCheckoutSlot({ options, placeholderLabel, ...slotProps }: ApplePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
@@ -37,9 +37,9 @@ const applePayExpressCheckoutOptions = {
37
37
  },
38
38
  };
39
39
  /**
40
- * @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
41
- * exported and runtime-compatible as an advanced low-level primitive; its checkout session must
42
- * never be shared across placements.
40
+ * @deprecated Prefer StripeSubscriptionWalletSurface or StripeOneTimeWalletSurface for visible
41
+ * wallet placements. This slot stays exported and runtime-compatible as an advanced low-level
42
+ * primitive; its checkout session must never be shared across placements.
43
43
  */
44
44
  export function ApplePaySubscriptionCheckoutSlot(_a) {
45
45
  var _b, _c, _d, _e, _f, _g, _h, _j, _k;
@@ -5,8 +5,8 @@ export type GooglePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionChec
5
5
  placeholderLabel?: string;
6
6
  };
7
7
  /**
8
- * @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
9
- * exported and runtime-compatible as an advanced low-level primitive; its checkout session must
10
- * never be shared across placements.
8
+ * @deprecated Prefer StripeSubscriptionWalletSurface or StripeOneTimeWalletSurface for visible
9
+ * wallet placements. This slot stays exported and runtime-compatible as an advanced low-level
10
+ * primitive; its checkout session must never be shared across placements.
11
11
  */
12
12
  export declare function GooglePaySubscriptionCheckoutSlot({ options, placeholderLabel, ...slotProps }: GooglePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
@@ -37,9 +37,9 @@ const googlePayExpressCheckoutOptions = {
37
37
  },
38
38
  };
39
39
  /**
40
- * @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
41
- * exported and runtime-compatible as an advanced low-level primitive; its checkout session must
42
- * never be shared across placements.
40
+ * @deprecated Prefer StripeSubscriptionWalletSurface or StripeOneTimeWalletSurface for visible
41
+ * wallet placements. This slot stays exported and runtime-compatible as an advanced low-level
42
+ * primitive; its checkout session must never be shared across placements.
43
43
  */
44
44
  export function GooglePaySubscriptionCheckoutSlot(_a) {
45
45
  var _b, _c, _d, _e, _f, _g, _h, _j, _k;
@@ -95,8 +95,18 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
95
95
  }
96
96
  await (onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess());
97
97
  };
98
+ const queueCheckoutStarted = (paymentMethod) => {
99
+ if (!checkoutAnalytics) {
100
+ return;
101
+ }
102
+ queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, checkoutStartSource: 'wallet_click', paymentMethod }));
103
+ };
98
104
  const handleConfirm = async (event) => {
99
105
  var _a, _b, _c;
106
+ // Checkout Sessions omits the Express Checkout onClick callback. Confirmation
107
+ // is therefore the first reliable wallet-intent boundary for this provider.
108
+ // Session-based analytics deduplication keeps this idempotent if onClick fires.
109
+ queueCheckoutStarted(event.expressPaymentType);
100
110
  if (checkoutState.type !== 'success') {
101
111
  const message = checkoutState.type === 'error'
102
112
  ? checkoutState.error.message
@@ -163,9 +173,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
163
173
  }
164
174
  };
165
175
  const handleClick = (event) => {
166
- if (checkoutAnalytics) {
167
- queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, checkoutStartSource: 'wallet_click', paymentMethod: event.expressPaymentType }));
168
- }
176
+ queueCheckoutStarted(event.expressPaymentType);
169
177
  event.resolve();
170
178
  };
171
179
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: [
@@ -0,0 +1,15 @@
1
+ import { type StripeOneTimeCheckoutSessionInput } from '../hooks/useStripeOneTimeCheckoutSession.js';
2
+ import { type StripeWalletSurfaceAvailability, type StripeWalletSurfaceErrorContext, type StripeWalletSurfaceProps } from './StripeWalletSurface.internal.js';
3
+ export type StripeOneTimeWalletSurfaceAvailability = StripeWalletSurfaceAvailability;
4
+ export type StripeOneTimeWalletSurfaceErrorContext = StripeWalletSurfaceErrorContext;
5
+ export type StripeOneTimeWalletSurfaceCheckoutInput = Omit<StripeOneTimeCheckoutSessionInput, 'onError'> & {
6
+ customerName?: string | null;
7
+ };
8
+ export type StripeOneTimeWalletSurfaceProps = Omit<StripeWalletSurfaceProps, 'applePayOptions' | 'applePayPlaceholderLabel' | 'customerEmail' | 'customerName' | 'googlePayOptions' | 'googlePayPlaceholderLabel' | 'returnUrl' | 'session'> & {
9
+ applePayOptions?: StripeWalletSurfaceProps['applePayOptions'];
10
+ applePayPlaceholderLabel?: string;
11
+ checkout: StripeOneTimeWalletSurfaceCheckoutInput;
12
+ googlePayOptions?: StripeWalletSurfaceProps['googlePayOptions'];
13
+ googlePayPlaceholderLabel?: string;
14
+ };
15
+ export declare function StripeOneTimeWalletSurface({ applePayOptions, applePayPlaceholderLabel, checkout, googlePayOptions, googlePayPlaceholderLabel, surfaceId, ...surfaceProps }: StripeOneTimeWalletSurfaceProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,28 @@
1
+ 'use client';
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { useMemo } from 'react';
15
+ import { useStripeOneTimeCheckoutSession, } from '../hooks/useStripeOneTimeCheckoutSession.js';
16
+ import { StripeWalletSurface, buildStripeWalletSurfaceAnalyticsMetadata, } from './StripeWalletSurface.internal.js';
17
+ export function StripeOneTimeWalletSurface(_a) {
18
+ var _b, _c;
19
+ var { applePayOptions, applePayPlaceholderLabel = 'Buy with Apple Pay', checkout, googlePayOptions, googlePayPlaceholderLabel = 'Buy with Google Pay', surfaceId } = _a, surfaceProps = __rest(_a, ["applePayOptions", "applePayPlaceholderLabel", "checkout", "googlePayOptions", "googlePayPlaceholderLabel", "surfaceId"]);
20
+ const normalizedSurfaceId = surfaceId.trim();
21
+ const { customerName } = checkout, checkoutSessionInput = __rest(checkout, ["customerName"]);
22
+ const surfaceAnalyticsMetadata = useMemo(() => {
23
+ var _a;
24
+ return buildStripeWalletSurfaceAnalyticsMetadata(Object.assign(Object.assign({}, ((_a = checkout.analyticsMetadata) !== null && _a !== void 0 ? _a : {})), { paymentMode: 'one_time' }), normalizedSurfaceId);
25
+ }, [checkout.analyticsMetadata, normalizedSurfaceId]);
26
+ const session = useStripeOneTimeCheckoutSession(Object.assign(Object.assign({}, checkoutSessionInput), { analyticsMetadata: surfaceAnalyticsMetadata }));
27
+ return (_jsx(StripeWalletSurface, Object.assign({}, surfaceProps, { applePayOptions: Object.assign(Object.assign({}, applePayOptions), { buttonType: Object.assign({ applePay: 'buy' }, ((_b = applePayOptions === null || applePayOptions === void 0 ? void 0 : applePayOptions.buttonType) !== null && _b !== void 0 ? _b : {})) }), applePayPlaceholderLabel: applePayPlaceholderLabel, customerEmail: checkout.customerEmail, customerName: customerName, googlePayOptions: Object.assign(Object.assign({}, googlePayOptions), { buttonType: Object.assign({ googlePay: 'buy' }, ((_c = googlePayOptions === null || googlePayOptions === void 0 ? void 0 : googlePayOptions.buttonType) !== null && _c !== void 0 ? _c : {})) }), googlePayPlaceholderLabel: googlePayPlaceholderLabel, returnUrl: checkout.returnUrl, session: session, surfaceId: normalizedSurfaceId })));
28
+ }
@@ -1,82 +1,11 @@
1
- import { type ReactNode } from 'react';
2
- import type { Appearance, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
3
- import { type PlatformWalletPaymentMethod } from './walletPlatform.js';
4
- import type { StripeSubscriptionCheckoutAnalyticsContext } from '../services/checkoutCompletionAnalytics.service.js';
5
1
  import { type StripeSubscriptionCheckoutSessionInput } from '../hooks/useStripeSubscriptionCheckoutSession.js';
2
+ import { applyStripeWalletSurfaceMethodAvailability as applyStripeSubscriptionWalletSurfaceMethodAvailability, buildStripeWalletSurfaceAnalyticsMetadata as buildStripeSubscriptionWalletSurfaceAnalyticsMetadata, buildStripeWalletSurfaceAvailability as buildStripeSubscriptionWalletSurfaceAvailability, buildStripeWalletSurfaceCheckoutAnalytics as buildStripeSubscriptionWalletSurfaceCheckoutAnalytics, getStripeWalletSurfaceMethodAvailability as getStripeSubscriptionWalletSurfaceMethodAvailability, getStripeWalletSurfaceVisibleMethods as getStripeSubscriptionWalletSurfaceVisibleMethods, normalizeStripeWalletSurfaceMethods as normalizeStripeSubscriptionWalletSurfaceMethods, resolveStripeWalletSurfaceError as resolveStripeSubscriptionWalletSurfaceError, shouldRenderStripeWalletSurface as shouldRenderStripeSubscriptionWalletSurface, type StripeWalletSurfaceAvailability as StripeSubscriptionWalletSurfaceAvailability, type StripeWalletSurfaceAvailabilityState as StripeSubscriptionWalletSurfaceAvailabilityState, type StripeWalletSurfaceErrorContext as StripeSubscriptionWalletSurfaceErrorContext, type StripeWalletSurfaceErrorEmission as StripeSubscriptionWalletSurfaceErrorEmission, type StripeWalletSurfaceErrorState as StripeSubscriptionWalletSurfaceErrorState, type StripeWalletSurfaceProps, type StripeWalletSurfaceRenderStateInput as StripeSubscriptionWalletSurfaceRenderStateInput } from './StripeWalletSurface.internal.js';
3
+ export { applyStripeSubscriptionWalletSurfaceMethodAvailability, buildStripeSubscriptionWalletSurfaceAnalyticsMetadata, buildStripeSubscriptionWalletSurfaceAvailability, buildStripeSubscriptionWalletSurfaceCheckoutAnalytics, getStripeSubscriptionWalletSurfaceMethodAvailability, getStripeSubscriptionWalletSurfaceVisibleMethods, normalizeStripeSubscriptionWalletSurfaceMethods, resolveStripeSubscriptionWalletSurfaceError, shouldRenderStripeSubscriptionWalletSurface, };
4
+ export type { StripeSubscriptionWalletSurfaceAvailability, StripeSubscriptionWalletSurfaceAvailabilityState, StripeSubscriptionWalletSurfaceErrorContext, StripeSubscriptionWalletSurfaceErrorEmission, StripeSubscriptionWalletSurfaceErrorState, StripeSubscriptionWalletSurfaceRenderStateInput, };
6
5
  export type StripeSubscriptionWalletSurfaceCheckoutInput = Omit<StripeSubscriptionCheckoutSessionInput, 'onError'> & {
7
6
  customerName?: string | null;
8
7
  };
9
- export type StripeSubscriptionWalletSurfaceAvailability = {
10
- anyAvailable: boolean;
11
- methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
12
- resolved: boolean;
13
- };
14
- export type StripeSubscriptionWalletSurfaceAvailabilityState = {
15
- intentKey: string;
16
- methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
17
- };
18
- export type StripeSubscriptionWalletSurfaceErrorContext = {
19
- paymentMethod?: PlatformWalletPaymentMethod;
20
- surfaceId: string;
21
- };
22
- export type StripeSubscriptionWalletSurfaceErrorEmission = {
23
- context: StripeSubscriptionWalletSurfaceErrorContext;
24
- errorState: StripeSubscriptionWalletSurfaceErrorState;
25
- message: string | null;
26
- };
27
- export type StripeSubscriptionWalletSurfaceErrorState = {
28
- methodMessages: Partial<Record<PlatformWalletPaymentMethod, string>>;
29
- surfaceMessage: string | null;
30
- };
31
- export type StripeSubscriptionWalletSurfaceRenderStateInput = {
32
- anyAvailable: boolean;
33
- configured: boolean;
34
- paymentMethodCount: number;
35
- resolved: boolean;
36
- suspended: boolean;
37
- };
38
- export declare function buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(analyticsMetadata: Record<string, unknown> | null | undefined, surfaceId: string): Record<string, unknown>;
39
- export declare function buildStripeSubscriptionWalletSurfaceCheckoutAnalytics(checkoutAnalytics: StripeSubscriptionCheckoutAnalyticsContext | null | undefined, surfaceId: string): StripeSubscriptionCheckoutAnalyticsContext | null;
40
- export declare function normalizeStripeSubscriptionWalletSurfaceMethods(paymentMethods: readonly PlatformWalletPaymentMethod[]): PlatformWalletPaymentMethod[];
41
- export declare function getStripeSubscriptionWalletSurfaceVisibleMethods(paymentMethods: readonly PlatformWalletPaymentMethod[], methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>): PlatformWalletPaymentMethod[];
42
- export declare function getStripeSubscriptionWalletSurfaceMethodAvailability(state: StripeSubscriptionWalletSurfaceAvailabilityState, intentKey: string): Partial<Record<PlatformWalletPaymentMethod, boolean>>;
43
- export declare function applyStripeSubscriptionWalletSurfaceMethodAvailability(state: StripeSubscriptionWalletSurfaceAvailabilityState, intentKey: string, method: PlatformWalletPaymentMethod, available: boolean): StripeSubscriptionWalletSurfaceAvailabilityState;
44
- export declare function buildStripeSubscriptionWalletSurfaceAvailability(paymentMethods: readonly PlatformWalletPaymentMethod[], methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>): StripeSubscriptionWalletSurfaceAvailability;
45
- export declare function shouldRenderStripeSubscriptionWalletSurface({ anyAvailable, configured, paymentMethodCount, resolved, suspended, }: StripeSubscriptionWalletSurfaceRenderStateInput): boolean;
46
- export declare function resolveStripeSubscriptionWalletSurfaceError({ errorState, message, paymentMethod, surfaceId, }: {
47
- errorState: StripeSubscriptionWalletSurfaceErrorState;
48
- message: string | null;
49
- paymentMethod?: PlatformWalletPaymentMethod;
50
- surfaceId: string;
51
- }): StripeSubscriptionWalletSurfaceErrorEmission | null;
52
- export type StripeSubscriptionWalletSurfaceProps = {
53
- amountCents: number;
54
- appearance?: Appearance;
55
- applePayOptions?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
56
- applePayPlaceholderLabel?: string;
57
- beforeConfirm?: () => Promise<string | null>;
8
+ export type StripeSubscriptionWalletSurfaceProps = Omit<StripeWalletSurfaceProps, 'customerEmail' | 'customerName' | 'returnUrl' | 'session'> & {
58
9
  checkout: StripeSubscriptionWalletSurfaceCheckoutInput;
59
- checkoutAnalytics?: StripeSubscriptionCheckoutAnalyticsContext | null;
60
- className?: string;
61
- containerClassName?: string;
62
- disabled?: boolean;
63
- googlePayOptions?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
64
- googlePayPlaceholderLabel?: string;
65
- onAvailabilityChange?: (availability: StripeSubscriptionWalletSurfaceAvailability) => void;
66
- onCancel?: () => void;
67
- onError?: (message: string | null, context: StripeSubscriptionWalletSurfaceErrorContext) => void;
68
- onPaymentInfoSubmitted?: () => void | Promise<void>;
69
- onSuccess?: () => void | Promise<void>;
70
- onUnavailableClick?: () => Promise<void> | void;
71
- paymentMethods?: readonly PlatformWalletPaymentMethod[];
72
- renderHeader?: (props: {
73
- availability: StripeSubscriptionWalletSurfaceAvailability;
74
- }) => ReactNode;
75
- resolveMethodClassName?: (method: PlatformWalletPaymentMethod) => string | undefined;
76
- resolveMethodWrapperClassName?: (method: PlatformWalletPaymentMethod) => string | undefined;
77
- slotClassName?: string;
78
- summaryLabel: string;
79
- surfaceId: string;
80
- suspended?: boolean;
81
10
  };
82
- export declare function StripeSubscriptionWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkout, checkoutAnalytics, className, containerClassName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, slotClassName, summaryLabel, surfaceId, suspended, }: StripeSubscriptionWalletSurfaceProps): import("react/jsx-runtime").JSX.Element | null;
11
+ export declare function StripeSubscriptionWalletSurface({ checkout, surfaceId, ...surfaceProps }: StripeSubscriptionWalletSurfaceProps): import("react/jsx-runtime").JSX.Element;
@@ -10,179 +10,16 @@ var __rest = (this && this.__rest) || function (s, e) {
10
10
  }
11
11
  return t;
12
12
  };
13
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
15
- import { usePlatformWalletPaymentMethods, } from './walletPlatform.js';
16
- import { ApplePaySubscriptionCheckoutSlot } from './ApplePaySubscriptionCheckoutSlot.js';
17
- import { GooglePaySubscriptionCheckoutSlot } from './GooglePaySubscriptionCheckoutSlot.js';
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { useMemo } from 'react';
18
15
  import { useStripeSubscriptionCheckoutSession, } from '../hooks/useStripeSubscriptionCheckoutSession.js';
19
- export function buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(analyticsMetadata, surfaceId) {
20
- return Object.assign(Object.assign({}, (analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {})), { checkoutSurfaceId: surfaceId });
21
- }
22
- export function buildStripeSubscriptionWalletSurfaceCheckoutAnalytics(checkoutAnalytics, surfaceId) {
23
- if (!checkoutAnalytics) {
24
- return null;
25
- }
26
- return Object.assign(Object.assign({}, checkoutAnalytics), { metadata: buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(checkoutAnalytics.metadata, surfaceId) });
27
- }
28
- export function normalizeStripeSubscriptionWalletSurfaceMethods(paymentMethods) {
29
- return paymentMethods.filter((method, index) => paymentMethods.indexOf(method) === index);
30
- }
31
- export function getStripeSubscriptionWalletSurfaceVisibleMethods(paymentMethods, methods) {
32
- return paymentMethods.filter((method) => methods[method] !== false);
33
- }
34
- export function getStripeSubscriptionWalletSurfaceMethodAvailability(state, intentKey) {
35
- return state.intentKey === intentKey ? state.methods : {};
36
- }
37
- export function applyStripeSubscriptionWalletSurfaceMethodAvailability(state, intentKey, method, available) {
38
- return {
39
- intentKey,
40
- methods: Object.assign(Object.assign({}, getStripeSubscriptionWalletSurfaceMethodAvailability(state, intentKey)), { [method]: available }),
41
- };
42
- }
43
- export function buildStripeSubscriptionWalletSurfaceAvailability(paymentMethods, methods) {
44
- const reportedMethods = paymentMethods.filter((method) => typeof methods[method] === 'boolean');
45
- return {
46
- anyAvailable: reportedMethods.some((method) => methods[method] === true),
47
- methods: reportedMethods.reduce((current, method) => (Object.assign(Object.assign({}, current), { [method]: methods[method] === true })), {}),
48
- resolved: reportedMethods.length === paymentMethods.length,
49
- };
50
- }
51
- export function shouldRenderStripeSubscriptionWalletSurface({ anyAvailable, configured, paymentMethodCount, resolved, suspended, }) {
52
- return configured && !suspended && paymentMethodCount > 0 && (!resolved || anyAvailable);
53
- }
54
- export function resolveStripeSubscriptionWalletSurfaceError({ errorState, message, paymentMethod, surfaceId, }) {
55
- if (paymentMethod && errorState.methodMessages[paymentMethod] === message) {
56
- return null;
57
- }
58
- if (!paymentMethod) {
59
- const methodMessages = Object.values(errorState.methodMessages);
60
- if (message === null) {
61
- if (errorState.surfaceMessage === null && methodMessages.length === 0) {
62
- return null;
63
- }
64
- }
65
- else if (errorState.surfaceMessage === message || methodMessages.includes(message)) {
66
- return null;
67
- }
68
- }
69
- const nextErrorState = message === null
70
- ? { methodMessages: {}, surfaceMessage: null }
71
- : paymentMethod
72
- ? Object.assign(Object.assign({}, errorState), { methodMessages: Object.assign(Object.assign({}, errorState.methodMessages), { [paymentMethod]: message }) }) : Object.assign(Object.assign({}, errorState), { surfaceMessage: message });
73
- return {
74
- context: Object.assign(Object.assign({}, (paymentMethod ? { paymentMethod } : {})), { surfaceId }),
75
- errorState: nextErrorState,
76
- message,
77
- };
78
- }
79
- // Availability identity used to notify consumers only when the aggregate actually changed.
80
- function buildAvailabilitySignature(availability) {
81
- return [
82
- availability.resolved ? 'resolved' : 'unresolved',
83
- availability.anyAvailable ? 'available' : 'unavailable',
84
- Object.entries(availability.methods)
85
- .map(([method, available]) => `${method}:${available ? '1' : '0'}`)
86
- .sort()
87
- .join(','),
88
- ].join('|');
89
- }
90
- export function StripeSubscriptionWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkout, checkoutAnalytics, className, containerClassName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, slotClassName, summaryLabel, surfaceId, suspended = false, }) {
91
- var _a;
92
- const platformWalletPaymentMethods = usePlatformWalletPaymentMethods();
93
- const walletPaymentMethods = normalizeStripeSubscriptionWalletSurfaceMethods(paymentMethods !== null && paymentMethods !== void 0 ? paymentMethods : platformWalletPaymentMethods);
16
+ import { StripeWalletSurface, applyStripeWalletSurfaceMethodAvailability as applyStripeSubscriptionWalletSurfaceMethodAvailability, buildStripeWalletSurfaceAnalyticsMetadata as buildStripeSubscriptionWalletSurfaceAnalyticsMetadata, buildStripeWalletSurfaceAvailability as buildStripeSubscriptionWalletSurfaceAvailability, buildStripeWalletSurfaceCheckoutAnalytics as buildStripeSubscriptionWalletSurfaceCheckoutAnalytics, getStripeWalletSurfaceMethodAvailability as getStripeSubscriptionWalletSurfaceMethodAvailability, getStripeWalletSurfaceVisibleMethods as getStripeSubscriptionWalletSurfaceVisibleMethods, normalizeStripeWalletSurfaceMethods as normalizeStripeSubscriptionWalletSurfaceMethods, resolveStripeWalletSurfaceError as resolveStripeSubscriptionWalletSurfaceError, shouldRenderStripeWalletSurface as shouldRenderStripeSubscriptionWalletSurface, } from './StripeWalletSurface.internal.js';
17
+ export { applyStripeSubscriptionWalletSurfaceMethodAvailability, buildStripeSubscriptionWalletSurfaceAnalyticsMetadata, buildStripeSubscriptionWalletSurfaceAvailability, buildStripeSubscriptionWalletSurfaceCheckoutAnalytics, getStripeSubscriptionWalletSurfaceMethodAvailability, getStripeSubscriptionWalletSurfaceVisibleMethods, normalizeStripeSubscriptionWalletSurfaceMethods, resolveStripeSubscriptionWalletSurfaceError, shouldRenderStripeSubscriptionWalletSurface, };
18
+ export function StripeSubscriptionWalletSurface(_a) {
19
+ var { checkout, surfaceId } = _a, surfaceProps = __rest(_a, ["checkout", "surfaceId"]);
94
20
  const normalizedSurfaceId = surfaceId.trim();
95
21
  const { customerName } = checkout, checkoutSessionInput = __rest(checkout, ["customerName"]);
96
22
  const surfaceAnalyticsMetadata = useMemo(() => buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(checkout.analyticsMetadata, normalizedSurfaceId), [checkout.analyticsMetadata, normalizedSurfaceId]);
97
23
  const session = useStripeSubscriptionCheckoutSession(Object.assign(Object.assign({}, checkoutSessionInput), { analyticsMetadata: surfaceAnalyticsMetadata }));
98
- const surfaceCheckoutAnalytics = useMemo(() => buildStripeSubscriptionWalletSurfaceCheckoutAnalytics(checkoutAnalytics, normalizedSurfaceId), [checkoutAnalytics, normalizedSurfaceId]);
99
- const [availabilityState, setAvailabilityState] = useState({
100
- intentKey: session.intentKey,
101
- methods: {},
102
- });
103
- const availability = buildStripeSubscriptionWalletSurfaceAvailability(walletPaymentMethods, getStripeSubscriptionWalletSurfaceMethodAvailability(availabilityState, session.intentKey));
104
- const availabilitySignature = buildAvailabilitySignature(availability);
105
- const reportedAvailabilitySignatureRef = useRef(null);
106
- const errorStateRef = useRef({
107
- methodMessages: {},
108
- surfaceMessage: null,
109
- });
110
- const emitError = useCallback((message, paymentMethod) => {
111
- const emission = resolveStripeSubscriptionWalletSurfaceError({
112
- errorState: errorStateRef.current,
113
- message,
114
- paymentMethod,
115
- surfaceId: normalizedSurfaceId,
116
- });
117
- if (!emission) {
118
- return;
119
- }
120
- errorStateRef.current = emission.errorState;
121
- onError === null || onError === void 0 ? void 0 : onError(emission.message, emission.context);
122
- }, [normalizedSurfaceId, onError]);
123
- useEffect(() => {
124
- if (reportedAvailabilitySignatureRef.current === availabilitySignature) {
125
- return;
126
- }
127
- reportedAvailabilitySignatureRef.current = availabilitySignature;
128
- onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(availability);
129
- }, [availability, availabilitySignature, onAvailabilityChange]);
130
- useEffect(() => {
131
- emitError(session.error);
132
- }, [emitError, session.error]);
133
- const renderWalletMethod = (method) => {
134
- var _a;
135
- const slotProps = {
136
- amountCents,
137
- appearance,
138
- beforeConfirm,
139
- checkoutAnalytics: surfaceCheckoutAnalytics,
140
- className: (_a = resolveMethodClassName === null || resolveMethodClassName === void 0 ? void 0 : resolveMethodClassName(method)) !== null && _a !== void 0 ? _a : className,
141
- customerEmail: checkout.customerEmail,
142
- customerName,
143
- disabled,
144
- onAvailabilityChange: (available) => setAvailabilityState((current) => applyStripeSubscriptionWalletSurfaceMethodAvailability(current, session.intentKey, method, available)),
145
- onCancel,
146
- onError: (message) => emitError(message, method),
147
- onPaymentInfoSubmitted,
148
- onSuccess,
149
- onUnavailableClick,
150
- returnUrl: checkout.returnUrl,
151
- session,
152
- slotClassName,
153
- summaryLabel,
154
- };
155
- if (method === 'applePay') {
156
- return (_jsx(ApplePaySubscriptionCheckoutSlot, Object.assign({}, slotProps, { options: applePayOptions, placeholderLabel: applePayPlaceholderLabel })));
157
- }
158
- if (method === 'googlePay') {
159
- return (_jsx(GooglePaySubscriptionCheckoutSlot, Object.assign({}, slotProps, { options: googlePayOptions, placeholderLabel: googlePayPlaceholderLabel })));
160
- }
161
- return null;
162
- };
163
- const visibleWalletPaymentMethods = getStripeSubscriptionWalletSurfaceVisibleMethods(walletPaymentMethods, availability.methods);
164
- const shouldRenderSurface = shouldRenderStripeSubscriptionWalletSurface({
165
- anyAvailable: availability.anyAvailable,
166
- configured: session.configured,
167
- paymentMethodCount: walletPaymentMethods.length,
168
- resolved: availability.resolved,
169
- suspended,
170
- });
171
- if (!shouldRenderSurface) {
172
- return null;
173
- }
174
- return (_jsxs("div", { className: [
175
- 'stripe-subscription-wallet-surface',
176
- containerClassName !== null && containerClassName !== void 0 ? containerClassName : '',
177
- ]
178
- .filter(Boolean)
179
- .join(' '), "data-wallet-surface-id": normalizedSurfaceId, children: [(_a = renderHeader === null || renderHeader === void 0 ? void 0 : renderHeader({ availability })) !== null && _a !== void 0 ? _a : null, visibleWalletPaymentMethods.map((method) => {
180
- var _a;
181
- return (_jsx("div", { className: [
182
- 'stripe-subscription-wallet-surface__method',
183
- (_a = resolveMethodWrapperClassName === null || resolveMethodWrapperClassName === void 0 ? void 0 : resolveMethodWrapperClassName(method)) !== null && _a !== void 0 ? _a : '',
184
- ]
185
- .filter(Boolean)
186
- .join(' '), "data-wallet-payment-method": method, children: renderWalletMethod(method) }, method));
187
- })] }));
24
+ return (_jsx(StripeWalletSurface, Object.assign({}, surfaceProps, { customerEmail: checkout.customerEmail, customerName: customerName, returnUrl: checkout.returnUrl, session: session, surfaceId: normalizedSurfaceId })));
188
25
  }
@@ -0,0 +1,82 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { Appearance, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
3
+ import { type PlatformWalletPaymentMethod } from './walletPlatform.js';
4
+ import type { StripeSubscriptionCheckoutAnalyticsContext } from '../services/checkoutCompletionAnalytics.service.js';
5
+ import type { StripeWalletCheckoutSession } from './WalletSubscriptionCheckoutSlot.js';
6
+ export type StripeWalletSurfaceAvailability = {
7
+ anyAvailable: boolean;
8
+ methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
9
+ resolved: boolean;
10
+ };
11
+ export type StripeWalletSurfaceAvailabilityState = {
12
+ intentKey: string;
13
+ methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
14
+ };
15
+ export type StripeWalletSurfaceErrorContext = {
16
+ paymentMethod?: PlatformWalletPaymentMethod;
17
+ surfaceId: string;
18
+ };
19
+ export type StripeWalletSurfaceErrorEmission = {
20
+ context: StripeWalletSurfaceErrorContext;
21
+ errorState: StripeWalletSurfaceErrorState;
22
+ message: string | null;
23
+ };
24
+ export type StripeWalletSurfaceErrorState = {
25
+ methodMessages: Partial<Record<PlatformWalletPaymentMethod, string>>;
26
+ surfaceMessage: string | null;
27
+ };
28
+ export type StripeWalletSurfaceRenderStateInput = {
29
+ anyAvailable: boolean;
30
+ configured: boolean;
31
+ paymentMethodCount: number;
32
+ resolved: boolean;
33
+ suspended: boolean;
34
+ };
35
+ export declare function buildStripeWalletSurfaceAnalyticsMetadata(analyticsMetadata: Record<string, unknown> | null | undefined, surfaceId: string): Record<string, unknown>;
36
+ export declare function buildStripeWalletSurfaceCheckoutAnalytics(checkoutAnalytics: StripeSubscriptionCheckoutAnalyticsContext | null | undefined, surfaceId: string): StripeSubscriptionCheckoutAnalyticsContext | null;
37
+ export declare function normalizeStripeWalletSurfaceMethods(paymentMethods: readonly PlatformWalletPaymentMethod[]): PlatformWalletPaymentMethod[];
38
+ export declare function getStripeWalletSurfaceVisibleMethods(paymentMethods: readonly PlatformWalletPaymentMethod[], methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>): PlatformWalletPaymentMethod[];
39
+ export declare function getStripeWalletSurfaceMethodAvailability(state: StripeWalletSurfaceAvailabilityState, intentKey: string): Partial<Record<PlatformWalletPaymentMethod, boolean>>;
40
+ export declare function applyStripeWalletSurfaceMethodAvailability(state: StripeWalletSurfaceAvailabilityState, intentKey: string, method: PlatformWalletPaymentMethod, available: boolean): StripeWalletSurfaceAvailabilityState;
41
+ export declare function buildStripeWalletSurfaceAvailability(paymentMethods: readonly PlatformWalletPaymentMethod[], methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>): StripeWalletSurfaceAvailability;
42
+ export declare function shouldRenderStripeWalletSurface({ anyAvailable, configured, paymentMethodCount, resolved, suspended, }: StripeWalletSurfaceRenderStateInput): boolean;
43
+ export declare function resolveStripeWalletSurfaceError({ errorState, message, paymentMethod, surfaceId, }: {
44
+ errorState: StripeWalletSurfaceErrorState;
45
+ message: string | null;
46
+ paymentMethod?: PlatformWalletPaymentMethod;
47
+ surfaceId: string;
48
+ }): StripeWalletSurfaceErrorEmission | null;
49
+ export type StripeWalletSurfaceProps = {
50
+ amountCents: number;
51
+ appearance?: Appearance;
52
+ applePayOptions?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
53
+ applePayPlaceholderLabel?: string;
54
+ beforeConfirm?: () => Promise<string | null>;
55
+ checkoutAnalytics?: StripeSubscriptionCheckoutAnalyticsContext | null;
56
+ className?: string;
57
+ containerClassName?: string;
58
+ customerEmail?: string | null;
59
+ customerName?: string | null;
60
+ disabled?: boolean;
61
+ googlePayOptions?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
62
+ googlePayPlaceholderLabel?: string;
63
+ onAvailabilityChange?: (availability: StripeWalletSurfaceAvailability) => void;
64
+ onCancel?: () => void;
65
+ onError?: (message: string | null, context: StripeWalletSurfaceErrorContext) => void;
66
+ onPaymentInfoSubmitted?: () => void | Promise<void>;
67
+ onSuccess?: () => void | Promise<void>;
68
+ onUnavailableClick?: () => Promise<void> | void;
69
+ paymentMethods?: readonly PlatformWalletPaymentMethod[];
70
+ renderHeader?: (props: {
71
+ availability: StripeWalletSurfaceAvailability;
72
+ }) => ReactNode;
73
+ resolveMethodClassName?: (method: PlatformWalletPaymentMethod) => string | undefined;
74
+ resolveMethodWrapperClassName?: (method: PlatformWalletPaymentMethod) => string | undefined;
75
+ returnUrl: string;
76
+ session: StripeWalletCheckoutSession;
77
+ slotClassName?: string;
78
+ summaryLabel: string;
79
+ surfaceId: string;
80
+ suspended?: boolean;
81
+ };
82
+ export declare function StripeWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkoutAnalytics, className, containerClassName, customerEmail, customerName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, returnUrl, session, slotClassName, summaryLabel, surfaceId, suspended, }: StripeWalletSurfaceProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,172 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
4
+ import { usePlatformWalletPaymentMethods, } from './walletPlatform.js';
5
+ import { ApplePaySubscriptionCheckoutSlot } from './ApplePaySubscriptionCheckoutSlot.js';
6
+ import { GooglePaySubscriptionCheckoutSlot } from './GooglePaySubscriptionCheckoutSlot.js';
7
+ export function buildStripeWalletSurfaceAnalyticsMetadata(analyticsMetadata, surfaceId) {
8
+ return Object.assign(Object.assign({}, (analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {})), { checkoutSurfaceId: surfaceId });
9
+ }
10
+ export function buildStripeWalletSurfaceCheckoutAnalytics(checkoutAnalytics, surfaceId) {
11
+ if (!checkoutAnalytics) {
12
+ return null;
13
+ }
14
+ return Object.assign(Object.assign({}, checkoutAnalytics), { metadata: buildStripeWalletSurfaceAnalyticsMetadata(checkoutAnalytics.metadata, surfaceId) });
15
+ }
16
+ export function normalizeStripeWalletSurfaceMethods(paymentMethods) {
17
+ return paymentMethods.filter((method, index) => paymentMethods.indexOf(method) === index);
18
+ }
19
+ export function getStripeWalletSurfaceVisibleMethods(paymentMethods, methods) {
20
+ return paymentMethods.filter((method) => methods[method] !== false);
21
+ }
22
+ export function getStripeWalletSurfaceMethodAvailability(state, intentKey) {
23
+ return state.intentKey === intentKey ? state.methods : {};
24
+ }
25
+ export function applyStripeWalletSurfaceMethodAvailability(state, intentKey, method, available) {
26
+ return {
27
+ intentKey,
28
+ methods: Object.assign(Object.assign({}, getStripeWalletSurfaceMethodAvailability(state, intentKey)), { [method]: available }),
29
+ };
30
+ }
31
+ export function buildStripeWalletSurfaceAvailability(paymentMethods, methods) {
32
+ const reportedMethods = paymentMethods.filter((method) => typeof methods[method] === 'boolean');
33
+ return {
34
+ anyAvailable: reportedMethods.some((method) => methods[method] === true),
35
+ methods: reportedMethods.reduce((current, method) => (Object.assign(Object.assign({}, current), { [method]: methods[method] === true })), {}),
36
+ resolved: reportedMethods.length === paymentMethods.length,
37
+ };
38
+ }
39
+ export function shouldRenderStripeWalletSurface({ anyAvailable, configured, paymentMethodCount, resolved, suspended, }) {
40
+ return configured && !suspended && paymentMethodCount > 0 && (!resolved || anyAvailable);
41
+ }
42
+ export function resolveStripeWalletSurfaceError({ errorState, message, paymentMethod, surfaceId, }) {
43
+ if (paymentMethod && errorState.methodMessages[paymentMethod] === message) {
44
+ return null;
45
+ }
46
+ if (!paymentMethod) {
47
+ const methodMessages = Object.values(errorState.methodMessages);
48
+ if (message === null) {
49
+ if (errorState.surfaceMessage === null && methodMessages.length === 0) {
50
+ return null;
51
+ }
52
+ }
53
+ else if (errorState.surfaceMessage === message || methodMessages.includes(message)) {
54
+ return null;
55
+ }
56
+ }
57
+ const nextErrorState = message === null
58
+ ? { methodMessages: {}, surfaceMessage: null }
59
+ : paymentMethod
60
+ ? Object.assign(Object.assign({}, errorState), { methodMessages: Object.assign(Object.assign({}, errorState.methodMessages), { [paymentMethod]: message }) }) : Object.assign(Object.assign({}, errorState), { surfaceMessage: message });
61
+ return {
62
+ context: Object.assign(Object.assign({}, (paymentMethod ? { paymentMethod } : {})), { surfaceId }),
63
+ errorState: nextErrorState,
64
+ message,
65
+ };
66
+ }
67
+ function buildAvailabilitySignature(availability) {
68
+ return [
69
+ availability.resolved ? 'resolved' : 'unresolved',
70
+ availability.anyAvailable ? 'available' : 'unavailable',
71
+ Object.entries(availability.methods)
72
+ .map(([method, available]) => `${method}:${available ? '1' : '0'}`)
73
+ .sort()
74
+ .join(','),
75
+ ].join('|');
76
+ }
77
+ export function StripeWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkoutAnalytics, className, containerClassName, customerEmail, customerName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, returnUrl, session, slotClassName, summaryLabel, surfaceId, suspended = false, }) {
78
+ var _a;
79
+ const platformWalletPaymentMethods = usePlatformWalletPaymentMethods();
80
+ const walletPaymentMethods = normalizeStripeWalletSurfaceMethods(paymentMethods !== null && paymentMethods !== void 0 ? paymentMethods : platformWalletPaymentMethods);
81
+ const normalizedSurfaceId = surfaceId.trim();
82
+ const surfaceCheckoutAnalytics = useMemo(() => buildStripeWalletSurfaceCheckoutAnalytics(checkoutAnalytics, normalizedSurfaceId), [checkoutAnalytics, normalizedSurfaceId]);
83
+ const [availabilityState, setAvailabilityState] = useState({
84
+ intentKey: session.intentKey,
85
+ methods: {},
86
+ });
87
+ const availability = buildStripeWalletSurfaceAvailability(walletPaymentMethods, getStripeWalletSurfaceMethodAvailability(availabilityState, session.intentKey));
88
+ const availabilitySignature = buildAvailabilitySignature(availability);
89
+ const reportedAvailabilitySignatureRef = useRef(null);
90
+ const errorStateRef = useRef({
91
+ methodMessages: {},
92
+ surfaceMessage: null,
93
+ });
94
+ const emitError = useCallback((message, paymentMethod) => {
95
+ const emission = resolveStripeWalletSurfaceError({
96
+ errorState: errorStateRef.current,
97
+ message,
98
+ paymentMethod,
99
+ surfaceId: normalizedSurfaceId,
100
+ });
101
+ if (!emission) {
102
+ return;
103
+ }
104
+ errorStateRef.current = emission.errorState;
105
+ onError === null || onError === void 0 ? void 0 : onError(emission.message, emission.context);
106
+ }, [normalizedSurfaceId, onError]);
107
+ useEffect(() => {
108
+ if (reportedAvailabilitySignatureRef.current === availabilitySignature) {
109
+ return;
110
+ }
111
+ reportedAvailabilitySignatureRef.current = availabilitySignature;
112
+ onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(availability);
113
+ }, [availability, availabilitySignature, onAvailabilityChange]);
114
+ useEffect(() => {
115
+ emitError(session.error);
116
+ }, [emitError, session.error]);
117
+ const renderWalletMethod = (method) => {
118
+ var _a;
119
+ const slotProps = {
120
+ amountCents,
121
+ appearance,
122
+ beforeConfirm,
123
+ checkoutAnalytics: surfaceCheckoutAnalytics,
124
+ className: (_a = resolveMethodClassName === null || resolveMethodClassName === void 0 ? void 0 : resolveMethodClassName(method)) !== null && _a !== void 0 ? _a : className,
125
+ customerEmail,
126
+ customerName,
127
+ disabled,
128
+ onAvailabilityChange: (available) => setAvailabilityState((current) => applyStripeWalletSurfaceMethodAvailability(current, session.intentKey, method, available)),
129
+ onCancel,
130
+ onError: (message) => emitError(message, method),
131
+ onPaymentInfoSubmitted,
132
+ onSuccess,
133
+ onUnavailableClick,
134
+ returnUrl,
135
+ session,
136
+ slotClassName,
137
+ summaryLabel,
138
+ };
139
+ if (method === 'applePay') {
140
+ return (_jsx(ApplePaySubscriptionCheckoutSlot, Object.assign({}, slotProps, { options: applePayOptions, placeholderLabel: applePayPlaceholderLabel })));
141
+ }
142
+ if (method === 'googlePay') {
143
+ return (_jsx(GooglePaySubscriptionCheckoutSlot, Object.assign({}, slotProps, { options: googlePayOptions, placeholderLabel: googlePayPlaceholderLabel })));
144
+ }
145
+ return null;
146
+ };
147
+ const visibleWalletPaymentMethods = getStripeWalletSurfaceVisibleMethods(walletPaymentMethods, availability.methods);
148
+ const shouldRenderSurface = shouldRenderStripeWalletSurface({
149
+ anyAvailable: availability.anyAvailable,
150
+ configured: session.configured,
151
+ paymentMethodCount: walletPaymentMethods.length,
152
+ resolved: availability.resolved,
153
+ suspended,
154
+ });
155
+ if (!shouldRenderSurface) {
156
+ return null;
157
+ }
158
+ return (_jsxs("div", { className: [
159
+ 'stripe-subscription-wallet-surface',
160
+ containerClassName !== null && containerClassName !== void 0 ? containerClassName : '',
161
+ ]
162
+ .filter(Boolean)
163
+ .join(' '), "data-wallet-surface-id": normalizedSurfaceId, children: [(_a = renderHeader === null || renderHeader === void 0 ? void 0 : renderHeader({ availability })) !== null && _a !== void 0 ? _a : null, visibleWalletPaymentMethods.map((method) => {
164
+ var _a;
165
+ return (_jsx("div", { className: [
166
+ 'stripe-subscription-wallet-surface__method',
167
+ (_a = resolveMethodWrapperClassName === null || resolveMethodWrapperClassName === void 0 ? void 0 : resolveMethodWrapperClassName(method)) !== null && _a !== void 0 ? _a : '',
168
+ ]
169
+ .filter(Boolean)
170
+ .join(' '), "data-wallet-payment-method": method, children: renderWalletMethod(method) }, method));
171
+ })] }));
172
+ }
@@ -1,7 +1,27 @@
1
1
  import { type ReactNode } from 'react';
2
- import type { Appearance, AvailablePaymentMethods, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
2
+ import type { Appearance, AvailablePaymentMethods, Stripe, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
3
3
  import { type StripeSubscriptionCheckoutAnalyticsContext } from '../services/checkoutCompletionAnalytics.service.js';
4
- import { type StripeSubscriptionCheckoutSession } from '../hooks/useStripeSubscriptionCheckoutSession.js';
4
+ import type { CheckoutPreparationLoading } from '../../paymentProvider.types.js';
5
+ export type StripeWalletCheckoutPreparationOptions = {
6
+ checkoutStartSource?: 'card_click' | 'checkout_render' | 'wallet_click' | 'wallet_render' | (string & {});
7
+ forceNew?: boolean;
8
+ paymentMethod?: unknown;
9
+ };
10
+ export type StripeWalletCheckoutSession = {
11
+ activeClientSecret: string | null;
12
+ activePlanKey?: string | null;
13
+ checkoutSessionId: string | null;
14
+ configured: boolean;
15
+ error: string | null;
16
+ intentKey: string;
17
+ loading: CheckoutPreparationLoading;
18
+ planKey: string;
19
+ prepareWalletCheckout: (options?: StripeWalletCheckoutPreparationOptions) => Promise<string | null>;
20
+ restartWalletCheckout: (options?: StripeWalletCheckoutPreparationOptions) => Promise<string | null>;
21
+ setError: (message: string | null) => void;
22
+ stripePromise: Promise<Stripe | null> | null;
23
+ updateCheckoutSessionPlan?: () => Promise<boolean>;
24
+ };
5
25
  export type WalletSubscriptionCheckoutRenderStateInput = {
6
26
  expressCheckoutAllowed: boolean;
7
27
  hasActiveClientSecret: boolean;
@@ -44,16 +64,16 @@ export type WalletSubscriptionCheckoutSlotProps = {
44
64
  renderPreparingPlaceholder?: (props: WalletPlaceholderButtonProps) => ReactNode;
45
65
  renderPlaceholder: (props: WalletPlaceholderButtonProps) => ReactNode;
46
66
  returnUrl: string;
47
- session: StripeSubscriptionCheckoutSession;
67
+ session: StripeWalletCheckoutSession;
48
68
  slotClassName?: string;
49
69
  summaryLabel: string;
50
70
  suspended?: boolean;
51
71
  };
52
72
  export declare function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label, }: Pick<WalletPlaceholderButtonProps, 'className' | 'label'>): import("react/jsx-runtime").JSX.Element;
53
73
  /**
54
- * @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
55
- * exported and runtime-compatible as an advanced low-level primitive; its checkout session must
56
- * never be shared across placements.
74
+ * @deprecated Prefer StripeSubscriptionWalletSurface or StripeOneTimeWalletSurface for visible
75
+ * wallet placements. This slot stays exported and runtime-compatible as an advanced low-level
76
+ * primitive; its checkout session must never be shared across placements.
57
77
  */
58
78
  export declare function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availabilityPaymentMethod, beforeConfirm, checkoutAnalytics, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, slotClassName, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
59
79
  export {};
@@ -30,9 +30,9 @@ export function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label,
30
30
  .join(' '), "aria-label": label, role: 'status', children: [_jsx("span", { className: 'wallet-subscription-checkout-slot__loading-bar' }), _jsx("style", { children: walletSubscriptionCheckoutLoadingPlaceholderStyles })] }));
31
31
  }
32
32
  /**
33
- * @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
34
- * exported and runtime-compatible as an advanced low-level primitive; its checkout session must
35
- * never be shared across placements.
33
+ * @deprecated Prefer StripeSubscriptionWalletSurface or StripeOneTimeWalletSurface for visible
34
+ * wallet placements. This slot stays exported and runtime-compatible as an advanced low-level
35
+ * primitive; its checkout session must never be shared across placements.
36
36
  */
37
37
  export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availabilityPaymentMethod, beforeConfirm, checkoutAnalytics, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, slotClassName, summaryLabel, suspended = false, }) {
38
38
  const [walletAvailability, setWalletAvailability] = useState({
@@ -55,7 +55,8 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
55
55
  walletAvailable,
56
56
  walletPreparing: session.loading.wallet,
57
57
  });
58
- const serverUpdateKey = session.activeClientSecret &&
58
+ const serverUpdateKey = session.updateCheckoutSessionPlan &&
59
+ session.activeClientSecret &&
59
60
  session.checkoutSessionId &&
60
61
  session.activePlanKey !== session.planKey
61
62
  ? session.planKey
@@ -7,6 +7,7 @@ export * from './SharedStripeCheckoutV2Dialog.js';
7
7
  export * from './StripeCheckoutExpressCheckoutButton.js';
8
8
  export * from './StripeExpressCheckoutButton.js';
9
9
  export * from './StripeExpressCheckoutElement.js';
10
+ export * from './StripeOneTimeWalletSurface.js';
10
11
  export * from './StripePlanSelector.js';
11
12
  export * from './StripeSubscriptionWalletSurface.js';
12
13
  export * from './WalletSubscriptionCheckoutSlot.js';
@@ -7,6 +7,7 @@ export * from './SharedStripeCheckoutV2Dialog.js';
7
7
  export * from './StripeCheckoutExpressCheckoutButton.js';
8
8
  export * from './StripeExpressCheckoutButton.js';
9
9
  export * from './StripeExpressCheckoutElement.js';
10
+ export * from './StripeOneTimeWalletSurface.js';
10
11
  export * from './StripePlanSelector.js';
11
12
  export * from './StripeSubscriptionWalletSurface.js';
12
13
  export * from './WalletSubscriptionCheckoutSlot.js';
@@ -278,6 +278,9 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
278
278
  await updateStripeSubscriptionCheckoutPlan({
279
279
  checkoutSessionId: activeCheckoutSessionId,
280
280
  plan,
281
+ runtimeConfigRevisionId,
282
+ paymentProfileId,
283
+ provider,
281
284
  analyticsMetadata: analyticsMetadataRef.current,
282
285
  couponId,
283
286
  customerEmail,
@@ -302,6 +305,9 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
302
305
  displayPlan,
303
306
  plan,
304
307
  planKey,
308
+ runtimeConfigRevisionId,
309
+ paymentProfileId,
310
+ provider,
305
311
  runtimeConfig,
306
312
  setError,
307
313
  userId,
@@ -155,6 +155,9 @@ type CreateSubscriptionCheckoutResponse = {
155
155
  type UpdateSubscriptionCheckoutPlanInput = {
156
156
  checkoutSessionId: string;
157
157
  plan: CheckoutSessionPlanInput;
158
+ runtimeConfigRevisionId?: string | null;
159
+ paymentProfileId?: string | null;
160
+ provider?: 'stripe' | null;
158
161
  couponId?: string | null;
159
162
  analyticsMetadata?: Record<string, unknown> | null;
160
163
  customerEmail?: string | null;
@@ -417,7 +417,7 @@ export async function createStripeSubscriptionCheckout(input) {
417
417
  });
418
418
  }
419
419
  export async function updateStripeSubscriptionCheckoutPlan(input) {
420
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
420
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
421
421
  const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
422
422
  if (!recurring) {
423
423
  throw new Error('Unable to resolve the recurring interval for the selected plan');
@@ -427,21 +427,25 @@ export async function updateStripeSubscriptionCheckoutPlan(input) {
427
427
  offerSetId: ((_a = input.plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
428
428
  offerSetKey: ((_b = input.plan.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
429
429
  planId: getPaywallPlanRequestId(input.plan),
430
- providerPlanId: ((_c = input.plan.providerPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
430
+ planKey: ((_c = input.plan.funnelPlanKey) === null || _c === void 0 ? void 0 : _c.trim()) || input.plan.id,
431
+ providerPlanId: ((_d = input.plan.providerPlanId) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
432
+ provider: input.provider,
433
+ paymentProfileId: ((_e = input.paymentProfileId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
434
+ runtimeConfigRevisionId: ((_f = input.runtimeConfigRevisionId) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
431
435
  title: input.plan.title,
432
- description: ((_d = input.plan.description) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
436
+ description: ((_g = input.plan.description) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
433
437
  amountCents: input.plan.amountCents,
434
438
  billingInterval: recurring.interval,
435
439
  billingIntervalCount: recurring.intervalCount,
436
- followUpProviderPlanId: ((_e = input.plan.followUpProviderPlanId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
437
- followUpPlanTitle: ((_f = input.plan.followUpPlanTitle) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
440
+ followUpProviderPlanId: ((_h = input.plan.followUpProviderPlanId) === null || _h === void 0 ? void 0 : _h.trim()) || undefined,
441
+ followUpPlanTitle: ((_j = input.plan.followUpPlanTitle) === null || _j === void 0 ? void 0 : _j.trim()) || undefined,
438
442
  followUpBillingInterval: input.plan.followUpBillingInterval,
439
443
  followUpBillingIntervalCount: input.plan.followUpBillingIntervalCount,
440
444
  introIterations: input.plan.introIterations,
441
- couponId: ((_g = input.couponId) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
445
+ couponId: ((_k = input.couponId) === null || _k === void 0 ? void 0 : _k.trim()) || undefined,
442
446
  analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
443
- customerEmail: ((_h = input.customerEmail) === null || _h === void 0 ? void 0 : _h.trim()) || undefined,
444
- userId: ((_j = input.user_id) === null || _j === void 0 ? void 0 : _j.trim()) || undefined,
447
+ customerEmail: ((_l = input.customerEmail) === null || _l === void 0 ? void 0 : _l.trim()) || undefined,
448
+ userId: ((_m = input.user_id) === null || _m === void 0 ? void 0 : _m.trim()) || undefined,
445
449
  environment: input.environment,
446
450
  runtimeConfig: input.runtimeConfig,
447
451
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",
@@ -32,8 +32,8 @@
32
32
  "test:run": "vitest run --passWithNoTests"
33
33
  },
34
34
  "dependencies": {
35
- "@funnelsgrove/analytics": "^0.1.82",
36
- "@funnelsgrove/runtime": "^0.9.0",
35
+ "@funnelsgrove/analytics": "^0.1.83",
36
+ "@funnelsgrove/runtime": "^0.10.0",
37
37
  "@solidgate/react-sdk": "1.34.0",
38
38
  "@stripe/react-stripe-js": "^5.6.0",
39
39
  "@stripe/stripe-js": "^8.7.0",