@blocklet/payment-react 1.18.47 → 1.18.49
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 +50 -0
- package/es/components/loading-button.js +1 -1
- package/es/components/resume-subscription.d.ts +25 -0
- package/es/components/resume-subscription.js +158 -0
- package/es/index.d.ts +2 -1
- package/es/index.js +3 -1
- package/es/libs/util.js +2 -1
- package/es/locales/en.js +8 -3
- package/es/locales/zh.js +7 -3
- package/es/payment/form/stripe/form.js +32 -2
- package/lib/components/loading-button.js +1 -1
- package/lib/components/resume-subscription.d.ts +25 -0
- package/lib/components/resume-subscription.js +211 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +8 -0
- package/lib/libs/util.js +2 -1
- package/lib/locales/en.js +8 -3
- package/lib/locales/zh.js +7 -3
- package/lib/payment/form/stripe/form.js +31 -1
- package/package.json +7 -7
- package/src/components/loading-button.tsx +1 -1
- package/src/components/resume-subscription.tsx +217 -0
- package/src/index.ts +2 -0
- package/src/libs/util.ts +1 -0
- package/src/locales/en.tsx +9 -3
- package/src/locales/zh.tsx +7 -3
- package/src/payment/form/stripe/form.tsx +31 -1
package/README.md
CHANGED
|
@@ -321,6 +321,56 @@ function DonationPage() {
|
|
|
321
321
|
|
|
322
322
|
### Subscription Management Example
|
|
323
323
|
|
|
324
|
+
- `ResumeSubscription` component
|
|
325
|
+
- Resume subscription, with support for re-stake if needed
|
|
326
|
+
- Props:
|
|
327
|
+
- `subscriptionId`: [Required] The subscription ID to resume
|
|
328
|
+
- `onResumed`: [Optional] Callback function called after successful resume, receives `(subscription)`
|
|
329
|
+
- `dialogProps`: [Optional] Dialog properties, default is `{ open: true }`
|
|
330
|
+
- `successToast`: [Optional] Whether to show success toast, default is `true`
|
|
331
|
+
- `authToken`: [Optional] Authentication token for API requests
|
|
332
|
+
|
|
333
|
+
```tsx
|
|
334
|
+
import {
|
|
335
|
+
PaymentProvider,
|
|
336
|
+
ResumeSubscription,
|
|
337
|
+
CustomerInvoiceList,
|
|
338
|
+
Amount
|
|
339
|
+
} from '@blocklet/payment-react';
|
|
340
|
+
|
|
341
|
+
function SubscriptionPage({ subscriptionId }) {
|
|
342
|
+
return (
|
|
343
|
+
<PaymentProvider session={session}>
|
|
344
|
+
<ResumeSubscription
|
|
345
|
+
subscriptionId={subscriptionId}
|
|
346
|
+
onResumed={(subscription) => {
|
|
347
|
+
// Refresh subscription status
|
|
348
|
+
refetchSubscription();
|
|
349
|
+
}}
|
|
350
|
+
/>
|
|
351
|
+
|
|
352
|
+
{/* Custom dialog props */}
|
|
353
|
+
<ResumeSubscription
|
|
354
|
+
subscriptionId={subscriptionId}
|
|
355
|
+
dialogProps={{
|
|
356
|
+
open: true,
|
|
357
|
+
title: 'Resume Your Subscription',
|
|
358
|
+
onClose: () => {
|
|
359
|
+
// Handle dialog close
|
|
360
|
+
}
|
|
361
|
+
}}
|
|
362
|
+
/>
|
|
363
|
+
|
|
364
|
+
{/* With auth token */}
|
|
365
|
+
<ResumeSubscription
|
|
366
|
+
subscriptionId={subscriptionId}
|
|
367
|
+
authToken="your-auth-token"
|
|
368
|
+
/>
|
|
369
|
+
</PaymentProvider>
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
324
374
|
- `OverdueInvoicePayment` component
|
|
325
375
|
- Display overdue invoices for a subscription, and support batch payment
|
|
326
376
|
- Props:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Subscription, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
2
|
+
type DialogProps = {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
onClose?: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
};
|
|
7
|
+
type Props = {
|
|
8
|
+
subscriptionId: string;
|
|
9
|
+
onResumed?: (subscription: Subscription | TSubscriptionExpanded) => void;
|
|
10
|
+
dialogProps?: DialogProps;
|
|
11
|
+
successToast?: boolean;
|
|
12
|
+
authToken?: string;
|
|
13
|
+
};
|
|
14
|
+
declare function RecoverSubscription({ subscriptionId, dialogProps, onResumed, successToast, authToken, }: Props): import("react").JSX.Element | null;
|
|
15
|
+
declare namespace RecoverSubscription {
|
|
16
|
+
var defaultProps: {
|
|
17
|
+
onResumed: () => void;
|
|
18
|
+
dialogProps: {
|
|
19
|
+
open: boolean;
|
|
20
|
+
};
|
|
21
|
+
successToast: boolean;
|
|
22
|
+
authToken: undefined;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export default RecoverSubscription;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
import { Button, Typography, Stack, Alert } from "@mui/material";
|
|
4
|
+
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
5
|
+
import Toast from "@arcblock/ux/lib/Toast";
|
|
6
|
+
import { joinURL } from "ufo";
|
|
7
|
+
import { useRequest } from "ahooks";
|
|
8
|
+
import { Dialog } from "@arcblock/ux";
|
|
9
|
+
import { usePaymentContext } from "../contexts/payment.js";
|
|
10
|
+
import { formatError, formatToDate, getPrefix, isCrossOrigin } from "../libs/util.js";
|
|
11
|
+
import api from "../libs/api.js";
|
|
12
|
+
import LoadingButton from "./loading-button.js";
|
|
13
|
+
const fetchSubscriptionDetail = async (params) => {
|
|
14
|
+
const url = `/api/subscriptions/${params.subscriptionId}`;
|
|
15
|
+
const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
16
|
+
return res.data;
|
|
17
|
+
};
|
|
18
|
+
const fetchRecoverInfo = async (params) => {
|
|
19
|
+
const url = `/api/subscriptions/${params.subscriptionId}/recover-info`;
|
|
20
|
+
const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
21
|
+
return res.data;
|
|
22
|
+
};
|
|
23
|
+
const recoverSubscription = async (params) => {
|
|
24
|
+
const url = `/api/subscriptions/${params.subscriptionId}/recover`;
|
|
25
|
+
const res = await api.put(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
26
|
+
return res.data;
|
|
27
|
+
};
|
|
28
|
+
function RecoverSubscription({
|
|
29
|
+
subscriptionId,
|
|
30
|
+
dialogProps = {},
|
|
31
|
+
onResumed = () => {
|
|
32
|
+
},
|
|
33
|
+
successToast = true,
|
|
34
|
+
authToken
|
|
35
|
+
}) {
|
|
36
|
+
const { t, locale } = useLocaleContext();
|
|
37
|
+
const { connect } = usePaymentContext();
|
|
38
|
+
const [recoverLoading, setRecoverLoading] = useState(false);
|
|
39
|
+
const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false);
|
|
40
|
+
const { data, error, loading } = useRequest(() => fetchRecoverInfo({ subscriptionId, authToken }), {
|
|
41
|
+
ready: !!subscriptionId
|
|
42
|
+
});
|
|
43
|
+
const isCrossOriginRequest = isCrossOrigin();
|
|
44
|
+
const needStake = data?.needStake ?? false;
|
|
45
|
+
const handleSuccess = async () => {
|
|
46
|
+
try {
|
|
47
|
+
const subscription = await fetchSubscriptionDetail({ subscriptionId, authToken });
|
|
48
|
+
if (successToast) {
|
|
49
|
+
Toast.success(t("payment.customer.recover.success"));
|
|
50
|
+
}
|
|
51
|
+
setDialogOpen(false);
|
|
52
|
+
onResumed(subscription);
|
|
53
|
+
setRecoverLoading(false);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
console.error("Failed to fetch updated subscription:", err);
|
|
56
|
+
Toast.error(formatError(err));
|
|
57
|
+
setRecoverLoading(false);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
const handleRecoverWithStake = () => {
|
|
61
|
+
setRecoverLoading(true);
|
|
62
|
+
try {
|
|
63
|
+
connect.open({
|
|
64
|
+
locale,
|
|
65
|
+
containerEl: void 0,
|
|
66
|
+
saveConnect: false,
|
|
67
|
+
action: "re-stake",
|
|
68
|
+
prefix: joinURL(getPrefix(), "/api/did"),
|
|
69
|
+
useSocket: !isCrossOriginRequest,
|
|
70
|
+
extraParams: { subscriptionId },
|
|
71
|
+
messages: {
|
|
72
|
+
scan: t("common.connect.defaultScan"),
|
|
73
|
+
title: t("payment.customer.recover.reStakeTitle"),
|
|
74
|
+
confirm: t("common.connect.confirm")
|
|
75
|
+
},
|
|
76
|
+
onSuccess: handleSuccess,
|
|
77
|
+
onClose: () => {
|
|
78
|
+
connect.close();
|
|
79
|
+
setRecoverLoading(false);
|
|
80
|
+
},
|
|
81
|
+
onError: (err) => {
|
|
82
|
+
Toast.error(formatError(err));
|
|
83
|
+
setRecoverLoading(false);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
} catch (err) {
|
|
87
|
+
Toast.error(formatError(err));
|
|
88
|
+
setRecoverLoading(false);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const handleDirectRecover = async () => {
|
|
92
|
+
setRecoverLoading(true);
|
|
93
|
+
try {
|
|
94
|
+
const result = await recoverSubscription({ subscriptionId, authToken });
|
|
95
|
+
if (result.needStake) {
|
|
96
|
+
handleRecoverWithStake();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const { subscription } = result;
|
|
100
|
+
if (successToast) {
|
|
101
|
+
Toast.success(t("payment.customer.recover.success"));
|
|
102
|
+
}
|
|
103
|
+
setDialogOpen(false);
|
|
104
|
+
onResumed(subscription);
|
|
105
|
+
setRecoverLoading(false);
|
|
106
|
+
} catch (err) {
|
|
107
|
+
Toast.error(formatError(err));
|
|
108
|
+
setRecoverLoading(false);
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
const handleRecover = () => {
|
|
112
|
+
if (needStake) {
|
|
113
|
+
handleRecoverWithStake();
|
|
114
|
+
} else {
|
|
115
|
+
handleDirectRecover();
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const handleClose = () => {
|
|
119
|
+
setDialogOpen(false);
|
|
120
|
+
dialogProps.onClose?.();
|
|
121
|
+
};
|
|
122
|
+
if (loading) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
return /* @__PURE__ */ jsx(
|
|
126
|
+
Dialog,
|
|
127
|
+
{
|
|
128
|
+
PaperProps: {
|
|
129
|
+
style: { minHeight: "auto" }
|
|
130
|
+
},
|
|
131
|
+
...dialogProps || {},
|
|
132
|
+
open: dialogOpen,
|
|
133
|
+
title: dialogProps?.title || t("payment.customer.recover.title"),
|
|
134
|
+
sx: { "& .MuiDialogContent-root": { pt: 0 } },
|
|
135
|
+
onClose: handleClose,
|
|
136
|
+
children: error ? /* @__PURE__ */ jsx(Alert, { severity: "error", children: error.message }) : /* @__PURE__ */ jsxs(Stack, { gap: 2, children: [
|
|
137
|
+
/* @__PURE__ */ jsx(Typography, { color: "text.secondary", variant: "body2", children: t("payment.customer.recover.description", {
|
|
138
|
+
date: formatToDate(Number(data?.subscription?.current_period_end || "0") * 1e3)
|
|
139
|
+
}) }),
|
|
140
|
+
needStake && /* @__PURE__ */ jsx(Alert, { severity: "warning", children: t("payment.customer.recover.stakeRequiredDescription") }),
|
|
141
|
+
/* @__PURE__ */ jsxs(Stack, { direction: "row", justifyContent: "flex-end", gap: 2, mt: 2, children: [
|
|
142
|
+
/* @__PURE__ */ jsx(Button, { variant: "outlined", color: "primary", onClick: handleClose, children: t("common.cancel") }),
|
|
143
|
+
/* @__PURE__ */ jsx(LoadingButton, { variant: "contained", size: "small", loading: recoverLoading, onClick: handleRecover, children: t("common.confirm") })
|
|
144
|
+
] })
|
|
145
|
+
] })
|
|
146
|
+
}
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
RecoverSubscription.defaultProps = {
|
|
150
|
+
onResumed: () => {
|
|
151
|
+
},
|
|
152
|
+
dialogProps: {
|
|
153
|
+
open: true
|
|
154
|
+
},
|
|
155
|
+
successToast: true,
|
|
156
|
+
authToken: void 0
|
|
157
|
+
};
|
|
158
|
+
export default RecoverSubscription;
|
package/es/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { createLazyComponent } from './components/lazy-loader';
|
|
|
31
31
|
import OverdueInvoicePayment from './components/over-due-invoice-payment';
|
|
32
32
|
import PaymentBeneficiaries from './components/payment-beneficiaries';
|
|
33
33
|
import LoadingButton from './components/loading-button';
|
|
34
|
+
import ResumeSubscription from './components/resume-subscription';
|
|
34
35
|
export { PaymentThemeProvider } from './theme';
|
|
35
36
|
export * from './libs/util';
|
|
36
37
|
export * from './libs/connect';
|
|
@@ -45,4 +46,4 @@ export * from './hooks/scroll';
|
|
|
45
46
|
export * from './hooks/keyboard';
|
|
46
47
|
export * from './libs/validator';
|
|
47
48
|
export { translations, createTranslator } from './locales';
|
|
48
|
-
export { createLazyComponent, api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, };
|
|
49
|
+
export { createLazyComponent, api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, };
|
package/es/index.js
CHANGED
|
@@ -31,6 +31,7 @@ import { createLazyComponent } from "./components/lazy-loader.js";
|
|
|
31
31
|
import OverdueInvoicePayment from "./components/over-due-invoice-payment.js";
|
|
32
32
|
import PaymentBeneficiaries from "./components/payment-beneficiaries.js";
|
|
33
33
|
import LoadingButton from "./components/loading-button.js";
|
|
34
|
+
import ResumeSubscription from "./components/resume-subscription.js";
|
|
34
35
|
export { PaymentThemeProvider } from "./theme/index.js";
|
|
35
36
|
export * from "./libs/util.js";
|
|
36
37
|
export * from "./libs/connect.js";
|
|
@@ -79,5 +80,6 @@ export {
|
|
|
79
80
|
OverdueInvoicePayment,
|
|
80
81
|
PaymentBeneficiaries,
|
|
81
82
|
LoadingButton,
|
|
82
|
-
DonateDetails
|
|
83
|
+
DonateDetails,
|
|
84
|
+
ResumeSubscription
|
|
83
85
|
};
|
package/es/libs/util.js
CHANGED
|
@@ -943,7 +943,8 @@ export function getInvoiceDescriptionAndReason(invoice, locale = "en") {
|
|
|
943
943
|
"Stake for subscription overdraft protection": t(
|
|
944
944
|
"payment.invoice.reason.stakeForSubscriptionOverdraftProtection",
|
|
945
945
|
locale
|
|
946
|
-
)
|
|
946
|
+
),
|
|
947
|
+
"Re-stake to resume subscription": t("payment.invoice.reason.reStakeToResumeSubscription", locale)
|
|
947
948
|
};
|
|
948
949
|
return {
|
|
949
950
|
description: descMap[description] || description,
|
package/es/locales/en.js
CHANGED
|
@@ -248,8 +248,8 @@ export default flat({
|
|
|
248
248
|
button: "Unsubscribe",
|
|
249
249
|
title: "Cancel your subscription",
|
|
250
250
|
comment: "Any additional feedback?",
|
|
251
|
-
description: "Your subscription will be canceled, but it is still available until the end of your current billing period on {date}",
|
|
252
|
-
trialDescription: "Free trial subscriptions will be canceled immediately and no longer available, confirm to continue",
|
|
251
|
+
description: "Your subscription will be canceled, but it is still available until the end of your current billing period on {date}.",
|
|
252
|
+
trialDescription: "Free trial subscriptions will be canceled immediately and no longer available, confirm to continue.",
|
|
253
253
|
feedback: {
|
|
254
254
|
tip: "We would love your feedback, it will help us improve our service",
|
|
255
255
|
too_expensive: "The service is too expensive",
|
|
@@ -276,7 +276,11 @@ export default flat({
|
|
|
276
276
|
recover: {
|
|
277
277
|
button: "Resume Subscription",
|
|
278
278
|
title: "Resume Your Subscription",
|
|
279
|
-
description: "Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue"
|
|
279
|
+
description: "Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue.",
|
|
280
|
+
success: "Subscription resumed successfully",
|
|
281
|
+
reStakeTitle: "Re-stake to resume subscription",
|
|
282
|
+
stakeRequiredDescription: "Your previous stake has been revoked. You need to re-stake to resume your subscription.",
|
|
283
|
+
stakeWarning: "Please ensure you have sufficient balance for staking before proceeding."
|
|
280
284
|
},
|
|
281
285
|
changePlan: {
|
|
282
286
|
button: "Change Plan",
|
|
@@ -373,6 +377,7 @@ export default flat({
|
|
|
373
377
|
rechargeForSubscription: "Add funds for subscription",
|
|
374
378
|
overdraftProtection: "SubGuard\u2122 Fee",
|
|
375
379
|
stakeForSubscriptionOverdraftProtection: "SubGuard\u2122",
|
|
380
|
+
reStakeToResumeSubscription: "Subscription resume",
|
|
376
381
|
gas: "Gas",
|
|
377
382
|
fee: "Fee"
|
|
378
383
|
}
|
package/es/locales/zh.js
CHANGED
|
@@ -248,8 +248,8 @@ export default flat({
|
|
|
248
248
|
button: "\u53D6\u6D88\u8BA2\u9605",
|
|
249
249
|
title: "\u53D6\u6D88\u60A8\u7684\u8BA2\u9605",
|
|
250
250
|
comment: "\u4F60\u8FD8\u6709\u5176\u4ED6\u53CD\u9988\u4E48\uFF1F",
|
|
251
|
-
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u88AB\u53D6\u6D88\uFF0C\u4F46\u4ECD\u7136\u53EF\u7528\u76F4\u5230\u60A8\u5F53\u524D\u8BA1\u8D39\u5468\u671F\u7ED3\u675F\u4E8E{date}",
|
|
252
|
-
trialDescription: "\u514D\u8D39\u8BD5\u7528\u7684\u8BA2\u9605\u5C06\u88AB\u7ACB\u5373\u53D6\u6D88\uFF0C\u4E0D\u518D\u53EF\u7528\uFF0C\u786E\u8BA4\u662F\u5426\u7EE7\u7EED",
|
|
251
|
+
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u88AB\u53D6\u6D88\uFF0C\u4F46\u4ECD\u7136\u53EF\u7528\u76F4\u5230\u60A8\u5F53\u524D\u8BA1\u8D39\u5468\u671F\u7ED3\u675F\u4E8E{date}\u3002",
|
|
252
|
+
trialDescription: "\u514D\u8D39\u8BD5\u7528\u7684\u8BA2\u9605\u5C06\u88AB\u7ACB\u5373\u53D6\u6D88\uFF0C\u4E0D\u518D\u53EF\u7528\uFF0C\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u3002",
|
|
253
253
|
feedback: {
|
|
254
254
|
tip: "\u6211\u4EEC\u5E0C\u671B\u542C\u5230\u60A8\u7684\u53CD\u9988\uFF0C\u8FD9\u5C06\u5E2E\u52A9\u6211\u4EEC\u6539\u8FDB\u6211\u4EEC\u7684\u670D\u52A1",
|
|
255
255
|
too_expensive: "\u670D\u52A1\u8D39\u7528\u592A\u9AD8",
|
|
@@ -277,7 +277,10 @@ export default flat({
|
|
|
277
277
|
recover: {
|
|
278
278
|
button: "\u6062\u590D\u8BA2\u9605",
|
|
279
279
|
title: "\u6062\u590D\u60A8\u7684\u8BA2\u9605",
|
|
280
|
-
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\u4F1A\u88AB\u53D6\u6D88\uFF0C\u5E76\u5C06\u5728{date}\u81EA\u52A8\u7EED\u8BA2\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED"
|
|
280
|
+
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\u4F1A\u88AB\u53D6\u6D88\uFF0C\u5E76\u5C06\u5728{date}\u81EA\u52A8\u7EED\u8BA2\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u3002",
|
|
281
|
+
success: "\u8BA2\u9605\u6062\u590D\u6210\u529F",
|
|
282
|
+
reStakeTitle: "\u91CD\u65B0\u8D28\u62BC\u4EE5\u6062\u590D\u8BA2\u9605",
|
|
283
|
+
stakeRequiredDescription: "\u60A8\u4E4B\u524D\u7684\u8D28\u62BC\u5DF2\u88AB\u7533\u8BF7\u53D6\u56DE\uFF0C\u9700\u8981\u91CD\u65B0\u8D28\u62BC\u624D\u80FD\u6062\u590D\u8BA2\u9605\u3002"
|
|
281
284
|
},
|
|
282
285
|
changePlan: {
|
|
283
286
|
button: "\u5207\u6362\u5957\u9910",
|
|
@@ -374,6 +377,7 @@ export default flat({
|
|
|
374
377
|
rechargeForSubscription: "\u8BA2\u9605\u5145\u503C",
|
|
375
378
|
overdraftProtection: "\u8BA2\u9605\u5B88\u62A4\u670D\u52A1\u8D39",
|
|
376
379
|
stakeForSubscriptionOverdraftProtection: "\u8BA2\u9605\u5B88\u62A4\u670D\u52A1",
|
|
380
|
+
reStakeToResumeSubscription: "\u8BA2\u9605\u6062\u590D",
|
|
377
381
|
gas: "\u624B\u7EED\u8D39",
|
|
378
382
|
fee: "\u670D\u52A1\u8D39"
|
|
379
383
|
}
|
|
@@ -2,7 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import Center from "@arcblock/ux/lib/Center";
|
|
3
3
|
import Dialog from "@arcblock/ux/lib/Dialog";
|
|
4
4
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
5
|
-
import { CircularProgress, Typography } from "@mui/material";
|
|
5
|
+
import { CircularProgress, Typography, useTheme } from "@mui/material";
|
|
6
6
|
import { styled } from "@mui/system";
|
|
7
7
|
import { useSetState } from "ahooks";
|
|
8
8
|
import { useEffect, useCallback } from "react";
|
|
@@ -28,6 +28,7 @@ function StripeCheckoutForm({
|
|
|
28
28
|
const stripe = useStripe();
|
|
29
29
|
const elements = useElements();
|
|
30
30
|
const { t } = useLocaleContext();
|
|
31
|
+
const theme = useTheme();
|
|
31
32
|
const [state, setState] = useSetState({
|
|
32
33
|
message: "",
|
|
33
34
|
confirming: false,
|
|
@@ -171,6 +172,16 @@ function StripeCheckoutForm({
|
|
|
171
172
|
email: customer.email,
|
|
172
173
|
address: customer.address
|
|
173
174
|
}
|
|
175
|
+
},
|
|
176
|
+
appearance: {
|
|
177
|
+
theme: theme.palette.mode,
|
|
178
|
+
variables: {
|
|
179
|
+
colorPrimary: theme.palette.primary.main,
|
|
180
|
+
colorBackground: theme.palette.background.paper,
|
|
181
|
+
colorText: theme.palette.text.primary,
|
|
182
|
+
colorDanger: theme.palette.error.main,
|
|
183
|
+
borderRadius: "4px"
|
|
184
|
+
}
|
|
174
185
|
}
|
|
175
186
|
},
|
|
176
187
|
onChange: handlePaymentMethodChange,
|
|
@@ -217,6 +228,7 @@ export default function StripeCheckout({
|
|
|
217
228
|
const stripePromise = loadStripe(publicKey);
|
|
218
229
|
const { isMobile } = useMobile();
|
|
219
230
|
const { t, locale } = useLocaleContext();
|
|
231
|
+
const theme = useTheme();
|
|
220
232
|
const [state, setState] = useSetState({
|
|
221
233
|
open: true,
|
|
222
234
|
closable: true
|
|
@@ -243,6 +255,15 @@ export default function StripeCheckout({
|
|
|
243
255
|
},
|
|
244
256
|
form: {
|
|
245
257
|
justifyContent: "flex-start"
|
|
258
|
+
},
|
|
259
|
+
".StripeElement--focus": {
|
|
260
|
+
borderColor: theme.palette.primary.main
|
|
261
|
+
},
|
|
262
|
+
".StripeElement--invalid": {
|
|
263
|
+
borderColor: theme.palette.error.main
|
|
264
|
+
},
|
|
265
|
+
".StripeElement--complete": {
|
|
266
|
+
borderColor: theme.palette.success.main
|
|
246
267
|
}
|
|
247
268
|
},
|
|
248
269
|
PaperProps: {
|
|
@@ -255,7 +276,16 @@ export default function StripeCheckout({
|
|
|
255
276
|
{
|
|
256
277
|
options: {
|
|
257
278
|
clientSecret,
|
|
258
|
-
locale: locale === "zh" ? "zh-CN" : "en"
|
|
279
|
+
locale: locale === "zh" ? "zh-CN" : "en",
|
|
280
|
+
appearance: {
|
|
281
|
+
theme: theme.palette.mode,
|
|
282
|
+
variables: {
|
|
283
|
+
colorPrimary: theme.palette.primary.main,
|
|
284
|
+
colorBackground: theme.palette.background.paper,
|
|
285
|
+
colorText: theme.palette.text.primary,
|
|
286
|
+
colorDanger: theme.palette.error.main
|
|
287
|
+
}
|
|
288
|
+
}
|
|
259
289
|
},
|
|
260
290
|
stripe: stripePromise,
|
|
261
291
|
children: /* @__PURE__ */ jsx(
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Subscription, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
2
|
+
type DialogProps = {
|
|
3
|
+
open?: boolean;
|
|
4
|
+
onClose?: () => void;
|
|
5
|
+
title?: string;
|
|
6
|
+
};
|
|
7
|
+
type Props = {
|
|
8
|
+
subscriptionId: string;
|
|
9
|
+
onResumed?: (subscription: Subscription | TSubscriptionExpanded) => void;
|
|
10
|
+
dialogProps?: DialogProps;
|
|
11
|
+
successToast?: boolean;
|
|
12
|
+
authToken?: string;
|
|
13
|
+
};
|
|
14
|
+
declare function RecoverSubscription({ subscriptionId, dialogProps, onResumed, successToast, authToken, }: Props): import("react").JSX.Element | null;
|
|
15
|
+
declare namespace RecoverSubscription {
|
|
16
|
+
var defaultProps: {
|
|
17
|
+
onResumed: () => void;
|
|
18
|
+
dialogProps: {
|
|
19
|
+
open: boolean;
|
|
20
|
+
};
|
|
21
|
+
successToast: boolean;
|
|
22
|
+
authToken: undefined;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export default RecoverSubscription;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _material = require("@mui/material");
|
|
10
|
+
var _context = require("@arcblock/ux/lib/Locale/context");
|
|
11
|
+
var _Toast = _interopRequireDefault(require("@arcblock/ux/lib/Toast"));
|
|
12
|
+
var _ufo = require("ufo");
|
|
13
|
+
var _ahooks = require("ahooks");
|
|
14
|
+
var _ux = require("@arcblock/ux");
|
|
15
|
+
var _payment = require("../contexts/payment");
|
|
16
|
+
var _util = require("../libs/util");
|
|
17
|
+
var _api = _interopRequireDefault(require("../libs/api"));
|
|
18
|
+
var _loadingButton = _interopRequireDefault(require("./loading-button"));
|
|
19
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
20
|
+
const fetchSubscriptionDetail = async params => {
|
|
21
|
+
const url = `/api/subscriptions/${params.subscriptionId}`;
|
|
22
|
+
const res = await _api.default.get(params.authToken ? (0, _ufo.joinURL)(url, `?authToken=${params.authToken}`) : url);
|
|
23
|
+
return res.data;
|
|
24
|
+
};
|
|
25
|
+
const fetchRecoverInfo = async params => {
|
|
26
|
+
const url = `/api/subscriptions/${params.subscriptionId}/recover-info`;
|
|
27
|
+
const res = await _api.default.get(params.authToken ? (0, _ufo.joinURL)(url, `?authToken=${params.authToken}`) : url);
|
|
28
|
+
return res.data;
|
|
29
|
+
};
|
|
30
|
+
const recoverSubscription = async params => {
|
|
31
|
+
const url = `/api/subscriptions/${params.subscriptionId}/recover`;
|
|
32
|
+
const res = await _api.default.put(params.authToken ? (0, _ufo.joinURL)(url, `?authToken=${params.authToken}`) : url);
|
|
33
|
+
return res.data;
|
|
34
|
+
};
|
|
35
|
+
function RecoverSubscription({
|
|
36
|
+
subscriptionId,
|
|
37
|
+
dialogProps = {},
|
|
38
|
+
onResumed = () => {},
|
|
39
|
+
successToast = true,
|
|
40
|
+
authToken
|
|
41
|
+
}) {
|
|
42
|
+
const {
|
|
43
|
+
t,
|
|
44
|
+
locale
|
|
45
|
+
} = (0, _context.useLocaleContext)();
|
|
46
|
+
const {
|
|
47
|
+
connect
|
|
48
|
+
} = (0, _payment.usePaymentContext)();
|
|
49
|
+
const [recoverLoading, setRecoverLoading] = (0, _react.useState)(false);
|
|
50
|
+
const [dialogOpen, setDialogOpen] = (0, _react.useState)(dialogProps.open || false);
|
|
51
|
+
const {
|
|
52
|
+
data,
|
|
53
|
+
error,
|
|
54
|
+
loading
|
|
55
|
+
} = (0, _ahooks.useRequest)(() => fetchRecoverInfo({
|
|
56
|
+
subscriptionId,
|
|
57
|
+
authToken
|
|
58
|
+
}), {
|
|
59
|
+
ready: !!subscriptionId
|
|
60
|
+
});
|
|
61
|
+
const isCrossOriginRequest = (0, _util.isCrossOrigin)();
|
|
62
|
+
const needStake = data?.needStake ?? false;
|
|
63
|
+
const handleSuccess = async () => {
|
|
64
|
+
try {
|
|
65
|
+
const subscription = await fetchSubscriptionDetail({
|
|
66
|
+
subscriptionId,
|
|
67
|
+
authToken
|
|
68
|
+
});
|
|
69
|
+
if (successToast) {
|
|
70
|
+
_Toast.default.success(t("payment.customer.recover.success"));
|
|
71
|
+
}
|
|
72
|
+
setDialogOpen(false);
|
|
73
|
+
onResumed(subscription);
|
|
74
|
+
setRecoverLoading(false);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error("Failed to fetch updated subscription:", err);
|
|
77
|
+
_Toast.default.error((0, _util.formatError)(err));
|
|
78
|
+
setRecoverLoading(false);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const handleRecoverWithStake = () => {
|
|
82
|
+
setRecoverLoading(true);
|
|
83
|
+
try {
|
|
84
|
+
connect.open({
|
|
85
|
+
locale,
|
|
86
|
+
containerEl: void 0,
|
|
87
|
+
saveConnect: false,
|
|
88
|
+
action: "re-stake",
|
|
89
|
+
prefix: (0, _ufo.joinURL)((0, _util.getPrefix)(), "/api/did"),
|
|
90
|
+
useSocket: !isCrossOriginRequest,
|
|
91
|
+
extraParams: {
|
|
92
|
+
subscriptionId
|
|
93
|
+
},
|
|
94
|
+
messages: {
|
|
95
|
+
scan: t("common.connect.defaultScan"),
|
|
96
|
+
title: t("payment.customer.recover.reStakeTitle"),
|
|
97
|
+
confirm: t("common.connect.confirm")
|
|
98
|
+
},
|
|
99
|
+
onSuccess: handleSuccess,
|
|
100
|
+
onClose: () => {
|
|
101
|
+
connect.close();
|
|
102
|
+
setRecoverLoading(false);
|
|
103
|
+
},
|
|
104
|
+
onError: err => {
|
|
105
|
+
_Toast.default.error((0, _util.formatError)(err));
|
|
106
|
+
setRecoverLoading(false);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
} catch (err) {
|
|
110
|
+
_Toast.default.error((0, _util.formatError)(err));
|
|
111
|
+
setRecoverLoading(false);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const handleDirectRecover = async () => {
|
|
115
|
+
setRecoverLoading(true);
|
|
116
|
+
try {
|
|
117
|
+
const result = await recoverSubscription({
|
|
118
|
+
subscriptionId,
|
|
119
|
+
authToken
|
|
120
|
+
});
|
|
121
|
+
if (result.needStake) {
|
|
122
|
+
handleRecoverWithStake();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const {
|
|
126
|
+
subscription
|
|
127
|
+
} = result;
|
|
128
|
+
if (successToast) {
|
|
129
|
+
_Toast.default.success(t("payment.customer.recover.success"));
|
|
130
|
+
}
|
|
131
|
+
setDialogOpen(false);
|
|
132
|
+
onResumed(subscription);
|
|
133
|
+
setRecoverLoading(false);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
_Toast.default.error((0, _util.formatError)(err));
|
|
136
|
+
setRecoverLoading(false);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const handleRecover = () => {
|
|
140
|
+
if (needStake) {
|
|
141
|
+
handleRecoverWithStake();
|
|
142
|
+
} else {
|
|
143
|
+
handleDirectRecover();
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const handleClose = () => {
|
|
147
|
+
setDialogOpen(false);
|
|
148
|
+
dialogProps.onClose?.();
|
|
149
|
+
};
|
|
150
|
+
if (loading) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_ux.Dialog, {
|
|
154
|
+
PaperProps: {
|
|
155
|
+
style: {
|
|
156
|
+
minHeight: "auto"
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
...(dialogProps || {}),
|
|
160
|
+
open: dialogOpen,
|
|
161
|
+
title: dialogProps?.title || t("payment.customer.recover.title"),
|
|
162
|
+
sx: {
|
|
163
|
+
"& .MuiDialogContent-root": {
|
|
164
|
+
pt: 0
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
onClose: handleClose,
|
|
168
|
+
children: error ? /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Alert, {
|
|
169
|
+
severity: "error",
|
|
170
|
+
children: error.message
|
|
171
|
+
}) : /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
172
|
+
gap: 2,
|
|
173
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
174
|
+
color: "text.secondary",
|
|
175
|
+
variant: "body2",
|
|
176
|
+
children: t("payment.customer.recover.description", {
|
|
177
|
+
date: (0, _util.formatToDate)(Number(data?.subscription?.current_period_end || "0") * 1e3)
|
|
178
|
+
})
|
|
179
|
+
}), needStake && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Alert, {
|
|
180
|
+
severity: "warning",
|
|
181
|
+
children: t("payment.customer.recover.stakeRequiredDescription")
|
|
182
|
+
}), /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Stack, {
|
|
183
|
+
direction: "row",
|
|
184
|
+
justifyContent: "flex-end",
|
|
185
|
+
gap: 2,
|
|
186
|
+
mt: 2,
|
|
187
|
+
children: [/* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Button, {
|
|
188
|
+
variant: "outlined",
|
|
189
|
+
color: "primary",
|
|
190
|
+
onClick: handleClose,
|
|
191
|
+
children: t("common.cancel")
|
|
192
|
+
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_loadingButton.default, {
|
|
193
|
+
variant: "contained",
|
|
194
|
+
size: "small",
|
|
195
|
+
loading: recoverLoading,
|
|
196
|
+
onClick: handleRecover,
|
|
197
|
+
children: t("common.confirm")
|
|
198
|
+
})]
|
|
199
|
+
})]
|
|
200
|
+
})
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
RecoverSubscription.defaultProps = {
|
|
204
|
+
onResumed: () => {},
|
|
205
|
+
dialogProps: {
|
|
206
|
+
open: true
|
|
207
|
+
},
|
|
208
|
+
successToast: true,
|
|
209
|
+
authToken: void 0
|
|
210
|
+
};
|
|
211
|
+
module.exports = RecoverSubscription;
|
package/lib/index.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { createLazyComponent } from './components/lazy-loader';
|
|
|
31
31
|
import OverdueInvoicePayment from './components/over-due-invoice-payment';
|
|
32
32
|
import PaymentBeneficiaries from './components/payment-beneficiaries';
|
|
33
33
|
import LoadingButton from './components/loading-button';
|
|
34
|
+
import ResumeSubscription from './components/resume-subscription';
|
|
34
35
|
export { PaymentThemeProvider } from './theme';
|
|
35
36
|
export * from './libs/util';
|
|
36
37
|
export * from './libs/connect';
|
|
@@ -45,4 +46,4 @@ export * from './hooks/scroll';
|
|
|
45
46
|
export * from './hooks/keyboard';
|
|
46
47
|
export * from './libs/validator';
|
|
47
48
|
export { translations, createTranslator } from './locales';
|
|
48
|
-
export { createLazyComponent, api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, };
|
|
49
|
+
export { createLazyComponent, api, dayjs, FormInput, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, };
|
package/lib/index.js
CHANGED
|
@@ -38,6 +38,7 @@ var _exportNames = {
|
|
|
38
38
|
OverdueInvoicePayment: true,
|
|
39
39
|
PaymentBeneficiaries: true,
|
|
40
40
|
LoadingButton: true,
|
|
41
|
+
ResumeSubscription: true,
|
|
41
42
|
PaymentThemeProvider: true,
|
|
42
43
|
translations: true,
|
|
43
44
|
createTranslator: true
|
|
@@ -186,6 +187,12 @@ Object.defineProperty(exports, "ProductSkeleton", {
|
|
|
186
187
|
return _productSkeleton.default;
|
|
187
188
|
}
|
|
188
189
|
});
|
|
190
|
+
Object.defineProperty(exports, "ResumeSubscription", {
|
|
191
|
+
enumerable: true,
|
|
192
|
+
get: function () {
|
|
193
|
+
return _resumeSubscription.default;
|
|
194
|
+
}
|
|
195
|
+
});
|
|
189
196
|
Object.defineProperty(exports, "SafeGuard", {
|
|
190
197
|
enumerable: true,
|
|
191
198
|
get: function () {
|
|
@@ -297,6 +304,7 @@ var _lazyLoader = require("./components/lazy-loader");
|
|
|
297
304
|
var _overDueInvoicePayment = _interopRequireDefault(require("./components/over-due-invoice-payment"));
|
|
298
305
|
var _paymentBeneficiaries = _interopRequireDefault(require("./components/payment-beneficiaries"));
|
|
299
306
|
var _loadingButton = _interopRequireDefault(require("./components/loading-button"));
|
|
307
|
+
var _resumeSubscription = _interopRequireDefault(require("./components/resume-subscription"));
|
|
300
308
|
var _theme = require("./theme");
|
|
301
309
|
var _util = require("./libs/util");
|
|
302
310
|
Object.keys(_util).forEach(function (key) {
|
package/lib/libs/util.js
CHANGED
|
@@ -1172,7 +1172,8 @@ function getInvoiceDescriptionAndReason(invoice, locale = "en") {
|
|
|
1172
1172
|
"Recharge for subscription": (0, _locales.t)("payment.invoice.reason.rechargeForSubscription", locale),
|
|
1173
1173
|
"Add funds for subscription": (0, _locales.t)("payment.invoice.reason.rechargeForSubscription", locale),
|
|
1174
1174
|
"Overdraft protection": (0, _locales.t)("payment.invoice.reason.overdraftProtection", locale),
|
|
1175
|
-
"Stake for subscription overdraft protection": (0, _locales.t)("payment.invoice.reason.stakeForSubscriptionOverdraftProtection", locale)
|
|
1175
|
+
"Stake for subscription overdraft protection": (0, _locales.t)("payment.invoice.reason.stakeForSubscriptionOverdraftProtection", locale),
|
|
1176
|
+
"Re-stake to resume subscription": (0, _locales.t)("payment.invoice.reason.reStakeToResumeSubscription", locale)
|
|
1176
1177
|
};
|
|
1177
1178
|
return {
|
|
1178
1179
|
description: descMap[description] || description,
|
package/lib/locales/en.js
CHANGED
|
@@ -255,8 +255,8 @@ module.exports = (0, _flat.default)({
|
|
|
255
255
|
button: "Unsubscribe",
|
|
256
256
|
title: "Cancel your subscription",
|
|
257
257
|
comment: "Any additional feedback?",
|
|
258
|
-
description: "Your subscription will be canceled, but it is still available until the end of your current billing period on {date}",
|
|
259
|
-
trialDescription: "Free trial subscriptions will be canceled immediately and no longer available, confirm to continue",
|
|
258
|
+
description: "Your subscription will be canceled, but it is still available until the end of your current billing period on {date}.",
|
|
259
|
+
trialDescription: "Free trial subscriptions will be canceled immediately and no longer available, confirm to continue.",
|
|
260
260
|
feedback: {
|
|
261
261
|
tip: "We would love your feedback, it will help us improve our service",
|
|
262
262
|
too_expensive: "The service is too expensive",
|
|
@@ -283,7 +283,11 @@ module.exports = (0, _flat.default)({
|
|
|
283
283
|
recover: {
|
|
284
284
|
button: "Resume Subscription",
|
|
285
285
|
title: "Resume Your Subscription",
|
|
286
|
-
description: "Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue"
|
|
286
|
+
description: "Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue.",
|
|
287
|
+
success: "Subscription resumed successfully",
|
|
288
|
+
reStakeTitle: "Re-stake to resume subscription",
|
|
289
|
+
stakeRequiredDescription: "Your previous stake has been revoked. You need to re-stake to resume your subscription.",
|
|
290
|
+
stakeWarning: "Please ensure you have sufficient balance for staking before proceeding."
|
|
287
291
|
},
|
|
288
292
|
changePlan: {
|
|
289
293
|
button: "Change Plan",
|
|
@@ -380,6 +384,7 @@ module.exports = (0, _flat.default)({
|
|
|
380
384
|
rechargeForSubscription: "Add funds for subscription",
|
|
381
385
|
overdraftProtection: "SubGuard\u2122 Fee",
|
|
382
386
|
stakeForSubscriptionOverdraftProtection: "SubGuard\u2122",
|
|
387
|
+
reStakeToResumeSubscription: "Subscription resume",
|
|
383
388
|
gas: "Gas",
|
|
384
389
|
fee: "Fee"
|
|
385
390
|
}
|
package/lib/locales/zh.js
CHANGED
|
@@ -255,8 +255,8 @@ module.exports = (0, _flat.default)({
|
|
|
255
255
|
button: "\u53D6\u6D88\u8BA2\u9605",
|
|
256
256
|
title: "\u53D6\u6D88\u60A8\u7684\u8BA2\u9605",
|
|
257
257
|
comment: "\u4F60\u8FD8\u6709\u5176\u4ED6\u53CD\u9988\u4E48\uFF1F",
|
|
258
|
-
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u88AB\u53D6\u6D88\uFF0C\u4F46\u4ECD\u7136\u53EF\u7528\u76F4\u5230\u60A8\u5F53\u524D\u8BA1\u8D39\u5468\u671F\u7ED3\u675F\u4E8E{date}",
|
|
259
|
-
trialDescription: "\u514D\u8D39\u8BD5\u7528\u7684\u8BA2\u9605\u5C06\u88AB\u7ACB\u5373\u53D6\u6D88\uFF0C\u4E0D\u518D\u53EF\u7528\uFF0C\u786E\u8BA4\u662F\u5426\u7EE7\u7EED",
|
|
258
|
+
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u88AB\u53D6\u6D88\uFF0C\u4F46\u4ECD\u7136\u53EF\u7528\u76F4\u5230\u60A8\u5F53\u524D\u8BA1\u8D39\u5468\u671F\u7ED3\u675F\u4E8E{date}\u3002",
|
|
259
|
+
trialDescription: "\u514D\u8D39\u8BD5\u7528\u7684\u8BA2\u9605\u5C06\u88AB\u7ACB\u5373\u53D6\u6D88\uFF0C\u4E0D\u518D\u53EF\u7528\uFF0C\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u3002",
|
|
260
260
|
feedback: {
|
|
261
261
|
tip: "\u6211\u4EEC\u5E0C\u671B\u542C\u5230\u60A8\u7684\u53CD\u9988\uFF0C\u8FD9\u5C06\u5E2E\u52A9\u6211\u4EEC\u6539\u8FDB\u6211\u4EEC\u7684\u670D\u52A1",
|
|
262
262
|
too_expensive: "\u670D\u52A1\u8D39\u7528\u592A\u9AD8",
|
|
@@ -284,7 +284,10 @@ module.exports = (0, _flat.default)({
|
|
|
284
284
|
recover: {
|
|
285
285
|
button: "\u6062\u590D\u8BA2\u9605",
|
|
286
286
|
title: "\u6062\u590D\u60A8\u7684\u8BA2\u9605",
|
|
287
|
-
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\u4F1A\u88AB\u53D6\u6D88\uFF0C\u5E76\u5C06\u5728{date}\u81EA\u52A8\u7EED\u8BA2\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED"
|
|
287
|
+
description: "\u60A8\u7684\u8BA2\u9605\u5C06\u4E0D\u4F1A\u88AB\u53D6\u6D88\uFF0C\u5E76\u5C06\u5728{date}\u81EA\u52A8\u7EED\u8BA2\uFF0C\u8BF7\u786E\u8BA4\u662F\u5426\u7EE7\u7EED\u3002",
|
|
288
|
+
success: "\u8BA2\u9605\u6062\u590D\u6210\u529F",
|
|
289
|
+
reStakeTitle: "\u91CD\u65B0\u8D28\u62BC\u4EE5\u6062\u590D\u8BA2\u9605",
|
|
290
|
+
stakeRequiredDescription: "\u60A8\u4E4B\u524D\u7684\u8D28\u62BC\u5DF2\u88AB\u7533\u8BF7\u53D6\u56DE\uFF0C\u9700\u8981\u91CD\u65B0\u8D28\u62BC\u624D\u80FD\u6062\u590D\u8BA2\u9605\u3002"
|
|
288
291
|
},
|
|
289
292
|
changePlan: {
|
|
290
293
|
button: "\u5207\u6362\u5957\u9910",
|
|
@@ -381,6 +384,7 @@ module.exports = (0, _flat.default)({
|
|
|
381
384
|
rechargeForSubscription: "\u8BA2\u9605\u5145\u503C",
|
|
382
385
|
overdraftProtection: "\u8BA2\u9605\u5B88\u62A4\u670D\u52A1\u8D39",
|
|
383
386
|
stakeForSubscriptionOverdraftProtection: "\u8BA2\u9605\u5B88\u62A4\u670D\u52A1",
|
|
387
|
+
reStakeToResumeSubscription: "\u8BA2\u9605\u6062\u590D",
|
|
384
388
|
gas: "\u624B\u7EED\u8D39",
|
|
385
389
|
fee: "\u670D\u52A1\u8D39"
|
|
386
390
|
}
|
|
@@ -44,6 +44,7 @@ function StripeCheckoutForm({
|
|
|
44
44
|
const {
|
|
45
45
|
t
|
|
46
46
|
} = (0, _context.useLocaleContext)();
|
|
47
|
+
const theme = (0, _material.useTheme)();
|
|
47
48
|
const [state, setState] = (0, _ahooks.useSetState)({
|
|
48
49
|
message: "",
|
|
49
50
|
confirming: false,
|
|
@@ -212,6 +213,16 @@ function StripeCheckoutForm({
|
|
|
212
213
|
email: customer.email,
|
|
213
214
|
address: customer.address
|
|
214
215
|
}
|
|
216
|
+
},
|
|
217
|
+
appearance: {
|
|
218
|
+
theme: theme.palette.mode,
|
|
219
|
+
variables: {
|
|
220
|
+
colorPrimary: theme.palette.primary.main,
|
|
221
|
+
colorBackground: theme.palette.background.paper,
|
|
222
|
+
colorText: theme.palette.text.primary,
|
|
223
|
+
colorDanger: theme.palette.error.main,
|
|
224
|
+
borderRadius: "4px"
|
|
225
|
+
}
|
|
215
226
|
}
|
|
216
227
|
},
|
|
217
228
|
onChange: handlePaymentMethodChange,
|
|
@@ -275,6 +286,7 @@ function StripeCheckout({
|
|
|
275
286
|
t,
|
|
276
287
|
locale
|
|
277
288
|
} = (0, _context.useLocaleContext)();
|
|
289
|
+
const theme = (0, _material.useTheme)();
|
|
278
290
|
const [state, setState] = (0, _ahooks.useSetState)({
|
|
279
291
|
open: true,
|
|
280
292
|
closable: true
|
|
@@ -303,6 +315,15 @@ function StripeCheckout({
|
|
|
303
315
|
},
|
|
304
316
|
form: {
|
|
305
317
|
justifyContent: "flex-start"
|
|
318
|
+
},
|
|
319
|
+
".StripeElement--focus": {
|
|
320
|
+
borderColor: theme.palette.primary.main
|
|
321
|
+
},
|
|
322
|
+
".StripeElement--invalid": {
|
|
323
|
+
borderColor: theme.palette.error.main
|
|
324
|
+
},
|
|
325
|
+
".StripeElement--complete": {
|
|
326
|
+
borderColor: theme.palette.success.main
|
|
306
327
|
}
|
|
307
328
|
},
|
|
308
329
|
PaperProps: {
|
|
@@ -313,7 +334,16 @@ function StripeCheckout({
|
|
|
313
334
|
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(Elements, {
|
|
314
335
|
options: {
|
|
315
336
|
clientSecret,
|
|
316
|
-
locale: locale === "zh" ? "zh-CN" : "en"
|
|
337
|
+
locale: locale === "zh" ? "zh-CN" : "en",
|
|
338
|
+
appearance: {
|
|
339
|
+
theme: theme.palette.mode,
|
|
340
|
+
variables: {
|
|
341
|
+
colorPrimary: theme.palette.primary.main,
|
|
342
|
+
colorBackground: theme.palette.background.paper,
|
|
343
|
+
colorText: theme.palette.text.primary,
|
|
344
|
+
colorDanger: theme.palette.error.main
|
|
345
|
+
}
|
|
346
|
+
}
|
|
317
347
|
},
|
|
318
348
|
stripe: stripePromise,
|
|
319
349
|
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(StripeCheckoutForm, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.49",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
}
|
|
55
55
|
},
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@arcblock/did-connect": "^2.13.
|
|
58
|
-
"@arcblock/ux": "^2.13.
|
|
57
|
+
"@arcblock/did-connect": "^2.13.54",
|
|
58
|
+
"@arcblock/ux": "^2.13.54",
|
|
59
59
|
"@arcblock/ws": "^1.20.11",
|
|
60
|
-
"@blocklet/theme": "^2.13.
|
|
61
|
-
"@blocklet/ui-react": "^2.13.
|
|
60
|
+
"@blocklet/theme": "^2.13.54",
|
|
61
|
+
"@blocklet/ui-react": "^2.13.54",
|
|
62
62
|
"@mui/icons-material": "^5.16.6",
|
|
63
63
|
"@mui/lab": "^5.0.0-alpha.173",
|
|
64
64
|
"@mui/material": "^5.16.6",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"@babel/core": "^7.25.2",
|
|
95
95
|
"@babel/preset-env": "^7.25.2",
|
|
96
96
|
"@babel/preset-react": "^7.24.7",
|
|
97
|
-
"@blocklet/payment-types": "1.18.
|
|
97
|
+
"@blocklet/payment-types": "1.18.49",
|
|
98
98
|
"@storybook/addon-essentials": "^7.6.20",
|
|
99
99
|
"@storybook/addon-interactions": "^7.6.20",
|
|
100
100
|
"@storybook/addon-links": "^7.6.20",
|
|
@@ -125,5 +125,5 @@
|
|
|
125
125
|
"vite-plugin-babel": "^1.2.0",
|
|
126
126
|
"vite-plugin-node-polyfills": "^0.21.0"
|
|
127
127
|
},
|
|
128
|
-
"gitHead": "
|
|
128
|
+
"gitHead": "05028e0b605756db083baa56b1faa36ef9431042"
|
|
129
129
|
}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Button, Typography, Stack, Alert } from '@mui/material';
|
|
3
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
|
+
import Toast from '@arcblock/ux/lib/Toast';
|
|
5
|
+
import { joinURL } from 'ufo';
|
|
6
|
+
import type { Subscription, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
7
|
+
import { useRequest } from 'ahooks';
|
|
8
|
+
import { Dialog } from '@arcblock/ux';
|
|
9
|
+
import { usePaymentContext } from '../contexts/payment';
|
|
10
|
+
import { formatError, formatToDate, getPrefix, isCrossOrigin } from '../libs/util';
|
|
11
|
+
import api from '../libs/api';
|
|
12
|
+
import LoadingButton from './loading-button';
|
|
13
|
+
|
|
14
|
+
type DialogProps = {
|
|
15
|
+
open?: boolean;
|
|
16
|
+
onClose?: () => void;
|
|
17
|
+
title?: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type Props = {
|
|
21
|
+
subscriptionId: string;
|
|
22
|
+
onResumed?: (subscription: Subscription | TSubscriptionExpanded) => void;
|
|
23
|
+
dialogProps?: DialogProps;
|
|
24
|
+
successToast?: boolean;
|
|
25
|
+
authToken?: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type RecoverInfo = {
|
|
29
|
+
subscription: Subscription;
|
|
30
|
+
needStake?: boolean;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const fetchSubscriptionDetail = async (params: {
|
|
34
|
+
subscriptionId: string;
|
|
35
|
+
authToken?: string;
|
|
36
|
+
}): Promise<TSubscriptionExpanded> => {
|
|
37
|
+
const url = `/api/subscriptions/${params.subscriptionId}`;
|
|
38
|
+
const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
39
|
+
return res.data;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const fetchRecoverInfo = async (params: { subscriptionId: string; authToken?: string }): Promise<RecoverInfo> => {
|
|
43
|
+
const url = `/api/subscriptions/${params.subscriptionId}/recover-info`;
|
|
44
|
+
const res = await api.get(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
45
|
+
return res.data;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const recoverSubscription = async (params: {
|
|
49
|
+
subscriptionId: string;
|
|
50
|
+
authToken?: string;
|
|
51
|
+
}): Promise<{
|
|
52
|
+
subscription: Subscription;
|
|
53
|
+
needStake?: boolean;
|
|
54
|
+
}> => {
|
|
55
|
+
const url = `/api/subscriptions/${params.subscriptionId}/recover`;
|
|
56
|
+
const res = await api.put(params.authToken ? joinURL(url, `?authToken=${params.authToken}`) : url);
|
|
57
|
+
return res.data;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function RecoverSubscription({
|
|
61
|
+
subscriptionId,
|
|
62
|
+
dialogProps = {},
|
|
63
|
+
onResumed = () => {},
|
|
64
|
+
successToast = true,
|
|
65
|
+
authToken,
|
|
66
|
+
}: Props) {
|
|
67
|
+
const { t, locale } = useLocaleContext();
|
|
68
|
+
const { connect } = usePaymentContext();
|
|
69
|
+
const [recoverLoading, setRecoverLoading] = useState(false);
|
|
70
|
+
const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false);
|
|
71
|
+
|
|
72
|
+
const { data, error, loading } = useRequest(() => fetchRecoverInfo({ subscriptionId, authToken }), {
|
|
73
|
+
ready: !!subscriptionId,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const isCrossOriginRequest = isCrossOrigin();
|
|
77
|
+
const needStake = data?.needStake ?? false;
|
|
78
|
+
|
|
79
|
+
const handleSuccess = async () => {
|
|
80
|
+
try {
|
|
81
|
+
// 获取最新的订阅数据
|
|
82
|
+
const subscription = await fetchSubscriptionDetail({ subscriptionId, authToken });
|
|
83
|
+
|
|
84
|
+
if (successToast) {
|
|
85
|
+
Toast.success(t('payment.customer.recover.success'));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setDialogOpen(false);
|
|
89
|
+
onResumed(subscription);
|
|
90
|
+
setRecoverLoading(false);
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error('Failed to fetch updated subscription:', err);
|
|
93
|
+
Toast.error(formatError(err));
|
|
94
|
+
setRecoverLoading(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleRecoverWithStake = () => {
|
|
99
|
+
setRecoverLoading(true);
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
connect.open({
|
|
103
|
+
locale: locale as 'en' | 'zh',
|
|
104
|
+
containerEl: undefined as unknown as Element,
|
|
105
|
+
saveConnect: false,
|
|
106
|
+
action: 're-stake',
|
|
107
|
+
prefix: joinURL(getPrefix(), '/api/did'),
|
|
108
|
+
useSocket: !isCrossOriginRequest,
|
|
109
|
+
extraParams: { subscriptionId },
|
|
110
|
+
messages: {
|
|
111
|
+
scan: t('common.connect.defaultScan'),
|
|
112
|
+
title: t('payment.customer.recover.reStakeTitle'),
|
|
113
|
+
confirm: t('common.connect.confirm'),
|
|
114
|
+
} as any,
|
|
115
|
+
onSuccess: handleSuccess,
|
|
116
|
+
onClose: () => {
|
|
117
|
+
connect.close();
|
|
118
|
+
setRecoverLoading(false);
|
|
119
|
+
},
|
|
120
|
+
onError: (err: any) => {
|
|
121
|
+
Toast.error(formatError(err));
|
|
122
|
+
setRecoverLoading(false);
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
} catch (err) {
|
|
126
|
+
Toast.error(formatError(err));
|
|
127
|
+
setRecoverLoading(false);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const handleDirectRecover = async () => {
|
|
132
|
+
setRecoverLoading(true);
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
const result = await recoverSubscription({ subscriptionId, authToken });
|
|
136
|
+
|
|
137
|
+
if (result.needStake) {
|
|
138
|
+
handleRecoverWithStake();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const { subscription } = result;
|
|
142
|
+
if (successToast) {
|
|
143
|
+
Toast.success(t('payment.customer.recover.success'));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
setDialogOpen(false);
|
|
147
|
+
onResumed(subscription);
|
|
148
|
+
setRecoverLoading(false);
|
|
149
|
+
} catch (err: any) {
|
|
150
|
+
Toast.error(formatError(err));
|
|
151
|
+
setRecoverLoading(false);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const handleRecover = () => {
|
|
156
|
+
if (needStake) {
|
|
157
|
+
handleRecoverWithStake();
|
|
158
|
+
} else {
|
|
159
|
+
handleDirectRecover();
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleClose = () => {
|
|
164
|
+
setDialogOpen(false);
|
|
165
|
+
dialogProps.onClose?.();
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
if (loading) {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<Dialog
|
|
174
|
+
PaperProps={{
|
|
175
|
+
style: { minHeight: 'auto' },
|
|
176
|
+
}}
|
|
177
|
+
{...(dialogProps || {})}
|
|
178
|
+
open={dialogOpen}
|
|
179
|
+
title={dialogProps?.title || t('payment.customer.recover.title')}
|
|
180
|
+
sx={{ '& .MuiDialogContent-root': { pt: 0 } }}
|
|
181
|
+
onClose={handleClose}>
|
|
182
|
+
{error ? (
|
|
183
|
+
<Alert severity="error">{error.message}</Alert>
|
|
184
|
+
) : (
|
|
185
|
+
<Stack gap={2}>
|
|
186
|
+
<Typography color="text.secondary" variant="body2">
|
|
187
|
+
{t('payment.customer.recover.description', {
|
|
188
|
+
date: formatToDate(Number(data?.subscription?.current_period_end || '0') * 1000),
|
|
189
|
+
})}
|
|
190
|
+
</Typography>
|
|
191
|
+
|
|
192
|
+
{needStake && <Alert severity="warning">{t('payment.customer.recover.stakeRequiredDescription')}</Alert>}
|
|
193
|
+
|
|
194
|
+
<Stack direction="row" justifyContent="flex-end" gap={2} mt={2}>
|
|
195
|
+
<Button variant="outlined" color="primary" onClick={handleClose}>
|
|
196
|
+
{t('common.cancel')}
|
|
197
|
+
</Button>
|
|
198
|
+
<LoadingButton variant="contained" size="small" loading={recoverLoading} onClick={handleRecover}>
|
|
199
|
+
{t('common.confirm')}
|
|
200
|
+
</LoadingButton>
|
|
201
|
+
</Stack>
|
|
202
|
+
</Stack>
|
|
203
|
+
)}
|
|
204
|
+
</Dialog>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
RecoverSubscription.defaultProps = {
|
|
209
|
+
onResumed: () => {},
|
|
210
|
+
dialogProps: {
|
|
211
|
+
open: true,
|
|
212
|
+
},
|
|
213
|
+
successToast: true,
|
|
214
|
+
authToken: undefined,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
export default RecoverSubscription;
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,7 @@ import { createLazyComponent } from './components/lazy-loader';
|
|
|
31
31
|
import OverdueInvoicePayment from './components/over-due-invoice-payment';
|
|
32
32
|
import PaymentBeneficiaries from './components/payment-beneficiaries';
|
|
33
33
|
import LoadingButton from './components/loading-button';
|
|
34
|
+
import ResumeSubscription from './components/resume-subscription';
|
|
34
35
|
|
|
35
36
|
export { PaymentThemeProvider } from './theme';
|
|
36
37
|
|
|
@@ -84,4 +85,5 @@ export {
|
|
|
84
85
|
PaymentBeneficiaries,
|
|
85
86
|
LoadingButton,
|
|
86
87
|
DonateDetails,
|
|
88
|
+
ResumeSubscription,
|
|
87
89
|
};
|
package/src/libs/util.ts
CHANGED
|
@@ -1214,6 +1214,7 @@ export function getInvoiceDescriptionAndReason(invoice: TInvoiceExpanded, locale
|
|
|
1214
1214
|
'payment.invoice.reason.stakeForSubscriptionOverdraftProtection',
|
|
1215
1215
|
locale
|
|
1216
1216
|
),
|
|
1217
|
+
'Re-stake to resume subscription': t('payment.invoice.reason.reStakeToResumeSubscription', locale),
|
|
1217
1218
|
};
|
|
1218
1219
|
|
|
1219
1220
|
return {
|
package/src/locales/en.tsx
CHANGED
|
@@ -257,9 +257,9 @@ export default flat({
|
|
|
257
257
|
title: 'Cancel your subscription',
|
|
258
258
|
comment: 'Any additional feedback?',
|
|
259
259
|
description:
|
|
260
|
-
'Your subscription will be canceled, but it is still available until the end of your current billing period on {date}',
|
|
260
|
+
'Your subscription will be canceled, but it is still available until the end of your current billing period on {date}.',
|
|
261
261
|
trialDescription:
|
|
262
|
-
'Free trial subscriptions will be canceled immediately and no longer available, confirm to continue',
|
|
262
|
+
'Free trial subscriptions will be canceled immediately and no longer available, confirm to continue.',
|
|
263
263
|
feedback: {
|
|
264
264
|
tip: 'We would love your feedback, it will help us improve our service',
|
|
265
265
|
too_expensive: 'The service is too expensive',
|
|
@@ -287,7 +287,12 @@ export default flat({
|
|
|
287
287
|
button: 'Resume Subscription',
|
|
288
288
|
title: 'Resume Your Subscription',
|
|
289
289
|
description:
|
|
290
|
-
'Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue',
|
|
290
|
+
'Your subscription will not be canceled and will be automatically renewed on {date}, please confirm to continue.',
|
|
291
|
+
success: 'Subscription resumed successfully',
|
|
292
|
+
reStakeTitle: 'Re-stake to resume subscription',
|
|
293
|
+
stakeRequiredDescription:
|
|
294
|
+
'Your previous stake has been revoked. You need to re-stake to resume your subscription.',
|
|
295
|
+
stakeWarning: 'Please ensure you have sufficient balance for staking before proceeding.',
|
|
291
296
|
},
|
|
292
297
|
changePlan: {
|
|
293
298
|
button: 'Change Plan',
|
|
@@ -388,6 +393,7 @@ export default flat({
|
|
|
388
393
|
rechargeForSubscription: 'Add funds for subscription',
|
|
389
394
|
overdraftProtection: 'SubGuard™ Fee',
|
|
390
395
|
stakeForSubscriptionOverdraftProtection: 'SubGuard™',
|
|
396
|
+
reStakeToResumeSubscription: 'Subscription resume',
|
|
391
397
|
gas: 'Gas',
|
|
392
398
|
fee: 'Fee',
|
|
393
399
|
},
|
package/src/locales/zh.tsx
CHANGED
|
@@ -251,8 +251,8 @@ export default flat({
|
|
|
251
251
|
button: '取消订阅',
|
|
252
252
|
title: '取消您的订阅',
|
|
253
253
|
comment: '你还有其他反馈么?',
|
|
254
|
-
description: '您的订阅将被取消,但仍然可用直到您当前计费周期结束于{date}',
|
|
255
|
-
trialDescription: '
|
|
254
|
+
description: '您的订阅将被取消,但仍然可用直到您当前计费周期结束于{date}。',
|
|
255
|
+
trialDescription: '免费试用的订阅将被立即取消,不再可用,确认是否继续。',
|
|
256
256
|
feedback: {
|
|
257
257
|
tip: '我们希望听到您的反馈,这将帮助我们改进我们的服务',
|
|
258
258
|
too_expensive: '服务费用太高',
|
|
@@ -280,7 +280,10 @@ export default flat({
|
|
|
280
280
|
recover: {
|
|
281
281
|
button: '恢复订阅',
|
|
282
282
|
title: '恢复您的订阅',
|
|
283
|
-
description: '您的订阅将不会被取消,并将在{date}
|
|
283
|
+
description: '您的订阅将不会被取消,并将在{date}自动续订,请确认是否继续。',
|
|
284
|
+
success: '订阅恢复成功',
|
|
285
|
+
reStakeTitle: '重新质押以恢复订阅',
|
|
286
|
+
stakeRequiredDescription: '您之前的质押已被申请取回,需要重新质押才能恢复订阅。',
|
|
284
287
|
},
|
|
285
288
|
changePlan: {
|
|
286
289
|
button: '切换套餐',
|
|
@@ -378,6 +381,7 @@ export default flat({
|
|
|
378
381
|
rechargeForSubscription: '订阅充值',
|
|
379
382
|
overdraftProtection: '订阅守护服务费',
|
|
380
383
|
stakeForSubscriptionOverdraftProtection: '订阅守护服务',
|
|
384
|
+
reStakeToResumeSubscription: '订阅恢复',
|
|
381
385
|
gas: '手续费',
|
|
382
386
|
fee: '服务费',
|
|
383
387
|
},
|
|
@@ -3,7 +3,7 @@ import Center from '@arcblock/ux/lib/Center';
|
|
|
3
3
|
import Dialog from '@arcblock/ux/lib/Dialog';
|
|
4
4
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
5
5
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
6
|
-
import { CircularProgress, Typography } from '@mui/material';
|
|
6
|
+
import { CircularProgress, Typography, useTheme } from '@mui/material';
|
|
7
7
|
import { styled } from '@mui/system';
|
|
8
8
|
import { useSetState } from 'ahooks';
|
|
9
9
|
import { useEffect, useCallback } from 'react';
|
|
@@ -43,6 +43,7 @@ function StripeCheckoutForm({
|
|
|
43
43
|
const stripe = useStripe();
|
|
44
44
|
const elements = useElements();
|
|
45
45
|
const { t } = useLocaleContext();
|
|
46
|
+
const theme = useTheme();
|
|
46
47
|
|
|
47
48
|
const [state, setState] = useSetState({
|
|
48
49
|
message: '',
|
|
@@ -204,6 +205,16 @@ function StripeCheckoutForm({
|
|
|
204
205
|
address: customer.address,
|
|
205
206
|
},
|
|
206
207
|
},
|
|
208
|
+
appearance: {
|
|
209
|
+
theme: theme.palette.mode,
|
|
210
|
+
variables: {
|
|
211
|
+
colorPrimary: theme.palette.primary.main,
|
|
212
|
+
colorBackground: theme.palette.background.paper,
|
|
213
|
+
colorText: theme.palette.text.primary,
|
|
214
|
+
colorDanger: theme.palette.error.main,
|
|
215
|
+
borderRadius: '4px',
|
|
216
|
+
},
|
|
217
|
+
},
|
|
207
218
|
}}
|
|
208
219
|
onChange={handlePaymentMethodChange}
|
|
209
220
|
onReady={() => setState({ loaded: true })}
|
|
@@ -266,6 +277,7 @@ export default function StripeCheckout({
|
|
|
266
277
|
const stripePromise = loadStripe(publicKey);
|
|
267
278
|
const { isMobile } = useMobile();
|
|
268
279
|
const { t, locale } = useLocaleContext();
|
|
280
|
+
const theme = useTheme();
|
|
269
281
|
const [state, setState] = useSetState({
|
|
270
282
|
open: true,
|
|
271
283
|
closable: true,
|
|
@@ -295,6 +307,15 @@ export default function StripeCheckout({
|
|
|
295
307
|
form: {
|
|
296
308
|
justifyContent: 'flex-start',
|
|
297
309
|
},
|
|
310
|
+
'.StripeElement--focus': {
|
|
311
|
+
borderColor: theme.palette.primary.main,
|
|
312
|
+
},
|
|
313
|
+
'.StripeElement--invalid': {
|
|
314
|
+
borderColor: theme.palette.error.main,
|
|
315
|
+
},
|
|
316
|
+
'.StripeElement--complete': {
|
|
317
|
+
borderColor: theme.palette.success.main,
|
|
318
|
+
},
|
|
298
319
|
}}
|
|
299
320
|
PaperProps={{
|
|
300
321
|
style: {
|
|
@@ -305,6 +326,15 @@ export default function StripeCheckout({
|
|
|
305
326
|
options={{
|
|
306
327
|
clientSecret,
|
|
307
328
|
locale: locale === 'zh' ? 'zh-CN' : 'en',
|
|
329
|
+
appearance: {
|
|
330
|
+
theme: theme.palette.mode,
|
|
331
|
+
variables: {
|
|
332
|
+
colorPrimary: theme.palette.primary.main,
|
|
333
|
+
colorBackground: theme.palette.background.paper,
|
|
334
|
+
colorText: theme.palette.text.primary,
|
|
335
|
+
colorDanger: theme.palette.error.main,
|
|
336
|
+
},
|
|
337
|
+
},
|
|
308
338
|
}}
|
|
309
339
|
stripe={stripePromise}>
|
|
310
340
|
<StripeCheckoutForm
|