@funnelsgrove/payments 0.1.7 → 0.1.9

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.
@@ -12,31 +12,12 @@ var __rest = (this && this.__rest) || function (s, e) {
12
12
  };
13
13
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
14
  import { useEffect, useMemo, useState, } from 'react';
15
- import { CardCvcElement, CardExpiryElement, CardNumberElement, Elements, useElements, useStripe, } from '@stripe/react-stripe-js';
15
+ import { CheckoutProvider, PaymentElement, useCheckout, } from '@stripe/react-stripe-js/checkout';
16
16
  import { PaymentBrandMark, SecurityLockIcon } from './CheckoutBrandAssets.js';
17
- import { StripeExpressCheckoutButton } from './StripeExpressCheckoutButton.js';
17
+ import { StripeCheckoutExpressCheckoutButton } from './StripeCheckoutExpressCheckoutButton.js';
18
18
  import { getStripeWalletPaymentMethodOrder, usePlatformWalletPaymentMethods, } from './walletPlatform.js';
19
19
  const defaultPaymentMethodLabels = ['Visa', 'Mastercard', 'Maestro', 'Discover'];
20
20
  const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
21
- const normalizeBillingName = (input) => {
22
- var _a, _b, _c, _d;
23
- const normalizedName = ((_a = input.name) === null || _a === void 0 ? void 0 : _a.trim()) || ((_b = input.fallback) === null || _b === void 0 ? void 0 : _b.trim());
24
- if (normalizedName) {
25
- return normalizedName;
26
- }
27
- const normalizedEmail = (_c = input.email) === null || _c === void 0 ? void 0 : _c.trim();
28
- if (normalizedEmail) {
29
- const localPart = (_d = normalizedEmail
30
- .split('@')[0]) === null || _d === void 0 ? void 0 : _d.replace(/[._-]+/g, ' ').trim();
31
- if (localPart) {
32
- return localPart;
33
- }
34
- }
35
- return 'Customer';
36
- };
37
- const isSuccessfulIntentStatus = (status) => {
38
- return status === 'succeeded' || status === 'processing' || status === 'requires_capture';
39
- };
40
21
  const formatCountdown = (remainingSeconds) => {
41
22
  const normalizedSeconds = Number.isFinite(remainingSeconds)
42
23
  ? Math.max(0, Math.floor(remainingSeconds))
@@ -45,43 +26,13 @@ const formatCountdown = (remainingSeconds) => {
45
26
  const seconds = normalizedSeconds % 60;
46
27
  return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
47
28
  };
48
- const stripeCardElementStyle = {
49
- base: {
50
- color: '#303136',
51
- fontFamily: 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
52
- fontSize: '18px',
53
- fontSmoothing: 'antialiased',
54
- fontWeight: '500',
55
- '::placeholder': {
56
- color: '#8c909a',
57
- },
58
- },
59
- invalid: {
60
- color: '#ff2544',
61
- iconColor: '#ff2544',
62
- },
63
- };
64
- const cardNumberElementOptions = {
65
- placeholder: 'Credit or Debit Card Number',
66
- showIcon: false,
67
- style: stripeCardElementStyle,
68
- };
69
- const cardExpiryElementOptions = {
70
- placeholder: 'Expiry Date',
71
- style: stripeCardElementStyle,
72
- };
73
- const cardCvcElementOptions = {
74
- placeholder: 'CVV/CVC',
75
- style: stripeCardElementStyle,
76
- };
77
29
  function getFriendlyCardErrorMessage(error) {
78
30
  return error instanceof Error && error.message
79
31
  ? error.message
80
32
  : 'Payment failed. Please review your card details and try again.';
81
33
  }
82
- function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel, clientSecret, closeAriaLabel, countdownLabel, customerEmail, customerEmailEditable = false, customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmailInvalidMessage = 'Enter a valid email address.', customerName, discountAmountLabel, discountLabel, discountPercent, initialWalletAvailable, nameOnCardPlaceholder = 'Name on card', onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, promoActive, processingLabel, promoCode, promoCodeLabel, remainingSeconds, returnUrl, savedLabel, secureLabel, satisfactionLabel, satisfactionPercent, summaryLabel, themeColor, title, totalLabel, totalValue, walletButtonLabel, originalPriceLabel, originalPriceValue, }) {
83
- const stripe = useStripe();
84
- const elements = useElements();
34
+ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel, closeAriaLabel, countdownLabel, customerEmail, customerEmailEditable = false, customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmailInvalidMessage = 'Enter a valid email address.', customerName, discountAmountLabel, discountLabel, discountPercent, initialWalletAvailable, nameOnCardPlaceholder = 'Name on card', onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, promoActive, processingLabel, promoCode, promoCodeLabel, remainingSeconds, returnUrl, savedLabel, secureLabel, satisfactionLabel, satisfactionPercent, summaryLabel, themeColor, title, totalLabel, totalValue, walletButtonLabel, originalPriceLabel, originalPriceValue, }) {
35
+ const checkoutState = useCheckout();
85
36
  const walletPaymentMethods = usePlatformWalletPaymentMethods();
86
37
  const [selectedMethod, setSelectedMethod] = useState('wallet');
87
38
  const [walletAvailable, setWalletAvailable] = useState(initialWalletAvailable !== null && initialWalletAvailable !== void 0 ? initialWalletAvailable : null);
@@ -90,9 +41,6 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
90
41
  : walletAvailable;
91
42
  const [submitting, setSubmitting] = useState(false);
92
43
  const [error, setError] = useState(null);
93
- const [cardNumberError, setCardNumberError] = useState(null);
94
- const [cardExpiryError, setCardExpiryError] = useState(null);
95
- const [cardCvcError, setCardCvcError] = useState(null);
96
44
  const [emailDraft, setEmailDraft] = useState((customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || '');
97
45
  const [nameOnCard, setNameOnCard] = useState((customerName === null || customerName === void 0 ? void 0 : customerName.trim()) || '');
98
46
  const cardBrandLabels = useMemo(() => paymentMethodLabels.length > 0 ? paymentMethodLabels : defaultPaymentMethodLabels, [paymentMethodLabels]);
@@ -124,7 +72,6 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
124
72
  applePay: 'subscribe',
125
73
  googlePay: 'subscribe',
126
74
  },
127
- emailRequired: true,
128
75
  layout: {
129
76
  maxColumns: 1,
130
77
  maxRows: 2,
@@ -141,6 +88,16 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
141
88
  },
142
89
  };
