@funnelsgrove/payments 0.1.55 → 0.1.57
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 +12 -4
- package/dist/components/shared/SharedStripeCheckoutDialog.js +7 -10
- package/dist/components/shared/SharedStripeCheckoutV2Dialog.js +5 -8
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.js +2 -2
- package/dist/components/shared/StripeExpressCheckoutButton.d.ts +4 -1
- package/dist/components/shared/StripeExpressCheckoutButton.js +5 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/providers/stripe/ApplePaySubscriptionCheckoutSlot.d.ts +5 -0
- package/dist/providers/stripe/ApplePaySubscriptionCheckoutSlot.js +5 -0
- package/dist/providers/stripe/GooglePaySubscriptionCheckoutSlot.d.ts +5 -0
- package/dist/providers/stripe/GooglePaySubscriptionCheckoutSlot.js +5 -0
- package/dist/providers/stripe/StripeSubscriptionWalletSurface.d.ts +82 -0
- package/dist/providers/stripe/StripeSubscriptionWalletSurface.js +188 -0
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.d.ts +5 -0
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.js +13 -1
- package/dist/providers/stripe/useStripeOneTimeCheckoutSession.d.ts +5 -2
- package/dist/providers/stripe/useStripeOneTimeCheckoutSession.js +24 -8
- package/dist/providers/stripe/useStripeSubscriptionCheckoutSession.d.ts +5 -2
- package/dist/providers/stripe/useStripeSubscriptionCheckoutSession.js +24 -8
- package/dist/services/checkoutCompletionAnalytics.service.d.ts +2 -0
- package/dist/services/checkoutCompletionAnalytics.service.js +18 -2
- package/dist/services/paymentMethodAnalytics.service.d.ts +2 -0
- package/dist/services/paymentMethodAnalytics.service.js +14 -0
- package/dist/services/stripe.service.d.ts +15 -1
- package/dist/services/stripe.service.js +2 -2
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -13,13 +13,17 @@ Read this file before editing checkout code. Deeper implementation docs:
|
|
|
13
13
|
|
|
14
14
|
## Current Checkout Path
|
|
15
15
|
|
|
16
|
+
Render every visible wallet placement with `StripeSubscriptionWalletSurface`. It is the normal current path for Apple Pay and Google Pay:
|
|
17
|
+
|
|
18
|
+
- Each surface owns one private Checkout Session per simultaneously visible wallet placement, isolated by its own `surfaceId`.
|
|
19
|
+
- Simultaneously visible placements may use the same plan and coupon; nothing is shared because each surface keeps its own session.
|
|
20
|
+
- When the selected plan changes, the surface updates the existing Checkout Session in place without changing `surfaceId`, React `key`, `clientSecret`, `CheckoutProvider`, or the Express Checkout iframe.
|
|
21
|
+
|
|
16
22
|
Use these for new subscription paywalls:
|
|
17
23
|
|
|
24
|
+
- `StripeSubscriptionWalletSurface`
|
|
18
25
|
- `useStripeSubscriptionCheckoutSession`
|
|
19
26
|
- `SharedStripeCheckoutV2Dialog`
|
|
20
|
-
- `ApplePaySubscriptionCheckoutSlot`
|
|
21
|
-
- `GooglePaySubscriptionCheckoutSlot`
|
|
22
|
-
- `WalletSubscriptionCheckoutSlot`
|
|
23
27
|
- `trackPaidStripeSubscriptionCheckoutCompleted`
|
|
24
28
|
|
|
25
29
|
Use these for new one-time paywall plans:
|
|
@@ -29,6 +33,10 @@ Use these for new one-time paywall plans:
|
|
|
29
33
|
|
|
30
34
|
Use `chargeStripeOneClickPayment` for post-checkout one-click upsells when the shared client helper fits the funnel. It delegates to `funnelSdkService.chargeOneClickPayment`.
|
|
31
35
|
|
|
36
|
+
## Advanced Wallet Primitives
|
|
37
|
+
|
|
38
|
+
`ApplePaySubscriptionCheckoutSlot`, `GooglePaySubscriptionCheckoutSlot`, and `WalletSubscriptionCheckoutSlot` stay exported and runtime-compatible. They are backward-compatible advanced low-level primitives: reach for them only when a placement needs wiring `StripeSubscriptionWalletSurface` does not cover. Never share one `useStripeSubscriptionCheckoutSession` result across placements; give each visible placement its own session.
|
|
39
|
+
|
|
32
40
|
## Compatibility-Only Surfaces
|
|
33
41
|
|
|
34
42
|
Keep these exports available for saved drafts, old published artifacts, and controlled migrations. Do not use them for new subscription checkout work:
|
|
@@ -70,7 +78,7 @@ When a live funnel still uses one of these, migrate the funnel deliberately with
|
|
|
70
78
|
- `services/runtimeBillingPlanCatalog.service`: test/live runtime catalog selection.
|
|
71
79
|
- `services/paywallOffer.service`: timed discount and display-plan helpers.
|
|
72
80
|
- `services/stripe.service`: Stripe loader, Checkout Session, hosted checkout, one-click, one-time Checkout Session clients, and deprecated PaymentIntent compatibility clients.
|
|
73
|
-
- `providers/*`: provider-neutral checkout state and Stripe subscription checkout slots.
|
|
81
|
+
- `providers/*`: provider-neutral checkout state, the Stripe subscription wallet surface, and Stripe subscription checkout slots.
|
|
74
82
|
- `components/shared/*`: plan selector, checkout dialogs, Express Checkout wrappers, wallet placeholders, and trust assets.
|
|
75
83
|
- `testing/walletCheckoutSmoke`: wallet checkout smoke assertions.
|
|
76
84
|
|
|
@@ -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 {
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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';
|
|
@@ -13,6 +14,7 @@ export * from './providers/stripe/useStripeOneTimeCheckoutSession.js';
|
|
|
13
14
|
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
14
15
|
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
15
16
|
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
17
|
+
export * from './providers/stripe/StripeSubscriptionWalletSurface.js';
|
|
16
18
|
export { ManageSubscriptionScreen, type ManageSubscriptionContent, type ManageSubscriptionScreenProps, } from './components/ManageSubscriptionScreen.js';
|
|
17
19
|
export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
|
|
18
20
|
export * from './components/shared/StripeExpressCheckoutElement.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';
|
|
@@ -16,6 +17,7 @@ export * from './providers/stripe/useStripeOneTimeCheckoutSession.js';
|
|
|
16
17
|
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
17
18
|
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
18
19
|
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
20
|
+
export * from './providers/stripe/StripeSubscriptionWalletSurface.js';
|
|
19
21
|
// Shared checkout and subscription-management UI.
|
|
20
22
|
export { ManageSubscriptionScreen, } from './components/ManageSubscriptionScreen.js';
|
|
21
23
|
export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
|
|
@@ -4,4 +4,9 @@ export type ApplePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionCheck
|
|
|
4
4
|
options?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
|
|
5
5
|
placeholderLabel?: string;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
|
|
9
|
+
* exported and runtime-compatible as an advanced low-level primitive; its checkout session must
|
|
10
|
+
* never be shared across placements.
|
|
11
|
+
*/
|
|
7
12
|
export declare function ApplePaySubscriptionCheckoutSlot({ options, placeholderLabel, ...slotProps }: ApplePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -36,6 +36,11 @@ const applePayExpressCheckoutOptions = {
|
|
|
36
36
|
paypal: 'never',
|
|
37
37
|
},
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
|
|
41
|
+
* exported and runtime-compatible as an advanced low-level primitive; its checkout session must
|
|
42
|
+
* never be shared across placements.
|
|
43
|
+
*/
|
|
39
44
|
export function ApplePaySubscriptionCheckoutSlot(_a) {
|
|
40
45
|
var _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
41
46
|
var { options, placeholderLabel = 'Subscribe with Apple Pay' } = _a, slotProps = __rest(_a, ["options", "placeholderLabel"]);
|
|
@@ -4,4 +4,9 @@ export type GooglePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionChec
|
|
|
4
4
|
options?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
|
|
5
5
|
placeholderLabel?: string;
|
|
6
6
|
};
|
|
7
|
+
/**
|
|
8
|
+
* @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
|
|
9
|
+
* exported and runtime-compatible as an advanced low-level primitive; its checkout session must
|
|
10
|
+
* never be shared across placements.
|
|
11
|
+
*/
|
|
7
12
|
export declare function GooglePaySubscriptionCheckoutSlot({ options, placeholderLabel, ...slotProps }: GooglePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -36,6 +36,11 @@ const googlePayExpressCheckoutOptions = {
|
|
|
36
36
|
paypal: 'never',
|
|
37
37
|
},
|
|
38
38
|
};
|
|
39
|
+
/**
|
|
40
|
+
* @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
|
|
41
|
+
* exported and runtime-compatible as an advanced low-level primitive; its checkout session must
|
|
42
|
+
* never be shared across placements.
|
|
43
|
+
*/
|
|
39
44
|
export function GooglePaySubscriptionCheckoutSlot(_a) {
|
|
40
45
|
var _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
41
46
|
var { options, placeholderLabel = 'Subscribe with Google Pay' } = _a, slotProps = __rest(_a, ["options", "placeholderLabel"]);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import type { Appearance, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
|
|
3
|
+
import { type PlatformWalletPaymentMethod } from '../../components/shared/walletPlatform.js';
|
|
4
|
+
import type { StripeSubscriptionCheckoutAnalyticsContext } from '../../services/checkoutCompletionAnalytics.service.js';
|
|
5
|
+
import { type StripeSubscriptionCheckoutSessionInput } from './useStripeSubscriptionCheckoutSession.js';
|
|
6
|
+
export type StripeSubscriptionWalletSurfaceCheckoutInput = Omit<StripeSubscriptionCheckoutSessionInput, 'onError'> & {
|
|
7
|
+
customerName?: string | null;
|
|
8
|
+
};
|
|
9
|
+
export type StripeSubscriptionWalletSurfaceAvailability = {
|
|
10
|
+
anyAvailable: boolean;
|
|
11
|
+
methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
|
|
12
|
+
resolved: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type StripeSubscriptionWalletSurfaceAvailabilityState = {
|
|
15
|
+
intentKey: string;
|
|
16
|
+
methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>;
|
|
17
|
+
};
|
|
18
|
+
export type StripeSubscriptionWalletSurfaceErrorContext = {
|
|
19
|
+
paymentMethod?: PlatformWalletPaymentMethod;
|
|
20
|
+
surfaceId: string;
|
|
21
|
+
};
|
|
22
|
+
export type StripeSubscriptionWalletSurfaceErrorEmission = {
|
|
23
|
+
context: StripeSubscriptionWalletSurfaceErrorContext;
|
|
24
|
+
errorState: StripeSubscriptionWalletSurfaceErrorState;
|
|
25
|
+
message: string | null;
|
|
26
|
+
};
|
|
27
|
+
export type StripeSubscriptionWalletSurfaceErrorState = {
|
|
28
|
+
methodMessages: Partial<Record<PlatformWalletPaymentMethod, string>>;
|
|
29
|
+
surfaceMessage: string | null;
|
|
30
|
+
};
|
|
31
|
+
export type StripeSubscriptionWalletSurfaceRenderStateInput = {
|
|
32
|
+
anyAvailable: boolean;
|
|
33
|
+
configured: boolean;
|
|
34
|
+
paymentMethodCount: number;
|
|
35
|
+
resolved: boolean;
|
|
36
|
+
suspended: boolean;
|
|
37
|
+
};
|
|
38
|
+
export declare function buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(analyticsMetadata: Record<string, unknown> | null | undefined, surfaceId: string): Record<string, unknown>;
|
|
39
|
+
export declare function buildStripeSubscriptionWalletSurfaceCheckoutAnalytics(checkoutAnalytics: StripeSubscriptionCheckoutAnalyticsContext | null | undefined, surfaceId: string): StripeSubscriptionCheckoutAnalyticsContext | null;
|
|
40
|
+
export declare function normalizeStripeSubscriptionWalletSurfaceMethods(paymentMethods: readonly PlatformWalletPaymentMethod[]): PlatformWalletPaymentMethod[];
|
|
41
|
+
export declare function getStripeSubscriptionWalletSurfaceVisibleMethods(paymentMethods: readonly PlatformWalletPaymentMethod[], methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>): PlatformWalletPaymentMethod[];
|
|
42
|
+
export declare function getStripeSubscriptionWalletSurfaceMethodAvailability(state: StripeSubscriptionWalletSurfaceAvailabilityState, intentKey: string): Partial<Record<PlatformWalletPaymentMethod, boolean>>;
|
|
43
|
+
export declare function applyStripeSubscriptionWalletSurfaceMethodAvailability(state: StripeSubscriptionWalletSurfaceAvailabilityState, intentKey: string, method: PlatformWalletPaymentMethod, available: boolean): StripeSubscriptionWalletSurfaceAvailabilityState;
|
|
44
|
+
export declare function buildStripeSubscriptionWalletSurfaceAvailability(paymentMethods: readonly PlatformWalletPaymentMethod[], methods: Partial<Record<PlatformWalletPaymentMethod, boolean>>): StripeSubscriptionWalletSurfaceAvailability;
|
|
45
|
+
export declare function shouldRenderStripeSubscriptionWalletSurface({ anyAvailable, configured, paymentMethodCount, resolved, suspended, }: StripeSubscriptionWalletSurfaceRenderStateInput): boolean;
|
|
46
|
+
export declare function resolveStripeSubscriptionWalletSurfaceError({ errorState, message, paymentMethod, surfaceId, }: {
|
|
47
|
+
errorState: StripeSubscriptionWalletSurfaceErrorState;
|
|
48
|
+
message: string | null;
|
|
49
|
+
paymentMethod?: PlatformWalletPaymentMethod;
|
|
50
|
+
surfaceId: string;
|
|
51
|
+
}): StripeSubscriptionWalletSurfaceErrorEmission | null;
|
|
52
|
+
export type StripeSubscriptionWalletSurfaceProps = {
|
|
53
|
+
amountCents: number;
|
|
54
|
+
appearance?: Appearance;
|
|
55
|
+
applePayOptions?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
|
|
56
|
+
applePayPlaceholderLabel?: string;
|
|
57
|
+
beforeConfirm?: () => Promise<string | null>;
|
|
58
|
+
checkout: StripeSubscriptionWalletSurfaceCheckoutInput;
|
|
59
|
+
checkoutAnalytics?: StripeSubscriptionCheckoutAnalyticsContext | null;
|
|
60
|
+
className?: string;
|
|
61
|
+
containerClassName?: string;
|
|
62
|
+
disabled?: boolean;
|
|
63
|
+
googlePayOptions?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
|
|
64
|
+
googlePayPlaceholderLabel?: string;
|
|
65
|
+
onAvailabilityChange?: (availability: StripeSubscriptionWalletSurfaceAvailability) => void;
|
|
66
|
+
onCancel?: () => void;
|
|
67
|
+
onError?: (message: string | null, context: StripeSubscriptionWalletSurfaceErrorContext) => void;
|
|
68
|
+
onPaymentInfoSubmitted?: () => void | Promise<void>;
|
|
69
|
+
onSuccess?: () => void | Promise<void>;
|
|
70
|
+
onUnavailableClick?: () => Promise<void> | void;
|
|
71
|
+
paymentMethods?: readonly PlatformWalletPaymentMethod[];
|
|
72
|
+
renderHeader?: (props: {
|
|
73
|
+
availability: StripeSubscriptionWalletSurfaceAvailability;
|
|
74
|
+
}) => ReactNode;
|
|
75
|
+
resolveMethodClassName?: (method: PlatformWalletPaymentMethod) => string | undefined;
|
|
76
|
+
resolveMethodWrapperClassName?: (method: PlatformWalletPaymentMethod) => string | undefined;
|
|
77
|
+
slotClassName?: string;
|
|
78
|
+
summaryLabel: string;
|
|
79
|
+
surfaceId: string;
|
|
80
|
+
suspended?: boolean;
|
|
81
|
+
};
|
|
82
|
+
export declare function StripeSubscriptionWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkout, checkoutAnalytics, className, containerClassName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, slotClassName, summaryLabel, surfaceId, suspended, }: StripeSubscriptionWalletSurfaceProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -0,0 +1,188 @@
|
|
|
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, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
15
|
+
import { usePlatformWalletPaymentMethods, } from '../../components/shared/walletPlatform.js';
|
|
16
|
+
import { ApplePaySubscriptionCheckoutSlot } from './ApplePaySubscriptionCheckoutSlot.js';
|
|
17
|
+
import { GooglePaySubscriptionCheckoutSlot } from './GooglePaySubscriptionCheckoutSlot.js';
|
|
18
|
+
import { useStripeSubscriptionCheckoutSession, } from './useStripeSubscriptionCheckoutSession.js';
|
|
19
|
+
export function buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(analyticsMetadata, surfaceId) {
|
|
20
|
+
return Object.assign(Object.assign({}, (analyticsMetadata !== null && analyticsMetadata !== void 0 ? analyticsMetadata : {})), { checkoutSurfaceId: surfaceId });
|
|
21
|
+
}
|
|
22
|
+
export function buildStripeSubscriptionWalletSurfaceCheckoutAnalytics(checkoutAnalytics, surfaceId) {
|
|
23
|
+
if (!checkoutAnalytics) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return Object.assign(Object.assign({}, checkoutAnalytics), { metadata: buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(checkoutAnalytics.metadata, surfaceId) });
|
|
27
|
+
}
|
|
28
|
+
export function normalizeStripeSubscriptionWalletSurfaceMethods(paymentMethods) {
|
|
29
|
+
return paymentMethods.filter((method, index) => paymentMethods.indexOf(method) === index);
|
|
30
|
+
}
|
|
31
|
+
export function getStripeSubscriptionWalletSurfaceVisibleMethods(paymentMethods, methods) {
|
|
32
|
+
return paymentMethods.filter((method) => methods[method] !== false);
|
|
33
|
+
}
|
|
34
|
+
export function getStripeSubscriptionWalletSurfaceMethodAvailability(state, intentKey) {
|
|
35
|
+
return state.intentKey === intentKey ? state.methods : {};
|
|
36
|
+
}
|
|
37
|
+
export function applyStripeSubscriptionWalletSurfaceMethodAvailability(state, intentKey, method, available) {
|
|
38
|
+
return {
|
|
39
|
+
intentKey,
|
|
40
|
+
methods: Object.assign(Object.assign({}, getStripeSubscriptionWalletSurfaceMethodAvailability(state, intentKey)), { [method]: available }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function buildStripeSubscriptionWalletSurfaceAvailability(paymentMethods, methods) {
|
|
44
|
+
const reportedMethods = paymentMethods.filter((method) => typeof methods[method] === 'boolean');
|
|
45
|
+
return {
|
|
46
|
+
anyAvailable: reportedMethods.some((method) => methods[method] === true),
|
|
47
|
+
methods: reportedMethods.reduce((current, method) => (Object.assign(Object.assign({}, current), { [method]: methods[method] === true })), {}),
|
|
48
|
+
resolved: reportedMethods.length === paymentMethods.length,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export function shouldRenderStripeSubscriptionWalletSurface({ anyAvailable, configured, paymentMethodCount, resolved, suspended, }) {
|
|
52
|
+
return configured && !suspended && paymentMethodCount > 0 && (!resolved || anyAvailable);
|
|
53
|
+
}
|
|
54
|
+
export function resolveStripeSubscriptionWalletSurfaceError({ errorState, message, paymentMethod, surfaceId, }) {
|
|
55
|
+
if (paymentMethod && errorState.methodMessages[paymentMethod] === message) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!paymentMethod) {
|
|
59
|
+
const methodMessages = Object.values(errorState.methodMessages);
|
|
60
|
+
if (message === null) {
|
|
61
|
+
if (errorState.surfaceMessage === null && methodMessages.length === 0) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else if (errorState.surfaceMessage === message || methodMessages.includes(message)) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const nextErrorState = message === null
|
|
70
|
+
? { methodMessages: {}, surfaceMessage: null }
|
|
71
|
+
: paymentMethod
|
|
72
|
+
? Object.assign(Object.assign({}, errorState), { methodMessages: Object.assign(Object.assign({}, errorState.methodMessages), { [paymentMethod]: message }) }) : Object.assign(Object.assign({}, errorState), { surfaceMessage: message });
|
|
73
|
+
return {
|
|
74
|
+
context: Object.assign(Object.assign({}, (paymentMethod ? { paymentMethod } : {})), { surfaceId }),
|
|
75
|
+
errorState: nextErrorState,
|
|
76
|
+
message,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
// Availability identity used to notify consumers only when the aggregate actually changed.
|
|
80
|
+
function buildAvailabilitySignature(availability) {
|
|
81
|
+
return [
|
|
82
|
+
availability.resolved ? 'resolved' : 'unresolved',
|
|
83
|
+
availability.anyAvailable ? 'available' : 'unavailable',
|
|
84
|
+
Object.entries(availability.methods)
|
|
85
|
+
.map(([method, available]) => `${method}:${available ? '1' : '0'}`)
|
|
86
|
+
.sort()
|
|
87
|
+
.join(','),
|
|
88
|
+
].join('|');
|
|
89
|
+
}
|
|
90
|
+
export function StripeSubscriptionWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkout, checkoutAnalytics, className, containerClassName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, slotClassName, summaryLabel, surfaceId, suspended = false, }) {
|
|
91
|
+
var _a;
|
|
92
|
+
const platformWalletPaymentMethods = usePlatformWalletPaymentMethods();
|
|
93
|
+
const walletPaymentMethods = normalizeStripeSubscriptionWalletSurfaceMethods(paymentMethods !== null && paymentMethods !== void 0 ? paymentMethods : platformWalletPaymentMethods);
|
|
94
|
+
const normalizedSurfaceId = surfaceId.trim();
|
|
95
|
+
const { customerName } = checkout, checkoutSessionInput = __rest(checkout, ["customerName"]);
|
|
96
|
+
const surfaceAnalyticsMetadata = useMemo(() => buildStripeSubscriptionWalletSurfaceAnalyticsMetadata(checkout.analyticsMetadata, normalizedSurfaceId), [checkout.analyticsMetadata, normalizedSurfaceId]);
|
|
97
|
+
const session = useStripeSubscriptionCheckoutSession(Object.assign(Object.assign({}, checkoutSessionInput), { analyticsMetadata: surfaceAnalyticsMetadata }));
|
|
98
|
+
const surfaceCheckoutAnalytics = useMemo(() => buildStripeSubscriptionWalletSurfaceCheckoutAnalytics(checkoutAnalytics, normalizedSurfaceId), [checkoutAnalytics, normalizedSurfaceId]);
|
|
99
|
+
const [availabilityState, setAvailabilityState] = useState({
|
|
100
|
+
intentKey: session.intentKey,
|
|
101
|
+
methods: {},
|
|
102
|
+
});
|
|
103
|
+
const availability = buildStripeSubscriptionWalletSurfaceAvailability(walletPaymentMethods, getStripeSubscriptionWalletSurfaceMethodAvailability(availabilityState, session.intentKey));
|
|
104
|
+
const availabilitySignature = buildAvailabilitySignature(availability);
|
|
105
|
+
const reportedAvailabilitySignatureRef = useRef(null);
|
|
106
|
+
const errorStateRef = useRef({
|
|
107
|
+
methodMessages: {},
|
|
108
|
+
surfaceMessage: null,
|
|
109
|
+
});
|
|
110
|
+
const emitError = useCallback((message, paymentMethod) => {
|
|
111
|
+
const emission = resolveStripeSubscriptionWalletSurfaceError({
|
|
112
|
+
errorState: errorStateRef.current,
|
|
113
|
+
message,
|
|
114
|
+
paymentMethod,
|
|
115
|
+
surfaceId: normalizedSurfaceId,
|
|
116
|
+
});
|
|
117
|
+
if (!emission) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
errorStateRef.current = emission.errorState;
|
|
121
|
+
onError === null || onError === void 0 ? void 0 : onError(emission.message, emission.context);
|
|
122
|
+
}, [normalizedSurfaceId, onError]);
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
if (reportedAvailabilitySignatureRef.current === availabilitySignature) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
reportedAvailabilitySignatureRef.current = availabilitySignature;
|
|
128
|
+
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(availability);
|
|
129
|
+
}, [availability, availabilitySignature, onAvailabilityChange]);
|
|
130
|
+
useEffect(() => {
|
|
131
|
+
emitError(session.error);
|
|
132
|
+
}, [emitError, session.error]);
|
|
133
|
+
const renderWalletMethod = (method) => {
|
|
134
|
+
var _a;
|
|
135
|
+
const slotProps = {
|
|
136
|
+
amountCents,
|
|
137
|
+
appearance,
|
|
138
|
+
beforeConfirm,
|
|
139
|
+
checkoutAnalytics: surfaceCheckoutAnalytics,
|
|
140
|
+
className: (_a = resolveMethodClassName === null || resolveMethodClassName === void 0 ? void 0 : resolveMethodClassName(method)) !== null && _a !== void 0 ? _a : className,
|
|
141
|
+
customerEmail: checkout.customerEmail,
|
|
142
|
+
customerName,
|
|
143
|
+
disabled,
|
|
144
|
+
onAvailabilityChange: (available) => setAvailabilityState((current) => applyStripeSubscriptionWalletSurfaceMethodAvailability(current, session.intentKey, method, available)),
|
|
145
|
+
onCancel,
|
|
146
|
+
onError: (message) => emitError(message, method),
|
|
147
|
+
onPaymentInfoSubmitted,
|
|
148
|
+
onSuccess,
|
|
149
|
+
onUnavailableClick,
|
|
150
|
+
returnUrl: checkout.returnUrl,
|
|
151
|
+
session,
|
|
152
|
+
slotClassName,
|
|
153
|
+
summaryLabel,
|
|
154
|
+
};
|
|
155
|
+
if (method === 'applePay') {
|
|
156
|
+
return (_jsx(ApplePaySubscriptionCheckoutSlot, Object.assign({}, slotProps, { options: applePayOptions, placeholderLabel: applePayPlaceholderLabel })));
|
|
157
|
+
}
|
|
158
|
+
if (method === 'googlePay') {
|
|
159
|
+
return (_jsx(GooglePaySubscriptionCheckoutSlot, Object.assign({}, slotProps, { options: googlePayOptions, placeholderLabel: googlePayPlaceholderLabel })));
|
|
160
|
+
}
|
|
161
|
+
return null;
|
|
162
|
+
};
|
|
163
|
+
const visibleWalletPaymentMethods = getStripeSubscriptionWalletSurfaceVisibleMethods(walletPaymentMethods, availability.methods);
|
|
164
|
+
const shouldRenderSurface = shouldRenderStripeSubscriptionWalletSurface({
|
|
165
|
+
anyAvailable: availability.anyAvailable,
|
|
166
|
+
configured: session.configured,
|
|
167
|
+
paymentMethodCount: walletPaymentMethods.length,
|
|
168
|
+
resolved: availability.resolved,
|
|
169
|
+
suspended,
|
|
170
|
+
});
|
|
171
|
+
if (!shouldRenderSurface) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
return (_jsxs("div", { className: [
|
|
175
|
+
'stripe-subscription-wallet-surface',
|
|
176
|
+
containerClassName !== null && containerClassName !== void 0 ? containerClassName : '',
|
|
177
|
+
]
|
|
178
|
+
.filter(Boolean)
|
|
179
|
+
.join(' '), "data-wallet-surface-id": normalizedSurfaceId, children: [(_a = renderHeader === null || renderHeader === void 0 ? void 0 : renderHeader({ availability })) !== null && _a !== void 0 ? _a : null, visibleWalletPaymentMethods.map((method) => {
|
|
180
|
+
var _a;
|
|
181
|
+
return (_jsx("div", { className: [
|
|
182
|
+
'stripe-subscription-wallet-surface__method',
|
|
183
|
+
(_a = resolveMethodWrapperClassName === null || resolveMethodWrapperClassName === void 0 ? void 0 : resolveMethodWrapperClassName(method)) !== null && _a !== void 0 ? _a : '',
|
|
184
|
+
]
|
|
185
|
+
.filter(Boolean)
|
|
186
|
+
.join(' '), "data-wallet-payment-method": method, children: renderWalletMethod(method) }, method));
|
|
187
|
+
})] }));
|
|
188
|
+
}
|
|
@@ -50,5 +50,10 @@ export type WalletSubscriptionCheckoutSlotProps = {
|
|
|
50
50
|
suspended?: boolean;
|
|
51
51
|
};
|
|
52
52
|
export declare function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label, }: Pick<WalletPlaceholderButtonProps, 'className' | 'label'>): import("react/jsx-runtime").JSX.Element;
|
|
53
|
+
/**
|
|
54
|
+
* @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
|
|
55
|
+
* exported and runtime-compatible as an advanced low-level primitive; its checkout session must
|
|
56
|
+
* never be shared across placements.
|
|
57
|
+
*/
|
|
53
58
|
export declare function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availabilityPaymentMethod, beforeConfirm, checkoutAnalytics, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, slotClassName, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
54
59
|
export {};
|
|
@@ -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 &&
|
|
@@ -28,6 +29,11 @@ export function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label,
|
|
|
28
29
|
.filter(Boolean)
|
|
29
30
|
.join(' '), "aria-label": label, role: 'status', children: [_jsx("span", { className: 'wallet-subscription-checkout-slot__loading-bar' }), _jsx("style", { children: walletSubscriptionCheckoutLoadingPlaceholderStyles })] }));
|
|
30
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* @deprecated Prefer StripeSubscriptionWalletSurface for visible wallet placements. This slot stays
|
|
34
|
+
* exported and runtime-compatible as an advanced low-level primitive; its checkout session must
|
|
35
|
+
* never be shared across placements.
|
|
36
|
+
*/
|
|
31
37
|
export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availabilityPaymentMethod, beforeConfirm, checkoutAnalytics, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, slotClassName, summaryLabel, suspended = false, }) {
|
|
32
38
|
const [walletAvailability, setWalletAvailability] = useState({
|
|
33
39
|
available: null,
|
|
@@ -37,6 +43,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
37
43
|
const autoWalletPreparationIntentKeyRef = useRef(null);
|
|
38
44
|
const walletCheckoutStartedAfterClickSessionIdRef = useRef(null);
|
|
39
45
|
const slotDisabled = disabled || !session.configured;
|
|
46
|
+
const analyticsPaymentMethod = toAnalyticsPaymentMethod(availabilityPaymentMethod);
|
|
40
47
|
const walletAvailable = walletAvailability.intentKey === session.intentKey
|
|
41
48
|
? walletAvailability.available
|
|
42
49
|
: null;
|
|
@@ -84,6 +91,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
84
91
|
void session
|
|
85
92
|
.prepareWalletCheckout({
|
|
86
93
|
checkoutStartSource: 'wallet_render',
|
|
94
|
+
paymentMethod: analyticsPaymentMethod,
|
|
87
95
|
})
|
|
88
96
|
.then((readyClientSecret) => {
|
|
89
97
|
if (!readyClientSecret) {
|
|
@@ -91,6 +99,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
91
99
|
}
|
|
92
100
|
});
|
|
93
101
|
}, [
|
|
102
|
+
analyticsPaymentMethod,
|
|
94
103
|
expressCheckoutAllowed,
|
|
95
104
|
reportWalletPreparationFailure,
|
|
96
105
|
session.activeClientSecret,
|
|
@@ -112,8 +121,9 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
112
121
|
return;
|
|
113
122
|
}
|
|
114
123
|
walletCheckoutStartedAfterClickSessionIdRef.current = session.checkoutSessionId;
|
|
115
|
-
void trackStripeSubscriptionCheckoutStarted(Object.assign({ checkoutSessionId: session.checkoutSessionId }
|
|
124
|
+
void trackStripeSubscriptionCheckoutStarted(Object.assign(Object.assign({}, checkoutAnalytics), { checkoutSessionId: session.checkoutSessionId, paymentMethod: analyticsPaymentMethod }));
|
|
116
125
|
}, [
|
|
126
|
+
analyticsPaymentMethod,
|
|
117
127
|
checkoutAnalytics,
|
|
118
128
|
session.checkoutSessionId,
|
|
119
129
|
session.intentKey,
|
|
@@ -127,6 +137,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
127
137
|
});
|
|
128
138
|
void session.restartWalletCheckout({
|
|
129
139
|
checkoutStartSource: 'wallet_render',
|
|
140
|
+
paymentMethod: analyticsPaymentMethod,
|
|
130
141
|
});
|
|
131
142
|
return;
|
|
132
143
|
}
|
|
@@ -147,6 +158,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
147
158
|
setWalletCheckoutStartedAfterClickIntentKey(session.intentKey);
|
|
148
159
|
const readyClientSecret = await session.prepareWalletCheckout({
|
|
149
160
|
checkoutStartSource: 'wallet_render',
|
|
161
|
+
paymentMethod: analyticsPaymentMethod,
|
|
150
162
|
});
|
|
151
163
|
if (!readyClientSecret) {
|
|
152
164
|
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
|
|
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
|
-
|
|
18
|
-
|
|
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:
|
|
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(
|
|
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
|
|
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
|
-
|
|
17
|
-
|
|
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:
|
|
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(
|
|
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
|
|
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,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
|
+
}
|
|
@@ -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(
|
|
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(
|
|
436
|
-
const resolvedPublishableKey = (runtimePublishableKey
|
|
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.
|
|
3
|
+
"version": "0.1.57",
|
|
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",
|