@funnelsgrove/payments 0.27.0 → 0.29.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.29.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.29.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,10 +1,11 @@
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';
6
- import { pollSolidgateCheckoutStatus, prepareSolidgateCheckout, } from '../services/solidgate.service.js';
6
+ import { pollSolidgateCheckoutStatus, prepareSolidgateCheckout, SolidgateExistingSubscriptionError, } from '../services/solidgate.service.js';
7
7
  import { trackCheckoutCanceled, trackCheckoutFailureShown, } from '../../../services/checkoutObservability.service.js';
8
+ import { solidgateDeclineMessage } from '../services/solidgateDeclineMessage.js';
8
9
  import { SolidgateCheckoutSummary } from './SolidgateCheckoutSummary.js';
9
10
  import { SolidgatePaymentForm } from './SolidgatePaymentForm.js';
10
11
  import { solidgateCheckoutDialogStyles } from './SolidgateCheckoutDialog.styles.js';
@@ -65,7 +66,7 @@ const trackSolidgateCheckoutEvent = async (eventName, attemptId, context) => {
65
66
  }
66
67
  };
67
68
  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, }) {
69
+ export function SolidgateCheckoutDialog({ open, layout = 'dialog', request, customerEmail, checkoutAnalytics, content, presentation, onClose, onCustomerEmailCommit, onSuccess, onRetry, onError, onPreparationStart, onStateChange, onPaymentPendingChange, }) {
69
70
  const copy = useMemo(() => (Object.assign(Object.assign({}, defaultContent), content)), [content]);
70
71
  const [view, setView] = useState(initialView);
71
72
  const [retryRequest, setRetryRequest] = useState(null);
@@ -75,6 +76,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
75
76
  const [emailError, setEmailError] = useState(null);
76
77
  const [emailSubmitting, setEmailSubmitting] = useState(false);
77
78
  const [mountedPaymentAttemptId, setMountedPaymentAttemptId] = useState(null);
79
+ const declinedAttemptRef = useRef(null);
78
80
  const prepareAbortRef = useRef(null);
79
81
  const preparedRequestRef = useRef(null);
80
82
  const statusAbortRef = useRef(null);
@@ -85,6 +87,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
85
87
  const copyRef = useRef(copy);
86
88
  const onCloseRef = useRef(onClose);
87
89
  const onErrorRef = useRef(onError);
90
+ const onPaymentPendingChangeRef = useRef(onPaymentPendingChange);
88
91
  const onStateChangeRef = useRef(onStateChange);
89
92
  const onSuccessRef = useRef(onSuccess);
90
93
  useEffect(() => {
@@ -93,8 +96,9 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
93
96
  onCloseRef.current = onClose;
94
97
  onErrorRef.current = onError;
95
98
  onStateChangeRef.current = onStateChange;
99
+ onPaymentPendingChangeRef.current = onPaymentPendingChange;
96
100
  onSuccessRef.current = onSuccess;
97
- }, [checkoutAnalytics, copy, onClose, onError, onStateChange, onSuccess]);
101
+ }, [checkoutAnalytics, copy, onClose, onError, onStateChange, onSuccess, onPaymentPendingChange]);
98
102
  useEffect(() => {
99
103
  setEmailInput((customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || '');
100
104
  setCommittedEmail(normalizedCustomerEmail);
@@ -103,6 +107,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
103
107
  const { discountKeyOrProviderId, failUrl, funnelPlanKey, paymentProfileId, providerPlanId, checkoutType, environment, funnelEndUserId, funnelId, idempotencyKey, offerSetId, returnUrl, runtimeConfig, successUrl, } = request;
104
108
  const checkoutKey = (retryRequest === null || retryRequest === void 0 ? void 0 : retryRequest.originalKey) === idempotencyKey
105
109
  ? retryRequest.key : idempotencyKey;
110
+ const retryAttemptId = (retryRequest === null || retryRequest === void 0 ? void 0 : retryRequest.originalKey) === idempotencyKey ? retryRequest.attemptId : request.retryAttemptId;
106
111
  const { apiBaseUrl, funnelId: runtimeFunnelId, funnelSdkPublishableKey, funnelVersionId, paymentsApiVersion, } = runtimeConfig || {};
107
112
  const hasRuntimeConfig = runtimeConfig !== undefined;
108
113
  const normalizedRequest = useMemo(() => ({
@@ -115,6 +120,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
115
120
  checkoutType: checkoutType || null,
116
121
  environment: environment || null,
117
122
  idempotencyKey: checkoutKey.trim(),
123
+ retryAttemptId,
118
124
  discountKeyOrProviderId: (discountKeyOrProviderId === null || discountKeyOrProviderId === void 0 ? void 0 : discountKeyOrProviderId.trim()) || null,
119
125
  returnUrl: returnUrl.trim(),
120
126
  successUrl: successUrl.trim(),
@@ -141,6 +147,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
141
147
  funnelVersionId,
142
148
  paymentsApiVersion,
143
149
  checkoutKey,
150
+ retryAttemptId,
144
151
  offerSetId,
145
152
  returnUrl,
146
153
  hasRuntimeConfig,
@@ -150,9 +157,11 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
150
157
  const latestRequestRef = useRef(normalizedRequest);
151
158
  latestRequestRef.current = normalizedRequest;
152
159
  const setState = useCallback((nextView) => {
153
- var _a;
160
+ var _a, _b;
154
161
  setView(nextView);
155
162
  (_a = onStateChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onStateChangeRef, nextView.state);
163
+ (_b = onPaymentPendingChangeRef.current) === null || _b === void 0 ? void 0 : _b.call(onPaymentPendingChangeRef, nextView.state === 'submitting'
164
+ || nextView.state === 'verifying' || nextView.state === 'success' || nextView.canResumeVerification);
156
165
  }, []);
157
166
  const trackFailure = useCallback((message, failureStage, attemptId) => {
158
167
  const analytics = checkoutAnalyticsRef.current;
@@ -169,9 +178,11 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
169
178
  });
170
179
  }, []);
171
180
  const requiresEmailCommit = Boolean(onCustomerEmailCommit);
181
+ const startedAttemptRef = useRef(null);
182
+ const active = open && (layout !== 'wallet' || Boolean(normalizedCustomerEmail));
172
183
  useEffect(() => {
173
184
  var _a, _b, _c, _d, _e, _f, _g;
174
- if (!open) {
185
+ if (!active) {
175
186
  preparedRequestRef.current = null;
176
187
  (_a = prepareAbortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
177
188
  (_b = statusAbortRef.current) === null || _b === void 0 ? void 0 : _b.abort();
@@ -204,26 +215,31 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
204
215
  void prepareSolidgateCheckout(Object.assign(Object.assign({}, preparedRequest), { signal: controller.signal }))
205
216
  .then((checkout) => {
206
217
  if (!controller.signal.aborted) {
218
+ declinedAttemptRef.current = null;
207
219
  setState({ state: 'ready', checkout, canResumeVerification: false });
208
- void trackSolidgateCheckoutEvent('checkout_started', checkout.attemptId, checkoutAnalyticsRef.current);
220
+ if (layout !== 'wallet') {
221
+ void trackSolidgateCheckoutEvent('checkout_started', checkout.attemptId, checkoutAnalyticsRef.current);
222
+ }
209
223
  }
210
224
  })
211
225
  .catch((error) => {
212
226
  var _a;
213
227
  if (!controller.signal.aborted && !isAbortError(error)) {
214
- setState({ state: 'recoverable_error', checkout: null, canResumeVerification: false });
215
- trackFailure(copyRef.current.error, 'checkout_session_creation');
216
- (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, copyRef.current.error);
228
+ const failureMessage = error instanceof SolidgateExistingSubscriptionError
229
+ ? error.message : copyRef.current.error;
230
+ setState({ state: 'recoverable_error', checkout: null, canResumeVerification: false, failureMessage });
231
+ trackFailure(failureMessage, 'checkout_session_creation');
232
+ (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, failureMessage, error instanceof SolidgateExistingSubscriptionError ? error.code : undefined);
217
233
  }
218
234
  });
219
235
  return () => controller.abort();
220
- }, [checkoutKey, committedEmail, onPreparationStart, open, requiresEmailCommit, setState, trackFailure]);
236
+ }, [checkoutKey, committedEmail, layout, onPreparationStart, active, requiresEmailCommit, setState, trackFailure]);
221
237
  const paymentFormMounted = Boolean(view.checkout && mountedPaymentAttemptId === view.checkout.attemptId);
222
238
  const showPreparingLoader = view.state === 'loading'
223
239
  || (view.state === 'ready' && !paymentFormMounted);
224
240
  useEffect(() => {
225
241
  var _a;
226
- if (!open) {
242
+ if (!open || layout === 'wallet') {
227
243
  return;
228
244
  }
229
245
  const previouslyFocused = document.activeElement instanceof HTMLElement
@@ -253,7 +269,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
253
269
  window.removeEventListener('keydown', handleKeyDown);
254
270
  previouslyFocused === null || previouslyFocused === void 0 ? void 0 : previouslyFocused.focus();
255
271
  };
256
- }, [open, showPreparingLoader, view.checkout]);
272
+ }, [layout, open, showPreparingLoader, view.checkout]);
257
273
  useEffect(() => () => {
258
274
  var _a, _b;
259
275
  (_a = prepareAbortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
@@ -296,9 +312,11 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
296
312
  trackFailure(copyRef.current.expired, 'payment_expired', checkout.attemptId);
297
313
  }
298
314
  else if (result.status === 'failed') {
299
- setState({ state: 'recoverable_error', checkout, canResumeVerification: false });
300
- trackFailure(copyRef.current.error, 'payment_failed', checkout.attemptId);
301
- (_c = onErrorRef.current) === null || _c === void 0 ? void 0 : _c.call(onErrorRef, copyRef.current.error);
315
+ declinedAttemptRef.current = checkout.attemptId;
316
+ const failureMessage = solidgateDeclineMessage(result.failureCode);
317
+ setState({ state: 'recoverable_error', checkout, canResumeVerification: false, failureMessage });
318
+ trackFailure(failureMessage, 'payment_failed', checkout.attemptId);
319
+ (_c = onErrorRef.current) === null || _c === void 0 ? void 0 : _c.call(onErrorRef, failureMessage);
302
320
  }
303
321
  else if (result.status === 'requires_payment') {
304
322
  setState({ state: 'ready', checkout, canResumeVerification: false });
@@ -356,7 +374,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
356
374
  }
357
375
  onCloseRef.current();
358
376
  };
359
- if (!open) {
377
+ if (!active) {
360
378
  return null;
361
379
  }
362
380
  const showForm = view.state === 'ready'
@@ -372,13 +390,56 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
372
390
  : view.state === 'expired'
373
391
  ? copy.expired
374
392
  : view.state === 'recoverable_error'
375
- ? copy.error
393
+ ? view.failureMessage || copy.error
376
394
  : null;
377
395
  const presentationStyle = presentation
378
396
  ? {
379
397
  '--solidgate-checkout-theme-color': presentation.themeColor,
380
398
  }
381
399
  : undefined;
400
+ 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: () => {
401
+ var _a;
402
+ const source = checkoutType === 'subscription'
403
+ ? declinedAttemptRef.current || (retryAttemptId ? ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId) || retryAttemptId : null)
404
+ : null;
405
+ if (!source && onRetry) {
406
+ onRetry();
407
+ return;
408
+ }
409
+ preparedRequestRef.current = null;
410
+ setRetryRequest({
411
+ originalKey: idempotencyKey,
412
+ key: crypto.randomUUID(),
413
+ attemptId: source,
414
+ });
415
+ }, className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.retry })) : null] }));
416
+ 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: () => {
417
+ var _a;
418
+ setMountedPaymentAttemptId(((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId) || null);
419
+ }, onSubmit: () => {
420
+ var _a, _b, _c;
421
+ if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
422
+ paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
423
+ void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
424
+ }
425
+ setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
426
+ }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
427
+ var _a, _b;
428
+ setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
429
+ trackFailure(copyRef.current.error, 'payment_form', (_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId);
430
+ (_b = onErrorRef.current) === null || _b === void 0 ? void 0 : _b.call(onErrorRef, copyRef.current.error);
431
+ } }, view.checkout.attemptId)) : null;
432
+ if (layout === 'wallet') {
433
+ return (_jsxs("div", { className: 'solidgate-inline-wallet', onClick: (event) => {
434
+ var _a;
435
+ const attemptId = (_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId;
436
+ if (attemptId && startedAttemptRef.current !== attemptId
437
+ && event.target instanceof Element && event.target.closest('[data-solidgate-wallet]')) {
438
+ startedAttemptRef.current = attemptId;
439
+ void trackSolidgateCheckoutEvent('checkout_started', attemptId, checkoutAnalyticsRef.current);
440
+ }
441
+ }, children: [paymentForm, statusMessage && !showPreparingLoader ? _jsx("p", { role: 'status', children: statusMessage }) : null, recoveryActions, _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
442
+ }
382
443
  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
444
  'solidgate-checkout-dialog',
384
445
  showPreparingLoader ? 'is-preparing' : '',
@@ -386,26 +447,5 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
386
447
  ].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
448
  setEmailInput(event.target.value);
388
449
  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 })] }));
