@funnelsgrove/payments 0.27.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- export declare const PAYMENTS_PACKAGE_VERSION = "0.27.0";
1
+ export declare const PAYMENTS_PACKAGE_VERSION = "0.28.0";
@@ -1,2 +1,2 @@
1
1
  // Generated by scripts/generate-package-version.mjs.
2
- export const PAYMENTS_PACKAGE_VERSION = "0.27.0";
2
+ export const PAYMENTS_PACKAGE_VERSION = "0.28.0";
@@ -1,3 +1,3 @@
1
1
  import type { SolidgateCheckoutDialogProps } from './SolidgateCheckoutDialog.types.js';
2
2
  export type { SolidgateCheckoutDialogState, SolidgateCheckoutDialogContent, SolidgateCheckoutDialogPresentation, SolidgateCheckoutDialogProps, SolidgateCheckoutAnalyticsContext, } from './SolidgateCheckoutDialog.types.js';
3
- export declare function SolidgateCheckoutDialog({ open, request, customerEmail, checkoutAnalytics, content, presentation, onClose, onCustomerEmailCommit, onSuccess, onRetry, onError, onPreparationStart, onStateChange, }: SolidgateCheckoutDialogProps): import("react/jsx-runtime").JSX.Element | null;
3
+ export declare function SolidgateCheckoutDialog({ open, layout, request, customerEmail, checkoutAnalytics, content, presentation, onClose, onCustomerEmailCommit, onSuccess, onRetry, onError, onPreparationStart, onStateChange, onPaymentPendingChange, }: SolidgateCheckoutDialogProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { completePaywallCheckout } from '@funnelsgrove/runtime';
4
4
  import { publicAnalyticsSdk } from '@funnelsgrove/analytics';
5
5
  import { useCallback, useEffect, useMemo, useRef, useState, } from 'react';
@@ -65,7 +65,7 @@ const trackSolidgateCheckoutEvent = async (eventName, attemptId, context) => {
65
65
  }
66
66
  };
67
67
  const isAbortError = (error) => (error instanceof DOMException && error.name === 'AbortError');
