payment-kit 1.19.17 → 1.19.19

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 (62) hide show
  1. package/api/src/index.ts +3 -1
  2. package/api/src/integrations/ethereum/tx.ts +11 -0
  3. package/api/src/integrations/stripe/handlers/invoice.ts +26 -6
  4. package/api/src/integrations/stripe/handlers/setup-intent.ts +34 -2
  5. package/api/src/integrations/stripe/resource.ts +185 -1
  6. package/api/src/libs/invoice.ts +2 -1
  7. package/api/src/libs/notification/template/customer-credit-low-balance.ts +155 -0
  8. package/api/src/libs/session.ts +6 -1
  9. package/api/src/libs/ws.ts +3 -2
  10. package/api/src/locales/en.ts +6 -6
  11. package/api/src/locales/zh.ts +4 -4
  12. package/api/src/queues/auto-recharge.ts +343 -0
  13. package/api/src/queues/credit-consume.ts +51 -1
  14. package/api/src/queues/credit-grant.ts +15 -0
  15. package/api/src/queues/notification.ts +16 -13
  16. package/api/src/queues/payment.ts +14 -1
  17. package/api/src/queues/space.ts +1 -0
  18. package/api/src/routes/auto-recharge-configs.ts +454 -0
  19. package/api/src/routes/connect/auto-recharge-auth.ts +182 -0
  20. package/api/src/routes/connect/recharge-account.ts +72 -10
  21. package/api/src/routes/connect/setup.ts +5 -3
  22. package/api/src/routes/connect/shared.ts +45 -4
  23. package/api/src/routes/customers.ts +10 -6
  24. package/api/src/routes/index.ts +2 -0
  25. package/api/src/routes/invoices.ts +10 -1
  26. package/api/src/routes/meter-events.ts +1 -1
  27. package/api/src/routes/meters.ts +1 -1
  28. package/api/src/routes/payment-currencies.ts +129 -0
  29. package/api/src/store/migrate.ts +20 -0
  30. package/api/src/store/migrations/20250821-auto-recharge-config.ts +38 -0
  31. package/api/src/store/models/auto-recharge-config.ts +225 -0
  32. package/api/src/store/models/credit-grant.ts +2 -11
  33. package/api/src/store/models/customer.ts +1 -0
  34. package/api/src/store/models/index.ts +3 -0
  35. package/api/src/store/models/invoice.ts +2 -1
  36. package/api/src/store/models/payment-currency.ts +10 -2
  37. package/api/src/store/models/types.ts +12 -1
  38. package/blocklet.yml +3 -3
  39. package/package.json +18 -18
  40. package/src/components/currency.tsx +3 -1
  41. package/src/components/customer/credit-overview.tsx +103 -18
  42. package/src/components/customer/overdraft-protection.tsx +5 -5
  43. package/src/components/info-metric.tsx +11 -2
  44. package/src/components/invoice/recharge.tsx +8 -2
  45. package/src/components/metadata/form.tsx +29 -27
  46. package/src/components/meter/form.tsx +1 -2
  47. package/src/components/price/form.tsx +39 -26
  48. package/src/components/product/form.tsx +1 -2
  49. package/src/components/subscription/items/index.tsx +8 -2
  50. package/src/components/subscription/metrics.tsx +5 -1
  51. package/src/locales/en.tsx +15 -0
  52. package/src/locales/zh.tsx +14 -0
  53. package/src/pages/admin/billing/meters/detail.tsx +18 -0
  54. package/src/pages/admin/customers/customers/credit-grant/detail.tsx +10 -0
  55. package/src/pages/admin/products/prices/actions.tsx +42 -2
  56. package/src/pages/admin/products/products/create.tsx +1 -2
  57. package/src/pages/admin/settings/vault-config/edit-form.tsx +8 -8
  58. package/src/pages/customer/credit-grant/detail.tsx +9 -1
  59. package/src/pages/customer/recharge/account.tsx +14 -7
  60. package/src/pages/customer/recharge/subscription.tsx +4 -4
  61. package/src/pages/customer/subscription/detail.tsx +6 -1
  62. package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +0 -151
@@ -1,5 +1,5 @@
1
1
  import React, { useState, useEffect, useRef } from 'react';
2
- import { useParams, useNavigate } from 'react-router-dom';
2
+ import { useParams, useNavigate, useSearchParams } from 'react-router-dom';
3
3
  import Toast from '@arcblock/ux/lib/Toast';
