@funnelsgrove/payments 0.13.0 → 0.15.0
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/dist/providers/stripe/components/SharedStripeCheckoutV2Dialog.js +12 -6
- package/dist/providers/stripe/components/StripeOneTimeWalletSurface.d.ts +1 -1
- package/dist/providers/stripe/components/StripeOneTimeWalletSurface.js +3 -3
- package/dist/providers/stripe/hooks/useStripeOneTimeCheckoutSession.d.ts +3 -1
- package/dist/providers/stripe/hooks/useStripeOneTimeCheckoutSession.js +25 -9
- package/dist/providers/stripe/hooks/useStripeSubscriptionCheckoutSession.js +9 -8
- package/dist/providers/stripe/services/stripeCheckoutFailure.d.ts +6 -0
- package/dist/providers/stripe/services/stripeCheckoutFailure.js +22 -0
- package/dist/services/checkoutObservability.service.js +8 -3
- package/package.json +1 -1
|
@@ -11,7 +11,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
11
11
|
return t;
|
|
12
12
|
};
|
|
13
13
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
14
|
-
import { useCallback, useEffect, useMemo, useState, } from 'react';
|
|
14
|
+
import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
|
|
15
15
|
import { CheckoutProvider, PaymentElement, useCheckout, } from '@stripe/react-stripe-js/checkout';
|
|
16
16
|
import { PaymentBrandMark, SecurityLockIcon } from './CheckoutBrandAssets.js';
|
|
17
17
|
import { StripeCheckoutExpressCheckoutButton } from './StripeCheckoutExpressCheckoutButton.js';
|
|
@@ -70,6 +70,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
|
|
|
70
70
|
? true
|
|
71
71
|
: walletAvailable;
|
|
72
72
|
const [submitting, setSubmitting] = useState(false);
|
|
73
|
+
const submitInFlightRef = useRef(false);
|
|
73
74
|
const [paymentElementReady, setPaymentElementReady] = useState(false);
|
|
74
75
|
const [error, setError] = useState(null);
|
|
75
76
|
const [billingCountry, setBillingCountry] = useState(null);
|
|
@@ -250,7 +251,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
|
|
|
250
251
|
};
|
|
251
252
|
const handleSubmit = async (event) => {
|
|
252
253
|
event.preventDefault();
|
|
253
|
-
if (
|
|
254
|
+
if (submitInFlightRef.current) {
|
|
254
255
|
return;
|
|
255
256
|
}
|
|
256
257
|
const billingCountryError = getBillingCountryError(billingCountry !== null && billingCountry !== void 0 ? billingCountry : fixedBillingCountry);
|
|
@@ -268,12 +269,17 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
|
|
|
268
269
|
setSharedError('Payment form is not ready yet.', true);
|
|
269
270
|
return;
|
|
270
271
|
}
|
|
272
|
+
submitInFlightRef.current = true;
|
|
271
273
|
setSubmitting(true);
|
|
272
274
|
setSharedError(null);
|
|
275
|
+
const finishSubmitting = () => {
|
|
276
|
+
submitInFlightRef.current = false;
|
|
277
|
+
setSubmitting(false);
|
|
278
|
+
};
|
|
273
279
|
try {
|
|
274
280
|
const syncedEmail = await ensureCustomerEmail();
|
|
275
281
|
if (!syncedEmail) {
|
|
276
|
-
|
|
282
|
+
finishSubmitting();
|
|
277
283
|
return;
|
|
278
284
|
}
|
|
279
285
|
await trackPaymentInfoSubmitted();
|
|
@@ -283,7 +289,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
|
|
|
283
289
|
if (result.type === 'error') {
|
|
284
290
|
const message = result.error.message || 'Payment failed.';
|
|
285
291
|
setSharedError(message, false, result.error);
|
|
286
|
-
|
|
292
|
+
finishSubmitting();
|
|
287
293
|
return;
|
|
288
294
|
}
|
|
289
295
|
if (typeof window !== 'undefined') {
|
|
@@ -291,11 +297,11 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
|
|
|
291
297
|
window.location.assign(returnUrl);
|
|
292
298
|
return;
|
|
293
299
|
}
|
|
294
|
-
|
|
300
|
+
finishSubmitting();
|
|
295
301
|
}
|
|
296
302
|
catch (submitError) {
|
|
297
303
|
setSharedError(getFriendlyCardErrorMessage(submitError));
|
|
298
|
-
|
|
304
|
+
finishSubmitting();
|
|
299
305
|
}
|
|
300
306
|
};
|
|
301
307
|
const visibleError = error;
|
|
@@ -12,4 +12,4 @@ export type StripeOneTimeWalletSurfaceProps = Omit<StripeWalletSurfaceProps, 'ap
|
|
|
12
12
|
googlePayOptions?: StripeWalletSurfaceProps['googlePayOptions'];
|
|
13
13
|
googlePayPlaceholderLabel?: string;
|
|
14
14
|
};
|
|
15
|
-
export declare function StripeOneTimeWalletSurface({ applePayOptions, applePayPlaceholderLabel, checkout, googlePayOptions, googlePayPlaceholderLabel, surfaceId, ...surfaceProps }: StripeOneTimeWalletSurfaceProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare function StripeOneTimeWalletSurface({ applePayOptions, applePayPlaceholderLabel, checkout, checkoutAnalytics, googlePayOptions, googlePayPlaceholderLabel, surfaceId, ...surfaceProps }: StripeOneTimeWalletSurfaceProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -16,13 +16,13 @@ import { useStripeOneTimeCheckoutSession, } from '../hooks/useStripeOneTimeCheck
|
|
|
16
16
|
import { StripeWalletSurface, buildStripeWalletSurfaceAnalyticsMetadata, } from './StripeWalletSurface.internal.js';
|
|
17
17
|
export function StripeOneTimeWalletSurface(_a) {
|
|
18
18
|
var _b, _c;
|
|
19
|
-
var { applePayOptions, applePayPlaceholderLabel = 'Buy with Apple Pay', checkout, googlePayOptions, googlePayPlaceholderLabel = 'Buy with Google Pay', surfaceId } = _a, surfaceProps = __rest(_a, ["applePayOptions", "applePayPlaceholderLabel", "checkout", "googlePayOptions", "googlePayPlaceholderLabel", "surfaceId"]);
|
|
19
|
+
var { applePayOptions, applePayPlaceholderLabel = 'Buy with Apple Pay', checkout, checkoutAnalytics, googlePayOptions, googlePayPlaceholderLabel = 'Buy with Google Pay', surfaceId } = _a, surfaceProps = __rest(_a, ["applePayOptions", "applePayPlaceholderLabel", "checkout", "checkoutAnalytics", "googlePayOptions", "googlePayPlaceholderLabel", "surfaceId"]);
|
|
20
20
|
const normalizedSurfaceId = surfaceId.trim();
|
|
21
21
|
const { customerName } = checkout, checkoutSessionInput = __rest(checkout, ["customerName"]);
|
|
22
22
|
const surfaceAnalyticsMetadata = useMemo(() => {
|
|
23
23
|
var _a;
|
|
24
24
|
return buildStripeWalletSurfaceAnalyticsMetadata(Object.assign(Object.assign({}, ((_a = checkout.analyticsMetadata) !== null && _a !== void 0 ? _a : {})), { paymentMode: 'one_time' }), normalizedSurfaceId);
|
|
25
25
|
}, [checkout.analyticsMetadata, normalizedSurfaceId]);
|
|
26
|
-
const session = useStripeOneTimeCheckoutSession(Object.assign(Object.assign({}, checkoutSessionInput), { analyticsMetadata: surfaceAnalyticsMetadata }));
|
|
27
|
-
return (_jsx(StripeWalletSurface, Object.assign({}, surfaceProps, { applePayOptions: Object.assign(Object.assign({}, applePayOptions), { buttonType: Object.assign({ applePay: 'buy' }, ((_b = applePayOptions === null || applePayOptions === void 0 ? void 0 : applePayOptions.buttonType) !== null && _b !== void 0 ? _b : {})) }), applePayPlaceholderLabel: applePayPlaceholderLabel, customerEmail: checkout.customerEmail, customerName: customerName, googlePayOptions: Object.assign(Object.assign({}, googlePayOptions), { buttonType: Object.assign({ googlePay: 'buy' }, ((_c = googlePayOptions === null || googlePayOptions === void 0 ? void 0 : googlePayOptions.buttonType) !== null && _c !== void 0 ? _c : {})) }), googlePayPlaceholderLabel: googlePayPlaceholderLabel, returnUrl: checkout.returnUrl, session: session, surfaceId: normalizedSurfaceId })));
|
|
26
|
+
const session = useStripeOneTimeCheckoutSession(Object.assign(Object.assign({}, checkoutSessionInput), { analyticsMetadata: surfaceAnalyticsMetadata, checkoutAnalytics }));
|
|
27
|
+
return (_jsx(StripeWalletSurface, Object.assign({}, surfaceProps, { checkoutAnalytics: checkoutAnalytics, applePayOptions: Object.assign(Object.assign({}, applePayOptions), { buttonType: Object.assign({ applePay: 'buy' }, ((_b = applePayOptions === null || applePayOptions === void 0 ? void 0 : applePayOptions.buttonType) !== null && _b !== void 0 ? _b : {})) }), applePayPlaceholderLabel: applePayPlaceholderLabel, customerEmail: checkout.customerEmail, customerName: customerName, googlePayOptions: Object.assign(Object.assign({}, googlePayOptions), { buttonType: Object.assign({ googlePay: 'buy' }, ((_c = googlePayOptions === null || googlePayOptions === void 0 ? void 0 : googlePayOptions.buttonType) !== null && _c !== void 0 ? _c : {})) }), googlePayPlaceholderLabel: googlePayPlaceholderLabel, returnUrl: checkout.returnUrl, session: session, surfaceId: normalizedSurfaceId })));
|
|
28
28
|
}
|
|
@@ -2,6 +2,7 @@ import type { Stripe } from '@stripe/stripe-js';
|
|
|
2
2
|
import type { RuntimeMode } from '@funnelsgrove/runtime';
|
|
3
3
|
import { type CheckoutPreparationLoading, type CheckoutPreparationSource } from '../../paymentProvider.types.js';
|
|
4
4
|
import { type PaywallPlan, type StripeRuntimeConfigOverrides } from '../services/stripe.service.js';
|
|
5
|
+
import { type CheckoutObservabilityAnalyticsContext } from '../../../services/checkoutObservability.service.js';
|
|
5
6
|
export { isStripeCheckoutIdentityReady as isStripeOneTimeCheckoutIdentityReady, } from './checkoutIdentity.js';
|
|
6
7
|
type StripeOneTimeCheckoutPlanInput = Pick<PaywallPlan, 'amountCents' | 'analyticsPurchaseValue' | 'description' | 'id' | 'providerPlanId' | 'title' | 'funnelPlanKey' | 'offerSetId' | 'offerSetKey'>;
|
|
7
8
|
export type StripeOneTimeCheckoutSessionInput = {
|
|
@@ -9,6 +10,7 @@ export type StripeOneTimeCheckoutSessionInput = {
|
|
|
9
10
|
paymentProfileId?: string | null;
|
|
10
11
|
provider?: 'stripe' | null;
|
|
11
12
|
analyticsMetadata?: Record<string, unknown> | null;
|
|
13
|
+
checkoutAnalytics?: CheckoutObservabilityAnalyticsContext | null;
|
|
12
14
|
checkoutMode: RuntimeMode;
|
|
13
15
|
couponId?: string | null;
|
|
14
16
|
customerEmail?: string | null;
|
|
@@ -49,4 +51,4 @@ export type StripeOneTimeCheckoutIntentKeyInput = Pick<StripeOneTimeCheckoutSess
|
|
|
49
51
|
};
|
|
50
52
|
export declare function buildStripeOneTimeCheckoutIntentKey({ runtimeConfigRevisionId, paymentProfileId, provider, checkoutMode, couponId, customerEmail, plan, returnUrl, runtimeConfig, stripePublishableKey, userId, }: StripeOneTimeCheckoutIntentKeyInput): string;
|
|
51
53
|
export declare function shouldResetStripeOneTimeCheckoutSessionForIntentKey(previousIntentKey: string | null, nextIntentKey: string): boolean;
|
|
52
|
-
export declare function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, paymentProfileId, provider, analyticsMetadata, checkoutMode, couponId, customerEmail, enabled, onError, plan, returnUrl, runtimeConfig, stripePublishableKey, userId, }: StripeOneTimeCheckoutSessionInput): StripeOneTimeCheckoutSession;
|
|
54
|
+
export declare function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, paymentProfileId, provider, analyticsMetadata, checkoutAnalytics, checkoutMode, couponId, customerEmail, enabled, onError, plan, returnUrl, runtimeConfig, stripePublishableKey, userId, }: StripeOneTimeCheckoutSessionInput): StripeOneTimeCheckoutSession;
|
|
@@ -13,8 +13,10 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
13
13
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
14
14
|
import { createEmptyCheckoutPreparationLoading, } from '../../paymentProvider.types.js';
|
|
15
15
|
import { createStripeOneTimeCheckout, getStripePromise, isStripeConfigured, } from '../services/stripe.service.js';
|
|
16
|
-
import {
|
|
16
|
+
import { describeStripeCheckoutFailure } from '../services/stripeCheckoutFailure.js';
|
|
17
|
+
import { isInactiveCheckoutSessionError, shouldReportStripeCheckoutFailureShown, } from './useStripeSubscriptionCheckoutSession.js';
|
|
17
18
|
import { toAnalyticsPaymentMethod } from '../../../services/paymentMethodAnalytics.service.js';
|
|
19
|
+
import { resolveCheckoutObservabilityAnalyticsContext, trackCheckoutFailureShown, } from '../../../services/checkoutObservability.service.js';
|
|
18
20
|
import { resolveStripeCheckoutAttempt, } from './checkoutAttempt.js';
|
|
19
21
|
import { isStripeCheckoutIdentityReady } from './checkoutIdentity.js';
|
|
20
22
|
export { isStripeCheckoutIdentityReady as isStripeOneTimeCheckoutIdentityReady, } from './checkoutIdentity.js';
|
|
@@ -24,12 +26,6 @@ export function resolveStripeOneTimeCheckoutPreparationOptions(source, options =
|
|
|
24
26
|
const paymentMethod = (_b = options.paymentMethod) !== null && _b !== void 0 ? _b : (source === 'card' && checkoutStartSource !== 'checkout_render' ? 'card' : undefined);
|
|
25
27
|
return Object.assign(Object.assign(Object.assign({}, options), { checkoutStartSource }), (paymentMethod !== undefined ? { paymentMethod } : {}));
|
|
26
28
|
}
|
|
27
|
-
const getFriendlyStripeCheckoutError = (error) => {
|
|
28
|
-
const message = error instanceof Error ? error.message : 'Unable to initialize checkout';
|
|
29
|
-
return message === 'Internal server error'
|
|
30
|
-
? 'Unable to initialize checkout. Stripe settings may be missing for this funnel.'
|
|
31
|
-
: message;
|
|
32
|
-
};
|
|
33
29
|
const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
|
|
34
30
|
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
35
31
|
export const buildStripeOneTimeCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
|
|
@@ -75,7 +71,7 @@ export function buildStripeOneTimeCheckoutIntentKey({ runtimeConfigRevisionId, p
|
|
|
75
71
|
export function shouldResetStripeOneTimeCheckoutSessionForIntentKey(previousIntentKey, nextIntentKey) {
|
|
76
72
|
return previousIntentKey !== null && previousIntentKey !== nextIntentKey;
|
|
77
73
|
}
|
|
78
|
-
export function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, paymentProfileId, provider, analyticsMetadata, checkoutMode, couponId, customerEmail, enabled = true, onError, plan, returnUrl, runtimeConfig, stripePublishableKey, userId, }) {
|
|
74
|
+
export function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, paymentProfileId, provider, analyticsMetadata, checkoutAnalytics, checkoutMode, couponId, customerEmail, enabled = true, onError, plan, returnUrl, runtimeConfig, stripePublishableKey, userId, }) {
|
|
79
75
|
var _a, _b, _c;
|
|
80
76
|
const [clientSecret, setClientSecret] = useState(null);
|
|
81
77
|
const [clientSecretIntentKey, setClientSecretIntentKey] = useState(null);
|
|
@@ -226,7 +222,26 @@ export function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, payme
|
|
|
226
222
|
catch (checkoutError) {
|
|
227
223
|
if (((_e = creatingIntentRef.current) === null || _e === void 0 ? void 0 : _e.key) === requestKey &&
|
|
228
224
|
((_f = creatingIntentRef.current) === null || _f === void 0 ? void 0 : _f.id) === requestId) {
|
|
229
|
-
|
|
225
|
+
const failure = describeStripeCheckoutFailure(checkoutError);
|
|
226
|
+
const message = failure.message;
|
|
227
|
+
const observabilityAnalytics = resolveCheckoutObservabilityAnalyticsContext({
|
|
228
|
+
analytics: checkoutAnalytics,
|
|
229
|
+
environment: checkoutMode,
|
|
230
|
+
metadata: analyticsMetadataRef.current,
|
|
231
|
+
runtimeConfig,
|
|
232
|
+
});
|
|
233
|
+
if (observabilityAnalytics && shouldReportStripeCheckoutFailureShown(message)) {
|
|
234
|
+
trackCheckoutFailureShown({
|
|
235
|
+
analytics: observabilityAnalytics,
|
|
236
|
+
errorCode: failure.errorCode,
|
|
237
|
+
errorMessage: message,
|
|
238
|
+
failureStage: 'checkout_session_creation',
|
|
239
|
+
paymentMethod: options.paymentMethod,
|
|
240
|
+
provider: 'stripe',
|
|
241
|
+
providerErrorCode: failure.providerErrorCode,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
setError(message);
|
|
230
245
|
}
|
|
231
246
|
return null;
|
|
232
247
|
}
|
|
@@ -249,6 +264,7 @@ export function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, payme
|
|
|
249
264
|
paymentProfileId,
|
|
250
265
|
provider,
|
|
251
266
|
checkoutMode,
|
|
267
|
+
checkoutAnalytics,
|
|
252
268
|
clearActiveCheckoutSession,
|
|
253
269
|
configured,
|
|
254
270
|
couponId,
|
|
@@ -13,6 +13,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
13
13
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from 'react';
|
|
14
14
|
import { createEmptyCheckoutPreparationLoading, } from '../../paymentProvider.types.js';
|
|
15
15
|
import { createStripeSubscriptionCheckout, getStripePromise, isStripeConfigured, updateStripeSubscriptionCheckoutPlan, } from '../services/stripe.service.js';
|
|
16
|
+
import { describeStripeCheckoutFailure } from '../services/stripeCheckoutFailure.js';
|
|
16
17
|
import { toAnalyticsPaymentMethod } from '../../../services/paymentMethodAnalytics.service.js';
|
|
17
18
|
import { resolveCheckoutObservabilityAnalyticsContext, trackCheckoutFailureShown, } from '../../../services/checkoutObservability.service.js';
|
|
18
19
|
import { resolveStripeCheckoutAttempt, } from './checkoutAttempt.js';
|
|
@@ -24,12 +25,6 @@ export function resolveStripeSubscriptionCheckoutPreparationOptions(source, opti
|
|
|
24
25
|
const paymentMethod = (_b = options.paymentMethod) !== null && _b !== void 0 ? _b : (source === 'card' && checkoutStartSource !== 'checkout_render' ? 'card' : undefined);
|
|
25
26
|
return Object.assign(Object.assign(Object.assign({}, options), { checkoutStartSource }), (paymentMethod !== undefined ? { paymentMethod } : {}));
|
|
26
27
|
}
|
|
27
|
-
const getFriendlyStripeCheckoutError = (error) => {
|
|
28
|
-
const message = error instanceof Error ? error.message : 'Unable to initialize checkout';
|
|
29
|
-
return message === 'Internal server error'
|
|
30
|
-
? 'Unable to initialize checkout. Stripe settings may be missing for this funnel.'
|
|
31
|
-
: message;
|
|
32
|
-
};
|
|
33
28
|
const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
|
|
34
29
|
const useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
|
|
35
30
|
export const buildStripeSubscriptionCheckoutPreparationAnalyticsMetadata = (analyticsMetadata, options = {}) => {
|
|
@@ -246,7 +241,8 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
|
|
|
246
241
|
catch (subscriptionError) {
|
|
247
242
|
if (((_f = creatingIntentRef.current) === null || _f === void 0 ? void 0 : _f.key) === requestKey &&
|
|
248
243
|
((_g = creatingIntentRef.current) === null || _g === void 0 ? void 0 : _g.id) === requestId) {
|
|
249
|
-
const
|
|
244
|
+
const failure = describeStripeCheckoutFailure(subscriptionError);
|
|
245
|
+
const message = failure.message;
|
|
250
246
|
const observabilityAnalytics = resolveCheckoutObservabilityAnalyticsContext({
|
|
251
247
|
analytics: checkoutAnalytics,
|
|
252
248
|
environment: checkoutMode,
|
|
@@ -257,10 +253,12 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
|
|
|
257
253
|
&& shouldReportStripeCheckoutFailureShown(message)) {
|
|
258
254
|
trackCheckoutFailureShown({
|
|
259
255
|
analytics: observabilityAnalytics,
|
|
256
|
+
errorCode: failure.errorCode,
|
|
260
257
|
errorMessage: message,
|
|
261
258
|
failureStage: 'checkout_session_creation',
|
|
262
259
|
paymentMethod: options.paymentMethod,
|
|
263
260
|
provider: 'stripe',
|
|
261
|
+
providerErrorCode: failure.providerErrorCode,
|
|
264
262
|
});
|
|
265
263
|
}
|
|
266
264
|
setError(message);
|
|
@@ -364,7 +362,8 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
|
|
|
364
362
|
return true;
|
|
365
363
|
}
|
|
366
364
|
catch (subscriptionError) {
|
|
367
|
-
const
|
|
365
|
+
const failure = describeStripeCheckoutFailure(subscriptionError);
|
|
366
|
+
const message = failure.message;
|
|
368
367
|
const observabilityAnalytics = resolveCheckoutObservabilityAnalyticsContext({
|
|
369
368
|
analytics: checkoutAnalytics,
|
|
370
369
|
environment: checkoutMode,
|
|
@@ -376,9 +375,11 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
|
|
|
376
375
|
trackCheckoutFailureShown({
|
|
377
376
|
analytics: observabilityAnalytics,
|
|
378
377
|
checkoutSessionId: activeCheckoutSessionId,
|
|
378
|
+
errorCode: failure.errorCode,
|
|
379
379
|
errorMessage: message,
|
|
380
380
|
failureStage: 'checkout_session_update',
|
|
381
381
|
provider: 'stripe',
|
|
382
|
+
providerErrorCode: failure.providerErrorCode,
|
|
382
383
|
});
|
|
383
384
|
}
|
|
384
385
|
setError(message);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { FunnelSdkHttpError } from '@funnelsgrove/runtime';
|
|
2
|
+
export const describeStripeCheckoutFailure = (error) => {
|
|
3
|
+
const errorCode = error instanceof FunnelSdkHttpError
|
|
4
|
+
? error.code || undefined
|
|
5
|
+
: undefined;
|
|
6
|
+
const providerErrorCode = (errorCode === null || errorCode === void 0 ? void 0 : errorCode.startsWith('stripe_'))
|
|
7
|
+
? errorCode.slice('stripe_'.length)
|
|
8
|
+
: undefined;
|
|
9
|
+
const rawMessage = error instanceof Error
|
|
10
|
+
? error.message
|
|
11
|
+
: 'Unable to initialize checkout';
|
|
12
|
+
const message = errorCode === 'stripe_email_invalid'
|
|
13
|
+
? 'Enter a valid email address to continue.'
|
|
14
|
+
: rawMessage === 'Internal server error'
|
|
15
|
+
? 'Unable to initialize checkout. Stripe settings may be missing for this funnel.'
|
|
16
|
+
: rawMessage;
|
|
17
|
+
return {
|
|
18
|
+
errorCode,
|
|
19
|
+
message,
|
|
20
|
+
providerErrorCode,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -39,14 +39,19 @@ const STRIPE_PAYMENT_REJECTION_CODES = new Set([
|
|
|
39
39
|
'payment_intent_authentication_failure',
|
|
40
40
|
'paymentFailed',
|
|
41
41
|
]);
|
|
42
|
+
const STRIPE_VALIDATION_ERROR_CODES = new Set(['email_invalid']);
|
|
42
43
|
export const classifyCheckoutFailure = (input) => {
|
|
43
44
|
const providerDeclineCode = asNonEmptyString(input.providerDeclineCode);
|
|
44
45
|
const providerErrorCode = asNonEmptyString(input.providerErrorCode);
|
|
45
46
|
const providerErrorType = asNonEmptyString(input.providerErrorType);
|
|
47
|
+
if (input.provider === 'stripe' && providerErrorCode
|
|
48
|
+
&& STRIPE_VALIDATION_ERROR_CODES.has(providerErrorCode)) {
|
|
49
|
+
return 'validation_failure';
|
|
50
|
+
}
|
|
46
51
|
if (input.provider === 'stripe' && (Boolean(providerDeclineCode)
|
|
47
52
|
|| providerErrorType === 'card_error'
|
|
48
53
|
|| (providerErrorCode ? STRIPE_PAYMENT_REJECTION_CODES.has(providerErrorCode) : false)
|
|
49
|
-
|| /card has insufficient funds|unable to authenticate your payment method/i.test(input.errorMessage))) {
|
|
54
|
+
|| /card has insufficient funds|unable to authenticate your payment method|card(?: was| is)? declined|security code is incorrect/i.test(input.errorMessage))) {
|
|
50
55
|
return 'payment_rejection';
|
|
51
56
|
}
|
|
52
57
|
const message = input.errorMessage.trim();
|
|
@@ -57,8 +62,8 @@ export const classifyCheckoutFailure = (input) => {
|
|
|
57
62
|
if (/failed to fetch|load failed|network|connection to (?:the )?provider|too many requests/i.test(message)) {
|
|
58
63
|
return 'network_failure';
|
|
59
64
|
}
|
|
60
|
-
if (/(?:email|card number|expiration|expiry|cvc|zip|postal code|billing address|country).*(?:required|incomplete|incorrect|invalid|unsupported)/i.test(message)
|
|
61
|
-
|| /(?:required|incomplete|incorrect|invalid|unsupported).*(?:email|card number|expiration|expiry|cvc|zip|postal code|billing address|country)/i.test(message)
|
|
65
|
+
if (/(?:email|card number|expiration|expiry|cvc|security code|zip|postal code|billing address|country).*(?:required|incomplete|incorrect|invalid|unsupported)/i.test(message)
|
|
66
|
+
|| /(?:required|incomplete|incorrect|invalid|unsupported).*(?:email|card number|expiration|expiry|cvc|security code|zip|postal code|billing address|country)/i.test(message)
|
|
62
67
|
|| /(?:enter|provide|use) a valid email(?: address)?/i.test(message)) {
|
|
63
68
|
return 'validation_failure';
|
|
64
69
|
}
|