68
- export function SolidgateCheckoutDialog({ open, request, customerEmail, checkoutAnalytics, content, presentation, onClose, onCustomerEmailCommit, onSuccess, onRetry, onError, onPreparationStart, onStateChange, }) {
68
+ export function SolidgateCheckoutDialog({ open, layout = 'dialog', request, customerEmail, checkoutAnalytics, content, presentation, onClose, onCustomerEmailCommit, onSuccess, onRetry, onError, onPreparationStart, onStateChange, onPaymentPendingChange, }) {
69
69
  const copy = useMemo(() => (Object.assign(Object.assign({}, defaultContent), content)), [content]);
70
70
  const [view, setView] = useState(initialView);
71
71
  const [retryRequest, setRetryRequest] = useState(null);
@@ -85,6 +85,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
85
85
  const copyRef = useRef(copy);
86
86
  const onCloseRef = useRef(onClose);
87
87
  const onErrorRef = useRef(onError);
88
+ const onPaymentPendingChangeRef = useRef(onPaymentPendingChange);
88
89
  const onStateChangeRef = useRef(onStateChange);
89
90
  const onSuccessRef = useRef(onSuccess);
90
91
  useEffect(() => {
@@ -93,8 +94,9 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
93
94
  onCloseRef.current = onClose;
94
95
  onErrorRef.current = onError;
95
96
  onStateChangeRef.current = onStateChange;
97
+ onPaymentPendingChangeRef.current = onPaymentPendingChange;
96
98
  onSuccessRef.current = onSuccess;
97
- }, [checkoutAnalytics, copy, onClose, onError, onStateChange, onSuccess]);
99
+ }, [checkoutAnalytics, copy, onClose, onError, onStateChange, onSuccess, onPaymentPendingChange]);
98
100
  useEffect(() => {
99
101
  setEmailInput((customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || '');
100
102
  setCommittedEmail(normalizedCustomerEmail);
@@ -150,9 +152,11 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
150
152
  const latestRequestRef = useRef(normalizedRequest);
151
153
  latestRequestRef.current = normalizedRequest;
152
154
  const setState = useCallback((nextView) => {
153
- var _a;
155
+ var _a, _b;
154
156
  setView(nextView);
155
157
  (_a = onStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onStateChangeRef, nextView.state);
158
+ (_b = onPaymentPendingChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onPaymentPendingChangeRef, nextView.state === 'submitting'
159
+ || nextView.state === 'verifying' || nextView.state === 'success' || nextView.canResumeVerification);
156
160
  }, []);
157
161
  const trackFailure = useCallback((message, failureStage, attemptId) => {
158
162
  const analytics = checkoutAnalyticsRef.current;
@@ -169,9 +173,11 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
169
173
  });
170
174
  }, []);
171
175
  const requiresEmailCommit = Boolean(onCustomerEmailCommit);
176
+ const startedAttemptRef = useRef(null);
177
+ const active = open && (layout !== 'wallet' || Boolean(normalizedCustomerEmail));
172
178
  useEffect(() => {
173
179
  var _a, _b, _c, _d, _e, _f, _g;
174
- if (!open) {
180
+ if (!active) {
175
181
  preparedRequestRef.current = null;
176
182
  (_a = prepareAbortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
177
183
  (_b = statusAbortRef.current) === null || _b === void 0 ? void 0 : _b.abort();
@@ -205,7 +211,9 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
205
211
  .then((checkout) => {
206
212
  if (!controller.signal.aborted) {
207
213
  setState({ state: 'ready', checkout, canResumeVerification: false });
208
- void trackSolidgateCheckoutEvent('checkout_started', checkout.attemptId, checkoutAnalyticsRef.current);
214
+ if (layout !== 'wallet') {
215
+ void trackSolidgateCheckoutEvent('checkout_started', checkout.attemptId, checkoutAnalyticsRef.current);
216
+ }
209
217
  }
210
218
  })
211
219
  .catch((error) => {
@@ -217,13 +225,13 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
217
225
  }
218
226
  });
219
227
  return () => controller.abort();
220
- }, [checkoutKey, committedEmail, onPreparationStart, open, requiresEmailCommit, setState, trackFailure]);
228
+ }, [checkoutKey, committedEmail, layout, onPreparationStart, active, requiresEmailCommit, setState, trackFailure]);
221
229
  const paymentFormMounted = Boolean(view.checkout && mountedPaymentAttemptId === view.checkout.attemptId);
222
230
  const showPreparingLoader = view.state === 'loading'
223
231
  || (view.state === 'ready' && !paymentFormMounted);
224
232
  useEffect(() => {
225
233
  var _a;
226
- if (!open) {
234
+ if (!open || layout === 'wallet') {
227
235
  return;
228
236
  }
229
237
  const previouslyFocused = document.activeElement instanceof HTMLElement
@@ -253,7 +261,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
253
261
  window.removeEventListener('keydown', handleKeyDown);
254
262
  previouslyFocused === null || previouslyFocused === void 0 ? void 0 : previouslyFocused.focus();
255
263
  };
256
- }, [open, showPreparingLoader, view.checkout]);
264
+ }, [layout, open, showPreparingLoader, view.checkout]);
257
265
  useEffect(() => () => {
258
266
  var _a, _b;
259
267
  (_a = prepareAbortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
@@ -356,7 +364,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
356
364
  }
357
365
  onCloseRef.current();
358
366
  };
359
- if (!open) {
367
+ if (!active) {
360
368
  return null;
361
369
  }
362
370
  const showForm = view.state === 'ready'
@@ -379,6 +387,40 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
379
387
  '--solidgate-checkout-theme-color': presentation.themeColor,
380
388
  }
381
389
  : undefined;
390
+ const recoveryActions = (_jsxs(_Fragment, { children: [view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: () => void verifyCanonicalStatus(), className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.resumeVerification })) : null, (view.state === 'recoverable_error' || view.state === 'expired') && !view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: onRetry || (() => {
391
+ preparedRequestRef.current = null;
392
+ setRetryRequest({
393
+ originalKey: idempotencyKey,
394
+ key: crypto.randomUUID(),
395
+ });
396
+ }), className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.retry })) : null] }));
397
+ const paymentForm = view.checkout ? (_jsx(SolidgatePaymentForm, { walletOnly: layout === 'wallet', secureLabel: presentation === null || presentation === void 0 ? void 0 : presentation.secureLabel, cardLabel: presentation === null || presentation === void 0 ? void 0 : presentation.cardLabel, paymentMethodLabel: presentation === null || presentation === void 0 ? void 0 : presentation.paymentMethodLabel, buttonColor: (presentation === null || presentation === void 0 ? void 0 : presentation.buttonColor) || '#3f51b5', cardSubmitLabel: (presentation === null || presentation === void 0 ? void 0 : presentation.cardSubmitLabel) || 'Pay securely', checkout: view.checkout, hidden: !showForm, loadingLabel: copy.loading, onMounted: () => {
398
+ var _a;
399
+ setMountedPaymentAttemptId(((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId) || null);
400
+ }, onSubmit: () => {
401
+ var _a, _b, _c;
402
+ if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
403
+ paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
404
+ void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
405
+ }
406
+ setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
407
+ }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
408
+ var _a, _b;
409
+ setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
410
+ trackFailure(copyRef.current.error, 'payment_form', (_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId);
411
+ (_b = onErrorRef.current) === null || _b === void 0 ? void 0 : _b.call(onErrorRef, copyRef.current.error);
412
+ } }, view.checkout.attemptId)) : null;
413
+ if (layout === 'wallet') {
414
+ return (_jsxs("div", { className: 'solidgate-inline-wallet', onClick: (event) => {
415
+ var _a;
416
+ const attemptId = (_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId;
417
+ if (attemptId && startedAttemptRef.current !== attemptId
418
+ && event.target instanceof Element && event.target.closest('[data-solidgate-wallet]')) {
419
+ startedAttemptRef.current = attemptId;
420
+ void trackSolidgateCheckoutEvent('checkout_started', attemptId, checkoutAnalyticsRef.current);
421
+ }
422
+ }, children: [paymentForm, statusMessage && !showPreparingLoader ? _jsx("p", { role: 'status', children: statusMessage }) : null, recoveryActions, _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
423
+ }
382
424
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: `solidgate-checkout-overlay${presentation ? ' is-branded' : ''}`, style: presentationStyle, children: [showPreparingLoader ? (_jsxs("div", { className: 'solidgate-checkout-loader', role: 'status', "aria-live": 'polite', children: [_jsx("span", { className: 'solidgate-checkout-spinner', "aria-hidden": 'true' }), _jsx("span", { className: 'solidgate-checkout-visually-hidden', children: copy.loading })] })) : null, view.state !== 'loading' || view.checkout ? (_jsxs("div", { role: 'dialog', "aria-modal": 'true', "aria-hidden": showPreparingLoader || undefined, "aria-labelledby": 'solidgate-checkout-title', "data-checkout-state": view.state, className: [
383
425
  'solidgate-checkout-dialog',
384
426
  showPreparingLoader ? 'is-preparing' : '',
@@ -386,26 +428,5 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
386
428
  ].filter(Boolean).join(' '), children: [_jsxs("div", { className: 'solidgate-checkout-header', children: [_jsx("button", { ref: closeButtonRef, type: 'button', onClick: handleClose, "aria-label": copy.closeAriaLabel, className: 'solidgate-checkout-close', children: _jsx("span", { "aria-hidden": 'true' }) }), _jsx("h2", { id: 'solidgate-checkout-title', className: 'solidgate-checkout-title', children: (presentation === null || presentation === void 0 ? void 0 : presentation.title) || copy.title })] }), presentation ? _jsx(SolidgateCheckoutSummary, { presentation: presentation }) : null, view.state === 'collecting_email' ? (_jsxs("form", { className: 'solidgate-checkout-email-form', onSubmit: (event) => void commitCustomerEmail(event), children: [_jsxs("label", { className: 'solidgate-checkout-email-label', children: [copy.customerEmailLabel, _jsx("input", { type: 'email', autoComplete: 'email', inputMode: 'email', maxLength: 320, required: true, value: emailInput, onChange: (event) => {
387
429
  setEmailInput(event.target.value);
388
430
  setEmailError(null);
389
- }, placeholder: copy.customerEmailPlaceholder, "aria-invalid": Boolean(emailError), className: 'solidgate-checkout-email-input' })] }), emailError ? (_jsx("p", { role: 'alert', className: 'solidgate-checkout-alert', children: emailError })) : null, _jsx("button", { type: 'submit', disabled: emailSubmitting, className: 'solidgate-checkout-action', children: copy.customerEmailContinue })] })) : null, view.checkout ? (_jsx(SolidgatePaymentForm, { buttonColor: (presentation === null || presentation === void 0 ? void 0 : presentation.buttonColor) || '#3f51b5', cardSubmitLabel: (presentation === null || presentation === void 0 ? void 0 : presentation.cardSubmitLabel) || 'Pay securely', checkout: view.checkout, hidden: !showForm, loadingLabel: copy.loading, onMounted: () => {
390
- var _a;
391
- setMountedPaymentAttemptId(((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId) || null);
392
- }, onSubmit: () => {
393
- var _a, _b, _c;
394
- if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
395
- paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
396
- void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
397
- }
398
- setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
399
- }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
400
- var _a, _b;
401
- setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
402
- trackFailure(copyRef.current.error, 'payment_form', (_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId);
403
- (_b = onErrorRef.current) === null || _b === void 0 ? void 0 : _b.call(onErrorRef, copyRef.current.error);
404
- } }, view.checkout.attemptId)) : null, presentation && view.checkout && showForm ? (_jsx("p", { className: 'solidgate-checkout-secure', children: presentation.secureLabel })) : null, statusMessage && view.state !== 'ready' ? (_jsx("div", { className: 'solidgate-checkout-status', children: _jsx("p", { role: 'status', "aria-live": 'polite', children: statusMessage }) })) : null, view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: () => void verifyCanonicalStatus(), className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.resumeVerification })) : null, (view.state === 'recoverable_error' || view.state === 'expired') && !view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: onRetry || (() => {
405
- preparedRequestRef.current = null;
406
- setRetryRequest({
407
- originalKey: idempotencyKey,
408
- key: crypto.randomUUID(),
409
- });
410
- }), className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.retry })) : null] })) : null] }), _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
431
+ }, placeholder: copy.customerEmailPlaceholder, "aria-invalid": Boolean(emailError), className: 'solidgate-checkout-email-input' })] }), emailError ? (_jsx("p", { role: 'alert', className: 'solidgate-checkout-alert', children: emailError })) : null, _jsx("button", { type: 'submit', disabled: emailSubmitting, className: 'solidgate-checkout-action', children: copy.customerEmailContinue })] })) : null, paymentForm, statusMessage && view.state !== 'ready' ? (_jsx("div", { className: 'solidgate-checkout-status', children: _jsx("p", { role: 'status', "aria-live": 'polite', children: statusMessage }) })) : null, recoveryActions] })) : null] }), _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
411
432
  }
