@funnelsgrove/payments 0.1.54 → 0.1.56

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.
@@ -16,7 +16,7 @@ import { CardCvcElement, CardExpiryElement, CardNumberElement, Elements, useElem
16
16
  import { ApplePaySubscribeButton } from './ApplePaySubscribeButton.js';
17
17
  import { PaymentBrandMark, SecurityLockIcon, StripeWordmark, } from './CheckoutBrandAssets.js';
18
18
  import { StripeExpressCheckoutButton } from './StripeExpressCheckoutButton.js';
19
- import { trackStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
19
+ import { queueStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
20
20
  const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
21
21
  const defaultPaymentMethodLabels = ['Visa', 'Mastercard', 'PayPal', 'G Pay', 'Apple Pay'];
22
22
  const normalizeBillingName = (input) => {
@@ -100,7 +100,7 @@ function getFriendlyCardErrorMessage(error) {
100
100
  ? error.message
101
101
  : 'Payment failed. Please review your card details and try again.';
102
102
  }
103
- function SharedStripeCheckoutForm({ amountCents, clientSecret, closeAriaLabel, customerEmailEditable = false, customerEmailInvalidMessage = 'Enter a valid email address.', customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmail, customerName, legalNotice, onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, poweredByLabel, processingLabel, renewalNotice, returnUrl, secureLabel, submitLabel, summaryLabel, summaryRows, summaryTitle, title, totalLabel, totalValue, walletPlaceholderLabel, summaryNote, }) {
103
+ function SharedStripeCheckoutForm({ amountCents, checkoutAnalytics, checkoutSessionId, clientSecret, closeAriaLabel, customerEmailEditable = false, customerEmailInvalidMessage = 'Enter a valid email address.', customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmail, customerName, legalNotice, onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, poweredByLabel, processingLabel, renewalNotice, returnUrl, secureLabel, submitLabel, summaryLabel, summaryRows, summaryTitle, title, totalLabel, totalValue, walletPlaceholderLabel, summaryNote, }) {
104
104
  const stripe = useStripe();
105
105
  const elements = useElements();
106
106
  const [submitting, setSubmitting] = useState(false);
@@ -191,6 +191,9 @@ function SharedStripeCheckoutForm({ amountCents, clientSecret, closeAriaLabel, c
191
191
  setSubmitting(false);
192
192
  return;
193
193
  }
194
+ if (checkoutAnalytics) {
195
+ queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, paymentMethod: 'card' }));
196
+ }
194
197
  const result = await stripe.confirmCardPayment(clientSecret, {
195
198
  payment_method: {
196
199
  card: cardNumberElement,
@@ -227,7 +230,7 @@ function SharedStripeCheckoutForm({ amountCents, clientSecret, closeAriaLabel, c
227
230
  ? 'is-positive'
228
231
  : row.tone === 'negative'
229
232
  ? 'is-negative'
230
- : undefined, children: row.value })] }, row.id))), _jsxs("div", { className: 'shared-stripe-checkout-summary-total', children: [_jsx("span", { children: totalLabel }), _jsx("strong", { children: totalValue })] }), summaryNote ? (_jsx("p", { className: 'shared-stripe-checkout-summary-note', children: summaryNote })) : null] }), _jsxs("form", { className: 'shared-stripe-checkout-form', onSubmit: (event) => void handleSubmit(event), children: [_jsxs("div", { className: 'shared-stripe-checkout-wallet-shell', children: [walletAvailable !== true ? (_jsx(ApplePaySubscribeButton, { className: 'shared-stripe-checkout-wallet-placeholder', disabled: walletFallbackDisabled, label: walletPlaceholderLabel })) : null, walletCheckoutAllowed ? (_jsx(StripeExpressCheckoutButton, { amountCents: amountCents, beforeConfirm: ensureCustomerEmail, summaryLabel: summaryLabel, returnUrl: returnUrl, customerEmail: resolvedCustomerEmail, customerName: customerName, className: 'shared-stripe-checkout-wallet-button', onAvailabilityChange: setWalletAvailable, onError: setSharedError })) : null] }), _jsxs("div", { className: 'shared-stripe-checkout-card-surface', children: [_jsxs("div", { className: 'shared-stripe-checkout-field-group shared-stripe-checkout-field-group--wide', children: [_jsx("p", { className: 'shared-stripe-checkout-field-label', children: customerEmailLabel }), _jsx("div", { className: 'shared-stripe-checkout-field shared-stripe-checkout-field--wide', children: _jsx("input", { type: 'email', inputMode: 'email', autoComplete: 'email', className: [
233
+ : undefined, children: row.value })] }, row.id))), _jsxs("div", { className: 'shared-stripe-checkout-summary-total', children: [_jsx("span", { children: totalLabel }), _jsx("strong", { children: totalValue })] }), summaryNote ? (_jsx("p", { className: 'shared-stripe-checkout-summary-note', children: summaryNote })) : null] }), _jsxs("form", { className: 'shared-stripe-checkout-form', onSubmit: (event) => void handleSubmit(event), children: [_jsxs("div", { className: 'shared-stripe-checkout-wallet-shell', children: [walletAvailable !== true ? (_jsx(ApplePaySubscribeButton, { className: 'shared-stripe-checkout-wallet-placeholder', disabled: walletFallbackDisabled, label: walletPlaceholderLabel })) : null, walletCheckoutAllowed ? (_jsx(StripeExpressCheckoutButton, { amountCents: amountCents, beforeConfirm: ensureCustomerEmail, checkoutAnalytics: checkoutAnalytics, checkoutSessionId: checkoutSessionId, summaryLabel: summaryLabel, returnUrl: returnUrl, customerEmail: resolvedCustomerEmail, customerName: customerName, className: 'shared-stripe-checkout-wallet-button', onAvailabilityChange: setWalletAvailable, onError: setSharedError })) : null] }), _jsxs("div", { className: 'shared-stripe-checkout-card-surface', children: [_jsxs("div", { className: 'shared-stripe-checkout-field-group shared-stripe-checkout-field-group--wide', children: [_jsx("p", { className: 'shared-stripe-checkout-field-label', children: customerEmailLabel }), _jsx("div", { className: 'shared-stripe-checkout-field shared-stripe-checkout-field--wide', children: _jsx("input", { type: 'email', inputMode: 'email', autoComplete: 'email', className: [
231
234
  'shared-stripe-checkout-text-input',
232
235
  customerEmailEditable ? '' : 'is-readonly',
233
236
  ]
@@ -246,12 +249,6 @@ function SharedStripeCheckoutForm({ amountCents, clientSecret, closeAriaLabel, c
246
249
  }
247
250
  export function SharedStripeCheckoutDialog(_a) {
248
251
  var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise"]);
249
- useEffect(() => {
250
- if (!checkoutAnalytics) {
251
- return;
252
- }
253
- void trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
254
- }, [checkoutAnalytics, checkoutSessionId, clientSecret]);
255
252
  useEffect(() => {
256
253
  const handleKeyDown = (event) => {
257
254
  if (event.key === 'Escape') {
@@ -263,7 +260,7 @@ export function SharedStripeCheckoutDialog(_a) {
263
260
  }, [onClose]);
264
261
  return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'shared-stripe-checkout-overlay', role: 'presentation', onClick: onClose, children: _jsx("div", { className: 'shared-stripe-checkout-shell', role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'shared-stripe-checkout-title', onClick: (event) => event.stopPropagation(), children: _jsx(Elements, { stripe: stripePromise, options: {
265
262
  clientSecret,
266
- }, children: _jsx(SharedStripeCheckoutForm, Object.assign({}, props, { clientSecret: clientSecret, onClose: onClose })) }) }) }), _jsx("style", { children: sharedStripeCheckoutDialogStyles })] }));
263
+ }, children: _jsx(SharedStripeCheckoutForm, Object.assign({}, props, { checkoutAnalytics: checkoutAnalytics, checkoutSessionId: checkoutSessionId, clientSecret: clientSecret, onClose: onClose })) }) }) }), _jsx("style", { children: sharedStripeCheckoutDialogStyles })] }));
267
264
  }
268
265
  const sharedStripeCheckoutDialogStyles = `
269
266
  .shared-stripe-checkout-overlay {
@@ -18,7 +18,7 @@ import { StripeCheckoutExpressCheckoutButton } from './StripeCheckoutExpressChec
18
18
  import { getUnsupportedCheckoutCountryMessage, normalizeSupportedCheckoutCountries, } from './checkoutCountries.js';
19
19
  import { isInactiveCheckoutSessionError } from '../../providers/stripe/useStripeSubscriptionCheckoutSession.js';
20
20
  import { getStripeWalletPaymentMethodOrder, usePlatformWalletPaymentMethods, } from './walletPlatform.js';
21
- import { trackPaidStripeSubscriptionCheckoutCompleted, trackStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
21
+ import { queueStripeSubscriptionCheckoutStarted, trackPaidStripeSubscriptionCheckoutCompleted, } from '../../services/checkoutCompletionAnalytics.service.js';
22
22
  const defaultPaymentMethodLabels = ['Visa', 'Mastercard', 'Maestro', 'Discover'];
23
23
  const stripeCheckoutV2Appearance = {
24
24
  labels: 'above',
@@ -250,6 +250,9 @@ 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
+ }
253
256
  await (onPaymentInfoSubmitted === null || onPaymentInfoSubmitted === void 0 ? void 0 : onPaymentInfoSubmitted());
254
257
  const result = await checkoutState.checkout.confirm({
255
258
  redirect: 'if_required',
@@ -293,7 +296,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
293
296
  effectiveSelectedMethod === 'wallet' ? 'is-visible' : '',
294
297
  ]
295
298
  .filter(Boolean)
296
- .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, supportedCountries: normalizedSupportedCountries, unsupportedCountryMessage: unsupportedCountryMessage }), _jsx("span", { className: 'shared-checkout-v2-sr-only', children: walletAriaLabel })] })] }), _jsxs("div", { hidden: effectiveSelectedMethod !== 'card', className: [
299
+ .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, checkoutAnalytics: checkoutAnalytics, checkoutSessionId: checkoutSessionId, 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, supportedCountries: normalizedSupportedCountries, unsupportedCountryMessage: unsupportedCountryMessage }), _jsx("span", { className: 'shared-checkout-v2-sr-only', children: walletAriaLabel })] })] }), _jsxs("div", { hidden: effectiveSelectedMethod !== 'card', className: [
297
300
  'shared-checkout-v2-card-panel',
298
301
  effectiveSelectedMethod === 'card' ? 'is-visible' : '',
299
302
  ]
@@ -327,12 +330,6 @@ export function SharedStripeCheckoutV2Dialog(_a) {
327
330
  var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, supportedCountries } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise", "supportedCountries"]);
328
331
  const normalizedSupportedCountries = useMemo(() => normalizeSupportedCheckoutCountries(supportedCountries), [supportedCountries]);
329
332
  const fixedBillingCountry = getFixedSupportedBillingCountry(normalizedSupportedCountries);
330
- useEffect(() => {
331
- if (!checkoutAnalytics) {
332
- return;
333
- }
334
- void trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
335
- }, [checkoutAnalytics, checkoutSessionId, clientSecret]);
336
333
  useEffect(() => {
337
334
  const handleKeyDown = (event) => {
338
335
  if (event.key === 'Escape') {
@@ -2,7 +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
+ import { queueStripeSubscriptionCheckoutStarted, trackPaidStripeSubscriptionCheckoutCompleted, } from '../../services/checkoutCompletionAnalytics.service.js';
6
6
  import { getUnsupportedCheckoutCountryMessage, normalizeSupportedCheckoutCountries, } from './checkoutCountries.js';
7
7
  const ExpressCheckoutElementWithCancel = ExpressCheckoutElement;
8
8
  const buildDefaultOptions = () => ({
@@ -124,7 +124,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
124
124
  return;
125
125
  }
126
126
  if (checkoutAnalytics) {
127
- await trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId }, checkoutAnalytics));
127
+ queueStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId, paymentMethod: event.expressPaymentType }));
128
128
  }
129
129
  let preparedCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || null;
130
130
  if (beforeConfirm) {
@@ -1,7 +1,10 @@
1
1
  import type { AvailablePaymentMethods, StripeExpressCheckoutElementOptions } from '@stripe/stripe-js';
2
+ import { type StripeSubscriptionCheckoutAnalyticsContext } from '../../services/checkoutCompletionAnalytics.service.js';
2
3
  export type StripeExpressCheckoutButtonProps = {
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
  customerEmail?: string | null;
@@ -17,4 +20,4 @@ export type StripeExpressCheckoutButtonProps = {
17
20
  onPaymentInfoSubmitted?: () => void | Promise<void>;
18
21
  onSuccess?: () => void | Promise<void>;
19
22
  };
20
- export declare function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }: StripeExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checkoutAnalytics, checkoutSessionId, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }: StripeExpressCheckoutButtonProps): 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 { useMemo, useState } from 'react';
4
4
  import { ExpressCheckoutElement, useElements, useStripe } from '@stripe/react-stripe-js';
5
+ import { queueStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
5
6
  const normalizeBillingName = (input) => {
6
7
  var _a, _b, _c;
7
8
  const normalizedName = (_a = input.name) === null || _a === void 0 ? void 0 : _a.trim();
@@ -53,7 +54,7 @@ const buildDefaultOptions = (input) => {
53
54
  const isSuccessfulIntentStatus = (status) => {
54
55
  return status === 'succeeded' || status === 'processing' || status === 'requires_capture';
55
56
  };
56
- export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }) {
57
+ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checkoutAnalytics, checkoutSessionId, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, }) {
57
58
  const stripe = useStripe();
58
59
  const elements = useElements();
59
60
  const [walletAvailable, setWalletAvailable] = useState(initialAvailable !== null && initialAvailable !== void 0 ? initialAvailable : null);
@@ -77,6 +78,9 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summar
77
78
  return;
78
79
  }
79
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
+ }
80
84
  let preparedCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || null;
81
85
  if (beforeConfirm) {
82
86
  try {
package/dist/index.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from './services/runtimeBillingPlanCatalog.service.js';
6
6
  export * from './services/paywallOffer.service.js';
7
7
  export * from './services/stripe.service.js';
8
8
  export * from './services/checkoutCompletionAnalytics.service.js';
9
+ export * from './services/paymentMethodAnalytics.service.js';
9
10
  export * from './hooks/useResolvedPaywallPlans.js';
10
11
  export * from './providers/paymentProvider.types.js';
11
12
  export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
package/dist/index.js CHANGED
@@ -8,6 +8,7 @@ export * from './services/paywallOffer.service.js';
8
8
  // Stripe API clients and checkout completion handoff.
9
9
  export * from './services/stripe.service.js';
10
10
  export * from './services/checkoutCompletionAnalytics.service.js';
11
+ export * from './services/paymentMethodAnalytics.service.js';
11
12
  export * from './hooks/useResolvedPaywallPlans.js';
12
13
  // Provider-neutral and Stripe subscription checkout primitives.
13
14
  export * from './providers/paymentProvider.types.js';
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { StripeExpressCheckoutElement } from '../../components/shared/StripeExpressCheckoutElement.js';
5
5
  import { trackStripeSubscriptionCheckoutStarted, } from '../../services/checkoutCompletionAnalytics.service.js';
6
+ import { toAnalyticsPaymentMethod } from '../../services/paymentMethodAnalytics.service.js';
6
7
  import { isInactiveCheckoutSessionError, } from './useStripeSubscriptionCheckoutSession.js';
7
8
  export function getWalletSubscriptionCheckoutRenderState({ expressCheckoutAllowed, hasActiveClientSecret, hasStripePromise, suspended, walletAvailable, walletPreparing, }) {
8
9
  const shouldRenderExpressCheckout = !suspended &&
@@ -37,6 +38,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
37
38
  const autoWalletPreparationIntentKeyRef = useRef(null);
38
39
  const walletCheckoutStartedAfterClickSessionIdRef = useRef(null);
39
40
  const slotDisabled = disabled || !session.configured;
41
+ const analyticsPaymentMethod = toAnalyticsPaymentMethod(availabilityPaymentMethod);
40
42
  const walletAvailable = walletAvailability.intentKey === session.intentKey
41
43
  ? walletAvailability.available
42
44
  : null;
@@ -84,6 +86,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
84
86
  void session
85
87
  .prepareWalletCheckout({
86
88
  checkoutStartSource: 'wallet_render',
89
+ paymentMethod: analyticsPaymentMethod,
87
90
  })
88
91
  .then((readyClientSecret) => {
89
92
  if (!readyClientSecret) {
@@ -91,6 +94,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
91
94
  }
92
95
  });
93
96
  }, [
97
+ analyticsPaymentMethod,
94
98
  expressCheckoutAllowed,
95
99
  reportWalletPreparationFailure,
96
100
  session.activeClientSecret,
@@ -112,8 +116,9 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
112
116
  return;
113
117
  }
114
118
  walletCheckoutStartedAfterClickSessionIdRef.current = session.checkoutSessionId;
115
- void trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId: session.checkoutSessionId }, checkoutAnalytics));
119
+ void trackStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId: session.checkoutSessionId, paymentMethod: analyticsPaymentMethod }));
116
120
  }, [
121
+ analyticsPaymentMethod,
117
122
  checkoutAnalytics,
118
123
  session.checkoutSessionId,
119
124
  session.intentKey,
@@ -127,6 +132,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
127
132
  });
128
133
  void session.restartWalletCheckout({
129
134
  checkoutStartSource: 'wallet_render',
135
+ paymentMethod: analyticsPaymentMethod,
130
136
  });
131
137
  return;
132
138
  }
@@ -147,6 +153,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
147
153
  setWalletCheckoutStartedAfterClickIntentKey(session.intentKey);
148
154
  const readyClientSecret = await session.prepareWalletCheckout({
149
155
  checkoutStartSource: 'wallet_render',
156
+ paymentMethod: analyticsPaymentMethod,
150
157
  });
151
158
  if (!readyClientSecret) {
152
159
  setWalletCheckoutStartedAfterClickIntentKey(null);
@@ -1,6 +1,6 @@
1
1
  import type { Stripe } from '@stripe/stripe-js';
2
2
  import type { RuntimeMode } from '@funnelsgrove/runtime';
3
- import { type CheckoutPreparationLoading } from '../paymentProvider.types.js';
3
+ import { type CheckoutPreparationLoading, type CheckoutPreparationSource } from '../paymentProvider.types.js';
4
4
  import { type PaywallPlan, type StripeRuntimeConfigOverrides } from '../../services/stripe.service.js';
5
5
  type StripeOneTimeCheckoutPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'description' | 'id' | 'providerPlanId' | 'title'>;
6
6
  export type StripeOneTimeCheckoutSessionInput = {
@@ -16,9 +16,11 @@ export type StripeOneTimeCheckoutSessionInput = {
16
16
  userId?: string | null;
17
17
  };
18
18
  export type StripeOneTimeCheckoutPreparationOptions = {
19
- checkoutStartSource?: 'card_click' | 'wallet_click' | 'wallet_render' | (string & {});
19
+ checkoutStartSource?: 'card_click' | 'checkout_render' | 'wallet_click' | 'wallet_render' | (string & {});
20
20
  forceNew?: boolean;
21
+ paymentMethod?: unknown;
21
22
  };
23
+ export declare function resolveStripeOneTimeCheckoutPreparationOptions(source: CheckoutPreparationSource, options?: StripeOneTimeCheckoutPreparationOptions): StripeOneTimeCheckoutPreparationOptions;
22
24
  export type StripeOneTimeCheckoutSession = {
23
25
  activeClientSecret: string | null;
24
26
  checkoutSessionId: string | null;
@@ -36,6 +38,7 @@ export type StripeOneTimeCheckoutSession = {
36
38
  setError: (message: string | null) => void;
37
39
  stripePromise: Promise<Stripe | null> | null;
38
40
  };
41
+ export declare const buildStripeOneTimeCheckoutPreparationAnalyticsMetadata: (analyticsMetadata: Record<string, unknown> | null | undefined, options?: StripeOneTimeCheckoutPreparationOptions) => Record<string, unknown> | null | undefined;
39
42
  export type StripeOneTimeCheckoutIntentKeyInput = Pick<StripeOneTimeCheckoutSessionInput, 'checkoutMode' | 'couponId' | 'customerEmail' | 'plan' | 'returnUrl' | 'runtimeConfig' | 'userId'> & {
40
43
  analyticsMetadata?: Record<string, unknown> | null;
41
44
  };
@@ -1,8 +1,26 @@
1
1
  'use client';
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
14
  import { createEmptyCheckoutPreparationLoading, } from '../paymentProvider.types.js';
4
15
  import { createStripeOneTimeCheckout, getStripePromise, isStripeConfigured, } from '../../services/stripe.service.js';
5
16
  import { isInactiveCheckoutSessionError } from './useStripeSubscriptionCheckoutSession.js';
17
+ import { toAnalyticsPaymentMethod } from '../../services/paymentMethodAnalytics.service.js';
18
+ export function resolveStripeOneTimeCheckoutPreparationOptions(source, options = {}) {
19
+ var _a, _b;
20
+ const checkoutStartSource = (_a = options.checkoutStartSource) !== null && _a !== void 0 ? _a : (source === 'wallet' ? 'wallet_render' : 'card_click');
21
+ const paymentMethod = (_b = options.paymentMethod) !== null && _b !== void 0 ? _b : (source === 'card' && checkoutStartSource !== 'checkout_render' ? 'card' : undefined);
22
+ return Object.assign(Object.assign(Object.assign({}, options), { checkoutStartSource }), (paymentMethod !== undefined ? { paymentMethod } : {}));
23
+ }
6
24
  const getFriendlyStripeCheckoutError = (error) => {
7
25
  const message = error instanceof Error ? error.message : 'Unable to initialize checkout';
8
26
  return message === 'Internal server error'
@@ -11,13 +29,12 @@ const getFriendlyStripeCheckoutError = (error) => {
11
29
  };
12
30
  const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
13
31
  const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
14
- const buildCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
32
+ export const buildStripeOneTimeCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
15
33
  var _a;
16
34
  const checkoutStartSource = (_a = options.checkoutStartSource) === null || _a === void 0 ? void 0 : _a.trim();
17
- if (!checkoutStartSource) {
18
- return analyticsMetadata;
19
- }
20
- return Object.assign(Object.assign({}, (analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {})), { checkoutStartSource });
35
+ const paymentMethod = toAnalyticsPaymentMethod(options.paymentMethod);
36
+ const _b = analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {}, { payment_method: _paymentMethod, paymentMethod: _camelCasePaymentMethod } = _b, metadata = __rest(_b, ["payment_method", "paymentMethod"]);
37
+ return Object.assign(Object.assign(Object.assign({}, metadata), (checkoutStartSource ? { checkoutStartSource } : {})), (paymentMethod ? { payment_method: paymentMethod } : {}));
21
38
  };
22
39
  export function buildStripeOneTimeCheckoutIntentKey({ checkoutMode, couponId, customerEmail, plan, returnUrl, runtimeConfig, userId, }) {
23
40
  var _a;
@@ -141,7 +158,7 @@ export function useStripeOneTimeCheckoutSession({ analyticsMetadata, checkoutMod
141
158
  try {
142
159
  const response = await createStripeOneTimeCheckout({
143
160
  plan,
144
- analyticsMetadata: buildCheckoutPreparationAnalyticsMetadata(analyticsMetadataRef.current, options),
161
+ analyticsMetadata: buildStripeOneTimeCheckoutPreparationAnalyticsMetadata(analyticsMetadataRef.current, options),
145
162
  couponId,
146
163
  customerEmail,
147
164
  returnUrl,
@@ -198,7 +215,6 @@ export function useStripeOneTimeCheckoutSession({ analyticsMetadata, checkoutMod
198
215
  userId,
199
216
  ]);
200
217
  const prepareCheckout = useCallback(async (source, options = {}) => {
201
- var _a;
202
218
  if (!configured || !plan) {
203
219
  return null;
204
220
  }
@@ -208,7 +224,7 @@ export function useStripeOneTimeCheckoutSession({ analyticsMetadata, checkoutMod
208
224
  setSourceLoading(source, true);
209
225
  setError(null);
210
226
  try {
211
- return await createCheckoutSession(Object.assign(Object.assign({}, options), { checkoutStartSource: (_a = options.checkoutStartSource) !== null && _a !== void 0 ? _a : (source === 'wallet' ? 'wallet_render' : 'card_click') }));
227
+ return await createCheckoutSession(Object.assign({}, resolveStripeOneTimeCheckoutPreparationOptions(source, options)));
212
228
  }
213
229
  finally {
214
230
  setSourceLoading(source, false);
@@ -1,6 +1,6 @@
1
1
  import type { Stripe } from '@stripe/stripe-js';
2
2
  import type { RuntimeMode } from '@funnelsgrove/runtime';
3
- import { type CheckoutPreparationLoading } from '../paymentProvider.types.js';
3
+ import { type CheckoutPreparationLoading, type CheckoutPreparationSource } from '../paymentProvider.types.js';
4
4
  import type { PaywallDisplayPlan } from '../../services/paywallOffer.service.js';
5
5
  import { type PaywallPlan, type StripeRuntimeConfigOverrides } from '../../services/stripe.service.js';
6
6
  export type StripeSubscriptionCheckoutSessionInput = {
@@ -17,9 +17,11 @@ export type StripeSubscriptionCheckoutSessionInput = {
17
17
  userId?: string | null;
18
18
  };
19
19
  export type StripeSubscriptionCheckoutPreparationOptions = {
20
- checkoutStartSource?: 'card_click' | 'wallet_click' | 'wallet_render' | (string & {});
20
+ checkoutStartSource?: 'card_click' | 'checkout_render' | 'wallet_click' | 'wallet_render' | (string & {});
21
21
  forceNew?: boolean;
22
+ paymentMethod?: unknown;
22
23
  };
24
+ export declare function resolveStripeSubscriptionCheckoutPreparationOptions(source: CheckoutPreparationSource, options?: StripeSubscriptionCheckoutPreparationOptions): StripeSubscriptionCheckoutPreparationOptions;
23
25
  export type StripeSubscriptionCheckoutSession = {
24
26
  activeClientSecret: string | null;
25
27
  activePlanKey: string | null;
@@ -39,6 +41,7 @@ export type StripeSubscriptionCheckoutSession = {
39
41
  stripePromise: Promise<Stripe | null> | null;
40
42
  updateCheckoutSessionPlan: () => Promise<boolean>;
41
43
  };
44
+ export declare const buildStripeSubscriptionCheckoutPreparationAnalyticsMetadata: (analyticsMetadata: Record<string, unknown> | null | undefined, options?: StripeSubscriptionCheckoutPreparationOptions) => Record<string, unknown> | null | undefined;
42
45
  export type StripeSubscriptionCheckoutIntentKeyInput = Pick<StripeSubscriptionCheckoutSessionInput, 'checkoutMode' | 'couponId' | 'customerEmail' | 'returnUrl' | 'runtimeConfig' | 'userId'> & {
43
46
  analyticsMetadata?: Record<string, unknown> | null;
44
47
  };
@@ -1,7 +1,25 @@
1
1
  'use client';
2
+ var __rest = (this && this.__rest) || function (s, e) {
3
+ var t = {};
4
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
5
+ t[p] = s[p];
6
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
7
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
8
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
9
+ t[p[i]] = s[p[i]];
10
+ }
11
+ return t;
12
+ };
2
13
  import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
3
14
  import { createEmptyCheckoutPreparationLoading, } from '../paymentProvider.types.js';
4
15
  import { createStripeSubscriptionCheckout, getStripePromise, isStripeConfigured, updateStripeSubscriptionCheckoutPlan, } from '../../services/stripe.service.js';
16
+ import { toAnalyticsPaymentMethod } from '../../services/paymentMethodAnalytics.service.js';
17
+ export function resolveStripeSubscriptionCheckoutPreparationOptions(source, options = {}) {
18
+ var _a, _b;
19
+ const checkoutStartSource = (_a = options.checkoutStartSource) !== null && _a !== void 0 ? _a : (source === 'wallet' ? 'wallet_render' : 'card_click');
20
+ const paymentMethod = (_b = options.paymentMethod) !== null && _b !== void 0 ? _b : (source === 'card' && checkoutStartSource !== 'checkout_render' ? 'card' : undefined);
21
+ return Object.assign(Object.assign(Object.assign({}, options), { checkoutStartSource }), (paymentMethod !== undefined ? { paymentMethod } : {}));
22
+ }
5
23
  const getFriendlyStripeCheckoutError = (error) => {
6
24
  const message = error instanceof Error ? error.message : 'Unable to initialize checkout';
7
25
  return message === 'Internal server error'
@@ -10,13 +28,12 @@ const getFriendlyStripeCheckoutError = (error) => {
10
28
  };
11
29
  const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
12
30
  const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
13
- const buildCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
31
+ export const buildStripeSubscriptionCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
14
32
  var _a;
15
33
  const checkoutStartSource = (_a = options.checkoutStartSource) === null || _a === void 0 ? void 0 : _a.trim();
16
- if (!checkoutStartSource) {
17
- return analyticsMetadata;
18
- }
19
- return Object.assign(Object.assign({}, (analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {})), { checkoutStartSource });
34
+ const paymentMethod = toAnalyticsPaymentMethod(options.paymentMethod);
35
+ const _b = analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {}, { payment_method: _paymentMethod, paymentMethod: _camelCasePaymentMethod } = _b, metadata = __rest(_b, ["payment_method", "paymentMethod"]);
36
+ return Object.assign(Object.assign(Object.assign({}, metadata), (checkoutStartSource ? { checkoutStartSource } : {})), (paymentMethod ? { payment_method: paymentMethod } : {}));
20
37
  };
21
38
  export function buildStripeSubscriptionCheckoutIntentKey({ checkoutMode, couponId, customerEmail, returnUrl, runtimeConfig, userId, }) {
22
39
  const runtimeConfigSignature = [
@@ -142,7 +159,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
142
159
  try {
143
160
  const response = await createStripeSubscriptionCheckout({
144
161
  plan,
145
- analyticsMetadata: buildCheckoutPreparationAnalyticsMetadata(analyticsMetadataRef.current, options),
162
+ analyticsMetadata: buildStripeSubscriptionCheckoutPreparationAnalyticsMetadata(analyticsMetadataRef.current, options),
146
163
  couponId,
147
164
  customerEmail,
148
165
  returnUrl,
@@ -201,7 +218,6 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
201
218
  userId,
202
219
  ]);
203
220
  const prepareCheckout = useCallback(async (source, options = {}) => {
204
- var _a;
205
221
  if (!configured || !plan || !displayPlan) {
206
222
  return null;
207
223
  }
@@ -211,7 +227,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
211
227
  setSourceLoading(source, true);
212
228
  setError(null);
213
229
  try {
214
- return await createCheckoutSubscription(Object.assign(Object.assign({}, options), { checkoutStartSource: (_a = options.checkoutStartSource) !== null && _a !== void 0 ? _a : (source === 'wallet' ? 'wallet_render' : 'card_click') }));
230
+ return await createCheckoutSubscription(Object.assign({}, resolveStripeSubscriptionCheckoutPreparationOptions(source, options)));
215
231
  }
216
232
  finally {
217
233
  setSourceLoading(source, false);
@@ -12,6 +12,7 @@ export type StripeSubscriptionCheckoutAnalyticsContext = {
12
12
  };
13
13
  export type TrackStripeSubscriptionCheckoutStartedInput = StripeSubscriptionCheckoutAnalyticsContext & {
14
14
  checkoutSessionId?: string | null;
15
+ paymentMethod?: unknown;
15
16
  };
16
17
  export type TrackPaidStripeSubscriptionCheckoutCompletedInput = StripeSubscriptionCheckoutAnalyticsContext & {
17
18
  checkoutSessionId?: string | null;
@@ -19,4 +20,5 @@ export type TrackPaidStripeSubscriptionCheckoutCompletedInput = StripeSubscripti
19
20
  export type TrackStripeSubscriptionCheckoutStartedResult = 'already_tracked' | 'analytics_rejected' | 'missing_checkout_session' | 'tracked';
20
21
  export type TrackPaidStripeSubscriptionCheckoutCompletedResult = 'already_tracked' | 'analytics_rejected' | 'missing_checkout_session' | 'tracked' | 'unpaid';
21
22
  export declare function trackStripeSubscriptionCheckoutStarted(input: TrackStripeSubscriptionCheckoutStartedInput): Promise<TrackStripeSubscriptionCheckoutStartedResult>;
23
+ export declare function queueStripeSubscriptionCheckoutStarted(input: TrackStripeSubscriptionCheckoutStartedInput): void;
22
24
  export declare function trackPaidStripeSubscriptionCheckoutCompleted(input: TrackPaidStripeSubscriptionCheckoutCompletedInput): Promise<TrackPaidStripeSubscriptionCheckoutCompletedResult>;
@@ -1,5 +1,17 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { publicAnalyticsSdk, } from '@funnelsgrove/analytics';
2
13
  import { verifyStripeSubscriptionCheckoutPayment, } from './stripe.service.js';
14
+ import { toAnalyticsPaymentMethod } from './paymentMethodAnalytics.service.js';
3
15
  const asRecord = (value) => {
4
16
  return value && typeof value === 'object' && !Array.isArray(value)
5
17
  ? value
@@ -49,7 +61,8 @@ export async function trackStripeSubscriptionCheckoutStarted(input) {
49
61
  }
50
62
  activeCheckoutStartedKeys.add(startedKey);
51
63
  try {
52
- const metadata = asRecord(input.metadata);
64
+ const _b = asRecord(input.metadata), { payment_method: _paymentMethod, payment_provider: _paymentProvider } = _b, metadata = __rest(_b, ["payment_method", "payment_provider"]);
65
+ const paymentMethod = toAnalyticsPaymentMethod(input.paymentMethod);
53
66
  const conversionInput = {
54
67
  eventId: checkoutSessionId,
55
68
  featureFlags: input.featureFlags,
@@ -57,7 +70,7 @@ export async function trackStripeSubscriptionCheckoutStarted(input) {
57
70
  stepName: input.stepName,
58
71
  stepType: input.stepType,
59
72
  stepContractVersion: input.stepContractVersion,
60
- metadata: Object.assign(Object.assign({}, metadata), { checkoutSessionId, checkout_session_id: checkoutSessionId, environment: input.environment || metadata.environment }),
73
+ metadata: Object.assign(Object.assign(Object.assign({}, metadata), { checkoutSessionId, checkout_session_id: checkoutSessionId, environment: input.environment || metadata.environment, payment_provider: 'stripe' }), (paymentMethod ? { payment_method: paymentMethod } : {})),
61
74
  };
62
75
  const eventId = publicAnalyticsSdk.trackCheckoutStarted(conversionInput);
63
76
  if (!eventId) {
@@ -71,6 +84,9 @@ export async function trackStripeSubscriptionCheckoutStarted(input) {
71
84
  activeCheckoutStartedKeys.delete(startedKey);
72
85
  }
73
86
  }
87
+ export function queueStripeSubscriptionCheckoutStarted(input) {
88
+ void trackStripeSubscriptionCheckoutStarted(input).catch(() => undefined);
89
+ }
74
90
  export async function trackPaidStripeSubscriptionCheckoutCompleted(input) {
75
91
  var _a;
76
92
  const checkoutSessionId = ((_a = input.checkoutSessionId) === null || _a === void 0 ? void 0 : _a.trim()) || '';
@@ -0,0 +1,2 @@
1
+ export type AnalyticsPaymentMethod = 'apple_pay' | 'card' | 'google_pay';
2
+ export declare function toAnalyticsPaymentMethod(value: unknown): AnalyticsPaymentMethod | undefined;
@@ -0,0 +1,14 @@
1
+ export function toAnalyticsPaymentMethod(value) {
2
+ switch (value) {
3
+ case 'applePay':
4
+ case 'apple_pay':
5
+ return 'apple_pay';
6
+ case 'card':
7
+ return 'card';
8
+ case 'googlePay':
9
+ case 'google_pay':
10
+ return 'google_pay';
11
+ default:
12
+ return undefined;
13
+ }
14
+ }
@@ -35,6 +35,10 @@ const BILLING_INTERVAL_DAYS = {
35
35
  month: 30,
36
36
  year: 365,
37
37
  };
38
+ const PAYWALL_DISCOUNT_COOLDOWN_MS = 30 * 60 * 1000;
39
+ const hasDiscountCooldownElapsed = (nowMs, expiresAtMs) => {
40
+ return nowMs >= expiresAtMs + PAYWALL_DISCOUNT_COOLDOWN_MS;
41
+ };
38
42
  const clampRemainingSeconds = (value) => {
39
43
  if (!Number.isFinite(value) || value <= 0) {
40
44
  return 0;
@@ -142,6 +146,9 @@ const createDiscountStateFromStarts = (input) => {
142
146
  const expiresAtMs = input.secondStartedAtMs + getDiscountDurationMs(input.discounts, 'second');
143
147
  const remainingSeconds = clampRemainingSeconds((expiresAtMs - input.nowMs) / 1000);
144
148
  if (remainingSeconds <= 0) {
149
+ if (hasDiscountCooldownElapsed(input.nowMs, expiresAtMs)) {
150
+ return createDiscountStateFromStarts(Object.assign(Object.assign({}, input), { firstStartedAtMs: input.nowMs, secondStartedAtMs: null }));
151
+ }
145
152
  return createExpiredDiscountState({
146
153
  stage: 'completed',
147
154
  firstStartedAtMs: input.firstStartedAtMs,
@@ -167,6 +174,9 @@ const createDiscountStateFromStarts = (input) => {
167
174
  const expiresAtMs = input.firstStartedAtMs + getDiscountDurationMs(input.discounts, 'first');
168
175
  const remainingSeconds = clampRemainingSeconds((expiresAtMs - input.nowMs) / 1000);
169
176
  if (remainingSeconds <= 0) {
177
+ if (hasDiscountCooldownElapsed(input.nowMs, expiresAtMs)) {
178
+ return createDiscountStateFromStarts(Object.assign(Object.assign({}, input), { firstStartedAtMs: input.nowMs, secondStartedAtMs: null }));
179
+ }
170
180
  return createExpiredDiscountState({
171
181
  stage: 'first',
172
182
  firstStartedAtMs: input.firstStartedAtMs,
@@ -227,7 +237,8 @@ export const advancePaywallDiscountState = (input) => {
227
237
  };
228
238
  export const activateSecondPaywallDiscount = (input) => {
229
239
  var _a;
230
- if (typeof input.state.secondStartedAtMs === 'number' ||
240
+ if (input.state.status !== 'active' ||
241
+ typeof input.state.secondStartedAtMs === 'number' ||
231
242
  input.state.stage === 'completed') {
232
243
  return input.state;
233
244
  }
@@ -44,7 +44,7 @@ export declare function getStripeSyncedPlans(mode: RuntimeMode, planCatalog?: Bi
44
44
  export declare function resolvePaywallPlansFromProjectPlans(projectPlans: readonly RemoteProjectBillingPlan[], planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): readonly PaywallPlan[];
45
45
  export declare function getPaywallPlans(mode: RuntimeMode, planCatalog?: BillingPlanInputCatalog | readonly BillingFallbackPlanConfig[], options?: PaywallPlanResolutionOptions): Promise<readonly PaywallPlan[]>;
46
46
  export declare function isStripeConfigured(mode: RuntimeMode): boolean;
47
- export declare function getStripePromise(mode: RuntimeMode, runtimePublishableKey?: string | null): Promise<Stripe | null> | null;
47
+ export declare function getStripePromise(_mode: RuntimeMode, runtimePublishableKey?: string | null): Promise<Stripe | null> | null;
48
48
  type CheckoutSessionPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'billingInterval' | 'billingIntervalCount' | 'description' | 'id' | 'providerPlanId' | 'title' | 'followUpProviderPlanId' | 'followUpPlanTitle' | 'followUpBillingInterval' | 'followUpBillingIntervalCount' | 'introIterations'>;
49
49
  type CheckoutPlanRecurringConfig = {
50
50
  interval: BillingPlanInterval;
@@ -80,6 +80,10 @@ type CreateOneTimeCheckoutResponse = {
80
80
  checkoutSessionId?: string;
81
81
  stripePublishableKey?: string;
82
82
  environment?: 'test' | 'live';
83
+ paymentProfileId?: string;
84
+ userId?: string;
85
+ anonymousUserId?: string;
86
+ funnelEndUserId?: string;
83
87
  };
84
88
  export declare function createStripeOneTimeCheckout(input: CreateOneTimeCheckoutInput): Promise<CreateOneTimeCheckoutResponse>;
85
89
  /**
@@ -124,6 +128,10 @@ type CreateSubscriptionCheckoutResponse = {
124
128
  subscriptionId?: string;
125
129
  stripePublishableKey?: string;
126
130
  environment?: 'test' | 'live';
131
+ paymentProfileId?: string;
132
+ userId?: string;
133
+ anonymousUserId?: string;
134
+ funnelEndUserId?: string;
127
135
  };
128
136
  type UpdateSubscriptionCheckoutPlanInput = {
129
137
  checkoutSessionId: string;
@@ -169,6 +177,12 @@ type CreateCheckoutSessionInput = {
169
177
  type CreateCheckoutSessionResponse = {
170
178
  sessionId?: string;
171
179
  url: string;
180
+ stripePublishableKey?: string;
181
+ environment?: 'test' | 'live';
182
+ paymentProfileId?: string;
183
+ userId?: string;
184
+ anonymousUserId?: string;
185
+ funnelEndUserId?: string;
172
186
  };
173
187
  export declare function createStripeCheckoutSession(input: CreateCheckoutSessionInput): Promise<CreateCheckoutSessionResponse>;
174
188
  export declare function redirectToStripeCheckout(input: CreateCheckoutSessionInput): Promise<string>;
@@ -432,8 +432,8 @@ export async function getPaywallPlans(mode, planCatalog, options) {
432
432
  export function isStripeConfigured(mode) {
433
433
  return getStripePublishableKeyFromEnv(mode).length > 0 || FUNNEL_SDK_PUBLISHABLE_KEY.length > 0;
434
434
  }
435
- export function getStripePromise(mode, runtimePublishableKey) {
436
- const resolvedPublishableKey = (runtimePublishableKey !== null && runtimePublishableKey !== void 0 ? runtimePublishableKey : getStripePublishableKeyFromEnv(mode)).trim();
435
+ export function getStripePromise(_mode, runtimePublishableKey) {
436
+ const resolvedPublishableKey = (runtimePublishableKey === null || runtimePublishableKey === void 0 ? void 0 : runtimePublishableKey.trim()) || '';
437
437
  if (!resolvedPublishableKey) {
438
438
  return null;
439
439
  }
package/package.json CHANGED
@@ -1,6 +1,11 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.1.54",
3
+ "version": "0.1.56",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",
7
+ "directory": "apps/funnel-payments"
8
+ },
4
9
  "type": "module",
5
10
  "private": false,
6
11
  "main": "./dist/index.js",