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
@@ -145,7 +145,7 @@ router.get('/', async (req, res) => {
145
145
  ],
146
146
  order: [['created_at', 'DESC']],
147
147
  offset: (page - 1) * pageSize,
148
- include: [{ model: Customer, as: 'customer', attributes: ['id', 'did', 'name'] }],
148
+ include: [{ model: Customer, as: 'customer', attributes: ['id', 'did', 'name', 'metadata'] }],
149
149
  limit: pageSize,
150
150
  });
151
151
 
@@ -143,7 +143,7 @@ router.get('/', authMine, async (req, res) => {
143
143
  { model: PaymentCurrency, as: 'paymentCurrency' },
144
144
  { model: PaymentMethod, as: 'paymentMethod' },
145
145
  // { model: PaymentIntent, as: 'paymentIntent' },
146
- // { model: Subscription, as: 'subscription' },
146
+ { model: Subscription, as: 'subscription', attributes: ['id', 'description'] },
147
147
  { model: Customer, as: 'customer' },
148
148
  ],
149
149
  });
@@ -180,6 +180,52 @@ router.get('/', authMine, async (req, res) => {
180
180
  }
181
181
  });
182
182
 
183
+ const rechargeSchema = createListParamSchema<{
184
+ status?: string;
185
+ customer_id?: string;
186
+ currency_id?: string;
187
+ }>({
188
+ status: Joi.string().empty(''),
189
+ customer_id: Joi.string().empty(''),
190
+ currency_id: Joi.string().empty(''),
191
+ });
192
+
193
+ router.get('/recharge', authMine, async (req, res) => {
194
+ const { page, pageSize, ...query } = await rechargeSchema.validateAsync(req.query, {
195
+ stripUnknown: false,
196
+ allowUnknown: true,
197
+ });
198
+ const where = getWhereFromKvQuery(query.q);
199
+ if (query.customer_id) {
200
+ where.customer_id = query.customer_id;
201
+ }
202
+ if (query.currency_id) {
203
+ where.currency_id = query.currency_id;
204
+ }
205
+
206
+ try {
207
+ const { rows: invoices, count } = await Invoice.findAndCountAll({
208
+ where: {
209
+ billing_reason: 'recharge',
210
+ paid: true,
211
+ ...where,
212
+ },
213
+ offset: (page - 1) * pageSize,
214
+ limit: pageSize,
215
+ order: [['created_at', 'DESC']],
216
+ include: [
217
+ { model: PaymentCurrency, as: 'paymentCurrency' },
218
+ { model: PaymentMethod, as: 'paymentMethod' },
219
+ ],
220
+ });
221
+
222
+ return res.json({ count, list: invoices, paging: { page, pageSize } });
223
+ } catch (err) {
224
+ logger.error(err);
225
+ return res.status(400).json({ error: err.message });
226
+ }
227
+ });
228
+
183
229
  const searchSchema = createListParamSchema<{}>({});
