@funnelsgrove/payments 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -3
- package/dist/components/shared/ApplePaySubscribeButton.d.ts +15 -0
- package/dist/components/shared/ApplePaySubscribeButton.js +76 -0
- package/dist/components/shared/CheckoutBrandAssets.d.ts +12 -0
- package/dist/components/shared/CheckoutBrandAssets.js +56 -0
- package/dist/components/shared/GooglePaySubscribeButton.d.ts +14 -0
- package/dist/components/shared/GooglePaySubscribeButton.js +94 -0
- package/dist/components/shared/SharedStripeCheckoutDialog.d.ts +44 -0
- package/dist/components/shared/SharedStripeCheckoutDialog.js +717 -0
- package/dist/components/shared/SharedStripeCheckoutV2Dialog.d.ts +60 -0
- package/dist/components/shared/SharedStripeCheckoutV2Dialog.js +988 -0
- package/dist/components/shared/StripeExpressCheckoutButton.d.ts +18 -0
- package/dist/components/shared/StripeExpressCheckoutButton.js +164 -0
- package/dist/components/shared/StripeExpressCheckoutElement.d.ts +8 -0
- package/dist/components/shared/StripeExpressCheckoutElement.js +19 -0
- package/dist/components/shared/StripePlanSelector.d.ts +5 -7
- package/dist/components/shared/StripePlanSelector.js +16 -57
- package/dist/components/shared/walletPlatform.d.ts +11 -0
- package/dist/components/shared/walletPlatform.js +39 -0
- package/dist/config/billing.config.d.ts +60 -9
- package/dist/config/billing.config.js +7 -2
- package/dist/hooks/useResolvedPaywallPlans.d.ts +15 -0
- package/dist/hooks/useResolvedPaywallPlans.js +68 -0
- package/dist/index.d.ts +21 -5
- package/dist/index.js +21 -5
- package/dist/providers/paymentProvider.types.d.ts +4 -0
- package/dist/providers/paymentProvider.types.js +4 -0
- package/dist/providers/stripe/ApplePaySubscriptionCheckoutSlot.d.ts +5 -0
- package/dist/providers/stripe/ApplePaySubscriptionCheckoutSlot.js +21 -0
- package/dist/providers/stripe/GooglePaySubscriptionCheckoutSlot.d.ts +7 -0
- package/dist/providers/stripe/GooglePaySubscriptionCheckoutSlot.js +44 -0
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.d.ts +32 -0
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.js +69 -0
- package/dist/providers/stripe/useStripeSubscriptionCheckoutSession.d.ts +30 -0
- package/dist/providers/stripe/useStripeSubscriptionCheckoutSession.js +164 -0
- package/dist/services/paywallOffer.service.d.ts +65 -0
- package/dist/services/paywallOffer.service.js +312 -0
- package/dist/services/planCatalog.service.d.ts +28 -0
- package/dist/services/planCatalog.service.js +60 -0
- package/dist/services/runtimeBillingPlanCatalog.service.d.ts +6 -0
- package/dist/services/runtimeBillingPlanCatalog.service.js +24 -0
- package/dist/services/stripe.service.d.ts +66 -6
- package/dist/services/stripe.service.js +409 -54
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
|
-
export * from './config/billing.config';
|
|
2
|
-
export * from './services/preview-frame.service';
|
|
3
|
-
export * from './services/runtime-mode.service';
|
|
4
|
-
export * from './services/
|
|
5
|
-
export * from './
|
|
1
|
+
export * from './config/billing.config.js';
|
|
2
|
+
export * from './services/preview-frame.service.js';
|
|
3
|
+
export * from './services/runtime-mode.service.js';
|
|
4
|
+
export * from './services/planCatalog.service.js';
|
|
5
|
+
export * from './services/runtimeBillingPlanCatalog.service.js';
|
|
6
|
+
export * from './services/paywallOffer.service.js';
|
|
7
|
+
export * from './services/stripe.service.js';
|
|
8
|
+
export * from './hooks/useResolvedPaywallPlans.js';
|
|
9
|
+
export * from './providers/paymentProvider.types.js';
|
|
10
|
+
export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
11
|
+
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
12
|
+
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
13
|
+
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
14
|
+
export * from './components/shared/SharedStripeCheckoutDialog.js';
|
|
15
|
+
export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
|
|
16
|
+
export * from './components/shared/ApplePaySubscribeButton.js';
|
|
17
|
+
export * from './components/shared/GooglePaySubscribeButton.js';
|
|
18
|
+
export * from './components/shared/StripeExpressCheckoutButton.js';
|
|
19
|
+
export * from './components/shared/StripeExpressCheckoutElement.js';
|
|
20
|
+
export * from './components/shared/StripePlanSelector.js';
|
|
21
|
+
export * from './components/shared/walletPlatform.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
|
-
export * from './config/billing.config';
|
|
2
|
-
export * from './services/preview-frame.service';
|
|
3
|
-
export * from './services/runtime-mode.service';
|
|
4
|
-
export * from './services/
|
|
5
|
-
export * from './
|
|
1
|
+
export * from './config/billing.config.js';
|
|
2
|
+
export * from './services/preview-frame.service.js';
|
|
3
|
+
export * from './services/runtime-mode.service.js';
|
|
4
|
+
export * from './services/planCatalog.service.js';
|
|
5
|
+
export * from './services/runtimeBillingPlanCatalog.service.js';
|
|
6
|
+
export * from './services/paywallOffer.service.js';
|
|
7
|
+
export * from './services/stripe.service.js';
|
|
8
|
+
export * from './hooks/useResolvedPaywallPlans.js';
|
|
9
|
+
export * from './providers/paymentProvider.types.js';
|
|
10
|
+
export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
11
|
+
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
12
|
+
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
13
|
+
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
14
|
+
export * from './components/shared/SharedStripeCheckoutDialog.js';
|
|
15
|
+
export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
|
|
16
|
+
export * from './components/shared/ApplePaySubscribeButton.js';
|
|
17
|
+
export * from './components/shared/GooglePaySubscribeButton.js';
|
|
18
|
+
export * from './components/shared/StripeExpressCheckoutButton.js';
|
|
19
|
+
export * from './components/shared/StripeExpressCheckoutElement.js';
|
|
20
|
+
export * from './components/shared/StripePlanSelector.js';
|
|
21
|
+
export * from './components/shared/walletPlatform.js';
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type PaymentProviderId = 'stripe' | (string & {});
|
|
2
|
+
export type CheckoutPreparationSource = 'card' | 'wallet';
|
|
3
|
+
export type CheckoutPreparationLoading = Record<CheckoutPreparationSource, boolean>;
|
|
4
|
+
export declare const createEmptyCheckoutPreparationLoading: () => CheckoutPreparationLoading;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WalletSubscriptionCheckoutSlotProps } from './WalletSubscriptionCheckoutSlot.js';
|
|
2
|
+
export type ApplePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionCheckoutSlotProps, 'availabilityPaymentMethod' | 'expressCheckoutAllowed' | 'placeholderLabel' | 'renderPlaceholder'> & {
|
|
3
|
+
placeholderLabel?: string;
|
|
4
|
+
};
|
|
5
|
+
export declare function ApplePaySubscriptionCheckoutSlot({ placeholderLabel, ...slotProps }: ApplePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
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
|
+
};
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
import { ApplePaySubscribeButton } from '../../components/shared/ApplePaySubscribeButton.js';
|
|
15
|
+
import { useApplePayExpressCheckoutAllowed } from '../../components/shared/SharedStripeCheckoutDialog.js';
|
|
16
|
+
import { WalletSubscriptionCheckoutSlot, } from './WalletSubscriptionCheckoutSlot.js';
|
|
17
|
+
export function ApplePaySubscriptionCheckoutSlot(_a) {
|
|
18
|
+
var { placeholderLabel = 'Subscribe with Apple Pay' } = _a, slotProps = __rest(_a, ["placeholderLabel"]);
|
|
19
|
+
const allowApplePayExpressCheckout = useApplePayExpressCheckoutAllowed();
|
|
20
|
+
return (_jsx(WalletSubscriptionCheckoutSlot, Object.assign({}, slotProps, { availabilityPaymentMethod: 'applePay', expressCheckoutAllowed: allowApplePayExpressCheckout, placeholderLabel: placeholderLabel, renderPlaceholder: (placeholderProps) => (_jsx(ApplePaySubscribeButton, Object.assign({}, placeholderProps))) })));
|
|
21
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { StripeExpressCheckoutElementOptions } from '@stripe/stripe-js';
|
|
2
|
+
import { type WalletSubscriptionCheckoutSlotProps } from './WalletSubscriptionCheckoutSlot.js';
|
|
3
|
+
export type GooglePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionCheckoutSlotProps, 'availabilityPaymentMethod' | 'options' | 'placeholderLabel' | 'renderPlaceholder'> & {
|
|
4
|
+
options?: StripeExpressCheckoutElementOptions;
|
|
5
|
+
placeholderLabel?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function GooglePaySubscriptionCheckoutSlot({ options, placeholderLabel, ...slotProps }: GooglePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
};
|
|
13
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
14
|
+
import { GooglePaySubscribeButton } from '../../components/shared/GooglePaySubscribeButton.js';
|
|
15
|
+
import { WalletSubscriptionCheckoutSlot, } from './WalletSubscriptionCheckoutSlot.js';
|
|
16
|
+
const googlePayExpressCheckoutOptions = {
|
|
17
|
+
buttonHeight: 55,
|
|
18
|
+
buttonTheme: {
|
|
19
|
+
googlePay: 'black',
|
|
20
|
+
},
|
|
21
|
+
buttonType: {
|
|
22
|
+
googlePay: 'subscribe',
|
|
23
|
+
},
|
|
24
|
+
emailRequired: true,
|
|
25
|
+
layout: {
|
|
26
|
+
maxColumns: 1,
|
|
27
|
+
maxRows: 1,
|
|
28
|
+
overflow: 'auto',
|
|
29
|
+
},
|
|
30
|
+
paymentMethodOrder: ['google_pay'],
|
|
31
|
+
paymentMethods: {
|
|
32
|
+
amazonPay: 'never',
|
|
33
|
+
applePay: 'never',
|
|
34
|
+
googlePay: 'always',
|
|
35
|
+
klarna: 'never',
|
|
36
|
+
link: 'never',
|
|
37
|
+
paypal: 'never',
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export function GooglePaySubscriptionCheckoutSlot(_a) {
|
|
41
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
42
|
+
var { options, placeholderLabel = 'Subscribe with Google Pay' } = _a, slotProps = __rest(_a, ["options", "placeholderLabel"]);
|
|
43
|
+
return (_jsx(WalletSubscriptionCheckoutSlot, Object.assign({}, slotProps, { availabilityPaymentMethod: 'googlePay', options: Object.assign(Object.assign(Object.assign({}, googlePayExpressCheckoutOptions), (options !== null && options !== void 0 ? options : {})), { buttonTheme: Object.assign(Object.assign({}, ((_b = googlePayExpressCheckoutOptions.buttonTheme) !== null && _b !== void 0 ? _b : {})), ((_c = options === null || options === void 0 ? void 0 : options.buttonTheme) !== null && _c !== void 0 ? _c : {})), buttonType: Object.assign(Object.assign({}, ((_d = googlePayExpressCheckoutOptions.buttonType) !== null && _d !== void 0 ? _d : {})), ((_e = options === null || options === void 0 ? void 0 : options.buttonType) !== null && _e !== void 0 ? _e : {})), layout: Object.assign(Object.assign({}, ((_f = googlePayExpressCheckoutOptions.layout) !== null && _f !== void 0 ? _f : {})), ((_g = options === null || options === void 0 ? void 0 : options.layout) !== null && _g !== void 0 ? _g : {})), paymentMethods: Object.assign(Object.assign({}, ((_h = googlePayExpressCheckoutOptions.paymentMethods) !== null && _h !== void 0 ? _h : {})), ((_j = options === null || options === void 0 ? void 0 : options.paymentMethods) !== null && _j !== void 0 ? _j : {})), paymentMethodOrder: (_k = options === null || options === void 0 ? void 0 : options.paymentMethodOrder) !== null && _k !== void 0 ? _k : googlePayExpressCheckoutOptions.paymentMethodOrder }), placeholderLabel: placeholderLabel, renderPlaceholder: (placeholderProps) => (_jsx(GooglePaySubscribeButton, Object.assign({}, placeholderProps))) })));
|
|
44
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { AvailablePaymentMethods, StripeExpressCheckoutElementOptions } from '@stripe/stripe-js';
|
|
3
|
+
import type { StripeSubscriptionCheckoutSession } from './useStripeSubscriptionCheckoutSession.js';
|
|
4
|
+
type WalletPlaceholderButtonProps = {
|
|
5
|
+
className?: string;
|
|
6
|
+
disabled: boolean;
|
|
7
|
+
label: string;
|
|
8
|
+
onClick: () => void;
|
|
9
|
+
};
|
|
10
|
+
export type WalletSubscriptionCheckoutSlotProps = {
|
|
11
|
+
amountCents: number;
|
|
12
|
+
availabilityPaymentMethod: keyof AvailablePaymentMethods;
|
|
13
|
+
beforeConfirm?: () => Promise<string | null>;
|
|
14
|
+
className?: string;
|
|
15
|
+
customerEmail?: string | null;
|
|
16
|
+
customerName?: string | null;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
expressCheckoutAllowed?: boolean;
|
|
19
|
+
onAvailabilityChange?: (available: boolean) => void;
|
|
20
|
+
onError?: (message: string | null) => void;
|
|
21
|
+
onSuccess?: () => void;
|
|
22
|
+
onUnavailableClick?: () => Promise<void> | void;
|
|
23
|
+
options?: StripeExpressCheckoutElementOptions;
|
|
24
|
+
placeholderLabel: string;
|
|
25
|
+
renderPlaceholder: (props: WalletPlaceholderButtonProps) => ReactNode;
|
|
26
|
+
returnUrl: string;
|
|
27
|
+
session: StripeSubscriptionCheckoutSession;
|
|
28
|
+
summaryLabel: string;
|
|
29
|
+
suspended?: boolean;
|
|
30
|
+
};
|
|
31
|
+
export declare function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onError, onSuccess, onUnavailableClick, options, placeholderLabel, renderPlaceholder, returnUrl, session, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { StripeExpressCheckoutElement } from '../../components/shared/StripeExpressCheckoutElement.js';
|
|
5
|
+
export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onError, onSuccess, onUnavailableClick, options, placeholderLabel, renderPlaceholder, returnUrl, session, summaryLabel, suspended = false, }) {
|
|
6
|
+
const [walletAvailability, setWalletAvailability] = useState({
|
|
7
|
+
available: null,
|
|
8
|
+
intentKey: session.intentKey,
|
|
9
|
+
});
|
|
10
|
+
const autoWalletAttemptedIntentKeyRef = useRef(null);
|
|
11
|
+
const slotDisabled = disabled || !session.configured;
|
|
12
|
+
const walletAvailable = walletAvailability.intentKey === session.intentKey
|
|
13
|
+
? walletAvailability.available
|
|
14
|
+
: null;
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
if (slotDisabled ||
|
|
17
|
+
suspended ||
|
|
18
|
+
!expressCheckoutAllowed ||
|
|
19
|
+
walletAvailable === false ||
|
|
20
|
+
session.activeClientSecret) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const requestKey = session.intentKey;
|
|
24
|
+
if (autoWalletAttemptedIntentKeyRef.current === requestKey) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
autoWalletAttemptedIntentKeyRef.current = requestKey;
|
|
28
|
+
void session.prepareWalletCheckout();
|
|
29
|
+
}, [
|
|
30
|
+
expressCheckoutAllowed,
|
|
31
|
+
session,
|
|
32
|
+
slotDisabled,
|
|
33
|
+
suspended,
|
|
34
|
+
walletAvailable,
|
|
35
|
+
]);
|
|
36
|
+
const handleError = (message) => {
|
|
37
|
+
session.setError(message);
|
|
38
|
+
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
39
|
+
};
|
|
40
|
+
const handleAvailabilityChange = (available) => {
|
|
41
|
+
setWalletAvailability({
|
|
42
|
+
available,
|
|
43
|
+
intentKey: session.intentKey,
|
|
44
|
+
});
|
|
45
|
+
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(available);
|
|
46
|
+
};
|
|
47
|
+
const handlePlaceholderClick = async () => {
|
|
48
|
+
if (slotDisabled || session.loading.wallet) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (!expressCheckoutAllowed || walletAvailable === false) {
|
|
52
|
+
await (onUnavailableClick === null || onUnavailableClick === void 0 ? void 0 : onUnavailableClick());
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await session.prepareWalletCheckout();
|
|
56
|
+
};
|
|
57
|
+
return (_jsxs(_Fragment, { children: [walletAvailable === null
|
|
58
|
+
? renderPlaceholder({
|
|
59
|
+
className,
|
|
60
|
+
disabled: slotDisabled || session.loading.wallet,
|
|
61
|
+
label: placeholderLabel,
|
|
62
|
+
onClick: () => void handlePlaceholderClick(),
|
|
63
|
+
})
|
|
64
|
+
: null, !suspended &&
|
|
65
|
+
expressCheckoutAllowed &&
|
|
66
|
+
walletAvailable !== false &&
|
|
67
|
+
session.activeClientSecret &&
|
|
68
|
+
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange, onError: handleError, onSuccess: onSuccess, options: options, returnUrl: returnUrl, stripePromise: session.stripePromise, summaryLabel: summaryLabel })) : null] }));
|
|
69
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Stripe } from '@stripe/stripe-js';
|
|
2
|
+
import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
3
|
+
import { type CheckoutPreparationLoading } from '../paymentProvider.types.js';
|
|
4
|
+
import type { PaywallDisplayPlan } from '../../services/paywallOffer.service.js';
|
|
5
|
+
import { type PaywallPlan, type StripeRuntimeConfigOverrides } from '../../services/stripe.service.js';
|
|
6
|
+
export type StripeSubscriptionCheckoutSessionInput = {
|
|
7
|
+
checkoutMode: RuntimeMode;
|
|
8
|
+
couponId?: string | null;
|
|
9
|
+
customerEmail?: string | null;
|
|
10
|
+
displayPlan?: PaywallDisplayPlan | null;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
onError?: (message: string | null) => void;
|
|
13
|
+
plan?: PaywallPlan | null;
|
|
14
|
+
runtimeConfig?: StripeRuntimeConfigOverrides;
|
|
15
|
+
userId?: string | null;
|
|
16
|
+
};
|
|
17
|
+
export type StripeSubscriptionCheckoutSession = {
|
|
18
|
+
activeClientSecret: string | null;
|
|
19
|
+
configured: boolean;
|
|
20
|
+
error: string | null;
|
|
21
|
+
intentKey: string;
|
|
22
|
+
loading: CheckoutPreparationLoading;
|
|
23
|
+
prepareCardCheckout: () => Promise<string | null>;
|
|
24
|
+
prepareWalletCheckout: () => Promise<string | null>;
|
|
25
|
+
provider: 'stripe';
|
|
26
|
+
reset: () => void;
|
|
27
|
+
setError: (message: string | null) => void;
|
|
28
|
+
stripePromise: Promise<Stripe | null> | null;
|
|
29
|
+
};
|
|
30
|
+
export declare function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, customerEmail, displayPlan, enabled, onError, plan, runtimeConfig, userId, }: StripeSubscriptionCheckoutSessionInput): StripeSubscriptionCheckoutSession;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
3
|
+
import { createEmptyCheckoutPreparationLoading, } from '../paymentProvider.types.js';
|
|
4
|
+
import { createStripeSubscriptionCheckout, getStripePromise, isStripeConfigured, } from '../../services/stripe.service.js';
|
|
5
|
+
const getFriendlyStripeCheckoutError = (error) => {
|
|
6
|
+
const message = error instanceof Error ? error.message : 'Unable to initialize checkout';
|
|
7
|
+
return message === 'Internal server error'
|
|
8
|
+
? 'Unable to initialize checkout. Stripe settings may be missing for this funnel.'
|
|
9
|
+
: message;
|
|
10
|
+
};
|
|
11
|
+
const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
|
|
12
|
+
export function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, customerEmail, displayPlan, enabled = true, onError, plan, runtimeConfig, userId, }) {
|
|
13
|
+
var _a, _b, _c, _d;
|
|
14
|
+
const [clientSecret, setClientSecret] = useState(null);
|
|
15
|
+
const [clientSecretIntentKey, setClientSecretIntentKey] = useState(null);
|
|
16
|
+
const [error, setErrorState] = useState(null);
|
|
17
|
+
const [loading, setLoading] = useState(createEmptyCheckoutPreparationLoading);
|
|
18
|
+
const [runtimeStripePublishableKey, setRuntimeStripePublishableKey] = useState(null);
|
|
19
|
+
const creatingIntentRef = useRef(null);
|
|
20
|
+
const runtimeConfigSignature = [
|
|
21
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.apiBaseUrl),
|
|
22
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId),
|
|
23
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelVersionId),
|
|
24
|
+
normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey),
|
|
25
|
+
].join('|');
|
|
26
|
+
const configured = enabled &&
|
|
27
|
+
(isStripeConfigured(checkoutMode) ||
|
|
28
|
+
Boolean(normalizeRuntimeConfigValue(runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelSdkPublishableKey)));
|
|
29
|
+
const intentKey = [
|
|
30
|
+
'stripe',
|
|
31
|
+
checkoutMode,
|
|
32
|
+
runtimeConfigSignature,
|
|
33
|
+
(_a = plan === null || plan === void 0 ? void 0 : plan.id) !== null && _a !== void 0 ? _a : '',
|
|
34
|
+
(_b = plan === null || plan === void 0 ? void 0 : plan.amountCents) !== null && _b !== void 0 ? _b : '',
|
|
35
|
+
(_c = displayPlan === null || displayPlan === void 0 ? void 0 : displayPlan.id) !== null && _c !== void 0 ? _c : '',
|
|
36
|
+
(_d = displayPlan === null || displayPlan === void 0 ? void 0 : displayPlan.amountCents) !== null && _d !== void 0 ? _d : '',
|
|
37
|
+
(couponId === null || couponId === void 0 ? void 0 : couponId.trim()) || '',
|
|
38
|
+
].join('|');
|
|
39
|
+
const activeClientSecret = clientSecretIntentKey === intentKey ? clientSecret : null;
|
|
40
|
+
const stripePromise = useMemo(() => getStripePromise(checkoutMode, runtimeStripePublishableKey), [checkoutMode, runtimeStripePublishableKey]);
|
|
41
|
+
const setError = useCallback((message) => {
|
|
42
|
+
setErrorState(message);
|
|
43
|
+
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
44
|
+
}, [onError]);
|
|
45
|
+
const reset = useCallback(() => {
|
|
46
|
+
creatingIntentRef.current = null;
|
|
47
|
+
setClientSecret(null);
|
|
48
|
+
setClientSecretIntentKey(null);
|
|
49
|
+
setLoading(createEmptyCheckoutPreparationLoading());
|
|
50
|
+
setRuntimeStripePublishableKey(null);
|
|
51
|
+
setError(null);
|
|
52
|
+
}, [setError]);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
reset();
|
|
55
|
+
}, [intentKey, reset]);
|
|
56
|
+
const setSourceLoading = useCallback((source, value) => {
|
|
57
|
+
setLoading((current) => current[source] === value
|
|
58
|
+
? current
|
|
59
|
+
: Object.assign(Object.assign({}, current), { [source]: value }));
|
|
60
|
+
}, []);
|
|
61
|
+
const createCheckoutSubscription = useCallback(async () => {
|
|
62
|
+
var _a;
|
|
63
|
+
if (!configured || !plan || !displayPlan) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
if (activeClientSecret) {
|
|
67
|
+
return activeClientSecret;
|
|
68
|
+
}
|
|
69
|
+
if (((_a = creatingIntentRef.current) === null || _a === void 0 ? void 0 : _a.key) === intentKey) {
|
|
70
|
+
return creatingIntentRef.current.promise;
|
|
71
|
+
}
|
|
72
|
+
const requestKey = intentKey;
|
|
73
|
+
const subscriptionRequest = (async () => {
|
|
74
|
+
var _a, _b, _c, _d;
|
|
75
|
+
try {
|
|
76
|
+
const response = await createStripeSubscriptionCheckout({
|
|
77
|
+
plan,
|
|
78
|
+
couponId,
|
|
79
|
+
customerEmail,
|
|
80
|
+
user_id: userId,
|
|
81
|
+
environment: checkoutMode,
|
|
82
|
+
runtimeConfig,
|
|
83
|
+
});
|
|
84
|
+
if (((_a = creatingIntentRef.current) === null || _a === void 0 ? void 0 : _a.key) !== requestKey) {
|
|
85
|
+
return response.clientSecret;
|
|
86
|
+
}
|
|
87
|
+
const publishableKey = (_b = response.stripePublishableKey) === null || _b === void 0 ? void 0 : _b.trim();
|
|
88
|
+
if (publishableKey) {
|
|
89
|
+
setRuntimeStripePublishableKey(publishableKey);
|
|
90
|
+
}
|
|
91
|
+
setClientSecret(response.clientSecret);
|
|
92
|
+
setClientSecretIntentKey(requestKey);
|
|
93
|
+
return response.clientSecret;
|
|
94
|
+
}
|
|
95
|
+
catch (subscriptionError) {
|
|
96
|
+
if (((_c = creatingIntentRef.current) === null || _c === void 0 ? void 0 : _c.key) === requestKey) {
|
|
97
|
+
setError(getFriendlyStripeCheckoutError(subscriptionError));
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
if (((_d = creatingIntentRef.current) === null || _d === void 0 ? void 0 : _d.key) === requestKey) {
|
|
103
|
+
creatingIntentRef.current = null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})();
|
|
107
|
+
creatingIntentRef.current = {
|
|
108
|
+
key: requestKey,
|
|
109
|
+
promise: subscriptionRequest,
|
|
110
|
+
};
|
|
111
|
+
return subscriptionRequest;
|
|
112
|
+
}, [
|
|
113
|
+
activeClientSecret,
|
|
114
|
+
checkoutMode,
|
|
115
|
+
configured,
|
|
116
|
+
couponId,
|
|
117
|
+
customerEmail,
|
|
118
|
+
displayPlan,
|
|
119
|
+
intentKey,
|
|
120
|
+
plan,
|
|
121
|
+
runtimeConfig,
|
|
122
|
+
setError,
|
|
123
|
+
userId,
|
|
124
|
+
]);
|
|
125
|
+
const prepareCheckout = useCallback(async (source) => {
|
|
126
|
+
if (!configured || !plan || !displayPlan) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (activeClientSecret) {
|
|
130
|
+
return activeClientSecret;
|
|
131
|
+
}
|
|
132
|
+
setSourceLoading(source, true);
|
|
133
|
+
setError(null);
|
|
134
|
+
try {
|
|
135
|
+
return await createCheckoutSubscription();
|
|
136
|
+
}
|
|
137
|
+
finally {
|
|
138
|
+
setSourceLoading(source, false);
|
|
139
|
+
}
|
|
140
|
+
}, [
|
|
141
|
+
activeClientSecret,
|
|
142
|
+
configured,
|
|
143
|
+
createCheckoutSubscription,
|
|
144
|
+
displayPlan,
|
|
145
|
+
plan,
|
|
146
|
+
setError,
|
|
147
|
+
setSourceLoading,
|
|
148
|
+
]);
|
|
149
|
+
const prepareCardCheckout = useCallback(() => prepareCheckout('card'), [prepareCheckout]);
|
|
150
|
+
const prepareWalletCheckout = useCallback(() => prepareCheckout('wallet'), [prepareCheckout]);
|
|
151
|
+
return {
|
|
152
|
+
activeClientSecret,
|
|
153
|
+
configured,
|
|
154
|
+
error,
|
|
155
|
+
intentKey,
|
|
156
|
+
loading,
|
|
157
|
+
prepareCardCheckout,
|
|
158
|
+
prepareWalletCheckout,
|
|
159
|
+
provider: 'stripe',
|
|
160
|
+
reset,
|
|
161
|
+
setError,
|
|
162
|
+
stripePromise,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { BillingDiscountCatalog, BillingDiscountList } from '../config/billing.config.js';
|
|
2
|
+
import type { PaywallPlan } from './stripe.service.js';
|
|
3
|
+
export type PaywallDiscountCatalog = BillingDiscountCatalog;
|
|
4
|
+
export type PaywallDiscountStage = 'first' | 'second' | 'completed';
|
|
5
|
+
export type PaywallActiveDiscountStage = Exclude<PaywallDiscountStage, 'completed'>;
|
|
6
|
+
export type PaywallDiscountSnapshot = {
|
|
7
|
+
firstStartedAt?: number | null;
|
|
8
|
+
secondStartedAt?: number | null;
|
|
9
|
+
discountStartedAt?: number | null;
|
|
10
|
+
additionalDiscountStartedAt?: number | null;
|
|
11
|
+
status?: 'active' | 'expired';
|
|
12
|
+
expiresAtMs?: number | null;
|
|
13
|
+
stage?: PaywallDiscountStage;
|
|
14
|
+
offerStage?: 'initial' | 'upgraded' | 'completed';
|
|
15
|
+
};
|
|
16
|
+
export type PaywallDiscountState = {
|
|
17
|
+
status: 'active' | 'expired';
|
|
18
|
+
stage: PaywallDiscountStage;
|
|
19
|
+
couponId: string | null;
|
|
20
|
+
discountPercent: number | null;
|
|
21
|
+
previousDiscountPercent: number | null;
|
|
22
|
+
expiresAtMs: number | null;
|
|
23
|
+
remainingSeconds: number;
|
|
24
|
+
firstStartedAtMs: number | null;
|
|
25
|
+
secondStartedAtMs: number | null;
|
|
26
|
+
};
|
|
27
|
+
export type PaywallDisplayPlan = PaywallPlan & {
|
|
28
|
+
baseAmountCents: number;
|
|
29
|
+
basePriceLabel: string;
|
|
30
|
+
basePerDayAmount: string;
|
|
31
|
+
oldPerDayAmount: string | null;
|
|
32
|
+
couponId: string | null;
|
|
33
|
+
hasActiveDiscount: boolean;
|
|
34
|
+
};
|
|
35
|
+
export declare const buildBillingDiscountCatalog: (discounts: PaywallDiscountCatalog | BillingDiscountList) => PaywallDiscountCatalog;
|
|
36
|
+
export declare const resolvePaywallDiscountState: (input: {
|
|
37
|
+
discounts: PaywallDiscountCatalog;
|
|
38
|
+
nowMs: number;
|
|
39
|
+
storedValue: string | null;
|
|
40
|
+
}) => PaywallDiscountState;
|
|
41
|
+
export declare const serializePaywallDiscountState: (state: PaywallDiscountState) => string;
|
|
42
|
+
export declare const advancePaywallDiscountState: (input: {
|
|
43
|
+
discounts: PaywallDiscountCatalog;
|
|
44
|
+
state: PaywallDiscountState;
|
|
45
|
+
nowMs: number;
|
|
46
|
+
}) => PaywallDiscountState;
|
|
47
|
+
export declare const activateSecondPaywallDiscount: (input: {
|
|
48
|
+
discounts: PaywallDiscountCatalog;
|
|
49
|
+
state: PaywallDiscountState;
|
|
50
|
+
nowMs: number;
|
|
51
|
+
}) => PaywallDiscountState;
|
|
52
|
+
export declare const getDiscountedAmountCents: (amountCents: number, discountPercent: number) => number;
|
|
53
|
+
export declare const buildDiscountedPaywallPlans: (input: {
|
|
54
|
+
plans: readonly PaywallPlan[];
|
|
55
|
+
couponId: string | null;
|
|
56
|
+
discountPercent: number | null;
|
|
57
|
+
currency?: string;
|
|
58
|
+
locale?: string;
|
|
59
|
+
}) => PaywallDisplayPlan[];
|
|
60
|
+
export declare const getPaywallPromoDisplayName: (referenceMs: number) => string;
|
|
61
|
+
export declare const buildPaywallRenewalDisclaimer: (input: {
|
|
62
|
+
plan: Pick<PaywallPlan, "priceLabel" | "billingInterval" | "billingIntervalCount">;
|
|
63
|
+
hasActiveDiscount: boolean;
|
|
64
|
+
productName?: string;
|
|
65
|
+
}) => string;
|