@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.
@@ -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 (onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess());
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: onSuccess, options: expressCheckoutOptions, returnUrl: returnUrl, summaryLabel: expressCheckoutSummaryLabel }), _jsx("span", { className: 'shared-checkout-v2-sr-only', children: walletAriaLabel })] })] }), _jsxs("div", { hidden: effectiveSelectedMethod !== 'card', className: [
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 (onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess());
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 TrackPaidStripeSubscriptionCheckoutCompletedInput = {
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 buildCheckoutCompletedStorageKey = (checkoutSessionId, runtimeConfig) => {
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:checkout_completed:${funnelId}:${checkoutSessionId}`;
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(buildCheckoutCompletedStorageKey(checkoutSessionId, runtimeConfig)));
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(buildCheckoutCompletedStorageKey(checkoutSessionId, runtimeConfig), '1');
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 = buildCheckoutCompletedStorageKey(checkoutSessionId, input.runtimeConfig);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",