payment-kit 1.18.56 → 1.19.1

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 (214) hide show
  1. package/.eslintrc.js +6 -0
  2. package/api/src/crons/index.ts +8 -0
  3. package/api/src/index.ts +4 -0
  4. package/api/src/libs/credit-grant.ts +146 -0
  5. package/api/src/libs/env.ts +1 -0
  6. package/api/src/libs/invoice.ts +4 -3
  7. package/api/src/libs/notification/template/base.ts +388 -2
  8. package/api/src/libs/notification/template/customer-credit-grant-granted.ts +149 -0
  9. package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +151 -0
  10. package/api/src/libs/notification/template/customer-credit-insufficient.ts +254 -0
  11. package/api/src/libs/notification/template/subscription-canceled.ts +193 -202
  12. package/api/src/libs/notification/template/subscription-refund-succeeded.ts +215 -237
  13. package/api/src/libs/notification/template/subscription-renewed.ts +130 -200
  14. package/api/src/libs/notification/template/subscription-succeeded.ts +100 -202
  15. package/api/src/libs/notification/template/subscription-trial-start.ts +142 -188
  16. package/api/src/libs/notification/template/subscription-trial-will-end.ts +146 -174
  17. package/api/src/libs/notification/template/subscription-upgraded.ts +96 -192
  18. package/api/src/libs/notification/template/subscription-will-canceled.ts +94 -135
  19. package/api/src/libs/notification/template/subscription-will-renew.ts +220 -245
  20. package/api/src/libs/payment.ts +69 -0
  21. package/api/src/libs/queue/index.ts +3 -2
  22. package/api/src/libs/session.ts +8 -0
  23. package/api/src/libs/subscription.ts +74 -3
  24. package/api/src/libs/ws.ts +23 -1
  25. package/api/src/locales/en.ts +33 -0
  26. package/api/src/locales/zh.ts +31 -0
  27. package/api/src/queues/credit-consume.ts +715 -0
  28. package/api/src/queues/credit-grant.ts +572 -0
  29. package/api/src/queues/notification.ts +173 -128
  30. package/api/src/queues/payment.ts +210 -122
  31. package/api/src/queues/subscription.ts +179 -0
  32. package/api/src/routes/checkout-sessions.ts +157 -9
  33. package/api/src/routes/connect/shared.ts +3 -2
  34. package/api/src/routes/credit-grants.ts +241 -0
  35. package/api/src/routes/credit-transactions.ts +208 -0
  36. package/api/src/routes/index.ts +8 -0
  37. package/api/src/routes/meter-events.ts +347 -0
  38. package/api/src/routes/meters.ts +219 -0
  39. package/api/src/routes/payment-currencies.ts +14 -2
  40. package/api/src/routes/payment-links.ts +1 -1
  41. package/api/src/routes/payment-methods.ts +14 -2
  42. package/api/src/routes/prices.ts +43 -0
  43. package/api/src/routes/pricing-table.ts +13 -7
  44. package/api/src/routes/products.ts +63 -4
  45. package/api/src/routes/settings.ts +1 -1
  46. package/api/src/routes/subscriptions.ts +4 -0
  47. package/api/src/store/migrations/20250610-billing-credit.ts +43 -0
  48. package/api/src/store/models/credit-grant.ts +486 -0
  49. package/api/src/store/models/credit-transaction.ts +268 -0
  50. package/api/src/store/models/customer.ts +8 -0
  51. package/api/src/store/models/index.ts +52 -1
  52. package/api/src/store/models/meter-event.ts +423 -0
  53. package/api/src/store/models/meter.ts +176 -0
  54. package/api/src/store/models/payment-currency.ts +66 -14
  55. package/api/src/store/models/price.ts +6 -0
  56. package/api/src/store/models/product.ts +2 -2
  57. package/api/src/store/models/subscription.ts +24 -0
  58. package/api/src/store/models/types.ts +28 -2
  59. package/api/tests/libs/subscription.spec.ts +53 -0
  60. package/blocklet.yml +9 -1
  61. package/package.json +57 -58
  62. package/scripts/sdk.js +233 -1
  63. package/src/app.tsx +10 -0
  64. package/src/components/actions.tsx +22 -9
  65. package/src/components/balance-list.tsx +40 -12
  66. package/src/components/collapse.tsx +33 -15
  67. package/src/components/copyable.tsx +8 -7
  68. package/src/components/currency.tsx +15 -7
  69. package/src/components/customer/actions.tsx +1 -5
  70. package/src/components/customer/credit-grant-item-list.tsx +99 -0
  71. package/src/components/customer/credit-overview.tsx +233 -0
  72. package/src/components/customer/form.tsx +7 -2
  73. package/src/components/customer/link.tsx +4 -12
  74. package/src/components/customer/notification-preference.tsx +18 -9
  75. package/src/components/customer/overdraft-protection.tsx +112 -41
  76. package/src/components/drawer-form.tsx +42 -18
  77. package/src/components/error.tsx +1 -5
  78. package/src/components/event/list.tsx +9 -10
  79. package/src/components/filter-toolbar.tsx +20 -19
  80. package/src/components/info-card.tsx +32 -18
  81. package/src/components/info-metric.tsx +16 -6
  82. package/src/components/info-row-group.tsx +1 -7
  83. package/src/components/info-row.tsx +30 -24
  84. package/src/components/invoice/action.tsx +1 -7
  85. package/src/components/invoice/list.tsx +34 -26
  86. package/src/components/invoice/recharge.tsx +5 -7
  87. package/src/components/invoice/table.tsx +17 -12
  88. package/src/components/layout/user.tsx +1 -1
  89. package/src/components/metadata/form.tsx +290 -94
  90. package/src/components/metadata/list.tsx +11 -3
  91. package/src/components/meter/actions.tsx +101 -0
  92. package/src/components/meter/add-usage-dialog.tsx +239 -0
  93. package/src/components/meter/events-list.tsx +657 -0
  94. package/src/components/meter/form.tsx +245 -0
  95. package/src/components/meter/products.tsx +264 -0
  96. package/src/components/meter/usage-guide.tsx +174 -0
  97. package/src/components/passport/actions.tsx +9 -4
  98. package/src/components/payment-currency/add.tsx +16 -3
  99. package/src/components/payment-currency/form.tsx +14 -6
  100. package/src/components/payment-intent/actions.tsx +24 -16
  101. package/src/components/payment-intent/list.tsx +30 -9
  102. package/src/components/payment-link/actions.tsx +1 -5
  103. package/src/components/payment-link/after-pay.tsx +4 -2
  104. package/src/components/payment-link/before-pay.tsx +14 -4
  105. package/src/components/payment-link/item.tsx +27 -6
  106. package/src/components/payment-link/preview.tsx +9 -9
  107. package/src/components/payment-link/product-select.tsx +69 -15
  108. package/src/components/payment-method/arcblock.tsx +8 -1
  109. package/src/components/payment-method/base.tsx +8 -1
  110. package/src/components/payment-method/bitcoin.tsx +8 -1
  111. package/src/components/payment-method/ethereum.tsx +8 -1
  112. package/src/components/payment-method/evm-rpc-input.tsx +11 -7
  113. package/src/components/payment-method/form.tsx +2 -7
  114. package/src/components/payment-method/stripe.tsx +2 -0
  115. package/src/components/payouts/actions.tsx +1 -5
  116. package/src/components/payouts/list.tsx +30 -10
  117. package/src/components/payouts/portal/list.tsx +11 -9
  118. package/src/components/price/currency-select.tsx +63 -32
  119. package/src/components/price/form.tsx +895 -370
  120. package/src/components/price/upsell-select.tsx +10 -2
  121. package/src/components/price/upsell.tsx +7 -2
  122. package/src/components/pricing-table/actions.tsx +1 -5
  123. package/src/components/pricing-table/customer-settings.tsx +5 -1
  124. package/src/components/pricing-table/payment-settings.tsx +14 -4
  125. package/src/components/pricing-table/preview.tsx +9 -9
  126. package/src/components/pricing-table/price-item.tsx +6 -1
  127. package/src/components/pricing-table/product-item.tsx +6 -1
  128. package/src/components/pricing-table/product-settings.tsx +17 -4
  129. package/src/components/product/actions.tsx +1 -5
  130. package/src/components/product/add-price.tsx +9 -7
  131. package/src/components/product/create.tsx +8 -9
  132. package/src/components/product/cross-sell-select.tsx +5 -1
  133. package/src/components/product/cross-sell.tsx +7 -2
  134. package/src/components/product/edit-price.tsx +21 -12
  135. package/src/components/product/features.tsx +26 -6
  136. package/src/components/product/form.tsx +115 -72
  137. package/src/components/progress-bar.tsx +1 -1
  138. package/src/components/refund/actions.tsx +1 -7
  139. package/src/components/refund/list.tsx +31 -18
  140. package/src/components/section/header.tsx +12 -14
  141. package/src/components/subscription/actions/cancel.tsx +22 -5
  142. package/src/components/subscription/actions/index.tsx +9 -10
  143. package/src/components/subscription/actions/pause.tsx +32 -6
  144. package/src/components/subscription/actions/slash-stake.tsx +5 -3
  145. package/src/components/subscription/description.tsx +12 -8
  146. package/src/components/subscription/items/index.tsx +31 -16
  147. package/src/components/subscription/items/usage-records.tsx +19 -5
  148. package/src/components/subscription/list.tsx +5 -7
  149. package/src/components/subscription/metrics.tsx +62 -15
  150. package/src/components/subscription/portal/actions.tsx +78 -71
  151. package/src/components/subscription/portal/cancel.tsx +10 -3
  152. package/src/components/subscription/portal/list.tsx +48 -26
  153. package/src/components/uploader.tsx +5 -13
  154. package/src/components/webhook/attempts.tsx +51 -16
  155. package/src/components/webhook/request-info.tsx +8 -6
  156. package/src/contexts/products.tsx +27 -10
  157. package/src/hooks/subscription.ts +34 -0
  158. package/src/libs/meter-utils.ts +196 -0
  159. package/src/libs/util.ts +4 -0
  160. package/src/locales/en.tsx +385 -4
  161. package/src/locales/zh.tsx +364 -0
  162. package/src/pages/admin/billing/index.tsx +61 -33
  163. package/src/pages/admin/billing/invoices/detail.tsx +49 -13
  164. package/src/pages/admin/billing/meters/create.tsx +60 -0
  165. package/src/pages/admin/billing/meters/detail.tsx +435 -0
  166. package/src/pages/admin/billing/meters/index.tsx +210 -0
  167. package/src/pages/admin/billing/meters/meter-event.tsx +346 -0
  168. package/src/pages/admin/billing/subscriptions/detail.tsx +90 -25
  169. package/src/pages/admin/customers/customers/credit-grant/detail.tsx +391 -0
  170. package/src/pages/admin/customers/customers/detail.tsx +67 -14
  171. package/src/pages/admin/customers/customers/index.tsx +6 -1
  172. package/src/pages/admin/customers/index.tsx +5 -0
  173. package/src/pages/admin/developers/events/detail.tsx +37 -11
  174. package/src/pages/admin/developers/index.tsx +1 -1
  175. package/src/pages/admin/developers/webhooks/detail.tsx +41 -11
  176. package/src/pages/admin/index.tsx +15 -2
  177. package/src/pages/admin/overview.tsx +107 -19
  178. package/src/pages/admin/payments/intents/detail.tsx +58 -14
  179. package/src/pages/admin/payments/payouts/detail.tsx +63 -15
  180. package/src/pages/admin/payments/refunds/detail.tsx +58 -14
  181. package/src/pages/admin/products/index.tsx +11 -4
  182. package/src/pages/admin/products/links/create.tsx +22 -4
  183. package/src/pages/admin/products/links/detail.tsx +43 -14
  184. package/src/pages/admin/products/passports/index.tsx +23 -4
  185. package/src/pages/admin/products/prices/actions.tsx +16 -9
  186. package/src/pages/admin/products/prices/detail.tsx +73 -14
  187. package/src/pages/admin/products/prices/list.tsx +15 -3
  188. package/src/pages/admin/products/pricing-tables/create.tsx +45 -12
  189. package/src/pages/admin/products/pricing-tables/detail.tsx +45 -14
  190. package/src/pages/admin/products/products/create.tsx +233 -54
  191. package/src/pages/admin/products/products/detail.tsx +74 -18
  192. package/src/pages/admin/settings/index.tsx +8 -1
  193. package/src/pages/admin/settings/payment-methods/index.tsx +87 -19
  194. package/src/pages/admin/settings/vault-config/edit-form.tsx +42 -28
  195. package/src/pages/admin/settings/vault-config/index.tsx +57 -10
  196. package/src/pages/customer/credit-grant/detail.tsx +308 -0
  197. package/src/pages/customer/index.tsx +76 -17
  198. package/src/pages/customer/invoice/detail.tsx +63 -14
  199. package/src/pages/customer/invoice/past-due.tsx +11 -3
  200. package/src/pages/customer/payout/detail.tsx +56 -13
  201. package/src/pages/customer/recharge/account.tsx +78 -18
  202. package/src/pages/customer/recharge/subscription.tsx +86 -25
  203. package/src/pages/customer/refund/list.tsx +60 -24
  204. package/src/pages/customer/subscription/change-payment.tsx +17 -6
  205. package/src/pages/customer/subscription/change-plan.tsx +34 -7
  206. package/src/pages/customer/subscription/detail.tsx +134 -34
  207. package/src/pages/customer/subscription/embed.tsx +25 -5
  208. package/src/pages/home.tsx +26 -4
  209. package/src/pages/integrations/donations/edit-form.tsx +25 -9
  210. package/src/pages/integrations/donations/index.tsx +26 -9
  211. package/src/pages/integrations/donations/preview.tsx +59 -15
  212. package/src/pages/integrations/index.tsx +10 -1
  213. package/src/pages/integrations/overview.tsx +78 -17
  214. package/vite.config.ts +60 -30
