@funnelsgrove/payments 0.1.59 → 0.3.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 +3 -2
- package/dist/components/shared/SharedStripeCheckoutDialog.js +6 -3
- package/dist/components/shared/SharedStripeCheckoutV2Dialog.js +6 -3
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.js +7 -4
- package/dist/components/shared/StripeExpressCheckoutButton.js +7 -4
- package/dist/config/billing.config.d.ts +3 -0
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.js +1 -1
- package/dist/services/checkoutCompletionAnalytics.service.d.ts +1 -0
- package/dist/services/checkoutCompletionAnalytics.service.js +6 -1
- package/dist/services/planCatalog.service.d.ts +2 -0
- package/dist/services/planCatalog.service.js +7 -4
- package/dist/services/stripe.service.d.ts +4 -1
- package/dist/services/stripe.service.js +66 -39
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -87,8 +87,9 @@ When a live funnel still uses one of these, migrate the funnel deliberately with
|
|
|
87
87
|
- Keep provider-specific implementation under `src/providers/<provider>`.
|
|
88
88
|
- Stripe owns real card fields and wallet controls. Local wallet buttons are placeholders until Stripe confirms availability.
|
|
89
89
|
- Wallet unavailable state must fall back to card/manual checkout or disappear.
|
|
90
|
-
-
|
|
91
|
-
- Wallet slots may create render-only Checkout Sessions on mount so Apple Pay and Google Pay render before the visitor taps.
|
|
90
|
+
- 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.
|
|
91
|
+
- 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`.
|
|
92
|
+
- Shared checkout analytics deduplicates `checkout_started` by Checkout Session id. Card submit and wallet confirmation must not emit it again.
|
|
92
93
|
- 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.
|
|
93
94
|
- Keep discount math reusable here, but keep offer activation timing in funnel code.
|
|
94
95
|
- Checkout metadata must avoid sensitive raw query params and use `@funnelsgrove/analytics` metadata helpers.
|
|
@@ -191,9 +191,6 @@ function SharedStripeCheckoutForm({ amountCents, checkoutAnalytics, checkoutSess
|
|
|
191
191
|
setSubmitting(false);
|
|
192
192
|
return;
|
|
193
193
|
}
|
|
194
|
-
if (checkoutAnalytics) {
|
|
195
|
-
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, paymentMethod: 'card' }));
|
|
196
|
-
}
|
|
197
194
|
const result = await stripe.confirmCardPayment(clientSecret, {
|
|
198
195
|
payment_method: {
|
|
199
196
|
card: cardNumberElement,
|
|
@@ -249,6 +246,12 @@ function SharedStripeCheckoutForm({ amountCents, checkoutAnalytics, checkoutSess
|
|
|
249
246
|
}
|
|
250
247
|
export function SharedStripeCheckoutDialog(_a) {
|
|
251
248
|
var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise"]);
|
|
249
|
+
useEffect(() => {
|
|
250
|
+
if (!checkoutAnalytics) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, checkoutStartSource: 'card_form_open', paymentMethod: 'card' }));
|
|
254
|
+
}, [checkoutAnalytics, checkoutSessionId]);
|
|
252
255
|
useEffect(() => {
|
|
253
256
|
const handleKeyDown = (event) => {
|
|
254
257
|
if (event.key === 'Escape') {
|
|
@@ -250,9 +250,6 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
|
|
|
250
250
|
setSubmitting(false);
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
|
-
if (checkoutAnalytics) {
|
|
254
|
-
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, paymentMethod: 'card' }));
|
|
255
|
-
}
|
|
256
253
|
await (onPaymentInfoSubmitted === null || onPaymentInfoSubmitted === void 0 ? void 0 : onPaymentInfoSubmitted());
|
|
257
254
|
const result = await checkoutState.checkout.confirm({
|
|
258
255
|
redirect: 'if_required',
|
|
@@ -330,6 +327,12 @@ export function SharedStripeCheckoutV2Dialog(_a) {
|
|
|
330
327
|
var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, supportedCountries } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise", "supportedCountries"]);
|
|
331
328
|
const normalizedSupportedCountries = useMemo(() => normalizeSupportedCheckoutCountries(supportedCountries), [supportedCountries]);
|
|
332
329
|
const fixedBillingCountry = getFixedSupportedBillingCountry(normalizedSupportedCountries);
|
|
330
|
+
useEffect(() => {
|
|
331
|
+
if (!checkoutAnalytics) {
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, checkoutStartSource: 'card_form_open', paymentMethod: 'card' }));
|
|
335
|
+
}, [checkoutAnalytics, checkoutSessionId]);
|
|
333
336
|
useEffect(() => {
|
|
334
337
|
const handleKeyDown = (event) => {
|
|
335
338
|
if (event.key === 'Escape') {
|
|
@@ -123,9 +123,6 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
|
|
|
123
123
|
event.paymentFailed({ reason: 'fail', message });
|
|
124
124
|
return;
|
|
125
125
|
}
|
|
126
|
-
if (checkoutAnalytics) {
|
|
127
|
-
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, paymentMethod: event.expressPaymentType }));
|
|
128
|
-
}
|
|
129
126
|
let preparedCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || null;
|
|
130
127
|
if (beforeConfirm) {
|
|
131
128
|
try {
|
|
@@ -162,13 +159,19 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
|
|
|
162
159
|
window.location.assign(returnUrl);
|
|
163
160
|
}
|
|
164
161
|
};
|
|
162
|
+
const handleClick = (event) => {
|
|
163
|
+
if (checkoutAnalytics) {
|
|
164
|
+
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, checkoutStartSource: 'wallet_click', paymentMethod: event.expressPaymentType }));
|
|
165
|
+
}
|
|
166
|
+
event.resolve();
|
|
167
|
+
};
|
|
165
168
|
return (_jsxs(_Fragment, { children: [_jsx("div", { className: [
|
|
166
169
|
'stripe-express-checkout-button',
|
|
167
170
|
className !== null && className !== void 0 ? className : '',
|
|
168
171
|
effectiveWalletAvailable === false ? 'is-hidden' : '',
|
|
169
172
|
]
|
|
170
173
|
.filter(Boolean)
|
|
171
|
-
.join(' '), children: _jsx(ExpressCheckoutElementWithCancel, { options: resolvedOptions, onCancel: onCancel, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
174
|
+
.join(' '), children: _jsx(ExpressCheckoutElementWithCancel, { options: resolvedOptions, onCancel: onCancel, onClick: handleClick, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
172
175
|
setWalletAvailable(false);
|
|
173
176
|
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(false);
|
|
174
177
|
onError === null || onError === void 0 ? void 0 : onError(error.message || 'Unable to load wallet checkout.');
|
|
@@ -78,9 +78,6 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checko
|
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
80
|
onError === null || onError === void 0 ? void 0 : onError(null);
|
|
81
|
-
if (checkoutAnalytics) {
|
|
82
|
-
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, paymentMethod: event.expressPaymentType }));
|
|
83
|
-
}
|
|
84
81
|
let preparedCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || null;
|
|
85
82
|
if (beforeConfirm) {
|
|
86
83
|
try {
|
|
@@ -138,13 +135,19 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checko
|
|
|
138
135
|
window.location.assign(returnUrl);
|
|
139
136
|
}
|
|
140
137
|
};
|
|
138
|
+
const handleClick = (event) => {
|
|
139
|
+
if (checkoutAnalytics) {
|
|
140
|
+
queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, checkoutStartSource: 'wallet_click', paymentMethod: event.expressPaymentType }));
|
|
141
|
+
}
|
|
142
|
+
event.resolve();
|
|
143
|
+
};
|
|
141
144
|
return (_jsxs(_Fragment, { children: [_jsx("div", { className: [
|
|
142
145
|
'stripe-express-checkout-button',
|
|
143
146
|
className !== null && className !== void 0 ? className : '',
|
|
144
147
|
effectiveWalletAvailable === false ? 'is-hidden' : '',
|
|
145
148
|
]
|
|
146
149
|
.filter(Boolean)
|
|
147
|
-
.join(' '), children: _jsx(ExpressCheckoutElement, { options: resolvedOptions, onCancel: onCancel, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
150
|
+
.join(' '), children: _jsx(ExpressCheckoutElement, { options: resolvedOptions, onCancel: onCancel, onClick: handleClick, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
148
151
|
setWalletAvailable(false);
|
|
149
152
|
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(false);
|
|
150
153
|
onError === null || onError === void 0 ? void 0 : onError(error.message || 'Unable to load wallet checkout.');
|
|
@@ -6,6 +6,7 @@ export type BillingPlanComparison = {
|
|
|
6
6
|
};
|
|
7
7
|
export type BillingPlanInterval = 'day' | 'week' | 'month' | 'year';
|
|
8
8
|
export type BillingPlanConfig = {
|
|
9
|
+
offerSetId?: string;
|
|
9
10
|
offerSetKey?: string;
|
|
10
11
|
projectPlanId: string;
|
|
11
12
|
title: string;
|
|
@@ -36,6 +37,8 @@ export type BillingFallbackPlanConfig = BillingPlanConfig & {
|
|
|
36
37
|
export type BillingPlanCatalog = Record<string, BillingFallbackPlanConfig>;
|
|
37
38
|
export type BillingPlanMappingConfig = {
|
|
38
39
|
projectPlanId: string;
|
|
40
|
+
offerSetId?: string;
|
|
41
|
+
offerSetKey?: string;
|
|
39
42
|
};
|
|
40
43
|
export type BillingPlanMappingCatalog = Record<string, BillingPlanMappingConfig>;
|
|
41
44
|
export type BillingPlanInputCatalog = BillingPlanCatalog | BillingPlanMappingCatalog;
|
|
@@ -121,7 +121,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
121
121
|
return;
|
|
122
122
|
}
|
|
123
123
|
walletCheckoutStartedAfterClickSessionIdRef.current = session.checkoutSessionId;
|
|
124
|
-
void trackStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId: session.checkoutSessionId, paymentMethod: analyticsPaymentMethod }));
|
|
124
|
+
void trackStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId: session.checkoutSessionId, checkoutStartSource: 'wallet_click', paymentMethod: analyticsPaymentMethod }));
|
|
125
125
|
}, [
|
|
126
126
|
analyticsPaymentMethod,
|
|
127
127
|
checkoutAnalytics,
|
|
@@ -12,6 +12,7 @@ export type StripeSubscriptionCheckoutAnalyticsContext = {
|
|
|
12
12
|
};
|
|
13
13
|
export type TrackStripeSubscriptionCheckoutStartedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
14
14
|
checkoutSessionId?: string | null;
|
|
15
|
+
checkoutStartSource?: 'card_form_open' | 'wallet_click';
|
|
15
16
|
paymentMethod?: unknown;
|
|
16
17
|
};
|
|
17
18
|
export type TrackPaidStripeSubscriptionCheckoutCompletedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
@@ -70,7 +70,12 @@ export async function trackStripeSubscriptionCheckoutStarted(input) {
|
|
|
70
70
|
stepName: input.stepName,
|
|
71
71
|
stepType: input.stepType,
|
|
72
72
|
stepContractVersion: input.stepContractVersion,
|
|
73
|
-
metadata: Object.assign(Object.assign(Object.assign({}, metadata), { checkoutSessionId, checkout_session_id: checkoutSessionId
|
|
73
|
+
metadata: Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, metadata), { checkoutSessionId, checkout_session_id: checkoutSessionId }), (input.checkoutStartSource
|
|
74
|
+
? {
|
|
75
|
+
checkoutStartSource: input.checkoutStartSource,
|
|
76
|
+
checkout_start_source: input.checkoutStartSource,
|
|
77
|
+
}
|
|
78
|
+
: {})), { environment: input.environment || metadata.environment, payment_provider: 'stripe' }), (paymentMethod ? { payment_method: paymentMethod } : {})),
|
|
74
79
|
};
|
|
75
80
|
const eventId = publicAnalyticsSdk.trackCheckoutStarted(conversionInput);
|
|
76
81
|
if (!eventId) {
|
|
@@ -2,6 +2,8 @@ import type { BillingFallbackPlanConfig, BillingPlanCatalog, BillingPlanMappingC
|
|
|
2
2
|
export type RemoteProjectBillingPlan = {
|
|
3
3
|
id: string;
|
|
4
4
|
key: string;
|
|
5
|
+
offerSetId?: string;
|
|
6
|
+
offerSetKey?: string;
|
|
5
7
|
projectPlanId: string | null;
|
|
6
8
|
providerPlanId: string;
|
|
7
9
|
displayName: string | null;
|
|
@@ -5,7 +5,11 @@ const isBillingPlanMappingConfig = (value) => {
|
|
|
5
5
|
if (!isRecord(value) || typeof value.projectPlanId !== 'string') {
|
|
6
6
|
return false;
|
|
7
7
|
}
|
|
8
|
-
|
|
8
|
+
if ((value.offerSetId !== undefined && typeof value.offerSetId !== 'string')
|
|
9
|
+
|| (value.offerSetKey !== undefined && typeof value.offerSetKey !== 'string')) {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
return Object.keys(value).every((key) => (key === 'projectPlanId' || key === 'offerSetId' || key === 'offerSetKey'));
|
|
9
13
|
};
|
|
10
14
|
export const isBillingPlanMappingCatalog = (value) => {
|
|
11
15
|
if (!isRecord(value)) {
|
|
@@ -16,6 +20,7 @@ export const isBillingPlanMappingCatalog = (value) => {
|
|
|
16
20
|
};
|
|
17
21
|
export const buildBillingPlanMappingCatalog = (planCatalog) => {
|
|
18
22
|
return Object.fromEntries(Object.entries(planCatalog).flatMap(([key, plan]) => {
|
|
23
|
+
var _a, _b;
|
|
19
24
|
const planKey = key.trim();
|
|
20
25
|
const projectPlanId = plan.projectPlanId.trim();
|
|
21
26
|
if (!planKey || !projectPlanId) {
|
|
@@ -24,9 +29,7 @@ export const buildBillingPlanMappingCatalog = (planCatalog) => {
|
|
|
24
29
|
return [
|
|
25
30
|
[
|
|
26
31
|
planKey,
|
|
27
|
-
{
|
|
28
|
-
projectPlanId,
|
|
29
|
-
},
|
|
32
|
+
Object.assign(Object.assign({ projectPlanId }, (((_a = plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) ? { offerSetId: plan.offerSetId.trim() } : {})), (((_b = plan.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) ? { offerSetKey: plan.offerSetKey.trim() } : {})),
|
|
30
33
|
],
|
|
31
34
|
];
|
|
32
35
|
}));
|
|
@@ -5,6 +5,7 @@ import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
|
5
5
|
import { type RemoteProjectBillingPlan } from './planCatalog.service.js';
|
|
6
6
|
export type PaywallPlan = {
|
|
7
7
|
id: string;
|
|
8
|
+
offerSetId?: string;
|
|
8
9
|
offerSetKey?: string;
|
|
9
10
|
title: string;
|
|
10
11
|
description?: string;
|
|
@@ -46,7 +47,7 @@ export declare function resolvePaywallPlansFromProjectPlans(projectPlans: readon
|
|
|
46
47
|
export declare function getPaywallPlans(mode: RuntimeMode, planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): Promise<readonly PaywallPlan[]>;
|
|
47
48
|
export declare function isStripeConfigured(mode: RuntimeMode): boolean;
|
|
48
49
|
export declare function getStripePromise(_mode: RuntimeMode, runtimePublishableKey?: string | null): Promise<Stripe | null> | null;
|
|
49
|
-
type CheckoutSessionPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'billingInterval' | 'billingIntervalCount' | 'description' | 'id' | 'providerPlanId' | 'title' | 'followUpProviderPlanId' | 'followUpPlanTitle' | 'followUpBillingInterval' | 'followUpBillingIntervalCount' | 'introIterations' | 'offerSetKey'>;
|
|
50
|
+
type CheckoutSessionPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'billingInterval' | 'billingIntervalCount' | 'description' | 'id' | 'providerPlanId' | 'title' | 'followUpProviderPlanId' | 'followUpPlanTitle' | 'followUpBillingInterval' | 'followUpBillingIntervalCount' | 'introIterations' | 'offerSetId' | 'offerSetKey'>;
|
|
50
51
|
type CheckoutPlanRecurringConfig = {
|
|
51
52
|
interval: BillingPlanInterval;
|
|
52
53
|
intervalCount: number;
|
|
@@ -54,6 +55,8 @@ type CheckoutPlanRecurringConfig = {
|
|
|
54
55
|
export declare const resolveCheckoutPlanRecurringConfig: (plan: CheckoutSessionPlanInput) => CheckoutPlanRecurringConfig | null;
|
|
55
56
|
type CreatePaymentIntentInput = {
|
|
56
57
|
planId: string;
|
|
58
|
+
offerSetId?: string | null;
|
|
59
|
+
offerSetKey?: string | null;
|
|
57
60
|
amountCents: number;
|
|
58
61
|
couponId?: string | null;
|
|
59
62
|
analyticsMetadata?: Record<string, unknown> | null;
|
|
@@ -194,6 +194,8 @@ const toRemoteProjectBillingPlan = (rawPlan) => {
|
|
|
194
194
|
return {
|
|
195
195
|
id: asTrimmedStringOrNull(rawPlan.id) || providerPlanId,
|
|
196
196
|
key: asTrimmedStringOrNull(rawPlan.key) || providerPlanId,
|
|
197
|
+
offerSetId: asTrimmedStringOrNull(rawPlan.offerSetId) || undefined,
|
|
198
|
+
offerSetKey: asTrimmedStringOrNull(rawPlan.offerSetKey) || undefined,
|
|
197
199
|
projectPlanId: asTrimmedStringOrNull(rawPlan.projectPlanId),
|
|
198
200
|
providerPlanId,
|
|
199
201
|
displayName: asTrimmedStringOrNull(rawPlan.displayName),
|
|
@@ -237,6 +239,8 @@ const toPlanFromRemoteProjectPlan = (rawPlan, options, planIdOverride) => {
|
|
|
237
239
|
: formatMoneyFromMinor(amountMinor, currencyCode, locale);
|
|
238
240
|
return {
|
|
239
241
|
id: planId,
|
|
242
|
+
offerSetId: rawPlan.offerSetId,
|
|
243
|
+
offerSetKey: rawPlan.offerSetKey,
|
|
240
244
|
title,
|
|
241
245
|
description: rawPlan.description || asTrimmedStringOrNull(metadata.description) || undefined,
|
|
242
246
|
providerPlanId: rawPlan.providerPlanId,
|
|
@@ -369,8 +373,8 @@ export function getDefaultPlanSelectionValue(plans, pricing) {
|
|
|
369
373
|
}
|
|
370
374
|
return getPaywallPlanSelectionValue(plans.find((plan) => plan.id === defaultPlanId) || plans[0]);
|
|
371
375
|
}
|
|
372
|
-
const getStripeSyncedPlanRecords = async (mode) => {
|
|
373
|
-
const cacheKey = `${mode}:${FUNNEL_ID || 'no-funnel'}:${FUNNEL_SDK_PUBLISHABLE_KEY}`;
|
|
376
|
+
const getStripeSyncedPlanRecords = async (mode, offerSet) => {
|
|
377
|
+
const cacheKey = `${mode}:${FUNNEL_ID || 'no-funnel'}:${FUNNEL_SDK_PUBLISHABLE_KEY}:${offerSet.offerSetId || ''}:${offerSet.offerSetKey || ''}`;
|
|
374
378
|
const cachedPromise = syncedPlansPromiseByMode.get(cacheKey);
|
|
375
379
|
if (cachedPromise) {
|
|
376
380
|
return cachedPromise;
|
|
@@ -378,6 +382,8 @@ const getStripeSyncedPlanRecords = async (mode) => {
|
|
|
378
382
|
const nextPromise = (async () => {
|
|
379
383
|
const payload = await funnelSdkService.listPaymentPlans({
|
|
380
384
|
environment: mode,
|
|
385
|
+
offerSetId: offerSet.offerSetId,
|
|
386
|
+
offerSetKey: offerSet.offerSetKey,
|
|
381
387
|
});
|
|
382
388
|
const rawPlans = Array.isArray(payload.plans) ? payload.plans : [];
|
|
383
389
|
return rawPlans
|
|
@@ -388,8 +394,23 @@ const getStripeSyncedPlanRecords = async (mode) => {
|
|
|
388
394
|
syncedPlansPromiseByMode.set(cacheKey, nextPromise);
|
|
389
395
|
return nextPromise;
|
|
390
396
|
};
|
|
397
|
+
const resolvePlanCatalogOfferSet = (planCatalog) => {
|
|
398
|
+
const configuredPlans = planCatalog
|
|
399
|
+
? (Array.isArray(planCatalog) ? planCatalog : Object.values(planCatalog))
|
|
400
|
+
: [];
|
|
401
|
+
const offerSetIds = [...new Set(configuredPlans
|
|
402
|
+
.map((plan) => { var _a; return (_a = plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim(); })
|
|
403
|
+
.filter((value) => Boolean(value)))];
|
|
404
|
+
const offerSetKeys = [...new Set(configuredPlans
|
|
405
|
+
.map((plan) => { var _a; return (_a = plan.offerSetKey) === null || _a === void 0 ? void 0 : _a.trim(); })
|
|
406
|
+
.filter((value) => Boolean(value)))];
|
|
407
|
+
return {
|
|
408
|
+
offerSetId: offerSetIds.length === 1 ? offerSetIds[0] : undefined,
|
|
409
|
+
offerSetKey: offerSetKeys.length === 1 ? offerSetKeys[0] : undefined,
|
|
410
|
+
};
|
|
411
|
+
};
|
|
391
412
|
export async function getStripeSyncedPlans(mode, planCatalog, options) {
|
|
392
|
-
const rawPlans = await getStripeSyncedPlanRecords(mode);
|
|
413
|
+
const rawPlans = await getStripeSyncedPlanRecords(mode, resolvePlanCatalogOfferSet(planCatalog));
|
|
393
414
|
const mappedPlans = (planCatalog && !Array.isArray(planCatalog) && isBillingPlanMappingCatalog(planCatalog)
|
|
394
415
|
? resolveMappedProjectPlans(planCatalog, rawPlans).map((rawPlan) => toPlanFromRemoteProjectPlan(rawPlan, options, rawPlan.funnelKey))
|
|
395
416
|
: rawPlans.map((rawPlan) => toPlanFromRemoteProjectPlan(rawPlan, options)))
|
|
@@ -410,7 +431,7 @@ export async function getPaywallPlans(mode, planCatalog, options) {
|
|
|
410
431
|
return getFallbackPlans(planCatalog, options);
|
|
411
432
|
}
|
|
412
433
|
try {
|
|
413
|
-
const syncedPlans = resolvePaywallPlansFromProjectPlans(await getStripeSyncedPlanRecords(mode), planCatalog, options);
|
|
434
|
+
const syncedPlans = resolvePaywallPlansFromProjectPlans(await getStripeSyncedPlanRecords(mode, resolvePlanCatalogOfferSet(planCatalog)), planCatalog, options);
|
|
414
435
|
if (syncedPlans.length > 0) {
|
|
415
436
|
return syncedPlans;
|
|
416
437
|
}
|
|
@@ -529,19 +550,20 @@ const normalizeCheckoutAnalyticsMetadata = (metadata, plan) => {
|
|
|
529
550
|
return Object.assign(Object.assign({}, (normalized !== null && normalized !== void 0 ? normalized : {})), { analyticsPurchaseValue });
|
|
530
551
|
};
|
|
531
552
|
export async function createStripeOneTimeCheckout(input) {
|
|
532
|
-
var _a, _b, _c, _d, _e, _f;
|
|
553
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
533
554
|
return funnelSdkService.createOneTimeCheckout({
|
|
534
|
-
|
|
555
|
+
offerSetId: ((_a = input.plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
|
|
556
|
+
offerSetKey: ((_b = input.plan.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
|
|
535
557
|
planId: getPaywallPlanSelectionValue(input.plan),
|
|
536
|
-
providerPlanId: ((
|
|
558
|
+
providerPlanId: ((_c = input.plan.providerPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
537
559
|
title: input.plan.title,
|
|
538
|
-
description: ((
|
|
560
|
+
description: ((_d = input.plan.description) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
539
561
|
amountCents: input.plan.amountCents,
|
|
540
|
-
couponId: ((
|
|
562
|
+
couponId: ((_e = input.couponId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
|
|
541
563
|
analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
|
|
542
|
-
customerEmail: ((
|
|
564
|
+
customerEmail: ((_f = input.customerEmail) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
|
|
543
565
|
returnUrl: input.returnUrl,
|
|
544
|
-
userId: ((
|
|
566
|
+
userId: ((_g = input.user_id) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
|
|
545
567
|
environment: input.environment,
|
|
546
568
|
runtimeConfig: input.runtimeConfig,
|
|
547
569
|
});
|
|
@@ -550,13 +572,15 @@ export async function createStripeOneTimeCheckout(input) {
|
|
|
550
572
|
* @deprecated Use createStripeOneTimeCheckout for one-time paywall checkout.
|
|
551
573
|
*/
|
|
552
574
|
export async function createStripePaymentIntent(input) {
|
|
553
|
-
var _a, _b;
|
|
575
|
+
var _a, _b, _c, _d;
|
|
554
576
|
return funnelSdkService.createOneTimePaymentIntent({
|
|
577
|
+
offerSetId: ((_a = input.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
|
|
578
|
+
offerSetKey: ((_b = input.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
|
|
555
579
|
planId: input.planId,
|
|
556
580
|
amountCents: input.amountCents,
|
|
557
|
-
couponId: ((
|
|
581
|
+
couponId: ((_c = input.couponId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
558
582
|
analyticsMetadata: normalizeAnalyticsMetadata(input.analyticsMetadata),
|
|
559
|
-
userId: ((
|
|
583
|
+
userId: ((_d = input.user_id) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
560
584
|
environment: input.environment,
|
|
561
585
|
runtimeConfig: input.runtimeConfig,
|
|
562
586
|
});
|
|
@@ -577,59 +601,61 @@ export async function chargeStripeOneClickPayment(input) {
|
|
|
577
601
|
});
|
|
578
602
|
}
|
|
579
603
|
export async function createStripeSubscriptionCheckout(input) {
|
|
580
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
604
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
581
605
|
const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
|
|
582
606
|
if (!recurring) {
|
|
583
607
|
throw new Error('Unable to resolve the recurring interval for the selected plan');
|
|
584
608
|
}
|
|
585
609
|
return funnelSdkService.createSubscriptionCheckout({
|
|
586
|
-
|
|
610
|
+
offerSetId: ((_a = input.plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
|
|
611
|
+
offerSetKey: ((_b = input.plan.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
|
|
587
612
|
planId: getPaywallPlanSelectionValue(input.plan),
|
|
588
|
-
providerPlanId: ((
|
|
613
|
+
providerPlanId: ((_c = input.plan.providerPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
589
614
|
title: input.plan.title,
|
|
590
|
-
description: ((
|
|
615
|
+
description: ((_d = input.plan.description) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
591
616
|
amountCents: input.plan.amountCents,
|
|
592
617
|
billingInterval: recurring.interval,
|
|
593
618
|
billingIntervalCount: recurring.intervalCount,
|
|
594
|
-
followUpProviderPlanId: ((
|
|
595
|
-
followUpPlanTitle: ((
|
|
619
|
+
followUpProviderPlanId: ((_e = input.plan.followUpProviderPlanId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
|
|
620
|
+
followUpPlanTitle: ((_f = input.plan.followUpPlanTitle) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
|
|
596
621
|
followUpBillingInterval: input.plan.followUpBillingInterval,
|
|
597
622
|
followUpBillingIntervalCount: input.plan.followUpBillingIntervalCount,
|
|
598
623
|
introIterations: input.plan.introIterations,
|
|
599
|
-
couponId: ((
|
|
624
|
+
couponId: ((_g = input.couponId) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
|
|
600
625
|
analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
|
|
601
|
-
customerEmail: ((
|
|
626
|
+
customerEmail: ((_h = input.customerEmail) === null || _h === void 0 ? void 0 : _h.trim()) || undefined,
|
|
602
627
|
returnUrl: input.returnUrl,
|
|
603
|
-
userId: ((
|
|
628
|
+
userId: ((_j = input.user_id) === null || _j === void 0 ? void 0 : _j.trim()) || undefined,
|
|
604
629
|
environment: input.environment,
|
|
605
630
|
runtimeConfig: input.runtimeConfig,
|
|
606
631
|
});
|
|
607
632
|
}
|
|
608
633
|
export async function updateStripeSubscriptionCheckoutPlan(input) {
|
|
609
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
634
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
610
635
|
const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
|
|
611
636
|
if (!recurring) {
|
|
612
637
|
throw new Error('Unable to resolve the recurring interval for the selected plan');
|
|
613
638
|
}
|
|
614
639
|
return funnelSdkService.updateSubscriptionCheckoutPlan({
|
|
615
640
|
checkoutSessionId: input.checkoutSessionId.trim(),
|
|
616
|
-
|
|
641
|
+
offerSetId: ((_a = input.plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
|
|
642
|
+
offerSetKey: ((_b = input.plan.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
|
|
617
643
|
planId: getPaywallPlanSelectionValue(input.plan),
|
|
618
|
-
providerPlanId: ((
|
|
644
|
+
providerPlanId: ((_c = input.plan.providerPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
619
645
|
title: input.plan.title,
|
|
620
|
-
description: ((
|
|
646
|
+
description: ((_d = input.plan.description) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
621
647
|
amountCents: input.plan.amountCents,
|
|
622
648
|
billingInterval: recurring.interval,
|
|
623
649
|
billingIntervalCount: recurring.intervalCount,
|
|
624
|
-
followUpProviderPlanId: ((
|
|
625
|
-
followUpPlanTitle: ((
|
|
650
|
+
followUpProviderPlanId: ((_e = input.plan.followUpProviderPlanId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
|
|
651
|
+
followUpPlanTitle: ((_f = input.plan.followUpPlanTitle) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
|
|
626
652
|
followUpBillingInterval: input.plan.followUpBillingInterval,
|
|
627
653
|
followUpBillingIntervalCount: input.plan.followUpBillingIntervalCount,
|
|
628
654
|
introIterations: input.plan.introIterations,
|
|
629
|
-
couponId: ((
|
|
655
|
+
couponId: ((_g = input.couponId) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
|
|
630
656
|
analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
|
|
631
|
-
customerEmail: ((
|
|
632
|
-
userId: ((
|
|
657
|
+
customerEmail: ((_h = input.customerEmail) === null || _h === void 0 ? void 0 : _h.trim()) || undefined,
|
|
658
|
+
userId: ((_j = input.user_id) === null || _j === void 0 ? void 0 : _j.trim()) || undefined,
|
|
633
659
|
environment: input.environment,
|
|
634
660
|
runtimeConfig: input.runtimeConfig,
|
|
635
661
|
});
|
|
@@ -646,26 +672,27 @@ export async function verifyStripeSubscriptionCheckoutPayment(input) {
|
|
|
646
672
|
});
|
|
647
673
|
}
|
|
648
674
|
export async function createStripeCheckoutSession(input) {
|
|
649
|
-
var _a, _b, _c, _d, _e, _f;
|
|
675
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
650
676
|
const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
|
|
651
677
|
if (!recurring) {
|
|
652
678
|
throw new Error('Unable to resolve the recurring interval for the selected plan');
|
|
653
679
|
}
|
|
654
680
|
return funnelSdkService.createHostedCheckoutSession({
|
|
655
|
-
|
|
681
|
+
offerSetId: ((_a = input.plan.offerSetId) === null || _a === void 0 ? void 0 : _a.trim()) || undefined,
|
|
682
|
+
offerSetKey: ((_b = input.plan.offerSetKey) === null || _b === void 0 ? void 0 : _b.trim()) || undefined,
|
|
656
683
|
planId: getPaywallPlanSelectionValue(input.plan),
|
|
657
|
-
providerPlanId: ((
|
|
684
|
+
providerPlanId: ((_c = input.plan.providerPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
|
|
658
685
|
title: input.plan.title,
|
|
659
|
-
description: ((
|
|
686
|
+
description: ((_d = input.plan.description) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
|
|
660
687
|
amountCents: input.plan.amountCents,
|
|
661
688
|
billingInterval: recurring.interval,
|
|
662
689
|
billingIntervalCount: recurring.intervalCount,
|
|
663
|
-
couponId: ((
|
|
690
|
+
couponId: ((_e = input.couponId) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
|
|
664
691
|
analyticsMetadata: normalizeCheckoutAnalyticsMetadata(input.analyticsMetadata, input.plan),
|
|
665
692
|
successUrl: input.successUrl,
|
|
666
693
|
cancelUrl: input.cancelUrl,
|
|
667
|
-
customerEmail: ((
|
|
668
|
-
userId: ((
|
|
694
|
+
customerEmail: ((_f = input.customerEmail) === null || _f === void 0 ? void 0 : _f.trim()) || undefined,
|
|
695
|
+
userId: ((_g = input.user_id) === null || _g === void 0 ? void 0 : _g.trim()) || undefined,
|
|
669
696
|
environment: input.environment,
|
|
670
697
|
runtimeConfig: input.runtimeConfig,
|
|
671
698
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnelsgrove/payments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@funnelsgrove/analytics": "^0.1.28",
|
|
36
|
-
"@funnelsgrove/runtime": "^0.
|
|
36
|
+
"@funnelsgrove/runtime": "^0.3.0",
|
|
37
37
|
"@stripe/react-stripe-js": "^5.6.0",
|
|
38
38
|
"@stripe/stripe-js": "^8.7.0",
|
|
39
39
|
"react": "19.2.3",
|