@funnelsgrove/payments 0.7.19 → 0.7.21

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/README.md CHANGED
@@ -32,6 +32,10 @@ Use these for new one-time paywall plans:
32
32
  - `createStripeOneTimeCheckout`
33
33
  - `SharedStripeCheckoutV2Dialog`
34
34
 
35
+ `SharedStripeCheckoutV2Dialog` defaults to the existing full-screen `standard` surface. Pass
36
+ `variant='compact'` for a centered desktop modal with the same Stripe-owned wallet/card flow;
37
+ mobile remains full-screen and all checkout analytics and verified-success behavior are unchanged.
38
+
35
39
  Use `completeStripeOneClickPayment` for normal post-checkout one-click upsells. It delegates the charge to `funnelSdkService.chargeOneClickPayment` and owns any required Stripe confirmation. `chargeStripeOneClickPayment` remains the lower-level charge-only primitive.
36
40
 
37
41
  Solidgate browser checkout is available for controlled provider-routed funnels through
@@ -51,6 +51,7 @@ export type SharedStripeCheckoutV2DialogProps = {
51
51
  walletButtonLabel: string;
52
52
  originalPriceLabel: string;
53
53
  originalPriceValue: string;
54
+ variant?: 'standard' | 'compact';
54
55
  };
55
56
  export type SharedCheckoutSpecialOfferDialogProps = {
56
57
  buttonColor: string;
@@ -63,5 +64,5 @@ export type SharedCheckoutSpecialOfferDialogProps = {
63
64
  themeColor: string;
64
65
  title: string;
65
66
  };
66
- export declare function SharedStripeCheckoutV2Dialog({ checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, supportedCountries, ...props }: SharedStripeCheckoutV2DialogProps): import("react/jsx-runtime").JSX.Element;
67
+ export declare function SharedStripeCheckoutV2Dialog({ checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, supportedCountries, variant, ...props }: SharedStripeCheckoutV2DialogProps): import("react/jsx-runtime").JSX.Element;
67
68
  export declare function SharedCheckoutSpecialOfferDialog({ buttonColor, buttonLabel, description, discountLabel, imageAlt, imageSrc, onAccept, themeColor, title, }: SharedCheckoutSpecialOfferDialogProps): import("react/jsx-runtime").JSX.Element;
@@ -40,6 +40,7 @@ const stripeCheckoutV2Appearance = {
40
40
  },
41
41
  },
42
42
  };
43
+ const compactStripeCheckoutV2Appearance = Object.assign(Object.assign({}, stripeCheckoutV2Appearance), { variables: Object.assign(Object.assign({}, stripeCheckoutV2Appearance.variables), { borderRadius: '14px', colorPrimary: '#1687f8' }) });
43
44
  const isValidEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
44
45
  const formatCountdown = (remainingSeconds) => {
45
46
  const normalizedSeconds = Number.isFinite(remainingSeconds)
@@ -59,7 +60,7 @@ function getFriendlyCardErrorMessage(error) {
59
60
  ? error.message
60
61
  : 'Payment failed. Please review your card details and try again.';
61
62
  }
62
- function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, cardSubmitLabel, closeAriaLabel, countdownLabel, checkoutAnalytics, checkoutSessionId, customerEmail, customerEmailEditable = false, customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmailInvalidMessage = 'Enter a valid email address.', customerName, discountAmountLabel, discountLabel, discountPercent, initialWalletAvailable, onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onInactiveCheckoutSession, onPaymentInfoSubmitted, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, promoActive, processingLabel, promoCode, promoCodeLabel, remainingSeconds, returnUrl, savedLabel, secureLabel, satisfactionLabel, satisfactionPercent, summaryLabel, supportedCountries, themeColor, title, totalLabel, totalValue, unsupportedCountryMessage, walletButtonLabel, originalPriceLabel, originalPriceValue, }) {
63
+ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, cardSubmitLabel, closeAriaLabel, countdownLabel, checkoutAnalytics, checkoutSessionId, customerEmail, customerEmailEditable = false, customerEmailLabel = 'Email', customerEmailPlaceholder = 'Enter your email', customerEmailInvalidMessage = 'Enter a valid email address.', customerName, discountAmountLabel, discountLabel, discountPercent, initialWalletAvailable, onClose, onCustomerEmailChange, onCustomerEmailCommit, onError, onInactiveCheckoutSession, onPaymentInfoSubmitted, onSuccess, paymentMethodLabels = defaultPaymentMethodLabels, promoActive, processingLabel, promoCode, promoCodeLabel, remainingSeconds, returnUrl, savedLabel, secureLabel, satisfactionLabel, satisfactionPercent, summaryLabel, supportedCountries, themeColor, title, totalLabel, totalValue, unsupportedCountryMessage, walletButtonLabel, originalPriceLabel, originalPriceValue, variant = 'standard', }) {
63
64
  const checkoutState = useCheckout();
64
65
  const walletPaymentMethods = usePlatformWalletPaymentMethods();
65
66
  const [selectedMethod, setSelectedMethod] = useState('wallet');
@@ -279,7 +280,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
279
280
  }
