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.
- package/api/src/index.ts +3 -1
- package/api/src/integrations/ethereum/tx.ts +11 -0
- package/api/src/integrations/stripe/handlers/invoice.ts +26 -6
- package/api/src/integrations/stripe/handlers/setup-intent.ts +34 -2
- package/api/src/integrations/stripe/resource.ts +185 -1
- package/api/src/libs/invoice.ts +2 -1
- package/api/src/libs/notification/template/customer-credit-low-balance.ts +155 -0
- package/api/src/libs/session.ts +6 -1
- package/api/src/libs/ws.ts +3 -2
- package/api/src/locales/en.ts +6 -6
- package/api/src/locales/zh.ts +4 -4
- package/api/src/queues/auto-recharge.ts +343 -0
- package/api/src/queues/credit-consume.ts +51 -1
- package/api/src/queues/credit-grant.ts +15 -0
- package/api/src/queues/notification.ts +16 -13
- package/api/src/queues/payment.ts +14 -1
- package/api/src/queues/space.ts +1 -0
- package/api/src/routes/auto-recharge-configs.ts +454 -0
- package/api/src/routes/connect/auto-recharge-auth.ts +182 -0
- package/api/src/routes/connect/recharge-account.ts +72 -10
- package/api/src/routes/connect/setup.ts +5 -3
- package/api/src/routes/connect/shared.ts +45 -4
- package/api/src/routes/customers.ts +10 -6
- package/api/src/routes/index.ts +2 -0
- package/api/src/routes/invoices.ts +10 -1
- package/api/src/routes/meter-events.ts +1 -1
- package/api/src/routes/meters.ts +1 -1
- package/api/src/routes/payment-currencies.ts +129 -0
- package/api/src/store/migrate.ts +20 -0
- package/api/src/store/migrations/20250821-auto-recharge-config.ts +38 -0
- package/api/src/store/models/auto-recharge-config.ts +225 -0
- package/api/src/store/models/credit-grant.ts +2 -11
- package/api/src/store/models/customer.ts +1 -0
- package/api/src/store/models/index.ts +3 -0
- package/api/src/store/models/invoice.ts +2 -1
- package/api/src/store/models/payment-currency.ts +10 -2
- package/api/src/store/models/types.ts +12 -1
- package/blocklet.yml +3 -3
- package/package.json +18 -18
- package/src/components/currency.tsx +3 -1
- package/src/components/customer/credit-overview.tsx +103 -18
- package/src/components/customer/overdraft-protection.tsx +5 -5
- package/src/components/info-metric.tsx +11 -2
- package/src/components/invoice/recharge.tsx +8 -2
- package/src/components/metadata/form.tsx +29 -27
- package/src/components/meter/form.tsx +1 -2
- package/src/components/price/form.tsx +39 -26
- package/src/components/product/form.tsx +1 -2
- package/src/components/subscription/items/index.tsx +8 -2
- package/src/components/subscription/metrics.tsx +5 -1
- package/src/locales/en.tsx +15 -0
- package/src/locales/zh.tsx +14 -0
- package/src/pages/admin/billing/meters/detail.tsx +18 -0
- package/src/pages/admin/customers/customers/credit-grant/detail.tsx +10 -0
- package/src/pages/admin/products/prices/actions.tsx +42 -2
- package/src/pages/admin/products/products/create.tsx +1 -2
- package/src/pages/admin/settings/vault-config/edit-form.tsx +8 -8
- package/src/pages/customer/credit-grant/detail.tsx +9 -1
- package/src/pages/customer/recharge/account.tsx +14 -7
- package/src/pages/customer/recharge/subscription.tsx +4 -4
- package/src/pages/customer/subscription/detail.tsx +6 -1
- 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
|
|
181
|
+
const supportRecharge = ['arcblock', 'ethereum', 'base'].includes(data.currency?.paymentMethod?.type);
|
|
178
182
|
if (supportRecharge) {
|
|
179
|
-
const payerTokenRes = await api.get(
|
|
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
|
|
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,
|
|
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
|
-
{
|
|
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
|
|
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
|
-
}
|