@funnelsgrove/payments 0.1.29 → 0.1.31

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.
@@ -14,6 +14,7 @@ export type BillingPlanConfig = {
14
14
  featuredTag?: string;
15
15
  perDayAmount: string;
16
16
  amountCents: number;
17
+ analyticsPurchaseValue?: number;
17
18
  checkoutSummaryLabel?: string;
18
19
  };
19
20
  export type BillingFallbackPlanConfig = BillingPlanConfig & {
@@ -1,6 +1,19 @@
1
1
  import { type ReactNode } from 'react';
2
2
  import type { AvailablePaymentMethods, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
3
3
  import { type StripeSubscriptionCheckoutSession } from './useStripeSubscriptionCheckoutSession.js';
4
+ export type WalletSubscriptionCheckoutRenderStateInput = {
5
+ expressCheckoutAllowed: boolean;
6
+ hasActiveClientSecret: boolean;
7
+ hasStripePromise: boolean;
8
+ suspended: boolean;
9
+ walletAvailable: boolean | null;
10
+ };
11
+ export type WalletSubscriptionCheckoutRenderState = {
12
+ placeholderIsPreparing: boolean;
13
+ shouldRenderExpressCheckout: boolean;
14
+ shouldRenderPlaceholder: boolean;
15
+ };
16
+ export declare function getWalletSubscriptionCheckoutRenderState({ expressCheckoutAllowed, hasActiveClientSecret, hasStripePromise, suspended, walletAvailable, }: WalletSubscriptionCheckoutRenderStateInput): WalletSubscriptionCheckoutRenderState;
4
17
  type WalletPlaceholderButtonProps = {
5
18
  className?: string;
6
19
  disabled: boolean;
@@ -3,6 +3,22 @@ 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 { isInactiveCheckoutSessionError, } from './useStripeSubscriptionCheckoutSession.js';
6
+ export function getWalletSubscriptionCheckoutRenderState({ expressCheckoutAllowed, hasActiveClientSecret, hasStripePromise, suspended, walletAvailable, }) {
7
+ const shouldRenderExpressCheckout = !suspended &&
8
+ expressCheckoutAllowed &&
9
+ walletAvailable !== false &&
10
+ hasActiveClientSecret &&
11
+ hasStripePromise;
12
+ const shouldRenderPlaceholder = !shouldRenderExpressCheckout &&
13
+ !suspended &&
14
+ expressCheckoutAllowed &&
15
+ walletAvailable === null;
16
+ return {
17
+ placeholderIsPreparing: shouldRenderPlaceholder,
18
+ shouldRenderExpressCheckout,
19
+ shouldRenderPlaceholder,
20
+ };
21
+ }
6
22
  export function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label, }) {
7
23
  return (_jsxs("div", { className: [
8
24
  'wallet-subscription-checkout-slot__loading',
@@ -21,16 +37,13 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymen
21
37
  const walletAvailable = walletAvailability.intentKey === session.intentKey
22
38
  ? walletAvailability.available
23
39
  : null;
24
- const shouldRenderExpressCheckout = !suspended &&
25
- expressCheckoutAllowed &&
26
- walletAvailable !== false &&
27
- Boolean(session.activeClientSecret) &&
28
- Boolean(session.stripePromise);
29
- const shouldRenderPlaceholder = !shouldRenderExpressCheckout &&
30
- !suspended &&
31
- expressCheckoutAllowed &&
32
- walletAvailable === null;
33
- const placeholderIsPreparing = shouldRenderPlaceholder;
40
+ const { placeholderIsPreparing, shouldRenderExpressCheckout, shouldRenderPlaceholder, } = getWalletSubscriptionCheckoutRenderState({
41
+ expressCheckoutAllowed,
42
+ hasActiveClientSecret: Boolean(session.activeClientSecret),
43
+ hasStripePromise: Boolean(session.stripePromise),
44
+ suspended,
45
+ walletAvailable,
46
+ });
34
47
  const serverUpdateKey = session.activeClientSecret &&
35
48
  session.checkoutSessionId &&
36
49
  session.activePlanKey !== session.planKey
@@ -35,5 +35,10 @@ export type StripeSubscriptionCheckoutSession = {
35
35
  stripePromise: Promise<Stripe | null> | null;
36
36
  updateCheckoutSessionPlan: () => Promise<boolean>;
37
37
  };
38
+ export type StripeSubscriptionCheckoutIntentKeyInput = Pick<StripeSubscriptionCheckoutSessionInput, 'checkoutMode' | 'couponId' | 'customerEmail' | 'returnUrl' | 'runtimeConfig' | 'userId'> & {
39
+ analyticsMetadata?: Record<string, unknown> | null;
40
+ };
41
+ export declare function buildStripeSubscriptionCheckoutIntentKey({ checkoutMode, couponId, customerEmail, returnUrl, runtimeConfig, userId, }: StripeSubscriptionCheckoutIntentKeyInput): string;
42
+ export declare function shouldResetStripeSubscriptionCheckoutSessionForIntentKey(previousIntentKey: string | null, nextIntentKey: string): boolean;
38
43
  export declare function isInactiveCheckoutSessionError(message?: string | null): boolean;
39
44
  export declare function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMetadata, couponId, customerEmail, displayPlan, enabled, onError, plan, returnUrl, runtimeConfig, userId, }: StripeSubscriptionCheckoutSessionInput): StripeSubscriptionCheckoutSession;
@@ -9,6 +9,28 @@ const getFriendlyStripeCheckoutError = (error) => {
9
9
  : message;
10
10
  };
11
11
  const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
12
+ export function buildStripeSubscriptionCheckoutIntentKey({ checkoutMode, couponId, customerEmail, returnUrl, runtimeConfig, userId, }) {
13
+ const runtimeConfigSignature = [
14
+ normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.apiBaseUrl),
15
+ normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId),
16
+ normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelVersionId),
17
+ normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey),
18
+ ].join('|');
19
+ const customerEmailSignature = normalizeRuntimeConfigValue(customerEmail);
20
+ const userIdSignature = normalizeRuntimeConfigValue(userId);
21
+ return [
22
+ 'stripe',
23
+ checkoutMode,
24
+ runtimeConfigSignature,
25
+ customerEmailSignature,
26
+ userIdSignature,
27
+ (couponId === null || couponId === void 0 ? void 0 : couponId.trim()) || '',
28
+ returnUrl.trim(),
29
+ ].join('|');
30
+ }
31
+ export function shouldResetStripeSubscriptionCheckoutSessionForIntentKey(previousIntentKey, nextIntentKey) {
32
+ return previousIntentKey !== null && previousIntentKey !== nextIntentKey;
33
+ }
12
34
  export function isInactiveCheckoutSessionError(message) {
13
35
  var _a;
14
36
  return /\bcheckout session\b.*\bno longer active\b/i.test((_a = message === null || message === void 0 ? void 0 : message.trim()) !== null && _a !== void 0 ? _a : '');
@@ -24,24 +46,17 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
24
46
  const [runtimeStripePublishableKey, setRuntimeStripePublishableKey] = useState(null);
25
47
  const creatingIntentRef = useRef(null);
26
48
  const requestIdRef = useRef(0);
27
- const runtimeConfigSignature = [
28
- normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.apiBaseUrl),
29
- normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId),
30
- normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelVersionId),
31
- normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey),
32
- ].join('|');
33
- const analyticsMetadataSignature = JSON.stringify(analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {});
34
49
  const configured = enabled &&
