@funnelsgrove/payments 0.1.40 → 0.1.42
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/shared/SharedStripeCheckoutV2Dialog.d.ts +4 -1
- package/dist/components/shared/SharedStripeCheckoutV2Dialog.js +18 -5
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.d.ts +4 -1
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.js +12 -2
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.d.ts +3 -1
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.js +2 -2
- package/dist/services/checkoutCompletionAnalytics.service.d.ts +11 -2
- package/dist/services/checkoutCompletionAnalytics.service.js +43 -9
- package/package.json +1 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { Stripe } from '@stripe/stripe-js';
|
|
2
|
+
import { type StripeSubscriptionCheckoutAnalyticsContext } from '../../services/checkoutCompletionAnalytics.service.js';
|
|
2
3
|
export type SharedStripeCheckoutV2DialogProps = {
|
|
3
4
|
amountCents: number;
|
|
4
5
|
buttonColor: string;
|
|
5
6
|
cardSubmitLabel: string;
|
|
7
|
+
checkoutAnalytics?: StripeSubscriptionCheckoutAnalyticsContext | null;
|
|
8
|
+
checkoutSessionId?: string | null;
|
|
6
9
|
clientSecret: string;
|
|
7
10
|
closeAriaLabel: string;
|
|
8
11
|
countdownLabel: string;
|
|
@@ -58,5 +61,5 @@ export type SharedCheckoutSpecialOfferDialogProps = {
|
|
|
58
61
|
themeColor: string;
|
|
59
62
|
title: string;
|
|
60
63
|
};
|
|
61
|
-
export declare function SharedStripeCheckoutV2Dialog({ clientSecret, onClose, stripePromise, ...props }: SharedStripeCheckoutV2DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
64
|
+
export declare function SharedStripeCheckoutV2Dialog({ checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, ...props }: SharedStripeCheckoutV2DialogProps): import("react/jsx-runtime").JSX.Element;
|
|
62
65
|
export declare function SharedCheckoutSpecialOfferDialog({ buttonColor, buttonLabel, description, discountLabel, imageAlt, imageSrc, onAccept, themeColor, title, }: SharedCheckoutSpecialOfferDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -17,6 +17,7 @@ import { PaymentBrandMark, SecurityLockIcon } from './CheckoutBrandAssets.js';
|
|
|
17
17
|
import { StripeCheckoutExpressCheckoutButton } from './StripeCheckoutExpressCheckoutButton.js';
|
|
18
18
|
import { isInactiveCheckoutSessionError } from '../../providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
19
19
|
import { getStripeWalletPaymentMethodOrder, usePlatformWalletPaymentMethods, } from './walletPlatform.js';
|
|
20
|
+
import { trackPaidStripeSubscriptionCheckoutCompleted, trackStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
|
|
20
21
|
const defaultPaymentMethodLabels = ['Visa', 'Mastercard', 'Maestro', 'Discover'];
|
|
21
22
|
const stripeCheckoutV2Appearance = {
|
|
22
23
|
labels: 'above',
|
|
@@ -52,7 +53,7 @@ function getFriendlyCardErrorMessage(error) {
|
|
|
52
53
|
? error.message
|
|
53
54
|
: 'Payment failed. Please review your card details and try again.';
|
|
54
55
|
}
|
|
55
|
-
function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel, closeAriaLabel, countdownLabel, customerEmail, customerEmailEditable = false, customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmailInvalidMessage = 'Enter a valid email address.', customerName, discountAmountLabel, discountLabel, discountPercent, initialWalletAvailable, onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onInactiveCheckoutSession, onPaymentInfoSubmitted, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, promoActive, processingLabel, promoCode, promoCodeLabel, remainingSeconds, returnUrl, savedLabel, secureLabel, satisfactionLabel, satisfactionPercent, summaryLabel, themeColor, title, totalLabel, totalValue, walletButtonLabel, originalPriceLabel, originalPriceValue, }) {
|
|
56
|
+
function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel, closeAriaLabel, countdownLabel, checkoutAnalytics, checkoutSessionId, customerEmail, customerEmailEditable = false, customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmailInvalidMessage = 'Enter a valid email address.', customerName, discountAmountLabel, discountLabel, discountPercent, initialWalletAvailable, onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onInactiveCheckoutSession, onPaymentInfoSubmitted, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, promoActive, processingLabel, promoCode, promoCodeLabel, remainingSeconds, returnUrl, savedLabel, secureLabel, satisfactionLabel, satisfactionPercent, summaryLabel, themeColor, title, totalLabel, totalValue, walletButtonLabel, originalPriceLabel, originalPriceValue, }) {
|
|
56
57
|
const checkoutState = useCheckout();
|
|
57
58
|
const walletPaymentMethods = usePlatformWalletPaymentMethods();
|
|
58
59
|
const [selectedMethod, setSelectedMethod] = useState('wallet');
|
|
@@ -157,6 +158,12 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
|
|
|
157
158
|
setSelectedMethod('card');
|
|
158
159
|
}
|
|
159
160
|
};
|
|
161
|
+
const trackCheckoutSuccess = async () => {
|
|
162
|
+
if (checkoutAnalytics) {
|
|
163
|
+
await trackPaidStripeSubscriptionCheckoutCompleted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
|
|
164
|
+
}
|
|
165
|
+
await (onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess());
|
|
166
|
+
};
|
|
160
167
|
useEffect(() => {
|
|
161
168
|
if (effectiveSelectedMethod !== 'card' || typeof window === 'undefined') {
|
|
162
169
|
return;
|
|
@@ -228,7 +235,7 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
|
|
|
228
235
|
return;
|
|
229
236
|
}
|
|
230
237
|
if (typeof window !== 'undefined') {
|
|
231
|
-
await (
|
|
238
|
+
await trackCheckoutSuccess();
|
|
232
239
|
window.location.assign(returnUrl);
|
|
233
240
|
return;
|
|
234
241
|
}
|
|
@@ -260,7 +267,7 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
|
|
|
260
267
|
effectiveSelectedMethod === 'wallet' ? 'is-visible' : '',
|
|
261
268
|
]
|
|
262
269
|
.filter(Boolean)
|
|
263
|
-
.join(' '), children: [_jsxs("p", { className: 'shared-checkout-v2-secure-pill', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] }), _jsxs("div", { className: 'shared-checkout-v2-wallet-shell', "aria-label": walletAriaLabel, children: [_jsx(StripeCheckoutExpressCheckoutButton, { amountCents: amountCents, availabilityPaymentMethods: walletPaymentMethods, beforeConfirm: ensureCustomerEmail, className: 'shared-checkout-v2-express-buttons', confirmEmail: false, customerEmail: resolvedCustomerEmail, customerName: customerName, initialAvailable: initialWalletAvailable, keepInitialAvailableOnReady: initialWalletAvailable === true, onAvailabilityChange: handleWalletAvailabilityChange, onError: setSharedError, onPaymentInfoSubmitted: onPaymentInfoSubmitted, onSuccess:
|
|
270
|
+
.join(' '), children: [_jsxs("p", { className: 'shared-checkout-v2-secure-pill', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] }), _jsxs("div", { className: 'shared-checkout-v2-wallet-shell', "aria-label": walletAriaLabel, children: [_jsx(StripeCheckoutExpressCheckoutButton, { amountCents: amountCents, availabilityPaymentMethods: walletPaymentMethods, beforeConfirm: ensureCustomerEmail, className: 'shared-checkout-v2-express-buttons', confirmEmail: false, customerEmail: resolvedCustomerEmail, customerName: customerName, initialAvailable: initialWalletAvailable, keepInitialAvailableOnReady: initialWalletAvailable === true, onAvailabilityChange: handleWalletAvailabilityChange, onError: setSharedError, onPaymentInfoSubmitted: onPaymentInfoSubmitted, onSuccess: trackCheckoutSuccess, options: expressCheckoutOptions, returnUrl: returnUrl, summaryLabel: expressCheckoutSummaryLabel }), _jsx("span", { className: 'shared-checkout-v2-sr-only', children: walletAriaLabel })] })] }), _jsxs("div", { hidden: effectiveSelectedMethod !== 'card', className: [
|
|
264
271
|
'shared-checkout-v2-card-panel',
|
|
265
272
|
effectiveSelectedMethod === 'card' ? 'is-visible' : '',
|
|
266
273
|
]
|
|
@@ -274,13 +281,19 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
|
|
|
274
281
|
} }) })] }), _jsx(PaymentElement, { options: paymentElementOptions })] }), visibleError ? _jsx("p", { className: 'shared-checkout-v2-error', children: visibleError }) : null, _jsxs("button", { type: 'submit', className: 'shared-checkout-v2-card-submit', disabled: submitting, children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-card-submit-icon' }), submitting ? processingLabel : cardSubmitLabel] }), _jsx("div", { className: 'shared-checkout-v2-card-brands', "aria-hidden": 'true', children: cardBrandLabels.map((label) => (_jsx(PaymentBrandMark, { label: label, className: 'shared-checkout-v2-card-brand' }, label))) }), _jsxs("p", { className: 'shared-checkout-v2-secure-pill shared-checkout-v2-secure-pill--card', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] })] })] })] }));
|
|
275
282
|
}
|
|
276
283
|
export function SharedStripeCheckoutV2Dialog(_a) {
|
|
277
|
-
var { clientSecret, onClose, stripePromise } = _a, props = __rest(_a, ["clientSecret", "onClose", "stripePromise"]);
|
|
284
|
+
var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise"]);
|
|
278
285
|
const checkoutProviderOptions = useMemo(() => ({
|
|
279
286
|
clientSecret,
|
|
280
287
|
elementsOptions: {
|
|
281
288
|
appearance: stripeCheckoutV2Appearance,
|
|
282
289
|
},
|
|
283
290
|
}), [clientSecret]);
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
if (!checkoutAnalytics) {
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
void trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
|
|
296
|
+
}, [checkoutAnalytics, checkoutSessionId, clientSecret]);
|
|
284
297
|
useEffect(() => {
|
|
285
298
|
const handleKeyDown = (event) => {
|
|
286
299
|
if (event.key === 'Escape') {
|
|
@@ -290,7 +303,7 @@ export function SharedStripeCheckoutV2Dialog(_a) {
|
|
|
290
303
|
window.addEventListener('keydown', handleKeyDown);
|
|
291
304
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
|
292
305
|
}, [onClose]);
|
|
293
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'shared-checkout-v2-overlay', children: _jsx("div", { className: 'shared-checkout-v2-dialog', role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'shared-checkout-v2-title', children: _jsx(CheckoutProvider, { stripe: stripePromise, options: checkoutProviderOptions, children: _jsx(SharedStripeCheckoutV2Form, Object.assign({}, props, { clientSecret: clientSecret, onClose: onClose })) }, clientSecret) }) }), _jsx("style", { children: sharedStripeCheckoutV2Styles })] }));
|
|
306
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'shared-checkout-v2-overlay', children: _jsx("div", { className: 'shared-checkout-v2-dialog', role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'shared-checkout-v2-title', children: _jsx(CheckoutProvider, { stripe: stripePromise, options: checkoutProviderOptions, children: _jsx(SharedStripeCheckoutV2Form, Object.assign({}, props, { checkoutAnalytics: checkoutAnalytics, checkoutSessionId: checkoutSessionId, clientSecret: clientSecret, onClose: onClose })) }, clientSecret) }) }), _jsx("style", { children: sharedStripeCheckoutV2Styles })] }));
|
|
294
307
|
}
|
|
295
308
|
function SharedCheckoutSpecialOfferGift({ discountLabel, imageAlt, imageSrc, }) {
|
|
296
309
|
if (imageSrc) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { AvailablePaymentMethods, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
|
|
2
|
+
import { type StripeSubscriptionCheckoutAnalyticsContext } from '../../services/checkoutCompletionAnalytics.service.js';
|
|
2
3
|
export type StripeCheckoutExpressCheckoutButtonProps = {
|
|
3
4
|
amountCents: number;
|
|
4
5
|
beforeConfirm?: () => Promise<string | null>;
|
|
6
|
+
checkoutAnalytics?: StripeSubscriptionCheckoutAnalyticsContext | null;
|
|
7
|
+
checkoutSessionId?: string | null;
|
|
5
8
|
summaryLabel: string;
|
|
6
9
|
returnUrl: string;
|
|
7
10
|
confirmEmail?: boolean;
|
|
@@ -20,4 +23,4 @@ export type StripeCheckoutExpressCheckoutButtonProps = {
|
|
|
20
23
|
onPaymentInfoSubmitted?: () => void | Promise<void>;
|
|
21
24
|
onSuccess?: () => void | Promise<void>;
|
|
22
25
|
};
|
|
23
|
-
export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, confirmEmail, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, serverUpdateKey, onServerUpdate, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAnalytics, checkoutSessionId, returnUrl, confirmEmail, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, serverUpdateKey, onServerUpdate, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { ExpressCheckoutElement, useCheckout, } from '@stripe/react-stripe-js/checkout';
|
|
5
|
+
import { trackPaidStripeSubscriptionCheckoutCompleted, trackStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
|
|
5
6
|
const ExpressCheckoutElementWithCancel = ExpressCheckoutElement;
|
|
6
7
|
const buildDefaultOptions = () => ({
|
|
7
8
|
buttonHeight: 55,
|
|
@@ -26,7 +27,7 @@ const buildDefaultOptions = () => ({
|
|
|
26
27
|
paypal: 'never',
|
|
27
28
|
},
|
|
28
29
|
});
|
|
29
|
-
export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, confirmEmail = true, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, serverUpdateKey, onServerUpdate, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }) {
|
|
30
|
+
export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAnalytics, checkoutSessionId, returnUrl, confirmEmail = true, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, serverUpdateKey, onServerUpdate, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }) {
|
|
30
31
|
const checkoutState = useCheckout();
|
|
31
32
|
const appliedServerUpdateKeyRef = useRef('');
|
|
32
33
|
const serverUpdatePromiseRef = useRef(null);
|
|
@@ -86,6 +87,12 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl,
|
|
|
86
87
|
}
|
|
87
88
|
void ensureServerUpdated();
|
|
88
89
|
}, [checkoutState.type, ensureServerUpdated, normalizedServerUpdateKey, onServerUpdate]);
|
|
90
|
+
const trackCheckoutSuccess = async () => {
|
|
91
|
+
if (checkoutAnalytics) {
|
|
92
|
+
await trackPaidStripeSubscriptionCheckoutCompleted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
|
|
93
|
+
}
|
|
94
|
+
await (onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess());
|
|
95
|
+
};
|
|
89
96
|
const handleConfirm = async (event) => {
|
|
90
97
|
if (checkoutState.type !== 'success') {
|
|
91
98
|
const message = checkoutState.type === 'error'
|
|
@@ -103,6 +110,9 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl,
|
|
|
103
110
|
event.paymentFailed({ reason: 'fail', message });
|
|
104
111
|
return;
|
|
105
112
|
}
|
|
113
|
+
if (checkoutAnalytics) {
|
|
114
|
+
await trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
|
|
115
|
+
}
|
|
106
116
|
let preparedCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || null;
|
|
107
117
|
if (beforeConfirm) {
|
|
108
118
|
try {
|
|
@@ -135,7 +145,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl,
|
|
|
135
145
|
}
|
|
136
146
|
onError === null || onError === void 0 ? void 0 : onError(null);
|
|
137
147
|
if (typeof window !== 'undefined') {
|
|
138
|
-
await (
|
|
148
|
+
await trackCheckoutSuccess();
|
|
139
149
|
window.location.assign(returnUrl);
|
|
140
150
|
}
|
|
141
151
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { AvailablePaymentMethods, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
|
|
3
|
+
import type { StripeSubscriptionCheckoutAnalyticsContext } from '../../services/checkoutCompletionAnalytics.service.js';
|
|
3
4
|
import { type StripeSubscriptionCheckoutSession } from './useStripeSubscriptionCheckoutSession.js';
|
|
4
5
|
export type WalletSubscriptionCheckoutRenderStateInput = {
|
|
5
6
|
expressCheckoutAllowed: boolean;
|
|
@@ -25,6 +26,7 @@ export type WalletSubscriptionCheckoutSlotProps = {
|
|
|
25
26
|
amountCents: number;
|
|
26
27
|
availabilityPaymentMethod: keyof AvailablePaymentMethods;
|
|
27
28
|
beforeConfirm?: () => Promise<string | null>;
|
|
29
|
+
checkoutAnalytics?: StripeSubscriptionCheckoutAnalyticsContext | null;
|
|
28
30
|
className?: string;
|
|
29
31
|
customerEmail?: string | null;
|
|
30
32
|
customerName?: string | null;
|
|
@@ -46,5 +48,5 @@ export type WalletSubscriptionCheckoutSlotProps = {
|
|
|
46
48
|
suspended?: boolean;
|
|
47
49
|
};
|
|
48
50
|
export declare function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label, }: Pick<WalletPlaceholderButtonProps, 'className' | 'label'>): import("react/jsx-runtime").JSX.Element;
|
|
49
|
-
export declare function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
51
|
+
export declare function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, checkoutAnalytics, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
50
52
|
export {};
|
|
@@ -27,7 +27,7 @@ export function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label,
|
|
|
27
27
|
.filter(Boolean)
|
|
28
28
|
.join(' '), "aria-label": label, role: 'status', children: [_jsx("span", { className: 'wallet-subscription-checkout-slot__loading-bar' }), _jsx("style", { children: walletSubscriptionCheckoutLoadingPlaceholderStyles })] }));
|
|
29
29
|
}
|
|
30
|
-
export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended = false, }) {
|
|
30
|
+
export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, checkoutAnalytics, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended = false, }) {
|
|
31
31
|
const [walletAvailability, setWalletAvailability] = useState({
|
|
32
32
|
available: null,
|
|
33
33
|
intentKey: session.intentKey,
|
|
@@ -131,7 +131,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymen
|
|
|
131
131
|
.filter(Boolean)
|
|
132
132
|
.join(' '), "aria-busy": placeholderIsPreparing || undefined, children: [shouldRenderExpressCheckout &&
|
|
133
133
|
session.activeClientSecret &&
|
|
134
|
-
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange, onCancel: onCancel, onError: handleError, onPaymentInfoSubmitted: onPaymentInfoSubmitted, onSuccess: onSuccess, options: options, returnUrl: returnUrl, serverUpdateKey: serverUpdateKey, stripePromise: session.stripePromise, summaryLabel: summaryLabel, onServerUpdate: session.updateCheckoutSessionPlan })) : null, shouldRenderPlaceholder ? (_jsx("div", { className: 'wallet-subscription-checkout-slot__placeholder', children: placeholderRenderer({
|
|
134
|
+
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, checkoutAnalytics: checkoutAnalytics, checkoutSessionId: session.checkoutSessionId, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange, onCancel: onCancel, onError: handleError, onPaymentInfoSubmitted: onPaymentInfoSubmitted, onSuccess: onSuccess, options: options, returnUrl: returnUrl, serverUpdateKey: serverUpdateKey, stripePromise: session.stripePromise, summaryLabel: summaryLabel, onServerUpdate: session.updateCheckoutSessionPlan })) : null, shouldRenderPlaceholder ? (_jsx("div", { className: 'wallet-subscription-checkout-slot__placeholder', children: placeholderRenderer({
|
|
135
135
|
className,
|
|
136
136
|
disabled: slotDisabled,
|
|
137
137
|
label: placeholderLabel,
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
2
2
|
import { type StripeRuntimeConfigOverrides } from './stripe.service.js';
|
|
3
|
-
export type
|
|
4
|
-
checkoutSessionId?: string | null;
|
|
3
|
+
export type StripeSubscriptionCheckoutAnalyticsContext = {
|
|
5
4
|
environment?: RuntimeMode;
|
|
5
|
+
featureFlags?: Record<string, string | null | undefined>;
|
|
6
6
|
metadata?: Record<string, unknown> | null;
|
|
7
7
|
runtimeConfig?: StripeRuntimeConfigOverrides;
|
|
8
8
|
stepId?: string;
|
|
9
9
|
stepName?: string;
|
|
10
|
+
stepType?: string;
|
|
11
|
+
};
|
|
12
|
+
export type TrackStripeSubscriptionCheckoutStartedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
13
|
+
checkoutSessionId?: string | null;
|
|
14
|
+
};
|
|
15
|
+
export type TrackPaidStripeSubscriptionCheckoutCompletedInput = StripeSubscriptionCheckoutAnalyticsContext & {
|
|
16
|
+
checkoutSessionId?: string | null;
|
|
10
17
|
};
|
|
18
|
+
export type TrackStripeSubscriptionCheckoutStartedResult = 'already_tracked' | 'missing_checkout_session' | 'tracked';
|
|
11
19
|
export type TrackPaidStripeSubscriptionCheckoutCompletedResult = 'already_tracked' | 'missing_checkout_session' | 'tracked' | 'unpaid';
|
|
20
|
+
export declare function trackStripeSubscriptionCheckoutStarted(input: TrackStripeSubscriptionCheckoutStartedInput): Promise<TrackStripeSubscriptionCheckoutStartedResult>;
|
|
12
21
|
export declare function trackPaidStripeSubscriptionCheckoutCompleted(input: TrackPaidStripeSubscriptionCheckoutCompletedInput): Promise<TrackPaidStripeSubscriptionCheckoutCompletedResult>;
|
|
@@ -6,44 +6,76 @@ const asRecord = (value) => {
|
|
|
6
6
|
: {};
|
|
7
7
|
};
|
|
8
8
|
const activeCheckoutCompletionKeys = new Set();
|
|
9
|
-
const
|
|
9
|
+
const activeCheckoutStartedKeys = new Set();
|
|
10
|
+
const buildCheckoutStorageKey = (eventName, checkoutSessionId, runtimeConfig) => {
|
|
10
11
|
var _a;
|
|
11
12
|
const funnelId = ((_a = runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId) === null || _a === void 0 ? void 0 : _a.trim()) || 'unknown_funnel';
|
|
12
|
-
return `funnelsgrove
|
|
13
|
+
return `funnelsgrove:${eventName}:${funnelId}:${checkoutSessionId}`;
|
|
13
14
|
};
|
|
14
|
-
const hasTrackedCheckoutSession = (checkoutSessionId, runtimeConfig) => {
|
|
15
|
+
const hasTrackedCheckoutSession = (eventName, checkoutSessionId, runtimeConfig) => {
|
|
15
16
|
var _a;
|
|
16
17
|
if (typeof window === 'undefined') {
|
|
17
18
|
return false;
|
|
18
19
|
}
|
|
19
20
|
try {
|
|
20
|
-
return Boolean((_a = window.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem(
|
|
21
|
+
return Boolean((_a = window.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem(buildCheckoutStorageKey(eventName, checkoutSessionId, runtimeConfig)));
|
|
21
22
|
}
|
|
22
23
|
catch (_b) {
|
|
23
24
|
return false;
|
|
24
25
|
}
|
|
25
26
|
};
|
|
26
|
-
const markCheckoutSessionTracked = (checkoutSessionId, runtimeConfig) => {
|
|
27
|
+
const markCheckoutSessionTracked = (eventName, checkoutSessionId, runtimeConfig) => {
|
|
27
28
|
var _a;
|
|
28
29
|
if (typeof window === 'undefined') {
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
32
|
try {
|
|
32
|
-
(_a = window.sessionStorage) === null || _a === void 0 ? void 0 : _a.setItem(
|
|
33
|
+
(_a = window.sessionStorage) === null || _a === void 0 ? void 0 : _a.setItem(buildCheckoutStorageKey(eventName, checkoutSessionId, runtimeConfig), '1');
|
|
33
34
|
}
|
|
34
35
|
catch (_b) {
|
|
35
36
|
// A stable checkout-session event id still protects provider-side dedupe.
|
|
36
37
|
}
|
|
37
38
|
};
|
|
39
|
+
export async function trackStripeSubscriptionCheckoutStarted(input) {
|
|
40
|
+
var _a;
|
|
41
|
+
const checkoutSessionId = ((_a = input.checkoutSessionId) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
42
|
+
if (!checkoutSessionId) {
|
|
43
|
+
return 'missing_checkout_session';
|
|
44
|
+
}
|
|
45
|
+
const startedKey = buildCheckoutStorageKey('checkout_started', checkoutSessionId, input.runtimeConfig);
|
|
46
|
+
if (activeCheckoutStartedKeys.has(startedKey) ||
|
|
47
|
+
hasTrackedCheckoutSession('checkout_started', checkoutSessionId, input.runtimeConfig)) {
|
|
48
|
+
return 'already_tracked';
|
|
49
|
+
}
|
|
50
|
+
activeCheckoutStartedKeys.add(startedKey);
|
|
51
|
+
try {
|
|
52
|
+
const metadata = asRecord(input.metadata);
|
|
53
|
+
const conversionInput = {
|
|
54
|
+
eventId: checkoutSessionId,
|
|
55
|
+
featureFlags: input.featureFlags,
|
|
56
|
+
stepId: input.stepId,
|
|
57
|
+
stepName: input.stepName,
|
|
58
|
+
stepType: input.stepType,
|
|
59
|
+
metadata: Object.assign(Object.assign({}, metadata), { checkoutSessionId, checkout_session_id: checkoutSessionId, environment: input.environment || metadata.environment }),
|
|
60
|
+
};
|
|
61
|
+
publicAnalyticsSdk.trackCheckoutStarted(conversionInput);
|
|
62
|
+
markCheckoutSessionTracked('checkout_started', checkoutSessionId, input.runtimeConfig);
|
|
63
|
+
await publicAnalyticsSdk.flush().catch(() => 0);
|
|
64
|
+
return 'tracked';
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
activeCheckoutStartedKeys.delete(startedKey);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
38
70
|
export async function trackPaidStripeSubscriptionCheckoutCompleted(input) {
|
|
39
71
|
var _a;
|
|
40
72
|
const checkoutSessionId = ((_a = input.checkoutSessionId) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
41
73
|
if (!checkoutSessionId) {
|
|
42
74
|
return 'missing_checkout_session';
|
|
43
75
|
}
|
|
44
|
-
const completionKey =
|
|
76
|
+
const completionKey = buildCheckoutStorageKey('checkout_completed', checkoutSessionId, input.runtimeConfig);
|
|
45
77
|
if (activeCheckoutCompletionKeys.has(completionKey) ||
|
|
46
|
-
hasTrackedCheckoutSession(checkoutSessionId, input.runtimeConfig)) {
|
|
78
|
+
hasTrackedCheckoutSession('checkout_completed', checkoutSessionId, input.runtimeConfig)) {
|
|
47
79
|
return 'already_tracked';
|
|
48
80
|
}
|
|
49
81
|
activeCheckoutCompletionKeys.add(completionKey);
|
|
@@ -59,12 +91,14 @@ export async function trackPaidStripeSubscriptionCheckoutCompleted(input) {
|
|
|
59
91
|
const analyticsMetadata = asRecord(checkoutStatus.analyticsMetadata);
|
|
60
92
|
const conversionInput = {
|
|
61
93
|
eventId: checkoutSessionId,
|
|
94
|
+
featureFlags: input.featureFlags,
|
|
62
95
|
stepId: input.stepId,
|
|
63
96
|
stepName: input.stepName,
|
|
97
|
+
stepType: input.stepType,
|
|
64
98
|
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 }),
|
|
65
99
|
};
|
|
66
100
|
publicAnalyticsSdk.trackCheckoutCompleted(conversionInput);
|
|
67
|
-
markCheckoutSessionTracked(checkoutSessionId, input.runtimeConfig);
|
|
101
|
+
markCheckoutSessionTracked('checkout_completed', checkoutSessionId, input.runtimeConfig);
|
|
68
102
|
await publicAnalyticsSdk.flush().catch(() => 0);
|
|
69
103
|
return 'tracked';
|
|
70
104
|
}
|