@funnelsgrove/payments 0.11.7 → 0.11.10

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.
@@ -211,14 +211,18 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
211
211
  setCommittedEmail(normalizedCustomerEmail);
212
212
  setEmailError(null);
213
213
  }, [customerEmail, normalizedCustomerEmail]);
214
- const { discountKeyOrProviderId, failUrl, funnelPlanKey, funnelEndUserId, funnelId, idempotencyKey, offerSetId, returnUrl, runtimeConfig, successUrl, } = request;
215
- const { apiBaseUrl, funnelId: runtimeFunnelId, funnelSdkPublishableKey, funnelVersionId, } = runtimeConfig || {};
214
+ const { discountKeyOrProviderId, failUrl, funnelPlanKey, paymentProfileId, providerPlanId, checkoutType, environment, funnelEndUserId, funnelId, idempotencyKey, offerSetId, returnUrl, runtimeConfig, successUrl, } = request;
215
+ const { apiBaseUrl, funnelId: runtimeFunnelId, funnelSdkPublishableKey, funnelVersionId, paymentsApiVersion, } = runtimeConfig || {};
216
216
  const hasRuntimeConfig = runtimeConfig !== undefined;
217
217
  const normalizedRequest = useMemo(() => ({
218
218
  funnelId: (funnelId === null || funnelId === void 0 ? void 0 : funnelId.trim()) || null,
219
219
  funnelEndUserId: funnelEndUserId.trim(),
220
220
  offerSetId: offerSetId.trim(),
221
221
  funnelPlanKey: funnelPlanKey.trim(),
222
+ paymentProfileId: (paymentProfileId === null || paymentProfileId === void 0 ? void 0 : paymentProfileId.trim()) || null,
223
+ providerPlanId: (providerPlanId === null || providerPlanId === void 0 ? void 0 : providerPlanId.trim()) || null,
224
+ checkoutType: checkoutType || null,
225
+ environment: environment || null,
222
226
  idempotencyKey: idempotencyKey.trim(),
223
227
  discountKeyOrProviderId: (discountKeyOrProviderId === null || discountKeyOrProviderId === void 0 ? void 0 : discountKeyOrProviderId.trim()) || null,
224
228
  returnUrl: returnUrl.trim(),
@@ -229,16 +233,22 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
229
233
  funnelId: (runtimeFunnelId === null || runtimeFunnelId === void 0 ? void 0 : runtimeFunnelId.trim()) || null,
230
234
  funnelVersionId: (funnelVersionId === null || funnelVersionId === void 0 ? void 0 : funnelVersionId.trim()) || null,
231
235
  funnelSdkPublishableKey: (funnelSdkPublishableKey === null || funnelSdkPublishableKey === void 0 ? void 0 : funnelSdkPublishableKey.trim()) || null,
236
+ paymentsApiVersion: paymentsApiVersion || null,
232
237
  } : undefined,
233
238
  }), [
234
239
  apiBaseUrl,
235
240
  discountKeyOrProviderId,
236
241
  failUrl,
237
242
  funnelPlanKey,
243
+ paymentProfileId,
244
+ providerPlanId,
245
+ checkoutType,
246
+ environment,
238
247
  funnelEndUserId,
239
248
  funnelId,
240
249
  funnelSdkPublishableKey,
241
250
  funnelVersionId,
251
+ paymentsApiVersion,
242
252
  idempotencyKey,
243
253
  offerSetId,
244
254
  returnUrl,
@@ -26,6 +26,10 @@ export type PrepareSolidgateCheckoutInput = {
26
26
  funnelEndUserId: string;
27
27
  offerSetId: string;
28
28
  funnelPlanKey: string;
29
+ paymentProfileId?: string | null;
30
+ providerPlanId?: string | null;
31
+ checkoutType?: 'subscription' | 'one_time' | null;
32
+ environment?: 'test' | 'live' | null;
29
33
  idempotencyKey: string;
30
34
  discountKeyOrProviderId?: string | null;
31
35
  returnUrl: string;
@@ -32,6 +32,18 @@ const postJson = async (path, body, runtimeConfig, signal) => {
32
32
  }
33
33
  return response.json();
34
34
  };
35
+ const getJson = async (path, runtimeConfig, signal) => {
36
+ const publishableKey = resolvePublishableKey(runtimeConfig);
37
+ const response = await fetch(resolveApiUrl(path, runtimeConfig), {
38
+ method: 'GET',
39
+ headers: Object.assign({}, (publishableKey ? { 'x-sdk-publishable-key': publishableKey } : {})),
40
+ signal,
41
+ });
42
+ if (!response.ok) {
43
+ throw new Error('Solidgate checkout is temporarily unavailable');
44
+ }
45
+ return response.json();
46
+ };
35
47
  const isRecord = (value) => (Boolean(value) && typeof value === 'object' && !Array.isArray(value));
36
48
  const parsePreparation = (value) => {
37
49
  if (!isRecord(value) || !isRecord(value.display) || !isRecord(value.initialization)) {
@@ -98,26 +110,40 @@ const parseStatus = (value) => {
98
110
  };
99
111
  };
100
112
  export const prepareSolidgateCheckout = async (input) => {
101
- var _a;
102
- return parsePreparation(await postJson('/sdk/public/payments/solidgate/checkout', {
103
- funnelId: resolveFunnelId(input.funnelId, input.runtimeConfig),
104
- funnelEndUserId: requiredString(input.funnelEndUserId, 'funnelEndUserId'),
105
- offerSetId: requiredString(input.offerSetId, 'offerSetId'),
106
- funnelPlanKey: requiredString(input.funnelPlanKey, 'funnelPlanKey'),
107
- idempotencyKey: requiredString(input.idempotencyKey, 'idempotencyKey'),
108
- discountKeyOrProviderId: ((_a = input.discountKeyOrProviderId) === null || _a === void 0 ? void 0 : _a.trim()) || null,
109
- returnUrl: requiredString(input.returnUrl, 'returnUrl'),
110
- successUrl: requiredString(input.successUrl, 'successUrl'),
111
- failUrl: requiredString(input.failUrl, 'failUrl'),
112
- }, input.runtimeConfig, input.signal));
113
+ var _a, _b, _c, _d;
114
+ const useV2 = ((_a = input.runtimeConfig) === null || _a === void 0 ? void 0 : _a.paymentsApiVersion) === 'v2';
115
+ const paymentProfileId = ((_b = input.paymentProfileId) === null || _b === void 0 ? void 0 : _b.trim()) || null;
116
+ const providerPlanId = ((_c = input.providerPlanId) === null || _c === void 0 ? void 0 : _c.trim()) || null;
117
+ const checkoutType = input.checkoutType || null;
118
+ const environment = input.environment || null;
119
+ if (useV2 && (!paymentProfileId || !providerPlanId || !checkoutType || !environment)) {
120
+ throw new Error('Solidgate V2 checkout selection is incomplete');
121
+ }
122
+ return parsePreparation(await postJson(useV2
123
+ ? '/sdk/public/v2/payments/checkout-sessions'
124
+ : '/sdk/public/payments/solidgate/checkout', Object.assign(Object.assign({}, (useV2 ? {
125
+ checkoutType,
126
+ uiMode: 'custom',
127
+ environment,
128
+ paymentProfileId,
129
+ provider: 'solidgate',
130
+ providerPlanId,
131
+ planKey: requiredString(input.funnelPlanKey, 'funnelPlanKey'),
132
+ } : {})), { funnelId: resolveFunnelId(input.funnelId, input.runtimeConfig), funnelEndUserId: requiredString(input.funnelEndUserId, 'funnelEndUserId'), offerSetId: requiredString(input.offerSetId, 'offerSetId'), funnelPlanKey: requiredString(input.funnelPlanKey, 'funnelPlanKey'), idempotencyKey: requiredString(input.idempotencyKey, 'idempotencyKey'), discountKeyOrProviderId: ((_d = input.discountKeyOrProviderId) === null || _d === void 0 ? void 0 : _d.trim()) || null, returnUrl: requiredString(input.returnUrl, 'returnUrl'), successUrl: requiredString(input.successUrl, 'successUrl'), failUrl: requiredString(input.failUrl, 'failUrl') }), input.runtimeConfig, input.signal));
113
133
  };
114
134
  export const retrieveSolidgateCheckoutStatus = async (input) => {
135
+ var _a;
115
136
  const attemptId = requiredString(input.attemptId, 'attemptId');
116
- const result = parseStatus(await postJson('/sdk/public/payments/solidgate/checkout/status', {
117
- funnelId: resolveFunnelId(input.funnelId, input.runtimeConfig),
118
- funnelEndUserId: requiredString(input.funnelEndUserId, 'funnelEndUserId'),
119
- attemptId,
120
- }, input.runtimeConfig, input.signal));
137
+ const useV2 = ((_a = input.runtimeConfig) === null || _a === void 0 ? void 0 : _a.paymentsApiVersion) === 'v2';
138
+ const funnelId = resolveFunnelId(input.funnelId, input.runtimeConfig);
139
+ const funnelEndUserId = requiredString(input.funnelEndUserId, 'funnelEndUserId');
140
+ const result = parseStatus(await (useV2
141
+ ? getJson(`/sdk/public/v2/payments/checkout-sessions/${encodeURIComponent(attemptId)}?provider=solidgate&funnelId=${encodeURIComponent(funnelId)}&funnelEndUserId=${encodeURIComponent(funnelEndUserId)}`, input.runtimeConfig, input.signal)
142
+ : postJson('/sdk/public/payments/solidgate/checkout/status', {
143
+ funnelId: resolveFunnelId(input.funnelId, input.runtimeConfig),
144
+ funnelEndUserId,
145
+ attemptId,
146
+ }, input.runtimeConfig, input.signal)));
121
147
  if (result.attemptId !== attemptId) {
122
148
  throw new Error('Solidgate checkout status returned the wrong attempt');
123
149
  }
@@ -142,7 +142,7 @@ function SharedStripeCheckoutForm({ amountCents, checkoutAnalytics, checkoutSess
142
142
  !submitting;
143
143
  const walletCheckoutAllowed = allowApplePayExpressCheckout || allowApplePayQrFallback;
144
144
  const walletFallbackDisabled = !walletCheckoutAllowed || walletAvailable === false;
145
- const setSharedError = (message, alreadyTracked = false) => {
145
+ const setSharedError = (message, alreadyTracked = false, providerError) => {
146
146
  if (message && checkoutAnalytics && !alreadyTracked) {
147
147
  trackCheckoutFailureShown({
148
148
  analytics: checkoutAnalytics,
@@ -151,6 +151,9 @@ function SharedStripeCheckoutForm({ amountCents, checkoutAnalytics, checkoutSess
151
151
  failureStage: 'card_form',
152
152
  paymentMethod: 'card',
153
153
  provider: 'stripe',
154
+ providerDeclineCode: providerError === null || providerError === void 0 ? void 0 : providerError.decline_code,
155
+ providerErrorCode: providerError === null || providerError === void 0 ? void 0 : providerError.code,
156
+ providerErrorType: providerError === null || providerError === void 0 ? void 0 : providerError.type,
154
157
  });
155
158
  }
156
159
  setError(message);
@@ -216,7 +219,7 @@ function SharedStripeCheckoutForm({ amountCents, checkoutAnalytics, checkoutSess
216
219
  return_url: returnUrl,
217
220
  });
218
221
  if (result.error) {
219
- setSharedError(result.error.message || 'Payment failed.');
222
+ setSharedError(result.error.message || 'Payment failed.', false, result.error);
220
223
  setSubmitting(false);
221
224
  return;
222
225
  }
@@ -70,6 +70,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
70
70
  ? true
71
71
  : walletAvailable;
72
72
  const [submitting, setSubmitting] = useState(false);
73
+ const [paymentElementReady, setPaymentElementReady] = useState(false);
73
74
  const [error, setError] = useState(null);
74
75
  const [billingCountry, setBillingCountry] = useState(null);
75
76
  const initialCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || '';
@@ -152,7 +153,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
152
153
  setError(null);
153
154
  onInactiveCheckoutSession === null || onInactiveCheckoutSession === void 0 ? void 0 : onInactiveCheckoutSession();
154
155
  };
155
- const setSharedError = (message, alreadyTracked = false) => {
156
+ const setSharedError = (message, alreadyTracked = false, providerError) => {
156
157
  if (isInactiveCheckoutSessionError(message)) {
157
158
  handleInactiveCheckoutSession();
158
159
  return;
@@ -165,6 +166,9 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
165
166
  failureStage: 'card_form',
166
167
  paymentMethod: 'card',
167
168
  provider: 'stripe',
169
+ providerDeclineCode: providerError === null || providerError === void 0 ? void 0 : providerError.decline_code,
170
+ providerErrorCode: providerError === null || providerError === void 0 ? void 0 : providerError.code,
171
+ providerErrorType: providerError === null || providerError === void 0 ? void 0 : providerError.type,
168
172
  });
169
173
  }
170
174
  setError(message);
@@ -260,6 +264,10 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
260
264
  : 'Payment form is not ready yet.');
261
265
  return;
262
266
  }
267
+ if (!paymentElementReady) {
268
+ setSharedError('Payment form is not ready yet.', true);
269
+ return;
270
+ }
263
271
  setSubmitting(true);
264
272
  setSharedError(null);
265
273
  try {
@@ -274,7 +282,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
274
282
  });
275
283
  if (result.type === 'error') {
276
284
  const message = result.error.message || 'Payment failed.';
277
- setSharedError(message);
285
+ setSharedError(message, false, result.error);
278
286
  setSubmitting(false);
279
287
  return;
280
288
  }
@@ -322,7 +330,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
322
330
  }
323
331
  setEmailDraft(event.target.value);
324
332
  onCustomerEmailChange === null || onCustomerEmailChange === void 0 ? void 0 : onCustomerEmailChange(event.target.value);
325
- } }) })] }), _jsx(PaymentElement, { options: paymentElementOptions, onChange: handlePaymentElementChange }), fixedBillingCountry && fixedBillingCountryLabel ? (_jsxs("label", { className: 'shared-checkout-v2-country-field', children: [_jsx("span", { className: 'shared-checkout-v2-country-label', children: "Country" }), _jsx("span", { className: 'shared-checkout-v2-country-control', children: _jsx("select", { "aria-label": 'Country', className: 'shared-checkout-v2-country-select', value: fixedBillingCountry, onChange: () => setBillingCountry(fixedBillingCountry), children: _jsx("option", { value: fixedBillingCountry, children: fixedBillingCountryLabel }) }) })] })) : null] }), visibleError ? _jsx("p", { className: 'shared-checkout-v2-error', children: visibleError }) : null, _jsxs("button", { type: 'submit', className: 'shared-checkout-v2-card-submit', disabled: submitting, children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-card-submit-icon' }), submitting ? processingLabel : cardSubmitLabel] }), _jsx("div", { className: 'shared-checkout-v2-card-brands', "aria-hidden": 'true', children: cardBrandLabels.map((label) => (_jsx(PaymentBrandMark, { label: label, className: 'shared-checkout-v2-card-brand' }, label))) }), _jsxs("p", { className: 'shared-checkout-v2-secure-pill shared-checkout-v2-secure-pill--card', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] })] })] })] }));
333
+ } }) })] }), _jsx(PaymentElement, { options: paymentElementOptions, onChange: handlePaymentElementChange, onReady: () => setPaymentElementReady(true) }), fixedBillingCountry && fixedBillingCountryLabel ? (_jsxs("label", { className: 'shared-checkout-v2-country-field', children: [_jsx("span", { className: 'shared-checkout-v2-country-label', children: "Country" }), _jsx("span", { className: 'shared-checkout-v2-country-control', children: _jsx("select", { "aria-label": 'Country', className: 'shared-checkout-v2-country-select', value: fixedBillingCountry, onChange: () => setBillingCountry(fixedBillingCountry), children: _jsx("option", { value: fixedBillingCountry, children: fixedBillingCountryLabel }) }) })] })) : null] }), visibleError ? _jsx("p", { className: 'shared-checkout-v2-error', children: visibleError }) : null, _jsxs("button", { type: 'submit', className: 'shared-checkout-v2-card-submit', disabled: submitting || !paymentElementReady, children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-card-submit-icon' }), submitting ? processingLabel : cardSubmitLabel] }), _jsx("div", { className: 'shared-checkout-v2-card-brands', "aria-hidden": 'true', children: cardBrandLabels.map((label) => (_jsx(PaymentBrandMark, { label: label, className: 'shared-checkout-v2-card-brand' }, label))) }), _jsxs("p", { className: 'shared-checkout-v2-secure-pill shared-checkout-v2-secure-pill--card', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] })] })] })] }));
326
334
  }
327
335
  function CheckoutSessionProvider({ children, clientSecret, fixedBillingCountry, stripePromise, variant, }) {
328
336
  const providerChildren = children;
@@ -120,7 +120,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
120
120
  checkoutStartSource,
121
121
  paymentMethod }));
