@funnelsgrove/payments 0.1.37 → 0.1.39
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 +7 -1
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.d.ts +2 -1
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.js +3 -2
- package/dist/components/shared/StripeExpressCheckoutButton.d.ts +2 -1
- package/dist/components/shared/StripeExpressCheckoutButton.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.d.ts +2 -1
- package/dist/providers/stripe/WalletSubscriptionCheckoutSlot.js +2 -2
- package/dist/services/checkoutCompletionAnalytics.service.d.ts +12 -0
- package/dist/services/checkoutCompletionAnalytics.service.js +74 -0
- package/dist/services/stripe.service.d.ts +14 -0
- package/dist/services/stripe.service.js +25 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Shared billing and checkout helpers for funnels.
|
|
4
4
|
|
|
5
|
+
Detailed implementation docs:
|
|
6
|
+
|
|
7
|
+
- [Shared Payments](../../docs/funnel-sdk/shared-payments.md)
|
|
8
|
+
- [Funnel API payment endpoints](../../docs/funnel-sdk/funnel-api.md#checkout-endpoints)
|
|
9
|
+
|
|
5
10
|
## Build And Publish
|
|
6
11
|
|
|
7
12
|
- Build distributable output with `npm run build --workspace @funnelsgrove/payments`.
|
|
@@ -42,6 +47,7 @@ Shared billing and checkout helpers for funnels.
|
|
|
42
47
|
- `src/providers/stripe/useStripeSubscriptionCheckoutSession.ts` for Stripe SDK initialization, publishable-key resolution, subscription client-secret preparation, card/wallet loading state, and friendly checkout errors
|
|
43
48
|
- `src/providers/stripe/ApplePaySubscriptionCheckoutSlot.tsx` and `src/providers/stripe/GooglePaySubscriptionCheckoutSlot.tsx` for wallet shells that prepare Stripe Express Checkout on load, keep the loading placeholder clickable, and let Stripe's real wallet controls sit above the placeholder as soon as they mount
|
|
44
49
|
- Stripe subscription checkout sessions reuse the active client secret across card checkout and paywall wallet checkout for the same intent key, so opening the embedded checkout after a paywall wallet button is ready does not create a second subscription session.
|
|
50
|
+
- `trackPaidStripeSubscriptionCheckoutCompleted` verifies a Stripe custom Checkout Session is paid before delegating to `@funnelsgrove/analytics` for the canonical `checkout_completed` purchase event.
|
|
45
51
|
- Stripe Express Checkout elements must stay mounted and measurable until Stripe reports availability; only hide the container after `onReady` or `onLoadError` reports that the requested wallet method is unavailable.
|
|
46
52
|
- Shared checkout dialogs must switch to card checkout when Stripe reports no requested wallet availability, so an unavailable Apple Pay or Google Pay shell never leaves a fake wallet tab blocking the card form.
|
|
47
53
|
- Wallet slots should not leave disabled grey placeholder shells blocking the paywall. While availability is unknown, the placeholder can sit behind Stripe's real wallet element; after Stripe reports unavailable or preparation cannot create a client secret, the placeholder must disappear instead of opening a card checkout under wallet branding.
|
|
@@ -57,7 +63,7 @@ Shared billing and checkout helpers for funnels.
|
|
|
57
63
|
## What Does Not Belong Here
|
|
58
64
|
|
|
59
65
|
- funnel routing
|
|
60
|
-
- funnel analytics
|
|
66
|
+
- funnel-owned analytics event definitions outside the shared checkout completion handoff
|
|
61
67
|
- funnel-owned step layout outside the shared checkout shell
|
|
62
68
|
- funnel-owned billing catalog entries
|
|
63
69
|
- funnel-specific decisions about when to activate or upgrade an offer
|
|
@@ -15,7 +15,8 @@ export type StripeCheckoutExpressCheckoutButtonProps = {
|
|
|
15
15
|
serverUpdateKey?: string | null;
|
|
16
16
|
onServerUpdate?: () => Promise<boolean>;
|
|
17
17
|
onAvailabilityChange?: (available: boolean) => void;
|
|
18
|
+
onCancel?: () => void;
|
|
18
19
|
onError?: (message: string | null) => void;
|
|
19
20
|
onSuccess?: () => void | Promise<void>;
|
|
20
21
|
};
|
|
21
|
-
export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, confirmEmail, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, serverUpdateKey, onServerUpdate, onAvailabilityChange, onError, onSuccess, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, confirmEmail, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, serverUpdateKey, onServerUpdate, onAvailabilityChange, onCancel, onError, onSuccess, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { ExpressCheckoutElement, useCheckout, } from '@stripe/react-stripe-js/checkout';
|
|
5
|
+
const ExpressCheckoutElementWithCancel = ExpressCheckoutElement;
|
|
5
6
|
const buildDefaultOptions = () => ({
|
|
6
7
|
buttonHeight: 55,
|
|
7
8
|
buttonTheme: {
|
|
@@ -25,7 +26,7 @@ const buildDefaultOptions = () => ({
|
|
|
25
26
|
paypal: 'never',
|
|
26
27
|
},
|
|
27
28
|
});
|
|
28
|
-
export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, confirmEmail = true, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, serverUpdateKey, onServerUpdate, onAvailabilityChange, onError, onSuccess, }) {
|
|
29
|
+
export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, confirmEmail = true, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, serverUpdateKey, onServerUpdate, onAvailabilityChange, onCancel, onError, onSuccess, }) {
|
|
29
30
|
const checkoutState = useCheckout();
|
|
30
31
|
const appliedServerUpdateKeyRef = useRef('');
|
|
31
32
|
const serverUpdatePromiseRef = useRef(null);
|
|
@@ -143,7 +144,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl,
|
|
|
143
144
|
effectiveWalletAvailable === false ? 'is-hidden' : '',
|
|
144
145
|
]
|
|
145
146
|
.filter(Boolean)
|
|
146
|
-
.join(' '), children: _jsx(
|
|
147
|
+
.join(' '), children: _jsx(ExpressCheckoutElementWithCancel, { options: resolvedOptions, onCancel: onCancel, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
147
148
|
setWalletAvailable(false);
|
|
148
149
|
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(false);
|
|
149
150
|
onError === null || onError === void 0 ? void 0 : onError(error.message || 'Unable to load wallet checkout.');
|
|
@@ -12,7 +12,8 @@ export type StripeExpressCheckoutButtonProps = {
|
|
|
12
12
|
initialAvailable?: boolean | null;
|
|
13
13
|
keepInitialAvailableOnReady?: boolean;
|
|
14
14
|
onAvailabilityChange?: (available: boolean) => void;
|
|
15
|
+
onCancel?: () => void;
|
|
15
16
|
onError?: (message: string | null) => void;
|
|
16
17
|
onSuccess?: () => void | Promise<void>;
|
|
17
18
|
};
|
|
18
|
-
export declare function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, onAvailabilityChange, onError, onSuccess, }: StripeExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
export declare function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, onAvailabilityChange, onCancel, onError, onSuccess, }: StripeExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -53,7 +53,7 @@ const buildDefaultOptions = (input) => {
|
|
|
53
53
|
const isSuccessfulIntentStatus = (status) => {
|
|
54
54
|
return status === 'succeeded' || status === 'processing' || status === 'requires_capture';
|
|
55
55
|
};
|
|
56
|
-
export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, onAvailabilityChange, onError, onSuccess, }) {
|
|
56
|
+
export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summaryLabel, returnUrl, customerEmail, customerName, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, onAvailabilityChange, onCancel, onError, onSuccess, }) {
|
|
57
57
|
const stripe = useStripe();
|
|
58
58
|
const elements = useElements();
|
|
59
59
|
const [walletAvailable, setWalletAvailable] = useState(initialAvailable !== null && initialAvailable !== void 0 ? initialAvailable : null);
|
|
@@ -139,7 +139,7 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, summar
|
|
|
139
139
|
effectiveWalletAvailable === false ? 'is-hidden' : '',
|
|
140
140
|
]
|
|
141
141
|
.filter(Boolean)
|
|
142
|
-
.join(' '), children: _jsx(ExpressCheckoutElement, { options: resolvedOptions, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
142
|
+
.join(' '), children: _jsx(ExpressCheckoutElement, { options: resolvedOptions, onCancel: onCancel, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
|
|
143
143
|
setWalletAvailable(false);
|
|
144
144
|
onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(false);
|
|
145
145
|
onError === null || onError === void 0 ? void 0 : onError(error.message || 'Unable to load wallet checkout.');
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './services/planCatalog.service.js';
|
|
|
5
5
|
export * from './services/runtimeBillingPlanCatalog.service.js';
|
|
6
6
|
export * from './services/paywallOffer.service.js';
|
|
7
7
|
export * from './services/stripe.service.js';
|
|
8
|
+
export * from './services/checkoutCompletionAnalytics.service.js';
|
|
8
9
|
export * from './hooks/useResolvedPaywallPlans.js';
|
|
9
10
|
export * from './providers/paymentProvider.types.js';
|
|
10
11
|
export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ export * from './services/planCatalog.service.js';
|
|
|
5
5
|
export * from './services/runtimeBillingPlanCatalog.service.js';
|
|
6
6
|
export * from './services/paywallOffer.service.js';
|
|
7
7
|
export * from './services/stripe.service.js';
|
|
8
|
+
export * from './services/checkoutCompletionAnalytics.service.js';
|
|
8
9
|
export * from './hooks/useResolvedPaywallPlans.js';
|
|
9
10
|
export * from './providers/paymentProvider.types.js';
|
|
10
11
|
export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
@@ -31,6 +31,7 @@ export type WalletSubscriptionCheckoutSlotProps = {
|
|
|
31
31
|
disabled?: boolean;
|
|
32
32
|
expressCheckoutAllowed?: boolean;
|
|
33
33
|
onAvailabilityChange?: (available: boolean) => void;
|
|
34
|
+
onCancel?: () => void;
|
|
34
35
|
onError?: (message: string | null) => void;
|
|
35
36
|
onSuccess?: () => void | Promise<void>;
|
|
36
37
|
onUnavailableClick?: () => Promise<void> | void;
|
|
@@ -44,5 +45,5 @@ export type WalletSubscriptionCheckoutSlotProps = {
|
|
|
44
45
|
suspended?: boolean;
|
|
45
46
|
};
|
|
46
47
|
export declare function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label, }: Pick<WalletPlaceholderButtonProps, 'className' | 'label'>): import("react/jsx-runtime").JSX.Element;
|
|
47
|
-
export declare function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onError, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
48
|
+
export declare function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled, expressCheckoutAllowed, onAvailabilityChange, onCancel, onError, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended, }: WalletSubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
|
|
48
49
|
export {};
|
|
@@ -27,7 +27,7 @@ export function WalletSubscriptionCheckoutLoadingPlaceholder({ className, label,
|
|
|
27
27
|
.filter(Boolean)
|
|
28
28
|
.join(' '), "aria-label": label, role: 'status', children: [_jsx("span", { className: 'wallet-subscription-checkout-slot__loading-bar' }), _jsx("style", { children: walletSubscriptionCheckoutLoadingPlaceholderStyles })] }));
|
|
29
29
|
}
|
|
30
|
-
export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onError, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended = false, }) {
|
|
30
|
+
export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymentMethod, beforeConfirm, className, customerEmail, customerName, disabled = false, expressCheckoutAllowed = true, onAvailabilityChange, onCancel, onError, onSuccess, onUnavailableClick, options, placeholderLabel, renderPreparingPlaceholder, renderPlaceholder, returnUrl, session, summaryLabel, suspended = false, }) {
|
|
31
31
|
const [walletAvailability, setWalletAvailability] = useState({
|
|
32
32
|
available: null,
|
|
33
33
|
intentKey: session.intentKey,
|
|
@@ -131,7 +131,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, availabilityPaymen
|
|
|
131
131
|
.filter(Boolean)
|
|
132
132
|
.join(' '), "aria-busy": placeholderIsPreparing || undefined, children: [shouldRenderExpressCheckout &&
|
|
133
133
|
session.activeClientSecret &&
|
|
134
|
-
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange, onError: handleError, onSuccess: onSuccess, options: options, returnUrl: returnUrl, serverUpdateKey: serverUpdateKey, stripePromise: session.stripePromise, summaryLabel: summaryLabel, onServerUpdate: session.updateCheckoutSessionPlan })) : null, shouldRenderPlaceholder ? (_jsx("div", { className: 'wallet-subscription-checkout-slot__placeholder', children: placeholderRenderer({
|
|
134
|
+
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange, onCancel: onCancel, onError: handleError, onSuccess: onSuccess, options: options, returnUrl: returnUrl, serverUpdateKey: serverUpdateKey, stripePromise: session.stripePromise, summaryLabel: summaryLabel, onServerUpdate: session.updateCheckoutSessionPlan })) : null, shouldRenderPlaceholder ? (_jsx("div", { className: 'wallet-subscription-checkout-slot__placeholder', children: placeholderRenderer({
|
|
135
135
|
className,
|
|
136
136
|
disabled: slotDisabled,
|
|
137
137
|
label: placeholderLabel,
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
2
|
+
import { type StripeRuntimeConfigOverrides } from './stripe.service.js';
|
|
3
|
+
export type TrackPaidStripeSubscriptionCheckoutCompletedInput = {
|
|
4
|
+
checkoutSessionId?: string | null;
|
|
5
|
+
environment?: RuntimeMode;
|
|
6
|
+
metadata?: Record<string, unknown> | null;
|
|
7
|
+
runtimeConfig?: StripeRuntimeConfigOverrides;
|
|
8
|
+
stepId?: string;
|
|
9
|
+
stepName?: string;
|
|
10
|
+
};
|
|
11
|
+
export type TrackPaidStripeSubscriptionCheckoutCompletedResult = 'already_tracked' | 'missing_checkout_session' | 'tracked' | 'unpaid';
|
|
12
|
+
export declare function trackPaidStripeSubscriptionCheckoutCompleted(input: TrackPaidStripeSubscriptionCheckoutCompletedInput): Promise<TrackPaidStripeSubscriptionCheckoutCompletedResult>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { publicAnalyticsSdk, } from '@funnelsgrove/analytics';
|
|
2
|
+
import { verifyStripeSubscriptionCheckoutPayment, } from './stripe.service.js';
|
|
3
|
+
const asRecord = (value) => {
|
|
4
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: {};
|
|
7
|
+
};
|
|
8
|
+
const activeCheckoutCompletionKeys = new Set();
|
|
9
|
+
const buildCheckoutCompletedStorageKey = (checkoutSessionId, runtimeConfig) => {
|
|
10
|
+
var _a;
|
|
11
|
+
const funnelId = ((_a = runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelId) === null || _a === void 0 ? void 0 : _a.trim()) || 'unknown_funnel';
|
|
12
|
+
return `funnelsgrove:checkout_completed:${funnelId}:${checkoutSessionId}`;
|
|
13
|
+
};
|
|
14
|
+
const hasTrackedCheckoutSession = (checkoutSessionId, runtimeConfig) => {
|
|
15
|
+
var _a;
|
|
16
|
+
if (typeof window === 'undefined') {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
return Boolean((_a = window.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem(buildCheckoutCompletedStorageKey(checkoutSessionId, runtimeConfig)));
|
|
21
|
+
}
|
|
22
|
+
catch (_b) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const markCheckoutSessionTracked = (checkoutSessionId, runtimeConfig) => {
|
|
27
|
+
var _a;
|
|
28
|
+
if (typeof window === 'undefined') {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
(_a = window.sessionStorage) === null || _a === void 0 ? void 0 : _a.setItem(buildCheckoutCompletedStorageKey(checkoutSessionId, runtimeConfig), '1');
|
|
33
|
+
}
|
|
34
|
+
catch (_b) {
|
|
35
|
+
// A stable checkout-session event id still protects provider-side dedupe.
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export async function trackPaidStripeSubscriptionCheckoutCompleted(input) {
|
|
39
|
+
var _a;
|
|
40
|
+
const checkoutSessionId = ((_a = input.checkoutSessionId) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
41
|
+
if (!checkoutSessionId) {
|
|
42
|
+
return 'missing_checkout_session';
|
|
43
|
+
}
|
|
44
|
+
const completionKey = buildCheckoutCompletedStorageKey(checkoutSessionId, input.runtimeConfig);
|
|
45
|
+
if (activeCheckoutCompletionKeys.has(completionKey) ||
|
|
46
|
+
hasTrackedCheckoutSession(checkoutSessionId, input.runtimeConfig)) {
|
|
47
|
+
return 'already_tracked';
|
|
48
|
+
}
|
|
49
|
+
activeCheckoutCompletionKeys.add(completionKey);
|
|
50
|
+
try {
|
|
51
|
+
const checkoutStatus = await verifyStripeSubscriptionCheckoutPayment({
|
|
52
|
+
checkoutSessionId,
|
|
53
|
+
environment: input.environment,
|
|
54
|
+
runtimeConfig: input.runtimeConfig,
|
|
55
|
+
});
|
|
56
|
+
if (!checkoutStatus.paid) {
|
|
57
|
+
return 'unpaid';
|
|
58
|
+
}
|
|
59
|
+
const analyticsMetadata = asRecord(checkoutStatus.analyticsMetadata);
|
|
60
|
+
const conversionInput = {
|
|
61
|
+
eventId: checkoutSessionId,
|
|
62
|
+
stepId: input.stepId,
|
|
63
|
+
stepName: input.stepName,
|
|
64
|
+
metadata: Object.assign(Object.assign(Object.assign({}, asRecord(input.metadata)), analyticsMetadata), { checkoutSessionId, checkout_session_id: checkoutSessionId, checkout_session_status: checkoutStatus.status || undefined, payment_status: checkoutStatus.paymentStatus || undefined, environment: checkoutStatus.environment || input.environment || analyticsMetadata.environment }),
|
|
65
|
+
};
|
|
66
|
+
publicAnalyticsSdk.trackCheckoutCompleted(conversionInput);
|
|
67
|
+
markCheckoutSessionTracked(checkoutSessionId, input.runtimeConfig);
|
|
68
|
+
await publicAnalyticsSdk.flush().catch(() => 0);
|
|
69
|
+
return 'tracked';
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
activeCheckoutCompletionKeys.delete(completionKey);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -119,8 +119,22 @@ type UpdateSubscriptionCheckoutPlanResponse = {
|
|
|
119
119
|
checkoutSessionId: string;
|
|
120
120
|
environment?: 'test' | 'live';
|
|
121
121
|
};
|
|
122
|
+
export type VerifySubscriptionCheckoutPaymentInput = {
|
|
123
|
+
checkoutSessionId: string;
|
|
124
|
+
environment?: RuntimeMode;
|
|
125
|
+
runtimeConfig?: StripeRuntimeConfigOverrides;
|
|
126
|
+
};
|
|
127
|
+
export type VerifySubscriptionCheckoutPaymentResponse = {
|
|
128
|
+
checkoutSessionId: string;
|
|
129
|
+
paid: boolean;
|
|
130
|
+
paymentStatus?: string | null;
|
|
131
|
+
status?: string | null;
|
|
132
|
+
environment?: 'test' | 'live';
|
|
133
|
+
analyticsMetadata?: Record<string, unknown>;
|
|
134
|
+
};
|
|
122
135
|
export declare function createStripeSubscriptionCheckout(input: CreateSubscriptionCheckoutInput): Promise<CreateSubscriptionCheckoutResponse>;
|
|
123
136
|
export declare function updateStripeSubscriptionCheckoutPlan(input: UpdateSubscriptionCheckoutPlanInput): Promise<UpdateSubscriptionCheckoutPlanResponse>;
|
|
137
|
+
export declare function verifyStripeSubscriptionCheckoutPayment(input: VerifySubscriptionCheckoutPaymentInput): Promise<VerifySubscriptionCheckoutPaymentResponse>;
|
|
124
138
|
type CreateCheckoutSessionInput = {
|
|
125
139
|
plan: CheckoutSessionPlanInput;
|
|
126
140
|
successUrl: string;
|
|
@@ -714,6 +714,31 @@ export async function updateStripeSubscriptionCheckoutPlan(input) {
|
|
|
714
714
|
}
|
|
715
715
|
return (await response.json());
|
|
716
716
|
}
|
|
717
|
+
export async function verifyStripeSubscriptionCheckoutPayment(input) {
|
|
718
|
+
const checkoutSessionId = input.checkoutSessionId.trim();
|
|
719
|
+
if (!checkoutSessionId) {
|
|
720
|
+
throw new Error('checkoutSessionId is required');
|
|
721
|
+
}
|
|
722
|
+
const runtimeConfig = resolveStripeRuntimeConfig(input.runtimeConfig);
|
|
723
|
+
const response = await fetch(buildStripeRuntimeApiUrl('/sdk/public/payments/subscriptions/checkout-session-status', runtimeConfig), {
|
|
724
|
+
method: 'POST',
|
|
725
|
+
headers: buildStripeRuntimeHeaders(runtimeConfig, {
|
|
726
|
+
'Content-Type': 'application/json',
|
|
727
|
+
}),
|
|
728
|
+
body: JSON.stringify({
|
|
729
|
+
publishableKey: runtimeConfig.funnelSdkPublishableKey || undefined,
|
|
730
|
+
funnelId: runtimeConfig.funnelId || undefined,
|
|
731
|
+
funnelVersionId: runtimeConfig.funnelVersionId || undefined,
|
|
732
|
+
checkoutSessionId,
|
|
733
|
+
environment: input.environment || undefined,
|
|
734
|
+
}),
|
|
735
|
+
});
|
|
736
|
+
if (!response.ok) {
|
|
737
|
+
const errorMessage = await parseErrorMessage(response, 'Unable to verify subscription checkout');
|
|
738
|
+
throw new Error(errorMessage);
|
|
739
|
+
}
|
|
740
|
+
return (await response.json());
|
|
741
|
+
}
|
|
717
742
|
export async function createStripeCheckoutSession(input) {
|
|
718
743
|
var _a, _b, _c, _d, _e;
|
|
719
744
|
const recurring = resolveCheckoutPlanRecurringConfig(input.plan);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnelsgrove/payments",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.39",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"test:run": "vitest run --passWithNoTests"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
+
"@funnelsgrove/analytics": "^0.1.13",
|
|
30
31
|
"@funnelsgrove/runtime": "^0.1.18",
|
|
31
32
|
"@stripe/react-stripe-js": "^5.6.0",
|
|
32
33
|
"@stripe/stripe-js": "^8.7.0",
|