280
281
  };
281
282
  const visibleError = error;
282
- 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: [
283
+ return (_jsxs("div", { className: 'shared-checkout-v2-shell', "data-variant": variant, 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, variant === 'standard' ? (_jsxs("p", { className: 'shared-checkout-v2-satisfaction', children: [_jsx("strong", { children: satisfactionPercent }), " ", satisfactionLabel] })) : null, 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: [
283
284
  'shared-checkout-v2-total',
284
285
  showPromo ? '' : 'is-plain',
285
286
  ]
@@ -312,7 +313,7 @@ function SharedStripeCheckoutV2CheckoutSessionForm({ amountCents, buttonColor, c
312
313
  onCustomerEmailChange === null || onCustomerEmailChange === void 0 ? void 0 : onCustomerEmailChange(event.target.value);
313
314
  } }) })] }), _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] })] })] })] }));
314
315
  }
315
- function CheckoutSessionProvider({ children, clientSecret, fixedBillingCountry, stripePromise, }) {
316
+ function CheckoutSessionProvider({ children, clientSecret, fixedBillingCountry, stripePromise, variant, }) {
316
317
  const providerChildren = children;
317
318
  const checkoutProviderOptions = useMemo(() => (Object.assign(Object.assign({ clientSecret }, (fixedBillingCountry
318
319
  ? {
@@ -325,12 +326,14 @@ function CheckoutSessionProvider({ children, clientSecret, fixedBillingCountry,
325
326
  },
326
327
  }
327
328
  : {})), { elementsOptions: {
328
- appearance: stripeCheckoutV2Appearance,
329
- } })), [clientSecret, fixedBillingCountry]);
329
+ appearance: variant === 'compact'
330
+ ? compactStripeCheckoutV2Appearance
331
+ : stripeCheckoutV2Appearance,
332
+ } })), [clientSecret, fixedBillingCountry, variant]);
330
333
  return (_jsx(CheckoutProvider, { stripe: stripePromise, options: checkoutProviderOptions, children: providerChildren }, clientSecret));
331
334
  }
332
335
  export function SharedStripeCheckoutV2Dialog(_a) {
333
- var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, supportedCountries } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise", "supportedCountries"]);
336
+ var { checkoutAnalytics, checkoutSessionId, clientSecret, onClose, stripePromise, supportedCountries, variant = 'standard' } = _a, props = __rest(_a, ["checkoutAnalytics", "checkoutSessionId", "clientSecret", "onClose", "stripePromise", "supportedCountries", "variant"]);
334
337
  const normalizedSupportedCountries = useMemo(() => normalizeSupportedCheckoutCountries(supportedCountries), [supportedCountries]);
335
338
  const fixedBillingCountry = getFixedSupportedBillingCountry(normalizedSupportedCountries);
336
339
  useEffect(() => {
@@ -348,7 +351,7 @@ export function SharedStripeCheckoutV2Dialog(_a) {
348
351
  window.addEventListener('keydown', handleKeyDown);
349
352
  return () => window.removeEventListener('keydown', handleKeyDown);
350
353
  }, [onClose]);
351
- 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(CheckoutSessionProvider, { clientSecret: clientSecret, fixedBillingCountry: fixedBillingCountry, stripePromise: stripePromise, children: _jsx(SharedStripeCheckoutV2CheckoutSessionForm, Object.assign({}, props, { checkoutAnalytics: checkoutAnalytics, checkoutSessionId: checkoutSessionId, clientSecret: clientSecret, onClose: onClose, supportedCountries: normalizedSupportedCountries })) }) }) }), _jsx("style", { children: sharedStripeCheckoutV2Styles })] }));
354
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: 'shared-checkout-v2-overlay', children: _jsx("div", { className: 'shared-checkout-v2-dialog', "data-variant": variant, role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'shared-checkout-v2-title', children: _jsx(CheckoutSessionProvider, { clientSecret: clientSecret, fixedBillingCountry: fixedBillingCountry, stripePromise: stripePromise, variant: variant, children: _jsx(SharedStripeCheckoutV2CheckoutSessionForm, Object.assign({}, props, { checkoutAnalytics: checkoutAnalytics, checkoutSessionId: checkoutSessionId, clientSecret: clientSecret, onClose: onClose, supportedCountries: normalizedSupportedCountries, variant: variant })) }) }) }), _jsx("style", { children: sharedStripeCheckoutV2Styles })] }));
352
355
  }
