payment-kit 1.15.33 → 1.15.35
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/api/src/integrations/stripe/handlers/setup-intent.ts +3 -1
- package/api/src/integrations/stripe/handlers/subscription.ts +2 -8
- package/api/src/integrations/stripe/resource.ts +0 -11
- package/api/src/libs/invoice.ts +202 -1
- package/api/src/libs/notification/template/subscription-canceled.ts +15 -2
- package/api/src/libs/notification/template/subscription-renew-failed.ts +1 -1
- package/api/src/libs/notification/template/subscription-renewed.ts +1 -1
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +9 -5
- package/api/src/libs/notification/template/subscription-will-canceled.ts +9 -5
- package/api/src/libs/notification/template/subscription-will-renew.ts +10 -12
- package/api/src/libs/payment.ts +3 -2
- package/api/src/libs/refund.ts +4 -0
- package/api/src/libs/subscription.ts +58 -14
- package/api/src/queues/invoice.ts +1 -0
- package/api/src/queues/payment.ts +3 -1
- package/api/src/queues/refund.ts +9 -8
- package/api/src/queues/subscription.ts +111 -40
- package/api/src/routes/checkout-sessions.ts +22 -6
- package/api/src/routes/connect/change-payment.ts +51 -34
- package/api/src/routes/connect/change-plan.ts +25 -3
- package/api/src/routes/connect/recharge.ts +28 -3
- package/api/src/routes/connect/setup.ts +27 -6
- package/api/src/routes/connect/shared.ts +223 -1
- package/api/src/routes/connect/subscribe.ts +25 -3
- package/api/src/routes/customers.ts +2 -2
- package/api/src/routes/invoices.ts +27 -105
- package/api/src/routes/payment-links.ts +3 -0
- package/api/src/routes/refunds.ts +22 -1
- package/api/src/routes/subscriptions.ts +112 -21
- package/api/src/routes/webhook-attempts.ts +14 -1
- package/api/src/store/models/invoice.ts +3 -1
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/app.tsx +3 -1
- package/src/components/invoice/list.tsx +83 -31
- package/src/components/invoice/recharge.tsx +244 -0
- package/src/components/payment-intent/actions.tsx +2 -1
- package/src/components/payment-link/actions.tsx +6 -6
- package/src/components/payment-link/item.tsx +53 -18
- package/src/components/pricing-table/actions.tsx +14 -3
- package/src/components/pricing-table/payment-settings.tsx +1 -1
- package/src/components/refund/actions.tsx +43 -1
- package/src/components/refund/list.tsx +1 -1
- package/src/components/subscription/actions/cancel.tsx +10 -7
- package/src/components/subscription/metrics.tsx +1 -1
- package/src/components/subscription/portal/actions.tsx +22 -1
- package/src/components/subscription/portal/list.tsx +1 -0
- package/src/components/webhook/attempts.tsx +19 -121
- package/src/components/webhook/request-info.tsx +139 -0
- package/src/locales/en.tsx +4 -0
- package/src/locales/zh.tsx +8 -0
- package/src/pages/admin/billing/invoices/detail.tsx +15 -0
- package/src/pages/admin/billing/invoices/index.tsx +1 -1
- package/src/pages/admin/billing/subscriptions/detail.tsx +12 -4
- package/src/pages/admin/customers/customers/detail.tsx +1 -0
- package/src/pages/admin/payments/refunds/detail.tsx +2 -2
- package/src/pages/admin/products/links/create.tsx +4 -1
- package/src/pages/customer/index.tsx +1 -1
- package/src/pages/customer/invoice/detail.tsx +34 -14
- package/src/pages/customer/recharge.tsx +45 -35
- package/src/pages/customer/subscription/change-plan.tsx +8 -1
- package/src/pages/customer/subscription/detail.tsx +12 -22
- package/src/pages/customer/subscription/embed.tsx +3 -1
|
@@ -30,12 +30,14 @@ import { joinURL } from 'ufo';
|
|
|
30
30
|
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
31
31
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
32
32
|
import { BN, fromUnitToToken } from '@ocap/util';
|
|
33
|
+
import RechargeList from '../../components/invoice/recharge';
|
|
33
34
|
import SubscriptionDescription from '../../components/subscription/description';
|
|
34
35
|
import InfoRow from '../../components/info-row';
|
|
35
36
|
import Currency from '../../components/currency';
|
|
36
37
|
import SubscriptionMetrics from '../../components/subscription/metrics';
|
|
37
38
|
import { goBackOrFallback } from '../../libs/util';
|
|
38
39
|
import CustomerLink from '../../components/customer/link';
|
|
40
|
+
import { useSessionContext } from '../../contexts/session';
|
|
39
41
|
|
|
40
42
|
const Root = styled(Stack)(({ theme }) => ({
|
|
41
43
|
marginBottom: theme.spacing(3),
|
|
@@ -70,6 +72,7 @@ export default function RechargePage() {
|
|
|
70
72
|
} | null>(null);
|
|
71
73
|
const [customAmount, setCustomAmount] = useState(false);
|
|
72
74
|
const [presetAmounts, setPresetAmounts] = useState<Array<{ amount: string; cycles: number }>>([]);
|
|
75
|
+
const { session } = useSessionContext();
|
|
73
76
|
|
|
74
77
|
const {
|
|
75
78
|
paymentCurrency,
|
|
@@ -84,42 +87,41 @@ export default function RechargePage() {
|
|
|
84
87
|
|
|
85
88
|
// @ts-ignore
|
|
86
89
|
const receiveAddress = paymentDetails?.[paymentMethod?.type]?.payer;
|
|
90
|
+
const fetchData = async () => {
|
|
91
|
+
try {
|
|
92
|
+
setLoading(true);
|
|
93
|
+
const [subscriptionRes, payerTokenRes, upcomingRes] = await Promise.all([
|
|
94
|
+
api.get(`/api/subscriptions/${subscriptionId}`),
|
|
95
|
+
api.get(`/api/subscriptions/${subscriptionId}/payer-token`),
|
|
96
|
+
api.get(`/api/subscriptions/${subscriptionId}/upcoming`),
|
|
97
|
+
]);
|
|
98
|
+
setSubscription(subscriptionRes.data);
|
|
99
|
+
setPayerValue(payerTokenRes.data);
|
|
100
|
+
|
|
101
|
+
// Calculate preset amounts
|
|
102
|
+
const getCycleAmount = (cycles: number) =>
|
|
103
|
+
fromUnitToToken(
|
|
104
|
+
new BN(upcomingRes.data.amount === '0' ? upcomingRes.data.minExpectedAmount : upcomingRes.data.amount)
|
|
105
|
+
.mul(new BN(cycles))
|
|
106
|
+
.toString(),
|
|
107
|
+
upcomingRes.data?.currency?.decimal
|
|
108
|
+
);
|
|
109
|
+
setPresetAmounts([
|
|
110
|
+
{ amount: getCycleAmount(1), cycles: 1 },
|
|
111
|
+
{ amount: getCycleAmount(2), cycles: 2 },
|
|
112
|
+
{ amount: getCycleAmount(3), cycles: 3 },
|
|
113
|
+
{ amount: getCycleAmount(5), cycles: 5 },
|
|
114
|
+
{ amount: getCycleAmount(10), cycles: 10 },
|
|
115
|
+
]);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
setError(err instanceof Error ? err.message : t('common.fetchError'));
|
|
118
|
+
console.error(err);
|
|
119
|
+
} finally {
|
|
120
|
+
setLoading(false);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
87
123
|
|
|
88
124
|
useEffect(() => {
|
|
89
|
-
const fetchData = async () => {
|
|
90
|
-
try {
|
|
91
|
-
setLoading(true);
|
|
92
|
-
const [subscriptionRes, payerTokenRes, upcomingRes] = await Promise.all([
|
|
93
|
-
api.get(`/api/subscriptions/${subscriptionId}`),
|
|
94
|
-
api.get(`/api/subscriptions/${subscriptionId}/payer-token`),
|
|
95
|
-
api.get(`/api/subscriptions/${subscriptionId}/upcoming`),
|
|
96
|
-
]);
|
|
97
|
-
setSubscription(subscriptionRes.data);
|
|
98
|
-
setPayerValue(payerTokenRes.data);
|
|
99
|
-
|
|
100
|
-
// Calculate preset amounts
|
|
101
|
-
const getCycleAmount = (cycles: number) =>
|
|
102
|
-
fromUnitToToken(
|
|
103
|
-
new BN(upcomingRes.data.amount === '0' ? upcomingRes.data.minExpectedAmount : upcomingRes.data.amount)
|
|
104
|
-
.mul(new BN(cycles))
|
|
105
|
-
.toString(),
|
|
106
|
-
upcomingRes.data?.currency?.decimal
|
|
107
|
-
);
|
|
108
|
-
setPresetAmounts([
|
|
109
|
-
{ amount: getCycleAmount(1), cycles: 1 },
|
|
110
|
-
{ amount: getCycleAmount(2), cycles: 2 },
|
|
111
|
-
{ amount: getCycleAmount(3), cycles: 3 },
|
|
112
|
-
{ amount: getCycleAmount(5), cycles: 5 },
|
|
113
|
-
{ amount: getCycleAmount(10), cycles: 10 },
|
|
114
|
-
]);
|
|
115
|
-
} catch (err) {
|
|
116
|
-
setError(err instanceof Error ? err.message : t('common.fetchError'));
|
|
117
|
-
console.error(err);
|
|
118
|
-
} finally {
|
|
119
|
-
setLoading(false);
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
|
|
123
125
|
fetchData();
|
|
124
126
|
}, [subscriptionId]);
|
|
125
127
|
|
|
@@ -135,7 +137,7 @@ export default function RechargePage() {
|
|
|
135
137
|
onSuccess: () => {
|
|
136
138
|
connect.close();
|
|
137
139
|
Toast.success(t('customer.recharge.success'));
|
|
138
|
-
|
|
140
|
+
fetchData();
|
|
139
141
|
},
|
|
140
142
|
onClose: () => {
|
|
141
143
|
connect.close();
|
|
@@ -181,6 +183,9 @@ export default function RechargePage() {
|
|
|
181
183
|
return <Alert severity="info">{t('common.dataNotFound')}</Alert>;
|
|
182
184
|
}
|
|
183
185
|
|
|
186
|
+
if (subscription?.customer?.did && session?.user?.did && subscription.customer.did !== session.user.did) {
|
|
187
|
+
return <Alert severity="error">You do not have permission to access other customer data</Alert>;
|
|
188
|
+
}
|
|
184
189
|
const currentBalance = formatBNStr(payerValue?.token || '0', paymentCurrency?.decimal, 6, false);
|
|
185
190
|
|
|
186
191
|
const supportRecharge = ['arcblock', 'ethereum'].includes(paymentMethod?.type || '');
|
|
@@ -414,6 +419,11 @@ export default function RechargePage() {
|
|
|
414
419
|
<Alert severity="info">{t('customer.recharge.unsupported')}</Alert>
|
|
415
420
|
)}
|
|
416
421
|
</Box>
|
|
422
|
+
<Divider />
|
|
423
|
+
<Typography variant="h2" gutterBottom>
|
|
424
|
+
{t('customer.recharge.history')}
|
|
425
|
+
</Typography>
|
|
426
|
+
<RechargeList subscription_id={subscriptionId} currency_id={paymentCurrency?.id} />
|
|
417
427
|
</Root>
|
|
418
428
|
);
|
|
419
429
|
}
|
|
@@ -243,7 +243,14 @@ export default function CustomerSubscriptionChangePlan() {
|
|
|
243
243
|
</Stack>
|
|
244
244
|
<Stack direction="column" sx={{ marginTop: 32 }}>
|
|
245
245
|
<SectionHeader title={t('payment.customer.changePlan.config')} />
|
|
246
|
-
<PricingTable
|
|
246
|
+
<PricingTable
|
|
247
|
+
mode="select"
|
|
248
|
+
alignItems="left"
|
|
249
|
+
interval={interval}
|
|
250
|
+
table={table}
|
|
251
|
+
onSelect={handleSelect}
|
|
252
|
+
hideCurrency
|
|
253
|
+
/>
|
|
247
254
|
</Stack>
|
|
248
255
|
{!data.table && <Alert severity="error">{t('payment.customer.changePlan.tableNotFound')}</Alert>}
|
|
249
256
|
{state.priceId && state.total && state.setup && (
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import { CustomerInvoiceList, TxLink, api, formatTime } from '@blocklet/payment-react';
|
|
3
|
+
import { CustomerInvoiceList, TxLink, api, formatTime, hasDelegateTxHash } from '@blocklet/payment-react';
|
|
4
4
|
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
5
5
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
6
6
|
import { Alert, Box, Button, CircularProgress, Divider, Stack, Typography } from '@mui/material';
|
|
@@ -15,7 +15,8 @@ import SubscriptionDescription from '../../../components/subscription/descriptio
|
|
|
15
15
|
import SubscriptionItemList from '../../../components/subscription/items';
|
|
16
16
|
import SubscriptionMetrics from '../../../components/subscription/metrics';
|
|
17
17
|
import SubscriptionActions from '../../../components/subscription/portal/actions';
|
|
18
|
-
import { canChangePaymentMethod
|
|
18
|
+
import { canChangePaymentMethod } from '../../../libs/util';
|
|
19
|
+
import { useSessionContext } from '../../../contexts/session';
|
|
19
20
|
|
|
20
21
|
const fetchData = (id: string | undefined): Promise<TSubscriptionExpanded> => {
|
|
21
22
|
return api.get(`/api/subscriptions/${id}`).then((res) => res.data);
|
|
@@ -24,19 +25,15 @@ const fetchData = (id: string | undefined): Promise<TSubscriptionExpanded> => {
|
|
|
24
25
|
const InfoDirection = 'column';
|
|
25
26
|
const InfoAlignItems = 'flex-start';
|
|
26
27
|
|
|
27
|
-
const supportRecharge = (subscription: TSubscriptionExpanded) => {
|
|
28
|
-
return (
|
|
29
|
-
['active', 'trialing', 'past_due'].includes(subscription?.status) &&
|
|
30
|
-
['arcblock', 'ethereum'].includes(subscription?.paymentMethod?.type)
|
|
31
|
-
);
|
|
32
|
-
};
|
|
33
|
-
|
|
34
28
|
export default function CustomerSubscriptionDetail() {
|
|
35
29
|
const { id } = useParams() as { id: string };
|
|
36
30
|
const navigate = useNavigate();
|
|
37
31
|
const { t } = useLocaleContext();
|
|
32
|
+
const { session } = useSessionContext();
|
|
38
33
|
const { loading, error, data, refresh } = useRequest(() => fetchData(id));
|
|
39
|
-
|
|
34
|
+
if (data?.customer?.did && session?.user?.did && data.customer.did !== session.user.did) {
|
|
35
|
+
return <Alert severity="error">You do not have permission to access other customer data</Alert>;
|
|
36
|
+
}
|
|
40
37
|
if (error) {
|
|
41
38
|
return <Alert severity="error">{error.message}</Alert>;
|
|
42
39
|
}
|
|
@@ -56,7 +53,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
56
53
|
sx={{ position: 'relative', mt: '16px' }}>
|
|
57
54
|
<Stack
|
|
58
55
|
direction="row"
|
|
59
|
-
onClick={() =>
|
|
56
|
+
onClick={() => navigate('/customer', { replace: true })}
|
|
60
57
|
alignItems="center"
|
|
61
58
|
sx={{ fontWeight: 'normal', cursor: 'pointer' }}>
|
|
62
59
|
<ArrowBackOutlined fontSize="small" sx={{ mr: 0.5, color: 'text.secondary' }} />
|
|
@@ -69,6 +66,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
69
66
|
subscription={data}
|
|
70
67
|
onChange={() => refresh()}
|
|
71
68
|
showExtra
|
|
69
|
+
showRecharge
|
|
72
70
|
actionProps={{
|
|
73
71
|
cancel: {
|
|
74
72
|
variant: 'outlined',
|
|
@@ -84,14 +82,6 @@ export default function CustomerSubscriptionDetail() {
|
|
|
84
82
|
},
|
|
85
83
|
}}
|
|
86
84
|
/>
|
|
87
|
-
{supportRecharge(data) && (
|
|
88
|
-
<Button
|
|
89
|
-
variant="outlined"
|
|
90
|
-
color="primary"
|
|
91
|
-
onClick={() => navigate(`/customer/subscription/${data.id}/recharge`)}>
|
|
92
|
-
{t('customer.recharge.title')}
|
|
93
|
-
</Button>
|
|
94
|
-
)}
|
|
95
85
|
</Stack>
|
|
96
86
|
</Stack>
|
|
97
87
|
<Box
|
|
@@ -243,7 +233,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
243
233
|
direction={InfoDirection}
|
|
244
234
|
alignItems={InfoAlignItems}
|
|
245
235
|
/>
|
|
246
|
-
{
|
|
236
|
+
{data.payment_details && hasDelegateTxHash(data.payment_details, data.paymentMethod) && (
|
|
247
237
|
<InfoRow
|
|
248
238
|
label={t('common.delegateTxHash')}
|
|
249
239
|
value={<TxLink details={data.payment_details} method={data.paymentMethod} />}
|
|
@@ -251,7 +241,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
251
241
|
alignItems={InfoAlignItems}
|
|
252
242
|
/>
|
|
253
243
|
)}
|
|
254
|
-
{data.payment_details?.arcblock?.staking?.tx_hash && (
|
|
244
|
+
{data.paymentMethod?.type === 'arcblock' && data.payment_details?.arcblock?.staking?.tx_hash && (
|
|
255
245
|
<InfoRow
|
|
256
246
|
label={t('common.stakeTxHash')}
|
|
257
247
|
value={
|
|
@@ -300,7 +290,7 @@ export default function CustomerSubscriptionDetail() {
|
|
|
300
290
|
{t('customer.invoiceHistory')}
|
|
301
291
|
</Typography>
|
|
302
292
|
<Box className="section-body">
|
|
303
|
-
<CustomerInvoiceList subscription_id={data.id}
|
|
293
|
+
<CustomerInvoiceList subscription_id={data.id} type="table" include_staking />
|
|
304
294
|
</Box>
|
|
305
295
|
</Box>
|
|
306
296
|
</Root>
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
formatToDate,
|
|
12
12
|
formatToDatetime,
|
|
13
13
|
getDidConnectQueryParams,
|
|
14
|
+
getInvoiceDescriptionAndReason,
|
|
14
15
|
getInvoiceStatusColor,
|
|
15
16
|
getPrefix,
|
|
16
17
|
getSubscriptionStatusColor,
|
|
@@ -210,8 +211,9 @@ export default function SubscriptionEmbed() {
|
|
|
210
211
|
{(invoices as any).map((item: any) => {
|
|
211
212
|
return (
|
|
212
213
|
<ListItem key={item.id} disableGutters sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
213
|
-
<Typography component="
|
|
214
|
+
<Typography component="div" sx={{ flex: 3, gap: 1, display: 'flex', alignItems: 'center' }}>
|
|
214
215
|
{formatToDate(item.created_at, locale, 'YYYY-MM-DD')}
|
|
216
|
+
<Status label={getInvoiceDescriptionAndReason(item, locale)?.type} />
|
|
215
217
|
</Typography>
|
|
216
218
|
<Typography component="span" sx={{ flex: 1, textAlign: 'right' }}>
|
|
217
219
|
{formatBNStr(item.total, item.paymentCurrency.decimal)}
|