122
122
  };
123
- const reportFailure = (message, failureStage, paymentMethod) => {
123
+ const reportFailure = (message, failureStage, paymentMethod, providerError) => {
124
124
  if (checkoutAnalytics) {
125
125
  trackCheckoutFailureShown({
126
126
  analytics: checkoutAnalytics,
@@ -129,6 +129,9 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
129
129
  failureStage,
130
130
  paymentMethod,
131
131
  provider: 'stripe',
132
+ providerDeclineCode: providerError === null || providerError === void 0 ? void 0 : providerError.decline_code,
133
+ providerErrorCode: providerError === null || providerError === void 0 ? void 0 : providerError.code,
134
+ providerErrorType: providerError === null || providerError === void 0 ? void 0 : providerError.type,
132
135
  });
133
136
  }
134
137
  onError === null || onError === void 0 ? void 0 : onError(message);
@@ -202,7 +205,7 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, checkoutAna
202
205
  : {})), { expressCheckoutConfirmEvent: event, redirect: 'if_required' }));
203
206
  if (result.type === 'error') {
204
207
  const message = result.error.message || 'Wallet checkout failed.';
205
- reportFailure(message, 'wallet_confirmation', event.expressPaymentType);
208
+ reportFailure(message, 'wallet_confirmation', event.expressPaymentType, result.error);
206
209
  event.paymentFailed({ reason: 'fail', message });
207
210
  return;
208
211
  }