184
230
  router.get('/search', authMine, async (req, res) => {
185
231
  const { page, pageSize, livemode, q, o } = await searchSchema.validateAsync(req.query, {
@@ -1972,9 +1972,6 @@ router.get('/:id/overdue/invoices', authPortal, async (req, res) => {
1972
1972
  });
1973
1973
 
1974
1974
  router.get('/:id/delegation', authPortal, async (req, res) => {
1975
- if (!req.user) {
1976
- return res.status(403).json({ error: 'Unauthorized' });
1977
- }
1978
1975
  try {
1979
1976
  const subscription = (await Subscription.findByPk(req.params.id, {
1980
1977
  include: [
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.18.15
17
+ version: 1.18.17
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -48,6 +48,7 @@ interfaces:
48
48
  - /methods/**
49
49
  - /currencies/**
50
50
  blockUnauthorized: false
51
+ proxyBehavior: service
51
52
  community: ''
52
53
  documentation: ''
53
54
  homepage: ''
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.18.15",
3
+ "version": "1.18.17",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -43,21 +43,21 @@
43
43
  ]
44
44
  },
45
45
  "dependencies": {
46
- "@abtnode/cron": "^1.16.39",
46
+ "@abtnode/cron": "^1.16.40",
47
47
  "@arcblock/did": "^1.19.15",
48
48
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
49
- "@arcblock/did-connect": "^2.12.12",
49
+ "@arcblock/did-connect": "^2.12.25",
50
50
  "@arcblock/did-util": "^1.19.15",
51
51
  "@arcblock/jwt": "^1.19.15",
52
- "@arcblock/ux": "^2.12.12",
52
+ "@arcblock/ux": "^2.12.25",
53
53
  "@arcblock/validator": "^1.19.15",
54
- "@blocklet/js-sdk": "^1.16.39",
55
- "@blocklet/logger": "^1.16.39",
56
- "@blocklet/payment-react": "1.18.15",
57
- "@blocklet/sdk": "^1.16.39",
58
- "@blocklet/ui-react": "^2.12.12",
59
- "@blocklet/uploader": "^0.1.71",
60
- "@blocklet/xss": "^0.1.27",
54
+ "@blocklet/js-sdk": "^1.16.40",
55
+ "@blocklet/logger": "^1.16.40",
56
+ "@blocklet/payment-react": "1.18.17",
57
+ "@blocklet/sdk": "^1.16.40",
58
+ "@blocklet/ui-react": "^2.12.25",
59
+ "@blocklet/uploader": "^0.1.76",
60
+ "@blocklet/xss": "^0.1.28",
61
61
  "@mui/icons-material": "^5.16.6",
62
62
  "@mui/lab": "^5.0.0-alpha.173",
63
63
  "@mui/material": "^5.16.6",
@@ -89,7 +89,7 @@
89
89
  "google-libphonenumber": "^3.2.38",
90
90
  "html2canvas": "^1.4.1",
91
91
  "iframe-resizer-react": "^1.1.1",
92
- "joi": "^17.13.3",
92
+ "joi": "^17.12.2",
93
93
  "json-stable-stringify": "^1.1.1",
94
94
  "jspdf": "^2.5.2",
95
95
  "lodash": "^4.17.21",
@@ -119,9 +119,9 @@
119
119
  "web3": "^4.16.0"
120
120
  },
121
121
  "devDependencies": {
122
- "@abtnode/types": "^1.16.39",
122
+ "@abtnode/types": "^1.16.40",
123
123
  "@arcblock/eslint-config-ts": "^0.3.3",
124
- "@blocklet/payment-types": "1.18.15",
124
+ "@blocklet/payment-types": "1.18.17",
125
125
  "@types/cookie-parser": "^1.4.7",
126
126
  "@types/cors": "^2.8.17",
127
127
  "@types/debug": "^4.1.12",
@@ -151,7 +151,7 @@
151
151
  "vite": "^5.3.5",
152
152
  "vite-node": "^2.0.4",
153
153
  "vite-plugin-babel-import": "^2.0.5",
154
- "vite-plugin-blocklet": "^0.9.23",
154
+ "vite-plugin-blocklet": "^0.9.25",
155
155
  "vite-plugin-node-polyfills": "^0.21.0",
156
156
  "vite-plugin-svgr": "^4.2.0",
157
157
  "vite-tsconfig-paths": "^4.3.2",
@@ -167,5 +167,5 @@
167
167
  "parser": "typescript"
168
168
  }
169
169
  },
170
- "gitHead": "fd58f00bb5d9c625439c03577d9b5f33676823e2"
170
+ "gitHead": "9d43381dd3744506455547260f5e621c6ad88e8f"
171
171
  }
package/src/app.tsx CHANGED
@@ -28,9 +28,10 @@ const CustomerSubscriptionDetail = React.lazy(() => import('./pages/customer/sub
28
28
  const CustomerSubscriptionEmbed = React.lazy(() => import('./pages/customer/subscription/embed'));
29
29
  const CustomerSubscriptionChangePlan = React.lazy(() => import('./pages/customer/subscription/change-plan'));
30
30
  const CustomerSubscriptionChangePayment = React.lazy(() => import('./pages/customer/subscription/change-payment'));
31
- const CustomerRecharge = React.lazy(() => import('./pages/customer/recharge'));
31
+ const CustomerRecharge = React.lazy(() => import('./pages/customer/recharge/subscription'));
32
32
  const CustomerPayoutDetail = React.lazy(() => import('./pages/customer/payout/detail'));
33
33
  const IntegrationsPage = React.lazy(() => import('./pages/integrations'));
34
+ const CustomerBalanceRecharge = React.lazy(() => import('./pages/customer/recharge/account'));
34
35
 
35
36
  // const theme = createTheme({
36
37
  // typography: {
@@ -108,14 +109,21 @@ function App() {
108
109
  </UserLayout>
109
110
  }
110
111
  />
112
+ <Route
113
+ key="customer-balance-recharge"
114
+ path="/customer/recharge/:currencyId"
115
+ element={
116
+ <UserLayout>
117
+ <CustomerBalanceRecharge />
118
+ </UserLayout>
119
+ }
120
+ />
111
121
  <Route key="customer-embed" path="/customer/embed/subscription" element={<CustomerSubscriptionEmbed />} />
112
- ,
113
122
  <Route
114
123
  key="subscription-embed"
115
124
  path="/embed/customer/subscription"
116
125
  element={<CustomerSubscriptionEmbed />}
117
126
  />
118
- ,
119
127
  <Route
120
128
  key="customer-due"
121
129
  path="/customer/invoice/past-due"
@@ -9,15 +9,17 @@ type Props = {
9
9
  size?: number;
10
10
  variant?: LiteralUnion<'square' | 'rounded' | 'circular', string>;
11
11
  sx?: SxProps;
12
+ className?: string;
13
+ logoName?: string;
12
14
  };
13
15
 
14
16
  export default function InfoCard(props: Props) {
15
17
  const dimensions = { width: props.size, height: props.size, ...props.sx };
16
18
  const avatarName = typeof props.name === 'string' ? props.name : props.logo;
17
19
  return (
18
- <Stack direction="row" alignItems="center" spacing={1}>
20
+ <Stack direction="row" alignItems="center" spacing={1} className={`info-card-wrapper ${props.className}`}>
19
21
  {props.logo ? (
20
- <Avatar src={props.logo} alt={avatarName} variant={props.variant as any} sx={dimensions} />
22
+ <Avatar src={props.logo} alt={props.logoName ?? avatarName} variant={props.variant as any} sx={dimensions} />
21
23
  ) : (
22
24
  <Avatar variant={props.variant as any} sx={dimensions}>
23
25
  {avatarName?.slice(0, 1)}
@@ -45,4 +47,6 @@ InfoCard.defaultProps = {
45
47
  size: 40,
46
48
  variant: 'rounded',
47
49
  sx: {},
50
+ className: '',
51
+ logoName: '',
48
52
  };
@@ -45,6 +45,7 @@ export default function InfoRow(props: Props) {
45
45
  {props.showLabel && (
46
46
  <Box
47
47
  flex={sizes[0]}
48
+ className="info-row-label"
48
49
  color="text.primary"
49
50
  fontWeight={500}
50
51
  fontSize={14}
@@ -7,7 +7,6 @@ import {
7
7
  getInvoiceStatusColor,
8
8
  Table,
9
9
  useDefaultPageSize,
10
- getInvoiceDescriptionAndReason,
11
10
  getTxLink,
12
11
  formatToDate,
13
12
  } from '@blocklet/payment-react';
@@ -19,9 +18,12 @@ import { styled } from '@mui/system';
19
18
 
20
19
  const fetchData = (
21
20
  subscriptionId: string,
21
+ currencyId: string,
22
22
  params: Record<string, any> = {}
23
23
  ): Promise<{ list: TInvoiceExpanded[]; count: number }> => {
24
24
  const search = new URLSearchParams();
25
+
26
+ // 处理通用查询参数
25
27
  Object.keys(params).forEach((key) => {
26
28
  let v = params[key];
27
29
  if (key === 'q') {
@@ -32,7 +34,16 @@ const fetchData = (
32
34
  search.set(key, String(v));
33
35
  });
34
36
 
35
- return api.get(`/api/subscriptions/${subscriptionId}/recharge?${search.toString()}`).then((res) => res.data);
37
+ if (subscriptionId) {
38
+ return api.get(`/api/subscriptions/${subscriptionId}/recharge?${search.toString()}`).then((res) => res.data);
39
+ }
40
+
41
+ if (currencyId) {
42
+ search.set('currency_id', currencyId);
43
+ return api.get(`/api/invoices/recharge?${search.toString()}`).then((res) => res.data);
44
+ }
45
+
46
+ return Promise.resolve({ list: [], count: 0 });
36
47
  };
37
48
 
38
49
  type SearchProps = {
@@ -60,6 +71,9 @@ const getListKey = (props: ListProps) => {
60
71
  if (props.subscription_id) {
61
72
  return `subscription-recharge-${props.subscription_id}`;
62
73
  }
74
+ if (props.currency_id) {
75
+ return `currency-recharge-${props.currency_id}`;
76
+ }
63
77
  return 'invoices';
64
78
  };
65
79
 
@@ -80,27 +94,32 @@ export default function RechargeList({ currency_id, subscription_id, features }:
80
94
  const [search, setSearch] = useLocalStorageState<SearchProps>(listKey, {
81
95
  defaultValue: {
82
96
  currency_id,
97
+ subscription_id,
83
98
  pageSize: defaultPageSize,
84
99
  page: 1,
85
100
  },
86
101
  });
87
102
 
88
- const [data, setData] = useState({}) as any;
103
+ const [data, setData] = useState<{ list?: TInvoiceExpanded[]; count?: number }>({});
104
+ const [loading, setLoading] = useState(true);
89
105
 
90
- const refresh = () =>
91
- fetchData(subscription_id!, {
92
- ...search,
93
- }).then((res: any) => {
106
+ const refresh = async () => {
107
+ setLoading(true);
108
+ try {
109
+ const res = await fetchData(subscription_id || '', currency_id || '', {
110
+ ...search,
111
+ });
94
112
  setData(res);
95
- });
113
+ } catch (error) {
114
+ console.error('Failed to fetch recharge records:', error);
115
+ } finally {
116
+ setLoading(false);
117
+ }
118
+ };
96
119
 
97
120
  useEffect(() => {
98
121
  refresh();
99
- }, [search]);
100
-
101
- if (!data.list) {
102
- return <CircularProgress />;
103
- }
122
+ }, [search, currency_id, subscription_id]);
104
123
 
105
124
  const getInvoiceLink = (invoice: TInvoiceExpanded) => {
106
125
  return {
@@ -118,7 +137,9 @@ export default function RechargeList({ currency_id, subscription_id, features }:
118
137
  align: 'right',
119
138
  options: {
120
139
  customBodyRenderLite: (_: string, index: number) => {
121
- const invoice = data?.list[index] as TInvoiceExpanded;
140
+ const invoice = data?.list?.[index] as TInvoiceExpanded;
141
+ if (!invoice) return null;
142
+
122
143
  const link = getInvoiceLink(invoice);
123
144
  return (
124
145
  <a href={link.url} target="_blank" rel="noreferrer">
@@ -136,7 +157,9 @@ export default function RechargeList({ currency_id, subscription_id, features }:
136
157
  name: 'number',
137
158
  options: {
138
159
  customBodyRenderLite: (_: string, index: number) => {
139
- const invoice = data?.list[index] as TInvoiceExpanded;
160
+ const invoice = data?.list?.[index] as TInvoiceExpanded;
161
+ if (!invoice) return null;
162
+
140
163
  const link = getInvoiceLink(invoice);
141
164
  return (
142
165
  <a href={link.url} target="_blank" rel="noreferrer">
@@ -148,10 +171,12 @@ export default function RechargeList({ currency_id, subscription_id, features }:
148
171
  },
149
172
  {
150
173
  label: t('common.rechargeTime'),
151
- name: 'name',
174
+ name: 'created_at',
152
175
  options: {
153
176
  customBodyRenderLite: (_: string, index: number) => {
154
- const invoice = data?.list[index] as TInvoiceExpanded;
177
+ const invoice = data?.list?.[index] as TInvoiceExpanded;
178
+ if (!invoice) return null;
179
+
155
180
  const link = getInvoiceLink(invoice);
156
181
  return (
157
182
  <a href={link.url} target="_blank" rel="noreferrer">
@@ -161,28 +186,14 @@ export default function RechargeList({ currency_id, subscription_id, features }:
161
186
  },
162
187
  },
163
188
  },
164
- {
165
- label: t('common.description'),
166
- name: '',
167
- options: {
168
- sort: false,
169
- customBodyRenderLite: (_: string, index: number) => {
170
- const invoice = data?.list[index] as TInvoiceExpanded;
171
- const link = getInvoiceLink(invoice);
172
- return (
173
- <a href={link.url} target="_blank" rel="noreferrer">
174
- {getInvoiceDescriptionAndReason(invoice, locale)?.description || invoice.id}
175
- </a>
176
- );
177
- },
178
- },
179
- },
180
189
  {
181
190
  label: t('common.status'),
182
191
  name: 'status',
183
192
  options: {
184
193
  customBodyRenderLite: (_: string, index: number) => {
185
- const invoice = data?.list[index] as TInvoiceExpanded;
194
+ const invoice = data?.list?.[index] as TInvoiceExpanded;
195
+ if (!invoice) return null;
196
+
186
197
  const link = getInvoiceLink(invoice);
187
198
  return (
188
199
  <a href={link.url} target="_blank" rel="noreferrer">
@@ -196,34 +207,52 @@ export default function RechargeList({ currency_id, subscription_id, features }:
196
207
 
197
208
  const onTableChange = ({ page, rowsPerPage }: any) => {
198
209
  if (search!.pageSize !== rowsPerPage) {
199
- setSearch((x) => ({ ...x, pageSize: rowsPerPage, page: 1 }));
210
+ setSearch((x) => {
211
+ const newState = { ...x };
212
+ if (newState) {
213
+ newState.pageSize = rowsPerPage;
214
+ newState.page = 1;
215
+ }
216
+ return newState as SearchProps;
217
+ });
200
218
  } else if (search!.page !== page + 1) {
201
- // @ts-ignore
202
- setSearch((x) => ({ ...x, page: page + 1 }));
219
+ setSearch((x) => {
220
+ const newState = { ...x };
221
+ if (newState) {
222
+ newState.page = page + 1;
223
+ }
224
+ return newState as SearchProps;
225
+ });
203
226
  }
204
227
  };
205
228
 
206
229
  return (
207
230
  <InvoiceTableRoot>
208
- <Table
209
- hasRowLink
210
- data={data.list}
211
- durable={`__${listKey}__`}
212
- durableKeys={['searchText']}
213
- columns={columns}
214
- loading={!data.list}
215
- onChange={onTableChange}
216
- mobileTDFlexDirection="row"
217
- options={{
218
- count: data.count,
219
- page: search!.page - 1,
220
- rowsPerPage: search!.pageSize,
221
- }}
222
- toolbar={false}
223
- showMobile={false}
224
- footer={features?.footer}
225
- emptyNodeText={`${t('empty.invoices')}`}
226
- />
231
+ {loading ? (
232
+ <Box sx={{ display: 'flex', justifyContent: 'center', p: 3 }}>
233
+ <CircularProgress />
234
+ </Box>
235
+ ) : (
236
+ <Table
237
+ hasRowLink
238
+ data={data.list || []}
239
+ durable={`__${listKey}__`}
240
+ durableKeys={['searchText']}
241
+ columns={columns}
242
+ loading={loading}
243
+ onChange={onTableChange}
244
+ mobileTDFlexDirection="row"
245
+ options={{
246
+ count: data.count || 0,
247
+ page: search!.page - 1,
248
+ rowsPerPage: search!.pageSize,
249
+ }}
250
+ toolbar={false}
251
+ showMobile={false}
252
+ footer={features?.footer}
253
+ emptyNodeText={`${t('empty.invoices')}`}
254
+ />
255
+ )}
227
256
  </InvoiceTableRoot>
228
257
  );
229
258
  }
@@ -196,6 +196,12 @@ export default function InvoiceTable({ invoice, simple, emptyNodeText }: Props)
196
196
  name: 'amount',
197
197
  width: 200,
198
198
  align: 'right',
199
+ options: {
200
+ customBodyRenderLite: (_: string, index: number) => {
201
+ const item = detail[index] as InvoiceDetailItem;
202
+ return <Typography component="span">{invoice.status === 'void' ? '0' : item.amount}</Typography>;
203
+ },
204
+ },
199
205
  },
200
206
  ...(simple
201
207
  ? []
@@ -237,7 +243,7 @@ export default function InvoiceTable({ invoice, simple, emptyNodeText }: Props)
237
243
  mobileTDFlexDirection="row"
238
244
  emptyNodeText={emptyNodeText || t('payment.customer.invoice.emptyList')}
239
245
  />
240
- {!isEmpty(detail) && (
246
+ {!isEmpty(detail) && invoice.status !== 'void' && (
241
247
  <Stack
242
248
  className="invoice-summary"
243
249
  sx={{
@@ -521,7 +521,7 @@ export function SubscriptionActionsInner({
521
521
  setState({ batchPay: false });
522
522
  onChange?.('batch-pay');
523
523
  }}
524
- inSubscriptionDetail
524
+ detailLinkOptions={{ enabled: false }}
525
525
  dialogProps={{
526
526
  open: state.batchPay,
527
527
  onClose: () => setState({ batchPay: false }),
@@ -36,6 +36,7 @@ type Props = {
36
36
  onlyActive?: boolean;
37
37
  changeActive?: (active: boolean) => void;
38
38
  setStatusState?: (state: boolean) => void;
39
+ setHasSubscriptions?: (state: boolean) => void;
39
40
  } & Omit<StackProps, 'onChange'>;
40
41
 
41
42
  const pageSize = 5;
@@ -48,6 +49,7 @@ export default function CurrentSubscriptions({
48
49
  onlyActive,
49
50
  changeActive = () => {},
50
51
  setStatusState = () => {},
52
+ setHasSubscriptions = () => {},
51
53
  ...rest
52
54
  }: Props) {
53
55
  const { t } = useLocaleContext();
@@ -64,6 +66,9 @@ export default function CurrentSubscriptions({
64
66
  onSuccess(res) {
65
67
  if (res.totalCount > 0 || res.count > 0) {
66
68
  setStatusState(true);
69
+ setHasSubscriptions(true);
70
+ } else {
71
+ setHasSubscriptions(false);
67
72
  }
68
73
  },
69
74
  ...(isMobile
@@ -279,4 +284,5 @@ CurrentSubscriptions.defaultProps = {
279
284
  onlyActive: false,
280
285
  changeActive: null,
281
286
  setStatusState: null,
287
+ setHasSubscriptions: null,
282
288
  };
@@ -796,6 +796,8 @@ export default flat({
796
796
  estimatedDuration: '{duration} {unit} est.',
797
797
  intervals: 'intervals',
798
798
  history: 'Fund History',
799
+ relatedSubscriptions: 'Related Subscriptions',
800
+ rechargeTooltip: 'Click to add funds',
799
801
  },
800
802
  delegation: {
801
803
  title:
@@ -855,6 +857,13 @@ export default flat({
855
857
  viewReference: 'View Reference',
856
858
  title: 'Revenue',
857
859
  },
860
+ pastDue: {
861
+ warning: 'You have due invoices, please pay them to keep your services active',
862
+ invoices: 'Due Invoices',
863
+ payNow: 'Pay Now',
864
+ alert: 'You have due invoices. Please pay them promptly to avoid service interruption.',
865
+ title: 'Settle Due Invoices',
866
+ },
858
867
  },
859
868
  integrations: {
860
869
  basicFeatures: 'Basic Features',
@@ -774,6 +774,8 @@ export default flat({
774
774
  custom: '自定义',
775
775
  intervals: '个周期',
776
776
  history: '充值记录',
777
+ relatedSubscriptions: '关联订阅',
778
+ rechargeTooltip: '点击充值余额',
777
779
  },
778
780
  delegation: {
779
781
  title: '检测到你未完成账户的授权,为了不影响订阅的扣费,请尽快完成授权',
@@ -828,6 +830,13 @@ export default flat({
828
830
  viewReference: '查看打赏原文',
829
831
  title: '收款记录',
830
832
  },
833
+ pastDue: {
834
+ warning: '您有欠费账单,请及时付款以保持服务正常运行',
835
+ invoices: '欠费账单',
836
+ payNow: '立即付款',
837
+ alert: '您有欠费账单需要处理,请及时付款以避免服务中断',
838
+ title: '结清欠费账单',
839
+ },
831
840
  },
832
841
  integrations: {
833
842
  basicFeatures: '基础功能',
@@ -112,6 +112,8 @@ export default function PayoutDetail(props: { id: string }) {
112
112
  return data.destination;
113
113
  };
114
114
 
115
+ const isAnonymousPayer = paymentIntent?.customer?.metadata?.anonymous;
116
+
115
117
  return (
116
118
  <Root direction="column" spacing={2.5} mb={4}>
117
119
  <Box>
@@ -206,23 +208,32 @@ export default function PayoutDetail(props: { id: string }) {
206
208
  : '',
207
209
  48
208
210
  )}
211
+ logoName={paymentIntent?.customer?.metadata?.anonymous ? '' : paymentIntent?.customer?.name}
209
212
  name={
210
213
  <Typography
211
214
  variant="subtitle2"
212
215
  sx={{
213
- cursor: 'pointer',
214
- '&:hover': {
215
- color: 'text.link',
216
- },
216
+ ...(isAnonymousPayer
217
+ ? {}
218
+ : {
219
+ cursor: 'pointer',
220
+ '&:hover': {
221
+ color: 'text.link',
222
+ },
223
+ }),
217
224
  }}
218
225
  onClick={() => {
226
+ if (isAnonymousPayer) {
227
+ return;
228
+ }
219
229
  const url = getCustomerProfileUrl({
220
230
  userDid: paymentIntent?.customer?.did,
221
231
  locale: 'zh',
222
232
  });
223
233
  window.open(url, '_blank');
224
234
  }}>
225
- {paymentIntent?.customer?.name} ({paymentIntent?.customer?.email})
235
+ {paymentIntent?.customer?.name}
236
+ {paymentIntent?.customer?.email ? ` (${paymentIntent?.customer?.email})` : ''}
226
237
  </Typography>
227
238
  }
228
239
  description={