@funnelsgrove/payments 0.1.15 → 0.1.17
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/components/ManageSubscriptionScreen.d.ts +27 -0
- package/dist/components/ManageSubscriptionScreen.js +449 -0
- package/dist/components/shared/SharedStripeCheckoutV2Dialog.js +0 -1
- package/dist/components/shared/StripeCheckoutExpressCheckoutButton.js +0 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type ManageSubscriptionContent, type ManageSubscriptionsResponse } from '@funnelsgrove/runtime';
|
|
2
|
+
import type { BillingFallbackPlanConfig, BillingPlanCatalog } from '../config/billing.config.js';
|
|
3
|
+
export type { ManageSubscriptionContent } from '@funnelsgrove/runtime';
|
|
4
|
+
export type ManageSubscriptionScreenProps = {
|
|
5
|
+
stepId: string;
|
|
6
|
+
content: ManageSubscriptionContent;
|
|
7
|
+
homeStepId?: string;
|
|
8
|
+
nodeId?: string;
|
|
9
|
+
plans?: BillingPlanCatalog | readonly BillingFallbackPlanConfig[];
|
|
10
|
+
};
|
|
11
|
+
type SubscriptionState = ManageSubscriptionsResponse;
|
|
12
|
+
type RuntimeSubscription = SubscriptionState['subscriptions'][number];
|
|
13
|
+
type ManageSubscription = RuntimeSubscription & {
|
|
14
|
+
providerPlanId?: string | null;
|
|
15
|
+
currentPeriodStart?: string | null;
|
|
16
|
+
createdAt?: string | null;
|
|
17
|
+
amountCents?: number | null;
|
|
18
|
+
currency?: string | null;
|
|
19
|
+
billingInterval?: string | null;
|
|
20
|
+
billingIntervalCount?: number | null;
|
|
21
|
+
};
|
|
22
|
+
export declare function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId, plans, }: ManageSubscriptionScreenProps): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export declare const __manageSubscriptionScreenTestables: {
|
|
24
|
+
resolveBillingDateLabel: (subscription: Pick<ManageSubscription, "cancelAtPeriodEnd" | "currentPeriodEnd" | "status">, options?: {
|
|
25
|
+
past?: boolean;
|
|
26
|
+
}) => string;
|
|
27
|
+
};
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
+
import { apiService, buildPreviewManageSubscriptionsFallback, isPreviewFrameRuntime, runtimePublicConfig, useFunnel, } from '@funnelsgrove/runtime';
|
|
5
|
+
import { getFallbackBillingPlans } from '../services/planCatalog.service.js';
|
|
6
|
+
const pastSubscriptionsNote = 'Past subscriptions are already canceled or expired. Dates below are historical, not upcoming charges.';
|
|
7
|
+
const isCancellableSubscription = (subscription) => {
|
|
8
|
+
const normalizedStatus = subscription.status.trim().toLowerCase();
|
|
9
|
+
return normalizedStatus !== 'canceled' && !subscription.cancelAtPeriodEnd;
|
|
10
|
+
};
|
|
11
|
+
const formatDate = (value) => {
|
|
12
|
+
if (!value) {
|
|
13
|
+
return 'n/a';
|
|
14
|
+
}
|
|
15
|
+
const timestamp = Date.parse(value);
|
|
16
|
+
if (!Number.isFinite(timestamp)) {
|
|
17
|
+
return 'n/a';
|
|
18
|
+
}
|
|
19
|
+
return new Intl.DateTimeFormat('en-US', {
|
|
20
|
+
month: 'numeric',
|
|
21
|
+
day: 'numeric',
|
|
22
|
+
year: 'numeric',
|
|
23
|
+
}).format(new Date(timestamp));
|
|
24
|
+
};
|
|
25
|
+
const formatSubscriptionNumber = (subscription) => {
|
|
26
|
+
const source = subscription.providerSubscriptionId || subscription.id;
|
|
27
|
+
const normalized = source.replace(/[^a-zA-Z0-9]/g, '');
|
|
28
|
+
return normalized.slice(-7) || source.slice(-7) || subscription.id;
|
|
29
|
+
};
|
|
30
|
+
const formatInterval = (interval, count) => {
|
|
31
|
+
const normalizedCount = count && Number.isFinite(count) && count > 0 ? count : 1;
|
|
32
|
+
const normalizedInterval = (interval === null || interval === void 0 ? void 0 : interval.trim()) || 'month';
|
|
33
|
+
const pluralSuffix = normalizedCount === 1 ? '' : 's';
|
|
34
|
+
return `${normalizedCount} ${normalizedInterval}${pluralSuffix}`;
|
|
35
|
+
};
|
|
36
|
+
const formatMoney = (amountCents, currency) => {
|
|
37
|
+
const normalizedCurrency = (currency === null || currency === void 0 ? void 0 : currency.trim().toLowerCase()) || 'usd';
|
|
38
|
+
return `${(amountCents / 100).toFixed(2)} ${normalizedCurrency}`;
|
|
39
|
+
};
|
|
40
|
+
const resolvePlanLine = (subscription, plans) => {
|
|
41
|
+
const plan = plans.find((item) => item.providerPlanId === subscription.providerPlanId);
|
|
42
|
+
if (plan) {
|
|
43
|
+
return `${formatMoney(plan.amountCents, 'usd')} / ${formatInterval(plan.billingInterval, 1)}`;
|
|
44
|
+
}
|
|
45
|
+
if (typeof subscription.amountCents === 'number' && subscription.amountCents > 0) {
|
|
46
|
+
return `${formatMoney(subscription.amountCents, subscription.currency)} / ${formatInterval(subscription.billingInterval, subscription.billingIntervalCount)}`;
|
|
47
|
+
}
|
|
48
|
+
return `${subscription.environment.toUpperCase()} subscription`;
|
|
49
|
+
};
|
|
50
|
+
const resolveCreatedDate = (subscription) => {
|
|
51
|
+
return formatDate(subscription.createdAt || subscription.currentPeriodStart);
|
|
52
|
+
};
|
|
53
|
+
const resolveBillingDateLabel = (subscription, options = {}) => {
|
|
54
|
+
const normalizedStatus = subscription.status.trim().toLowerCase();
|
|
55
|
+
if (subscription.cancelAtPeriodEnd) {
|
|
56
|
+
return `Active until: ${formatDate(subscription.currentPeriodEnd)}`;
|
|
57
|
+
}
|
|
58
|
+
if (options.past && normalizedStatus === 'canceled') {
|
|
59
|
+
return `Ended on: ${formatDate(subscription.currentPeriodEnd)}`;
|
|
60
|
+
}
|
|
61
|
+
if (options.past) {
|
|
62
|
+
return `Historical period ended: ${formatDate(subscription.currentPeriodEnd)}`;
|
|
63
|
+
}
|
|
64
|
+
return `Next charge: ${formatDate(subscription.currentPeriodEnd)}`;
|
|
65
|
+
};
|
|
66
|
+
const toManageSubscriptions = (subscriptions) => subscriptions;
|
|
67
|
+
export function ManageSubscriptionScreen({ stepId, content, homeStepId, nodeId, plans, }) {
|
|
68
|
+
var _a, _b;
|
|
69
|
+
const { attributes, completeStep, goToStep, setAnswer, setUser, user } = useFunnel();
|
|
70
|
+
const supportEmail = runtimePublicConfig.supportEmail;
|
|
71
|
+
const planList = useMemo(() => getFallbackBillingPlans(plans), [plans]);
|
|
72
|
+
const [stage, setStage] = useState('subscriptions');
|
|
73
|
+
const [data, setData] = useState(null);
|
|
74
|
+
const [loading, setLoading] = useState(true);
|
|
75
|
+
const [error, setError] = useState(null);
|
|
76
|
+
const [selectedReasonId, setSelectedReasonId] = useState('');
|
|
77
|
+
const [selectedSubscriptionId, setSelectedSubscriptionId] = useState(null);
|
|
78
|
+
const [cancelledActiveUntilDate, setCancelledActiveUntilDate] = useState(null);
|
|
79
|
+
const [cancelInFlight, setCancelInFlight] = useState(false);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
let active = true;
|
|
82
|
+
const load = async () => {
|
|
83
|
+
setLoading(true);
|
|
84
|
+
setError(null);
|
|
85
|
+
if (isPreviewFrameRuntime()) {
|
|
86
|
+
const payload = buildPreviewManageSubscriptionsFallback(apiService.getOrCreateClientUserId());
|
|
87
|
+
if (active) {
|
|
88
|
+
setData(payload);
|
|
89
|
+
setLoading(false);
|
|
90
|
+
}
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
const payload = await apiService.getManageSubscriptions();
|
|
95
|
+
if (active) {
|
|
96
|
+
setData(payload);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
catch (nextError) {
|
|
100
|
+
if (active) {
|
|
101
|
+
setError(nextError instanceof Error ? nextError.message : content.errorMessages.load);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
if (active) {
|
|
106
|
+
setLoading(false);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
void load();
|
|
111
|
+
return () => {
|
|
112
|
+
active = false;
|
|
113
|
+
};
|
|
114
|
+
}, [content.errorMessages.load]);
|
|
115
|
+
const subscriptions = useMemo(() => { var _a; return toManageSubscriptions((_a = data === null || data === void 0 ? void 0 : data.subscriptions) !== null && _a !== void 0 ? _a : []); }, [data]);
|
|
116
|
+
const activeSubscriptions = useMemo(() => subscriptions.filter(isCancellableSubscription), [subscriptions]);
|
|
117
|
+
const pastSubscriptions = useMemo(() => subscriptions.filter((subscription) => !isCancellableSubscription(subscription)), [subscriptions]);
|
|
118
|
+
const selectedSubscription = (_a = activeSubscriptions.find((subscription) => subscription.id === selectedSubscriptionId)) !== null && _a !== void 0 ? _a : null;
|
|
119
|
+
const selectedReason = (_b = content.whyStage.reasons.find((reason) => reason.id === selectedReasonId)) !== null && _b !== void 0 ? _b : null;
|
|
120
|
+
const handleSubscriptionSelected = (subscription) => {
|
|
121
|
+
if (!isCancellableSubscription(subscription)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
setSelectedSubscriptionId(subscription.id);
|
|
125
|
+
setAnswer('manageSubscriptionHasActive', true);
|
|
126
|
+
setAnswer('manageSubscriptionCancelledId', subscription.id);
|
|
127
|
+
setStage('why');
|
|
128
|
+
};
|
|
129
|
+
const handleReasonSelected = (reasonId) => {
|
|
130
|
+
setSelectedReasonId(reasonId);
|
|
131
|
+
setAnswer('manageSubscriptionReason', reasonId);
|
|
132
|
+
setStage('confirm');
|
|
133
|
+
};
|
|
134
|
+
const persistCancellationAnswers = async (input) => {
|
|
135
|
+
var _a;
|
|
136
|
+
const nextAttributes = Object.assign(Object.assign({}, attributes), { manageSubscriptionHasActive: true, manageSubscriptionReason: input.reasonId, manageSubscriptionReasonLabel: input.reasonLabel, manageSubscriptionCancelled: input.cancelled, manageSubscriptionCancelledId: input.subscriptionId });
|
|
137
|
+
setAnswer('manageSubscriptionReason', input.reasonId);
|
|
138
|
+
setAnswer('manageSubscriptionCancelled', input.cancelled);
|
|
139
|
+
setAnswer('manageSubscriptionCancelledId', input.subscriptionId);
|
|
140
|
+
setUser(Object.assign(Object.assign({}, user), { attributes: nextAttributes }));
|
|
141
|
+
if (isPreviewFrameRuntime()) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
await apiService.updateUser({
|
|
145
|
+
id: user.id,
|
|
146
|
+
name: user.name,
|
|
147
|
+
email: user.email,
|
|
148
|
+
attributes: nextAttributes,
|
|
149
|
+
document: (_a = user.document) !== null && _a !== void 0 ? _a : {},
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
const handleCancelSubscription = async () => {
|
|
153
|
+
if (!selectedSubscription || !selectedReason || cancelInFlight) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
setCancelInFlight(true);
|
|
157
|
+
setError(null);
|
|
158
|
+
try {
|
|
159
|
+
await persistCancellationAnswers({
|
|
160
|
+
cancelled: false,
|
|
161
|
+
reasonId: selectedReason.id,
|
|
162
|
+
reasonLabel: selectedReason.label,
|
|
163
|
+
subscriptionId: selectedSubscription.id,
|
|
164
|
+
});
|
|
165
|
+
const payload = await apiService.updateSubscription({
|
|
166
|
+
subscriptionId: selectedSubscription.id,
|
|
167
|
+
action: 'cancel',
|
|
168
|
+
});
|
|
169
|
+
setData(payload);
|
|
170
|
+
const refreshedSubscription = toManageSubscriptions(payload.subscriptions).find((subscription) => subscription.id === selectedSubscription.id);
|
|
171
|
+
setCancelledActiveUntilDate(formatDate((refreshedSubscription === null || refreshedSubscription === void 0 ? void 0 : refreshedSubscription.currentPeriodEnd) || selectedSubscription.currentPeriodEnd));
|
|
172
|
+
const finalAnswers = {
|
|
173
|
+
manageSubscriptionHasActive: true,
|
|
174
|
+
manageSubscriptionReason: selectedReason.id,
|
|
175
|
+
manageSubscriptionReasonLabel: selectedReason.label,
|
|
176
|
+
manageSubscriptionCancelled: true,
|
|
177
|
+
manageSubscriptionCancelledId: selectedSubscription.id,
|
|
178
|
+
};
|
|
179
|
+
await persistCancellationAnswers({
|
|
180
|
+
cancelled: true,
|
|
181
|
+
reasonId: selectedReason.id,
|
|
182
|
+
reasonLabel: selectedReason.label,
|
|
183
|
+
subscriptionId: selectedSubscription.id,
|
|
184
|
+
});
|
|
185
|
+
completeStep(stepId, finalAnswers);
|
|
186
|
+
setStage('done');
|
|
187
|
+
}
|
|
188
|
+
catch (nextError) {
|
|
189
|
+
setError(nextError instanceof Error ? nextError.message : content.errorMessages.cancel);
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
setCancelInFlight(false);
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
return (_jsxs(_Fragment, { children: [_jsxs("section", { className: 'manage-subscription', "data-node-id": nodeId || stepId, children: [stage === 'subscriptions' ? (_jsxs("div", { className: 'manage-subscription-inner', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.subscriptionsStage.title }), loading ? (_jsx("p", { className: 'manage-subscription-muted', children: content.subscriptionsStage.loadingLabel })) : null, error ? _jsx("p", { className: 'manage-subscription-error', children: error }) : null, !loading && !error && subscriptions.length === 0 ? (_jsx("p", { className: 'manage-subscription-empty', children: content.subscriptionsStage.emptyLabel })) : null, !loading && activeSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Active Subscriptions', subscriptions: activeSubscriptions, plans: planList, onSelect: handleSubscriptionSelected })) : null, !loading && pastSubscriptions.length > 0 ? (_jsx(SubscriptionSection, { title: 'Past Subscriptions', subscriptions: pastSubscriptions, plans: planList, onSelect: handleSubscriptionSelected, description: pastSubscriptionsNote, past: true })) : null, _jsxs("p", { className: 'manage-subscription-support', children: [content.subscriptionsStage.supportPrefix, ' ', _jsx("a", { href: `mailto:${supportEmail}`, children: supportEmail })] })] })) : null, stage === 'why' ? (_jsxs("div", { className: 'manage-subscription-inner', children: [_jsx("h1", { className: 'manage-subscription-title is-reason', children: content.whyStage.title }), _jsx("ul", { className: 'manage-subscription-reasons', role: 'list', "aria-label": content.whyStage.reasonsAriaLabel, children: content.whyStage.reasons.map((reason) => (_jsx("li", { children: _jsxs("button", { type: 'button', className: 'manage-subscription-reason', onClick: () => handleReasonSelected(reason.id), children: [_jsx("span", { className: 'manage-subscription-reason-icon', "aria-hidden": true, children: reason.icon }), _jsx("span", { children: reason.label }), _jsx("span", { className: 'manage-subscription-reason-arrow', "aria-hidden": true, children: ">" })] }) }, reason.id))) })] })) : null, stage === 'confirm' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.confirmStage.title }), error ? _jsx("p", { className: 'manage-subscription-error', children: error }) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', disabled: !selectedSubscription || !selectedReason || cancelInFlight, onClick: () => {
|
|
196
|
+
void handleCancelSubscription();
|
|
197
|
+
}, children: cancelInFlight
|
|
198
|
+
? content.confirmStage.cancellingLabel
|
|
199
|
+
: content.confirmStage.cancelLabel })] })) : null, stage === 'done' ? (_jsxs("div", { className: 'manage-subscription-inner is-short', children: [_jsx("h1", { className: 'manage-subscription-title', children: content.doneStage.title }), _jsx("p", { className: 'manage-subscription-done-copy', children: content.doneStage.cancelledMessage }), cancelledActiveUntilDate ? (_jsxs("p", { className: 'manage-subscription-done-copy is-active-until', children: ["Active until: ", cancelledActiveUntilDate] })) : null, _jsx("button", { type: 'button', className: 'manage-subscription-primary', onClick: () => {
|
|
200
|
+
if (homeStepId) {
|
|
201
|
+
goToStep(homeStepId);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
setStage('subscriptions');
|
|
205
|
+
}, children: content.doneStage.returnHomeLabel })] })) : null] }), _jsx("style", { children: manageSubscriptionStyles })] }));
|
|
206
|
+
}
|
|
207
|
+
function SubscriptionSection({ title, subscriptions, plans, onSelect, description, past = false, }) {
|
|
208
|
+
return (_jsxs("section", { className: 'manage-subscription-section', "aria-label": title, children: [_jsx("h2", { children: title }), description ? _jsx("p", { className: 'manage-subscription-section-note', children: description }) : null, _jsx("ul", { className: 'manage-subscription-list', role: 'list', children: subscriptions.map((subscription) => (_jsx("li", { children: _jsxs("button", { type: 'button', className: past ? 'manage-subscription-row is-past' : 'manage-subscription-row', disabled: past, onClick: () => onSelect(subscription), children: [_jsx("span", { className: 'manage-subscription-radio', "aria-hidden": true }), _jsxs("span", { className: 'manage-subscription-row-title', children: ["Subscription #", formatSubscriptionNumber(subscription)] }), _jsx("span", { className: 'manage-subscription-row-plan', children: resolvePlanLine(subscription, plans) }), _jsxs("span", { className: 'manage-subscription-row-meta', children: [_jsx("span", { children: resolveBillingDateLabel(subscription, { past }) }), _jsxs("span", { children: ["Created at: ", resolveCreatedDate(subscription)] })] })] }) }, subscription.id))) })] }));
|
|
209
|
+
}
|
|
210
|
+
const manageSubscriptionStyles = `
|
|
211
|
+
.manage-subscription {
|
|
212
|
+
position: absolute;
|
|
213
|
+
inset: 0;
|
|
214
|
+
overflow-y: auto;
|
|
215
|
+
box-sizing: border-box;
|
|
216
|
+
background: color-mix(in srgb, var(--color-primary, #4db53f) 8%, var(--color-surface, #fff) 92%);
|
|
217
|
+
color: var(--color-text, #262729);
|
|
218
|
+
font-family: var(--font-family-base, Inter, sans-serif);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.manage-subscription-inner {
|
|
222
|
+
box-sizing: border-box;
|
|
223
|
+
min-height: 100%;
|
|
224
|
+
padding: 22px 12px 32px;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.manage-subscription-inner.is-short {
|
|
228
|
+
padding-top: 22px;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
.manage-subscription-title {
|
|
232
|
+
margin: 0;
|
|
233
|
+
color: var(--color-text, #262729);
|
|
234
|
+
font-size: 20px;
|
|
235
|
+
line-height: 1.3;
|
|
236
|
+
font-weight: 800;
|
|
237
|
+
text-align: center;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.manage-subscription-title.is-reason {
|
|
241
|
+
max-width: 620px;
|
|
242
|
+
margin-inline: auto;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.manage-subscription-section {
|
|
246
|
+
margin-top: 22px;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.manage-subscription-section h2 {
|
|
250
|
+
margin: 0 0 10px;
|
|
251
|
+
color: var(--color-text, #262729);
|
|
252
|
+
font-size: 15px;
|
|
253
|
+
line-height: 1.25;
|
|
254
|
+
font-weight: 800;
|
|
255
|
+
text-align: center;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
.manage-subscription-section-note {
|
|
259
|
+
margin: 0 0 12px;
|
|
260
|
+
color: color-mix(in srgb, var(--color-text, #262729) 72%, transparent);
|
|
261
|
+
font-size: 13px;
|
|
262
|
+
line-height: 1.35;
|
|
263
|
+
text-align: center;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.manage-subscription-list {
|
|
267
|
+
margin: 0;
|
|
268
|
+
padding: 0;
|
|
269
|
+
display: grid;
|
|
270
|
+
gap: 8px;
|
|
271
|
+
list-style: none;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.manage-subscription-row {
|
|
275
|
+
width: 100%;
|
|
276
|
+
min-height: 118px;
|
|
277
|
+
border: 0;
|
|
278
|
+
border-left: 4px solid var(--color-primary, #4db53f);
|
|
279
|
+
border-radius: 6px;
|
|
280
|
+
background: var(--color-surface, #fff);
|
|
281
|
+
color: var(--color-text, #262729);
|
|
282
|
+
display: grid;
|
|
283
|
+
grid-template-columns: 1fr;
|
|
284
|
+
justify-items: start;
|
|
285
|
+
gap: 7px;
|
|
286
|
+
padding: 10px 12px;
|
|
287
|
+
text-align: left;
|
|
288
|
+
cursor: pointer;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.manage-subscription-row.is-past {
|
|
292
|
+
border-left-color: color-mix(in srgb, var(--color-text, #262729) 25%, var(--color-surface, #fff) 75%);
|
|
293
|
+
color: color-mix(in srgb, var(--color-text, #262729) 68%, transparent);
|
|
294
|
+
cursor: default;
|
|
295
|
+
opacity: 0.82;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.manage-subscription-radio {
|
|
299
|
+
width: 14px;
|
|
300
|
+
height: 14px;
|
|
301
|
+
border: 2px solid color-mix(in srgb, var(--color-text, #262729) 45%, var(--color-surface, #fff) 55%);
|
|
302
|
+
border-radius: 999px;
|
|
303
|
+
display: block;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.manage-subscription-row-title {
|
|
307
|
+
font-size: 15px;
|
|
308
|
+
line-height: 1.25;
|
|
309
|
+
font-weight: 800;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.manage-subscription-row-plan {
|
|
313
|
+
font-size: 13px;
|
|
314
|
+
line-height: 1.2;
|
|
315
|
+
font-weight: 800;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.manage-subscription-row-meta {
|
|
319
|
+
width: 100%;
|
|
320
|
+
display: grid;
|
|
321
|
+
grid-template-columns: 1fr 1fr;
|
|
322
|
+
gap: 8px;
|
|
323
|
+
color: color-mix(in srgb, var(--color-text, #262729) 68%, transparent);
|
|
324
|
+
font-size: 11px;
|
|
325
|
+
font-style: italic;
|
|
326
|
+
line-height: 1.3;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.manage-subscription-support {
|
|
330
|
+
margin: 30px 0 0;
|
|
331
|
+
color: var(--color-text, #262729);
|
|
332
|
+
font-size: 15px;
|
|
333
|
+
line-height: 1.35;
|
|
334
|
+
text-align: center;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.manage-subscription-support a {
|
|
338
|
+
color: var(--color-secondary, var(--color-primary, #4db53f));
|
|
339
|
+
text-decoration: underline;
|
|
340
|
+
text-underline-offset: 3px;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.manage-subscription-muted,
|
|
344
|
+
.manage-subscription-empty,
|
|
345
|
+
.manage-subscription-error {
|
|
346
|
+
margin: 18px 0 0;
|
|
347
|
+
border-radius: 6px;
|
|
348
|
+
background: var(--color-surface, #fff);
|
|
349
|
+
padding: 12px;
|
|
350
|
+
color: color-mix(in srgb, var(--color-text, #262729) 72%, transparent);
|
|
351
|
+
font-size: 12px;
|
|
352
|
+
line-height: 1.4;
|
|
353
|
+
text-align: center;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
.manage-subscription-error {
|
|
357
|
+
color: var(--color-danger, #c74b43);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.manage-subscription-done-copy {
|
|
361
|
+
margin: 15px 0 0;
|
|
362
|
+
color: color-mix(in srgb, var(--color-text, #262729) 76%, transparent);
|
|
363
|
+
font-size: 13px;
|
|
364
|
+
line-height: 1.4;
|
|
365
|
+
text-align: center;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.manage-subscription-done-copy.is-active-until {
|
|
369
|
+
margin-top: 8px;
|
|
370
|
+
color: var(--color-text, #262729);
|
|
371
|
+
font-weight: 800;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.manage-subscription-reasons {
|
|
375
|
+
margin: 18px 0 0;
|
|
376
|
+
padding: 0;
|
|
377
|
+
display: grid;
|
|
378
|
+
gap: 12px;
|
|
379
|
+
list-style: none;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.manage-subscription-reason {
|
|
383
|
+
width: 100%;
|
|
384
|
+
min-height: 68px;
|
|
385
|
+
border: 0;
|
|
386
|
+
border-radius: 10px;
|
|
387
|
+
background: var(--color-surface, #fff);
|
|
388
|
+
color: var(--color-text, #262729);
|
|
389
|
+
display: grid;
|
|
390
|
+
grid-template-columns: 36px 1fr auto;
|
|
391
|
+
align-items: center;
|
|
392
|
+
gap: 8px;
|
|
393
|
+
padding: 14px 16px;
|
|
394
|
+
font-size: 15px;
|
|
395
|
+
line-height: 1.25;
|
|
396
|
+
font-weight: 800;
|
|
397
|
+
text-align: left;
|
|
398
|
+
cursor: pointer;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.manage-subscription-reason-icon {
|
|
402
|
+
font-size: 20px;
|
|
403
|
+
line-height: 1;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.manage-subscription-reason-arrow {
|
|
407
|
+
color: var(--color-secondary, var(--color-primary, #4db53f));
|
|
408
|
+
font-size: 28px;
|
|
409
|
+
line-height: 0.8;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.manage-subscription-primary {
|
|
413
|
+
margin-top: 18px;
|
|
414
|
+
width: 100%;
|
|
415
|
+
min-height: 56px;
|
|
416
|
+
border: 0;
|
|
417
|
+
border-radius: 5px;
|
|
418
|
+
background: var(--color-primary, #4db53f);
|
|
419
|
+
color: var(--color-primary-text, #fff);
|
|
420
|
+
font-size: 19px;
|
|
421
|
+
line-height: 1.2;
|
|
422
|
+
font-weight: 800;
|
|
423
|
+
cursor: pointer;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.manage-subscription-primary:disabled {
|
|
427
|
+
opacity: 0.58;
|
|
428
|
+
cursor: default;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
@media (max-width: 420px) {
|
|
432
|
+
.manage-subscription-title {
|
|
433
|
+
font-size: 19px;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.manage-subscription-row-meta {
|
|
437
|
+
grid-template-columns: 1fr;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
.manage-subscription-reason {
|
|
441
|
+
grid-template-columns: 32px 1fr auto;
|
|
442
|
+
padding-inline: 14px;
|
|
443
|
+
font-size: 14px;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
`;
|
|
447
|
+
export const __manageSubscriptionScreenTestables = {
|
|
448
|
+
resolveBillingDateLabel,
|
|
449
|
+
};
|
|
@@ -220,7 +220,6 @@ function SharedStripeCheckoutV2Form({ amountCents, buttonColor, cardSubmitLabel,
|
|
|
220
220
|
const result = await checkoutState.checkout.confirm({
|
|
221
221
|
email: syncedEmail || undefined,
|
|
222
222
|
redirect: 'if_required',
|
|
223
|
-
returnUrl,
|
|
224
223
|
});
|
|
225
224
|
if (result.type === 'error') {
|
|
226
225
|
const message = result.error.message || 'Payment failed.';
|
|
@@ -70,7 +70,6 @@ export function StripeCheckoutExpressCheckoutButton({ beforeConfirm, returnUrl,
|
|
|
70
70
|
email: preparedCustomerEmail || (customerEmail === null || customerEmail === void 0 ? void 0 : customerEmail.trim()) || undefined,
|
|
71
71
|
expressCheckoutConfirmEvent: event,
|
|
72
72
|
redirect: 'if_required',
|
|
73
|
-
returnUrl,
|
|
74
73
|
});
|
|
75
74
|
if (result.type === 'error') {
|
|
76
75
|
const message = result.error.message || 'Wallet checkout failed.';
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
|
11
11
|
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
12
12
|
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
13
13
|
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
14
|
+
export { ManageSubscriptionScreen, type ManageSubscriptionContent, type ManageSubscriptionScreenProps, } from './components/ManageSubscriptionScreen.js';
|
|
14
15
|
export * from './components/shared/SharedStripeCheckoutDialog.js';
|
|
15
16
|
export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
|
|
16
17
|
export * from './components/shared/ApplePaySubscribeButton.js';
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ export * from './providers/stripe/useStripeSubscriptionCheckoutSession.js';
|
|
|
11
11
|
export * from './providers/stripe/WalletSubscriptionCheckoutSlot.js';
|
|
12
12
|
export * from './providers/stripe/ApplePaySubscriptionCheckoutSlot.js';
|
|
13
13
|
export * from './providers/stripe/GooglePaySubscriptionCheckoutSlot.js';
|
|
14
|
+
export { ManageSubscriptionScreen, } from './components/ManageSubscriptionScreen.js';
|
|
14
15
|
export * from './components/shared/SharedStripeCheckoutDialog.js';
|
|
15
16
|
export * from './components/shared/SharedStripeCheckoutV2Dialog.js';
|
|
16
17
|
export * from './components/shared/ApplePaySubscribeButton.js';
|