@@ -70,7 +70,7 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checko
70
70
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
71
71
  return Object.assign(Object.assign(Object.assign({}, baseOptions), (options !== null && options !== void 0 ? options : {})), { buttonTheme: Object.assign(Object.assign({}, ((_a = baseOptions.buttonTheme) !== null && _a !== void 0 ? _a : {})), ((_b = options === null || options === void 0 ? void 0 : options.buttonTheme) !== null && _b !== void 0 ? _b : {})), buttonType: Object.assign(Object.assign({}, ((_c = baseOptions.buttonType) !== null && _c !== void 0 ? _c : {})), ((_d = options === null || options === void 0 ? void 0 : options.buttonType) !== null && _d !== void 0 ? _d : {})), layout: Object.assign(Object.assign({}, ((_e = baseOptions.layout) !== null && _e !== void 0 ? _e : {})), ((_f = options === null || options === void 0 ? void 0 : options.layout) !== null && _f !== void 0 ? _f : {})), paymentMethods: Object.assign(Object.assign({}, ((_g = baseOptions.paymentMethods) !== null && _g !== void 0 ? _g : {})), ((_h = options === null || options === void 0 ? void 0 : options.paymentMethods) !== null && _h !== void 0 ? _h : {})), lineItems: (_j = options === null || options === void 0 ? void 0 : options.lineItems) !== null && _j !== void 0 ? _j : baseOptions.lineItems, paymentMethodOrder: (_k = options === null || options === void 0 ? void 0 : options.paymentMethodOrder) !== null && _k !== void 0 ? _k : baseOptions.paymentMethodOrder });