@@ -11,11 +11,14 @@ type Props = {
11
11
  variant?: string;
12
12
  };
13
13
 
14
- PassportActions.defaultProps = {
15
- variant: 'compact',
16
- };
14
+ export default function PassportActions(rawProps: Props) {
15
+ const props = Object.assign(
16
+ {
17
+ variant: 'compact',
18
+ },
19
+ rawProps
20
+ );
17
21
 
18
- export default function PassportActions(props: Props) {
19
22
  const { t } = useLocaleContext();
20
23
 
21
24
  const [state, setState] = useSetState({
@@ -26,6 +29,7 @@ export default function PassportActions(props: Props) {
26
29
  const onUnassign = async () => {
27
30
  try {
28
31
  setState({ loading: true });
32
+ // eslint-disable-next-line react/prop-types
29
33
  await api.delete(`/api/passports/assign/${props.data.name}`).then((res) => res.data);
30
34
  Toast.success(t('common.saved'));
31
35
  } catch (err) {
@@ -46,6 +50,7 @@ export default function PassportActions(props: Props) {
46
50
 
47
51
  return (
48
52
  <ClickBoundary>
53
+ {/* eslint-disable-next-line react/prop-types */}
49
54
  <Actions variant={props.variant} actions={actions} />
50
55
  {state.action === 'unassign' && (
51
56
  <ConfirmDialog
@@ -179,8 +179,17 @@ export default function PaymentCurrencyAdd({
179
179
  }}
180
180
  />
181
181
  <Stack>
182
- <Typography fontWeight={500}>{option.name}</Typography>
183
- <Typography variant="caption" color="text.secondary">
182
+ <Typography
183
+ sx={{
184
+ fontWeight: 500,
185
+ }}>
186
+ {option.name}
187
+ </Typography>
188
+ <Typography
189
+ variant="caption"
190
+ sx={{
191
+ color: 'text.secondary',
192
+ }}>
184
193
  {option.symbol}
185
194
  </Typography>
186
195
  </Stack>
@@ -189,7 +198,11 @@ export default function PaymentCurrencyAdd({
189
198
  isOptionEqualToValue={(option, value) => option.address === value.address}
190
199
  />
191
200
  <Divider sx={{ my: 3 }}>
192
- <Typography variant="caption" color="text.secondary">
201
+ <Typography
202
+ variant="caption"
203
+ sx={{
204
+ color: 'text.secondary',
205
+ }}>
193
206
  {t('admin.paymentCurrency.orManualInput')}
194
207
  </Typography>
195
208
  </Divider>
@@ -10,10 +10,6 @@ type TPaymentCurrencyFormProps = {
10
10
  disableKeys?: string[];
11
11
  };
12
12
 
13
- PaymentCurrencyForm.defaultProps = {
14
- disableKeys: [],
15
- };
16
-
17
13
  export default function PaymentCurrencyForm({ disableKeys = [] }: TPaymentCurrencyFormProps) {
18
14
  const { t } = useLocaleContext();
19
15
  const { control, setValue } = useFormContext();
@@ -29,7 +25,12 @@ export default function PaymentCurrencyForm({ disableKeys = [] }: TPaymentCurren
29
25
  };
30
26
 
31
27
  return (
32
- <Stack direction="column" alignItems="flex-start" spacing={2}>
28
+ <Stack
29
+ direction="column"
30
+ spacing={2}
31
+ sx={{
32
+ alignItems: 'flex-start',
33
+ }}>
33
34
  <FormInput
34
35
  key="name"
35
36
  name="name"
@@ -38,6 +39,7 @@ export default function PaymentCurrencyForm({ disableKeys = [] }: TPaymentCurren
38
39
  label={t('admin.paymentMethod.name.label')}
39
40
  placeholder={t('admin.paymentMethod.name.tip')}
40
41
  disabled={disableKeys.includes('name')}
42
+ inputProps={{ maxLength: 32 }}
41
43
  />
42
44
  <FormInput
43
45
  key="description"
@@ -47,6 +49,7 @@ export default function PaymentCurrencyForm({ disableKeys = [] }: TPaymentCurren
47
49
  label={t('admin.paymentMethod.description.label')}
48
50
  placeholder={t('admin.paymentMethod.description.tip')}
49
51
  disabled={disableKeys.includes('description')}
52
+ inputProps={{ maxLength: 255 }}
50
53
  />
51
54
  <FormInput
52
55
  key="contract"
@@ -58,7 +61,12 @@ export default function PaymentCurrencyForm({ disableKeys = [] }: TPaymentCurren
58
61
  disabled={disableKeys.includes('contract')}
59
62
  />
60
63
  <Stack direction="column">
61
- <Typography mb={1}>{t('admin.paymentCurrency.logo.label')}</Typography>
64
+ <Typography
65
+ sx={{
66
+ mb: 1,
67
+ }}>
68
+ {t('admin.paymentCurrency.logo.label')}
69
+ </Typography>
62
70
  <Uploader onUploaded={onUploaded} preview={logo} />
63
71
  </Stack>
64
72
  </Stack>
@@ -27,13 +27,6 @@ type Props = {
27
27
  onChange: (action: string) => void;
28
28
  };
29
29
 
30
- PaymentIntentActionsInner.defaultProps = {
31
- variant: 'compact',
32
- };
33
- PaymentIntentActions.defaultProps = {
34
- variant: 'compact',
35
- };
36
-
37
30
  const fetchRefundData = (id: string) => {
38
31
  return api.get(`/api/payment-intents/${id}/refundable-amount`).then((res) => res.data);
39
32
  };
@@ -64,7 +57,13 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
64
57
  return true;
65
58
  };
66
59
  return (
67
- <Stack direction="column" spacing={1} alignItems="flex-start" sx={{ width: 400 }}>
60
+ <Stack
61
+ direction="column"
62
+ spacing={1}
63
+ sx={{
64
+ alignItems: 'flex-start',
65
+ width: 400,
66
+ }}>
68
67
  <Controller
69
68
  name="refund.reason"
70
69
  control={control}
@@ -103,7 +102,6 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
103
102
  </RadioGroup>
104
103
  )}
105
104
  />
106
-
107
105
  <FormControl fullWidth component="fieldset" variant="outlined">
108
106
  <Controller
109
107
  name="refund.amount"
@@ -121,8 +119,10 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
121
119
  placeholder={t('admin.paymentIntent.refundForm.amount')}
122
120
  error={!!(errors as any)?.refund?.amount}
123
121
  helperText={(errors as any)?.refund?.amount?.message}
124
- InputProps={{
125
- endAdornment: <InputAdornment position="end">{data.paymentCurrency.symbol}</InputAdornment>,
122
+ slotProps={{
123
+ input: {
124
+ endAdornment: <InputAdornment position="end">{data.paymentCurrency.symbol}</InputAdornment>,
125
+ },
126
126
  }}
127
127
  />
128
128
  )}
@@ -135,7 +135,6 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
135
135
  })}
136
136
  </FormHelperText>
137
137
  </FormControl>
138
-
139
138
  <Controller
140
139
  name="refund.description"
141
140
  control={control}
@@ -155,8 +154,10 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
155
154
  placeholder={t('admin.paymentIntent.refundForm.description')}
156
155
  error={!!(errors as any)?.refund?.description}
157
156
  helperText={(errors as any)?.refund?.description?.message}
158
- inputProps={{
159
- maxLength: 200,
157
+ slotProps={{
158
+ htmlInput: {
159
+ maxLength: 200,
160
+ },
160
161
  }}
161
162
  />
162
163
  )}
@@ -165,7 +166,7 @@ function RefundForm({ data, refundMaxAmount }: { data: TPaymentIntentExpanded; r
165
166
  );
166
167
  }
167
168
 
168
- export function PaymentIntentActionsInner({ data, variant, onChange }: Props) {
169
+ export function PaymentIntentActionsInner({ data, variant = 'compact', onChange }: Props) {
169
170
  const { t } = useLocaleContext();
170
171
  const navigate = useNavigate();
171
172
  const { reset, getValues, setValue, handleSubmit } = useFormContext();
@@ -262,7 +263,14 @@ export function PaymentIntentActionsInner({ data, variant, onChange }: Props) {
262
263
  );
263
264
  }
264
265
 
265
- export default function PaymentIntentActions(props: Props) {
266
+ export default function PaymentIntentActions(rawProps: Props) {
267
+ const props = Object.assign(
268
+ {
269
+ variant: 'compact',
270
+ },
271
+ rawProps
272
+ );
273
+
266
274
  const methods = useForm({
267
275
  mode: 'onChange',
268
276
  defaultValues: {
@@ -10,7 +10,7 @@ import {
10
10
  useDefaultPageSize,
11
11
  } from '@blocklet/payment-react';
12
12
  import type { TPaymentIntentExpanded } from '@blocklet/payment-types';
13
- import { CircularProgress, Typography } from '@mui/material';
13
+ import { Avatar, CircularProgress, Typography } from '@mui/material';
14
14
  import { useLocalStorageState } from 'ahooks';
15
15
  import { useEffect, useState } from 'react';
16
16
  import { Link } from 'react-router-dom';
@@ -66,16 +66,14 @@ const getListKey = (props: ListProps) => {
66
66
  return 'payments';
67
67
  };
68
68
 
69
- PaymentList.defaultProps = {
70
- features: {
69
+ export default function PaymentList({
70
+ customer_id = '',
71
+ invoice_id = '',
72
+ features = {
71
73
  customer: true,
72
74
  filter: true,
73
75
  },
74
- customer_id: '',
75
- invoice_id: '',
76
- };
77
-
78
- export default function PaymentList({ customer_id, invoice_id, features }: ListProps) {
76
+ }: ListProps) {
79
77
  const { t } = useLocaleContext();
80
78
 
81
79
  const listKey = getListKey({ customer_id, invoice_id });
@@ -120,7 +118,12 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
120
118
  const highlight = item.amount_received === '0' && item.status !== 'canceled';
121
119
  return (
122
120
  <Link to={`/admin/payments/${item.id}`}>
123
- <Typography component="strong" sx={{ color: highlight ? 'warning.main' : 'inherit' }} fontWeight={600}>
121
+ <Typography
122
+ component="strong"
123
+ sx={{
124
+ fontWeight: 600,
125
+ color: highlight ? 'warning.main' : 'inherit',
126
+ }}>
124
127
  {formatBNStr(
125
128
  item.amount_received === '0' ? item.amount : item.amount_received,
126
129
  item?.paymentCurrency.decimal
@@ -133,6 +136,24 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
133
136
  },
134
137
  },
135
138
  },
139
+ {
140
+ label: t('common.paymentMethod'),
141
+ name: 'paymentMethod',
142
+ width: 120,
143
+ options: {
144
+ customBodyRenderLite: (_: string, index: number) => {
145
+ const item = data.list[index] as TPaymentIntentExpanded;
146
+ return (
147
+ <Link to={`/admin/payments/${item.id}`}>
148
+ <Typography sx={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}>
149
+ <Avatar src={item.paymentMethod.logo} sx={{ width: 18, height: 18, mr: 1 }} />
150
+ {item.paymentMethod.name}
151
+ </Typography>
152
+ </Link>
153
+ );
154
+ },
155
+ },
156
+ },
136
157
  {
137
158
  label: t('common.status'),
138
159
  name: 'status',
@@ -18,11 +18,7 @@ type Props = {
18
18
  variant?: LiteralUnion<'compact' | 'normal', string>;
19
19
  };
20
20
 
21
- PaymentLinkActions.defaultProps = {
22
- variant: 'compact',
23
- };
24
-
25
- export default function PaymentLinkActions({ data, variant, onChange }: Props) {
21
+ export default function PaymentLinkActions({ data, variant = 'compact', onChange }: Props) {
26
22
  const { t } = useLocaleContext();
27
23
  const [state, setState] = useSetState({
28
24
  action: '',
@@ -54,8 +54,10 @@ export default function AfterPay() {
54
54
  error={!!get(errors, field.name)}
55
55
  fullWidth
56
56
  size="small"
57
- inputProps={{
58
- maxLength: 200,
57
+ slotProps={{
58
+ htmlInput: {
59
+ maxLength: 200,
60
+ },
59
61
  }}
60
62
  />
61
63
  )}
@@ -16,7 +16,7 @@ import ProductSelect from './product-select';
16
16
  export default function BeforePay({
17
17
  triggerError = () => {},
18
18
  }: {
19
- triggerError: (keys: { [key: string]: boolean }) => void;
19
+ triggerError?: (keys: { [key: string]: boolean }) => void;
20
20
  }) {
21
21
  const { t } = useLocaleContext();
22
22
  const [params, setParams] = useSearchParams();
@@ -123,12 +123,20 @@ export default function BeforePay({
123
123
  );
124
124
  })}
125
125
  {items.fields.some((_, index) => !isPriceAligned(items.fields as any[], products, index).recurring) && (
126
- <Typography color="error" fontSize="small">
126
+ <Typography
127
+ color="error"
128
+ sx={{
129
+ fontSize: 'small',
130
+ }}>
127
131
  {t('admin.paymentLink.recurringNotAligned')}
128
132
  </Typography>
129
133
  )}
130
134
  {items.fields.some((_, index) => !isPriceAligned(items.fields as any[], products, index).currency) && (
131
- <Typography color="error" fontSize="small">
135
+ <Typography
136
+ color="error"
137
+ sx={{
138
+ fontSize: 'small',
139
+ }}>
132
140
  {t('admin.paymentLink.currencyNotAligned')}
133
141
  </Typography>
134
142
  )}
@@ -248,9 +256,11 @@ export default function BeforePay({
248
256
  <TextField
249
257
  {...field}
250
258
  size="small"
251
- InputProps={{ endAdornment: t('common.days') }}
252
259
  helperText={get(errors, 'subscription_data.trial_period_days')?.message as string}
253
260
  error={!!get(errors, 'subscription_data.trial_period_days')}
261
+ slotProps={{
262
+ input: { endAdornment: t('common.days') },
263
+ }}
254
264
  />
255
265
  )}
256
266
  />
@@ -75,7 +75,11 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
75
75
  ]}
76
76
  />
77
77
  </ClickBoundary>
78
- <Stack direction="column" alignItems="flex-start">
78
+ <Stack
79
+ direction="column"
80
+ sx={{
81
+ alignItems: 'flex-start',
82
+ }}>
79
83
  <InfoCard
80
84
  logo={product.images[0]}
81
85
  name={product.name}
@@ -85,10 +89,14 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
85
89
  name={getFieldName('quantity')}
86
90
  control={control}
87
91
  render={({ field }) => (
88
- <Stack direction="row" alignItems="center" mt={1}>
92
+ <Stack
93
+ direction="row"
94
+ sx={{
95
+ alignItems: 'center',
96
+ mt: 1,
97
+ }}>
89
98
  <TextField
90
99
  sx={{ width: 80, mr: 1 }}
91
- inputProps={{ style: { padding: '4px 8px' } }}
92
100
  size="small"
93
101
  type="number"
94
102
  {...field}
@@ -98,6 +106,9 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
98
106
  field.onChange(intValue);
99
107
  }
100
108
  }}
109
+ slotProps={{
110
+ htmlInput: { style: { padding: '4px 8px' } },
111
+ }}
101
112
  />
102
113
  <FormLabel style={{ marginBottom: 0 }}>{t('common.quantity')}</FormLabel>
103
114
  </Stack>
@@ -118,7 +129,13 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
118
129
  />
119
130
  {adjustable && (
120
131
  <>
121
- <Stack direction="row" alignItems="center" mt={1} ml={6}>
132
+ <Stack
133
+ direction="row"
134
+ sx={{
135
+ alignItems: 'center',
136
+ mt: 1,
137
+ ml: 6,
138
+ }}>
122
139
  <Typography sx={{ mr: 0.5 }}>Between</Typography>
123
140
  <Controller
124
141
  name={getFieldName('adjustable_quantity.minimum')}
@@ -131,10 +148,12 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
131
148
  render={({ field }) => (
132
149
  <TextField
133
150
  sx={{ width: 40 }}
134
- inputProps={{ style: { padding: '4px 8px' } }}
135
151
  size="small"
136
152
  error={!!adjustableError}
137
153
  {...field}
154
+ slotProps={{
155
+ htmlInput: { style: { padding: '4px 8px' } },
156
+ }}
138
157
  />
139
158
  )}
140
159
  />
@@ -152,10 +171,12 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
152
171
  render={({ field }) => (
153
172
  <TextField
154
173
  sx={{ width: 40 }}
155
- inputProps={{ style: { padding: '4px 8px' } }}
156
174
  size="small"
157
175
  error={!!adjustableError}
158
176
  {...field}
177
+ slotProps={{
178
+ htmlInput: { style: { padding: '4px 8px' } },
179
+ }}
159
180
  />
160
181
  )}
161
182
  />
@@ -1,10 +1,16 @@
1
- import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
1
+ import { useEffect, useImperativeHandle, useRef, useState } from 'react';
2
2
  import { useFullscreen, useSize } from 'ahooks';
3
3
  import IframeResizer from 'iframe-resizer-react';
4
4
  import { useTheme } from '@mui/material';
5
5
  import Chrome from './chrome';
6
6
 
7
- const PaymentLinkPreview = forwardRef(({ id, version = 1 }: { id: string; version?: number }, ref) => {
7
+ export default function PaymentLinkPreview({
8
+ ref = undefined,
9
+ id,
10
+ version = 1,
11
+ }: { id: string; version?: number } & {
12
+ ref?: React.RefObject<unknown | null>;
13
+ }) {
8
14
  const theme = useTheme();
9
15
  const innerRef = useRef(null);
10
16
  const size = useSize(innerRef);
@@ -50,10 +56,4 @@ const PaymentLinkPreview = forwardRef(({ id, version = 1 }: { id: string; versio
50
56
  )}
51
57
  </div>
52
58
  );
53
- });
54
-
55
- PaymentLinkPreview.defaultProps = {
56
- version: 1,
57
- };
58
-
59
- export default PaymentLinkPreview;
59
+ }
@@ -7,40 +7,75 @@ import cloneDeep from 'lodash/cloneDeep';
7
7
  import { useState } from 'react';
8
8
  import type { LiteralUnion } from 'type-fest';
9
9
 
10
+ import Empty from '@arcblock/ux/lib/Empty';
10
11
  import { useProductsContext } from '../../contexts/products';
11
12
 
12
13
  type Props = {
13
14
  mode: LiteralUnion<'waiting' | 'selecting' | 'inline', string>;
14
15
  hasSelected: (price: any) => boolean;
15
16
  onSelect: (priceId: string) => void;
17
+ addProduct?: boolean;
18
+ filterPrice?: (price: any) => boolean;
16
19
  };
17
20
 
18
- const filterPrices = (product: TProductExpanded, hasSelected: (price: any) => boolean) => {
19
- product.prices = product.prices.filter((x) => x.active && !hasSelected(x));
21
+ const filterPrices = (
22
+ product: TProductExpanded,
23
+ hasSelected: (price: any) => boolean,
24
+ filterPrice?: (price: any) => boolean
25
+ ) => {
26
+ product.prices = product.prices.filter((x) => {
27
+ const isActive = x.active;
28
+ const notSelected = !hasSelected(x);
29
+ const customFilter = filterPrice ? filterPrice(x) : true;
30
+ return isActive && notSelected && customFilter;
31
+ });
20
32
  return product;
21
33
  };
22
34
 
23
- const filterProducts = (products: TProductExpanded[], hasSelected: (price: any) => boolean) => {
24
- const filtered = cloneDeep(products).map((x) => filterPrices(x, hasSelected));
35
+ const filterProducts = (
36
+ products: TProductExpanded[],
37
+ hasSelected: (price: any) => boolean,
38
+ filterPrice?: (price: any) => boolean
39
+ ) => {
40
+ const filtered = cloneDeep(products).map((x) => filterPrices(x, hasSelected, filterPrice));
25
41
  return filtered.filter((x) => x.prices.length);
26
42
  };
27
43
 
28
- export default function ProductSelect({ mode: initialMode, hasSelected, onSelect }: Props) {
44
+ export default function ProductSelect({
45
+ mode: initialMode,
46
+ hasSelected,
47
+ onSelect,
48
+ addProduct = true,
49
+ filterPrice = () => true,
50
+ }: Props) {
29
51
  const { t } = useLocaleContext();
30
52
  const [mode, setMode] = useState(initialMode);
31
53
  const { products } = useProductsContext();
32
54
  const { settings } = usePaymentContext();
55
+ const [value, setValue] = useState('');
33
56
  const size = { width: 16, height: 16 };
34
57
 
35
58
  const handleSelect = (e: any) => {
59
+ setValue(e.target.value);
36
60
  setMode('waiting');
37
61
  onSelect(e.target.value);
38
62
  };
39
63
 
40
- const items = (callback?: any) =>
41
- filterProducts(products, hasSelected).map((product) => [
64
+ const items = (callback?: any) => {
65
+ const filteredProducts = filterProducts(products, hasSelected, filterPrice);
66
+
67
+ if (filteredProducts.length === 0) {
68
+ return <Empty>{t('admin.product.empty')}</Empty>;
69
+ }
70
+
71
+ return filteredProducts.map((product) => [
42
72
  <ListSubheader key={product.id} sx={{ fontSize: '0.875rem', color: 'text.secondary', lineHeight: '2.1875rem' }}>
43
- <Stack direction="row" alignItems="center" spacing={0.5}>
73
+ <Stack
74
+ direction="row"
75
+ spacing={0.5}
76
+ sx={{
77
+ alignItems: 'center',
78
+ }}>
44
79
  {product.images[0] ? (
45
80
  <Avatar src={product.images[0]} alt={product.name} variant="square" sx={size} />
46
81
  ) : (
@@ -65,8 +100,17 @@ export default function ProductSelect({ mode: initialMode, hasSelected, onSelect
65
100
  callback(price);
66
101
  }
67
102
  }}>
68
- <Typography color="text.primary">{formatPrice(price, currency!)}</Typography>
69
- <Typography color="text.secondary" sx={{ ml: 2 }}>
103
+ <Typography
104
+ sx={{
105
+ color: 'text.primary',
106
+ }}>
107
+ {formatPrice(price, currency!)}
108
+ </Typography>
109
+ <Typography
110
+ sx={{
111
+ color: 'text.secondary',
112
+ ml: 2,
113
+ }}>
70
114
  {getPriceCurrencyOptions(price).length > 1
71
115
  ? ` +${getPriceCurrencyOptions(price).length - 1} more currencies`
72
116
  : ''}
@@ -75,6 +119,7 @@ export default function ProductSelect({ mode: initialMode, hasSelected, onSelect
75
119
  );
76
120
  }),
77
121
  ]);
122
+ };
78
123
 
79
124
  if (mode === 'inline') {
80
125
  return <>{items(onSelect)}</>;
@@ -82,11 +127,20 @@ export default function ProductSelect({ mode: initialMode, hasSelected, onSelect
82
127
 
83
128
  if (mode === 'selecting') {
84
129
  return (
85
- <Select value="" fullWidth size="small" onChange={handleSelect} MenuProps={{ style: { maxHeight: 480 } }}>
86
- <MenuItem value="add">
87
- <AddOutlined />
88
- {t('admin.product.add')}
89
- </MenuItem>
130
+ <Select
131
+ fullWidth
132
+ size="small"
133
+ onChange={handleSelect}
134
+ value={value}
135
+ MenuProps={{
136
+ style: { maxHeight: 480 },
137
+ }}>
138
+ {addProduct && (
139
+ <MenuItem value="add">
140
+ <AddOutlined />
141
+ {t('admin.product.add')}
142
+ </MenuItem>
143
+ )}
90
144
  {items()}
91
145
  </Select>
92
146
  );
@@ -30,6 +30,7 @@ export default function ArcBlockMethodForm({ checkDisabled }: { checkDisabled: (
30
30
  label={t('admin.paymentMethod.name.label')}
31
31
  placeholder={t('admin.paymentMethod.name.tip')}
32
32
  disabled={checkDisabled('name')}
33
+ inputProps={{ maxLength: 32 }}
33
34
  />
34
35
  <FormInput
35
36
  key="description"
@@ -39,6 +40,7 @@ export default function ArcBlockMethodForm({ checkDisabled }: { checkDisabled: (
39
40
  label={t('admin.paymentMethod.description.label')}
40
41
  placeholder={t('admin.paymentMethod.description.tip')}
41
42
  disabled={checkDisabled('description')}
43
+ inputProps={{ maxLength: 255 }}
42
44
  />
43
45
  <FormInput
44
46
  key="secret_key"
@@ -68,7 +70,12 @@ export default function ArcBlockMethodForm({ checkDisabled }: { checkDisabled: (
68
70
  disabled={checkDisabled('settings.arcblock.explorer_host')}
69
71
  />
70
72
  <Stack direction="column">
71
- <Typography mb={1}>{t('admin.paymentCurrency.logo.label')}</Typography>
73
+ <Typography
74
+ sx={{
75
+ mb: 1,
76
+ }}>
77
+ {t('admin.paymentCurrency.logo.label')}
78
+ </Typography>
72
79
  <Uploader onUploaded={onUploaded} preview={logo} disabled={checkDisabled('logo')} />
73
80
  </Stack>
74
81
  </>
@@ -31,6 +31,7 @@ export default function BaseMethodForm({ checkDisabled }: { checkDisabled: (key:
31
31
  label={t('admin.paymentMethod.name.label')}
32
32
  placeholder={t('admin.paymentMethod.name.tip')}
33
33
  disabled={checkDisabled('name')}
34
+ inputProps={{ maxLength: 32 }}
34
35
  />
35
36
  <FormInput
36
37
  key="description"
@@ -40,6 +41,7 @@ export default function BaseMethodForm({ checkDisabled }: { checkDisabled: (key:
40
41
  label={t('admin.paymentMethod.description.label')}
41
42
  placeholder={t('admin.paymentMethod.description.tip')}
42
43
  disabled={checkDisabled('description')}
44
+ inputProps={{ maxLength: 255 }}
43
45
  />
44
46
  <EvmRpcInput
45
47
  name="settings.base.api_host"
@@ -75,7 +77,12 @@ export default function BaseMethodForm({ checkDisabled }: { checkDisabled: (key:
75
77
  disabled={checkDisabled('settings.base.confirmation')}
76
78
  />
77
79
  <Stack direction="column">
78
- <Typography mb={1}>{t('admin.paymentCurrency.logo.label')}</Typography>
80
+ <Typography
81
+ sx={{
82
+ mb: 1,
83
+ }}>
84
+ {t('admin.paymentCurrency.logo.label')}
85
+ </Typography>
79
86
  <Uploader onUploaded={onUploaded} preview={logo} disabled={checkDisabled('logo')} />
80
87
  </Stack>
81
88
  </>