143
90
  }, [walletPaymentMethods]);
91
+ const paymentElementOptions = useMemo(() => {
92
+ return {
93
+ layout: 'tabs',
94
+ wallets: {
95
+ applePay: 'never',
96
+ googlePay: 'never',
97
+ link: 'auto',
98
+ },
99
+ };
100
+ }, []);
144
101
  const setSharedError = (message) => {
145
102
  setError(message);
146
103
  onError === null || onError === void 0 ? void 0 : onError(message);
@@ -183,28 +140,15 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
183
140
  onCustomerEmailChange === null || onCustomerEmailChange === void 0 ? void 0 : onCustomerEmailChange(nextEmail);
184
141
  return nextEmail;
185
142
  };
186
- const handleFieldError = (event, setFieldError) => {
187
- var _a, _b, _c;
188
- setFieldError((_b = (_a = event.error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : null);
189
- if ((_c = event.error) === null || _c === void 0 ? void 0 : _c.message) {
190
- setSharedError(event.error.message);
191
- return;
192
- }
193
- setSharedError(null);
194
- };
195
143
  const handleSubmit = async (event) => {
196
- var _a;
197
144
  event.preventDefault();
198
145
  if (submitting) {
199
146
  return;
200
147
  }
201
- if (!stripe || !elements) {
202
- setSharedError('Payment form is not ready yet.');
203
- return;
204
- }
205
- const cardNumberElement = elements.getElement(CardNumberElement);
206
- if (!cardNumberElement) {
207
- setSharedError('Payment form is not ready yet.');
148
+ if (checkoutState.type !== 'success') {
149
+ setSharedError(checkoutState.type === 'error'
150
+ ? checkoutState.error.message
151
+ : 'Payment form is not ready yet.');
208
152
  return;
209
153
  }
210
154
  setSubmitting(true);
@@ -215,27 +159,17 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
215
159
  setSubmitting(false);
216
160
  return;
217
161
  }
218
- const result = await stripe.confirmCardPayment(clientSecret, {
219
- payment_method: {
220
- card: cardNumberElement,
221
- billing_details: {
222
- email: syncedEmail || undefined,
223
- name: normalizeBillingName({
224
- email: syncedEmail,
225
- name: customerName,
226
- fallback: nameOnCard,
227
- }),
228
- },
229
- },
230
- return_url: returnUrl,
162
+ const result = await checkoutState.checkout.confirm({
163
+ email: syncedEmail || undefined,
164
+ redirect: 'if_required',
165
+ returnUrl,
231
166
  });
232
- if (result.error) {
167
+ if (result.type === 'error') {
233
168
  setSharedError(result.error.message || 'Payment failed.');
234
169
  setSubmitting(false);
235
170
  return;
236
171
  }
237
- if (typeof window !== 'undefined' &&
238
- isSuccessfulIntentStatus((_a = result.paymentIntent) === null || _a === void 0 ? void 0 : _a.status)) {
172
+ if (typeof window !== 'undefined') {
239
173
  onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess();
240
174
  window.location.assign(returnUrl);
241
175
  return;
@@ -247,7 +181,7 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
247
181
  setSubmitting(false);
248
182
  }
249
183
  };
250
- const visibleError = error || cardNumberError || cardExpiryError || cardCvcError;
184
+ const visibleError = error;
251
185
  return (_jsxs("div", { className: 'shared-checkout-v2-shell', style: checkoutStyle, children: [_jsxs("header", { className: 'shared-checkout-v2-header', children: [_jsx("button", { type: 'button', className: 'shared-checkout-v2-close', onClick: onClose, "aria-label": closeAriaLabel, children: _jsx("span", { "aria-hidden": 'true' }) }), _jsx("h2", { id: 'shared-checkout-v2-title', children: title })] }), _jsxs("section", { className: 'shared-checkout-v2-summary', children: [showPromo ? (_jsxs("p", { className: 'shared-checkout-v2-countdown', children: [discountPercent, "% ", countdownLabel, " ", countdownValue, " min"] })) : null, _jsxs("p", { className: 'shared-checkout-v2-satisfaction', children: [_jsx("strong", { children: satisfactionPercent }), " ", satisfactionLabel] }), showPromo ? (_jsxs("div", { className: 'shared-checkout-v2-price-stack', children: [_jsxs("div", { className: 'shared-checkout-v2-price-row is-muted', children: [_jsx("span", { children: originalPriceLabel }), _jsx("span", { className: 'shared-checkout-v2-strike', children: originalPriceValue })] }), _jsxs("div", { className: 'shared-checkout-v2-price-row is-discount', children: [_jsx("strong", { children: discountLabel }), _jsx("strong", { children: discountAmountLabel })] }), _jsxs("div", { className: 'shared-checkout-v2-promo', children: [_jsx("span", { children: promoCodeLabel }), _jsx("strong", { children: normalizedPromoCode })] })] })) : null, _jsxs("div", { className: [
252
186
  'shared-checkout-v2-total',
253
187
  showPromo ? '' : 'is-plain',
@@ -273,7 +207,7 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
273
207
  effectiveSelectedMethod === 'wallet' ? 'is-visible' : '',
274
208
  ]
275
209
  .filter(Boolean)
276
- .join(' '), children: [_jsxs("p", { className: 'shared-checkout-v2-secure-pill', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] }), _jsxs("div", { className: 'shared-checkout-v2-wallet-shell', "aria-label": walletAriaLabel, children: [_jsx(StripeExpressCheckoutButton, { amountCents: amountCents, availabilityPaymentMethods: walletPaymentMethods, beforeConfirm: ensureCustomerEmail, className: 'shared-checkout-v2-express-buttons', customerEmail: resolvedCustomerEmail, customerName: customerName, initialAvailable: initialWalletAvailable, keepInitialAvailableOnReady: initialWalletAvailable === true, onAvailabilityChange: handleWalletAvailabilityChange, onError: setSharedError, onSuccess: onSuccess, options: expressCheckoutOptions, returnUrl: returnUrl, summaryLabel: expressCheckoutSummaryLabel }), _jsx("span", { className: 'shared-checkout-v2-sr-only', children: walletAriaLabel })] })] }), _jsxs("div", { hidden: effectiveSelectedMethod !== 'card', className: [
210
+ .join(' '), children: [_jsxs("p", { className: 'shared-checkout-v2-secure-pill', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] }), _jsxs("div", { className: 'shared-checkout-v2-wallet-shell', "aria-label": walletAriaLabel, children: [_jsx(StripeCheckoutExpressCheckoutButton, { amountCents: amountCents, availabilityPaymentMethods: walletPaymentMethods, beforeConfirm: ensureCustomerEmail, className: 'shared-checkout-v2-express-buttons', customerEmail: resolvedCustomerEmail, customerName: customerName, initialAvailable: initialWalletAvailable, keepInitialAvailableOnReady: initialWalletAvailable === true, onAvailabilityChange: handleWalletAvailabilityChange, onError: setSharedError, onSuccess: onSuccess, options: expressCheckoutOptions, returnUrl: returnUrl, summaryLabel: expressCheckoutSummaryLabel }), _jsx("span", { className: 'shared-checkout-v2-sr-only', children: walletAriaLabel })] })] }), _jsxs("div", { hidden: effectiveSelectedMethod !== 'card', className: [
277
211
  'shared-checkout-v2-card-panel',
278
212
  effectiveSelectedMethod === 'card' ? 'is-visible' : '',
279
213
  ]
@@ -281,13 +215,7 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
281
215
  .join(' '), children: [customerEmailEditable ? (_jsxs("label", { className: 'shared-checkout-v2-email-field', children: [_jsx("span", { children: customerEmailLabel }), _jsx("input", { type: 'email', autoComplete: 'email', className: 'shared-checkout-v2-email-input', inputMode: 'email', value: emailDraft, placeholder: customerEmailPlaceholder, onChange: (event) => {
282
216
  setEmailDraft(event.target.value);
283
217
  onCustomerEmailChange === null || onCustomerEmailChange === void 0 ? void 0 : onCustomerEmailChange(event.target.value);
284
- } })] })) : null, _jsxs("div", { className: 'shared-checkout-v2-card-box', children: [_jsx("div", { className: 'shared-checkout-v2-card-number', children: _jsx(CardNumberElement, { options: cardNumberElementOptions, onChange: (event) => {
285
- handleFieldError(event, setCardNumberError);
286
- } }) }), _jsxs("div", { className: 'shared-checkout-v2-card-row', children: [_jsx("div", { className: 'shared-checkout-v2-card-half', children: _jsx(CardExpiryElement, { options: cardExpiryElementOptions, onChange: (event) => {
287
- handleFieldError(event, setCardExpiryError);
288
- } }) }), _jsxs("div", { className: 'shared-checkout-v2-card-half shared-checkout-v2-card-half--cvc', children: [_jsx(CardCvcElement, { options: cardCvcElementOptions, onChange: (event) => {
289
- handleFieldError(event, setCardCvcError);
290
- } }), _jsx("span", { className: 'shared-checkout-v2-cvc-info', "aria-hidden": 'true', children: "i" })] })] })] }), _jsx("input", { type: 'text', autoComplete: 'cc-name', className: 'shared-checkout-v2-name-input', value: nameOnCard, placeholder: nameOnCardPlaceholder, onChange: (event) => setNameOnCard(event.target.value) }), 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', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] })] })] })] }));
218
+ } })] })) : null, _jsx("div", { className: 'shared-checkout-v2-card-box', children: _jsx(PaymentElement, { options: paymentElementOptions }) }), _jsx("input", { type: 'text', autoComplete: 'cc-name', className: 'shared-checkout-v2-name-input', value: nameOnCard, placeholder: nameOnCardPlaceholder, onChange: (event) => setNameOnCard(event.target.value) }), 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', children: [_jsx(SecurityLockIcon, { className: 'shared-checkout-v2-secure-pill-icon' }), secureLabel] })] })] })] }));
291
219
  }
292
220
  export function SharedStripeCheckoutV2Dialog(_a) {
293
221
  var { clientSecret, onClose, stripePromise } = _a, props = __rest(_a, ["clientSecret", "onClose", "stripePromise"]);
@@ -300,7 +228,7 @@ export function SharedStripeCheckoutV2Dialog(_a) {
300
228
  window.addEventListener('keydown', handleKeyDown);
301
229
  return () => window.removeEventListener('keydown', handleKeyDown);
302
230
  }, [onClose]);
303
- return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'shared-checkout-v2-overlay', children: _jsx("div", { className: 'shared-checkout-v2-dialog', role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'shared-checkout-v2-title', children: _jsx(Elements, { stripe: stripePromise, options: {
231
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'shared-checkout-v2-overlay', children: _jsx("div", { className: 'shared-checkout-v2-dialog', role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'shared-checkout-v2-title', children: _jsx(CheckoutProvider, { stripe: stripePromise, options: {
304
232
  clientSecret,
305
233
  }, children: _jsx(SharedStripeCheckoutV2Form, Object.assign({}, props, { clientSecret: clientSecret, onClose: onClose })) }) }) }), _jsx("style", { children: sharedStripeCheckoutV2Styles })] }));
306
234
  }
@@ -0,0 +1,18 @@
1
+ import type { AvailablePaymentMethods, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
2
+ export type StripeCheckoutExpressCheckoutButtonProps = {
3
+ amountCents: number;
4
+ beforeConfirm?: () => Promise<string | null>;
5
+ summaryLabel: string;
6
+ returnUrl: string;
7
+ customerEmail?: string | null;
8
+ customerName?: string | null;
9
+ className?: string;
10
+ options?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
11
+ availabilityPaymentMethods?: readonly (keyof AvailablePaymentMethods)[];
12
+ initialAvailable?: boolean | null;
13
+ keepInitialAvailableOnReady?: boolean;
14
+ onAvailabilityChange?: (available: boolean) => void;
15
+ onError?: (message: string | null) => void;
16
+ onSuccess?: () => void;
17
+ };
18
+ export declare function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, customerEmail, className, options, availabilityPaymentMethods, initialAvailable, keepInitialAvailableOnReady, onAvailabilityChange, onError, onSuccess, }: StripeCheckoutExpressCheckoutButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,116 @@
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo, useState } from 'react';
4
+ import { ExpressCheckoutElement, useCheckout, } from '@stripe/react-stripe-js/checkout';
5
+ const buildDefaultOptions = () => ({
6
+ buttonHeight: 55,
7
+ buttonTheme: {
8
+ applePay: 'black',
9
+ },
10
+ buttonType: {
11
+ applePay: 'subscribe',
12
+ },
13
+ layout: {
14
+ maxColumns: 1,
15
+ maxRows: 1,
16
+ overflow: 'auto',
17
+ },
18
+ paymentMethodOrder: ['apple_pay'],
19
+ paymentMethods: {
20
+ amazonPay: 'never',
21
+ applePay: 'always',
22
+ googlePay: 'never',
23
+ klarna: 'never',
24
+ link: 'never',
25
+ paypal: 'never',
26
+ },
27
+ });
28
+ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl, customerEmail, className, options, availabilityPaymentMethods = ['applePay'], initialAvailable, keepInitialAvailableOnReady = false, onAvailabilityChange, onError, onSuccess, }) {
29
+ const checkoutState = useCheckout();
30
+ const [walletAvailable, setWalletAvailable] = useState(initialAvailable !== null && initialAvailable !== void 0 ? initialAvailable : null);
31
+ const effectiveWalletAvailable = initialAvailable === true && walletAvailable !== false
32
+ ? true
33
+ : walletAvailable;
34
+ const baseOptions = useMemo(() => buildDefaultOptions(), []);
35
+ const resolvedOptions = useMemo(() => {
36
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
37
+ 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 : {})), paymentMethodOrder: (_j = options === null || options === void 0 ? void 0 : options.paymentMethodOrder) !== null && _j !== void 0 ? _j : baseOptions.paymentMethodOrder });
38
+ }, [baseOptions, options]);
39
+ const handleConfirm = async (event) => {
40
+ if (checkoutState.type !== 'success') {
41
+ const message = checkoutState.type === 'error'
42
+ ? checkoutState.error.message
43
+ : 'Wallet checkout is not ready yet.';
44
+ onError === null || onError === void 0 ? void 0 : onError(message);
45
+ event.paymentFailed({ reason: 'fail', message });
46
+ return;
47
+ }
48
+ onError === null || onError === void 0 ? void 0 : onError(null);
49
+ let preparedCustomerEmail = (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || null;
50
+ if (beforeConfirm) {
51
+ try {
52
+ preparedCustomerEmail = await beforeConfirm();
53
+ }
54
+ catch (confirmError) {
55
+ const message = confirmError instanceof Error && confirmError.message
56
+ ? confirmError.message
57
+ : 'Unable to prepare wallet checkout.';
58
+ onError === null || onError === void 0 ? void 0 : onError(message);
59
+ event.paymentFailed({ reason: 'invalid_payment_data', message });
60
+ return;
61
+ }
62
+ }
63
+ if (beforeConfirm && !preparedCustomerEmail) {
64
+ const message = 'Email is required to continue.';
65
+ onError === null || onError === void 0 ? void 0 : onError(message);
66
+ event.paymentFailed({ reason: 'invalid_payment_data', message });
67
+ return;
68
+ }
69
+ const result = await checkoutState.checkout.confirm({
70
+ email: preparedCustomerEmail || (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || undefined,
71
+ expressCheckoutConfirmEvent: event,
72
+ redirect: 'if_required',
73
+ returnUrl,
74
+ });
75
+ if (result.type === 'error') {
76
+ const message = result.error.message || 'Wallet checkout failed.';
77
+ onError === null || onError === void 0 ? void 0 : onError(message);
78
+ event.paymentFailed({ reason: 'fail', message });
79
+ return;
80
+ }
81
+ onError === null || onError === void 0 ? void 0 : onError(null);
82
+ if (typeof window !== 'undefined') {
83
+ onSuccess === null || onSuccess === void 0 ? void 0 : onSuccess();
84
+ window.location.assign(returnUrl);
85
+ }
86
+ };
87
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: [
88
+ 'stripe-express-checkout-button',
89
+ className !== null && className !== void 0 ? className : '',
90
+ effectiveWalletAvailable === false ? 'is-hidden' : '',
91
+ ]
92
+ .filter(Boolean)
93
+ .join(' '), children: _jsx(ExpressCheckoutElement, { options: resolvedOptions, onConfirm: (event) => void handleConfirm(event), onLoadError: ({ error }) => {
94
+ setWalletAvailable(false);
95
+ onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(false);
96
+ onError === null || onError === void 0 ? void 0 : onError(error.message || 'Unable to load wallet checkout.');
97
+ }, onReady: ({ availablePaymentMethods: availablePaymentMethodsMap }) => {
98
+ const available = Boolean(availabilityPaymentMethods.some((paymentMethod) => availablePaymentMethodsMap === null || availablePaymentMethodsMap === void 0 ? void 0 : availablePaymentMethodsMap[paymentMethod]));
99
+ const resolvedAvailable = available || (keepInitialAvailableOnReady && initialAvailable === true);
100
+ setWalletAvailable(resolvedAvailable);
101
+ onAvailabilityChange === null || onAvailabilityChange === void 0 ? void 0 : onAvailabilityChange(resolvedAvailable);
102
+ if (resolvedAvailable) {
103
+ onError === null || onError === void 0 ? void 0 : onError(null);
104
+ }
105
+ } }) }), _jsx("style", { children: stripeExpressCheckoutButtonStyles })] }));
106
+ }
107
+ const stripeExpressCheckoutButtonStyles = `
108
+ .stripe-express-checkout-button {
109
+ width: 100%;
110
+ min-height: 55px;
111
+ }
112
+
113
+ .stripe-express-checkout-button.is-hidden {
114
+ display: none;
115
+ }
116
+ `;
@@ -1,6 +1,6 @@
1
1
  import type { Appearance, Stripe } from '@stripe/stripe-js';
