@blocklet/payment-react 1.18.15 → 1.18.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/es/checkout/donate.js +17 -8
- package/es/components/over-due-invoice-payment.d.ts +20 -7
- package/es/components/over-due-invoice-payment.js +223 -75
- package/es/contexts/payment.js +14 -1
- package/es/history/invoice/list.d.ts +2 -0
- package/es/history/invoice/list.js +61 -13
- package/es/locales/en.js +17 -7
- package/es/locales/zh.js +17 -7
- package/es/payment/donation-form.js +2 -1
- package/es/payment/form/address.js +63 -65
- package/es/payment/form/index.d.ts +3 -1
- package/es/payment/form/index.js +17 -5
- package/lib/checkout/donate.js +18 -9
- package/lib/components/over-due-invoice-payment.d.ts +20 -7
- package/lib/components/over-due-invoice-payment.js +218 -76
- package/lib/contexts/payment.js +14 -1
- package/lib/history/invoice/list.d.ts +2 -0
- package/lib/history/invoice/list.js +84 -24
- package/lib/locales/en.js +17 -7
- package/lib/locales/zh.js +17 -7
- package/lib/payment/donation-form.js +2 -1
- package/lib/payment/form/address.js +46 -50
- package/lib/payment/form/index.d.ts +3 -1
- package/lib/payment/form/index.js +17 -5
- package/package.json +8 -7
- package/src/checkout/donate.tsx +16 -7
- package/src/components/over-due-invoice-payment.tsx +277 -81
- package/src/contexts/payment.tsx +15 -1
- package/src/history/invoice/list.tsx +75 -16
- package/src/locales/en.tsx +14 -4
- package/src/locales/zh.tsx +14 -3
- package/src/payment/donation-form.tsx +1 -0
- package/src/payment/form/address.tsx +36 -38
- package/src/payment/form/index.tsx +16 -4
package/src/checkout/donate.tsx
CHANGED
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
} from '@mui/material';
|
|
29
29
|
import { useRequest, useSetState } from 'ahooks';
|
|
30
30
|
import omit from 'lodash/omit';
|
|
31
|
-
import uniqBy from 'lodash/
|
|
31
|
+
import uniqBy from 'lodash/uniqBy';
|
|
32
32
|
import { useEffect, useRef, useState } from 'react';
|
|
33
33
|
import { Settings } from '@mui/icons-material';
|
|
34
34
|
|
|
@@ -190,11 +190,14 @@ export function DonateDetails({ supporters = [], currency, method }: DonateHisto
|
|
|
190
190
|
<Avatar
|
|
191
191
|
key={x.id}
|
|
192
192
|
src={getCustomerAvatar(x.customer?.did, x?.updated_at ? new Date(x.updated_at).toISOString() : '', 20)}
|
|
193
|
-
alt={x.customer?.name}
|
|
193
|
+
alt={x.customer?.metadata?.anonymous ? '' : x.customer?.name}
|
|
194
194
|
variant="circular"
|
|
195
195
|
sx={{ width: 20, height: 20 }}
|
|
196
196
|
onClick={(e) => {
|
|
197
197
|
e.stopPropagation();
|
|
198
|
+
if (x.customer?.metadata?.anonymous) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
198
201
|
if (x.customer?.did) {
|
|
199
202
|
window.open(getUserProfileLink(x.customer?.did, locale), '_blank');
|
|
200
203
|
}
|
|
@@ -210,6 +213,9 @@ export function DonateDetails({ supporters = [], currency, method }: DonateHisto
|
|
|
210
213
|
textOverflow: 'ellipsis',
|
|
211
214
|
}}
|
|
212
215
|
onClick={(e) => {
|
|
216
|
+
if (x.customer?.metadata?.anonymous) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
213
219
|
e.stopPropagation();
|
|
214
220
|
if (x.customer?.did) {
|
|
215
221
|
window.open(getUserProfileLink(x.customer?.did, locale), '_blank');
|
|
@@ -244,7 +250,7 @@ function SupporterAvatar({
|
|
|
244
250
|
showDonateDetails = false,
|
|
245
251
|
}: DonateHistory & { showDonateDetails?: boolean }) {
|
|
246
252
|
const [open, setOpen] = useState(false);
|
|
247
|
-
const customers = uniqBy(supporters, '
|
|
253
|
+
const customers = uniqBy(supporters, 'customer_id');
|
|
248
254
|
const customersNum = customers.length;
|
|
249
255
|
if (customersNum === 0) return null;
|
|
250
256
|
return (
|
|
@@ -254,6 +260,9 @@ function SupporterAvatar({
|
|
|
254
260
|
sx={{
|
|
255
261
|
'& .MuiAvatar-root': {
|
|
256
262
|
backgroundColor: 'background.paper',
|
|
263
|
+
'&.MuiAvatar-colorDefault': {
|
|
264
|
+
backgroundColor: '#bdbdbd',
|
|
265
|
+
},
|
|
257
266
|
},
|
|
258
267
|
}}>
|
|
259
268
|
{customers.slice(0, 5).map((supporter) => (
|
|
@@ -263,7 +272,7 @@ function SupporterAvatar({
|
|
|
263
272
|
supporter?.updated_at ? new Date(supporter.updated_at).toISOString() : '',
|
|
264
273
|
24
|
|
265
274
|
)}
|
|
266
|
-
alt={supporter.customer?.name}
|
|
275
|
+
alt={supporter.customer?.metadata?.anonymous ? '' : supporter.customer?.name}
|
|
267
276
|
key={supporter.customer?.id}
|
|
268
277
|
sx={{
|
|
269
278
|
width: '24px',
|
|
@@ -312,7 +321,7 @@ function SupporterAvatar({
|
|
|
312
321
|
|
|
313
322
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
314
323
|
function SupporterTable({ supporters = [], totalAmount = '0', currency, method }: DonateHistory) {
|
|
315
|
-
const customers = uniqBy(supporters, '
|
|
324
|
+
const customers = uniqBy(supporters, 'customer_id');
|
|
316
325
|
const customersNum = customers.length;
|
|
317
326
|
if (customersNum === 0) return null;
|
|
318
327
|
return (
|
|
@@ -326,7 +335,7 @@ function SupporterTable({ supporters = [], totalAmount = '0', currency, method }
|
|
|
326
335
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
327
336
|
function SupporterSimple({ supporters = [], totalAmount = '0', currency, method }: DonateHistory) {
|
|
328
337
|
const { t } = useLocaleContext();
|
|
329
|
-
const customers = uniqBy(supporters, '
|
|
338
|
+
const customers = uniqBy(supporters, 'customer_id');
|
|
330
339
|
const customersNum = customers.length;
|
|
331
340
|
|
|
332
341
|
return (
|
|
@@ -354,7 +363,7 @@ function SupporterSimple({ supporters = [], totalAmount = '0', currency, method
|
|
|
354
363
|
<Avatar
|
|
355
364
|
key={x.id}
|
|
356
365
|
title={x.customer?.name}
|
|
357
|
-
alt={x.customer?.name}
|
|
366
|
+
alt={x.customer?.metadata?.anonymous ? '' : x.customer?.name}
|
|
358
367
|
src={getCustomerAvatar(x.customer?.did, x?.updated_at ? new Date(x.updated_at).toISOString() : '', 48)}
|
|
359
368
|
variant="circular"
|
|
360
369
|
sx={{ width: 24, height: 24 }}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
1
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { Button, Typography, Stack,
|
|
3
|
+
import { Button, Typography, Stack, Alert, SxProps } from '@mui/material';
|
|
3
4
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
5
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
5
6
|
import { joinURL } from 'ufo';
|
|
6
7
|
import type { Invoice, PaymentCurrency, PaymentMethod, Subscription, TInvoiceExpanded } from '@blocklet/payment-types';
|
|
7
8
|
import { useRequest } from 'ahooks';
|
|
8
9
|
import { Dialog } from '@arcblock/ux';
|
|
10
|
+
import { CheckCircle as CheckCircleIcon } from '@mui/icons-material';
|
|
11
|
+
import debounce from 'lodash/debounce';
|
|
9
12
|
import { usePaymentContext } from '../contexts/payment';
|
|
10
13
|
import { formatAmount, formatError, getPrefix } from '../libs/util';
|
|
11
14
|
import { useSubscription } from '../hooks/subscription';
|
|
12
15
|
import api from '../libs/api';
|
|
16
|
+
import LoadingButton from './loading-button';
|
|
13
17
|
|
|
14
18
|
type DialogProps = {
|
|
15
19
|
open?: boolean;
|
|
@@ -17,19 +21,28 @@ type DialogProps = {
|
|
|
17
21
|
title?: string;
|
|
18
22
|
};
|
|
19
23
|
|
|
24
|
+
type DetailLinkOptions = {
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
onClick?: (e: React.MouseEvent) => void;
|
|
27
|
+
title?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
20
30
|
type Props = {
|
|
21
|
-
subscriptionId
|
|
31
|
+
subscriptionId?: string;
|
|
32
|
+
customerId?: string;
|
|
22
33
|
mode?: 'default' | 'custom';
|
|
23
|
-
onPaid?: (
|
|
34
|
+
onPaid?: (id: string, currencyId: string, type: 'subscription' | 'customer') => void;
|
|
24
35
|
dialogProps?: DialogProps;
|
|
25
|
-
|
|
36
|
+
detailLinkOptions?: DetailLinkOptions;
|
|
37
|
+
successToast?: boolean;
|
|
26
38
|
children?: (
|
|
27
39
|
handlePay: (item: SummaryItem) => void,
|
|
28
40
|
data: {
|
|
29
|
-
subscription
|
|
41
|
+
subscription?: Subscription;
|
|
30
42
|
summary: { [key: string]: SummaryItem };
|
|
31
43
|
invoices: Invoice[];
|
|
32
|
-
|
|
44
|
+
subscriptionCount?: number;
|
|
45
|
+
detailUrl: string;
|
|
33
46
|
}
|
|
34
47
|
) => React.ReactNode;
|
|
35
48
|
};
|
|
@@ -40,24 +53,41 @@ type SummaryItem = {
|
|
|
40
53
|
method: PaymentMethod;
|
|
41
54
|
};
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
): Promise<{
|
|
46
|
-
subscription: Subscription;
|
|
56
|
+
type OverdueInvoicesResult = {
|
|
57
|
+
subscription?: Subscription;
|
|
47
58
|
summary: { [key: string]: SummaryItem };
|
|
48
59
|
invoices: Invoice[];
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
subscriptionCount?: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const fetchOverdueInvoices = async (params: {
|
|
64
|
+
subscriptionId?: string;
|
|
65
|
+
customerId?: string;
|
|
66
|
+
}): Promise<OverdueInvoicesResult> => {
|
|
67
|
+
if (!params.subscriptionId && !params.customerId) {
|
|
68
|
+
throw new Error('Either subscriptionId or customerId must be provided');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let url;
|
|
72
|
+
if (params.subscriptionId) {
|
|
73
|
+
url = `/api/subscriptions/${params.subscriptionId}/overdue/invoices`;
|
|
74
|
+
} else {
|
|
75
|
+
url = `/api/customers/${params.customerId}/overdue/invoices`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const res = await api.get(url);
|
|
51
79
|
return res.data;
|
|
52
80
|
};
|
|
53
81
|
|
|
54
82
|
function OverdueInvoicePayment({
|
|
55
83
|
subscriptionId,
|
|
84
|
+
customerId,
|
|
56
85
|
mode = 'default',
|
|
57
86
|
dialogProps = {},
|
|
58
87
|
children,
|
|
59
88
|
onPaid = () => {},
|
|
60
|
-
|
|
89
|
+
detailLinkOptions = { enabled: true },
|
|
90
|
+
successToast = true,
|
|
61
91
|
}: Props) {
|
|
62
92
|
const { t } = useLocaleContext();
|
|
63
93
|
const { connect } = usePaymentContext();
|
|
@@ -65,18 +95,33 @@ function OverdueInvoicePayment({
|
|
|
65
95
|
const [payLoading, setPayLoading] = useState(false);
|
|
66
96
|
const [dialogOpen, setDialogOpen] = useState(dialogProps.open || false);
|
|
67
97
|
const [processedCurrencies, setProcessedCurrencies] = useState<{ [key: string]: number }>({});
|
|
98
|
+
const [paymentStatus, setPaymentStatus] = useState<{ [key: string]: 'success' | 'error' | 'idle' }>({});
|
|
99
|
+
|
|
100
|
+
const sourceType = subscriptionId ? 'subscription' : 'customer';
|
|
101
|
+
const sourceId = subscriptionId || customerId;
|
|
102
|
+
|
|
68
103
|
const {
|
|
69
104
|
data = {
|
|
70
|
-
subscription: {} as Subscription,
|
|
71
105
|
summary: {},
|
|
72
106
|
invoices: [],
|
|
73
|
-
},
|
|
107
|
+
} as OverdueInvoicesResult,
|
|
74
108
|
error,
|
|
75
109
|
loading,
|
|
76
110
|
runAsync: refresh,
|
|
77
|
-
} = useRequest(() => fetchOverdueInvoices(subscriptionId)
|
|
111
|
+
} = useRequest(() => fetchOverdueInvoices({ subscriptionId, customerId }), {
|
|
112
|
+
ready: !!subscriptionId || !!customerId,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const detailUrl = useMemo(() => {
|
|
116
|
+
if (subscriptionId) {
|
|
117
|
+
return joinURL(getPrefix(), `/customer/subscription/${subscriptionId}`);
|
|
118
|
+
}
|
|
119
|
+
if (customerId) {
|
|
120
|
+
return joinURL(getPrefix(), '/customer/invoice/past-due');
|
|
121
|
+
}
|
|
122
|
+
return '';
|
|
123
|
+
}, [subscriptionId, customerId]);
|
|
78
124
|
|
|
79
|
-
const subscriptionUrl = joinURL(getPrefix(), `/customer/subscription/${subscriptionId}`);
|
|
80
125
|
const summaryList = useMemo(() => {
|
|
81
126
|
if (!data?.summary) {
|
|
82
127
|
return [];
|
|
@@ -84,26 +129,47 @@ function OverdueInvoicePayment({
|
|
|
84
129
|
return Object.values(data.summary);
|
|
85
130
|
}, [data?.summary]);
|
|
86
131
|
|
|
132
|
+
const debouncedHandleInvoicePaid = debounce(
|
|
133
|
+
async (currencyId: string) => {
|
|
134
|
+
if (successToast) {
|
|
135
|
+
Toast.close();
|
|
136
|
+
Toast.success(t('payment.customer.invoice.paySuccess'));
|
|
137
|
+
}
|
|
138
|
+
setPayLoading(false);
|
|
139
|
+
const res = await refresh();
|
|
140
|
+
if (res.invoices?.length === 0) {
|
|
141
|
+
setDialogOpen(false);
|
|
142
|
+
onPaid(sourceId as string, currencyId, sourceType as 'subscription' | 'customer');
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
1000,
|
|
146
|
+
{
|
|
147
|
+
leading: false,
|
|
148
|
+
trailing: true,
|
|
149
|
+
maxWait: 5000,
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
|
|
87
153
|
const subscription = useSubscription('events');
|
|
88
154
|
useEffect(() => {
|
|
89
155
|
if (subscription) {
|
|
90
156
|
subscription.on('invoice.paid', ({ response }: { response: TInvoiceExpanded }) => {
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
157
|
+
const relevantId = subscriptionId || response.customer_id;
|
|
158
|
+
const uniqueKey = `${relevantId}-${response.currency_id}`;
|
|
159
|
+
|
|
160
|
+
if (
|
|
161
|
+
(subscriptionId && response.subscription_id === subscriptionId) ||
|
|
162
|
+
(customerId && response.customer_id === customerId)
|
|
163
|
+
) {
|
|
164
|
+
if (!processedCurrencies[uniqueKey]) {
|
|
165
|
+
setProcessedCurrencies((prev) => ({ ...prev, [uniqueKey]: 1 }));
|
|
166
|
+
debouncedHandleInvoicePaid(response.currency_id);
|
|
167
|
+
}
|
|
102
168
|
}
|
|
103
169
|
});
|
|
104
170
|
}
|
|
105
171
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
106
|
-
}, [subscription]);
|
|
172
|
+
}, [subscription, subscriptionId, customerId]);
|
|
107
173
|
|
|
108
174
|
const handlePay = (item: SummaryItem) => {
|
|
109
175
|
const { currency, method } = item;
|
|
@@ -116,15 +182,33 @@ function OverdueInvoicePayment({
|
|
|
116
182
|
}
|
|
117
183
|
setSelectCurrencyId(currency.id);
|
|
118
184
|
setPayLoading(true);
|
|
185
|
+
setPaymentStatus((prev) => ({
|
|
186
|
+
...prev,
|
|
187
|
+
[currency.id]: 'idle',
|
|
188
|
+
}));
|
|
189
|
+
|
|
119
190
|
if (['arcblock', 'ethereum', 'base'].includes(method.type)) {
|
|
191
|
+
const extraParams: any = { currencyId: currency.id };
|
|
192
|
+
|
|
193
|
+
if (subscriptionId) {
|
|
194
|
+
extraParams.subscriptionId = subscriptionId;
|
|
195
|
+
} else if (customerId) {
|
|
196
|
+
extraParams.customerId = customerId;
|
|
197
|
+
}
|
|
198
|
+
|
|
120
199
|
connect.open({
|
|
121
200
|
containerEl: undefined as unknown as Element,
|
|
122
201
|
saveConnect: false,
|
|
123
202
|
action: 'collect-batch',
|
|
124
203
|
prefix: joinURL(getPrefix(), '/api/did'),
|
|
125
|
-
extraParams
|
|
204
|
+
extraParams,
|
|
126
205
|
onSuccess: () => {
|
|
127
206
|
connect.close();
|
|
207
|
+
setPayLoading(false);
|
|
208
|
+
setPaymentStatus((prev) => ({
|
|
209
|
+
...prev,
|
|
210
|
+
[currency.id]: 'success',
|
|
211
|
+
}));
|
|
128
212
|
},
|
|
129
213
|
onClose: () => {
|
|
130
214
|
connect.close();
|
|
@@ -132,6 +216,10 @@ function OverdueInvoicePayment({
|
|
|
132
216
|
},
|
|
133
217
|
onError: (err: any) => {
|
|
134
218
|
Toast.error(formatError(err));
|
|
219
|
+
setPaymentStatus((prev) => ({
|
|
220
|
+
...prev,
|
|
221
|
+
[currency.id]: 'error',
|
|
222
|
+
}));
|
|
135
223
|
setPayLoading(false);
|
|
136
224
|
},
|
|
137
225
|
});
|
|
@@ -144,7 +232,10 @@ function OverdueInvoicePayment({
|
|
|
144
232
|
};
|
|
145
233
|
|
|
146
234
|
const handleViewDetailClick = (e: React.MouseEvent) => {
|
|
147
|
-
if (
|
|
235
|
+
if (detailLinkOptions.onClick) {
|
|
236
|
+
e.preventDefault();
|
|
237
|
+
detailLinkOptions.onClick(e);
|
|
238
|
+
} else if (!detailLinkOptions.enabled) {
|
|
148
239
|
e.preventDefault();
|
|
149
240
|
handleClose();
|
|
150
241
|
}
|
|
@@ -154,37 +245,146 @@ function OverdueInvoicePayment({
|
|
|
154
245
|
return null;
|
|
155
246
|
}
|
|
156
247
|
|
|
157
|
-
const
|
|
158
|
-
|
|
248
|
+
const getDetailLinkText = () => {
|
|
249
|
+
if (detailLinkOptions.title) {
|
|
250
|
+
return detailLinkOptions.title;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (subscriptionId) {
|
|
254
|
+
return t('payment.subscription.overdue.view');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return t('payment.customer.pastDue.view');
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const renderPayButton = (
|
|
261
|
+
item: SummaryItem,
|
|
262
|
+
primaryButton = true,
|
|
263
|
+
props: {
|
|
264
|
+
variant?: 'contained' | 'text';
|
|
265
|
+
sx?: SxProps;
|
|
266
|
+
} = {
|
|
267
|
+
variant: 'contained',
|
|
268
|
+
}
|
|
269
|
+
) => {
|
|
270
|
+
const { currency } = item;
|
|
271
|
+
const inProcess = payLoading && selectCurrencyId === currency.id;
|
|
272
|
+
const status = paymentStatus[currency.id] || 'idle';
|
|
273
|
+
|
|
274
|
+
if (status === 'success') {
|
|
275
|
+
return (
|
|
276
|
+
<Button
|
|
277
|
+
// eslint-disable-next-line react/prop-types
|
|
278
|
+
variant={props?.variant || 'contained'}
|
|
279
|
+
size="small"
|
|
280
|
+
{...(primaryButton
|
|
281
|
+
? {}
|
|
282
|
+
: {
|
|
283
|
+
color: 'success',
|
|
284
|
+
startIcon: <CheckCircleIcon />,
|
|
285
|
+
})}>
|
|
286
|
+
{t('payment.subscription.overdue.paid')}
|
|
287
|
+
</Button>
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (status === 'error') {
|
|
292
|
+
return (
|
|
293
|
+
<Button variant="contained" size="small" onClick={() => handlePay(item)} {...props}>
|
|
294
|
+
{t('payment.subscription.overdue.retry')}
|
|
295
|
+
</Button>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
159
299
|
if (item.method.type === 'stripe') {
|
|
160
300
|
return (
|
|
161
|
-
<Button variant="contained" color="primary" onClick={() => window.open(
|
|
301
|
+
<Button variant="contained" color="primary" onClick={() => window.open(detailUrl, '_blank')} {...props}>
|
|
162
302
|
{t('payment.subscription.overdue.viewNow')}
|
|
163
303
|
</Button>
|
|
164
304
|
);
|
|
165
305
|
}
|
|
166
306
|
return (
|
|
167
|
-
<
|
|
168
|
-
|
|
307
|
+
<LoadingButton
|
|
308
|
+
variant="contained"
|
|
309
|
+
size="small"
|
|
310
|
+
disabled={inProcess}
|
|
311
|
+
loading={inProcess}
|
|
312
|
+
onClick={() => handlePay(item)}
|
|
313
|
+
{...props}>
|
|
169
314
|
{t('payment.subscription.overdue.payNow')}
|
|
170
|
-
</
|
|
315
|
+
</LoadingButton>
|
|
171
316
|
);
|
|
172
317
|
};
|
|
173
318
|
|
|
319
|
+
const getMethodText = (method: PaymentMethod) => {
|
|
320
|
+
if (method.name && method.type !== 'arcblock') {
|
|
321
|
+
return ` (${method.name})`;
|
|
322
|
+
}
|
|
323
|
+
return '';
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const getOverdueTitle = () => {
|
|
327
|
+
if (subscriptionId && data.subscription) {
|
|
328
|
+
if (summaryList.length === 1) {
|
|
329
|
+
return t('payment.subscription.overdue.title', {
|
|
330
|
+
name: data.subscription?.description,
|
|
331
|
+
count: data.invoices?.length,
|
|
332
|
+
total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
|
|
333
|
+
symbol: summaryList[0]?.currency?.symbol,
|
|
334
|
+
method: getMethodText(summaryList[0]?.method),
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
return t('payment.subscription.overdue.simpleTitle', {
|
|
338
|
+
name: data.subscription?.description,
|
|
339
|
+
count: data.invoices?.length,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
if (customerId) {
|
|
343
|
+
if (summaryList.length === 1) {
|
|
344
|
+
return t('payment.customer.overdue.title', {
|
|
345
|
+
subscriptionCount: data.subscriptionCount || 0,
|
|
346
|
+
count: data.invoices?.length,
|
|
347
|
+
total: formatAmount(summaryList[0]?.amount, summaryList[0]?.currency?.decimal),
|
|
348
|
+
symbol: summaryList[0]?.currency?.symbol,
|
|
349
|
+
method: getMethodText(summaryList[0]?.method),
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return t('payment.customer.overdue.simpleTitle', {
|
|
353
|
+
subscriptionCount: data.subscriptionCount || 0,
|
|
354
|
+
count: data.invoices?.length,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return '';
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
const getEmptyStateMessage = () => {
|
|
362
|
+
if (subscriptionId && data.subscription) {
|
|
363
|
+
return t('payment.subscription.overdue.empty', {
|
|
364
|
+
name: data.subscription?.description,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
if (customerId) {
|
|
368
|
+
return t('payment.customer.overdue.empty');
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return '';
|
|
372
|
+
};
|
|
373
|
+
|
|
174
374
|
if (mode === 'custom' && children && typeof children === 'function') {
|
|
175
375
|
return (
|
|
176
376
|
<Stack>
|
|
177
377
|
{children(handlePay, {
|
|
178
|
-
subscription: data?.subscription
|
|
179
|
-
subscriptionUrl,
|
|
378
|
+
subscription: data?.subscription,
|
|
180
379
|
summary: data?.summary as { [key: string]: SummaryItem },
|
|
181
380
|
invoices: data?.invoices as Invoice[],
|
|
381
|
+
subscriptionCount: data?.subscriptionCount,
|
|
382
|
+
detailUrl,
|
|
182
383
|
})}
|
|
183
384
|
</Stack>
|
|
184
385
|
);
|
|
185
386
|
}
|
|
186
387
|
|
|
187
|
-
// Default mode
|
|
188
388
|
return (
|
|
189
389
|
<Dialog
|
|
190
390
|
PaperProps={{
|
|
@@ -201,12 +401,7 @@ function OverdueInvoicePayment({
|
|
|
201
401
|
<Stack gap={1}>
|
|
202
402
|
{summaryList.length === 0 && (
|
|
203
403
|
<>
|
|
204
|
-
<Alert severity="success">
|
|
205
|
-
{t('payment.subscription.overdue.empty', {
|
|
206
|
-
// @ts-ignore
|
|
207
|
-
name: data?.subscription?.description,
|
|
208
|
-
})}
|
|
209
|
-
</Alert>
|
|
404
|
+
<Alert severity="success">{getEmptyStateMessage()}</Alert>
|
|
210
405
|
<Stack direction="row" justifyContent="flex-end" mt={2}>
|
|
211
406
|
<Button variant="outlined" color="primary" onClick={handleClose} sx={{ width: 'fit-content' }}>
|
|
212
407
|
{/* @ts-ignore */}
|
|
@@ -218,23 +413,21 @@ function OverdueInvoicePayment({
|
|
|
218
413
|
{summaryList.length === 1 && (
|
|
219
414
|
<>
|
|
220
415
|
<Typography color="text.secondary" variant="body1">
|
|
221
|
-
{
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
{t('payment.subscription.overdue.view')}
|
|
237
|
-
</a>
|
|
416
|
+
{getOverdueTitle()}
|
|
417
|
+
{detailLinkOptions.enabled && (
|
|
418
|
+
<>
|
|
419
|
+
<br />
|
|
420
|
+
{t('payment.subscription.overdue.description')}
|
|
421
|
+
<a
|
|
422
|
+
href={detailUrl}
|
|
423
|
+
target="_blank"
|
|
424
|
+
onClick={handleViewDetailClick}
|
|
425
|
+
rel="noreferrer"
|
|
426
|
+
style={{ color: 'var(--foregrounds-fg-interactive, 0086FF)' }}>
|
|
427
|
+
{getDetailLinkText()}
|
|
428
|
+
</a>
|
|
429
|
+
</>
|
|
430
|
+
)}
|
|
238
431
|
</Typography>
|
|
239
432
|
<Stack direction="row" justifyContent="flex-end" gap={2} mt={2}>
|
|
240
433
|
<Button variant="outlined" color="primary" onClick={handleClose}>
|
|
@@ -249,22 +442,21 @@ function OverdueInvoicePayment({
|
|
|
249
442
|
{summaryList.length > 1 && (
|
|
250
443
|
<>
|
|
251
444
|
<Typography color="text.secondary" variant="body1">
|
|
252
|
-
{
|
|
253
|
-
{
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
</a>
|
|
445
|
+
{getOverdueTitle()}
|
|
446
|
+
{detailLinkOptions.enabled && (
|
|
447
|
+
<>
|
|
448
|
+
<br />
|
|
449
|
+
{t('payment.subscription.overdue.description')}
|
|
450
|
+
<a
|
|
451
|
+
href={detailUrl}
|
|
452
|
+
target="_blank"
|
|
453
|
+
rel="noreferrer"
|
|
454
|
+
onClick={handleViewDetailClick}
|
|
455
|
+
style={{ color: 'var(--foregrounds-fg-interactive, 0086FF)' }}>
|
|
456
|
+
{getDetailLinkText()}
|
|
457
|
+
</a>
|
|
458
|
+
</>
|
|
459
|
+
)}
|
|
268
460
|
</Typography>
|
|
269
461
|
<Typography color="text.secondary" variant="body1">
|
|
270
462
|
{t('payment.subscription.overdue.list')}
|
|
@@ -289,9 +481,10 @@ function OverdueInvoicePayment({
|
|
|
289
481
|
{t('payment.subscription.overdue.total', {
|
|
290
482
|
total: formatAmount(item?.amount, item?.currency?.decimal),
|
|
291
483
|
currency: item?.currency?.symbol,
|
|
484
|
+
method: getMethodText(item?.method),
|
|
292
485
|
})}
|
|
293
486
|
</Typography>
|
|
294
|
-
{renderPayButton(item, {
|
|
487
|
+
{renderPayButton(item, false, {
|
|
295
488
|
variant: 'text',
|
|
296
489
|
sx: {
|
|
297
490
|
color: 'text.link',
|
|
@@ -315,7 +508,10 @@ OverdueInvoicePayment.defaultProps = {
|
|
|
315
508
|
open: true,
|
|
316
509
|
},
|
|
317
510
|
children: null,
|
|
318
|
-
|
|
511
|
+
detailLinkOptions: { enabled: true },
|
|
512
|
+
subscriptionId: undefined,
|
|
513
|
+
customerId: undefined,
|
|
514
|
+
successToast: true,
|
|
319
515
|
};
|
|
320
516
|
|
|
321
517
|
export default OverdueInvoicePayment;
|
package/src/contexts/payment.tsx
CHANGED
|
@@ -36,6 +36,20 @@ export type PaymentContextProps = {
|
|
|
36
36
|
baseUrl?: string;
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
+
const formatData = (data: any) => {
|
|
40
|
+
if (!data) {
|
|
41
|
+
return {
|
|
42
|
+
paymentMethods: [],
|
|
43
|
+
baseCurrency: {},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
...data,
|
|
48
|
+
paymentMethods: data.paymentMethods || [],
|
|
49
|
+
baseCurrency: data.baseCurrency || {},
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
|
|
39
53
|
// @ts-ignore
|
|
40
54
|
const PaymentContext = createContext<PaymentContextType>({ api });
|
|
41
55
|
const { Provider, Consumer } = PaymentContext;
|
|
@@ -95,7 +109,7 @@ function PaymentProvider({ session, connect, children, baseUrl }: PaymentContext
|
|
|
95
109
|
connect,
|
|
96
110
|
prefix,
|
|
97
111
|
livemode: !!livemode,
|
|
98
|
-
settings: data as Settings,
|
|
112
|
+
settings: formatData(data) as Settings,
|
|
99
113
|
getCurrency: (currencyId: string) => getCurrency(currencyId, (data as Settings)?.paymentMethods || []),
|
|
100
114
|
getMethod: (methodId: string) => getMethod(methodId, (data as Settings)?.paymentMethods || []),
|
|
101
115
|
refresh: run,
|