353
356
  function SharedCheckoutSpecialOfferGift({ discountLabel, imageAlt, imageSrc, }) {
354
357
  if (imageSrc) {
@@ -383,6 +386,68 @@ const sharedStripeCheckoutV2Styles = `
383
386
  background: #fff;
384
387
  }
385
388
 
389
+ @media (min-width: 640px) {
390
+ .shared-checkout-v2-overlay:has(.shared-checkout-v2-dialog[data-variant='compact']) {
391
+ align-items: center;
392
+ padding: 24px;
393
+ background: rgb(0 0 0 / 48%);
394
+ }
395
+
396
+ .shared-checkout-v2-dialog[data-variant='compact'] {
397
+ width: min(100%, 520px);
398
+ height: auto;
399
+ max-height: calc(100svh - 48px);
400
+ border-radius: 24px;
401
+ box-shadow: 0 24px 70px rgb(0 0 0 / 28%);
402
+ }
403
+ }
404
+
405
+ .shared-checkout-v2-shell[data-variant='compact'] {
406
+ height: auto;
407
+ max-height: min(100svh, 760px);
408
+ }
409
+
410
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-header {
411
+ min-height: 88px;
412
+ padding: 0 72px;
413
+ }
414
+
415
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-header h2 {
416
+ font-size: 27px;
417
+ }
418
+
419
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-close {
420
+ right: 22px;
421
+ left: auto;
422
+ }
423
+
424
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-summary {
425
+ padding: 0 36px;
426
+ }
427
+
428
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-total {
429
+ min-height: 82px;
430
+ border: 1px solid #ececef;
431
+ border-radius: 16px;
432
+ padding: 0 22px;
433
+ background: #fbfbfc;
434
+ }
435
+
436
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-total svg {
437
+ display: none;
438
+ }
439
+
440
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-payment {
441
+ padding-right: 36px;
442
+ padding-left: 36px;
443
+ }
444
+
445
+ .shared-checkout-v2-shell[data-variant='compact'] .shared-checkout-v2-card-submit {
446
+ border-radius: 14px;
447
+ background: var(--shared-checkout-v2-button-color);
448
+ text-transform: none;
449
+ }
450
+
386
451
  .shared-checkout-v2-shell {
387
452
  height: 100%;
388
453
  max-height: 100svh;
@@ -75,37 +75,40 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
75
75
  intentKey,
76
76
  });
77
77
  }, []);
78
+ const autoWalletClientSecret = session.activeClientSecret;
79
+ const autoWalletIntentKey = session.intentKey;
80
+ const autoWalletLoading = session.loading.wallet;
81
+ const prepareWalletCheckout = session.prepareWalletCheckout;
78
82
  useEffect(() => {
79
83
  if (slotDisabled ||
80
84
  suspended ||
81
85
  !expressCheckoutAllowed ||
82
86
  walletAvailable === false ||
83
- session.loading.wallet ||
84
- session.activeClientSecret) {
87
+ autoWalletLoading ||
88
+ autoWalletClientSecret) {
85
89
  return;
86
90
  }
87
- if (autoWalletPreparationIntentKeyRef.current === session.intentKey) {
91
+ if (autoWalletPreparationIntentKeyRef.current === autoWalletIntentKey) {
88
92
  return;
89
93
  }
90
- autoWalletPreparationIntentKeyRef.current = session.intentKey;
91
- void session
92
- .prepareWalletCheckout({
94
+ autoWalletPreparationIntentKeyRef.current = autoWalletIntentKey;
95
+ void prepareWalletCheckout({
93
96
  checkoutStartSource: 'wallet_render',
94
97
  paymentMethod: analyticsPaymentMethod,
95
98
  })
96
99
  .then((readyClientSecret) => {
97
100
  if (!readyClientSecret) {
98
- reportWalletPreparationFailure(session.intentKey);
101
+ reportWalletPreparationFailure(autoWalletIntentKey);
99
102
  }
100
103
  });
101
104
  }, [
102
105
  analyticsPaymentMethod,
106
+ autoWalletClientSecret,
107
+ autoWalletIntentKey,
108
+ autoWalletLoading,
103
109
  expressCheckoutAllowed,
110
+ prepareWalletCheckout,
104
111
  reportWalletPreparationFailure,
105
- session.activeClientSecret,
106
- session.intentKey,
107
- session.loading.wallet,
108
- session.prepareWalletCheckout,
109
112
  slotDisabled,
110
113
  suspended,
111
114
  walletAvailable,
@@ -136,7 +139,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
136
139
  intentKey: session.intentKey,
137
140
  });
138
141
  void session.restartWalletCheckout({
139
- checkoutStartSource: 'wallet_render',
142
+ checkoutStartSource: 'wallet_click',
140
143
  paymentMethod: analyticsPaymentMethod,
141
144
  });
142
145
  return;
@@ -157,7 +160,7 @@ export function WalletSubscriptionCheckoutSlot({ amountCents, appearance, availa
157
160
  }
158
161
  setWalletCheckoutStartedAfterClickIntentKey(session.intentKey);
159
162
  const readyClientSecret = await session.prepareWalletCheckout({
160
- checkoutStartSource: 'wallet_render',
163
+ checkoutStartSource: 'wallet_click',
161
164
  paymentMethod: analyticsPaymentMethod,
162
165
  });
163
166
  if (!readyClientSecret) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.7.19",
3
+ "version": "0.7.21",
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.67",
36
- "@funnelsgrove/runtime": "^0.7.19",
35
+ "@funnelsgrove/analytics": "^0.1.78",
36
+ "@funnelsgrove/runtime": "^0.7.29",
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",