2
- import { type StripeExpressCheckoutButtonProps } from './StripeExpressCheckoutButton.js';
3
- export type StripeExpressCheckoutElementProps = StripeExpressCheckoutButtonProps & {
2
+ import { type StripeCheckoutExpressCheckoutButtonProps } from './StripeCheckoutExpressCheckoutButton.js';
3
+ export type StripeExpressCheckoutElementProps = StripeCheckoutExpressCheckoutButtonProps & {
4
4
  appearance?: Appearance;
5
5
  clientSecret: string;
6
6
  stripePromise: Promise<Stripe | null>;
@@ -11,9 +11,9 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  import { jsx as _jsx } from "react/jsx-runtime";
14
- import { Elements } from '@stripe/react-stripe-js';
15
- import { StripeExpressCheckoutButton, } from './StripeExpressCheckoutButton.js';
14
+ import { CheckoutProvider } from '@stripe/react-stripe-js/checkout';
15
+ import { StripeCheckoutExpressCheckoutButton, } from './StripeCheckoutExpressCheckoutButton.js';
16
16
  export function StripeExpressCheckoutElement(_a) {
17
17
  var { appearance, clientSecret, stripePromise } = _a, buttonProps = __rest(_a, ["appearance", "clientSecret", "stripePromise"]);
18
- return (_jsx(Elements, { stripe: stripePromise, options: Object.assign({ clientSecret }, (appearance ? { appearance } : {})), children: _jsx(StripeExpressCheckoutButton, Object.assign({}, buttonProps)) }));
18
+ return (_jsx(CheckoutProvider, { stripe: stripePromise, options: Object.assign({ clientSecret }, (appearance ? { elementsOptions: { appearance } } : {})), children: _jsx(StripeCheckoutExpressCheckoutButton, Object.assign({}, buttonProps)) }));
19
19
  }
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ export * from './components/shared/SharedStripeCheckoutDialog.js';
15
15
  export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
16
16
  export * from './components/shared/ApplePaySubscribeButton.js';
17
17
  export * from './components/shared/GooglePaySubscribeButton.js';
18
+ export * from './components/shared/StripeCheckoutExpressCheckoutButton.js';
18
19
  export * from './components/shared/StripeExpressCheckoutButton.js';
19
20
  export * from './components/shared/StripeExpressCheckoutElement.js';
20
21
  export * from './components/shared/StripePlanSelector.js';
package/dist/index.js CHANGED
@@ -15,6 +15,7 @@ export * from './components/shared/SharedStripeCheckoutDialog.js';
15
15
  export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
16
16
  export * from './components/shared/ApplePaySubscribeButton.js';
17
17
  export * from './components/shared/GooglePaySubscribeButton.js';
18
+ export * from './components/shared/StripeCheckoutExpressCheckoutButton.js';
18
19
  export * from './components/shared/StripeExpressCheckoutButton.js';
19
20
  export * from './components/shared/StripeExpressCheckoutElement.js';
20
21
  export * from './components/shared/StripePlanSelector.js';
@@ -1,7 +1,7 @@
1
- import type { StripeExpressCheckoutElementOptions } from '@stripe/stripe-js';
1
+ import type { StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
2
2
  import { type WalletSubscriptionCheckoutSlotProps } from './WalletSubscriptionCheckoutSlot.js';
3
3
  export type GooglePaySubscriptionCheckoutSlotProps = Omit<WalletSubscriptionCheckoutSlotProps, 'availabilityPaymentMethod' | 'options' | 'placeholderLabel' | 'renderPlaceholder' | 'renderPreparingPlaceholder'> & {
4
- options?: StripeExpressCheckoutElementOptions;
4
+ options?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
5
5
  placeholderLabel?: string;
6
6
  };
7
7
  export declare function GooglePaySubscriptionCheckoutSlot({ options, placeholderLabel, ...slotProps }: GooglePaySubscriptionCheckoutSlotProps): import("react/jsx-runtime").JSX.Element;
@@ -21,7 +21,6 @@ const googlePayExpressCheckoutOptions = {
21
21
  buttonType: {
22
22
  googlePay: 'subscribe',
23
23
  },
24
- emailRequired: true,
25
24
  layout: {
26
25
  maxColumns: 1,
27
26
  maxRows: 1,
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from 'react';
2
- import type { AvailablePaymentMethods, StripeExpressCheckoutElementOptions } from '@stripe/stripe-js';
2
+ import type { AvailablePaymentMethods, StripeCheckoutExpressCheckoutElementOptions } from '@stripe/stripe-js';
3
3
  import type { StripeSubscriptionCheckoutSession } from './useStripeSubscriptionCheckoutSession.js';
4
4
  type WalletPlaceholderButtonProps = {
5
5
  className?: string;
@@ -20,7 +20,7 @@ export type WalletSubscriptionCheckoutSlotProps = {
20
20
  onError?: (message: string | null) => void;
21
21
  onSuccess?: () => void;
22
22
  onUnavailableClick?: () => Promise<void> | void;
23
- options?: StripeExpressCheckoutElementOptions;
23
+ options?: Partial<StripeCheckoutExpressCheckoutElementOptions>;
24
24
  placeholderLabel: string;
25
25
  renderPreparingPlaceholder?: (props: WalletPlaceholderButtonProps) => ReactNode;
26
26
  renderPlaceholder: (props: WalletPlaceholderButtonProps) => ReactNode;
@@ -11,6 +11,7 @@ export type StripeSubscriptionCheckoutSessionInput = {
11
11
  enabled?: boolean;
12
12
  onError?: (message: string | null) => void;
13
13
  plan?: PaywallPlan | null;
14
+ returnUrl: string;
14
15
  runtimeConfig?: StripeRuntimeConfigOverrides;
15
16
  userId?: string | null;
16
17
  };
@@ -27,4 +28,4 @@ export type StripeSubscriptionCheckoutSession = {
27
28
  setError: (message: string | null) => void;
28
29
  stripePromise: Promise<Stripe | null> | null;
29
30
  };
30
- export declare function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, customerEmail, displayPlan, enabled, onError, plan, runtimeConfig, userId, }: StripeSubscriptionCheckoutSessionInput): StripeSubscriptionCheckoutSession;
31
+ export declare function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, customerEmail, displayPlan, enabled, onError, plan, returnUrl, runtimeConfig, userId, }: StripeSubscriptionCheckoutSessionInput): StripeSubscriptionCheckoutSession;
@@ -9,7 +9,7 @@ const getFriendlyStripeCheckoutError = (error) => {
9
9
  : message;
10
10
  };
11
11
  const normalizeRuntimeConfigValue = (value) => (value === null || value === void 0 ? void 0 : value.trim()) || '';
12
- export function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, customerEmail, displayPlan, enabled = true, onError, plan, runtimeConfig, userId, }) {
12
+ export function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, customerEmail, displayPlan, enabled = true, onError, plan, returnUrl, runtimeConfig, userId, }) {
13
13
  var _a, _b, _c, _d;
14
14
  const [clientSecret, setClientSecret] = useState(null);
15
15
  const [clientSecretIntentKey, setClientSecretIntentKey] = useState(null);
@@ -35,6 +35,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, c
35
35
  (_c = displayPlan === null || displayPlan === void 0 ? void 0 : displayPlan.id) !== null && _c !== void 0 ? _c : '',
36
36
  (_d = displayPlan === null || displayPlan === void 0 ? void 0 : displayPlan.amountCents) !== null && _d !== void 0 ? _d : '',
37
37
  (couponId === null || couponId === void 0 ? void 0 : couponId.trim()) || '',
38
+ returnUrl.trim(),
38
39
  ].join('|');
