@funnelsgrove/payments 0.14.0 → 0.15.1

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.
@@ -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 (submitting) {
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
- setSubmitting(false);
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
- setSubmitting(false);
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
- setSubmitting(false);
300
+ finishSubmitting();
295
301
  }
296
302
  catch (submitError) {
297
303
  setSharedError(getFriendlyCardErrorMessage(submitError));
298
- setSubmitting(false);
304
+ finishSubmitting();
299
305
  }
300
306
  };
301
307
  const visibleError = error;
@@ -239,6 +239,7 @@ export function useStripeOneTimeCheckoutSession({ runtimeConfigRevisionId, payme
239
239
  paymentMethod: options.paymentMethod,
240
240
  provider: 'stripe',
241
241
  providerErrorCode: failure.providerErrorCode,
242
+ checkoutRequestId: failure.checkoutRequestId,
242
243
  });
243
244
  }
244
245
  setError(message);
@@ -259,6 +259,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
259
259
  paymentMethod: options.paymentMethod,
260
260
  provider: 'stripe',
261
261
  providerErrorCode: failure.providerErrorCode,
262
+ checkoutRequestId: failure.checkoutRequestId,
262
263
  });
263
264
  }
264
265
  setError(message);
@@ -380,6 +381,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, analyticsMe
380
381
  failureStage: 'checkout_session_update',
381
382
  provider: 'stripe',
382
383
  providerErrorCode: failure.providerErrorCode,
384
+ checkoutRequestId: failure.checkoutRequestId,
383
385
  });
384
386
  }
385
387
  setError(message);