@@ -1 +1 @@
1
- export declare const solidgateCheckoutDialogStyles = "\n.solidgate-checkout-overlay {\n position: fixed;\n inset: 0;\n z-index: 100;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 16px;\n background: rgb(17 24 39 / 60%);\n backdrop-filter: blur(4px);\n}\n\n.solidgate-checkout-overlay.is-branded {\n z-index: 110;\n align-items: stretch;\n padding: 0;\n background: #fff;\n backdrop-filter: none;\n}\n\n.solidgate-checkout-dialog {\n box-sizing: border-box;\n width: min(100%, 512px);\n max-height: calc(100svh - 32px);\n overflow-y: auto;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 16px;\n background: var(--color-surface, #fff);\n padding: 20px;\n color: var(--color-text, #111827);\n box-shadow: 0 24px 80px rgb(17 24 39 / 22%);\n}\n\n.solidgate-checkout-dialog.is-branded {\n width: min(100%, 430px);\n height: 100svh;\n max-height: 100svh;\n border: 0;\n border-radius: 0;\n background: #fff;\n padding: 0 17px 28px;\n color: #303136;\n box-shadow: none;\n font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.solidgate-checkout-dialog.is-preparing {\n position: absolute;\n opacity: 0;\n pointer-events: none;\n}\n\n.solidgate-checkout-loader {\n display: grid;\n place-items: center;\n}\n\n.solidgate-checkout-spinner {\n box-sizing: border-box;\n width: 44px;\n height: 44px;\n border: 4px solid var(--color-border, #d1d5db);\n border-top-color: var(--color-primary, #22c55e);\n border-radius: 50%;\n animation: solidgate-checkout-spin 720ms linear infinite;\n}\n\n.solidgate-checkout-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@keyframes solidgate-checkout-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .solidgate-checkout-spinner {\n animation-duration: 1.6s;\n }\n}\n\n.solidgate-checkout-header {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 16px;\n margin-bottom: 16px;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-header {\n min-height: 70px;\n margin: 0 -17px;\n padding: 0 56px;\n}\n\n.solidgate-checkout-title {\n margin: 0;\n color: var(--color-text, #111827);\n font-size: 18px;\n font-weight: 600;\n line-height: 1.3;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-title {\n color: #020203;\n font-size: 24px;\n font-weight: 900;\n line-height: 1.15;\n text-align: center;\n}\n\n.solidgate-checkout-close {\n position: absolute;\n left: 0;\n top: 50%;\n width: 36px;\n height: 36px;\n flex: none;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 8px;\n background: var(--color-bg, #f9fafb);\n color: var(--color-text, #111827);\n font-size: 22px;\n transform: translateY(-50%);\n cursor: pointer;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-close {\n left: 16px;\n width: 40px;\n height: 40px;\n border: 0;\n border-radius: 999px;\n background: #f2f2f3;\n color: #aaa;\n}\n\n.solidgate-checkout-close span,\n.solidgate-checkout-close span::after {\n position: absolute;\n left: 50%;\n top: 50%;\n width: 22px;\n height: 3px;\n border-radius: 999px;\n background: currentColor;\n content: '';\n}\n\n.solidgate-checkout-close span {\n transform: translate(-50%, -50%) rotate(45deg);\n}\n\n.solidgate-checkout-close span::after {\n transform: translate(-50%, -50%) rotate(90deg);\n}\n\n.solidgate-checkout-close:hover {\n background: var(--color-accent-soft, #eef2ff);\n}\n\n.solidgate-checkout-summary {\n color: #303136;\n}\n\n.solidgate-checkout-countdown {\n min-height: 37px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n border-radius: 12px;\n background: color-mix(in srgb, var(--solidgate-checkout-theme-color) 10%, #fff);\n color: var(--solidgate-checkout-theme-color);\n font-size: 16px;\n font-weight: 900;\n text-align: center;\n}\n\n.solidgate-checkout-satisfaction {\n margin: 17px 0 21px;\n color: #2f3034;\n font-size: 19px;\n font-weight: 800;\n line-height: 1.15;\n text-align: center;\n}\n\n.solidgate-checkout-satisfaction strong {\n color: var(--solidgate-checkout-theme-color);\n}\n\n.solidgate-checkout-price-stack {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.solidgate-checkout-price-row,\n.solidgate-checkout-promo,\n.solidgate-checkout-total {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.solidgate-checkout-price-row {\n font-size: 17px;\n}\n\n.solidgate-checkout-price-row.is-muted {\n color: #9d9d9f;\n font-weight: 500;\n}\n\n.solidgate-checkout-strike {\n text-decoration: line-through 2px;\n}\n\n.solidgate-checkout-price-row.is-discount {\n color: #040508;\n font-weight: 900;\n}\n\n.solidgate-checkout-price-row.is-discount strong:last-child {\n color: #ff2544;\n}\n\n.solidgate-checkout-promo {\n min-height: 42px;\n border-radius: 13px;\n background: #f0f0f0;\n padding: 0 14px;\n color: #8d8d90;\n font-size: 16px;\n}\n\n.solidgate-checkout-promo strong {\n color: #050609;\n font-weight: 900;\n}\n\n.solidgate-checkout-total {\n min-height: 76px;\n margin-top: 20px;\n border-top: 1px solid #dedede;\n font-size: 23px;\n font-weight: 800;\n}\n\n.solidgate-checkout-total.is-plain {\n margin-top: 0;\n}\n\n.solidgate-checkout-total strong {\n font-size: 25px;\n font-weight: 900;\n}\n\n.solidgate-checkout-saved {\n width: fit-content;\n min-height: 36px;\n display: flex;\n align-items: center;\n gap: 7px;\n margin: 0 auto 12px;\n border-radius: 999px;\n background: #fff3cf;\n padding: 0 13px;\n color: #2e3034;\n font-size: 14px;\n font-weight: 800;\n}\n\n.solidgate-checkout-email-form,\n.solidgate-checkout-email-label,\n.solidgate-checkout-wallets {\n display: grid;\n}\n\n.solidgate-checkout-email-form {\n gap: 16px;\n}\n\n.solidgate-checkout-email-label {\n gap: 8px;\n color: var(--color-text, #111827);\n font-size: 14px;\n font-weight: 500;\n}\n\n.solidgate-checkout-email-input {\n box-sizing: border-box;\n width: 100%;\n min-height: 44px;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 8px;\n background: var(--color-bg, #fff);\n padding: 8px 12px;\n color: var(--color-text, #111827);\n font: inherit;\n}\n\n.solidgate-checkout-email-input::placeholder,\n.solidgate-checkout-status,\n.solidgate-checkout-status-text {\n color: var(--color-text-muted, #6b7280);\n}\n\n.solidgate-checkout-email-input:focus-visible,\n.solidgate-checkout-close:focus-visible,\n.solidgate-checkout-action:focus-visible {\n outline: 2px solid var(--color-accent, #3f51b5);\n outline-offset: 2px;\n}\n\n.solidgate-checkout-alert {\n margin: 0;\n color: var(--color-danger, #b91c1c);\n font-size: 14px;\n}\n\n.solidgate-checkout-action {\n box-sizing: border-box;\n width: 100%;\n min-height: 44px;\n border: 1px solid var(--color-accent, #3f51b5);\n border-radius: 8px;\n background: var(--color-accent, #3f51b5);\n padding: 8px 16px;\n color: var(--color-primary-text, #fff);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n}\n\n.solidgate-checkout-action:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.solidgate-checkout-action--spaced {\n margin-top: 16px;\n}\n\n.solidgate-checkout-wallets {\n gap: 12px;\n margin-bottom: 12px;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-wallets {\n margin-top: 4px;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-wallets > div:not(:empty) {\n min-height: 48px;\n}\n\n.solidgate-checkout-secure {\n width: fit-content;\n margin: 14px auto 0;\n border-radius: 999px;\n background: #edfff4;\n padding: 8px 14px;\n color: #2b2d31;\n font-size: 13px;\n font-weight: 700;\n}\n\n.solidgate-checkout-payment[hidden] {\n display: none;\n}\n\n.solidgate-checkout-status {\n margin-top: 12px;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 12px;\n background: var(--color-bg, #f9fafb);\n padding: 16px;\n font-size: 14px;\n}\n\n.solidgate-checkout-status p,\n.solidgate-checkout-status-text {\n margin: 0;\n}\n";
1
+ export declare const solidgateCheckoutDialogStyles = "\n.solidgate-checkout-overlay {\n position: fixed;\n inset: 0;\n z-index: 100;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: hidden;\n padding: 16px;\n background: rgb(17 24 39 / 60%);\n backdrop-filter: blur(4px);\n}\n\n.solidgate-checkout-overlay.is-branded {\n z-index: 110;\n align-items: stretch;\n padding: 0;\n background: #fff;\n backdrop-filter: none;\n}\n\n.solidgate-checkout-dialog {\n box-sizing: border-box;\n width: min(100%, 512px);\n max-height: calc(100svh - 32px);\n overflow-y: auto;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 16px;\n background: var(--color-surface, #fff);\n padding: 20px;\n color: var(--color-text, #111827);\n box-shadow: 0 24px 80px rgb(17 24 39 / 22%);\n}\n\n.solidgate-checkout-dialog.is-branded {\n width: min(100%, 430px);\n height: 100svh;\n max-height: 100svh;\n border: 0;\n border-radius: 0;\n background: #fff;\n padding: 0 17px 28px;\n color: #303136;\n box-shadow: none;\n font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n}\n\n.solidgate-checkout-dialog.is-preparing {\n position: absolute;\n opacity: 0;\n pointer-events: none;\n}\n\n.solidgate-checkout-loader {\n display: grid;\n place-items: center;\n}\n\n.solidgate-checkout-spinner {\n box-sizing: border-box;\n width: 44px;\n height: 44px;\n border: 4px solid var(--color-border, #d1d5db);\n border-top-color: var(--color-primary, #22c55e);\n border-radius: 50%;\n animation: solidgate-checkout-spin 720ms linear infinite;\n}\n\n.solidgate-checkout-visually-hidden {\n position: absolute;\n width: 1px;\n height: 1px;\n overflow: hidden;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n white-space: nowrap;\n}\n\n@keyframes solidgate-checkout-spin {\n to {\n transform: rotate(360deg);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .solidgate-checkout-spinner {\n animation-duration: 1.6s;\n }\n}\n\n.solidgate-checkout-header {\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 16px;\n margin-bottom: 16px;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-header {\n min-height: 70px;\n margin: 0 -17px;\n padding: 0 56px;\n}\n\n.solidgate-checkout-title {\n margin: 0;\n color: var(--color-text, #111827);\n font-size: 18px;\n font-weight: 600;\n line-height: 1.3;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-title {\n color: #020203;\n font-size: 24px;\n font-weight: 900;\n line-height: 1.15;\n text-align: center;\n}\n\n.solidgate-checkout-close {\n position: absolute;\n left: 0;\n top: 50%;\n width: 36px;\n height: 36px;\n flex: none;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 8px;\n background: var(--color-bg, #f9fafb);\n color: var(--color-text, #111827);\n font-size: 22px;\n transform: translateY(-50%);\n cursor: pointer;\n}\n\n.solidgate-checkout-dialog.is-branded .solidgate-checkout-close {\n left: 16px;\n width: 40px;\n height: 40px;\n border: 0;\n border-radius: 999px;\n background: #f2f2f3;\n color: #aaa;\n}\n\n.solidgate-checkout-close span,\n.solidgate-checkout-close span::after {\n position: absolute;\n left: 50%;\n top: 50%;\n width: 22px;\n height: 3px;\n border-radius: 999px;\n background: currentColor;\n content: '';\n}\n\n.solidgate-checkout-close span {\n transform: translate(-50%, -50%) rotate(45deg);\n}\n\n.solidgate-checkout-close span::after {\n transform: translate(-50%, -50%) rotate(90deg);\n}\n\n.solidgate-checkout-close:hover {\n background: var(--color-accent-soft, #eef2ff);\n}\n\n.solidgate-checkout-summary {\n color: #303136;\n}\n\n.solidgate-checkout-countdown {\n min-height: 37px;\n display: flex;\n align-items: center;\n justify-content: center;\n margin: 0;\n border-radius: 12px;\n background: color-mix(in srgb, var(--solidgate-checkout-theme-color) 10%, #fff);\n color: var(--solidgate-checkout-theme-color);\n font-size: 16px;\n font-weight: 900;\n text-align: center;\n}\n\n.solidgate-checkout-satisfaction {\n margin: 17px 0 21px;\n color: #2f3034;\n font-size: 19px;\n font-weight: 800;\n line-height: 1.15;\n text-align: center;\n}\n\n.solidgate-checkout-satisfaction strong {\n color: var(--solidgate-checkout-theme-color);\n}\n\n.solidgate-checkout-price-stack {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.solidgate-checkout-price-row,\n.solidgate-checkout-promo,\n.solidgate-checkout-total {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n}\n\n.solidgate-checkout-price-row {\n font-size: 17px;\n}\n\n.solidgate-checkout-price-row.is-muted {\n color: #9d9d9f;\n font-weight: 500;\n}\n\n.solidgate-checkout-strike {\n text-decoration: line-through 2px;\n}\n\n.solidgate-checkout-price-row.is-discount {\n color: #040508;\n font-weight: 900;\n}\n\n.solidgate-checkout-price-row.is-discount strong:last-child {\n color: #ff2544;\n}\n\n.solidgate-checkout-promo {\n min-height: 42px;\n border-radius: 13px;\n background: #f0f0f0;\n padding: 0 14px;\n color: #8d8d90;\n font-size: 16px;\n}\n\n.solidgate-checkout-promo strong {\n color: #050609;\n font-weight: 900;\n}\n\n.solidgate-checkout-total {\n min-height: 76px;\n margin-top: 20px;\n border-top: 1px solid #dedede;\n font-size: 23px;\n font-weight: 800;\n}\n\n.solidgate-checkout-total.is-plain {\n margin-top: 0;\n}\n\n.solidgate-checkout-total strong {\n font-size: 25px;\n font-weight: 900;\n}\n\n.solidgate-checkout-total strong {\n display: inline-flex;\n align-items: center;\n gap: 7px;\n}\n.solidgate-checkout-total svg {\n width: 25px;\n height: 25px;\n fill: none;\n stroke: currentColor;\n stroke-width: 2;\n stroke-linejoin: round;\n}\n.solidgate-checkout-saved {\n width: fit-content;\n min-height: 36px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n margin: -8px 0 9px auto;\n border-radius: 12px;\n background: #ffdce3;\n padding: 0 14px;\n color: #ff2544;\n font-size: 14px;\n font-weight: 900;\n line-height: 1.1;\n}\n\n.solidgate-checkout-email-form,\n.solidgate-checkout-email-label,\n.solidgate-checkout-wallets {\n display: grid;\n}\n\n.solidgate-checkout-email-form {\n gap: 16px;\n}\n\n.solidgate-checkout-email-label {\n gap: 8px;\n color: var(--color-text, #111827);\n font-size: 14px;\n font-weight: 500;\n}\n\n.solidgate-checkout-email-input {\n box-sizing: border-box;\n width: 100%;\n min-height: 44px;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 8px;\n background: var(--color-bg, #fff);\n padding: 8px 12px;\n color: var(--color-text, #111827);\n font: inherit;\n}\n\n.solidgate-checkout-email-input::placeholder,\n.solidgate-checkout-status,\n.solidgate-checkout-status-text {\n color: var(--color-text-muted, #6b7280);\n}\n\n.solidgate-checkout-email-input:focus-visible,\n.solidgate-checkout-close:focus-visible,\n.solidgate-checkout-action:focus-visible {\n outline: 2px solid var(--color-accent, #3f51b5);\n outline-offset: 2px;\n}\n\n.solidgate-checkout-alert {\n margin: 0;\n color: var(--color-danger, #b91c1c);\n font-size: 14px;\n}\n\n.solidgate-checkout-action {\n box-sizing: border-box;\n width: 100%;\n min-height: 44px;\n border: 1px solid var(--color-accent, #3f51b5);\n border-radius: 8px;\n background: var(--color-accent, #3f51b5);\n padding: 8px 16px;\n color: var(--color-primary-text, #fff);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n}\n\n.solidgate-checkout-action:disabled {\n cursor: not-allowed;\n opacity: 0.6;\n}\n\n.solidgate-checkout-action--spaced {\n margin-top: 16px;\n}\n\n.solidgate-checkout-wallets {\n gap: 12px;\n}\n.solidgate-checkout-wallets > div:empty,\n.solidgate-checkout-wallets:not(:has(> div:not(:empty))) { display: none; }\n.solidgate-checkout-wallets > div:not(:empty) {\n width: 100%;\n height: 54px;\n}\n.solidgate-checkout-wallets apple-pay-button {\n --apple-pay-button-width: 100%;\n --apple-pay-button-height: 54px;\n --apple-pay-button-border-radius: 0;\n width: 100%;\n height: 54px;\n}\n.solidgate-checkout-payment.is-branded {\n display: flex;\n flex-direction: column;\n gap: 12px;\n margin: 0 -17px;\n padding: 9px 17px 32px;\n background: #f2f2f3;\n}\n.solidgate-checkout-methods {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);\n gap: 42px;\n}\n.solidgate-checkout-method {\n min-height: 74px;\n border: 2px solid #e6e7eb;\n border-radius: 13px;\n background: #fff;\n color: #303136;\n padding: 9px 8px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 6px;\n font: inherit;\n cursor: pointer;\n}\n.solidgate-checkout-method[aria-selected='true'] { border: 3px solid #050609; }\n.solidgate-checkout-method:focus-visible { outline: 2px solid var(--solidgate-checkout-theme-color, #3f51b5); outline-offset: 2px; }\n.solidgate-checkout-method strong { font-size: 20px; font-weight: 800; line-height: 1.1; }\n.solidgate-checkout-tab-wallet { width: 58px; height: 36px; }\n.solidgate-checkout-tab-brands { display: flex; align-items: center; gap: 4px; }\n.solidgate-checkout-tab-brand { width: 24px; height: 15px; }\n.solidgate-checkout-tab-wallet svg, .solidgate-checkout-tab-brand svg { width: 100%; height: 100%; }\n.solidgate-checkout-secure {\n min-height: 47px;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 9px;\n margin: 0;\n border-radius: 12px;\n background: #dcffd0;\n color: #207733;\n font-size: 18px;\n font-weight: 800;\n}\n.solidgate-checkout-secure svg { width: 23px; height: 23px; }\n.solidgate-checkout-payment.is-branded .solidgate-checkout-card { padding: 10px 14px 0; }\n.solidgate-checkout-secure.is-card { width: fit-content; margin: 14px auto 0; padding: 0 14px; }\n.solidgate-sdk-card-container iframe { border: 0; }\n.solidgate-checkout-payment[hidden],\n.solidgate-checkout-wallets[hidden],\n.solidgate-checkout-card[hidden] { display: none; }\n.solidgate-inline-wallet { width: 100%; }\n.solidgate-inline-wallet .solidgate-checkout-wallets:has(> div:not(:empty)) { margin-bottom: 12px; }\n\n.solidgate-checkout-status {\n margin-top: 12px;\n border: 1px solid var(--color-border, #d1d5db);\n border-radius: 12px;\n background: var(--color-bg, #f9fafb);\n padding: 16px;\n font-size: 14px;\n}\n\n.solidgate-checkout-status p,\n.solidgate-checkout-status-text {\n margin: 0;\n}\n";
@@ -270,19 +270,35 @@ export const solidgateCheckoutDialogStyles = `
270
270
  font-weight: 900;
271
271
  }
272
272
 
273
+ .solidgate-checkout-total strong {
274
+ display: inline-flex;
275
+ align-items: center;
276
+ gap: 7px;
277
+ }
278
+ .solidgate-checkout-total svg {
279
+ width: 25px;
280
+ height: 25px;
281
+ fill: none;
282
+ stroke: currentColor;
283
+ stroke-width: 2;
284
+ stroke-linejoin: round;
285
+ }
273
286
  .solidgate-checkout-saved {
274
287
  width: fit-content;
275
288
  min-height: 36px;
289
+ box-sizing: border-box;
276
290
  display: flex;
277
291
  align-items: center;
278
- gap: 7px;
279
- margin: 0 auto 12px;
280
- border-radius: 999px;
281
- background: #fff3cf;
282
- padding: 0 13px;
283
- color: #2e3034;
292
+ justify-content: center;
293
+ gap: 8px;
294
+ margin: -8px 0 9px auto;
295
+ border-radius: 12px;
296
+ background: #ffdce3;
297
+ padding: 0 14px;
298
+ color: #ff2544;
284
299
  font-size: 14px;
285
- font-weight: 800;
300
+ font-weight: 900;
301
+ line-height: 1.1;
286
302
  }
287
303
 
288
304
  .solidgate-checkout-email-form,
@@ -358,31 +374,77 @@ export const solidgateCheckoutDialogStyles = `
358
374
 