72
72
  }, [baseOptions, options]);
73
- const reportFailure = (message, failureStage, paymentMethod) => {
73
+ const reportFailure = (message, failureStage, paymentMethod, providerError) => {
74
74
  if (checkoutAnalytics) {
75
75
  trackCheckoutFailureShown({
76
76
  analytics: checkoutAnalytics,
@@ -79,6 +79,9 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checko
79
79
  failureStage,
80
80
  paymentMethod,
81
81
  provider: 'stripe',
82
+ providerDeclineCode: providerError === null || providerError === void 0 ? void 0 : providerError.decline_code,
83
+ providerErrorCode: providerError === null || providerError === void 0 ? void 0 : providerError.code,
84
+ providerErrorType: providerError === null || providerError === void 0 ? void 0 : providerError.type,
82
85
  });
83
86
  }
84
87
  onError === null || onError === void 0 ? void 0 : onError(message);
@@ -150,7 +153,7 @@ export function StripeExpressCheckoutButton({ amountCents, beforeConfirm, checko
150
153
  });
151
154
  if (result.error) {
152
155
  const message = result.error.message || 'Wallet checkout failed.';
153
- reportFailure(message, 'wallet_confirmation', event.expressPaymentType);
156
+ reportFailure(message, 'wallet_confirmation', event.expressPaymentType, result.error);
154
157
  event.paymentFailed({ reason: 'fail', message });
155
158
  return;
156
159
  }
@@ -25,6 +25,9 @@ type CheckoutObservabilityInput = {
25
25
  type CheckoutFailureShownInput = CheckoutObservabilityInput & {
26
26
  errorCode?: string | null;
27
27
  errorMessage: string;
28
+ providerDeclineCode?: string | null;
29
+ providerErrorCode?: string | null;
30
+ providerErrorType?: string | null;
28
31
  };
29
32
  export declare const trackCheckoutFailureShown: (input: CheckoutFailureShownInput) => string | null;
30
33
  export declare const trackCheckoutCanceled: (input: CheckoutObservabilityInput) => string | null;
@@ -26,6 +26,18 @@ export const resolveCheckoutObservabilityAnalyticsContext = (input) => {
26
26
  stepContractVersion: (_x = (_w = input.analytics) === null || _w === void 0 ? void 0 : _w.stepContractVersion) !== null && _x !== void 0 ? _x : validStepContractVersion,
27
27
  };
28
28
  };
29
+ const STRIPE_PAYMENT_REJECTION_CODES = new Set([
30
+ 'card_declined',
31
+ 'expired_card',
32
+ 'incorrect_cvc',
33
+ 'incorrect_number',
34
+ 'incorrect_zip',
35
+ 'insufficient_funds',
36
+ 'invalid_cvc',
37
+ 'invalid_expiry_month',
38
+ 'invalid_expiry_year',
39
+ 'payment_intent_authentication_failure',
40
+ ]);
29
41
  const sanitizeErrorMessage = (value) => value
30
42
  .replace(/[\r\n\t]+/g, ' ')
31
43
  .replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, '[redacted-email]')
@@ -63,6 +75,9 @@ const reportCheckoutObservability = (input) => {
63
75
  funnelVersionId: ((_g = runtimeConfig === null || runtimeConfig === void 0 ? void 0 : runtimeConfig.funnelVersionId) === null || _g === void 0 ? void 0 : _g.trim()) || FUNNEL_VERSION_ID.trim() || undefined,
64
76
  paymentMethod: input.paymentMethod || undefined,
65
77
  provider: input.provider,
78
+ providerDeclineCode: input.providerDeclineCode,
79
+ providerErrorCode: input.providerErrorCode,
80
+ providerErrorType: input.providerErrorType,
66
81
  }),