450
+ }, 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
451
  }
@@ -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;
@@ -52,8 +56,10 @@ export type SolidgateCheckoutDialogProps = {
52
56
  onCustomerEmailCommit?: (email: string) => Promise<string>;
53
57
  onSuccess?: (checkout: SolidgateCheckoutPreparation) => void | Promise<void>;
54
58
  onRetry?: () => void;
55
- onError?: (message: string) => void;
59
+ onError?: (message: string, code?: 'already_subscribed_to_product') => 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,8 +26,14 @@ 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
- && boundary.querySelector('iframe#solid-payment-form-iframe, iframe[name="solid-payment-form-iframe"]')) {
36
+ && boundary.querySelector('iframe#payment-form-iframe, iframe[name="payment-form-iframe"], iframe#solid-payment-form-iframe, iframe[name="solid-payment-form-iframe"]')) {
42
37
  mountedReportedRef.current = true;
43
38
  onMountedRef.current();
44
39
  }
@@ -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: false, 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('https://cdn.charge-auth.com/js/form.js');
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
+ }
@@ -1,5 +1,9 @@
1
1
  import { type FunnelSdkRuntimeConfigOverrides } from '@funnelsgrove/runtime';
2
2
  export type SolidgateCheckoutStatus = 'preparing' | 'requires_payment' | 'verifying' | 'succeeded' | 'failed' | 'expired';
