@funnelsgrove/payments 0.23.0 → 0.23.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -68,6 +68,23 @@ unchanged. Set `errorMessagePolicy: 'content'` to render load/cancel failures fr
|
|
|
68
68
|
the content's `errorMessages`. The default `'server'` policy preserves existing
|
|
69
69
|
error rendering independently of the selected labels.
|
|
70
70
|
|
|
71
|
+
## Checkout email prompt
|
|
72
|
+
|
|
73
|
+
When the access email is missing or invalid, wallet surfaces render nothing.
|
|
74
|
+
The funnel's primary payment CTA (such as Get My Plan) opens `CheckoutEmailDialog`
|
|
75
|
+
and saves the email through the existing caller-owned identity update. Once the
|
|
76
|
+
caller adopts the saved email, wallet controls can mount. Provider email rejections
|
|
77
|
+
still open the surface's repair dialog; saving never confirms the old session.
|
|
78
|
+
|
|
79
|
+
`CheckoutEmailDialog` defaults to a light surface regardless of the browser or
|
|
80
|
+
ancestor theme. It inherits the funnel font. Set inherited `--checkout-email-*`
|
|
81
|
+
CSS variables on the funnel root or `.checkout-email-dialog` to customize it:
|
|
82
|
+
`background`, `foreground`, `muted`, `border`, `input-background`,
|
|
83
|
+
`button-background`, `button-foreground`, `focus`, and `error`.
|
|
84
|
+
For example, `--checkout-email-button-background: var(--color-primary)` uses the
|
|
85
|
+
funnel accent. Set foreground/background pairs together for custom dark surfaces.
|
|
86
|
+
Existing `dialog.checkout-email-dialog` CSS overrides continue to work.
|
|
87
|
+
|
|
71
88
|
## Advanced Wallet Primitives
|
|
72
89
|
|
|
73
90
|
`ApplePaySubscriptionCheckoutSlot`, `GooglePaySubscriptionCheckoutSlot`, and `WalletSubscriptionCheckoutSlot` stay exported and runtime-compatible. They are backward-compatible advanced low-level primitives: reach for them only when a placement needs wiring the shared wallet surfaces do not cover. Never share one checkout hook result across placements; give each visible placement its own session.
|
|
@@ -47,20 +47,19 @@ export function CheckoutEmailDialog({ customerEmail, rejectedEmail, onSave, onCl
|
|
|
47
47
|
if (!busyRef.current)
|
|
48
48
|
onClose();
|
|
49
49
|
}, children: [_jsxs("form", { onSubmit: submit, noValidate: true, "aria-busy": busy, children: [_jsx("h2", { id: titleId, children: title }), _jsx("p", { children: description }), _jsxs("label", { children: [emailLabel, _jsx("input", { type: 'email', autoComplete: 'email', inputMode: 'email', autoFocus: true, className: 'bg-background text-foreground border-border', value: draft, placeholder: placeholder, disabled: busy, "aria-invalid": Boolean(error), "aria-describedby": error ? errorId : undefined, onChange: (event) => { setDraft(event.target.value); setError(null); } })] }), error ? _jsx("p", { id: errorId, role: 'alert', children: error }) : null, _jsxs("div", { className: 'checkout-email-dialog-actions', children: [_jsx("button", { type: 'button', className: 'bg-background text-foreground border-border', disabled: busy, onClick: onClose, children: cancelLabel }), _jsx("button", { type: 'submit', className: 'bg-accent text-foreground border-border', disabled: busy, children: busy ? 'Saving…' : continueLabel })] })] }), _jsx("style", { children: `
|
|
50
|
-
.checkout-email-dialog { color-scheme: light
|
|
51
|
-
.
|
|
52
|
-
.
|
|
53
|
-
.checkout-email-dialog
|
|
54
|
-
.checkout-email-dialog
|
|
55
|
-
.checkout-email-dialog
|
|
56
|
-
.checkout-email-dialog
|
|
57
|
-
.checkout-email-dialog
|
|
58
|
-
.checkout-email-dialog input::placeholder { color: GrayText; }
|
|
59
|
-
.checkout-email-dialog [role=alert] { margin-top: 12px; font-weight: 600; }
|
|
50
|
+
.checkout-email-dialog { color-scheme: light; box-sizing: border-box; width: min(420px, calc(100vw - 32px)); max-height: calc(100dvh - 32px); overflow-y: auto; margin: auto; padding: 24px; border: 1px solid var(--checkout-email-border, #e3e6ed); border-radius: 24px; background: var(--checkout-email-background, #fff); color: var(--checkout-email-foreground, #252936); box-shadow: 0 24px 64px rgb(0 0 0 / 18%), 0 4px 16px rgb(0 0 0 / 6%); font: inherit; }
|
|
51
|
+
.checkout-email-dialog::backdrop { background: rgb(0 0 0 / 35%); }
|
|
52
|
+
.checkout-email-dialog h2 { color: inherit; margin: 0 0 8px; font-size: 24px; line-height: 1.2; }
|
|
53
|
+
.checkout-email-dialog p { color: var(--checkout-email-muted, #626978); margin: 0 0 20px; line-height: 1.5; }
|
|
54
|
+
.checkout-email-dialog label { display: grid; gap: 8px; font-weight: 600; }
|
|
55
|
+
.checkout-email-dialog input { box-sizing: border-box; width: 100%; min-height: 48px; padding: 12px; border: 1px solid var(--checkout-email-border, #e3e6ed); border-radius: 12px; background: var(--checkout-email-input-background, #f7f8fb); color: inherit; font: inherit; font-size: 16px; }
|
|
56
|
+
.checkout-email-dialog input::placeholder { color: var(--checkout-email-muted, #626978); }
|
|
57
|
+
.checkout-email-dialog [role=alert] { margin-top: 12px; color: var(--checkout-email-error, #b42318); font-weight: 600; }
|
|
60
58
|
.checkout-email-dialog-actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 24px; }
|
|
61
|
-
.checkout-email-dialog button { flex: 1; padding: 12px; min-height: 48px; border: 1px solid
|
|
62
|
-
.checkout-email-dialog button[type=submit] { font-weight: 700; border:
|
|
59
|
+
.checkout-email-dialog button { flex: 1; padding: 12px; min-height: 48px; border: 1px solid var(--checkout-email-border, #e3e6ed); border-radius: 12px; background: var(--checkout-email-background, #fff); color: inherit; font: inherit; cursor: pointer; }
|
|
60
|
+
.checkout-email-dialog button[type=submit] { font-weight: 700; border-color: transparent; background: var(--checkout-email-button-background, #3156d3); color: var(--checkout-email-button-foreground, #fff); }
|
|
61
|
+
.checkout-email-dialog button:hover:not(:disabled) { filter: brightness(.96); }
|
|
63
62
|
.checkout-email-dialog button:disabled { opacity: .6; cursor: wait; }
|
|
64
|
-
.checkout-email-dialog :focus-visible { outline: 2px solid
|
|
63
|
+
.checkout-email-dialog :focus-visible { outline: 2px solid var(--checkout-email-focus, #3156d3); outline-offset: 3px; }
|
|
65
64
|
` })] }));
|
|
66
65
|
}
|
|
@@ -59,6 +59,7 @@ export type StripeWalletSurfaceProps = {
|
|
|
59
59
|
containerClassName?: string;
|
|
60
60
|
customerEmail?: string | null;
|
|
61
61
|
onCustomerEmailCommit?: CheckoutEmailDialogProps['onSave'];
|
|
62
|
+
/** @deprecated Resolve the email from the funnel's primary payment CTA instead. */
|
|
62
63
|
onResolveCustomerEmail?: () => Promise<string | null>;
|
|
63
64
|
emailDialogContent?: Pick<CheckoutEmailDialogProps, 'title' | 'description' | 'emailLabel' | 'placeholder' | 'invalidMessage' | 'continueLabel' | 'cancelLabel'>;
|
|
64
65
|
customerName?: string | null;
|
|
@@ -84,4 +85,4 @@ export type StripeWalletSurfaceProps = {
|
|
|
84
85
|
surfaceId: string;
|
|
85
86
|
suspended?: boolean;
|
|
86
87
|
};
|
|
87
|
-
export declare function StripeWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkoutAnalytics, className, containerClassName, customerEmail, onCustomerEmailCommit,
|
|
88
|
+
export declare function StripeWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkoutAnalytics, className, containerClassName, customerEmail, onCustomerEmailCommit, emailDialogContent, customerName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, returnUrl, session, slotClassName, summaryLabel, surfaceId, suspended, }: StripeWalletSurfaceProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -76,8 +76,8 @@ function buildAvailabilitySignature(availability) {
|
|
|
76
76
|
.join(','),
|
|
77
77
|
].join('|');
|
|
78
78
|
}
|
|
79
|
-
export function StripeWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkoutAnalytics, className, containerClassName, customerEmail, onCustomerEmailCommit,
|
|
80
|
-
var _a, _b
|
|
79
|
+
export function StripeWalletSurface({ amountCents, appearance, applePayOptions, applePayPlaceholderLabel, beforeConfirm, checkoutAnalytics, className, containerClassName, customerEmail, onCustomerEmailCommit, emailDialogContent, customerName, disabled, googlePayOptions, googlePayPlaceholderLabel, onAvailabilityChange, onCancel, onError, onPaymentInfoSubmitted, onSuccess, onUnavailableClick, paymentMethods, renderHeader, resolveMethodClassName, resolveMethodWrapperClassName, returnUrl, session, slotClassName, summaryLabel, surfaceId, suspended = false, }) {
|
|
80
|
+
var _a, _b;
|
|
81
81
|
const [emailDialogOpen, setEmailDialogOpen] = useState(false);
|
|
82
82
|
const [rejectedEmail, setRejectedEmail] = useState(null);
|
|
83
83
|
const [savedEmail, setSavedEmail] = useState(null);
|
|
@@ -85,32 +85,10 @@ export function StripeWalletSurface({ amountCents, appearance, applePayOptions,
|
|
|
85
85
|
const needsEmail = !isValidCheckoutEmail(normalizedEmail)
|
|
86
86
|
|| rejectedEmail === normalizedEmail
|
|
87
87
|
|| (savedEmail !== null && savedEmail !== normalizedEmail);
|
|
88
|
-
const [resolvingEmail, setResolvingEmail] = useState(false);
|
|
89
88
|
useEffect(() => {
|
|
90
89
|
if (savedEmail === normalizedEmail)
|
|
91
90
|
setSavedEmail(null);
|
|
92
91
|
}, [normalizedEmail, savedEmail]);
|
|
93
|
-
const openEmailDialog = async () => {
|
|
94
|
-
if (resolvingEmail)
|
|
95
|
-
return;
|
|
96
|
-
if (!onCustomerEmailCommit) {
|
|
97
|
-
await (onUnavailableClick === null || onUnavailableClick === void 0 ? void 0 : onUnavailableClick());
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
setResolvingEmail(true);
|
|
101
|
-
try {
|
|
102
|
-
const resolved = await (onResolveCustomerEmail === null || onResolveCustomerEmail === void 0 ? void 0 : onResolveCustomerEmail());
|
|
103
|
-
if (isValidCheckoutEmail(resolved) && (resolved === null || resolved === void 0 ? void 0 : resolved.trim()) !== rejectedEmail)
|
|
104
|
-
return;
|
|
105
|
-
setEmailDialogOpen(true);
|
|
106
|
-
}
|
|
107
|
-
catch (_a) {
|
|
108
|
-
setEmailDialogOpen(true);
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
setResolvingEmail(false);
|
|
112
|
-
}
|
|
113
|
-
};
|
|
114
92
|
const platformWalletPaymentMethods = usePlatformWalletPaymentMethods();
|
|
115
93
|
const walletPaymentMethods = normalizeStripeWalletSurfaceMethods(paymentMethods !== null && paymentMethods !== void 0 ? paymentMethods : platformWalletPaymentMethods);
|
|
116
94
|
const normalizedSurfaceId = surfaceId.trim();
|
|
@@ -192,14 +170,16 @@ export function StripeWalletSurface({ amountCents, appearance, applePayOptions,
|
|
|
192
170
|
suspended,
|
|
193
171
|
});
|
|
194
172
|
if (needsEmail && session.configured && !suspended && walletPaymentMethods.length > 0) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
173
|
+
if (!emailDialogOpen || !onCustomerEmailCommit)
|
|
174
|
+
return null;
|
|
175
|
+
return (_jsx("div", { className: containerClassName, "data-wallet-surface-id": normalizedSurfaceId, children: _jsx(CheckoutEmailDialog, Object.assign({}, emailDialogContent, { customerEmail: normalizedEmail, rejectedEmail: rejectedEmail, onClose: () => setEmailDialogOpen(false), onSave: async (email) => {
|
|
176
|
+
const saved = await onCustomerEmailCommit(email);
|
|
177
|
+
const nextEmail = normalizeCheckoutEmailResult(saved, email, emailDialogContent === null || emailDialogContent === void 0 ? void 0 : emailDialogContent.invalidMessage);
|
|
178
|
+
setSavedEmail(nextEmail);
|
|
179
|
+
setRejectedEmail(null);
|
|
180
|
+
session.setError(null);
|
|
181
|
+
return nextEmail;
|
|
182
|
+
} })) }));
|
|
203
183
|
}
|
|
204
184
|
if (!shouldRenderSurface) {
|
|
205
185
|
return null;
|
|
@@ -209,7 +189,7 @@ export function StripeWalletSurface({ amountCents, appearance, applePayOptions,
|
|
|
209
189
|
containerClassName !== null && containerClassName !== void 0 ? containerClassName : '',
|
|
210
190
|
]
|
|
211
191
|
.filter(Boolean)
|
|
212
|
-
.join(' '), "data-wallet-surface-id": normalizedSurfaceId, children: [(
|
|
192
|
+
.join(' '), "data-wallet-surface-id": normalizedSurfaceId, children: [(_b = renderHeader === null || renderHeader === void 0 ? void 0 : renderHeader({ availability })) !== null && _b !== void 0 ? _b : null, visibleWalletPaymentMethods.map((method) => {
|
|
213
193
|
var _a;
|
|
214
194
|
return (_jsx("div", { className: [
|
|
215
195
|
'stripe-subscription-wallet-surface__method',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@funnelsgrove/payments",
|
|
3
|
-
"version": "0.23.
|
|
3
|
+
"version": "0.23.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/The-Solid-Grove/funnelsgrove.git",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"jsdom": "20.0.3",
|
|
47
47
|
"typescript": "^5",
|
|
48
48
|
"vitest": "^3.2.4",
|
|
49
|
-
"@funnelsgrove/runtime": "0.21.
|
|
50
|
-
"@funnelsgrove/analytics": "0.4.
|
|
49
|
+
"@funnelsgrove/runtime": "0.21.1",
|
|
50
|
+
"@funnelsgrove/analytics": "0.4.1"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
|
-
"@funnelsgrove/runtime": "^0.21.
|
|
54
|
-
"@funnelsgrove/analytics": "^0.4.
|
|
53
|
+
"@funnelsgrove/runtime": "^0.21.1",
|
|
54
|
+
"@funnelsgrove/analytics": "^0.4.1"
|
|
55
55
|
}
|
|
56
56
|
}
|