payment-kit 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.
Files changed (37) hide show
  1. package/api/src/index.ts +2 -0
  2. package/api/src/libs/invoice.ts +5 -3
  3. package/api/src/libs/notification/template/customer-reward-succeeded.ts +32 -14
  4. package/api/src/libs/session.ts +9 -1
  5. package/api/src/libs/util.ts +12 -4
  6. package/api/src/routes/checkout-sessions.ts +286 -120
  7. package/api/src/routes/connect/change-payment.ts +9 -1
  8. package/api/src/routes/connect/change-plan.ts +9 -1
  9. package/api/src/routes/connect/collect-batch.ts +7 -5
  10. package/api/src/routes/connect/pay.ts +1 -1
  11. package/api/src/routes/connect/recharge-account.ts +124 -0
  12. package/api/src/routes/connect/setup.ts +8 -1
  13. package/api/src/routes/connect/shared.ts +175 -54
  14. package/api/src/routes/connect/subscribe.ts +11 -1
  15. package/api/src/routes/customers.ts +150 -7
  16. package/api/src/routes/donations.ts +1 -1
  17. package/api/src/routes/invoices.ts +47 -1
  18. package/api/src/routes/subscriptions.ts +0 -3
  19. package/blocklet.yml +2 -1
  20. package/package.json +16 -16
  21. package/src/app.tsx +11 -3
  22. package/src/components/info-card.tsx +6 -2
  23. package/src/components/info-row.tsx +1 -0
  24. package/src/components/invoice/recharge.tsx +85 -56
  25. package/src/components/invoice/table.tsx +7 -1
  26. package/src/components/subscription/portal/actions.tsx +1 -1
  27. package/src/components/subscription/portal/list.tsx +6 -0
  28. package/src/locales/en.tsx +9 -0
  29. package/src/locales/zh.tsx +9 -0
  30. package/src/pages/admin/payments/payouts/detail.tsx +16 -5
  31. package/src/pages/customer/index.tsx +226 -284
  32. package/src/pages/customer/invoice/detail.tsx +24 -16
  33. package/src/pages/customer/invoice/past-due.tsx +46 -23
  34. package/src/pages/customer/payout/detail.tsx +16 -5
  35. package/src/pages/customer/recharge/account.tsx +513 -0
  36. package/src/pages/customer/{recharge.tsx → recharge/subscription.tsx} +22 -19
  37. package/src/pages/customer/subscription/embed.tsx +16 -1
@@ -17,7 +17,7 @@ import {
17
17
  } from '@blocklet/payment-react';
18
18
  import type { TCheckoutSession, TInvoiceExpanded, TPaymentLink } from '@blocklet/payment-types';
19
19
  import { ArrowBackOutlined } from '@mui/icons-material';
20
- import { Alert, Box, Button, CircularProgress, Divider, Stack, Typography } from '@mui/material';
20
+ import { Alert, Box, Button, CircularProgress, Divider, Stack, Tooltip, Typography } from '@mui/material';
21
21
  import { styled } from '@mui/system';
22
22
  import { useRequest, useSetState } from 'ahooks';
23
23
  import { useEffect } from 'react';
@@ -204,9 +204,31 @@ export default function CustomerInvoiceDetail() {
204
204
  }}>
205
205
  <InfoMetric
206
206
  label={t('common.status')}
207
- value={<Status label={data.status} color={getInvoiceStatusColor(data.status)} />}
207
+ value={
208
+ <Tooltip
209
+ title={data.status === 'void' ? t('payment.customer.invoice.noPaymentRequired') : ''}
210
+ arrow
211
+ placement="top">
212
+ <span>
213
+ <Status label={data.status} color={getInvoiceStatusColor(data.status)} />
214
+ </span>
215
+ </Tooltip>
216
+ }
208
217
  divider
209
218
  />