3
+ export declare class SolidgateExistingSubscriptionError extends Error {
4
+ readonly code: "already_subscribed_to_product";
5
+ constructor();
6
+ }
3
7
  export type SolidgateCheckoutPreparation = {
4
8
  attemptId: string;
5
9
  display: {
@@ -31,6 +35,7 @@ export type PrepareSolidgateCheckoutInput = {
31
35
  checkoutType?: 'subscription' | 'one_time' | null;
32
36
  environment?: 'test' | 'live' | null;
33
37
  idempotencyKey: string;
38
+ retryAttemptId?: string | null;
34
39
  discountKeyOrProviderId?: string | null;
35
40
  returnUrl: string;
36
41
  successUrl: string;
@@ -49,6 +54,7 @@ export type SolidgateCheckoutStatusResponse = {
49
54
  attemptId: string;
50
55
  status: SolidgateCheckoutStatus;
51
56
  retryAfterMs: number | null;
57
+ failureCode?: string;
52
58
  };
53
59
  export type PollSolidgateCheckoutStatusResult = SolidgateCheckoutStatusResponse & {
54
60
  timedOut: boolean;
@@ -1,4 +1,11 @@
1
1
  import { paywallCheckoutReturnUrl, retainPaywallCheckout, FUNNEL_ID, FUNNEL_SDK_PUBLISHABLE_KEY, buildMainApiUrl, resolvePreviewPaymentPublishableKey, } from '@funnelsgrove/runtime';
2
+ export class SolidgateExistingSubscriptionError extends Error {
3
+ constructor() {
4
+ super('You already have an active subscription to this product.');
5
+ this.code = 'already_subscribed_to_product';
6
+ this.name = 'SolidgateExistingSubscriptionError';
7
+ }
8
+ }
2
9
  const trimTrailingSlash = (value) => value.replace(/\/+$/, '');
3
10
  const requiredString = (value, name) => {
4
11
  const normalized = typeof value === 'string' ? value.trim() : '';
@@ -28,6 +35,13 @@ const postJson = async (path, body, runtimeConfig, signal) => {
28
35
  signal,
29
36
  });
30
37
  if (!response.ok) {
38
+ if (response.status === 409) {
39
+ const payload = await response.json().catch(() => null);
40
+ if (payload && typeof payload === 'object' && 'code' in payload
41
+ && payload.code === 'already_subscribed_to_product') {
42
+ throw new SolidgateExistingSubscriptionError();
43
+ }
44
+ }
31
45
  throw new Error('Solidgate checkout is temporarily unavailable');
32
46
  }
33
47
  return response.json();
@@ -103,11 +117,8 @@ const parseStatus = (value) => {
103
117
  && (typeof retryAfterMs !== 'number' || !Number.isFinite(retryAfterMs) || retryAfterMs < 0)) {
104
118
  throw new Error('Solidgate checkout status returned an invalid retry delay');
105
119
  }
106
- return {
107
- attemptId: requiredString(value.attemptId, 'attemptId'),
108
- status: value.status,
109
- retryAfterMs,
110
- };
120
+ return Object.assign({ attemptId: requiredString(value.attemptId, 'attemptId'), status: value.status, retryAfterMs }, (value.status === 'failed' && typeof value.failureCode === 'string'
121
+ && /^\d\.\d{2}$/.test(value.failureCode) ? { failureCode: value.failureCode } : {}));
111
122
  };
112
123
  export const prepareSolidgateCheckout = async (input) => {
113
124
  var _a, _b, _c;
@@ -118,23 +129,8 @@ export const prepareSolidgateCheckout = async (input) => {
118
129
  if (!paymentProfileId || !providerPlanId || !checkoutType || !environment) {
119
130
  throw new Error('Solidgate V2 checkout selection is incomplete');
120
131
  }
121
- const result = parsePreparation(await postJson('/sdk/public/v2/payments/checkout-sessions', {
122
- checkoutType,
123
- uiMode: 'custom',
124
- environment,
125
- paymentProfileId,
126
- provider: 'solidgate',
127
- providerPlanId,
128
- planKey: requiredString(input.funnelPlanKey, 'funnelPlanKey'),
129
- funnelId: resolveFunnelId(input.funnelId, input.runtimeConfig),
130
- funnelEndUserId: requiredString(input.funnelEndUserId, 'funnelEndUserId'),
131
- offerSetId: requiredString(input.offerSetId, 'offerSetId'),
132
- idempotencyKey: requiredString(input.idempotencyKey, 'idempotencyKey'),
133
- couponId: ((_c = input.discountKeyOrProviderId) === null || _c === void 0 ? void 0 : _c.trim()) || null,
134
- returnUrl: paywallCheckoutReturnUrl(requiredString(input.returnUrl, 'returnUrl'), 'solidgate'),
135
- successUrl: paywallCheckoutReturnUrl(requiredString(input.successUrl, 'successUrl'), 'solidgate'),
136
- failUrl: paywallCheckoutReturnUrl(requiredString(input.failUrl, 'failUrl'), 'solidgate', true),
137
- }, input.runtimeConfig, input.signal));
132
+ const result = parsePreparation(await postJson('/sdk/public/v2/payments/checkout-sessions', Object.assign(Object.assign({ checkoutType, uiMode: 'custom', environment,
133
+ paymentProfileId, provider: 'solidgate', providerPlanId, planKey: requiredString(input.funnelPlanKey, 'funnelPlanKey'), funnelId: resolveFunnelId(input.funnelId, input.runtimeConfig), funnelEndUserId: requiredString(input.funnelEndUserId, 'funnelEndUserId'), offerSetId: requiredString(input.offerSetId, 'offerSetId'), idempotencyKey: requiredString(input.idempotencyKey, 'idempotencyKey') }, (input.retryAttemptId ? { retryAttemptId: input.retryAttemptId } : {})), { couponId: ((_c = input.discountKeyOrProviderId) === null || _c === void 0 ? void 0 : _c.trim()) || null, returnUrl: paywallCheckoutReturnUrl(requiredString(input.returnUrl, 'returnUrl'), 'solidgate'), successUrl: paywallCheckoutReturnUrl(requiredString(input.successUrl, 'successUrl'), 'solidgate'), failUrl: paywallCheckoutReturnUrl(requiredString(input.failUrl, 'failUrl'), 'solidgate', true) }), input.runtimeConfig, input.signal));
138
134
  retainPaywallCheckout('solidgate', result.attemptId);
139
135
  return result;
140
136
  };
@@ -0,0 +1 @@
1
+ export declare const solidgateDeclineMessage: (code?: string) => string;
@@ -0,0 +1,24 @@
1
+ // Customer-safe descriptions; never render raw processor messages or risk decisions.
2
+ // https://docs.solidgate.com/payments/payments-insights/error-codes/
3
+ const messages = {
4
+ '0.02': 'The payment session expired. Start a new checkout to continue.',
5
+ '2.04': 'This card type is not supported. Use another payment method.',
6
+ '2.06': 'The security code is incorrect. Check the code on your card and try again.',
7
+ '2.08': 'The card number is invalid. Check the number and try again.',
8
+ '2.09': 'The card expiration date is invalid. Check the date or use another card.',
9
+ '2.14': 'We could not start this subscription. Check whether you already have access, or contact support.',
10
+ '2.15': 'Your bank requires additional verification. Try again and complete the bank verification step.',
11
+ '3.01': 'Your bank has blocked this card. Contact your bank or use another payment method.',
12
+ '3.02': 'Your card has insufficient funds. Add funds or use another payment method.',
13
+ '3.03': 'This payment exceeds your card limit. Check your limit with your bank or use another card.',
14
+ '3.04': 'Your bank declined this payment. Contact your bank or use another payment method.',
15
+ '3.05': 'Contact your bank to authorize this payment, or use another payment method.',
16
+ '3.06': 'Debit cards cannot be used for this payment. Use a credit card or another payment method.',
17
+ '3.07': 'This card type is not supported. Use another payment method.',
18
+ '3.09': 'Your bank requires additional verification. Try again and complete the bank verification step.',
19
+ '3.11': 'Recurring payments are not authorized for this card. Use another payment method.',
20
+ '3.13': 'The payment was cancelled. Try again and keep the payment window open until it finishes.',
21
+ '4.08': 'The billing address does not match your card. Check the address with your bank and try again.',
22
+ };
23
+ export const solidgateDeclineMessage = (code) => ((code && Object.hasOwn(messages, code) ? messages[code] : undefined)
24
+ || 'Your payment was declined. Use another payment method or contact your bank.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",