payment-kit 1.19.1 → 1.19.3
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/libs/notification/template/customer-credit-grant-low-balance.ts +1 -1
- package/api/src/libs/notification/template/customer-credit-insufficient.ts +1 -1
- package/api/src/libs/security.ts +6 -3
- package/api/src/libs/util.ts +3 -1
- package/api/src/queues/credit-consume.ts +15 -2
- package/api/src/routes/checkout-sessions.ts +9 -4
- package/api/src/routes/credit-grants.ts +24 -2
- package/api/src/routes/customers.ts +36 -12
- package/api/src/routes/payment-currencies.ts +8 -0
- package/api/src/routes/payment-methods.ts +1 -0
- package/api/src/routes/webhook-endpoints.ts +0 -3
- package/api/src/store/migrations/20250610-billing-credit.ts +0 -3
- package/api/src/store/migrations/20250708-currency-precision.ts +14 -0
- package/api/src/store/models/webhook-attempt.ts +1 -1
- package/blocklet.yml +1 -1
- package/package.json +25 -25
- package/src/components/conditional-section.tsx +87 -0
- package/src/components/customer/credit-overview.tsx +30 -17
- package/src/components/customer/form.tsx +2 -1
- package/src/components/edit-in-line.tsx +197 -0
- package/src/components/metadata/form.tsx +2 -2
- package/src/components/meter/add-usage-dialog.tsx +2 -2
- package/src/components/meter/form.tsx +2 -2
- package/src/components/meter/products.tsx +2 -2
- package/src/components/payment-link/item.tsx +2 -2
- package/src/components/payouts/portal/list.tsx +6 -11
- package/src/components/price/currency-select.tsx +13 -9
- package/src/components/price/form.tsx +47 -16
- package/src/components/product/form.tsx +3 -8
- package/src/components/subscription/portal/list.tsx +0 -1
- package/src/locales/en.tsx +6 -3
- package/src/locales/zh.tsx +6 -3
- package/src/pages/admin/customers/customers/detail.tsx +5 -13
- package/src/pages/admin/settings/payment-methods/index.tsx +56 -85
- package/src/pages/customer/index.tsx +17 -15
- package/src/pages/customer/recharge/account.tsx +1 -1
package/src/locales/zh.tsx
CHANGED
|
@@ -361,7 +361,7 @@ export default flat({
|
|
|
361
361
|
},
|
|
362
362
|
unit_label: {
|
|
363
363
|
label: '单位标签',
|
|
364
|
-
placeholder: '
|
|
364
|
+
placeholder: '单位',
|
|
365
365
|
},
|
|
366
366
|
billingType: {
|
|
367
367
|
label: '计费类型',
|
|
@@ -424,6 +424,7 @@ export default flat({
|
|
|
424
424
|
lookup_key: {
|
|
425
425
|
label: '查找键',
|
|
426
426
|
placeholder: '',
|
|
427
|
+
description: '查找键用于在API中识别价格',
|
|
427
428
|
},
|
|
428
429
|
recurring: {
|
|
429
430
|
interval: '计费周期',
|
|
@@ -495,6 +496,7 @@ export default flat({
|
|
|
495
496
|
format: '可售{num}件',
|
|
496
497
|
noLimit: '不限制可售数量',
|
|
497
498
|
valid: '可售数量不得少于已售数量',
|
|
499
|
+
description: '输入可售数量,0表示不限制可售数量',
|
|
498
500
|
},
|
|
499
501
|
quantitySold: {
|
|
500
502
|
label: '已售数量',
|
|
@@ -505,6 +507,7 @@ export default flat({
|
|
|
505
507
|
placeholder: '0表示无限制',
|
|
506
508
|
format: '单次最多购买{num}件',
|
|
507
509
|
noLimit: '不限制单次购买数量',
|
|
510
|
+
description: '输入限制单次购买的最大数量, 0表示无限制',
|
|
508
511
|
},
|
|
509
512
|
inventory: '库存设置',
|
|
510
513
|
},
|
|
@@ -945,7 +948,7 @@ export default flat({
|
|
|
945
948
|
totalAmount: '总额度',
|
|
946
949
|
pendingAmount: '欠费额度',
|
|
947
950
|
grantCount: '额度数量',
|
|
948
|
-
noGrantsDescription: '
|
|
951
|
+
noGrantsDescription: '您还没有任何信用额度',
|
|
949
952
|
status: {
|
|
950
953
|
granted: '生效中',
|
|
951
954
|
pending: '待生效',
|
|
@@ -1121,7 +1124,7 @@ export default flat({
|
|
|
1121
1124
|
totalAmount: '总额度',
|
|
1122
1125
|
pendingAmount: '欠费额度',
|
|
1123
1126
|
grantCount: '额度数量',
|
|
1124
|
-
noGrantsDescription: '
|
|
1127
|
+
noGrantsDescription: '您还没有任何信用额度',
|
|
1125
1128
|
status: {
|
|
1126
1129
|
granted: '生效中',
|
|
1127
1130
|
pending: '待生效',
|
|
@@ -42,10 +42,6 @@ const fetchData = async (
|
|
|
42
42
|
): Promise<{
|
|
43
43
|
customer: TCustomerExpanded;
|
|
44
44
|
summary: { [key: string]: GroupedBN };
|
|
45
|
-
creditSummary?: {
|
|
46
|
-
grants?: { [currencyId: string]: { totalAmount: string; remainingAmount: string } };
|
|
47
|
-
pendingAmount?: { [currencyId: string]: string };
|
|
48
|
-
} | null;
|
|
49
45
|
}> => {
|
|
50
46
|
const [customer, summary] = await Promise.all([
|
|
51
47
|
api.get(`/api/customers/${id}`).then((res) => res.data),
|
|
@@ -311,15 +307,11 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
311
307
|
}}>
|
|
312
308
|
<Box className="payment-link-column-1" sx={{ flex: 1, gap: 2.5, display: 'flex', flexDirection: 'column' }}>
|
|
313
309
|
{/* 信用管理区域 */}
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
</Box>
|
|
320
|
-
<Divider />
|
|
321
|
-
</>
|
|
322
|
-
)}
|
|
310
|
+
<Box className="section">
|
|
311
|
+
<SectionHeader title={t('admin.creditGrants.title')} mb={0} />
|
|
312
|
+
<CreditOverview customerId={props.id} settings={settings} mode="dashboard" />
|
|
313
|
+
</Box>
|
|
314
|
+
<Divider />
|
|
323
315
|
|
|
324
316
|
<Box className="section" sx={{ containerType: 'inline-size' }}>
|
|
325
317
|
<SectionHeader title={t('admin.details')} />
|
|
@@ -3,8 +3,6 @@ import { ConfirmDialog, Switch, api, formatError, usePaymentContext } from '@blo
|
|
|
3
3
|
import type { ChainType, TPaymentCurrency, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
4
4
|
import {
|
|
5
5
|
AddOutlined,
|
|
6
|
-
Check,
|
|
7
|
-
Close,
|
|
8
6
|
DeleteOutlined,
|
|
9
7
|
EditOutlined,
|
|
10
8
|
InfoOutlined,
|
|
@@ -27,7 +25,6 @@ import {
|
|
|
27
25
|
ListItemText,
|
|
28
26
|
Skeleton,
|
|
29
27
|
Stack,
|
|
30
|
-
TextField,
|
|
31
28
|
Tooltip,
|
|
32
29
|
Typography,
|
|
33
30
|
useTheme,
|
|
@@ -35,7 +32,6 @@ import {
|
|
|
35
32
|
import { useRequest, useSessionStorageState, useSetState } from 'ahooks';
|
|
36
33
|
import useBus from 'use-bus';
|
|
37
34
|
|
|
38
|
-
import { useState } from 'react';
|
|
39
35
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
40
36
|
import { DIDDialog } from '@arcblock/ux/lib/DID';
|
|
41
37
|
import { fromUnitToToken } from '@ocap/util';
|
|
@@ -45,6 +41,7 @@ import InfoCard from '../../../../components/info-card';
|
|
|
45
41
|
import InfoRow from '../../../../components/info-row';
|
|
46
42
|
import PaymentCurrencyAdd from '../../../../components/payment-currency/add';
|
|
47
43
|
import PaymentCurrencyEdit from '../../../../components/payment-currency/edit';
|
|
44
|
+
import EditInLine from '../../../../components/edit-in-line';
|
|
48
45
|
import { useRpcStatus } from '../../../../hooks/rpc-status';
|
|
49
46
|
import PaymentMethodEdit from './edit';
|
|
50
47
|
|
|
@@ -62,94 +59,51 @@ const getMethods = (
|
|
|
62
59
|
return api.get(`/api/payment-methods?${search.toString()}`).then((res) => res.data);
|
|
63
60
|
};
|
|
64
61
|
|
|
65
|
-
const updateApiHost = (id: string,
|
|
66
|
-
return api.put(`/api/payment-methods/${id}/settings`, { arcblock:
|
|
62
|
+
const updateApiHost = (id: string, params: Record<string, any>) => {
|
|
63
|
+
return api.put(`/api/payment-methods/${id}/settings`, { arcblock: params }).then((res) => res.data);
|
|
67
64
|
};
|
|
68
65
|
|
|
69
|
-
function EditApiHost({ method }: { method: TPaymentMethodExpanded }) {
|
|
70
|
-
const [edit, setEdit] = useState(false);
|
|
66
|
+
function EditApiHost({ method, refresh }: { method: TPaymentMethodExpanded; refresh: () => void }) {
|
|
71
67
|
const { t } = useLocaleContext();
|
|
72
|
-
const [value, setValue] = useState(method.settings?.arcblock?.api_host || '');
|
|
73
|
-
const [tempValue, setTempValue] = useState(value);
|
|
74
|
-
const [error, setError] = useState('');
|
|
75
68
|
|
|
76
|
-
const handleSave = async () => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
try {
|
|
82
|
-
await updateApiHost(method.id, tempValue);
|
|
83
|
-
Toast.success(t('common.saved'));
|
|
84
|
-
setValue(tempValue);
|
|
85
|
-
setEdit(false);
|
|
86
|
-
setError('');
|
|
87
|
-
} catch (err) {
|
|
88
|
-
Toast.error(formatError(err));
|
|
89
|
-
}
|
|
69
|
+
const handleSave = async (newValue: string) => {
|
|
70
|
+
await updateApiHost(method.id, { api_host: newValue });
|
|
71
|
+
refresh();
|
|
90
72
|
};
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
74
|
+
return (
|
|
75
|
+
<InfoRow
|
|
76
|
+
label={t('admin.paymentMethod.arcblock.api_host.label')}
|
|
77
|
+
value={
|
|
78
|
+
<EditInLine
|
|
79
|
+
value={method.settings?.arcblock?.api_host || ''}
|
|
80
|
+
onSave={handleSave}
|
|
81
|
+
placeholder={t('admin.paymentMethod.arcblock.api_host.tip')}
|
|
82
|
+
required
|
|
83
|
+
/>
|
|
84
|
+
}
|
|
85
|
+
/>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function EditApiExplorerHost({ method, refresh }: { method: TPaymentMethodExpanded; refresh: () => void }) {
|
|
90
|
+
const { t } = useLocaleContext();
|
|
91
|
+
|
|
92
|
+
const handleSave = async (newValue: string) => {
|
|
93
|
+
await updateApiHost(method.id, { explorer_host: newValue });
|
|
94
|
+
refresh();
|
|
96
95
|
};
|
|
97
96
|
|
|
98
97
|
return (
|
|
99
98
|
<InfoRow
|
|
100
|
-
label={t('admin.paymentMethod.arcblock.
|
|
99
|
+
label={t('admin.paymentMethod.arcblock.explorer_host.label')}
|
|
101
100
|
value={
|
|
102
|
-
<
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}}>
|
|
109
|
-
{edit ? (
|
|
110
|
-
<>
|
|
111
|
-
<TextField
|
|
112
|
-
value={tempValue}
|
|
113
|
-
onChange={(e) => {
|
|
114
|
-
const val = e.target.value;
|
|
115
|
-
if (!val) {
|
|
116
|
-
setError(t('common.required'));
|
|
117
|
-
} else {
|
|
118
|
-
setError('');
|
|
119
|
-
}
|
|
120
|
-
setTempValue(val);
|
|
121
|
-
}}
|
|
122
|
-
variant="outlined"
|
|
123
|
-
size="small"
|
|
124
|
-
sx={{ flex: 1 }}
|
|
125
|
-
placeholder={t('admin.paymentMethod.arcblock.api_host.tip')}
|
|
126
|
-
error={!!error}
|
|
127
|
-
slotProps={{
|
|
128
|
-
input: {
|
|
129
|
-
endAdornment: error ? (
|
|
130
|
-
<Typography color="error" sx={{ whiteSpace: 'nowrap' }}>
|
|
131
|
-
{error}
|
|
132
|
-
</Typography>
|
|
133
|
-
) : undefined,
|
|
134
|
-
},
|
|
135
|
-
}}
|
|
136
|
-
/>
|
|
137
|
-
<IconButton onClick={handleSave} size="small">
|
|
138
|
-
<Check />
|
|
139
|
-
</IconButton>
|
|
140
|
-
<IconButton onClick={handleCancel} size="small">
|
|
141
|
-
<Close />
|
|
142
|
-
</IconButton>
|
|
143
|
-
</>
|
|
144
|
-
) : (
|
|
145
|
-
<>
|
|
146
|
-
<Typography variant="body2">{value}</Typography>
|
|
147
|
-
<IconButton onClick={() => setEdit(true)} size="small">
|
|
148
|
-
<EditOutlined fontSize="small" />
|
|
149
|
-
</IconButton>
|
|
150
|
-
</>
|
|
151
|
-
)}
|
|
152
|
-
</Stack>
|
|
101
|
+
<EditInLine
|
|
102
|
+
value={method.settings?.arcblock?.explorer_host || ''}
|
|
103
|
+
onSave={handleSave}
|
|
104
|
+
placeholder={t('admin.paymentMethod.arcblock.explorer_host.tip')}
|
|
105
|
+
required
|
|
106
|
+
/>
|
|
153
107
|
}
|
|
154
108
|
/>
|
|
155
109
|
);
|
|
@@ -219,11 +173,13 @@ function Balance({
|
|
|
219
173
|
balances,
|
|
220
174
|
addresses,
|
|
221
175
|
setDidDialog,
|
|
176
|
+
refresh,
|
|
222
177
|
}: {
|
|
223
178
|
method: TPaymentMethodExpanded;
|
|
224
179
|
balances: { [currencyId: string]: string };
|
|
225
180
|
addresses: { arcblock: string; ethereum: string };
|
|
226
181
|
setDidDialog: (value: any) => void;
|
|
182
|
+
refresh: () => void;
|
|
227
183
|
}) {
|
|
228
184
|
const { t } = useLocaleContext();
|
|
229
185
|
const defaultCurrency = (method.payment_currencies || [])?.find(
|
|
@@ -252,7 +208,11 @@ function Balance({
|
|
|
252
208
|
};
|
|
253
209
|
return (
|
|
254
210
|
<>
|
|
255
|
-
|
|
211
|
+
{method.type === 'arcblock' ? (
|
|
212
|
+
<EditApiExplorerHost method={method} refresh={refresh} />
|
|
213
|
+
) : (
|
|
214
|
+
<InfoRow label={t('admin.paymentMethod.props.explorer_host')} value={explorerHost} />
|
|
215
|
+
)}
|
|
256
216
|
<InfoRow
|
|
257
217
|
label={
|
|
258
218
|
<Box
|
|
@@ -461,6 +421,11 @@ export default function PaymentMethods() {
|
|
|
461
421
|
}
|
|
462
422
|
};
|
|
463
423
|
|
|
424
|
+
const handleRefresh = () => {
|
|
425
|
+
runAsync();
|
|
426
|
+
refresh(true);
|
|
427
|
+
};
|
|
428
|
+
|
|
464
429
|
return (
|
|
465
430
|
<>
|
|
466
431
|
{Object.keys(groups).map((x) => (
|
|
@@ -513,7 +478,7 @@ export default function PaymentMethods() {
|
|
|
513
478
|
container
|
|
514
479
|
spacing={2}
|
|
515
480
|
sx={{
|
|
516
|
-
mt:
|
|
481
|
+
mt: 1,
|
|
517
482
|
}}>
|
|
518
483
|
<Grid
|
|
519
484
|
size={{
|
|
@@ -521,10 +486,16 @@ export default function PaymentMethods() {
|
|
|
521
486
|
md: 6,
|
|
522
487
|
}}>
|
|
523
488
|
<InfoRow label={t('admin.paymentMethod.props.type')} value={method.type} />
|
|
524
|
-
{method.type === 'arcblock' && <EditApiHost method={method} />}
|
|
489
|
+
{method.type === 'arcblock' && <EditApiHost method={method} refresh={handleRefresh} />}
|
|
525
490
|
{['ethereum', 'base'].includes(method.type) && <RpcStatus method={method} />}
|
|
526
491
|
{['arcblock', 'ethereum', 'base'].includes(method.type) && (
|
|
527
|
-
<Balance
|
|
492
|
+
<Balance
|
|
493
|
+
method={method}
|
|
494
|
+
balances={balances}
|
|
495
|
+
addresses={addresses}
|
|
496
|
+
setDidDialog={setDidDialog}
|
|
497
|
+
refresh={handleRefresh}
|
|
498
|
+
/>
|
|
528
499
|
)}
|
|
529
500
|
|
|
530
501
|
<InfoRow label={t('admin.paymentMethod.props.confirmation')} value={method.confirmation.type} />
|
|
@@ -40,6 +40,7 @@ import { memo, useEffect, useState } from 'react';
|
|
|
40
40
|
import { useNavigate, useSearchParams } from 'react-router-dom';
|
|
41
41
|
import { joinURL } from 'ufo';
|
|
42
42
|
import CreditOverview from '../../components/customer/credit-overview';
|
|
43
|
+
import ConditionalSection from '../../components/conditional-section';
|
|
43
44
|
|
|
44
45
|
import { useTransitionContext } from '../../components/progress-bar';
|
|
45
46
|
import CurrentSubscriptions from '../../components/subscription/portal/list';
|
|
@@ -214,7 +215,6 @@ export default function CustomerHome() {
|
|
|
214
215
|
const navigate = useNavigate();
|
|
215
216
|
const [subscriptionStatus, setSubscriptionStatus] = useState(false);
|
|
216
217
|
const [hasSubscriptions, setHasSubscriptions] = useState(false);
|
|
217
|
-
const [hasRevenues, setHasRevenues] = useState(false);
|
|
218
218
|
const { startTransition } = useTransitionContext();
|
|
219
219
|
const {
|
|
220
220
|
data,
|
|
@@ -480,15 +480,15 @@ export default function CustomerHome() {
|
|
|
480
480
|
);
|
|
481
481
|
|
|
482
482
|
// 独立的Credit Card组件
|
|
483
|
-
const CreditCard =
|
|
484
|
-
<
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
483
|
+
const CreditCard = (
|
|
484
|
+
<ConditionalSection skeleton={loadingCard}>
|
|
485
|
+
<Box className="base-card section section-credit">
|
|
486
|
+
<Box className="section-header" sx={{ mb: 2 }}>
|
|
487
|
+
<Typography variant="h3">{t('admin.creditGrants.title')}</Typography>
|
|
488
|
+
</Box>
|
|
489
|
+
<CreditOverview customerId={data?.id || ''} settings={settings} />
|
|
489
490
|
</Box>
|
|
490
|
-
|
|
491
|
-
</Box>
|
|
491
|
+
</ConditionalSection>
|
|
492
492
|
);
|
|
493
493
|
|
|
494
494
|
const InvoiceCard = loadingCard ? (
|
|
@@ -519,13 +519,15 @@ export default function CustomerHome() {
|
|
|
519
519
|
</Box>
|
|
520
520
|
);
|
|
521
521
|
|
|
522
|
-
const RevenueCard =
|
|
523
|
-
<
|
|
524
|
-
<Box className="section-
|
|
525
|
-
<
|
|
522
|
+
const RevenueCard = (
|
|
523
|
+
<ConditionalSection skeleton={loadingCard}>
|
|
524
|
+
<Box className="base-card section section-revenue">
|
|
525
|
+
<Box className="section-header">
|
|
526
|
+
<Typography variant="h3">{t('customer.payout.title')}</Typography>
|
|
527
|
+
</Box>
|
|
528
|
+
<CustomerRevenueList />
|
|
526
529
|
</Box>
|
|
527
|
-
|
|
528
|
-
</Box>
|
|
530
|
+
</ConditionalSection>
|
|
529
531
|
);
|
|
530
532
|
|
|
531
533
|
return (
|
|
@@ -260,7 +260,7 @@ export default function BalanceRechargePage() {
|
|
|
260
260
|
if (Number(value) > MAX_SAFE_AMOUNT) {
|
|
261
261
|
return;
|
|
262
262
|
}
|
|
263
|
-
const precision = currency.
|
|
263
|
+
const precision = currency.maximum_precision;
|
|
264
264
|
const errorMessage = formatAmountPrecisionLimit(value, locale, precision || 6);
|
|
265
265
|
setAmountError(errorMessage || '');
|
|
266
266
|
setAmount(value);
|