219
+ {data.subscription && (
220
+ <InfoMetric
221
+ label={t('admin.subscription.name')}
222
+ value={
223
+ <Link to={`/customer/subscription/${data.subscription.id}`}>
224
+ <Typography variant="body1" color="text.link">
225
+ {data.subscription.description || data.subscription.id}
226
+ </Typography>
227
+ </Link>
228
+ }
229
+ divider
230
+ />
231
+ )}
210
232
  <InfoMetric label={t('common.createdAt')} value={formatTime(data.created_at)} divider />
211
233
  {data.period_start > 0 && data.period_end > 0 && (
212
234
  <InfoMetric
@@ -301,20 +323,6 @@ export default function CustomerInvoiceDetail() {
301
323
  alignItems={InfoAlignItems}
302
324
  />
303
325
  )}
304
- {data.subscription && (
305
- <InfoRow
306
- label={t('admin.subscription.name')}
307
- value={
308
- <Link to={`/customer/subscription/${data.subscription.id}`}>
309
- <Typography variant="body1" color="text.link">
310
- {data.subscription.description || data.subscription.id}
311
- </Typography>
312
- </Link>
313
- }
314
- direction={InfoDirection}
315
- alignItems={InfoAlignItems}
316
- />
317
- )}
318
326
  {data?.relatedInvoice && (
319
327
  <InfoRow
320
328
  label={t('customer.invoice.relatedInvoice')}
@@ -6,6 +6,7 @@ import {
6
6
  getPrefix,
7
7
  getQueryParams,
8
8
  usePaymentContext,
9
+ OverdueInvoicePayment,
9
10
  } from '@blocklet/payment-react';
10
11
  import type { TCustomerExpanded } from '@blocklet/payment-types';
11
12
  import { ArrowBackOutlined } from '@mui/icons-material';
@@ -29,9 +30,10 @@ const fetchData = (): Promise<TCustomerExpanded> => {
29
30
  export default function CustomerInvoicePastDue() {
30
31
  const { t } = useLocaleContext();
31
32
  const { events } = useSessionContext();
32
- const { connect } = usePaymentContext();
33
+ const { connect, session } = usePaymentContext();
33
34
  const [params] = useSearchParams();
34
- const [alertVisible, setAlertVisible] = useState(false);
35
+ const [hasUnpaidInvoices, setHasUnpaidInvoices] = useState(false);
36
+ const [showOverduePayment, setShowOverduePayment] = useState(false);
35
37
 
36
38
  const { loading, error, data, runAsync } = useRequest(fetchData);
37
39
 
@@ -60,27 +62,31 @@ export default function CustomerInvoicePastDue() {
60
62
  const subscriptionId = params.get('subscription') || '';
61
63
  const currencyId = params.get('currency') || '';
62
64
  const handleBatchPay = () => {
63
- connect.open({
64
- containerEl: undefined as unknown as Element,
65
- saveConnect: false,
66
- action: 'collect-batch',
67
- prefix: joinURL(getPrefix(), '/api/did'),
68
- extraParams: { subscriptionId, currencyId },
69
- onSuccess: () => {
70
- connect.close();
71
- },
72
- onClose: () => {
73
- connect.close();
74
- },
75
- onError: (err: any) => {
76
- Toast.error(formatError(err));
77
- },
78
- });
65
+ if (subscriptionId && currencyId) {
66
+ connect.open({
67
+ containerEl: undefined as unknown as Element,
68
+ saveConnect: false,
69
+ action: 'collect-batch',
70
+ prefix: joinURL(getPrefix(), '/api/did'),
71
+ extraParams: { subscriptionId, currencyId },
72
+ onSuccess: () => {
73
+ connect.close();
74
+ },
75
+ onClose: () => {
76
+ connect.close();
77
+ },
78
+ onError: (err: any) => {
79
+ Toast.error(formatError(err));
80
+ },
81
+ });
82
+ } else {
83
+ setShowOverduePayment(true);
84
+ }
79
85
  };
80
86
 
81
87
  const onTableDataChange = (tableData: any, prevData: any) => {
82
88
  if (isEmpty(tableData) || tableData?.count === 0) {
83
- setAlertVisible(false);
89
+ setHasUnpaidInvoices(false);
84
90
  if (prevData?.count > 0) {
85
91
  // paid all invoices
86
92
  const referer = getQueryParams(window.location.href)?.referer;
@@ -92,7 +98,7 @@ export default function CustomerInvoicePastDue() {
92
98
  }
93
99
  return;
94
100
  }
95
- setAlertVisible(true);
101
+ setHasUnpaidInvoices(true);
96
102
  };
97
103
 
98
104
  return (
@@ -110,12 +116,28 @@ export default function CustomerInvoicePastDue() {
110
116
  </Stack>
111
117
  </Stack>
112
118
  <Root direction="column" spacing={3}>
113
- {alertVisible && <Alert severity="error">{t('payment.customer.pastDue.warning')}</Alert>}
119
+ {hasUnpaidInvoices && <Alert severity="error">{t('payment.customer.pastDue.warning')}</Alert>}
120
+ {showOverduePayment && (
121
+ <OverdueInvoicePayment
122
+ customerId={session.user.did}
123
+ onPaid={() => {
124
+ setShowOverduePayment(false);
125
+ }}
126
+ successToast={false}
127
+ dialogProps={{
128
+ open: showOverduePayment,
129
+ onClose: () => setShowOverduePayment(false),
130
+ }}
131
+ detailLinkOptions={{
132
+ enabled: false,
133
+ }}
134
+ />
135
+ )}
114
136
 
115
137
  <Box className="section">
116
138
  <SectionHeader title={t('payment.customer.pastDue.invoices')} mb={0}>
117
- {subscriptionId && currencyId && (
118
- <Button size="small" variant="contained" color="info" onClick={handleBatchPay}>
139
+ {hasUnpaidInvoices && (
140
+ <Button size="small" variant="contained" color="primary" onClick={handleBatchPay}>
119
141
  {t('admin.subscription.batchPay.button')}
120
142
  </Button>
121
143
  )}
@@ -131,6 +153,7 @@ export default function CustomerInvoicePastDue() {
131
153
  action="pay"
132
154
  type="table"
133
155
  onTableDataChange={onTableDataChange}
156
+ relatedSubscription
134
157
  />
135
158
  </Box>
136
159
  </Box>
@@ -59,6 +59,8 @@ export default function PayoutDetail() {
59
59
  const currency = data.paymentCurrency;
60
60
  const total = [formatBNStr(data?.amount, currency.decimal), currency.symbol].join(' ');
61
61
  const { paymentIntent, paymentLink } = data || {};
62
+
63
+ const isAnonymousPayer = paymentIntent?.customer?.metadata?.anonymous;
62
64
  return (
63
65
  <Root direction="column" spacing={2.5} mb={4}>
64
66
  <Box>
@@ -160,23 +162,32 @@ export default function PayoutDetail() {
160
162
  : '',
161
163
  48
162
164
  )}
165
+ logoName={paymentIntent?.customer?.metadata?.anonymous ? '' : paymentIntent?.customer?.name}
163
166
  name={
164
167
  <Typography
165
168
  variant="subtitle2"
166
169
  sx={{
167
- cursor: 'pointer',
168
- '&:hover': {
169
- color: 'text.link',
170
- },
170
+ ...(isAnonymousPayer
171
+ ? {}
172
+ : {
173
+ cursor: 'pointer',
174
+ '&:hover': {
175
+ color: 'text.link',
176
+ },
177
+ }),
171
178
  }}
172
179
  onClick={() => {
180
+ if (isAnonymousPayer) {
181
+ return;
182
+ }
173
183
  const url = getCustomerProfileUrl({
174
184
  userDid: paymentIntent?.customer?.did,
175
185
  locale: 'zh',
176
186
  });
177
187
  window.open(url, '_blank');
178
188
  }}>
179
- {paymentIntent?.customer?.name} ({paymentIntent?.customer?.email})
189
+ {paymentIntent?.customer?.name}
190
+ {paymentIntent?.customer?.email ? ` (${paymentIntent?.customer?.email})` : ''}
180
191
  </Typography>
181
192
  }
182
193
  description={