@funnelsgrove/payments 0.23.2 → 0.24.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/generated/package-version.d.ts +1 -0
- package/dist/generated/package-version.js +2 -0
- package/dist/providers/stripe/components/StripeCheckoutExpressCheckoutButton.d.ts +3 -1
- package/dist/providers/stripe/components/StripeCheckoutExpressCheckoutButton.js +8 -1
- package/dist/providers/stripe/components/WalletSubscriptionCheckoutSlot.js +110 -8
- package/dist/services/checkoutObservability.service.d.ts +18 -3
- package/dist/services/checkoutObservability.service.js +133 -14
- package/package.json +6 -6
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PAYMENTS_PACKAGE_VERSION = "0.24.0";
|
|
@@ -22,6 +22,8 @@ export type StripeCheckoutExpressCheckoutButtonProps = {
|
|
|
22
22
|
supportedCountries?: readonly string[];
|
|
23
23
|
onServerUpdate?: () => Promise<boolean>;
|
|
24
24
|
onAvailabilityChange?: (available: boolean) => void;
|
|
25
|
+
onElementMount?: () => void;
|
|
26
|
+
onElementRetry?: () => void;
|
|
25
27
|
onCancel?: () => void;
|
|
26
28
|
onError?: (message: string | null) => void;
|
|
27
29
|
onPaymentInfoSubmitted?: () => void | Promise<void>;
|
|
@@ -35,4 +37,4 @@ export type StripeCheckoutExpressCheckoutButtonProps = {
|
|
|
35
37
|
onSuccess?: (result: StripeWalletCheckoutSuccess) => void | Promise<void>;
|
|
36
38
|
unsupportedCountryMessage?: string;
|
|
37
39
|
};
|
|
38
|
-
export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAnalytics, checkoutSessionId, returnUrl, confirmEmail, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, serverUpdateKey, supportedCountries, onServerUpdate, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onPaymentRejected, onSuccess, onEmbeddedComplete, onVerificationPending, unsupportedCountryMessage, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAnalytics, checkoutSessionId, returnUrl, confirmEmail, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, serverUpdateKey, supportedCountries, onServerUpdate, onAvailabilityChange, onElementMount, onElementRetry, onCancel, onError, onPaymentInfoSubmitted, onPaymentRejected, onSuccess, onEmbeddedComplete, onVerificationPending, unsupportedCountryMessage, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -53,13 +53,19 @@ const normalizeStripeCheckoutProviderError = (error) => {
|
|
|
53
53
|
type: typeof record.type === 'string' ? record.type : undefined,
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
|
-
export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAnalytics, checkoutSessionId, returnUrl, confirmEmail = true, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, serverUpdateKey, supportedCountries, onServerUpdate, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onPaymentRejected, onSuccess, onEmbeddedComplete, onVerificationPending, unsupportedCountryMessage, }) {
|
|
56
|
+
export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAnalytics, checkoutSessionId, returnUrl, confirmEmail = true, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, serverUpdateKey, supportedCountries, onServerUpdate, onAvailabilityChange, onElementMount, onElementRetry, onCancel, onError, onPaymentInfoSubmitted, onPaymentRejected, onSuccess, onEmbeddedComplete, onVerificationPending, unsupportedCountryMessage, }) {
|
|
57
57
|
const checkoutState = useCheckout();
|
|
58
58
|
const [completed, setCompleted] = useState(false);
|
|
59
59
|
const completedRef = useRef(false);
|
|
60
60
|
const verificationInFlightRef = useRef(false);
|
|
61
61
|
const [verificationInFlight, setVerificationInFlight] = useState(false);
|
|
62
62
|
const [verificationError, setVerificationError] = useState(null);
|
|
63
|
+
const onElementMountRef = useRef(onElementMount);
|
|
64
|
+
onElementMountRef.current = onElementMount;
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
var _a;
|
|
67
|
+
(_a = onElementMountRef.current) === null || _a === void 0 ? void 0 : _a.call(onElementMountRef);
|
|
68
|
+
}, []);
|
|
63
69
|
const [loadRetry, setLoadRetry] = useState(0);
|
|
64
70
|
const loadRetryUsedRef = useRef(false);
|
|
65
71
|
const walletReadyRef = useRef(false);
|
|
@@ -373,6 +379,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
|
|
|
373
379
|
}
|
|
374
380
|
if (!walletReadyRef.current && !loadRetryUsedRef.current) {
|
|
375
381
|
loadRetryUsedRef.current = true;
|
|
382
|
+
onElementRetry === null || onElementRetry === void 0 ? void 0 : onElementRetry();
|
|
376
383
|
// Replace only the failed Element, retaining the Checkout Session.
|
|
377
384
|
loadRetryTimerRef.current = setTimeout(() => {
|
|
378
385
|
loadRetryTimerRef.current = null;
|
|
@@ -5,7 +5,8 @@ import { StripeExpressCheckoutElement } from './StripeExpressCheckoutElement.js'
|
|
|
5
5
|
import { trackStripeSubscriptionCheckoutStarted, } from '../services/checkoutCompletionAnalytics.service.js';
|
|
6
6
|
import { toAnalyticsPaymentMethod } from '../../../services/paymentMethodAnalytics.service.js';
|
|
7
7
|
import { isInactiveCheckoutSessionError, } from '../hooks/useStripeSubscriptionCheckoutSession.js';
|
|
8
|
-
import { trackCheckoutWalletReady } from '../../../services/checkoutObservability.service.js';
|
|
8
|
+
import { trackCheckoutWalletReady, trackCheckoutWalletSlow } from '../../../services/checkoutObservability.service.js';
|
|
9
|
+
const WALLET_SLOW_THRESHOLD_MS = 15000;
|
|
9
10
|
export function getWalletSubscriptionCheckoutRenderState({ expressCheckoutAllowed, hasActiveClientSecret, hasStripePromise, suspended, walletAvailable, walletPreparing, }) {
|
|
10
11
|
const shouldRenderExpressCheckout = !suspended &&
|
|
11
12
|
expressCheckoutAllowed &&
|
|
@@ -44,14 +45,21 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
44
45
|
const autoWalletPreparationIntentKeyRef = useRef(null);
|
|
45
46
|
const walletCheckoutStartedAfterClickSessionIdRef = useRef(null);
|
|
46
47
|
const walletReadinessCycleRef = useRef(null);
|
|
48
|
+
const checkoutAnalyticsRef = useRef(checkoutAnalytics);
|
|
49
|
+
checkoutAnalyticsRef.current = checkoutAnalytics;
|
|
47
50
|
if (!walletReadinessCycleRef.current
|
|
48
51
|
|| walletReadinessCycleRef.current.intentKey !== session.intentKey) {
|
|
49
52
|
walletReadinessCycleRef.current = {
|
|
53
|
+
activeSecretObservedAtMs: null,
|
|
50
54
|
clientSecretReceivedAtMs: null,
|
|
55
|
+
elementMountedAtMs: null,
|
|
56
|
+
elementRetryCount: 0,
|
|
51
57
|
intentKey: session.intentKey,
|
|
52
58
|
mountedAtMs: null,
|
|
53
59
|
reportedCheckoutSessionId: null,
|
|
54
60
|
requestStartedAtMs: null,
|
|
61
|
+
slowReportedCheckoutSessionId: null,
|
|
62
|
+
stripePromiseWaitMs: null,
|
|
55
63
|
};
|
|
56
64
|
}
|
|
57
65
|
useEffect(() => {
|
|
@@ -60,11 +68,71 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
60
68
|
cycle.mountedAtMs = performance.now();
|
|
61
69
|
}
|
|
62
70
|
}, [session.intentKey]);
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const cycle = walletReadinessCycleRef.current;
|
|
73
|
+
if (session.activeClientSecret && (cycle === null || cycle === void 0 ? void 0 : cycle.intentKey) === session.intentKey) {
|
|
74
|
+
cycle.activeSecretObservedAtMs = performance.now();
|
|
75
|
+
}
|
|
76
|
+
}, [session.activeClientSecret, session.intentKey]);
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
const stripePromise = session.stripePromise;
|
|
79
|
+
if (!stripePromise) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const intentKey = session.intentKey;
|
|
83
|
+
const startedAtMs = performance.now();
|
|
84
|
+
let active = true;
|
|
85
|
+
void stripePromise.then((stripe) => {
|
|
86
|
+
const cycle = walletReadinessCycleRef.current;
|
|
87
|
+
if (active && stripe && (cycle === null || cycle === void 0 ? void 0 : cycle.intentKey) === intentKey) {
|
|
88
|
+
cycle.stripePromiseWaitMs = Math.max(0, performance.now() - startedAtMs);
|
|
89
|
+
}
|
|
90
|
+
}).catch(() => undefined);
|
|
91
|
+
return () => {
|
|
92
|
+
active = false;
|
|
93
|
+
};
|
|
94
|
+
}, [session.intentKey, session.stripePromise]);
|
|
63
95
|
const slotDisabled = disabled || !session.configured;
|
|
64
96
|
const analyticsPaymentMethod = toAnalyticsPaymentMethod(availabilityPaymentMethod);
|
|
65
97
|
const walletAvailable = walletAvailability.intentKey === session.intentKey
|
|
66
98
|
? walletAvailability.available
|
|
67
99
|
: null;
|
|
100
|
+
useEffect(() => {
|
|
101
|
+
var _a;
|
|
102
|
+
const checkoutSessionId = (_a = session.checkoutSessionId) === null || _a === void 0 ? void 0 : _a.trim();
|
|
103
|
+
if (slotDisabled
|
|
104
|
+
|| suspended
|
|
105
|
+
|| !expressCheckoutAllowed
|
|
106
|
+
|| !session.activeClientSecret
|
|
107
|
+
|| !checkoutSessionId
|
|
108
|
+
|| walletAvailable !== null) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const intentKey = session.intentKey;
|
|
112
|
+
const observedAtMs = performance.now();
|
|
113
|
+
const timer = setTimeout(() => {
|
|
114
|
+
var _a;
|
|
115
|
+
const cycle = walletReadinessCycleRef.current;
|
|
116
|
+
const analytics = checkoutAnalyticsRef.current;
|
|
117
|
+
if (!analytics
|
|
118
|
+
|| !cycle
|
|
119
|
+
|| cycle.intentKey !== intentKey
|
|
120
|
+
|| cycle.reportedCheckoutSessionId === checkoutSessionId
|
|
121
|
+
|| cycle.slowReportedCheckoutSessionId === checkoutSessionId) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
cycle.slowReportedCheckoutSessionId = checkoutSessionId;
|
|
125
|
+
trackCheckoutWalletSlow({
|
|
126
|
+
analytics,
|
|
127
|
+
checkoutSessionId,
|
|
128
|
+
paymentMethod: availabilityPaymentMethod,
|
|
129
|
+
provider: 'stripe',
|
|
130
|
+
walletTimingOrigin: cycle.clientSecretReceivedAtMs === null ? 'already_active' : 'prepared_here',
|
|
131
|
+
walletClientSecretToSlowMs: Math.max(0, performance.now() - ((_a = cycle.clientSecretReceivedAtMs) !== null && _a !== void 0 ? _a : observedAtMs)),
|
|
132
|
+
});
|
|
133
|
+
}, WALLET_SLOW_THRESHOLD_MS);
|
|
134
|
+
return () => clearTimeout(timer);
|
|
135
|
+
}, [availabilityPaymentMethod, expressCheckoutAllowed, session.activeClientSecret, session.checkoutSessionId, session.intentKey, slotDisabled, suspended, walletAvailable]);
|
|
68
136
|
const { placeholderIsPreparing, shouldRenderExpressCheckout, shouldRenderPlaceholder, } = getWalletSubscriptionCheckoutRenderState({
|
|
69
137
|
expressCheckoutAllowed,
|
|
70
138
|
hasActiveClientSecret: Boolean(session.activeClientSecret),
|
|
@@ -103,7 +171,10 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
103
171
|
const requestStartedAtMs = performance.now();
|
|
104
172
|
(_a = cycle.mountedAtMs) !== null && _a !== void 0 ? _a : (cycle.mountedAtMs = requestStartedAtMs);
|
|
105
173
|
cycle.requestStartedAtMs = requestStartedAtMs;
|
|
174
|
+
cycle.activeSecretObservedAtMs = null;
|
|
106
175
|
cycle.clientSecretReceivedAtMs = null;
|
|
176
|
+
cycle.elementMountedAtMs = null;
|
|
177
|
+
cycle.elementRetryCount = 0;
|
|
107
178
|
cycle.reportedCheckoutSessionId = null;
|
|
108
179
|
}, []);
|
|
109
180
|
const finishWalletPreparationTiming = useCallback((intentKey, readyClientSecret) => {
|
|
@@ -180,9 +251,12 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
180
251
|
available: null,
|
|
181
252
|
intentKey: session.intentKey,
|
|
182
253
|
});
|
|
254
|
+
startWalletPreparationTiming(session.intentKey);
|
|
183
255
|
void session.restartWalletCheckout({
|
|
184
256
|
checkoutStartSource: 'wallet_click',
|
|
185
257
|
paymentMethod: analyticsPaymentMethod,
|
|
258
|
+
}).then((readyClientSecret) => {
|
|
259
|
+
finishWalletPreparationTiming(session.intentKey, readyClientSecret);
|
|
186
260
|
});
|
|
187
261
|
return;
|
|
188
262
|
}
|
|
@@ -190,7 +264,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
190
264
|
onError === null || onError === void 0 ? void 0 : onError(message);
|
|
191
265
|
};
|
|
192
266
|
const handleAvailabilityChange = (available) => {
|
|
193
|
-
var _a;
|
|
267
|
+
var _a, _b, _c;
|
|
194
268
|
reportWalletAvailability(session.intentKey, available);
|
|
195
269
|
const cycle = walletReadinessCycleRef.current;
|
|
196
270
|
const checkoutSessionId = ((_a = session.checkoutSessionId) === null || _a === void 0 ? void 0 : _a.trim()) || '';
|
|
@@ -200,22 +274,40 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
200
274
|
|| !cycle
|
|
201
275
|
|| cycle.intentKey !== session.intentKey
|
|
202
276
|
|| cycle.mountedAtMs === null
|
|
203
|
-
|| cycle.requestStartedAtMs === null
|
|
204
|
-
|| cycle.clientSecretReceivedAtMs === null
|
|
205
277
|
|| cycle.reportedCheckoutSessionId === checkoutSessionId) {
|
|
206
278
|
return;
|
|
207
279
|
}
|
|
208
280
|
const readyAtMs = performance.now();
|
|
281
|
+
const { requestStartedAtMs, clientSecretReceivedAtMs } = cycle;
|
|
282
|
+
const preparedHere = requestStartedAtMs !== null && clientSecretReceivedAtMs !== null;
|
|
283
|
+
const observedAtMs = (_b = cycle.activeSecretObservedAtMs) !== null && _b !== void 0 ? _b : cycle.mountedAtMs;
|
|
209
284
|
cycle.reportedCheckoutSessionId = checkoutSessionId;
|
|
210
285
|
trackCheckoutWalletReady({
|
|
211
286
|
analytics: checkoutAnalytics,
|
|
212
287
|
checkoutSessionId,
|
|
213
288
|
paymentMethod: availabilityPaymentMethod,
|
|
214
289
|
provider: 'stripe',
|
|
215
|
-
|
|
290
|
+
walletTimingOrigin: preparedHere ? 'prepared_here' : 'already_active',
|
|
291
|
+
walletClientSecretToReadyMs: preparedHere
|
|
292
|
+
? Math.max(0, readyAtMs - clientSecretReceivedAtMs) : undefined,
|
|
293
|
+
walletObservedActiveToReadyMs: preparedHere
|
|
294
|
+
? undefined : Math.max(0, readyAtMs - observedAtMs),
|
|
216
295
|
walletMountToReadyMs: Math.max(0, readyAtMs - cycle.mountedAtMs),
|
|
217
|
-
walletMountToRequestMs:
|
|
218
|
-
|
|
296
|
+
walletMountToRequestMs: preparedHere
|
|
297
|
+
? Math.max(0, requestStartedAtMs - cycle.mountedAtMs) : undefined,
|
|
298
|
+
walletRequestMs: preparedHere
|
|
299
|
+
? Math.max(0, clientSecretReceivedAtMs - requestStartedAtMs) : undefined,
|
|
300
|
+
walletClientSecretToElementMountMs: !preparedHere || cycle.elementMountedAtMs === null
|
|
301
|
+
? undefined
|
|
302
|
+
: Math.max(0, cycle.elementMountedAtMs - clientSecretReceivedAtMs),
|
|
303
|
+
walletObservedActiveToElementMountMs: preparedHere || cycle.elementMountedAtMs === null
|
|
304
|
+
? undefined
|
|
305
|
+
: Math.max(0, cycle.elementMountedAtMs - observedAtMs),
|
|
306
|
+
walletElementMountToReadyMs: cycle.elementMountedAtMs === null
|
|
307
|
+
? undefined
|
|
308
|
+
: Math.max(0, readyAtMs - cycle.elementMountedAtMs),
|
|
309
|
+
walletElementRetryCount: cycle.elementRetryCount,
|
|
310
|
+
walletStripePromiseWaitMs: (_c = cycle.stripePromiseWaitMs) !== null && _c !== void 0 ? _c : undefined,
|
|
219
311
|
});
|
|
220
312
|
};
|
|
221
313
|
const handlePlaceholderClick = async () => {
|
|
@@ -250,7 +342,17 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
|
|
|
250
342
|
.filter(Boolean)
|
|
251
343
|
.join(' '), "aria-busy": placeholderIsPreparing || undefined, children: [shouldRenderExpressCheckout &&
|
|
252
344
|
session.activeClientSecret &&
|
|
253
|
-
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, appearance: appearance, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, checkoutAnalytics: checkoutAnalytics, checkoutSessionId: session.checkoutSessionId, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange,
|
|
345
|
+
session.stripePromise ? (_jsx(StripeExpressCheckoutElement, { amountCents: amountCents, appearance: appearance, availabilityPaymentMethods: [availabilityPaymentMethod], beforeConfirm: beforeConfirm, checkoutAnalytics: checkoutAnalytics, checkoutSessionId: session.checkoutSessionId, className: className, clientSecret: session.activeClientSecret, customerEmail: customerEmail, customerName: customerName, onAvailabilityChange: handleAvailabilityChange, onElementMount: () => {
|
|
346
|
+
const cycle = walletReadinessCycleRef.current;
|
|
347
|
+
if ((cycle === null || cycle === void 0 ? void 0 : cycle.intentKey) === session.intentKey) {
|
|
348
|
+
cycle.elementMountedAtMs = performance.now();
|
|
349
|
+
}
|
|
350
|
+
}, onElementRetry: () => {
|
|
351
|
+
const cycle = walletReadinessCycleRef.current;
|
|
352
|
+
if ((cycle === null || cycle === void 0 ? void 0 : cycle.intentKey) === session.intentKey) {
|
|
353
|
+
cycle.elementRetryCount += 1;
|
|
354
|
+
}
|
|
355
|
+
}, onCancel: onCancel, onError: handleError, onPaymentInfoSubmitted: onPaymentInfoSubmitted, 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({
|
|
254
356
|
className,
|
|
255
357
|
disabled: slotDisabled,
|
|
256
358
|
label: placeholderLabel,
|
|
@@ -41,10 +41,25 @@ export type CheckoutWalletReadyInput = {
|
|
|
41
41
|
checkoutSessionId: string;
|
|
42
42
|
paymentMethod: unknown;
|
|
43
43
|
provider: 'stripe';
|
|
44
|
-
|
|
44
|
+
walletTimingOrigin?: 'prepared_here' | 'already_active';
|
|
45
|
+
walletClientSecretToReadyMs?: number;
|
|
46
|
+
walletClientSecretToElementMountMs?: number;
|
|
47
|
+
walletObservedActiveToReadyMs?: number;
|
|
48
|
+
walletObservedActiveToElementMountMs?: number;
|
|
49
|
+
walletElementMountToReadyMs?: number;
|
|
50
|
+
walletElementRetryCount?: number;
|
|
51
|
+
walletStripePromiseWaitMs?: number;
|
|
45
52
|
walletMountToReadyMs: number;
|
|
46
|
-
walletMountToRequestMs
|
|
47
|
-
walletRequestMs
|
|
53
|
+
walletMountToRequestMs?: number;
|
|
54
|
+
walletRequestMs?: number;
|
|
48
55
|
};
|
|
49
56
|
export declare const trackCheckoutWalletReady: (input: CheckoutWalletReadyInput) => string | null;
|
|
57
|
+
export declare const trackCheckoutWalletSlow: (input: {
|
|
58
|
+
analytics: CheckoutObservabilityAnalyticsContext;
|
|
59
|
+
checkoutSessionId: string;
|
|
60
|
+
paymentMethod: unknown;
|
|
61
|
+
provider: "stripe";
|
|
62
|
+
walletTimingOrigin: "prepared_here" | "already_active";
|
|
63
|
+
walletClientSecretToSlowMs: number;
|
|
64
|
+
}) => string | null;
|
|
50
65
|
export {};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { publicAnalyticsSdk, } from '@funnelsgrove/analytics';
|
|
2
2
|
import { buildMainApiUrl, buildSdkHeaders, FUNNEL_ID, FUNNEL_VERSION_ID, } from '@funnelsgrove/runtime';
|
|
3
3
|
import { toAnalyticsPaymentMethod } from './paymentMethodAnalytics.service.js';
|
|
4
|
+
import { PAYMENTS_PACKAGE_VERSION } from '../generated/package-version.js';
|
|
4
5
|
const asNonEmptyString = (value) => typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
5
6
|
export const resolveCheckoutObservabilityAnalyticsContext = (input) => {
|
|
6
7
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
@@ -112,6 +113,19 @@ const reportCheckoutObservability = (input) => {
|
|
|
112
113
|
providerErrorCode: input.providerErrorCode,
|
|
113
114
|
providerErrorType: input.providerErrorType,
|
|
114
115
|
walletClientSecretToReadyMs: input.walletClientSecretToReadyMs,
|
|
116
|
+
walletClientSecretToElementMountMs: input.walletClientSecretToElementMountMs,
|
|
117
|
+
walletClientSecretToSlowMs: input.walletClientSecretToSlowMs,
|
|
118
|
+
walletObservedActiveToReadyMs: input.walletObservedActiveToReadyMs,
|
|
119
|
+
walletObservedActiveToElementMountMs: input.walletObservedActiveToElementMountMs,
|
|
120
|
+
walletTimingOrigin: input.walletTimingOrigin,
|
|
121
|
+
walletElementMountToReadyMs: input.walletElementMountToReadyMs,
|
|
122
|
+
walletElementRetryCount: input.walletElementRetryCount,
|
|
123
|
+
walletStripePromiseWaitMs: input.walletStripePromiseWaitMs,
|
|
124
|
+
walletVisibilityAtReady: input.walletVisibilityAtReady,
|
|
125
|
+
walletVisibilityAtSlow: input.walletVisibilityAtSlow,
|
|
126
|
+
walletBrowserFamily: input.walletBrowserFamily,
|
|
127
|
+
walletOsFamily: input.walletOsFamily,
|
|
128
|
+
paymentsSdkVersion: input.paymentsSdkVersion,
|
|
115
129
|
walletMountToReadyMs: input.walletMountToReadyMs,
|
|
116
130
|
walletMountToRequestMs: input.walletMountToRequestMs,
|
|
117
131
|
walletRequestMs: input.walletRequestMs,
|
|
@@ -201,36 +215,90 @@ export const trackCheckoutFailureShown = (input) => {
|
|
|
201
215
|
};
|
|
202
216
|
export const trackCheckoutCanceled = (input) => emitCheckoutObservability('checkout_canceled', input);
|
|
203
217
|
const normalizeWalletTiming = (value) => (Number.isFinite(value) && value >= 0 && value <= 300000 ? value : null);
|
|
218
|
+
const browserFamily = (userAgent) => {
|
|
219
|
+
if (/Edg(?:e|iOS|A)?\//i.test(userAgent))
|
|
220
|
+
return 'edge';
|
|
221
|
+
if (/FxiOS\/|Firefox\//i.test(userAgent))
|
|
222
|
+
return 'firefox';
|
|
223
|
+
if (/CriOS\/|Chrome\//i.test(userAgent))
|
|
224
|
+
return 'chrome';
|
|
225
|
+
if (/Safari\//i.test(userAgent))
|
|
226
|
+
return 'safari';
|
|
227
|
+
return 'other';
|
|
228
|
+
};
|
|
229
|
+
const osFamily = (userAgent) => {
|
|
230
|
+
if (/iPhone|iPad|iPod/i.test(userAgent))
|
|
231
|
+
return 'ios';
|
|
232
|
+
if (/Android/i.test(userAgent))
|
|
233
|
+
return 'android';
|
|
234
|
+
if (/Windows/i.test(userAgent))
|
|
235
|
+
return 'windows';
|
|
236
|
+
if (/Macintosh|Mac OS X/i.test(userAgent))
|
|
237
|
+
return 'macos';
|
|
238
|
+
return 'other';
|
|
239
|
+
};
|
|
240
|
+
const getWalletClientContext = () => {
|
|
241
|
+
const userAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent;
|
|
242
|
+
const visibilityState = typeof document === 'undefined' ? undefined : document.visibilityState;
|
|
243
|
+
return {
|
|
244
|
+
walletBrowserFamily: browserFamily(userAgent),
|
|
245
|
+
walletOsFamily: osFamily(userAgent),
|
|
246
|
+
visibilityState: visibilityState === 'visible'
|
|
247
|
+
|| visibilityState === 'hidden'
|
|
248
|
+
|| visibilityState === 'prerender'
|
|
249
|
+
? visibilityState : 'unknown',
|
|
250
|
+
};
|
|
251
|
+
};
|
|
204
252
|
export const trackCheckoutWalletReady = (input) => {
|
|
205
|
-
var _a;
|
|
253
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
206
254
|
const checkoutSessionId = input.checkoutSessionId.trim();
|
|
207
255
|
const paymentMethod = toAnalyticsPaymentMethod(input.paymentMethod);
|
|
208
|
-
const walletClientSecretToReadyMs =
|
|
256
|
+
const walletClientSecretToReadyMs = input.walletClientSecretToReadyMs === undefined
|
|
257
|
+
? undefined : (_a = normalizeWalletTiming(input.walletClientSecretToReadyMs)) !== null && _a !== void 0 ? _a : undefined;
|
|
258
|
+
const walletClientSecretToElementMountMs = input.walletClientSecretToElementMountMs === undefined
|
|
259
|
+
? undefined : (_b = normalizeWalletTiming(input.walletClientSecretToElementMountMs)) !== null && _b !== void 0 ? _b : undefined;
|
|
260
|
+
const walletElementMountToReadyMs = input.walletElementMountToReadyMs === undefined
|
|
261
|
+
? undefined : (_c = normalizeWalletTiming(input.walletElementMountToReadyMs)) !== null && _c !== void 0 ? _c : undefined;
|
|
262
|
+
const walletObservedActiveToReadyMs = input.walletObservedActiveToReadyMs === undefined
|
|
263
|
+
? undefined : (_d = normalizeWalletTiming(input.walletObservedActiveToReadyMs)) !== null && _d !== void 0 ? _d : undefined;
|
|
264
|
+
const walletObservedActiveToElementMountMs = input.walletObservedActiveToElementMountMs === undefined
|
|
265
|
+
? undefined : (_e = normalizeWalletTiming(input.walletObservedActiveToElementMountMs)) !== null && _e !== void 0 ? _e : undefined;
|
|
266
|
+
const walletStripePromiseWaitMs = input.walletStripePromiseWaitMs === undefined
|
|
267
|
+
? undefined : (_f = normalizeWalletTiming(input.walletStripePromiseWaitMs)) !== null && _f !== void 0 ? _f : undefined;
|
|
268
|
+
const walletElementRetryCount = input.walletElementRetryCount !== undefined
|
|
269
|
+
&& Number.isInteger(input.walletElementRetryCount)
|
|
270
|
+
&& input.walletElementRetryCount >= 0
|
|
271
|
+
&& input.walletElementRetryCount <= 5
|
|
272
|
+
? input.walletElementRetryCount : undefined;
|
|
209
273
|
const walletMountToReadyMs = normalizeWalletTiming(input.walletMountToReadyMs);
|
|
210
|
-
const walletMountToRequestMs =
|
|
211
|
-
|
|
274
|
+
const walletMountToRequestMs = input.walletMountToRequestMs === undefined
|
|
275
|
+
? undefined : (_g = normalizeWalletTiming(input.walletMountToRequestMs)) !== null && _g !== void 0 ? _g : undefined;
|
|
276
|
+
const walletRequestMs = input.walletRequestMs === undefined
|
|
277
|
+
? undefined : (_h = normalizeWalletTiming(input.walletRequestMs)) !== null && _h !== void 0 ? _h : undefined;
|
|
278
|
+
const alreadyActive = input.walletTimingOrigin === 'already_active';
|
|
212
279
|
if (!checkoutSessionId
|
|
213
280
|
|| !paymentMethod
|
|
214
|
-
|| walletClientSecretToReadyMs === null
|
|
215
281
|
|| walletMountToReadyMs === null
|
|
216
|
-
||
|
|
217
|
-
|
|
282
|
+
|| (alreadyActive
|
|
283
|
+
? walletObservedActiveToReadyMs === undefined
|
|
284
|
+
: walletClientSecretToReadyMs === undefined
|
|
285
|
+
|| walletMountToRequestMs === undefined
|
|
286
|
+
|| walletRequestMs === undefined)) {
|
|
218
287
|
return null;
|
|
219
288
|
}
|
|
220
|
-
const
|
|
289
|
+
const { walletBrowserFamily, walletOsFamily, visibilityState: walletVisibilityAtReady } = getWalletClientContext();
|
|
290
|
+
const eventId = `wallet-ready:${checkoutSessionId}:${paymentMethod}`;
|
|
291
|
+
const analyticsEventId = publicAnalyticsSdk.track({
|
|
221
292
|
environment: input.analytics.environment,
|
|
222
|
-
eventId
|
|
293
|
+
eventId,
|
|
223
294
|
eventType: 'checkout_wallet_ready',
|
|
224
295
|
featureFlags: input.analytics.featureFlags,
|
|
225
|
-
metadata: Object.assign(Object.assign({}, ((
|
|
296
|
+
metadata: Object.assign(Object.assign({}, ((_j = input.analytics.metadata) !== null && _j !== void 0 ? _j : {})), { checkout_session_id: checkoutSessionId, payment_method: paymentMethod, payment_provider: input.provider, wallet_client_secret_to_ready_ms: walletClientSecretToReadyMs, wallet_observed_active_to_ready_ms: walletObservedActiveToReadyMs, wallet_observed_active_to_element_mount_ms: walletObservedActiveToElementMountMs, wallet_timing_origin: (_k = input.walletTimingOrigin) !== null && _k !== void 0 ? _k : 'prepared_here', wallet_client_secret_to_element_mount_ms: walletClientSecretToElementMountMs, wallet_element_mount_to_ready_ms: walletElementMountToReadyMs, wallet_element_retry_count: walletElementRetryCount, wallet_stripe_promise_wait_ms: walletStripePromiseWaitMs, wallet_visibility_at_ready: walletVisibilityAtReady, wallet_browser_family: walletBrowserFamily, wallet_os_family: walletOsFamily, payments_sdk_version: PAYMENTS_PACKAGE_VERSION, wallet_mount_to_ready_ms: walletMountToReadyMs, wallet_mount_to_request_ms: walletMountToRequestMs, wallet_checkout_request_ms: walletRequestMs }),
|
|
226
297
|
stepContractVersion: input.analytics.stepContractVersion,
|
|
227
298
|
stepId: input.analytics.stepId,
|
|
228
299
|
stepName: input.analytics.stepName,
|
|
229
300
|
stepType: input.analytics.stepType,
|
|
230
301
|
});
|
|
231
|
-
if (!eventId) {
|
|
232
|
-
return null;
|
|
233
|
-
}
|
|
234
302
|
void reportCheckoutObservability({
|
|
235
303
|
analytics: input.analytics,
|
|
236
304
|
checkoutSessionId,
|
|
@@ -239,10 +307,61 @@ export const trackCheckoutWalletReady = (input) => {
|
|
|
239
307
|
paymentMethod,
|
|
240
308
|
provider: input.provider,
|
|
241
309
|
walletClientSecretToReadyMs,
|
|
310
|
+
walletObservedActiveToReadyMs,
|
|
311
|
+
walletObservedActiveToElementMountMs,
|
|
312
|
+
walletTimingOrigin: (_l = input.walletTimingOrigin) !== null && _l !== void 0 ? _l : 'prepared_here',
|
|
313
|
+
walletClientSecretToElementMountMs,
|
|
314
|
+
walletElementMountToReadyMs,
|
|
315
|
+
walletElementRetryCount,
|
|
316
|
+
walletStripePromiseWaitMs,
|
|
317
|
+
walletVisibilityAtReady,
|
|
318
|
+
walletBrowserFamily,
|
|
319
|
+
walletOsFamily,
|
|
320
|
+
paymentsSdkVersion: PAYMENTS_PACKAGE_VERSION,
|
|
242
321
|
walletMountToReadyMs,
|
|
243
322
|
walletMountToRequestMs,
|
|
244
323
|
walletRequestMs,
|
|
245
324
|
}).catch(() => undefined);
|
|
246
|
-
|
|
325
|
+
if (analyticsEventId)
|
|
326
|
+
void publicAnalyticsSdk.flush().catch(() => 0);
|
|
327
|
+
return eventId;
|
|
328
|
+
};
|
|
329
|
+
export const trackCheckoutWalletSlow = (input) => {
|
|
330
|
+
var _a;
|
|
331
|
+
const checkoutSessionId = input.checkoutSessionId.trim();
|
|
332
|
+
const paymentMethod = toAnalyticsPaymentMethod(input.paymentMethod);
|
|
333
|
+
const walletClientSecretToSlowMs = normalizeWalletTiming(input.walletClientSecretToSlowMs);
|
|
334
|
+
if (!checkoutSessionId || !paymentMethod || walletClientSecretToSlowMs === null) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const { walletBrowserFamily, walletOsFamily, visibilityState: walletVisibilityAtSlow } = getWalletClientContext();
|
|
338
|
+
const eventId = `wallet-slow:${checkoutSessionId}:${paymentMethod}`;
|
|
339
|
+
const analyticsEventId = publicAnalyticsSdk.track({
|
|
340
|
+
environment: input.analytics.environment,
|
|
341
|
+
eventId,
|
|
342
|
+
eventType: 'checkout_wallet_slow',
|
|
343
|
+
featureFlags: input.analytics.featureFlags,
|
|
344
|
+
metadata: Object.assign(Object.assign({}, ((_a = input.analytics.metadata) !== null && _a !== void 0 ? _a : {})), { checkout_session_id: checkoutSessionId, payment_method: paymentMethod, payment_provider: input.provider, wallet_timing_origin: input.walletTimingOrigin, wallet_client_secret_to_slow_ms: walletClientSecretToSlowMs, wallet_visibility_at_slow: walletVisibilityAtSlow, wallet_browser_family: walletBrowserFamily, wallet_os_family: walletOsFamily, payments_sdk_version: PAYMENTS_PACKAGE_VERSION }),
|
|
345
|
+
stepContractVersion: input.analytics.stepContractVersion,
|
|
346
|
+
stepId: input.analytics.stepId,
|
|
347
|
+
stepName: input.analytics.stepName,
|
|
348
|
+
stepType: input.analytics.stepType,
|
|
349
|
+
});
|
|
350
|
+
void reportCheckoutObservability({
|
|
351
|
+
analytics: input.analytics,
|
|
352
|
+
checkoutSessionId,
|
|
353
|
+
eventId,
|
|
354
|
+
eventType: 'checkout_wallet_slow',
|
|
355
|
+
paymentMethod,
|
|
356
|
+
provider: input.provider,
|
|
357
|
+
walletTimingOrigin: input.walletTimingOrigin,
|
|
358
|
+
walletClientSecretToSlowMs,
|
|
359
|
+
walletVisibilityAtSlow,
|
|
360
|
+
walletBrowserFamily,
|
|
361
|
+
walletOsFamily,
|
|
362
|
+
paymentsSdkVersion: PAYMENTS_PACKAGE_VERSION,
|
|
363
|
+
}).catch(() => undefined);
|
|
364
|
+
if (analyticsEventId)
|
|
365
|
+
void publicAnalyticsSdk.flush().catch(() => 0);
|
|
247
366
|
return eventId;
|
|
248
367
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnelsgrove/payments",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
|
-
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-esm-relative-imports.mjs dist",
|
|
28
|
+
"build": "node scripts/generate-package-version.mjs && rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/fix-esm-relative-imports.mjs dist",
|
|
29
29
|
"lint": "eslint .",
|
|
30
30
|
"prepublishOnly": "npm run build",
|
|
31
31
|
"smoke:wallet": "npm run build && node scripts/wallet-checkout-smoke.mjs",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"jsdom": "20.0.3",
|
|
47
47
|
"typescript": "^5",
|
|
48
48
|
"vitest": "^3.2.4",
|
|
49
|
-
"@funnelsgrove/runtime": "0.
|
|
50
|
-
"@funnelsgrove/analytics": "0.
|
|
49
|
+
"@funnelsgrove/runtime": "0.23.0",
|
|
50
|
+
"@funnelsgrove/analytics": "0.5.0"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@funnelsgrove/runtime": "^0.
|
|
54
|
-
"@funnelsgrove/analytics": "^0.
|
|
53
|
+
"@funnelsgrove/runtime": "^0.23.0",
|
|
54
|
+
"@funnelsgrove/analytics": "^0.5.0"
|
|
55
55
|
}
|
|
56
56
|
}
|