67
82
  keepalive: true,
68
83
  }).then(() => undefined);
@@ -83,7 +98,7 @@ const resolveErrorCode = (message, requestedCode) => {
83
98
  const emitCheckoutObservability = (eventType, input, failure) => {
84
99
  var _a, _b;
85
100
  const paymentMethod = toAnalyticsPaymentMethod(input.paymentMethod);
86
- 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 });
101
+ 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, 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 });
87
102
  const eventId = publicAnalyticsSdk.track({
88
103
  eventType,
89
104
  environment: input.analytics.environment,
@@ -107,18 +122,33 @@ const emitCheckoutObservability = (eventType, input, failure) => {
107
122
  failureStage: input.failureStage,
108
123
  paymentMethod,
109
124
  provider: input.provider,
125
+ providerDeclineCode: failure === null || failure === void 0 ? void 0 : failure.providerDeclineCode,
126
+ providerErrorCode: failure === null || failure === void 0 ? void 0 : failure.providerErrorCode,
127
+ providerErrorType: failure === null || failure === void 0 ? void 0 : failure.providerErrorType,
110
128
  }).catch(() => undefined);
111
129
  void publicAnalyticsSdk.flush().catch(() => 0);
112
130
  return eventId;
113
131
  };
114
132
  export const trackCheckoutFailureShown = (input) => {
133
+ var _a, _b, _c;
115
134
  const errorMessage = sanitizeErrorMessage(input.errorMessage);
116
135
  if (!errorMessage) {
117
136
  return null;
118
137
  }
119
- return emitCheckoutObservability('checkout_failure_shown', input, {
120
- errorCode: resolveErrorCode(errorMessage, input.errorCode),
138
+ const providerDeclineCode = (_a = asNonEmptyString(input.providerDeclineCode)) === null || _a === void 0 ? void 0 : _a.slice(0, 120);
139
+ const providerErrorCode = (_b = asNonEmptyString(input.providerErrorCode)) === null || _b === void 0 ? void 0 : _b.slice(0, 120);
140
+ const providerErrorType = (_c = asNonEmptyString(input.providerErrorType)) === null || _c === void 0 ? void 0 : _c.slice(0, 120);
141
+ const paymentRejected = input.provider === 'stripe' && (Boolean(providerDeclineCode)
142
+ || providerErrorType === 'card_error'
143
+ || (providerErrorCode ? STRIPE_PAYMENT_REJECTION_CODES.has(providerErrorCode) : false));
144
+ return emitCheckoutObservability(paymentRejected ? 'checkout_payment_rejected' : 'checkout_failure_shown', input, {
145
+ errorCode: paymentRejected
146
+ ? providerDeclineCode || providerErrorCode || 'payment_rejected'
147
+ : resolveErrorCode(errorMessage, input.errorCode),
121
148
  errorMessage,
149
+ providerDeclineCode,
150
+ providerErrorCode,
151
+ providerErrorType,
122
152
  });
123
153
  };
124
154
  export const trackCheckoutCanceled = (input) => emitCheckoutObservability('checkout_canceled', input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.11.7",
3
+ "version": "0.11.10",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",