35
50
  (isStripeConfigured(checkoutMode) ||
36
51
  Boolean(normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey)));
37
- const intentKey = [
38
- 'stripe',
52
+ const intentKey = buildStripeSubscriptionCheckoutIntentKey({
39
53
  checkoutMode,
40
- runtimeConfigSignature,
41
- analyticsMetadataSignature,
42
- (couponId === null || couponId === void 0 ? void 0 : couponId.trim()) || '',
43
- returnUrl.trim(),
44
- ].join('|');
54
+ couponId,
55
+ customerEmail,
56
+ returnUrl,
57
+ runtimeConfig,
58
+ userId,
59
+ });
45
60
  const planKey = [
46
61
  (_a = plan === null || plan === void 0 ? void 0 : plan.id) !== null && _a !== void 0 ? _a : '',
47
62
  (_b = plan === null || plan === void 0 ? void 0 : plan.providerPlanId) !== null && _b !== void 0 ? _b : '',
@@ -53,6 +68,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
53
68
  const activeCheckoutSessionId = clientSecretIntentKey === intentKey ? checkoutSessionId : null;
54
69
  const activePlanKey = clientSecretIntentKey === intentKey ? clientSecretPlanKey : null;
55
70
  const stripePromise = useMemo(() => getStripePromise(checkoutMode, runtimeStripePublishableKey), [checkoutMode, runtimeStripePublishableKey]);
71
+ const previousIntentKeyRef = useRef(intentKey);
56
72
  const clearActiveCheckoutSession = useCallback(() => {
57
73
  requestIdRef.current += 1;
58
74
  creatingIntentRef.current = null;
@@ -78,7 +94,11 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
78
94
  setError(null);
79
95
  }, [clearActiveCheckoutSession, setError]);
80
96
  useEffect(() => {
81
- reset();
97
+ const previousIntentKey = previousIntentKeyRef.current;
98
+ previousIntentKeyRef.current = intentKey;
99
+ if (shouldResetStripeSubscriptionCheckoutSessionForIntentKey(previousIntentKey, intentKey)) {
100
+ reset();
101
+ }
82
102
  }, [intentKey, reset]);
83
103
  const setSourceLoading = useCallback((source, value) => {
84
104
  setLoading((current) => current[source] === value
@@ -14,6 +14,7 @@ export type PaywallPlan = {
14
14
  perDayAmount: string;
15
15
  perDayLabel: string;
16
16
  amountCents: number;
17
+ analyticsPurchaseValue?: number;
17
18
  comparison?: BillingFallbackPlanConfig['comparison'];
18
19
  checkoutOriginalAmountCents?: number;
19
20
  checkoutSummaryLabel?: string;
@@ -68,6 +68,17 @@ const asPositiveIntOrNull = (value) => {
68
68
  }
69
69
  return Math.round(numeric);
70
70
  };
71
+ const asNonNegativeNumberOrNull = (value) => {
72
+ const numeric = typeof value === 'number'
73
+ ? value
74
+ : typeof value === 'string' && value.trim()
75
+ ? Number(value.trim())
76
+ : null;
77
+ if (numeric === null || !Number.isFinite(numeric) || numeric < 0) {
78
+ return null;
79
+ }
80
+ return Math.round(numeric * 100) / 100;
81
+ };
71
82
  const asBillingPlanIntervalOrNull = (value) => {
72
83
  var _a;
73
84
  const normalized = (_a = asTrimmedStringOrNull(value)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
@@ -230,6 +241,7 @@ const toRemoteProjectBillingPlan = (rawPlan) => {
230
241
  };
231
242
  };
232
243
  const toPlanFromRemoteProjectPlan = (rawPlan, options, planIdOverride) => {
244
+ var _a;
233
245
  const planId = (planIdOverride === null || planIdOverride === void 0 ? void 0 : planIdOverride.trim()) || rawPlan.id || rawPlan.key || rawPlan.providerPlanId;
234
246
  if (!planId) {
235
247
  return null;
@@ -266,6 +278,7 @@ const toPlanFromRemoteProjectPlan = (rawPlan, options, planIdOverride) => {
266
278
  perDayAmount,
267
279
  perDayLabel: 'per day',
268
280
  amountCents: amountMinor,
281
+ analyticsPurchaseValue: (_a = asNonNegativeNumberOrNull(metadata.analyticsPurchaseValue)) !== null && _a !== void 0 ? _a : undefined,
269
282
  comparison: intervalDays
270
283
  ? {
271
284
  perDay: `${perDayAmount}/day`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",