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
@@ -30,6 +30,7 @@ export default function BitcoinMethodForm({ checkDisabled }: { checkDisabled: (k
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 BitcoinMethodForm({ checkDisabled }: { checkDisabled: (k
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="chain_id"
@@ -68,7 +70,12 @@ export default function BitcoinMethodForm({ checkDisabled }: { checkDisabled: (k
68
70
  disabled={checkDisabled('settings.bitcoin.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 EthereumMethodForm({ checkDisabled }: { checkDisabled: (
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 EthereumMethodForm({ checkDisabled }: { checkDisabled: (
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.ethereum.api_host"
@@ -75,7 +77,12 @@ export default function EthereumMethodForm({ checkDisabled }: { checkDisabled: (
75
77
  disabled={checkDisabled('settings.ethereum.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
  </>
@@ -13,11 +13,7 @@ interface Props {
13
13
  disabled?: boolean;
14
14
  }
15
15
 
16
- EvmRpcInput.defaultProps = {
17
- disabled: false,
18
- };
19
-
20
- export default function EvmRpcInput({ name, label, placeholder, disabled }: Props) {
16
+ export default function EvmRpcInput({ name, label, placeholder, disabled = false }: Props) {
21
17
  const { t } = useLocaleContext();
22
18
  const { control } = useFormContext();
23
19
  const apiHost = useWatch({
@@ -42,14 +38,22 @@ export default function EvmRpcInput({ name, label, placeholder, disabled }: Prop
42
38
  {status.loading ? (
43
39
  <>
44
40
  <CircularProgress size={14} />
45
- <Typography variant="caption" color="text.secondary">
41
+ <Typography
42
+ variant="caption"
43
+ sx={{
44
+ color: 'text.secondary',
45
+ }}>
46
46
  {t('admin.paymentMethod.evm.checking')}
47
47
  </Typography>
48
48
  </>
49
49
  ) : status.connected ? (
50
50
  <>
51
51
  <CheckCircleOutline sx={{ fontSize: 14 }} color="success" />
52
- <Typography variant="caption" color="text.secondary">
52
+ <Typography
53
+ variant="caption"
54
+ sx={{
55
+ color: 'text.secondary',
56
+ }}>
53
57
  {t('admin.paymentMethod.evm.connected')} | {t('admin.paymentMethod.evm.blockHeight')}:{' '}
54
58
  {status.blockNumber?.toLocaleString()}
55
59
  </Typography>
@@ -9,14 +9,9 @@ import EthereumMethodForm from './ethereum';
9
9
  import StripeMethodForm from './stripe';
10
10
  import BaseMethodForm from './base';
11
11
 
12
- PaymentMethodForm.defaultProps = {
13
- action: 'create',
14
- editableKeys: [],
15
- };
16
-
17
12
  export default function PaymentMethodForm({
18
- action,
19
- editableKeys,
13
+ action = 'create',
14
+ editableKeys = [],
20
15
  }: {
21
16
  action?: 'create' | 'edit';
22
17
  editableKeys?: string[];
@@ -14,6 +14,7 @@ export default function StripeMethodForm({ checkDisabled }: { checkDisabled: (ke
14
14
  label={t('admin.paymentMethod.name.label')}
15
15
  placeholder={t('admin.paymentMethod.name.tip')}
16
16
  disabled={checkDisabled('name')}
17
+ inputProps={{ maxLength: 32 }}
17
18
  />
18
19
  <FormInput
19
20
  name="description"
@@ -21,6 +22,7 @@ export default function StripeMethodForm({ checkDisabled }: { checkDisabled: (ke
21
22
  rules={{ required: true }}
22
23
  label={t('admin.paymentMethod.description.label')}
23
24
  placeholder={t('admin.paymentMethod.description.tip')}
25
+ inputProps={{ maxLength: 255 }}
24
26
  />
25
27
  <FormInput
26
28
  name="settings.stripe.dashboard"
@@ -11,11 +11,7 @@ type Props = {
11
11
  variant?: LiteralUnion<'compact' | 'normal', string>;
12
12
  };
13
13
 
14
- PayoutActions.defaultProps = {
15
- variant: 'compact',
16
- };
17
-
18
- export default function PayoutActions({ data, variant }: Props) {
14
+ export default function PayoutActions({ data, variant = 'compact' }: Props) {
19
15
  const { t } = useLocaleContext();
20
16
  const navigate = useNavigate();
21
17
  const actions = [
@@ -10,7 +10,7 @@ import {
10
10
  useDefaultPageSize,
11
11
  } from '@blocklet/payment-react';
12
12
  import type { TPayoutExpanded } 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';
@@ -70,17 +70,15 @@ const getListKey = (props: ListProps) => {
70
70
  return 'payouts';
71
71
  };
72
72
 
73
- PayoutList.defaultProps = {
74
- features: {
73
+ export default function PayoutList({
74
+ customer_id = '',
75
+ payment_intent_id = '',
76
+ status = '',
77
+ features = {
75
78
  customer: true,
76
79
  filter: true,
77
80
  },
78
- status: '',
79
- customer_id: '',
80
- payment_intent_id: '',
81
- };
82
-
83
- export default function PayoutList({ customer_id, payment_intent_id, status, features }: ListProps) {
81
+ }: ListProps) {
84
82
  const { t } = useLocaleContext();
85
83
 
86
84
  const listKey = getListKey({ customer_id, payment_intent_id });
@@ -120,7 +118,11 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
120
118
  const item = data.list[index] as TPayoutExpanded;
121
119
  return (
122
120
  <Link to={`/admin/payments/${item.id}`}>
123
- <Typography component="strong" fontWeight={600}>
121
+ <Typography
122
+ component="strong"
123
+ sx={{
124
+ fontWeight: 600,
125
+ }}>
124
126
  {formatBNStr(item.amount, item?.paymentCurrency.decimal)}
125
127
  &nbsp;
126
128
  {item?.paymentCurrency.symbol}
@@ -130,6 +132,24 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
130
132
  },
131
133
  },
132
134
  },
135
+ {
136
+ label: t('common.paymentMethod'),
137
+ name: 'paymentMethod',
138
+ width: 120,
139
+ options: {
140
+ customBodyRenderLite: (_: string, index: number) => {
141
+ const item = data.list[index] as TPayoutExpanded;
142
+ return (
143
+ <Link to={`/admin/payments/${item.id}`}>
144
+ <Typography sx={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}>
145
+ <Avatar src={item.paymentMethod.logo} sx={{ width: 18, height: 18, mr: 1 }} />
146
+ {item.paymentMethod.name}
147
+ </Typography>
148
+ </Link>
149
+ );
150
+ },
151
+ },
152
+ },
133
153
  {
134
154
  label: t('common.status'),
135
155
  name: 'status',
@@ -57,14 +57,12 @@ const getListKey = (props: ListProps) => {
57
57
  return 'payouts-mine';
58
58
  };
59
59
 
60
- CustomerRevenueList.defaultProps = {
61
- status: '',
62
- currency_id: '',
63
- customer_id: '',
64
- setHasRevenues: () => {},
65
- };
66
-
67
- export default function CustomerRevenueList({ currency_id, status, customer_id, setHasRevenues }: ListProps) {
60
+ export default function CustomerRevenueList({
61
+ currency_id = '',
62
+ status = '',
63
+ customer_id = '',
64
+ setHasRevenues = () => {},
65
+ }: ListProps) {
68
66
  const { t } = useLocaleContext();
69
67
  const { isMobile } = useMobile('sm');
70
68
 
@@ -107,7 +105,11 @@ export default function CustomerRevenueList({ currency_id, status, customer_id,
107
105
  const item = data.list[index] as TPayoutExpanded;
108
106
  return (
109
107
  <Link to={`/customer/payout/${item.id}`}>
110
- <Typography component="strong" fontWeight={600}>
108
+ <Typography
109
+ component="strong"
110
+ sx={{
111
+ fontWeight: 600,
112
+ }}>
111
113
  {formatBNStr(item.amount, item?.paymentCurrency.decimal)}
112
114
  &nbsp;
113
115
  {item?.paymentCurrency.symbol}
@@ -2,7 +2,7 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import { usePaymentContext } from '@blocklet/payment-react';
3
3
  import { AddOutlined, ArrowDropDown } from '@mui/icons-material';
4
4
  import { ListSubheader, MenuItem, Select, Stack, SxProps, Typography } from '@mui/material';
5
- import { useState } from 'react';
5
+ import { useState, useEffect } from 'react';
6
6
  import type { LiteralUnion } from 'type-fest';
7
7
 
8
8
  import { flatten } from 'lodash';
@@ -17,12 +17,7 @@ type Props = {
17
17
  width?: string;
18
18
  disabled?: boolean;
19
19
  selectSX?: SxProps;
20
- };
21
-
22
- CurrencySelect.defaultProps = {
23
- width: '100%',
24
- disabled: false,
25
- selectSX: {},
20
+ currencyFilter?: (currency: any) => boolean;
26
21
  };
27
22
 
28
23
  export default function CurrencySelect({
@@ -30,14 +25,30 @@ export default function CurrencySelect({
30
25
  hasSelected,
31
26
  onSelect,
32
27
  value,
33
- width,
34
- disabled,
35
- selectSX,
28
+ width = '100%',
29
+ disabled = false,
30
+ selectSX = {},
31
+ currencyFilter = () => true,
36
32
  }: Props) {
37
33
  const { t } = useLocaleContext();
38
34
  const { settings } = usePaymentContext();
39
35
  const [mode, setMode] = useState(initialMode);
40
36
 
37
+ const currencies = flatten(settings.paymentMethods.map((method) => method.payment_currencies));
38
+
39
+ useEffect(() => {
40
+ if (value && initialMode === 'selected' && currencies.length > 0) {
41
+ const currency = currencies.find((c) => c.id === value);
42
+ if (currency && !hasSelected(currency)) {
43
+ const timer = setTimeout(() => {
44
+ onSelect(value);
45
+ }, 0);
46
+ return () => clearTimeout(timer);
47
+ }
48
+ }
49
+ return undefined;
50
+ }, [value, initialMode]);
51
+
41
52
  const handleSelect = (e: any) => {
42
53
  if (disabled) {
43
54
  return;
@@ -46,8 +57,6 @@ export default function CurrencySelect({
46
57
  onSelect(e.target.value);
47
58
  };
48
59
 
49
- const currencies = flatten(settings.paymentMethods.map((method) => method.payment_currencies));
50
-
51
60
  const selectedCurrency = currencies.find((x) => x.id === value);
52
61
 
53
62
  const selectedPaymentMethod = settings.paymentMethods.find((x) => x.payment_currencies.some((c) => c.id === value));
@@ -59,13 +68,18 @@ export default function CurrencySelect({
59
68
  if (mode === 'selected') {
60
69
  return (
61
70
  <Typography
62
- fontSize="12px"
63
71
  onClick={() => {
64
72
  if (canSelect) {
65
73
  setMode('selecting');
66
74
  }
67
75
  }}
68
- sx={{ cursor: canSelect ? 'pointer' : 'default', display: 'inline-flex' }}>
76
+ sx={{
77
+ cursor: canSelect ? 'pointer' : 'default',
78
+ display: 'inline-flex',
79
+ minWidth: '120px',
80
+ justifyContent: 'flex-end',
81
+ textAlign: 'right',
82
+ }}>
69
83
  {selectedCurrency?.symbol} ({selectedPaymentMethod?.name})
70
84
  {canSelect && <ArrowDropDown sx={{ color: 'text.secondary', fontSize: 21 }} />}
71
85
  </Typography>
@@ -91,21 +105,35 @@ export default function CurrencySelect({
91
105
  sx={{ width, ...selectSX }}
92
106
  disabled={disabled}
93
107
  onClose={() => setMode(initialMode)}>
94
- {extraCurrencies.map((method) => [
95
- <ListSubheader
96
- key={method.id}
97
- sx={{ fontSize: '0.875rem', color: 'text.secondary', lineHeight: '2.1875rem' }}>
98
- {method.name}
99
- </ListSubheader>,
100
- ...method.payment_currencies.map((currency) => (
101
- <MenuItem key={currency.id} sx={{ pl: 3 }} value={currency.id}>
102
- <Stack direction="row" justifyContent="space-between" sx={{ width: '100%' }} gap={2}>
103
- <Currency logo={currency.logo} name={currency.name} />
104
- <Typography fontWeight="bold">{currency.symbol}</Typography>
105
- </Stack>
106
- </MenuItem>
107
- )),
108
- ])}
108
+ {extraCurrencies
109
+ .map((method) => {
110
+ const filteredCurrencies = method.payment_currencies.filter(currencyFilter);
111
+ if (filteredCurrencies.length === 0) {
112
+ return null;
113
+ }
114
+
115
+ return [
116
+ <ListSubheader
117
+ key={method.id}
118
+ sx={{ fontSize: '0.875rem', color: 'text.secondary', lineHeight: '2.1875rem' }}>
119
+ {method.name}
120
+ </ListSubheader>,
121
+ ...filteredCurrencies.map((currency) => (
122
+ <MenuItem key={currency.id} sx={{ pl: 3 }} value={currency.id}>
123
+ <Stack direction="row" sx={{ width: '100%', justifyContent: 'space-between', gap: 2 }}>
124
+ <Currency logo={currency.logo} name={currency.name} />
125
+ <Typography
126
+ sx={{
127
+ fontWeight: 'bold',
128
+ }}>
129
+ {currency.symbol}
130
+ </Typography>
131
+ </Stack>
132
+ </MenuItem>
133
+ )),
134
+ ];
135
+ })
136
+ .filter(Boolean)}
109
137
  </Select>
110
138
  );
111
139
  }
@@ -113,10 +141,13 @@ export default function CurrencySelect({
113
141
  return (
114
142
  <span>
115
143
  <Stack
116
- sx={{ cursor: 'pointer', display: 'inline-flex' }}
117
144
  direction="row"
118
- alignItems="center"
119
- onClick={() => setMode('selecting')}>
145
+ onClick={() => setMode('selecting')}
146
+ sx={{
147
+ alignItems: 'center',
148
+ cursor: 'pointer',
149
+ display: 'inline-flex',
150
+ }}>
120
151
  <AddOutlined color="primary" />
121
152
  <Typography color="primary">{t('admin.price.currency.add')}</Typography>
122
153
  </Stack>