@@ -1,4 +1,5 @@
1
1
  export type StripeCheckoutFailure = {
2
+ checkoutRequestId?: string;
2
3
  errorCode?: string;
3
4
  message: string;
4
5
  providerErrorCode?: string;
@@ -14,9 +14,8 @@ export const describeStripeCheckoutFailure = (error) => {
14
14
  : rawMessage === 'Internal server error'
15
15
  ? 'Unable to initialize checkout. Stripe settings may be missing for this funnel.'
16
16
  : rawMessage;
17
- return {
18
- errorCode,
17
+ return Object.assign(Object.assign({}, (error instanceof FunnelSdkHttpError && error.checkoutRequestId
18
+ ? { checkoutRequestId: error.checkoutRequestId } : {})), { errorCode,
19
19
  message,
20
- providerErrorCode,
21
- };
20
+ providerErrorCode });
22
21
  };
@@ -23,6 +23,7 @@ type CheckoutObservabilityInput = {
23
23
  provider: 'solidgate' | 'stripe';
24
24
  };
25
25
  type CheckoutFailureShownInput = CheckoutObservabilityInput & {
26
+ checkoutRequestId?: string;
26
27
  errorCode?: string | null;
27
28
  errorMessage: string;
28
29
  providerDeclineCode?: string | null;
@@ -96,6 +96,7 @@ const reportCheckoutObservability = (input) => {
96
96
  : buildSdkHeaders({ 'Content-Type': 'application/json' }),
97
97
  body: JSON.stringify({
98
98
  checkoutSessionId: input.checkoutSessionId || undefined,
99
+ checkoutRequestId: input.checkoutRequestId,
99
100
  environment,
100
101
  errorCode: input.errorCode,
101
102
  errorMessage: input.errorMessage,
@@ -134,7 +135,7 @@ const resolveErrorCode = (message, requestedCode) => {
134
135
  const emitCheckoutObservability = (eventType, input, failure) => {
135
136
  var _a, _b, _c, _d;
136
137
  const paymentMethod = toAnalyticsPaymentMethod(input.paymentMethod);
137
- const metadata = Object.assign(Object.assign({}, ((_a = input.analytics.metadata) !== null && _a !== void 0 ? _a : {})), { checkout_session_id: ((_b = input.checkoutSessionId) === null || _b === void 0 ? void 0 : _b.trim()) || undefined, checkout_failure_stage: input.failureStage, payment_provider: input.provider, payment_method: paymentMethod || undefined, error_code: failure === null || failure === void 0 ? void 0 : failure.errorCode, error_message: failure === null || failure === void 0 ? void 0 : failure.errorMessage, checkout_failure_category: (_c = failure === null || failure === void 0 ? void 0 : failure.failureCategory) !== null && _c !== void 0 ? _c : (eventType === 'checkout_canceled' ? 'user_canceled' : undefined), provider_decline_code: failure === null || failure === void 0 ? void 0 : failure.providerDeclineCode, provider_error_code: failure === null || failure === void 0 ? void 0 : failure.providerErrorCode, provider_error_type: failure === null || failure === void 0 ? void 0 : failure.providerErrorType });
138
+ const metadata = Object.assign(Object.assign({}, ((_a = input.analytics.metadata) !== null && _a !== void 0 ? _a : {})), { checkout_session_id: ((_b = input.checkoutSessionId) === null || _b === void 0 ? void 0 : _b.trim()) || undefined, checkout_request_id: failure === null || failure === void 0 ? void 0 : failure.checkoutRequestId, checkout_failure_stage: input.failureStage, payment_provider: input.provider, payment_method: paymentMethod || undefined, error_code: failure === null || failure === void 0 ? void 0 : failure.errorCode, error_message: failure === null || failure === void 0 ? void 0 : failure.errorMessage, checkout_failure_category: (_c = failure === null || failure === void 0 ? void 0 : failure.failureCategory) !== null && _c !== void 0 ? _c : (eventType === 'checkout_canceled' ? 'user_canceled' : undefined), provider_decline_code: failure === null || failure === void 0 ? void 0 : failure.providerDeclineCode, provider_error_code: failure === null || failure === void 0 ? void 0 : failure.providerErrorCode, provider_error_type: failure === null || failure === void 0 ? void 0 : failure.providerErrorType });
138
139
  const eventId = publicAnalyticsSdk.track({
139
140
  eventType,
140
141
  environment: input.analytics.environment,
@@ -151,6 +152,7 @@ const emitCheckoutObservability = (eventType, input, failure) => {
151
152
  void reportCheckoutObservability({
152
153
  analytics: input.analytics,
153
154
  checkoutSessionId: input.checkoutSessionId,
155
+ checkoutRequestId: failure === null || failure === void 0 ? void 0 : failure.checkoutRequestId,
154
156
  errorCode: failure === null || failure === void 0 ? void 0 : failure.errorCode,
155
157
  errorMessage: failure === null || failure === void 0 ? void 0 : failure.errorMessage,
156
158
  failureCategory: (_d = failure === null || failure === void 0 ? void 0 : failure.failureCategory) !== null && _d !== void 0 ? _d : (eventType === 'checkout_canceled' ? 'user_canceled' : undefined),
@@ -184,6 +186,9 @@ export const trackCheckoutFailureShown = (input) => {
184
186
  });
185
187
  const paymentRejected = failureCategory === 'payment_rejection';
186
188
  return emitCheckoutObservability(paymentRejected ? 'checkout_payment_rejected' : 'checkout_failure_shown', input, {
189
+ checkoutRequestId: input.checkoutRequestId
190
+ && /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(input.checkoutRequestId)
191
+ ? input.checkoutRequestId : undefined,
187
192
  errorCode: paymentRejected
188
193
  ? providerDeclineCode || providerErrorCode || 'payment_rejected'
189
194
  : resolveErrorCode(errorMessage, input.errorCode),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",
@@ -32,8 +32,8 @@
32
32
  "test:run": "vitest run --passWithNoTests"
33
33
  },
34
34
  "dependencies": {
35
- "@funnelsgrove/analytics": "^0.1.97",
36
- "@funnelsgrove/runtime": "^0.13.0",
35
+ "@funnelsgrove/analytics": "^0.1.99",
36
+ "@funnelsgrove/runtime": "^0.13.1",
37
37
  "@solidgate/react-sdk": "1.34.0",
38
38
  "@stripe/react-stripe-js": "^5.6.0",
39
39
  "@stripe/stripe-js": "^8.7.0",