@funnelsgrove/payments 0.1.52 → 0.1.54
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/dist/components/ManageSubscriptionScreen.js +2 -10
- package/dist/config/billing.config.d.ts +5 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/stripe/useStripeOneTimeCheckoutSession.d.ts +45 -0
- package/dist/providers/stripe/useStripeOneTimeCheckoutSession.js +260 -0
- package/dist/services/checkoutCompletionAnalytics.service.d.ts +7 -6
- package/dist/services/checkoutCompletionAnalytics.service.js +10 -2
- package/dist/services/stripe.service.d.ts +6 -1
- package/dist/services/stripe.service.js +23 -8
- package/package.json +1 -1
|
@@ -80,7 +80,7 @@ const resolveSubscriptionStatusLabel = (subscription) => {
|
|
|
80
80
|
};
|
|
81
81
|
export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId, plans, }) {
|
|
82
82
|
var _a, _b;
|
|
83
|
-
const { attributes,
|
|
83
|
+
const { attributes, goToStep, setAnswer, setUser, user } = useFunnel();
|
|
84
84
|
const supportEmail = runtimePublicConfig.supportEmail;
|
|
85
85
|
const planList = useMemo(() => getFallbackBillingPlans(plans), [plans]);
|
|
86
86
|
const [stage, setStage] = useState('subscriptions');
|
|
@@ -184,20 +184,12 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
|
|
|
184
184
|
setData(payload);
|
|
185
185
|
const refreshedSubscription = toManageSubscriptions(payload.subscriptions).find((subscription) => subscription.id === selectedSubscription.id);
|
|
186
186
|
setCancelledActiveUntilDate(formatDate((refreshedSubscription === null || refreshedSubscription === void 0 ? void 0 : refreshedSubscription.currentPeriodEnd) || selectedSubscription.currentPeriodEnd));
|
|
187
|
-
const finalAnswers = {
|
|
188
|
-
manageSubscriptionHasActive: true,
|
|
189
|
-
manageSubscriptionReason: selectedReason.id,
|
|
190
|
-
manageSubscriptionReasonLabel: selectedReason.label,
|
|
191
|
-
manageSubscriptionCancelled: true,
|
|
192
|
-
manageSubscriptionCancelledId: selectedSubscription.id,
|
|
193
|
-
};
|
|
194
187
|
await persistCancellationAnswers({
|
|
195
188
|
cancelled: true,
|
|
196
189
|
reasonId: selectedReason.id,
|
|
197
190
|
reasonLabel: selectedReason.label,
|
|
198
191
|
subscriptionId: selectedSubscription.id,
|
|
199
192
|
});
|
|
200
|
-
completeStep(stepId, finalAnswers);
|
|
201
193
|
setStage('done');
|
|
202
194
|
}
|
|
203
195
|
catch (nextError) {
|
|
@@ -213,7 +205,7 @@ export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId,
|
|
|
213
205
|
? content.confirmStage.cancellingLabel
|
|
214
206
|
: content.confirmStage.cancelLabel })] })) : null, stage === 'done' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.doneStage.title }), _jsx("p", { className: 'manage-subscription-done-status', children: "Cancelled" }), _jsx("p", { className: 'manage-subscription-done-copy', children: content.doneStage.cancelledMessage }), cancelledActiveUntilDate ? (_jsxs("p", { className: 'manage-subscription-done-copy is-active-until', children: ["Active until: ", cancelledActiveUntilDate] })) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', onClick: () => {
|
|
215
207
|
if (homeStepId) {
|
|
216
|
-
goToStep(homeStepId);
|
|
208
|
+
goToStep(homeStepId, { type: 'exit', reason: 'cancel' });
|
|
217
209
|
return;
|
|
218
210
|
}
|
|
219
211
|
setStage('subscriptions');
|
|
@@ -16,6 +16,11 @@ export type BillingPlanConfig = {
|
|
|
16
16
|
amountCents: number;
|
|
17
17
|
analyticsPurchaseValue?: number;
|
|
18
18
|
checkoutSummaryLabel?: string;
|
|
19
|
+
followUpProviderPlanId?: string;
|
|
20
|
+
followUpPlanTitle?: string;
|
|
21
|
+
followUpBillingInterval?: BillingPlanInterval;
|
|
22
|
+
followUpBillingIntervalCount?: number;
|
|
23
|
+
introIterations?: number;
|
|
19
24
|
};
|
|
20
25
|
export type BillingFallbackPlanConfig = BillingPlanConfig & {
|
|
21
26
|
id?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export * from './services/checkoutCompletionAnalytics.service.js';
|
|
|
9
9
|
export * from './hooks/useResolvedPaywallPlans.js';
|
|
10
10
|
export * from './providers/paymentProvider.types.js';
|
|
11
11
|
export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
12
|
+
export * from './providers/stripe/useStripeOneTimeCheckoutSession.js';
|
|
12
13
|
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
13
14
|
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
14
15
|
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from './hooks/useResolvedPaywallPlans.js';
|
|
|
12
12
|
// Provider-neutral and Stripe subscription checkout primitives.
|
|
13
13
|
export * from './providers/paymentProvider.types.js';
|
|
14
14
|
export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
15
|
+
export * from './providers/stripe/useStripeOneTimeCheckoutSession.js';
|
|
15
16
|
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
16
17
|
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
17
18
|
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Stripe } from '@stripe/stripe-js';
|
|
2
|
+
import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
3
|
+
import { type CheckoutPreparationLoading } from '../paymentProvider.types.js';
|
|
4
|
+
import { type PaywallPlan, type StripeRuntimeConfigOverrides } from '../../services/stripe.service.js';
|
|
5
|
+
type StripeOneTimeCheckoutPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'description' | 'id' | 'providerPlanId' | 'title'>;
|
|
6
|
+
export type StripeOneTimeCheckoutSessionInput = {
|
|
7
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
8
|
+
checkoutMode: RuntimeMode;
|
|
9
|
+
couponId?: string | null;
|
|
10
|
+
customerEmail?: string | null;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
onError?: (message: string | null) => void;
|
|
13
|
+
plan?: StripeOneTimeCheckoutPlanInput | null;
|
|
14
|
+
returnUrl: string;
|
|
15
|
+
runtimeConfig?: StripeRuntimeConfigOverrides;
|
|
16
|
+
userId?: string | null;
|
|
17
|
+
};
|
|
18
|
+
export type StripeOneTimeCheckoutPreparationOptions = {
|
|
19
|
+
checkoutStartSource?: 'card_click' | 'wallet_click' | 'wallet_render' | (string & {});
|
|
20
|
+
forceNew?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export type StripeOneTimeCheckoutSession = {
|
|
23
|
+
activeClientSecret: string | null;
|
|
24
|
+
checkoutSessionId: string | null;
|
|
25
|
+
configured: boolean;
|
|
26
|
+
error: string | null;
|
|
27
|
+
intentKey: string;
|
|
28
|
+
loading: CheckoutPreparationLoading;
|
|
29
|
+
prepareCardCheckout: (options?: StripeOneTimeCheckoutPreparationOptions) => Promise<string | null>;
|
|
30
|
+
prepareWalletCheckout: (options?: StripeOneTimeCheckoutPreparationOptions) => Promise<string | null>;
|
|
31
|
+
provider: 'stripe';
|
|
32
|
+
reset: () => void;
|
|
33
|
+
restartCardCheckout: (options?: StripeOneTimeCheckoutPreparationOptions) => Promise<string | null>;
|
|
34
|
+
restartWalletCheckout: (options?: StripeOneTimeCheckoutPreparationOptions) => Promise<string | null>;
|
|
35
|
+
planKey: string;
|
|
36
|
+
setError: (message: string | null) => void;
|
|
37
|
+
stripePromise: Promise<Stripe | null> | null;
|
|
38
|
+
};
|
|
39
|
+
export type StripeOneTimeCheckoutIntentKeyInput = Pick<StripeOneTimeCheckoutSessionInput, 'checkoutMode' | 'couponId' | 'customerEmail' | 'plan' | 'returnUrl' | 'runtimeConfig' | 'userId'> & {
|
|
40
|
+
analyticsMetadata?: Record<string, unknown> | null;
|
|
41
|
+
};
|
|
42
|
+
export declare function buildStripeOneTimeCheckoutIntentKey({ checkoutMode, couponId, customerEmail, plan, returnUrl, runtimeConfig, userId, }: StripeOneTimeCheckoutIntentKeyInput): string;
|
|
43
|
+
export declare function shouldResetStripeOneTimeCheckoutSessionForIntentKey(previousIntentKey: string | null, nextIntentKey: string): boolean;
|
|
44
|
+
export declare function useStripeOneTimeCheckoutSession({ analyticsMetadata, checkoutMode, couponId, customerEmail, enabled, onError, plan, returnUrl, runtimeConfig, userId, }: StripeOneTimeCheckoutSessionInput): StripeOneTimeCheckoutSession;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { createEmptyCheckoutPreparationLoading, } from '../paymentProvider.types.js';
|
|
4
|
+
import { createStripeOneTimeCheckout, getStripePromise, isStripeConfigured, } from '../../services/stripe.service.js';
|
|
5
|
+
import { isInactiveCheckoutSessionError } from './useStripeSubscriptionCheckoutSession.js';
|
|
6
|
+
const getFriendlyStripeCheckoutError = (error) => {
|
|
7
|
+
const message = error instanceof Error ? error.message : 'Unable to initialize checkout';
|
|
8
|
+
return message === 'Internal server error'
|
|
9
|
+
? 'Unable to initialize checkout. Stripe settings may be missing for this funnel.'
|
|
10
|
+
: message;
|
|
11
|
+
};
|
|
12
|
+
const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
|
|
13
|
+
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
14
|
+
const buildCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
|
|
15
|
+
var _a;
|
|
16
|
+
const checkoutStartSource = (_a = options.checkoutStartSource) === null || _a === void 0 ? void 0 : _a.trim();
|
|
17
|
+
if (!checkoutStartSource) {
|
|
18
|
+
return analyticsMetadata;
|
|
19
|
+
}
|
|
20
|
+
return Object.assign(Object.assign({}, (analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {})), { checkoutStartSource });
|
|
21
|
+
};
|
|
22
|
+
export function buildStripeOneTimeCheckoutIntentKey({ checkoutMode, couponId, customerEmail, plan, returnUrl, runtimeConfig, userId, }) {
|
|
23
|
+
var _a;
|
|
24
|
+
const runtimeConfigSignature = [
|
|
25
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.apiBaseUrl),
|
|
26
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId),
|
|
27
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelVersionId),
|
|
28
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey),
|
|
29
|
+
].join('|');
|
|
30
|
+
const planSignature = [
|
|
31
|
+
normalizeRuntimeConfigValue(plan === null || plan === void 0 ? void 0 : plan.id),
|
|
32
|
+
normalizeRuntimeConfigValue(plan === null || plan === void 0 ? void 0 : plan.providerPlanId),
|
|
33
|
+
(_a = plan === null || plan === void 0 ? void 0 : plan.amountCents) !== null && _a !== void 0 ? _a : '',
|
|
34
|
+
].join('|');
|
|
35
|
+
const customerEmailSignature = normalizeRuntimeConfigValue(customerEmail);
|
|
36
|
+
const userIdSignature = normalizeRuntimeConfigValue(userId);
|
|
37
|
+
return [
|
|
38
|
+
'stripe-one-time',
|
|
39
|
+
checkoutMode,
|
|
40
|
+
runtimeConfigSignature,
|
|
41
|
+
planSignature,
|
|
42
|
+
customerEmailSignature,
|
|
43
|
+
userIdSignature,
|
|
44
|
+
(couponId === null || couponId === void 0 ? void 0 : couponId.trim()) || '',
|
|
45
|
+
returnUrl.trim(),
|
|
46
|
+
].join('|');
|
|
47
|
+
}
|
|
48
|
+
export function shouldResetStripeOneTimeCheckoutSessionForIntentKey(previousIntentKey, nextIntentKey) {
|
|
49
|
+
return previousIntentKey !== null && previousIntentKey !== nextIntentKey;
|
|
50
|
+
}
|
|
51
|
+
export function useStripeOneTimeCheckoutSession({ analyticsMetadata, checkoutMode, couponId, customerEmail, enabled = true, onError, plan, returnUrl, runtimeConfig, userId, }) {
|
|
52
|
+
var _a, _b, _c;
|
|
53
|
+
const [clientSecret, setClientSecret] = useState(null);
|
|
54
|
+
const [clientSecretIntentKey, setClientSecretIntentKey] = useState(null);
|
|
55
|
+
const [checkoutSessionId, setCheckoutSessionId] = useState(null);
|
|
56
|
+
const [error, setErrorState] = useState(null);
|
|
57
|
+
const [loading, setLoading] = useState(createEmptyCheckoutPreparationLoading);
|
|
58
|
+
const [runtimeStripePublishableKey, setRuntimeStripePublishableKey] = useState(null);
|
|
59
|
+
const creatingIntentRef = useRef(null);
|
|
60
|
+
const requestIdRef = useRef(0);
|
|
61
|
+
const configured = enabled &&
|
|
62
|
+
(isStripeConfigured(checkoutMode) ||
|
|
63
|
+
Boolean(normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey)));
|
|
64
|
+
const intentKey = buildStripeOneTimeCheckoutIntentKey({
|
|
65
|
+
checkoutMode,
|
|
66
|
+
couponId,
|
|
67
|
+
customerEmail,
|
|
68
|
+
plan,
|
|
69
|
+
returnUrl,
|
|
70
|
+
runtimeConfig,
|
|
71
|
+
userId,
|
|
72
|
+
});
|
|
73
|
+
const planKey = [
|
|
74
|
+
(_a = plan === null || plan === void 0 ? void 0 : plan.id) !== null && _a !== void 0 ? _a : '',
|
|
75
|
+
(_b = plan === null || plan === void 0 ? void 0 : plan.providerPlanId) !== null && _b !== void 0 ? _b : '',
|
|
76
|
+
(_c = plan === null || plan === void 0 ? void 0 : plan.amountCents) !== null && _c !== void 0 ? _c : '',
|
|
77
|
+
].join('|');
|
|
78
|
+
const activeClientSecret = clientSecretIntentKey === intentKey ? clientSecret : null;
|
|
79
|
+
const activeCheckoutSessionId = clientSecretIntentKey === intentKey ? checkoutSessionId : null;
|
|
80
|
+
const stripePromise = useMemo(() => getStripePromise(checkoutMode, runtimeStripePublishableKey !== null && runtimeStripePublishableKey !== void 0 ? runtimeStripePublishableKey : runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey), [checkoutMode, runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey, runtimeStripePublishableKey]);
|
|
81
|
+
const previousIntentKeyRef = useRef(intentKey);
|
|
82
|
+
const analyticsMetadataRef = useRef(analyticsMetadata);
|
|
83
|
+
useEffect(() => {
|
|
84
|
+
analyticsMetadataRef.current = analyticsMetadata;
|
|
85
|
+
}, [analyticsMetadata]);
|
|
86
|
+
const clearActiveCheckoutSession = useCallback(() => {
|
|
87
|
+
requestIdRef.current += 1;
|
|
88
|
+
creatingIntentRef.current = null;
|
|
89
|
+
setClientSecret(null);
|
|
90
|
+
setClientSecretIntentKey(null);
|
|
91
|
+
setCheckoutSessionId(null);
|
|
92
|
+
}, []);
|
|
93
|
+
const setError = useCallback((message) => {
|
|
94
|
+
if (isInactiveCheckoutSessionError(message)) {
|
|
95
|
+
clearActiveCheckoutSession();
|
|
96
|
+
setErrorState(null);
|
|
97
|
+
onError === null || onError === void 0 ? void 0 : onError(null);
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
setErrorState(message);
|
|
101
|
+
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
102
|
+
}, [clearActiveCheckoutSession, onError]);
|
|
103
|
+
const reset = useCallback(() => {
|
|
104
|
+
clearActiveCheckoutSession();
|
|
105
|
+
setLoading(createEmptyCheckoutPreparationLoading());
|
|
106
|
+
setRuntimeStripePublishableKey(null);
|
|
107
|
+
setError(null);
|
|
108
|
+
}, [clearActiveCheckoutSession, setError]);
|
|
109
|
+
useIsomorphicLayoutEffect(() => {
|
|
110
|
+
const previousIntentKey = previousIntentKeyRef.current;
|
|
111
|
+
previousIntentKeyRef.current = intentKey;
|
|
112
|
+
if (shouldResetStripeOneTimeCheckoutSessionForIntentKey(previousIntentKey, intentKey)) {
|
|
113
|
+
reset();
|
|
114
|
+
}
|
|
115
|
+
}, [intentKey, reset]);
|
|
116
|
+
const setSourceLoading = useCallback((source, value) => {
|
|
117
|
+
setLoading((current) => current[source] === value
|
|
118
|
+
? current
|
|
119
|
+
: Object.assign(Object.assign({}, current), { [source]: value }));
|
|
120
|
+
}, []);
|
|
121
|
+
const createCheckoutSession = useCallback(async (options = {}) => {
|
|
122
|
+
var _a;
|
|
123
|
+
const forceNew = options.forceNew === true;
|
|
124
|
+
if (!configured || !plan) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
if (!forceNew && activeClientSecret) {
|
|
128
|
+
return activeClientSecret;
|
|
129
|
+
}
|
|
130
|
+
if (!forceNew && ((_a = creatingIntentRef.current) === null || _a === void 0 ? void 0 : _a.key) === intentKey) {
|
|
131
|
+
return creatingIntentRef.current.promise;
|
|
132
|
+
}
|
|
133
|
+
if (forceNew) {
|
|
134
|
+
clearActiveCheckoutSession();
|
|
135
|
+
}
|
|
136
|
+
const requestKey = intentKey;
|
|
137
|
+
const requestId = requestIdRef.current + 1;
|
|
138
|
+
requestIdRef.current = requestId;
|
|
139
|
+
const checkoutRequest = (async () => {
|
|
140
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
141
|
+
try {
|
|
142
|
+
const response = await createStripeOneTimeCheckout({
|
|
143
|
+
plan,
|
|
144
|
+
analyticsMetadata: buildCheckoutPreparationAnalyticsMetadata(analyticsMetadataRef.current, options),
|
|
145
|
+
couponId,
|
|
146
|
+
customerEmail,
|
|
147
|
+
returnUrl,
|
|
148
|
+
user_id: userId,
|
|
149
|
+
environment: checkoutMode,
|
|
150
|
+
runtimeConfig,
|
|
151
|
+
});
|
|
152
|
+
if (((_a = creatingIntentRef.current) === null || _a === void 0 ? void 0 : _a.key) !== requestKey ||
|
|
153
|
+
((_b = creatingIntentRef.current) === null || _b === void 0 ? void 0 : _b.id) !== requestId) {
|
|
154
|
+
return response.clientSecret;
|
|
155
|
+
}
|
|
156
|
+
const publishableKey = ((_c = response.stripePublishableKey) === null || _c === void 0 ? void 0 : _c.trim()) ||
|
|
157
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey);
|
|
158
|
+
if (publishableKey) {
|
|
159
|
+
setRuntimeStripePublishableKey(publishableKey);
|
|
160
|
+
}
|
|
161
|
+
setClientSecret(response.clientSecret);
|
|
162
|
+
setClientSecretIntentKey(requestKey);
|
|
163
|
+
setCheckoutSessionId(((_d = response.checkoutSessionId) === null || _d === void 0 ? void 0 : _d.trim()) || null);
|
|
164
|
+
return response.clientSecret;
|
|
165
|
+
}
|
|
166
|
+
catch (checkoutError) {
|
|
167
|
+
if (((_e = creatingIntentRef.current) === null || _e === void 0 ? void 0 : _e.key) === requestKey &&
|
|
168
|
+
((_f = creatingIntentRef.current) === null || _f === void 0 ? void 0 : _f.id) === requestId) {
|
|
169
|
+
setError(getFriendlyStripeCheckoutError(checkoutError));
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
if (((_g = creatingIntentRef.current) === null || _g === void 0 ? void 0 : _g.key) === requestKey &&
|
|
175
|
+
((_h = creatingIntentRef.current) === null || _h === void 0 ? void 0 : _h.id) === requestId) {
|
|
176
|
+
creatingIntentRef.current = null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
})();
|
|
180
|
+
creatingIntentRef.current = {
|
|
181
|
+
id: requestId,
|
|
182
|
+
key: requestKey,
|
|
183
|
+
promise: checkoutRequest,
|
|
184
|
+
};
|
|
185
|
+
return checkoutRequest;
|
|
186
|
+
}, [
|
|
187
|
+
activeClientSecret,
|
|
188
|
+
checkoutMode,
|
|
189
|
+
clearActiveCheckoutSession,
|
|
190
|
+
configured,
|
|
191
|
+
couponId,
|
|
192
|
+
customerEmail,
|
|
193
|
+
intentKey,
|
|
194
|
+
plan,
|
|
195
|
+
returnUrl,
|
|
196
|
+
runtimeConfig,
|
|
197
|
+
setError,
|
|
198
|
+
userId,
|
|
199
|
+
]);
|
|
200
|
+
const prepareCheckout = useCallback(async (source, options = {}) => {
|
|
201
|
+
var _a;
|
|
202
|
+
if (!configured || !plan) {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
if (!options.forceNew && activeClientSecret) {
|
|
206
|
+
return activeClientSecret;
|
|
207
|
+
}
|
|
208
|
+
setSourceLoading(source, true);
|
|
209
|
+
setError(null);
|
|
210
|
+
try {
|
|
211
|
+
return await createCheckoutSession(Object.assign(Object.assign({}, options), { checkoutStartSource: (_a = options.checkoutStartSource) !== null && _a !== void 0 ? _a : (source === 'wallet' ? 'wallet_render' : 'card_click') }));
|
|
212
|
+
}
|
|
213
|
+
finally {
|
|
214
|
+
setSourceLoading(source, false);
|
|
215
|
+
}
|
|
216
|
+
}, [
|
|
217
|
+
activeClientSecret,
|
|
218
|
+
configured,
|
|
219
|
+
createCheckoutSession,
|
|
220
|
+
plan,
|
|
221
|
+
setError,
|
|
222
|
+
setSourceLoading,
|
|
223
|
+
]);
|
|
224
|
+
const prepareCardCheckout = useCallback((options = {}) => prepareCheckout('card', options), [prepareCheckout]);
|
|
225
|
+
const prepareWalletCheckout = useCallback((options = {}) => prepareCheckout('wallet', options), [prepareCheckout]);
|
|
226
|
+
const restartCardCheckout = useCallback((options = {}) => prepareCheckout('card', Object.assign(Object.assign({}, options), { forceNew: true })), [prepareCheckout]);
|
|
227
|
+
const restartWalletCheckout = useCallback((options = {}) => prepareCheckout('wallet', Object.assign(Object.assign({}, options), { forceNew: true })), [prepareCheckout]);
|
|
228
|
+
return useMemo(() => ({
|
|
229
|
+
activeClientSecret,
|
|
230
|
+
checkoutSessionId: activeCheckoutSessionId,
|
|
231
|
+
configured,
|
|
232
|
+
error,
|
|
233
|
+
intentKey,
|
|
234
|
+
loading,
|
|
235
|
+
prepareCardCheckout,
|
|
236
|
+
prepareWalletCheckout,
|
|
237
|
+
provider: 'stripe',
|
|
238
|
+
reset,
|
|
239
|
+
restartCardCheckout,
|
|
240
|
+
restartWalletCheckout,
|
|
241
|
+
planKey,
|
|
242
|
+
setError,
|
|
243
|
+
stripePromise,
|
|
244
|
+
}), [
|
|
245
|
+
activeCheckoutSessionId,
|
|
246
|
+
activeClientSecret,
|
|
247
|
+
configured,
|
|
248
|
+
error,
|
|
249
|
+
intentKey,
|
|
250
|
+
loading,
|
|
251
|
+
planKey,
|
|
252
|
+
prepareCardCheckout,
|
|
253
|
+
prepareWalletCheckout,
|
|
254
|
+
reset,
|
|
255
|
+
restartCardCheckout,
|
|
256
|
+
restartWalletCheckout,
|
|
257
|
+
setError,
|
|
258
|
+
stripePromise,
|
|
259
|
+
]);
|
|
260
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
1
|
+
import type { FunnelStepType, RuntimeMode } from '@funnelsgrove/runtime';
|
|
2
2
|
import { type StripeRuntimeConfigOverrides } from './stripe.service.js';
|
|
3
3
|
export type StripeSubscriptionCheckoutAnalyticsContext = {
|
|
4
4
|
environment?: RuntimeMode;
|
|
5
5
|
featureFlags?: Record<string, string | null | undefined>;
|
|
6
6
|
metadata?: Record<string, unknown> | null;
|
|
7
7
|
runtimeConfig?: StripeRuntimeConfigOverrides;
|
|
8
|
-
stepId
|
|
9
|
-
stepName
|
|
10
|
-
stepType
|
|
8
|
+
stepId: string;
|
|
9
|
+
stepName: string;
|
|
10
|
+
stepType: Extract<FunnelStepType, 'checkout' | 'paywall_offer' | 'upsell_offer'>;
|
|
11
|
+
stepContractVersion: number;
|
|
11
12
|
};
|
|
12
13
|
export type TrackStripeSubscriptionCheckoutStartedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
13
14
|
checkoutSessionId?: string | null;
|
|
@@ -15,7 +16,7 @@ export type TrackStripeSubscriptionCheckoutStartedInput = StripeSubscriptionChec
|
|
|
15
16
|
export type TrackPaidStripeSubscriptionCheckoutCompletedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
16
17
|
checkoutSessionId?: string | null;
|
|
17
18
|
};
|
|
18
|
-
export type TrackStripeSubscriptionCheckoutStartedResult = 'already_tracked' | 'missing_checkout_session' | 'tracked';
|
|
19
|
-
export type TrackPaidStripeSubscriptionCheckoutCompletedResult = 'already_tracked' | 'missing_checkout_session' | 'tracked' | 'unpaid';
|
|
19
|
+
export type TrackStripeSubscriptionCheckoutStartedResult = 'already_tracked' | 'analytics_rejected' | 'missing_checkout_session' | 'tracked';
|
|
20
|
+
export type TrackPaidStripeSubscriptionCheckoutCompletedResult = 'already_tracked' | 'analytics_rejected' | 'missing_checkout_session' | 'tracked' | 'unpaid';
|
|
20
21
|
export declare function trackStripeSubscriptionCheckoutStarted(input: TrackStripeSubscriptionCheckoutStartedInput): Promise<TrackStripeSubscriptionCheckoutStartedResult>;
|
|
21
22
|
export declare function trackPaidStripeSubscriptionCheckoutCompleted(input: TrackPaidStripeSubscriptionCheckoutCompletedInput): Promise<TrackPaidStripeSubscriptionCheckoutCompletedResult>;
|
|
@@ -56,9 +56,13 @@ export async function trackStripeSubscriptionCheckoutStarted(input) {
|
|
|
56
56
|
stepId: input.stepId,
|
|
57
57
|
stepName: input.stepName,
|
|
58
58
|
stepType: input.stepType,
|
|
59
|
+
stepContractVersion: input.stepContractVersion,
|
|
59
60
|
metadata: Object.assign(Object.assign({}, metadata), { checkoutSessionId, checkout_session_id: checkoutSessionId, environment: input.environment || metadata.environment }),
|
|
60
61
|
};
|
|
61
|
-
publicAnalyticsSdk.trackCheckoutStarted(conversionInput);
|
|
62
|
+
const eventId = publicAnalyticsSdk.trackCheckoutStarted(conversionInput);
|
|
63
|
+
if (!eventId) {
|
|
64
|
+
return 'analytics_rejected';
|
|
65
|
+
}
|
|
62
66
|
markCheckoutSessionTracked('checkout_started', checkoutSessionId, input.runtimeConfig);
|
|
63
67
|
await publicAnalyticsSdk.flush().catch(() => 0);
|
|
64
68
|
return 'tracked';
|
|
@@ -95,9 +99,13 @@ export async function trackPaidStripeSubscriptionCheckoutCompleted(input) {
|
|
|
95
99
|
stepId: input.stepId,
|
|
96
100
|
stepName: input.stepName,
|
|
97
101
|
stepType: input.stepType,
|
|
102
|
+
stepContractVersion: input.stepContractVersion,
|
|
98
103
|
metadata: Object.assign(Object.assign(Object.assign({}, asRecord(input.metadata)), analyticsMetadata), { checkoutSessionId, checkout_session_id: checkoutSessionId, checkout_session_status: checkoutStatus.status || undefined, payment_status: checkoutStatus.paymentStatus || undefined, environment: checkoutStatus.environment || input.environment || analyticsMetadata.environment }),
|
|
99
104
|
};
|
|
100
|
-
publicAnalyticsSdk.trackCheckoutCompleted(conversionInput);
|
|
105
|
+
const eventId = publicAnalyticsSdk.trackCheckoutCompleted(conversionInput);
|
|
106
|
+
if (!eventId) {
|
|
107
|
+
return 'analytics_rejected';
|
|
108
|
+
}
|
|
101
109
|
markCheckoutSessionTracked('checkout_completed', checkoutSessionId, input.runtimeConfig);
|
|
102
110
|
await publicAnalyticsSdk.flush().catch(() => 0);
|
|
103
111
|
return 'tracked';
|
|
@@ -20,6 +20,11 @@ export type PaywallPlan = {
|
|
|
20
20
|
checkoutSummaryLabel?: string;
|
|
21
21
|
billingInterval?: BillingPlanInterval;
|
|
22
22
|
billingIntervalCount?: number;
|
|
23
|
+
followUpProviderPlanId?: string;
|
|
24
|
+
followUpPlanTitle?: string;
|
|
25
|
+
followUpBillingInterval?: BillingPlanInterval;
|
|
26
|
+
followUpBillingIntervalCount?: number;
|
|
27
|
+
introIterations?: number;
|
|
23
28
|
source: 'stripe' | 'config';
|
|
24
29
|
};
|
|
25
30
|
export type StripeRuntimeConfigOverrides = FunnelSdkRuntimeConfigOverrides;
|
|
@@ -40,7 +45,7 @@ export declare function resolvePaywallPlansFromProjectPlans(projectPlans: readon
|
|
|
40
45
|
export declare function getPaywallPlans(mode: RuntimeMode, planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): Promise<readonly PaywallPlan[]>;
|
|
41
46
|
export declare function isStripeConfigured(mode: RuntimeMode): boolean;
|
|
42
47
|
export declare function getStripePromise(mode: RuntimeMode, runtimePublishableKey?: string | null): Promise<Stripe | null> | null;
|
|
43
|
-
type CheckoutSessionPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'billingInterval' | 'billingIntervalCount' | 'description' | 'id' | 'providerPlanId' | 'title'>;
|
|
48
|
+
type CheckoutSessionPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'billingInterval' | 'billingIntervalCount' | 'description' | 'id' | 'providerPlanId' | 'title' | 'followUpProviderPlanId' | 'followUpPlanTitle' | 'followUpBillingInterval' | 'followUpBillingIntervalCount' | 'introIterations'>;
|
|
44
49
|
type CheckoutPlanRecurringConfig = {
|
|
45
50
|
interval: BillingPlanInterval;
|
|
46
51
|
intervalCount: number;
|
|
@@ -268,6 +268,11 @@ const toPlanFromRemoteProjectPlan = (rawPlan, options, planIdOverride) => {
|
|
|
268
268
|
checkoutSummaryLabel: asTrimmedStringOrNull(metadata.checkoutSummaryLabel) || undefined,
|
|
269
269
|
billingInterval: asBillingPlanIntervalOrNull(interval) || undefined,
|
|
270
270
|
billingIntervalCount: intervalCount || undefined,
|
|
271
|
+
followUpProviderPlanId: asTrimmedStringOrNull(metadata.followUpProviderPlanId) || undefined,
|
|
272
|
+
followUpPlanTitle: asTrimmedStringOrNull(metadata.followUpPlanTitle) || undefined,
|
|
273
|
+
followUpBillingInterval: asBillingPlanIntervalOrNull(metadata.followUpBillingInterval) || undefined,
|
|
274
|
+
followUpBillingIntervalCount: asPositiveIntOrNull(metadata.followUpBillingIntervalCount) || undefined,
|
|
275
|
+
introIterations: asPositiveIntOrNull(metadata.introIterations) || undefined,
|
|
271
276
|
source: 'stripe',
|
|
272
277
|
};
|
|
273
278
|
};
|
|
@@ -579,7 +584,7 @@ export async function chargeStripeOneClickPayment(input) {
|
|
|
579
584
|
});
|
|
580
585
|
}
|
|
581
586
|
export async function createStripeSubscriptionCheckout(input) {
|
|
582
|
-
var _a, _b, _c, _d, _e;
|
|
587
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
583
588
|
const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
|
|
584
589
|
if (!recurring) {
|
|
585
590
|
throw new Error('Unable to resolve the recurring interval for the selected plan');
|
|
@@ -592,17 +597,22 @@ export async function createStripeSubscriptionCheckout(input) {
|
|
|
592
597
|
amountCents: input.plan.amountCents,
|
|
593
598
|
billingInterval: recurring.interval,
|
|
594
599
|
billingIntervalCount: recurring.intervalCount,
|
|
595
|
-
|
|
600
|
+
followUpProviderPlanId: ((_c = input.plan.followUpProviderPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
601
|
+
followUpPlanTitle: ((_d = input.plan.followUpPlanTitle) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
602
|
+
followUpBillingInterval: input.plan.followUpBillingInterval,
|
|
603
|
+
followUpBillingIntervalCount: input.plan.followUpBillingIntervalCount,
|
|
604
|
+
introIterations: input.plan.introIterations,
|
|
605
|
+
couponId: ((_e = input.couponId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
|
|
596
606
|
analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
|
|
597
|
-
customerEmail: ((
|
|
607
|
+
customerEmail: ((_f = input.customerEmail) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
|
|
598
608
|
returnUrl: input.returnUrl,
|
|
599
|
-
userId: ((
|
|
609
|
+
userId: ((_g = input.user_id) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
|
|
600
610
|
environment: input.environment,
|
|
601
611
|
runtimeConfig: input.runtimeConfig,
|
|
602
612
|
});
|
|
603
613
|
}
|
|
604
614
|
export async function updateStripeSubscriptionCheckoutPlan(input) {
|
|
605
|
-
var _a, _b, _c, _d, _e;
|
|
615
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
606
616
|
const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
|
|
607
617
|
if (!recurring) {
|
|
608
618
|
throw new Error('Unable to resolve the recurring interval for the selected plan');
|
|
@@ -616,10 +626,15 @@ export async function updateStripeSubscriptionCheckoutPlan(input) {
|
|
|
616
626
|
amountCents: input.plan.amountCents,
|
|
617
627
|
billingInterval: recurring.interval,
|
|
618
628
|
billingIntervalCount: recurring.intervalCount,
|
|
619
|
-
|
|
629
|
+
followUpProviderPlanId: ((_c = input.plan.followUpProviderPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
630
|
+
followUpPlanTitle: ((_d = input.plan.followUpPlanTitle) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
631
|
+
followUpBillingInterval: input.plan.followUpBillingInterval,
|
|
632
|
+
followUpBillingIntervalCount: input.plan.followUpBillingIntervalCount,
|
|
633
|
+
introIterations: input.plan.introIterations,
|
|
634
|
+
couponId: ((_e = input.couponId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
|
|
620
635
|
analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
|
|
621
|
-
customerEmail: ((
|
|
622
|
-
userId: ((
|
|
636
|
+
customerEmail: ((_f = input.customerEmail) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
|
|
637
|
+
userId: ((_g = input.user_id) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
|
|
623
638
|
environment: input.environment,
|
|
624
639
|
runtimeConfig: input.runtimeConfig,
|
|
625
640
|
});
|