payment-kit 1.21.16 → 1.21.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/src/global.css CHANGED
@@ -79,3 +79,7 @@ th.MuiTableCell-head {
79
79
  .MuiTooltip-tooltip {
80
80
  max-width: 500px;
81
81
  }
82
+
83
+ .date-range-picker .MuiListItem-root {
84
+ cursor: pointer;
85
+ }
@@ -1421,6 +1421,19 @@ export default flat({
1421
1421
  batchPay: {
1422
1422
  button: 'Pay due invoices',
1423
1423
  },
1424
+ payerAddress: 'Payment Address',
1425
+ changePayer: {
1426
+ btn: 'Change',
1427
+ stripe: {
1428
+ linkType: 'Stripe Link',
1429
+ bankAccount: 'Bank Account',
1430
+ },
1431
+ connect: {
1432
+ title: 'Change Payment Address',
1433
+ success: 'Payment Address changed successfully',
1434
+ error: 'Failed to change payment Address',
1435
+ },
1436
+ },
1424
1437
  },
1425
1438
  customer: {
1426
1439
  view: 'View customer',
@@ -1387,6 +1387,19 @@ export default flat({
1387
1387
  batchPay: {
1388
1388
  button: '批量付款',
1389
1389
  },
1390
+ payerAddress: '扣费地址',
1391
+ changePayer: {
1392
+ btn: '变更',
1393
+ stripe: {
1394
+ linkType: 'Stripe Link',
1395
+ bankAccount: '银行账户',
1396
+ },
1397
+ connect: {
1398
+ title: '变更扣费地址',
1399
+ success: '扣费地址变更成功',
1400
+ error: '扣费地址变更失败',
1401
+ },
1402
+ },
1390
1403
  },
1391
1404
  customer: {
1392
1405
  view: '查看客户',
@@ -55,7 +55,7 @@ const fetchData = (
55
55
  relatedCreditGrants?: TCreditGrantExpanded[];
56
56
  }
57
57
  > => {
58
- return api.get(`/api/invoices/${id}`).then((res) => res.data);
58
+ return api.get(`/api/invoices/${id}?sync=true`).then((res: any) => res.data);
59
59
  };
60
60
 
61
61
  const InfoDirection = 'column';
@@ -92,7 +92,7 @@ export default function InvoiceDetail(props: { id: string }) {
92
92
  const createUpdater = (key: string) => async (updates: TInvoice) => {
93
93
  try {
94
94
  setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
95
- await api.put(`/api/invoices/${props.id}`, updates).then((res) => res.data);
95
+ await api.put(`/api/invoices/${props.id}`, updates).then((res: any) => res.data);
96
96
  Toast.success(t('common.saved'));
97
97
  runAsync();
98
98
  } catch (err) {
@@ -120,6 +120,8 @@ export default function InvoiceDetail(props: { id: string }) {
120
120
  };
121
121
  // @ts-ignore
122
122
  const isDonation = data?.checkoutSession?.submit_type === 'donate';
123
+ const showDownloadButton =
124
+ ['open', 'paid', 'uncollectible'].includes(data.status) && !isDonation && !isEmpty(data.lines);
123
125
  return (
124
126
  <Root direction="column" spacing={2.5} sx={{ mb: 4 }}>
125
127
  <Box>
@@ -144,7 +146,7 @@ export default function InvoiceDetail(props: { id: string }) {
144
146
  </Typography>
145
147
  </Stack>
146
148
  <Box style={{ display: 'flex', gap: '10px' }}>
147
- <Download data={data} />
149
+ {showDownloadButton && <Download data={data} />}
148
150
  <InvoiceActions data={data} onChange={runAsync} variant="normal" />
149
151
  </Box>
150
152
  </Stack>
@@ -35,6 +35,7 @@ import { goBackOrFallback } from '../../../../libs/util';
35
35
  import InfoRowGroup from '../../../../components/info-row-group';
36
36
  import VendorServiceList from '../../../../components/subscription/vendor-service-list';
37
37
  import { useSessionContext } from '../../../../contexts/session';
38
+ import PaymentMethodInfo from '../../../../components/subscription/payment-method-info';
38
39
 
39
40
  const fetchData = (id: string): Promise<TSubscriptionExpanded> => {
40
41
  return api.get(`/api/subscriptions/${id}`).then((res) => res.data);
@@ -285,6 +286,21 @@ export default function SubscriptionDetail(props: { id: string }) {
285
286
  />
286
287
  }
287
288
  />
289
+
290
+ {(data as any).paymentMethodDetails && (
291
+ <InfoRow
292
+ label={t('admin.subscription.payerAddress')}
293
+ value={
294
+ <PaymentMethodInfo
295
+ subscriptionId={data.id}
296
+ customer={data.customer}
297
+ paymentMethodDetails={(data as any).paymentMethodDetails}
298
+ editable={false}
299
+ paymentMethodType={data.paymentMethod?.type}
300
+ />
301
+ }
302
+ />
303
+ )}
288
304
  {data.payment_details && hasDelegateTxHash(data.payment_details, data.paymentMethod) && (
289
305
  <InfoRow
290
306
  label={t('common.delegateTxHash')}
@@ -3,7 +3,7 @@ import DID from '@arcblock/ux/lib/DID';
3
3
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
4
4
  import { api, formatBNStr, formatToDate, usePaymentContext } from '@blocklet/payment-react';
5
5
  import { BN } from '@ocap/util';
6
- import type { GroupedBN, TPaymentMethod, TPaymentStat } from '@blocklet/payment-types';
6
+ import type { GroupedBN, TPaymentCurrency, TPaymentMethod, TPaymentStat } from '@blocklet/payment-types';
7
7
  import {
8
8
  Avatar,
9
9
  Box,
@@ -175,8 +175,8 @@ export default function Overview() {
175
175
 
176
176
  const currencies: TCurrencyMap = useMemo(() => {
177
177
  const map: TCurrencyMap = {};
178
- (settings.paymentMethods || []).forEach((method) => {
179
- (method.payment_currencies || []).forEach((currency) => {
178
+ (settings.paymentMethods || []).forEach((method: { payment_currencies: TPaymentCurrency[] }) => {
179
+ (method.payment_currencies || []).forEach((currency: TPaymentCurrency) => {
180
180
  if (!map[currency.id]) {
181
181
  map[currency.id] = {
182
182
  ...currency,
@@ -1105,10 +1105,12 @@ export default function Overview() {
1105
1105
  sx={{
1106
1106
  p: 1.5,
1107
1107
  flex: '1 1 calc(50% - 8px)',
1108
+ minWidth: 0,
1108
1109
  borderRadius: 1,
1109
1110
  backgroundColor: theme.mode === 'dark' ? 'grey.100' : 'grey.50',
1110
1111
  border: 'none',
1111
1112
  boxShadow: 'none',
1113
+ containerType: 'inline-size',
1112
1114
  }}>
1113
1115
  <Box>
1114
1116
  <Typography
@@ -1124,13 +1126,11 @@ export default function Overview() {
1124
1126
  sx={{
1125
1127
  gap: 1.5,
1126
1128
  flexWrap: 'wrap',
1127
- flexDirection: {
1128
- xs: 'column',
1129
- md: 'row',
1130
- },
1131
- alignItems: {
1132
- xs: 'flex-start',
1133
- md: 'baseline',
1129
+ flexDirection: 'row',
1130
+ alignItems: 'baseline',
1131
+ '@container (max-width: 220px)': {
1132
+ flexDirection: 'column',
1133
+ alignItems: 'flex-start',
1134
1134
  },
1135
1135
  }}>
1136
1136
  <Typography
@@ -1239,14 +1239,14 @@ export default function Overview() {
1239
1239
 
1240
1240
  <Grid
1241
1241
  container
1242
+ spacing={{ xs: 3, lg: 6 }}
1242
1243
  sx={{
1243
- gap: { xs: 3, md: 6 },
1244
1244
  mb: 4,
1245
1245
  }}>
1246
1246
  <Grid
1247
1247
  size={{
1248
1248
  xs: 12,
1249
- md: 8,
1249
+ lg: 8,
1250
1250
  }}>
1251
1251
  {renderFinancialSection()}
1252
1252
  </Grid>
@@ -1254,7 +1254,7 @@ export default function Overview() {
1254
1254
  <Grid
1255
1255
  size={{
1256
1256
  xs: 12,
1257
- md: 3,
1257
+ lg: 4,
1258
1258
  }}>
1259
1259
  {renderBusinessMonitoringSection()}
1260
1260
  </Grid>
@@ -1265,7 +1265,7 @@ export default function Overview() {
1265
1265
  open={open}
1266
1266
  anchorEl={state.anchorEl}
1267
1267
  onClose={() => setState({ anchorEl: null })}
1268
- anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}>
1268
+ anchorOrigin={{ vertical: 'bottom', horizontal: 'left' }}>
1269
1269
  <DateRangePicker
1270
1270
  open
1271
1271
  toggle={onTogglePicker as any}
@@ -15,13 +15,14 @@ import {
15
15
  usePaymentContext,
16
16
  PaymentBeneficiaries,
17
17
  useMobile,
18
+ StripePaymentAction,
18
19
  } from '@blocklet/payment-react';
19
20
  import type { TCheckoutSession, TCreditGrantExpanded, TInvoiceExpanded, TPaymentLink } from '@blocklet/payment-types';
20
21
  import { ArrowBackOutlined } from '@mui/icons-material';
21
22
  import { Alert, Box, Button, CircularProgress, Divider, Stack, Tooltip, Typography, useTheme } from '@mui/material';
22
23
  import { styled } from '@mui/system';
23
24
  import { useRequest, useSetState } from 'ahooks';
24
- import { useEffect } from 'react';
25
+ import { useEffect, useRef } from 'react';
25
26
  import { Link, useParams, useSearchParams } from 'react-router-dom';
26
27
 
27
28
  import { joinURL } from 'ufo';
@@ -48,7 +49,7 @@ const fetchData = (
48
49
  relatedCreditGrants?: TCreditGrantExpanded[];
49
50
  }
50
51
  > => {
51
- return api.get(`/api/invoices/${id}`).then((res) => res.data);
52
+ return api.get(`/api/invoices/${id}`).then((res: any) => res.data);
52
53
  };
53
54
 
54
55
  export default function CustomerInvoiceDetail() {
@@ -64,16 +65,19 @@ export default function CustomerInvoiceDetail() {
64
65
  });
65
66
 
66
67
  const { loading, error, data, runAsync } = useRequest(() => fetchData(params.id as string));
68
+
67
69
  const action = searchParams.get('action');
68
70
  const { session } = useSessionContext();
69
71
  const isDonation = data?.checkoutSession?.submit_type === 'donate';
72
+ const payHandlerRef = useRef<(() => void) | null>(null);
70
73
 
71
- const onPay = () => {
74
+ const handleExternalPayment = () => {
72
75
  setState({ paying: true });
73
76
  connect.open({
74
77
  action: 'collect',
75
78
  saveConnect: false,
76
79
  locale: locale as 'en' | 'zh',
80
+ extraParams: { invoiceId: params.id, action },
77
81
  messages: {
78
82
  scan: '',
79
83
  title: t(`payment.customer.invoice.${action || 'pay'}`),
@@ -81,9 +85,9 @@ export default function CustomerInvoiceDetail() {
81
85
  error: t(`payment.customer.invoice.${action || 'pay'}Error`),
82
86
  confirm: '',
83
87
  } as any,
84
- extraParams: { invoiceId: params.id, action },
85
88
  onSuccess: async () => {
86
89
  connect.close();
90
+ setState({ paying: false });
87
91
  await runAsync();
88
92
  },
89
93
  onClose: () => {
@@ -110,7 +114,7 @@ export default function CustomerInvoiceDetail() {
110
114
  ['pay', 'renew'].includes(action as string) &&
111
115
  ['open', 'uncollectible'].includes(data?.status as string)
112
116
  ) {
113
- onPay();
117
+ handleExternalPayment();
114
118
  }
115
119
  // eslint-disable-next-line react-hooks/exhaustive-deps
116
120
  }, [error]);
@@ -133,6 +137,9 @@ export default function CustomerInvoiceDetail() {
133
137
 
134
138
  const hidePayButton = data.billing_reason === 'overdraft_protection';
135
139
  const paymentDetails = data.paymentIntent?.payment_details || data.metadata?.payment_details;
140
+
141
+ const showDownloadButton =
142
+ ['open', 'paid', 'uncollectible'].includes(data.status) && !isDonation && !isEmpty(data.lines);
136
143
  return (
137
144
  <InvoiceDetailRoot direction="column" spacing={3}>
138
145
  <Stack
@@ -161,7 +168,7 @@ export default function CustomerInvoiceDetail() {
161
168
  justifyContent: 'flex-end',
162
169
  alignItems: 'center',
163
170
  }}>
164
- {['open', 'paid', 'uncollectible'].includes(data.status) && !isDonation && <Download data={data} />}
171
+ {showDownloadButton && <Download data={data} />}
165
172
  {data?.paymentLink?.donation_settings?.reference && (
166
173
  <Button
167
174
  variant="outlined"
@@ -171,17 +178,52 @@ export default function CustomerInvoiceDetail() {
171
178
  {t('customer.payout.viewReference')}
172
179
  </Button>
173
180
  )}
174
- {['open', 'uncollectible'].includes(data.status) && !hidePayButton && (
175
- <Button
176
- variant="outlined"
177
- color="primary"
178
- size="small"
179
- sx={{ borderColor: 'divider' }}
180
- disabled={state.paying}
181
- onClick={onPay}>
182
- {t('payment.customer.invoice.pay')}
183
- </Button>
184
- )}
181
+ {['open', 'uncollectible'].includes(data.status) &&
182
+ !hidePayButton &&
183
+ (data.paymentMethod?.type === 'stripe' ? (
184
+ <StripePaymentAction
185
+ invoice={data}
186
+ paymentMethod={data.paymentMethod}
187
+ onSuccess={async () => {
188
+ Toast.close();
189
+ setState({ paying: false });
190
+ await runAsync();
191
+ }}
192
+ onError={() => {
193
+ setState({ paying: false });
194
+ }}
195
+ onClose={() => {
196
+ setState({ paying: false });
197
+ }}>
198
+ {(onPay: () => void, paying: boolean) => {
199
+ payHandlerRef.current = onPay;
200
+ return (
201
+ <Button
202
+ variant="outlined"
203
+ color="primary"
204
+ size="small"
205
+ sx={{ borderColor: 'divider' }}
206
+ disabled={state.paying || paying}
207
+ onClick={() => {
208
+ setState({ paying: true });
209
+ onPay();
210
+ }}>
211
+ {t('payment.customer.invoice.pay')}
212
+ </Button>
213
+ );
214
+ }}
215
+ </StripePaymentAction>
216
+ ) : (
217
+ <Button
218
+ variant="outlined"
219
+ color="primary"
220
+ size="small"
221
+ sx={{ borderColor: 'divider' }}
222
+ disabled={state.paying}
223
+ onClick={handleExternalPayment}>
224
+ {t('payment.customer.invoice.pay')}
225
+ </Button>
226
+ ))}
185
227
  </Stack>
186
228
  </Stack>
187
229
  <Box
@@ -52,6 +52,7 @@ import { useSessionContext } from '../../../contexts/session';
52
52
  import { usePendingAmountForSubscription, useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
53
53
  import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
54
54
  import { canChangePaymentMethod } from '../../../libs/util';
55
+ import PaymentMethodInfo from '../../../components/subscription/payment-method-info';
55
56
 
56
57
  const fetchData = (id: string | undefined): Promise<TSubscriptionExpanded> => {
57
58
  return api.get(`/api/subscriptions/${id}`).then((res) => res.data);
@@ -622,7 +623,7 @@ export default function CustomerSubscriptionDetail() {
622
623
  {canChangePaymentMethod(data) && (
623
624
  <Button
624
625
  variant="text"
625
- sx={{ color: 'text.link', fontSize: 14 }}
626
+ sx={{ color: 'text.link' }}
626
627
  size="small"
627
628
  onClick={async () => {
628
629
  // only check unpaid invoices when overdraft protection is enabled
@@ -640,6 +641,24 @@ export default function CustomerSubscriptionDetail() {
640
641
  </Stack>
641
642
  }
642
643
  />
644
+ {(data as any).paymentMethodDetails && (
645
+ <InfoRow
646
+ label={t('admin.subscription.payerAddress')}
647
+ value={
648
+ <PaymentMethodInfo
649
+ subscriptionId={id}
650
+ customer={data.customer}
651
+ paymentMethodDetails={(data as any).paymentMethodDetails}
652
+ editable={['active', 'trialing', 'past_due'].includes(data.status)}
653
+ onUpdate={() => {
654
+ refresh();
655
+ checkUnpaidInvoices();
656
+ }}
657
+ paymentMethodType={data.paymentMethod?.type}
658
+ />
659
+ }
660
+ />
661
+ )}
643
662
 
644
663
  {data.payment_details && hasDelegateTxHash(data.payment_details, data.paymentMethod) && (
645
664
  <InfoRow