@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.
- package/dist/generated/package-version.d.ts +1 -1
- package/dist/generated/package-version.js +1 -1
- package/dist/providers/solidgate/components/SolidgateCheckoutDialog.d.ts +1 -1
- package/dist/providers/solidgate/components/SolidgateCheckoutDialog.js +80 -40
- package/dist/providers/solidgate/components/SolidgateCheckoutDialog.styles.d.ts +1 -1
- package/dist/providers/solidgate/components/SolidgateCheckoutDialog.styles.js +89 -27
- package/dist/providers/solidgate/components/SolidgateCheckoutDialog.types.d.ts +7 -1
- package/dist/providers/solidgate/components/SolidgateCheckoutSummary.js +1 -1
- package/dist/providers/solidgate/components/SolidgatePaymentForm.d.ts +5 -1
- package/dist/providers/solidgate/components/SolidgatePaymentForm.js +24 -54
- package/dist/providers/solidgate/components/SolidgateSdkPayment.d.ts +8 -0
- package/dist/providers/solidgate/components/SolidgateSdkPayment.js +66 -0
- package/dist/providers/solidgate/services/solidgate.service.d.ts +6 -0
- package/dist/providers/solidgate/services/solidgate.service.js +18 -22
- package/dist/providers/solidgate/services/solidgateDeclineMessage.d.ts +1 -0
- package/dist/providers/solidgate/services/solidgateDeclineMessage.js +24 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PAYMENTS_PACKAGE_VERSION = "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.
|
|
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,
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
(
|
|
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,
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
(
|
|
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 (!
|
|
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.
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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:
|
|
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-
|
|
365
|
-
|
|
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
|
-
|
|
369
|
-
|
|
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
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
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
|
-
|
|
384
|
-
|
|
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 }),
|
|
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,
|
|
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 {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
})
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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.');
|