359
375
  .solidgate-checkout-wallets {
360
376
  gap: 12px;
361
- margin-bottom: 12px;
362
377
  }
363
-
364
- .solidgate-checkout-dialog.is-branded .solidgate-checkout-wallets {
365
- margin-top: 4px;
378
+ .solidgate-checkout-wallets > div:empty,
379
+ .solidgate-checkout-wallets:not(:has(> div:not(:empty))) { display: none; }
380
+ .solidgate-checkout-wallets > div:not(:empty) {
381
+ width: 100%;
382
+ height: 54px;
366
383
  }
367
-
368
- .solidgate-checkout-dialog.is-branded .solidgate-checkout-wallets > div:not(:empty) {
369
- min-height: 48px;
384
+ .solidgate-checkout-wallets apple-pay-button {
385
+ --apple-pay-button-width: 100%;
386
+ --apple-pay-button-height: 54px;
387
+ --apple-pay-button-border-radius: 0;
388
+ width: 100%;
389
+ height: 54px;
370
390
  }
371
-
372
- .solidgate-checkout-secure {
373
- width: fit-content;
374
- margin: 14px auto 0;
375
- border-radius: 999px;
376
- background: #edfff4;
377
- padding: 8px 14px;
378
- color: #2b2d31;
379
- font-size: 13px;
380
- font-weight: 700;
391
+ .solidgate-checkout-payment.is-branded {
392
+ display: flex;
393
+ flex-direction: column;
394
+ gap: 12px;
395
+ margin: 0 -17px;
396
+ padding: 9px 17px 32px;
397
+ background: #f2f2f3;
381
398
  }
382
-
383
- .solidgate-checkout-payment[hidden] {
384
- display: none;
399
+ .solidgate-checkout-methods {
400
+ display: grid;
401
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
402
+ gap: 42px;
403
+ }
404
+ .solidgate-checkout-method {
405
+ min-height: 74px;
406
+ border: 2px solid #e6e7eb;
407
+ border-radius: 13px;
408
+ background: #fff;
409
+ color: #303136;
410
+ padding: 9px 8px;
411
+ display: flex;
412
+ flex-direction: column;
413
+ align-items: center;
414
+ justify-content: center;
415
+ gap: 6px;
416
+ font: inherit;
417
+ cursor: pointer;
418
+ }
419
+ .solidgate-checkout-method[aria-selected='true'] { border: 3px solid #050609; }
420
+ .solidgate-checkout-method:focus-visible { outline: 2px solid var(--solidgate-checkout-theme-color, #3f51b5); outline-offset: 2px; }
421
+ .solidgate-checkout-method strong { font-size: 20px; font-weight: 800; line-height: 1.1; }
422
+ .solidgate-checkout-tab-wallet { width: 58px; height: 36px; }
423
+ .solidgate-checkout-tab-brands { display: flex; align-items: center; gap: 4px; }
424
+ .solidgate-checkout-tab-brand { width: 24px; height: 15px; }
425
+ .solidgate-checkout-tab-wallet svg, .solidgate-checkout-tab-brand svg { width: 100%; height: 100%; }
426
+ .solidgate-checkout-secure {
427
+ min-height: 47px;
428
+ display: flex;
429
+ align-items: center;
430
+ justify-content: center;
431
+ gap: 9px;
432
+ margin: 0;
433
+ border-radius: 12px;
434
+ background: #dcffd0;
435
+ color: #207733;
436
+ font-size: 18px;
437
+ font-weight: 800;
385
438
  }
439
+ .solidgate-checkout-secure svg { width: 23px; height: 23px; }
440
+ .solidgate-checkout-payment.is-branded .solidgate-checkout-card { padding: 10px 14px 0; }
441
+ .solidgate-checkout-secure.is-card { width: fit-content; margin: 14px auto 0; padding: 0 14px; }
442
+ .solidgate-sdk-card-container iframe { border: 0; }
443
+ .solidgate-checkout-payment[hidden],
444
+ .solidgate-checkout-wallets[hidden],
445
+ .solidgate-checkout-card[hidden] { display: none; }
446
+ .solidgate-inline-wallet { width: 100%; }
447
+ .solidgate-inline-wallet .solidgate-checkout-wallets:has(> div:not(:empty)) { margin-bottom: 12px; }
386
448
 
387
449
  .solidgate-checkout-status {
388
450
  margin-top: 12px;
@@ -22,6 +22,8 @@ export type SolidgateCheckoutDialogContent = {
22
22
  export type SolidgateCheckoutDialogPresentation = {
23
23
  buttonColor: string;
24
24
  cardSubmitLabel: string;
25
+ cardLabel?: string;
26
+ paymentMethodLabel?: string;
25
27
  countdownLabel: string;
26
28
  discountAmountLabel: string;
27
29
  discountLabel: string;
@@ -43,6 +45,8 @@ export type SolidgateCheckoutDialogPresentation = {
43
45
  };
44
46
  export type SolidgateCheckoutDialogProps = {
45
47
  open: boolean;
48
+ /** Wallet-only paywall placement; mount only one Solidgate surface at a time. */
49
+ layout?: 'dialog' | 'wallet';
46
50
  request: Omit<PrepareSolidgateCheckoutInput, 'signal'>;
47
51
  customerEmail?: string | null;
48
52
  checkoutAnalytics?: SolidgateCheckoutAnalyticsContext | null;
@@ -54,6 +58,8 @@ export type SolidgateCheckoutDialogProps = {
54
58
  onRetry?: () => void;
55
59
  onError?: (message: string) => void;
56
60
  onPreparationStart?: () => void;
61
+ /** True while a submitted payment still needs verification (including resumable errors). */
62
+ onPaymentPendingChange?: (pending: boolean) => void;
57
63
  onStateChange?: (state: SolidgateCheckoutDialogState) => void;
58
64
  };
59
65
  export type SolidgateCheckoutAnalyticsContext = {
@@ -11,5 +11,5 @@ export function SolidgateCheckoutSummary({ presentation }) {
11
11
  const showPromo = Boolean((presentation === null || presentation === void 0 ? void 0 : presentation.promoActive) &&
12
12
  presentation.discountPercent > 0 &&
13
13
  presentation.promoCode.trim());
14
- return (_jsxs("section", { className: 'solidgate-checkout-summary', children: [showPromo ? (_jsxs("p", { className: 'solidgate-checkout-countdown', children: [presentation.discountPercent, "% ", presentation.countdownLabel, ' ', formatCountdown(presentation.remainingSeconds), " min"] })) : null, _jsxs("p", { className: 'solidgate-checkout-satisfaction', children: [_jsx("strong", { children: presentation.satisfactionPercent }), ' ', presentation.satisfactionLabel] }), showPromo ? (_jsxs("div", { className: 'solidgate-checkout-price-stack', children: [_jsxs("div", { className: 'solidgate-checkout-price-row is-muted', children: [_jsx("span", { children: presentation.originalPriceLabel }), _jsx("span", { className: 'solidgate-checkout-strike', children: presentation.originalPriceValue })] }), _jsxs("div", { className: 'solidgate-checkout-price-row is-discount', children: [_jsx("strong", { children: presentation.discountLabel }), _jsx("strong", { children: presentation.discountAmountLabel })] }), _jsxs("div", { className: 'solidgate-checkout-promo', children: [_jsx("span", { children: presentation.promoCodeLabel }), _jsx("strong", { children: presentation.promoCode.trim() })] })] })) : null, _jsxs("div", { className: `solidgate-checkout-total${showPromo ? '' : ' is-plain'}`, children: [_jsx("span", { children: presentation.totalLabel }), _jsx("strong", { children: presentation.totalValue })] }), showPromo ? (_jsxs("p", { className: 'solidgate-checkout-saved', children: [_jsx("span", { "aria-hidden": 'true', children: "\uD83D\uDD25" }), presentation.savedLabel] })) : null] }));
14
+ return (_jsxs("section", { className: 'solidgate-checkout-summary', children: [showPromo ? (_jsxs("p", { className: 'solidgate-checkout-countdown', children: [presentation.discountPercent, "% ", presentation.countdownLabel, ' ', formatCountdown(presentation.remainingSeconds), " min"] })) : null, _jsxs("p", { className: 'solidgate-checkout-satisfaction', children: [_jsx("strong", { children: presentation.satisfactionPercent }), ' ', presentation.satisfactionLabel] }), showPromo ? (_jsxs("div", { className: 'solidgate-checkout-price-stack', children: [_jsxs("div", { className: 'solidgate-checkout-price-row is-muted', children: [_jsx("span", { children: presentation.originalPriceLabel }), _jsx("span", { className: 'solidgate-checkout-strike', children: presentation.originalPriceValue })] }), _jsxs("div", { className: 'solidgate-checkout-price-row is-discount', children: [_jsx("strong", { children: presentation.discountLabel }), _jsx("strong", { children: presentation.discountAmountLabel })] }), _jsxs("div", { className: 'solidgate-checkout-promo', children: [_jsx("span", { children: presentation.promoCodeLabel }), _jsx("strong", { children: presentation.promoCode.trim() })] })] })) : null, _jsxs("div", { className: `solidgate-checkout-total${showPromo ? '' : ' is-plain'}`, children: [_jsx("span", { children: presentation.totalLabel }), _jsxs("strong", { children: [_jsxs("svg", { viewBox: '0 0 24 24', "aria-hidden": 'true', children: [_jsx("path", { d: 'M20 13 11 22 2 13V2h11l9 9-2 2Z' }), _jsx("circle", { cx: '7', cy: '7', r: '1' })] }), presentation.totalValue] })] }), showPromo ? (_jsxs("p", { className: 'solidgate-checkout-saved', children: [_jsx("span", { "aria-hidden": 'true', children: "\uD83D\uDD25" }), presentation.savedLabel] })) : null] }));
15
15
  }
@@ -1,7 +1,11 @@
1
1
  import type { SolidgateCheckoutPreparation } from '../services/solidgate.service.js';
2
- export declare function SolidgatePaymentForm({ buttonColor, cardSubmitLabel, checkout, hidden, loadingLabel, onError, onMounted, onSubmit, onVerify, }: {
2
+ export declare function SolidgatePaymentForm({ buttonColor, walletOnly, cardSubmitLabel, secureLabel, cardLabel, paymentMethodLabel, checkout, hidden, onError, onMounted, onSubmit, onVerify, }: {
3
3
  buttonColor: string;
4
+ walletOnly?: boolean;
4
5
  cardSubmitLabel: string;
6
+ secureLabel?: string;
7
+ cardLabel?: string;
8
+ paymentMethodLabel?: string;
5
9
  checkout: SolidgateCheckoutPreparation;
6
10
  hidden: boolean;
7
11
  loadingLabel: string;
@@ -1,26 +1,15 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Suspense, lazy, useEffect, useRef } from 'react';
4
- const SolidgatePayment = lazy(async () => ({
5
- default: (await import('@solidgate/react-sdk')).default,
6
- }));
7
- const safeDestroyPaymentForm = (instance) => {
8
- var _a;
9
- try {
10
- instance === null || instance === void 0 ? void 0 : instance.unsubscribeAll();
11
- }
12
- catch (_b) {
13
- // The global destroy below is the final cleanup boundary.
14
- }
15
- try {
16
- (_a = window.PaymentFormSdk) === null || _a === void 0 ? void 0 : _a.destroy();
17
- }
18
- catch (_c) {
19
- // Cleanup must stay safe when the remote SDK is partially initialized.
20
- }
21
- };
22
- export function SolidgatePaymentForm({ buttonColor, cardSubmitLabel, checkout, hidden, loadingLabel, onError, onMounted, onSubmit, onVerify, }) {
23
- const instanceRef = useRef(null);
3
+ import { useEffect, useId, useRef, useState } from 'react';
4
+ import { PaymentBrandMark, SecurityLockIcon } from '../../stripe/components/CheckoutBrandAssets.js';
5
+ import { SolidgateSdkPayment } from './SolidgateSdkPayment.js';
6
+ export function SolidgatePaymentForm({ buttonColor, walletOnly = false, cardSubmitLabel, secureLabel, cardLabel = 'Credit card', paymentMethodLabel = 'Payment method', checkout, hidden, onError, onMounted, onSubmit, onVerify, }) {
7
+ const panelId = useId();
8
+ const [wallets, setWallets] = useState([]);
9
+ const [selectedMethod, setSelectedMethod] = useState('wallet');
10
+ const showTabs = !walletOnly && Boolean(secureLabel) && wallets.length > 0;
11
+ const showWallet = walletOnly || !showTabs || selectedMethod === 'wallet';
12
+ const showCard = !walletOnly && (!showTabs || selectedMethod === 'card');
24
13
  const submittedRef = useRef(false);
25
14
  const paymentBoundaryRef = useRef(null);
26
15
  const mountedReportedRef = useRef(false);
@@ -37,6 +26,12 @@ export function SolidgatePaymentForm({ buttonColor, cardSubmitLabel, checkout, h
37
26
  return;
38
27
  }
39
28
  const reportIframeMounted = () => {
29
+ var _a, _b;
30
+ const available = [
31
+ ((_a = applePayContainerRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('apple-pay-button')) ? 'Apple Pay' : null,
32
+ ((_b = googlePayContainerRef.current) === null || _b === void 0 ? void 0 : _b.querySelector('button')) ? 'Google Pay' : null,
33
+ ].filter((method) => Boolean(method));
34
+ setWallets((previous) => previous.join() === available.join() ? previous : available);
40
35
  if (!mountedReportedRef.current
41
36
  && boundary.querySelector('iframe#solid-payment-form-iframe, iframe[name="solid-payment-form-iframe"]')) {
42
37
  mountedReportedRef.current = true;
@@ -48,34 +43,19 @@ export function SolidgatePaymentForm({ buttonColor, cardSubmitLabel, checkout, h
48
43
  observer.observe(boundary, { childList: true, subtree: true });
49
44
  return () => observer.disconnect();
50
45
  }, [checkout.attemptId]);
51
- useEffect(() => {
52
- submittedRef.current = false;
53
- return () => {
54
- safeDestroyPaymentForm(instanceRef.current);
55
- instanceRef.current = null;
56
- };
57
- }, [checkout.attemptId]);
58
46
  const walletButtonType = checkout.display.billingInterval ? 'subscribe' : 'buy';
59
- return (_jsxs("div", { ref: paymentBoundaryRef, className: 'solidgate-checkout-payment', "data-solidgate-attempt-id": checkout.attemptId, hidden: hidden, "aria-hidden": hidden || undefined, children: [_jsxs("div", { className: 'solidgate-checkout-wallets', children: [_jsx("div", { ref: applePayContainerRef, "data-solidgate-wallet": 'apple-pay' }), _jsx("div", { ref: googlePayContainerRef, "data-solidgate-wallet": 'google-pay' })] }), _jsx(Suspense, { fallback: (_jsx("p", { role: 'status', className: 'solidgate-checkout-status-text', children: loadingLabel })), children: _jsx(SolidgatePayment, { merchantData: checkout.initialization.merchantData, width: '100%', formParams: {
60
- autoFocus: false,
61
- isSolidLogoVisible: true,
62
- submitButtonText: cardSubmitLabel,
63
- }, styles: {
47
+ return (_jsxs("div", { ref: paymentBoundaryRef, className: `solidgate-checkout-payment${secureLabel && !walletOnly ? ' is-branded' : ''}`, "data-solidgate-attempt-id": checkout.attemptId, hidden: hidden, "aria-hidden": hidden || undefined, children: [showTabs ? (_jsxs("div", { className: 'solidgate-checkout-methods', role: 'tablist', "aria-label": paymentMethodLabel, children: [_jsx("button", { type: 'button', role: 'tab', id: `${panelId}-wallet-tab`, "aria-label": wallets.join(' / '), "aria-selected": selectedMethod === 'wallet', "aria-controls": `${panelId}-wallet`, className: 'solidgate-checkout-method', onClick: () => setSelectedMethod('wallet'), children: wallets.map((label) => _jsx(PaymentBrandMark, { label: label, className: 'solidgate-checkout-tab-wallet' }, label)) }), _jsxs("button", { type: 'button', role: 'tab', id: `${panelId}-card-tab`, "aria-selected": selectedMethod === 'card', "aria-controls": `${panelId}-card`, className: 'solidgate-checkout-method', onClick: () => setSelectedMethod('card'), children: [_jsx("strong", { children: cardLabel }), _jsx("span", { className: 'solidgate-checkout-tab-brands', "aria-hidden": 'true', children: ['Visa', 'Mastercard', 'Maestro', 'Discover'].map((label) => (_jsx(PaymentBrandMark, { label: label, className: 'solidgate-checkout-tab-brand' }, label))) })] })] })) : null, !walletOnly && secureLabel && wallets.length > 0 && showWallet ? (_jsxs("p", { className: 'solidgate-checkout-secure', children: [_jsx(SecurityLockIcon, {}), secureLabel] })) : null, _jsxs("div", { className: 'solidgate-checkout-wallets', id: `${panelId}-wallet`, role: showTabs ? 'tabpanel' : undefined, "aria-labelledby": showTabs ? `${panelId}-wallet-tab` : undefined, hidden: !showWallet, children: [_jsx("div", { ref: applePayContainerRef, "data-solidgate-wallet": 'apple-pay' }), _jsx("div", { ref: googlePayContainerRef, "data-solidgate-wallet": 'google-pay' })] }), _jsx("div", { className: 'solidgate-checkout-card', id: `${panelId}-card`, role: showTabs ? 'tabpanel' : undefined, "aria-labelledby": showTabs ? `${panelId}-card-tab` : undefined, hidden: !showCard, children: _jsx(SolidgateSdkPayment, { onInitializationError: onError, merchantData: checkout.initialization.merchantData, width: '100%', formParams: Object.assign({ autoFocus: false, enabled: !walletOnly, isSolidLogoVisible: true, submitButtonText: cardSubmitLabel }, (secureLabel ? { cardNumberLabel: 'Card number', cardExpiryDateLabel: 'Expiration date', cardCvvLabel: 'Security code' } : {})), styles: {
64
48
  form_body: {
65
49
  'font-family': 'Inter, Arial, sans-serif',
66
50
  },
67
- input_group: {
68
- input: {
69
- 'border-color': '#d8d9dd',
70
- 'border-radius': '10px',
71
- },
72
- },
51
+ input_group: Object.assign({ input: Object.assign({ 'border-color': '#d8d9dd', 'border-radius': secureLabel ? '0' : '10px' }, (secureLabel ? { 'background-color': '#fff', 'font-size': '16px', height: '50px' } : {})) }, (secureLabel ? { '.label': { 'font-size': '14px', 'font-weight': '400', color: '#303136' } } : {})),
73
52
  submit_button: {
74
53
  'background-color': buttonColor,
75
54
  'border-radius': '10px',
76
- 'font-size': '16px',
55
+ 'font-size': secureLabel ? '21px' : '16px',
77
56
  'font-weight': '800',
78
- height: '52px',
57
+ height: secureLabel ? '60px' : '52px',
58
+ 'text-transform': 'none',
79
59
  },
80
60
  }, applePayButtonParams: {
81
61
  color: 'black',
@@ -86,17 +66,7 @@ export function SolidgatePaymentForm({ buttonColor, cardSubmitLabel, checkout, h
86
66
  color: 'black',
87
67
  enabled: true,
88
68
  type: walletButtonType,
89
- }, applePayContainerRef: applePayContainerRef, googlePayContainerRef: googlePayContainerRef, onReadyPaymentInstance: (instance) => {
90
- if (instanceRef.current && instanceRef.current !== instance) {
91
- try {
92
- instanceRef.current.unsubscribeAll();
93
- }
94
- catch (_a) {
95
- // The active instance remains usable even if stale-listener cleanup fails.
96
- }
97
- }
98
- instanceRef.current = instance;
99
- }, onMounted: () => {
69
+ }, applePayContainerRef: applePayContainerRef, googlePayContainerRef: googlePayContainerRef, onMounted: () => {
100
70
  if (!mountedReportedRef.current) {
101
71
  mountedReportedRef.current = true;
102
72
  onMountedRef.current();
@@ -110,5 +80,5 @@ export function SolidgatePaymentForm({ buttonColor, cardSubmitLabel, checkout, h
110
80
  return;
111
81
  }
112
82
  onError();
113
- } }, checkout.attemptId) })] }));
83
+ } }, checkout.attemptId) }), !walletOnly && secureLabel && (wallets.length === 0 || !showWallet) ? (_jsxs("p", { className: 'solidgate-checkout-secure is-card', children: [_jsx(SecurityLockIcon, {}), secureLabel] })) : null] }));
114
84
  }
@@ -0,0 +1,8 @@
1
+ import type Payment from '@solidgate/react-sdk';
2
+ import { type ComponentProps } from 'react';
3
+ type Props = ComponentProps<typeof Payment> & {
4
+ onInitializationError: () => void;
5
+ };
6
+ /** Own container IDs and cancel pending initialization when a placement goes away. */
7
+ export declare function SolidgateSdkPayment(props: Props): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,66 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect, useId, useRef } from 'react';
4
+ let activeOwner = null;
5
+ /** Own container IDs and cancel pending initialization when a placement goes away. */
6
+ export function SolidgateSdkPayment(props) {
7
+ const id = `solidgate-${useId().replace(/[^a-zA-Z0-9]/g, '')}`;
8
+ const initialProps = useRef(props);
9
+ const callbacks = useRef(props);
10
+ useEffect(() => { callbacks.current = props; });
11
+ useEffect(() => {
12
+ var _a, _b;
13
+ let active = true;
14
+ let client = null;
15
+ let instance = null;
16
+ const owner = Symbol(id);
17
+ const config = initialProps.current;
18
+ const apple = (_a = config.applePayContainerRef) === null || _a === void 0 ? void 0 : _a.current;
19
+ const google = (_b = config.googlePayContainerRef) === null || _b === void 0 ? void 0 : _b.current;
20
+ if (apple)
21
+ apple.id = `${id}-apple`;
22
+ if (google)
23
+ google.id = `${id}-google`;
24
+ void import('@solidgate/react-sdk').then(async (sdk) => {
25
+ var _a, _b;
26
+ const loaded = await sdk.SdkLoader.load();
27
+ if (!active || !loaded)
28
+ return;
29
+ client = loaded;
30
+ activeOwner = owner;
31
+ instance = client.init({
32
+ merchantData: config.merchantData,
33
+ iframeParams: { containerId: id, width: config.width || '100%' },
34
+ formParams: config.formParams,
35
+ styles: config.styles,
36
+ applePayButtonParams: Object.assign(Object.assign({}, config.applePayButtonParams), { containerId: apple === null || apple === void 0 ? void 0 : apple.id }),
37
+ googlePayButtonParams: Object.assign(Object.assign({}, config.googlePayButtonParams), { containerId: google === null || google === void 0 ? void 0 : google.id }),
38
+ });
39
+ instance.on(sdk.MessageType.Mounted, (event) => { var _a, _b; return (_b = (_a = callbacks.current).onMounted) === null || _b === void 0 ? void 0 : _b.call(_a, event.data); });
40
+ instance.on(sdk.MessageType.Submit, (event) => { var _a, _b; return (_b = (_a = callbacks.current).onSubmit) === null || _b === void 0 ? void 0 : _b.call(_a, event.data); });
41
+ instance.on(sdk.MessageType.Verify, (event) => { var _a, _b; return (_b = (_a = callbacks.current).onVerify) === null || _b === void 0 ? void 0 : _b.call(_a, event.data); });
42
+ instance.on(sdk.MessageType.Success, (event) => { var _a, _b; return (_b = (_a = callbacks.current).onSuccess) === null || _b === void 0 ? void 0 : _b.call(_a, event.data); });
43
+ instance.on(sdk.MessageType.Fail, (event) => { var _a, _b; return (_b = (_a = callbacks.current).onFail) === null || _b === void 0 ? void 0 : _b.call(_a, event.data); });
44
+ instance.on(sdk.MessageType.Error, (event) => { var _a, _b; return (_b = (_a = callbacks.current).onError) === null || _b === void 0 ? void 0 : _b.call(_a, event.data); });
45
+ (_b = (_a = callbacks.current).onReadyPaymentInstance) === null || _b === void 0 ? void 0 : _b.call(_a, instance);
46
+ }).catch(() => {
47
+ if (active)
48
+ callbacks.current.onInitializationError();
49
+ });
50
+ return () => {
51
+ active = false;
52
+ if (activeOwner !== owner)
53
+ return;
54
+ activeOwner = null;
55
+ try {
56
+ instance === null || instance === void 0 ? void 0 : instance.unsubscribeAll();
57
+ }
58
+ catch ( /* Remote SDK may be partially initialized. */_a) { /* Remote SDK may be partially initialized. */ }
59
+ try {
60
+ client === null || client === void 0 ? void 0 : client.destroy();
61
+ }
62
+ catch ( /* Cleanup must not interrupt navigation. */_b) { /* Cleanup must not interrupt navigation. */ }
63
+ };
64
+ }, [id]);
65
+ return _jsx("div", { id: id, className: 'solidgate-sdk-card-container' });
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",