4
4
  import {
5
5
  Box,
@@ -77,6 +77,8 @@ const BalanceCard = styled(Card)(({ theme }) => ({
77
77
  export default function BalanceRechargePage() {
78
78
  const { t, locale } = useLocaleContext();
79
79
  const { currencyId } = useParams<{ currencyId: string }>();
80
+ const [searchParams] = useSearchParams();
81
+ const rechargeAddress = searchParams.get('rechargeAddress');
80
82
  const navigate = useNavigate();
81
83
  const { connect } = usePaymentContext();
82
84
  const [amount, setAmount] = useState('50');
@@ -103,6 +105,8 @@ export default function BalanceRechargePage() {
103
105
  const [currency, setCurrency] = useState<ExtendedPaymentCurrency | null>(null);
104
106
  const [relatedSubscriptions, setRelatedSubscriptions] = useState<Subscription[]>([]);
105
107
 
108
+ const paymentAddress = rechargeAddress || payerValue?.paymentAddress || session?.user?.did;
109
+
106
110
  const fetchData = async () => {
107
111
  try {
108
112
  setLoading(true);
@@ -174,9 +178,11 @@ export default function BalanceRechargePage() {
174
178
  ]);
175
179
  }
176
180
 
177
- const supportRecharge = data.currency?.paymentMethod?.type === 'arcblock';
181
+ const supportRecharge = ['arcblock', 'ethereum', 'base'].includes(data.currency?.paymentMethod?.type);
178
182
  if (supportRecharge) {
179
- const payerTokenRes = await api.get(`/api/customers/payer-token?currencyId=${currencyId}`);
183
+ const payerTokenRes = await api.get(
184
+ `/api/customers/payer-token?currencyId=${currencyId}&payerAddress=${paymentAddress}`
185
+ );
180
186
  if (payerTokenRes?.data) {
181
187
  setPayerValue(payerTokenRes.data);
182
188
  }
@@ -231,6 +237,7 @@ export default function BalanceRechargePage() {
231
237
  customerDid: session?.user?.did,
232
238
  currencyId: currency.id,
233
239
  amount: Number(amount),
240
+ rechargeAddress: rechargeAddress || session?.user?.did,
234
241
  },
235
242
  messages: {
236
243
  scan: t('common.connect.defaultScan'),
@@ -291,7 +298,7 @@ export default function BalanceRechargePage() {
291
298
  );
292
299
  }
293
300
 
294
- if (currency?.paymentMethod?.type !== 'arcblock') {
301
+ if (!['arcblock', 'ethereum', 'base'].includes(currency?.paymentMethod?.type || '')) {
295
302
  return (
296
303
  <Box>
297
304
  <Button startIcon={<ArrowBackOutlined />} variant="outlined" onClick={() => goBackOrFallback('/customer')}>
@@ -317,7 +324,7 @@ export default function BalanceRechargePage() {
317
324
 
318
325
  const currentBalance = formatBNStr(payerValue?.token || '0', currency?.decimal || 0, 6, false);
319
326
  const balanceLink = currency?.paymentMethod
320
- ? getTokenBalanceLink(currency.paymentMethod, payerValue?.paymentAddress || '')
327
+ ? getTokenBalanceLink(currency.paymentMethod, paymentAddress || '')
321
328
  : undefined;
322
329
 
323
330
  return (
@@ -362,7 +369,7 @@ export default function BalanceRechargePage() {
362
369
  {t('customer.recharge.receiveAddress')}
363
370
  </Typography>
364
371
  <Typography variant="body1" sx={{ wordBreak: 'break-all', fontFamily: 'monospace' }}>
365
- {payerValue?.paymentAddress || t('customer.balance.addressNotFound')}
372
+ {paymentAddress || t('customer.balance.addressNotFound')}
366
373
  </Typography>
367
374
  </Stack>
368
375
  {currency.logo && (
@@ -669,7 +676,7 @@ export default function BalanceRechargePage() {
669
676
  {t('customer.recharge.history')}
670
677
  </Typography>
671
678
 
672
- <RechargeList currency_id={currencyId} />
679
+ <RechargeList currency_id={currencyId} customer_id={session?.user?.did} recharge_address={paymentAddress} />
673
680
  </Box>
674
681
  )}
675
682
  </Root>
@@ -537,11 +537,11 @@ export default function RechargePage() {
537
537
  slotProps={{
538
538
  input: {
539
539
  endAdornment: <Typography>{subscription.paymentCurrency.symbol}</Typography>,
540
+ },
541
+ htmlInput: {
542
+ min: 0,
543
+ max: MAX_SAFE_AMOUNT,
540
544
  autoComplete: 'off',
541
- inputProps: {
542
- min: 0,
543
- max: MAX_SAFE_AMOUNT,
544
- },
545
545
  },
546
546
  }}
547
547
  />
@@ -455,7 +455,12 @@ export default function CustomerSubscriptionDetail() {
455
455
  sx={{
456
456
  alignItems: 'center',
457
457
  }}>
458
- <Typography component="span" fontSize={14} fontWeight={500}>
458
+ <Typography
459
+ component="span"
460
+ sx={{
461
+ fontSize: 14,
462
+ fontWeight: 500,
463
+ }}>
459
464
  {t('customer.overdraftProtection.title')}
460
465
  </Typography>
461
466
  <MuiLink
@@ -1,151 +0,0 @@
1
- /* eslint-disable @typescript-eslint/brace-style */
2
- /* eslint-disable @typescript-eslint/indent */
3
- import { BN, fromUnitToToken } from '@ocap/util';
4
- import { getUserLocale } from '../../../integrations/blocklet/notification';
5
- import { translate } from '../../../locales';
6
- import { CreditGrant, Customer, PaymentCurrency } from '../../../store/models';
7
- import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
8
- import { formatNumber, getCustomerIndexUrl } from '../../util';
9
-
10
- export interface CustomerCreditGrantLowBalanceEmailTemplateOptions {
11
- creditGrantId: string;
12
- }
13
-
14
- interface CustomerCreditGrantLowBalanceEmailTemplateContext {
15
- locale: string;
16
- userDid: string;
17
- currencySymbol: string;
18
- availableAmount: string;
19
- totalGrantedAmount: string;
20
- lowBalancePercentage: string;
21
- }
22
-
23
- export class CustomerCreditGrantLowBalanceEmailTemplate
24
- implements BaseEmailTemplate<CustomerCreditGrantLowBalanceEmailTemplateContext>
25
- {
26
- options: CustomerCreditGrantLowBalanceEmailTemplateOptions;
27
-
28
- constructor(options: CustomerCreditGrantLowBalanceEmailTemplateOptions) {
29
- this.options = options;
30
- }
31
-
32
- async getContext(): Promise<CustomerCreditGrantLowBalanceEmailTemplateContext> {
33
- const creditGrant = await CreditGrant.findByPk(this.options.creditGrantId);
34
- if (!creditGrant) {
35
- throw new Error(`CreditGrant not found: ${this.options.creditGrantId}`);
36
- }
37
-
38
- const customer = await Customer.findByPk(creditGrant.customer_id);
39
- if (!customer) {
40
- throw new Error(`Customer not found: ${creditGrant.customer_id}`);
41
- }
42
-
43
- const paymentCurrency = await PaymentCurrency.findByPk(creditGrant.currency_id);
44
- if (!paymentCurrency) {
45
- throw new Error(`PaymentCurrency not found: ${creditGrant.currency_id}`);
46
- }
47
-
48
- const userDid = customer.did;
49
- const locale = await getUserLocale(userDid);
50
- const currencySymbol = paymentCurrency.symbol;
51
-
52
- // 计算百分比
53
- const available = new BN(creditGrant.remaining_amount);
54
- const total = new BN(creditGrant.amount);
55
- const percentage = total.gt(new BN(0)) ? available.mul(new BN(100)).div(total).toString() : '0';
56
-
57
- return {
58
- locale,
59
- userDid,
60
- currencySymbol,
61
- availableAmount: `${formatNumber(fromUnitToToken(available.toString(), paymentCurrency.decimal))} ${currencySymbol}`,
62
- totalGrantedAmount: `${formatNumber(fromUnitToToken(total.toString(), paymentCurrency.decimal))} ${currencySymbol}`,
63
- lowBalancePercentage: `${percentage}%`,
64
- };
65
- }
66
-
67
- async getTemplate(): Promise<BaseEmailTemplateType> {
68
- const context = await this.getContext();
69
- const { locale, userDid, availableAmount, totalGrantedAmount, lowBalancePercentage } = context;
70
-
71
- // 构建字段
72
- const fields = [
73
- {
74
- type: 'text',
75
- data: {
76
- type: 'plain',
77
- color: '#9397A1',
78
- text: translate('notification.common.account', locale),
79
- },
80
- },
81
- {
82
- type: 'text',
83
- data: {
84
- type: 'plain',
85
- text: userDid,
86
- },
87
- },
88
- {
89
- type: 'text',
90
- data: {
91
- type: 'plain',
92
- color: '#9397A1',
93
- text: translate('notification.creditInsufficient.availableCredit', locale),
94
- },
95
- },
96
- {
97
- type: 'text',
98
- data: {
99
- type: 'plain',
100
- color: '#FF6600', // 橙色警告
101
- text: `${availableAmount} (${lowBalancePercentage})`,
102
- },
103
- },
104
- {
105
- type: 'text',
106
- data: {
107
- type: 'plain',
108
- color: '#9397A1',
109
- text: translate('notification.creditGrantLowBalance.totalGrantedCredit', locale),
110
- },
111
- },
112
- {
113
- type: 'text',
114
- data: {
115
- type: 'plain',
116
- text: `${totalGrantedAmount}`,
117
- },
118
- },
119
- ];
120
-
121
- // 构建操作按钮
122
- const actions = [
123
- {
124
- name: translate('notification.common.viewCreditGrant', locale),
125
- title: translate('notification.common.viewCreditGrant', locale),
126
- link: getCustomerIndexUrl({
127
- locale,
128
- userDid,
129
- }),
130
- },
131
- ];
132
-
133
- const template: BaseEmailTemplateType = {
134
- title: translate('notification.creditGrantLowBalance.title', locale),
135
- body: translate('notification.creditGrantLowBalance.body', locale, {
136
- availableAmount,
137
- totalGrantedAmount,
138
- }),
139
- attachments: [
140
- {
141
- type: 'section',
142
- fields,
143
- },
144
- ],
145
- // @ts-ignore
146
- actions,
147
- };
148
-
149
- return template;
150
- }
151
- }