39
40
  const activeClientSecret = clientSecretIntentKey === intentKey ? clientSecret : null;
40
41
  const stripePromise = useMemo(() => getStripePromise(checkoutMode, runtimeStripePublishableKey), [checkoutMode, runtimeStripePublishableKey]);
@@ -77,6 +78,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, c
77
78
  plan,
78
79
  couponId,
79
80
  customerEmail,
81
+ returnUrl,
80
82
  user_id: userId,
81
83
  environment: checkoutMode,
82
84
  runtimeConfig,
@@ -118,6 +120,7 @@ export function useStripeSubscriptionCheckoutSession({ checkoutMode, couponId, c
118
120
  displayPlan,
119
121
  intentKey,
120
122
  plan,
123
+ returnUrl,
121
124
  runtimeConfig,
122
125
  setError,
123
126
  userId,
@@ -68,12 +68,14 @@ type CreateSubscriptionCheckoutInput = {
68
68
  plan: CheckoutSessionPlanInput;
69
69
  couponId?: string | null;
70
70
  customerEmail?: string | null;
71
+ returnUrl: string;
71
72
  user_id?: string | null;
72
73
  environment?: RuntimeMode;
73
74
  runtimeConfig?: StripeRuntimeConfigOverrides;
74
75
  };
75
76
  type CreateSubscriptionCheckoutResponse = {
76
77
  clientSecret: string;
78
+ checkoutSessionId?: string;
77
79
  subscriptionId?: string;
78
80
  stripePublishableKey?: string;
79
81
  environment?: 'test' | 'live';
@@ -577,6 +577,7 @@ export async function createStripeSubscriptionCheckout(input) {
577
577
  billingIntervalCount: recurring.intervalCount,
578
578
  couponId: ((_c = input.couponId) === null || _c === void 0 ? void 0 : _c.trim()) || undefined,
579
579
  customerEmail: ((_d = input.customerEmail) === null || _d === void 0 ? void 0 : _d.trim()) || undefined,
580
+ returnUrl: input.returnUrl,
580
581
  user_id: ((_e = input.user_id) === null || _e === void 0 ? void 0 : _e.trim()) || undefined,
581
582
  environment: input.environment || undefined,
582
583
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "main": "./dist/index.js",