@funnelsgrove/payments 0.7.12 → 0.7.14

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,5 +1,5 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { publicAnalyticsSdk } from '@funnelsgrove/analytics';
4
4
  import { Suspense, lazy, useCallback, useEffect, useMemo, useRef, useState, } from 'react';
5
5
  import { pollSolidgateCheckoutStatus, prepareSolidgateCheckout, } from '../services/solidgate.service.js';
@@ -84,7 +84,7 @@ function SolidgatePaymentForm({ checkout, hidden, loadingLabel, onError, onMount
84
84
  safeDestroyPaymentForm(instanceRef.current);
85
85
  instanceRef.current = null;
86
86
  }, [checkout.attemptId]);
87
- return (_jsxs("div", { className: hidden ? 'hidden' : 'block', "data-solidgate-attempt-id": checkout.attemptId, "aria-hidden": hidden || undefined, children: [_jsxs("div", { className: 'mb-3 grid gap-3', 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: 'text-sm text-muted-foreground', children: loadingLabel })), children: _jsx(SolidgatePayment, { merchantData: checkout.initialization.merchantData, width: '100%', formParams: { isSolidLogoVisible: true }, applePayContainerRef: applePayContainerRef, googlePayContainerRef: googlePayContainerRef, onReadyPaymentInstance: (instance) => {
87
+ return (_jsxs("div", { 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: { isSolidLogoVisible: true }, applePayContainerRef: applePayContainerRef, googlePayContainerRef: googlePayContainerRef, onReadyPaymentInstance: (instance) => {
88
88
  if (instanceRef.current && instanceRef.current !== instance) {
89
89
  try {
90
90
  instanceRef.current.unsubscribeAll();
@@ -104,6 +104,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
104
104
  const [committedEmail, setCommittedEmail] = useState(normalizedCustomerEmail);
105
105
  const [emailError, setEmailError] = useState(null);
106
106
  const [emailSubmitting, setEmailSubmitting] = useState(false);
107
+ const [mountedPaymentAttemptId, setMountedPaymentAttemptId] = useState(null);
107
108
  const prepareAbortRef = useRef(null);
108
109
  const statusAbortRef = useRef(null);
109
110
  const successAttemptRef = useRef(null);
@@ -192,6 +193,7 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
192
193
  prepareAbortRef.current = controller;
193
194
  successAttemptRef.current = null;
194
195
  paymentInfoAttemptRef.current = null;
196
+ setMountedPaymentAttemptId(null);
195
197
  queueMicrotask(() => {
196
198
  if (!controller.signal.aborted) {
197
199
  setState(initialView);
@@ -213,6 +215,9 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
213
215
  });
214
216
  return () => controller.abort();
215
217
  }, [committedEmail, normalizedRequest, open, requiresEmailCommit, setState]);
218
+ const paymentFormMounted = Boolean(view.checkout && mountedPaymentAttemptId === view.checkout.attemptId);
219
+ const showPreparingLoader = view.state === 'loading'
220
+ || (view.state === 'ready' && !paymentFormMounted);
216
221
  useEffect(() => {
217
222
  var _a;
218
223
  if (!open) {
@@ -227,12 +232,14 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
227
232
  }
228
233
  };
229
234
  window.addEventListener('keydown', handleKeyDown);
230
- (_a = closeButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
235
+ if (!showPreparingLoader) {
236
+ (_a = closeButtonRef.current) === null || _a === void 0 ? void 0 : _a.focus();
237
+ }
231
238
  return () => {
232
239
  window.removeEventListener('keydown', handleKeyDown);
233
240
  previouslyFocused === null || previouslyFocused === void 0 ? void 0 : previouslyFocused.focus();
234
241
  };
235
- }, [open]);
242
+ }, [open, showPreparingLoader]);
236
243
  useEffect(() => () => {
237
244
  var _a, _b;
238
245
  (_a = prepareAbortRef.current) === null || _a === void 0 ? void 0 : _a.abort();
@@ -330,23 +337,220 @@ export function SolidgateCheckoutDialog({ open, request, customerEmail, checkout
330
337
  : view.state === 'recoverable_error'
331
338
  ? copy.error
332
339
  : null;
333
- return (_jsx("div", { className: 'fixed inset-0 z-50 flex items-center justify-center bg-black/60 p-4', children: _jsxs("div", { role: 'dialog', "aria-modal": 'true', "aria-labelledby": 'solidgate-checkout-title', "data-checkout-state": view.state, className: 'w-full max-w-lg rounded-2xl border border-border bg-card p-5 text-foreground shadow-xl', children: [_jsxs("div", { className: 'mb-4 flex items-center justify-between gap-4', children: [_jsx("h2", { id: 'solidgate-checkout-title', className: 'text-lg font-semibold text-foreground', children: copy.title }), _jsx("button", { ref: closeButtonRef, type: 'button', onClick: onClose, "aria-label": copy.closeAriaLabel, className: 'rounded-md border border-border bg-background px-3 py-1.5 text-foreground hover:bg-accent', children: "\u00D7" })] }), view.state === 'collecting_email' ? (_jsxs("form", { className: 'grid gap-4', onSubmit: (event) => void commitCustomerEmail(event), children: [_jsxs("label", { className: 'grid gap-2 text-sm font-medium text-foreground', children: [copy.customerEmailLabel, _jsx("input", { type: 'email', autoComplete: 'email', inputMode: 'email', maxLength: 320, required: true, value: emailInput, onChange: (event) => {
334
- setEmailInput(event.target.value);
335
- setEmailError(null);
336
- }, placeholder: copy.customerEmailPlaceholder, "aria-invalid": Boolean(emailError), className: 'w-full rounded-lg border border-border bg-background px-3 py-2 text-foreground placeholder:text-muted-foreground' })] }), emailError ? (_jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: emailError })) : null, _jsx("button", { type: 'submit', disabled: emailSubmitting, className: 'w-full rounded-lg border border-border bg-accent px-4 py-2 font-medium text-foreground disabled:opacity-60', children: copy.customerEmailContinue })] })) : null, view.checkout ? (_jsx(SolidgatePaymentForm, { checkout: view.checkout, hidden: !showForm, loadingLabel: copy.loading, onMounted: () => {
337
- if (view.state === 'loading') {
338
- setState(Object.assign(Object.assign({}, view), { state: 'ready' }));
339
- }
340
- }, onSubmit: () => {
341
- var _a, _b, _c;
342
- if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
343
- paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
344
- void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
345
- }
346
- setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
347
- }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
348
- var _a;
349
- setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
350
- (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, copyRef.current.error);
351
- } }, view.checkout.attemptId)) : null, statusMessage && view.state !== 'ready' ? (_jsx("div", { className: 'rounded-xl border border-border bg-background p-4 text-sm text-muted-foreground', children: _jsx("p", { role: 'status', "aria-live": 'polite', children: statusMessage }) })) : null, view.state === 'verifying' && view.canResumeVerification ? (_jsx("button", { type: 'button', onClick: () => void verifyCanonicalStatus(), className: 'mt-4 w-full rounded-lg border border-border bg-accent px-4 py-2 font-medium text-foreground', children: copy.resumeVerification })) : null, (view.state === 'recoverable_error' || view.state === 'expired') && onRetry ? (_jsx("button", { type: 'button', onClick: onRetry, className: 'mt-4 w-full rounded-lg border border-border bg-accent px-4 py-2 font-medium text-foreground', children: copy.retry })) : null] }) }));
340
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: 'solidgate-checkout-overlay', 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: `solidgate-checkout-dialog${showPreparingLoader ? ' is-preparing' : ''}`, children: [_jsxs("div", { className: 'solidgate-checkout-header', children: [_jsx("h2", { id: 'solidgate-checkout-title', className: 'solidgate-checkout-title', children: copy.title }), _jsx("button", { ref: closeButtonRef, type: 'button', onClick: onClose, "aria-label": copy.closeAriaLabel, className: 'solidgate-checkout-close', children: "\u00D7" })] }), 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) => {
341
+ setEmailInput(event.target.value);
342
+ setEmailError(null);
343
+ }, 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, { checkout: view.checkout, hidden: !showForm, loadingLabel: copy.loading, onMounted: () => {
344
+ var _a;
345
+ setMountedPaymentAttemptId(((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId) || null);
346
+ }, onSubmit: () => {
347
+ var _a, _b, _c;
348
+ if (paymentInfoAttemptRef.current !== ((_a = view.checkout) === null || _a === void 0 ? void 0 : _a.attemptId)) {
349
+ paymentInfoAttemptRef.current = ((_b = view.checkout) === null || _b === void 0 ? void 0 : _b.attemptId) || null;
350
+ void trackSolidgateCheckoutEvent('add_payment_info', ((_c = view.checkout) === null || _c === void 0 ? void 0 : _c.attemptId) || '', checkoutAnalyticsRef.current);
351
+ }
352
+ setState(Object.assign(Object.assign({}, view), { state: 'submitting' }));
353
+ }, onVerify: () => void verifyCanonicalStatus(), onError: () => {
354
+ var _a;
355
+ setState(Object.assign(Object.assign({}, view), { state: 'recoverable_error', canResumeVerification: false }));
356
+ (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, copyRef.current.error);
357
+ } }, view.checkout.attemptId)) : null, statusMessage && view.state !== 'ready' ? (_jsx("div", { className: 'solidgate-checkout-status', children: _jsx("p", { role: 'status', "aria-live": 'polite', children: statusMessage }) })) : null, view.state === 'verifying' && 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') && onRetry ? (_jsx("button", { type: 'button', onClick: onRetry, className: 'solidgate-checkout-action solidgate-checkout-action--spaced', children: copy.retry })) : null] })) : null] }), _jsx("style", { children: solidgateCheckoutDialogStyles })] }));
358
+ }
359
+ const solidgateCheckoutDialogStyles = `
360
+ .solidgate-checkout-overlay {
361
+ position: fixed;
362
+ inset: 0;
363
+ z-index: 100;
364
+ box-sizing: border-box;
365
+ display: flex;
366
+ align-items: center;
367
+ justify-content: center;
368
+ overflow: hidden;
369
+ padding: 16px;
370
+ background: rgb(17 24 39 / 60%);
371
+ backdrop-filter: blur(4px);
372
+ }
373
+
374
+ .solidgate-checkout-dialog {
375
+ box-sizing: border-box;
376
+ width: min(100%, 512px);
377
+ max-height: calc(100svh - 32px);
378
+ overflow-y: auto;
379
+ border: 1px solid var(--color-border, #d1d5db);
380
+ border-radius: 16px;
381
+ background: var(--color-surface, #fff);
382
+ padding: 20px;
383
+ color: var(--color-text, #111827);
384
+ box-shadow: 0 24px 80px rgb(17 24 39 / 22%);
385
+ }
386
+
387
+ .solidgate-checkout-dialog.is-preparing {
388
+ position: absolute;
389
+ visibility: hidden;
390
+ pointer-events: none;
391
+ }
392
+
393
+ .solidgate-checkout-loader {
394
+ display: grid;
395
+ place-items: center;
396
+ }
397
+
398
+ .solidgate-checkout-spinner {
399
+ box-sizing: border-box;
400
+ width: 44px;
401
+ height: 44px;
402
+ border: 4px solid var(--color-border, #d1d5db);
403
+ border-top-color: var(--color-primary, #22c55e);
404
+ border-radius: 50%;
405
+ animation: solidgate-checkout-spin 720ms linear infinite;
406
+ }
407
+
408
+ .solidgate-checkout-visually-hidden {
409
+ position: absolute;
410
+ width: 1px;
411
+ height: 1px;
412
+ overflow: hidden;
413
+ clip: rect(0 0 0 0);
414
+ clip-path: inset(50%);
415
+ white-space: nowrap;
416
+ }
417
+
418
+ @keyframes solidgate-checkout-spin {
419
+ to {
420
+ transform: rotate(360deg);
421
+ }
422
+ }
423
+
424
+ @media (prefers-reduced-motion: reduce) {
425
+ .solidgate-checkout-spinner {
426
+ animation-duration: 1.6s;
427
+ }
428
+ }
429
+
430
+ .solidgate-checkout-header {
431
+ display: flex;
432
+ align-items: center;
433
+ justify-content: space-between;
434
+ gap: 16px;
435
+ margin-bottom: 16px;
436
+ }
437
+
438
+ .solidgate-checkout-title {
439
+ margin: 0;
440
+ color: var(--color-text, #111827);
441
+ font-size: 18px;
442
+ font-weight: 600;
443
+ line-height: 1.3;
444
+ }
445
+
446
+ .solidgate-checkout-close {
447
+ width: 36px;
448
+ height: 36px;
449
+ flex: none;
450
+ border: 1px solid var(--color-border, #d1d5db);
451
+ border-radius: 8px;
452
+ background: var(--color-bg, #f9fafb);
453
+ color: var(--color-text, #111827);
454
+ font-size: 22px;
455
+ line-height: 1;
456
+ cursor: pointer;
457
+ }
458
+
459
+ .solidgate-checkout-close:hover {
460
+ background: var(--color-accent-soft, #eef2ff);
461
+ }
462
+
463
+ .solidgate-checkout-email-form,
464
+ .solidgate-checkout-email-label,
465
+ .solidgate-checkout-wallets {
466
+ display: grid;
467
+ }
468
+
469
+ .solidgate-checkout-email-form {
470
+ gap: 16px;
471
+ }
472
+
473
+ .solidgate-checkout-email-label {
474
+ gap: 8px;
475
+ color: var(--color-text, #111827);
476
+ font-size: 14px;
477
+ font-weight: 500;
478
+ }
479
+
480
+ .solidgate-checkout-email-input {
481
+ box-sizing: border-box;
482
+ width: 100%;
483
+ min-height: 44px;
484
+ border: 1px solid var(--color-border, #d1d5db);
485
+ border-radius: 8px;
486
+ background: var(--color-bg, #fff);
487
+ padding: 8px 12px;
488
+ color: var(--color-text, #111827);
489
+ font: inherit;
490
+ }
491
+
492
+ .solidgate-checkout-email-input::placeholder,
493
+ .solidgate-checkout-status,
494
+ .solidgate-checkout-status-text {
495
+ color: var(--color-text-muted, #6b7280);
496
+ }
497
+
498
+ .solidgate-checkout-email-input:focus-visible,
499
+ .solidgate-checkout-close:focus-visible,
500
+ .solidgate-checkout-action:focus-visible {
501
+ outline: 2px solid var(--color-accent, #3f51b5);
502
+ outline-offset: 2px;
503
+ }
504
+
505
+ .solidgate-checkout-alert {
506
+ margin: 0;
507
+ color: var(--color-danger, #b91c1c);
508
+ font-size: 14px;
509
+ }
510
+
511
+ .solidgate-checkout-action {
512
+ box-sizing: border-box;
513
+ width: 100%;
514
+ min-height: 44px;
515
+ border: 1px solid var(--color-accent, #3f51b5);
516
+ border-radius: 8px;
517
+ background: var(--color-accent, #3f51b5);
518
+ padding: 8px 16px;
519
+ color: var(--color-primary-text, #fff);
520
+ font: inherit;
521
+ font-weight: 600;
522
+ cursor: pointer;
523
+ }
524
+
525
+ .solidgate-checkout-action:disabled {
526
+ cursor: not-allowed;
527
+ opacity: 0.6;
528
+ }
529
+
530
+ .solidgate-checkout-action--spaced {
531
+ margin-top: 16px;
532
+ }
533
+
534
+ .solidgate-checkout-wallets {
535
+ gap: 12px;
536
+ margin-bottom: 12px;
537
+ }
538
+
539
+ .solidgate-checkout-payment[hidden] {
540
+ display: none;
541
+ }
542
+
543
+ .solidgate-checkout-status {
544
+ margin-top: 12px;
545
+ border: 1px solid var(--color-border, #d1d5db);
546
+ border-radius: 12px;
547
+ background: var(--color-bg, #f9fafb);
548
+ padding: 16px;
549
+ font-size: 14px;
550
+ }
551
+
552
+ .solidgate-checkout-status p,
553
+ .solidgate-checkout-status-text {
554
+ margin: 0;
352
555
  }
556
+ `;
@@ -984,12 +984,13 @@ const sharedStripeCheckoutV2Styles = `
984
984
  .shared-checkout-special-offer-gift--image {
985
985
  position: relative;
986
986
  width: min(100%, 250px);
987
+ aspect-ratio: 1;
987
988
  }
988
989
 
989
990
  .shared-checkout-special-offer-gift-image {
990
991
  display: block;
991
992
  width: 100%;
992
- aspect-ratio: 1;
993
+ height: 100%;
993
994
  background-position: center;
994
995
  background-repeat: no-repeat;
995
996
  background-size: contain;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnelsgrove/payments",
3
- "version": "0.7.12",
3
+ "version": "0.7.14",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",