payment-kit 1.20.8 → 1.20.10
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/crons/index.ts +1 -1
- package/api/src/index.ts +2 -2
- package/api/src/integrations/stripe/handlers/index.ts +10 -2
- package/api/src/libs/{vendor → vendor-util}/adapters/factory.ts +9 -5
- package/api/src/libs/{vendor → vendor-util}/adapters/launcher-adapter.ts +20 -18
- package/api/src/libs/{vendor → vendor-util}/adapters/types.ts +1 -4
- package/api/src/libs/{vendor → vendor-util}/fulfillment.ts +24 -127
- package/api/src/queues/payment.ts +1 -5
- package/api/src/queues/{vendor → vendors}/commission.ts +19 -18
- package/api/src/queues/{vendor → vendors}/fulfillment-coordinator.ts +35 -6
- package/api/src/queues/{vendor → vendors}/fulfillment.ts +2 -2
- package/api/src/queues/{vendor → vendors}/status-check.ts +13 -8
- package/api/src/routes/payment-links.ts +2 -1
- package/api/src/routes/products.ts +1 -0
- package/api/src/routes/vendor.ts +157 -216
- package/api/src/store/migrations/20250911-add-vendor-type.ts +26 -0
- package/api/src/store/migrations/20250916-add-vendor-did.ts +20 -0
- package/api/src/store/models/payout.ts +2 -2
- package/api/src/store/models/product-vendor.ts +11 -24
- package/api/src/store/models/product.ts +2 -0
- package/blocklet.yml +1 -1
- package/doc/vendor_fulfillment_system.md +1 -1
- package/package.json +5 -5
- package/src/components/metadata/form.tsx +12 -19
- package/src/components/payment-link/before-pay.tsx +40 -0
- package/src/components/product/vendor-config.tsx +4 -11
- package/src/components/subscription/description.tsx +1 -6
- package/src/components/subscription/portal/list.tsx +82 -6
- package/src/components/subscription/vendor-service-list.tsx +128 -0
- package/src/components/vendor/actions.tsx +1 -33
- package/src/locales/en.tsx +16 -3
- package/src/locales/zh.tsx +18 -5
- package/src/pages/admin/products/links/create.tsx +2 -0
- package/src/pages/admin/products/vendors/create.tsx +140 -190
- package/src/pages/admin/products/vendors/index.tsx +14 -22
- package/src/pages/customer/subscription/detail.tsx +26 -11
|
@@ -15,24 +15,23 @@ import {
|
|
|
15
15
|
MenuItem,
|
|
16
16
|
} from '@mui/material';
|
|
17
17
|
import { useState } from 'react';
|
|
18
|
-
import { useForm, Controller } from 'react-hook-form';
|
|
18
|
+
import { useForm, Controller, FormProvider } from 'react-hook-form';
|
|
19
19
|
import { dispatch } from 'use-bus';
|
|
20
20
|
|
|
21
21
|
import { joinURL, withQuery } from 'ufo';
|
|
22
22
|
import DrawerForm from '../../../../components/drawer-form';
|
|
23
|
+
import MetadataForm from '../../../../components/metadata/form';
|
|
23
24
|
import { formatProxyUrl } from '../../../../libs/util';
|
|
24
25
|
|
|
25
26
|
interface Vendor {
|
|
26
27
|
id: string;
|
|
27
28
|
vendor_key: string;
|
|
29
|
+
vendor_type: string;
|
|
28
30
|
name: string;
|
|
29
31
|
description: string;
|
|
30
32
|
app_url: string;
|
|
31
|
-
|
|
32
|
-
default_commission_rate: number;
|
|
33
|
-
default_commission_type: 'percentage' | 'fixed_amount';
|
|
33
|
+
vendor_did?: string;
|
|
34
34
|
status: 'active' | 'inactive';
|
|
35
|
-
order_create_params: Record<string, any>;
|
|
36
35
|
metadata: Record<string, any>;
|
|
37
36
|
created_at: string;
|
|
38
37
|
updated_at: string;
|
|
@@ -47,16 +46,13 @@ interface VendorCreateProps {
|
|
|
47
46
|
|
|
48
47
|
interface VendorFormData {
|
|
49
48
|
vendor_key: string;
|
|
49
|
+
vendor_type: string;
|
|
50
50
|
name: string;
|
|
51
51
|
description: string;
|
|
52
52
|
app_url: string;
|
|
53
|
-
|
|
54
|
-
blocklet_meta_url: string;
|
|
55
|
-
default_commission_rate: number;
|
|
56
|
-
default_commission_type: 'percentage' | 'fixed_amount';
|
|
53
|
+
vendor_did?: string;
|
|
57
54
|
status: 'active' | 'inactive';
|
|
58
|
-
|
|
59
|
-
metadata: Record<string, any>;
|
|
55
|
+
metadata: Array<{ key: string; value: string }>;
|
|
60
56
|
app_pid?: string;
|
|
61
57
|
app_logo?: string;
|
|
62
58
|
}
|
|
@@ -72,41 +68,44 @@ export default function VendorCreate({
|
|
|
72
68
|
|
|
73
69
|
const isEditMode = !!vendorData;
|
|
74
70
|
|
|
71
|
+
const defaultValues = vendorData
|
|
72
|
+
? {
|
|
73
|
+
...vendorData,
|
|
74
|
+
metadata: vendorData.metadata
|
|
75
|
+
? Object.entries(vendorData.metadata).map(([key, value]) => ({
|
|
76
|
+
key,
|
|
77
|
+
value: typeof value === 'object' ? JSON.stringify(value) : String(value),
|
|
78
|
+
}))
|
|
79
|
+
: [],
|
|
80
|
+
}
|
|
81
|
+
: {
|
|
82
|
+
vendor_key: '',
|
|
83
|
+
vendor_type: 'launcher',
|
|
84
|
+
name: '',
|
|
85
|
+
description: '',
|
|
86
|
+
app_url: '',
|
|
87
|
+
vendor_did: '',
|
|
88
|
+
status: 'inactive' as const,
|
|
89
|
+
metadata: [{ key: 'blockletMetaUrl', value: '' }],
|
|
90
|
+
app_pid: '',
|
|
91
|
+
app_logo: '',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const methods = useForm<VendorFormData>({
|
|
95
|
+
mode: 'onChange',
|
|
96
|
+
defaultValues,
|
|
97
|
+
});
|
|
98
|
+
|
|
75
99
|
const {
|
|
76
100
|
control,
|
|
77
101
|
handleSubmit,
|
|
78
102
|
reset,
|
|
79
103
|
formState: { errors, isValid },
|
|
80
104
|
watch,
|
|
81
|
-
} =
|
|
82
|
-
mode: 'onChange',
|
|
83
|
-
defaultValues: vendorData
|
|
84
|
-
? {
|
|
85
|
-
...vendorData,
|
|
86
|
-
default_commission_rate: vendorData.default_commission_rate,
|
|
87
|
-
blocklet_meta_url: vendorData.metadata?.blockletMetaUrl || '',
|
|
88
|
-
}
|
|
89
|
-
: {
|
|
90
|
-
vendor_key: '',
|
|
91
|
-
name: '',
|
|
92
|
-
description: '',
|
|
93
|
-
app_url: '',
|
|
94
|
-
webhook_path: '',
|
|
95
|
-
blocklet_meta_url: '',
|
|
96
|
-
default_commission_rate: 80,
|
|
97
|
-
default_commission_type: 'percentage',
|
|
98
|
-
status: 'inactive',
|
|
99
|
-
order_create_params: {},
|
|
100
|
-
metadata: {},
|
|
101
|
-
app_pid: '',
|
|
102
|
-
app_logo: '',
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
+
} = methods;
|
|
105
106
|
|
|
106
107
|
const watchedValues = watch();
|
|
107
108
|
|
|
108
|
-
const isPercentage = watchedValues.default_commission_type === 'percentage';
|
|
109
|
-
|
|
110
109
|
const validateUrl = (url: string) => {
|
|
111
110
|
if (!url) return t('admin.vendor.appUrlRequired');
|
|
112
111
|
try {
|
|
@@ -118,25 +117,16 @@ export default function VendorCreate({
|
|
|
118
117
|
}
|
|
119
118
|
};
|
|
120
119
|
|
|
121
|
-
const
|
|
122
|
-
if (!
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
const validateDid = (did: string | undefined) => {
|
|
121
|
+
if (!did) return true; // DID 是可选的
|
|
122
|
+
// DID 格式验证
|
|
123
|
+
const didPattern = /^(did:abt:)?[1-9A-HJ-NP-Za-km-z]{37}$/;
|
|
124
|
+
if (!didPattern.test(did.trim())) {
|
|
125
|
+
return t('admin.vendor.vendorDidInvalid');
|
|
125
126
|
}
|
|
126
127
|
return true;
|
|
127
128
|
};
|
|
128
129
|
|
|
129
|
-
const validateBlockletMetaUrl = (url: string) => {
|
|
130
|
-
if (!url) return t('admin.vendor.blockletMetaUrlRequired'); // 必填字段
|
|
131
|
-
try {
|
|
132
|
-
// eslint-disable-next-line no-new
|
|
133
|
-
new URL(url);
|
|
134
|
-
return true;
|
|
135
|
-
} catch {
|
|
136
|
-
return t('admin.vendor.blockletMetaUrlInvalid');
|
|
137
|
-
}
|
|
138
|
-
};
|
|
139
|
-
|
|
140
130
|
const onSubmit = async (data: VendorFormData) => {
|
|
141
131
|
try {
|
|
142
132
|
setLoading(true);
|
|
@@ -151,14 +141,24 @@ export default function VendorCreate({
|
|
|
151
141
|
return;
|
|
152
142
|
}
|
|
153
143
|
|
|
154
|
-
// 准备提交数据,将
|
|
155
|
-
const {
|
|
144
|
+
// 准备提交数据,将 metadata 数组转换为对象
|
|
145
|
+
const { metadata: metadataArray, ...restData } = data;
|
|
146
|
+
const metadataObj = metadataArray.reduce((acc: Record<string, any>, item) => {
|
|
147
|
+
if (item.key && item.value) {
|
|
148
|
+
try {
|
|
149
|
+
// 尝试解析 JSON,如果失败则作为字符串处理
|
|
150
|
+
acc[item.key] = JSON.parse(item.value);
|
|
151
|
+
} catch {
|
|
152
|
+
acc[item.key] = item.value;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return acc;
|
|
156
|
+
}, {});
|
|
157
|
+
|
|
156
158
|
const submitData = {
|
|
157
159
|
...restData,
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
...(blockletMetaUrl && { blockletMetaUrl }),
|
|
161
|
-
},
|
|
160
|
+
vendor_did: restData.vendor_did?.replace('did:abt:', '').trim(),
|
|
161
|
+
metadata: metadataObj,
|
|
162
162
|
};
|
|
163
163
|
|
|
164
164
|
// 如果状态为启用,则检测应用地址可用性
|
|
@@ -181,6 +181,10 @@ export default function VendorCreate({
|
|
|
181
181
|
// 从响应中获取appPid和appLogo
|
|
182
182
|
const blockletInfo = await response.json();
|
|
183
183
|
if (blockletInfo) {
|
|
184
|
+
const component = blockletInfo.componentMountPoints?.find((x: any) => x.did === submitData.vendor_did);
|
|
185
|
+
if (component && !['', '/'].includes(component.mountPoint) && submitData.metadata) {
|
|
186
|
+
submitData.metadata.mountPoint = component.mountPoint;
|
|
187
|
+
}
|
|
184
188
|
submitData.app_pid = blockletInfo.pid || blockletInfo.appPid;
|
|
185
189
|
submitData.app_logo = blockletInfo.logo || blockletInfo.appLogo;
|
|
186
190
|
}
|
|
@@ -227,20 +231,35 @@ export default function VendorCreate({
|
|
|
227
231
|
};
|
|
228
232
|
|
|
229
233
|
return (
|
|
230
|
-
<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<Stack
|
|
234
|
+
<FormProvider {...methods}>
|
|
235
|
+
<DrawerForm
|
|
236
|
+
icon={isEditMode ? null : <AddOutlined />}
|
|
237
|
+
text={isEditMode ? t('admin.vendor.edit') : t('admin.vendor.create')}
|
|
238
|
+
open={open}
|
|
239
|
+
onClose={handleClose}
|
|
240
|
+
width={800}
|
|
241
|
+
addons={
|
|
242
|
+
<Button variant="contained" size="small" onClick={handleSubmit(onSubmit)} disabled={loading || !isValid}>
|
|
243
|
+
{loading && <CircularProgress size="small" sx={{ mr: 1 }} />}
|
|
244
|
+
{isEditMode ? t('admin.save') : t('admin.vendor.save')}
|
|
245
|
+
</Button>
|
|
246
|
+
}>
|
|
247
|
+
<Stack spacing={3}>
|
|
248
|
+
<Controller
|
|
249
|
+
name="vendor_type"
|
|
250
|
+
control={control}
|
|
251
|
+
rules={{
|
|
252
|
+
required: t('admin.vendor.vendorTypeRequired'),
|
|
253
|
+
}}
|
|
254
|
+
render={({ field }) => (
|
|
255
|
+
<FormControl fullWidth required error={!!errors.vendor_type}>
|
|
256
|
+
<InputLabel>{t('admin.vendor.vendorType')}</InputLabel>
|
|
257
|
+
<Select {...field} label={t('admin.vendor.vendorType')}>
|
|
258
|
+
<MenuItem value="launcher">{t('admin.vendor.launcher')}</MenuItem>
|
|
259
|
+
</Select>
|
|
260
|
+
</FormControl>
|
|
261
|
+
)}
|
|
262
|
+
/>
|
|
244
263
|
<Controller
|
|
245
264
|
name="vendor_key"
|
|
246
265
|
control={control}
|
|
@@ -266,9 +285,7 @@ export default function VendorCreate({
|
|
|
266
285
|
<Controller
|
|
267
286
|
name="name"
|
|
268
287
|
control={control}
|
|
269
|
-
rules={{
|
|
270
|
-
required: t('admin.vendor.nameRequired'),
|
|
271
|
-
}}
|
|
288
|
+
rules={{ required: t('admin.vendor.nameRequired') }}
|
|
272
289
|
render={({ field }) => (
|
|
273
290
|
<TextField
|
|
274
291
|
{...field}
|
|
@@ -276,143 +293,76 @@ export default function VendorCreate({
|
|
|
276
293
|
required
|
|
277
294
|
fullWidth
|
|
278
295
|
error={!!errors.name}
|
|
279
|
-
helperText={errors.name?.message}
|
|
296
|
+
helperText={errors.name?.message || t('admin.vendor.displayNameHelp')}
|
|
280
297
|
/>
|
|
281
298
|
)}
|
|
282
299
|
/>
|
|
283
|
-
</Stack>
|
|
284
|
-
|
|
285
|
-
<Controller
|
|
286
|
-
name="description"
|
|
287
|
-
control={control}
|
|
288
|
-
render={({ field }) => (
|
|
289
|
-
<TextField {...field} label={t('admin.vendor.description')} multiline rows={3} fullWidth />
|
|
290
|
-
)}
|
|
291
|
-
/>
|
|
292
|
-
|
|
293
|
-
<Controller
|
|
294
|
-
name="app_url"
|
|
295
|
-
control={control}
|
|
296
|
-
rules={{
|
|
297
|
-
required: t('admin.vendor.appUrlRequired'),
|
|
298
|
-
validate: validateUrl,
|
|
299
|
-
}}
|
|
300
|
-
render={({ field }) => (
|
|
301
|
-
<TextField
|
|
302
|
-
{...field}
|
|
303
|
-
label={t('admin.vendor.appUrl')}
|
|
304
|
-
required
|
|
305
|
-
fullWidth
|
|
306
|
-
error={!!errors.app_url}
|
|
307
|
-
helperText={errors.app_url?.message || t('admin.vendor.appUrlHelp')}
|
|
308
|
-
/>
|
|
309
|
-
)}
|
|
310
|
-
/>
|
|
311
300
|
|
|
312
|
-
<Controller
|
|
313
|
-
name="webhook_path"
|
|
314
|
-
control={control}
|
|
315
|
-
rules={{
|
|
316
|
-
validate: validateWebhookPath,
|
|
317
|
-
}}
|
|
318
|
-
render={({ field }) => (
|
|
319
|
-
<TextField
|
|
320
|
-
{...field}
|
|
321
|
-
label={t('admin.vendor.webhookPath')}
|
|
322
|
-
fullWidth
|
|
323
|
-
error={!!errors.webhook_path}
|
|
324
|
-
helperText={errors.webhook_path?.message || t('admin.vendor.webhookPathHelp')}
|
|
325
|
-
/>
|
|
326
|
-
)}
|
|
327
|
-
/>
|
|
328
|
-
|
|
329
|
-
<Controller
|
|
330
|
-
name="blocklet_meta_url"
|
|
331
|
-
control={control}
|
|
332
|
-
rules={{
|
|
333
|
-
required: t('admin.vendor.blockletMetaUrlRequired'),
|
|
334
|
-
validate: validateBlockletMetaUrl,
|
|
335
|
-
}}
|
|
336
|
-
render={({ field }) => (
|
|
337
|
-
<TextField
|
|
338
|
-
{...field}
|
|
339
|
-
label={t('admin.vendor.blockletMetaUrl')}
|
|
340
|
-
required
|
|
341
|
-
fullWidth
|
|
342
|
-
error={!!errors.blocklet_meta_url}
|
|
343
|
-
helperText={errors.blocklet_meta_url?.message || t('admin.vendor.blockletMetaUrlHelp')}
|
|
344
|
-
/>
|
|
345
|
-
)}
|
|
346
|
-
/>
|
|
347
|
-
|
|
348
|
-
<Stack direction="row" spacing={0} sx={{ width: '30%' }}>
|
|
349
|
-
{/* 分成类型 - 隐藏,固定为比例分成 */}
|
|
350
301
|
<Controller
|
|
351
|
-
name="
|
|
302
|
+
name="description"
|
|
352
303
|
control={control}
|
|
353
304
|
render={({ field }) => (
|
|
354
|
-
<
|
|
355
|
-
<InputLabel>{t('admin.vendor.commissionType')}</InputLabel>
|
|
356
|
-
<Select {...field} label={t('admin.vendor.commissionType')}>
|
|
357
|
-
<MenuItem value="percentage">{t('admin.vendor.percentage')}</MenuItem>
|
|
358
|
-
<MenuItem value="fixed_amount">{t('admin.vendor.fixedAmount')}</MenuItem>
|
|
359
|
-
</Select>
|
|
360
|
-
</FormControl>
|
|
305
|
+
<TextField {...field} label={t('admin.vendor.description')} multiline rows={3} fullWidth />
|
|
361
306
|
)}
|
|
362
307
|
/>
|
|
308
|
+
|
|
363
309
|
<Controller
|
|
364
|
-
name="
|
|
310
|
+
name="app_url"
|
|
365
311
|
control={control}
|
|
366
312
|
rules={{
|
|
367
|
-
required: t('admin.vendor.
|
|
368
|
-
|
|
369
|
-
value: 0,
|
|
370
|
-
message: t('admin.vendor.commissionRateMin'),
|
|
371
|
-
},
|
|
372
|
-
max: {
|
|
373
|
-
value: isPercentage ? 100 : 999999,
|
|
374
|
-
message: t('admin.vendor.commissionRateMax'),
|
|
375
|
-
},
|
|
313
|
+
required: t('admin.vendor.appUrlRequired'),
|
|
314
|
+
validate: validateUrl,
|
|
376
315
|
}}
|
|
377
316
|
render={({ field }) => (
|
|
378
317
|
<TextField
|
|
379
318
|
{...field}
|
|
380
|
-
label={
|
|
381
|
-
type="number"
|
|
319
|
+
label={t('admin.vendor.appUrl')}
|
|
382
320
|
required
|
|
383
321
|
fullWidth
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
322
|
+
error={!!errors.app_url}
|
|
323
|
+
helperText={errors.app_url?.message || t('admin.vendor.appUrlHelp')}
|
|
324
|
+
/>
|
|
325
|
+
)}
|
|
326
|
+
/>
|
|
327
|
+
|
|
328
|
+
<Controller
|
|
329
|
+
name="vendor_did"
|
|
330
|
+
control={control}
|
|
331
|
+
rules={{
|
|
332
|
+
required: t('admin.vendor.vendorDidRequired'),
|
|
333
|
+
validate: validateDid,
|
|
334
|
+
}}
|
|
335
|
+
render={({ field }) => (
|
|
336
|
+
<TextField
|
|
337
|
+
{...field}
|
|
338
|
+
label={t('admin.vendor.vendorDid')}
|
|
339
|
+
fullWidth
|
|
340
|
+
error={!!errors.vendor_did}
|
|
341
|
+
helperText={errors.vendor_did?.message || t('admin.vendor.vendorDidHelp')}
|
|
342
|
+
/>
|
|
343
|
+
)}
|
|
344
|
+
/>
|
|
345
|
+
|
|
346
|
+
<MetadataForm title={t('common.metadata.label')} />
|
|
347
|
+
|
|
348
|
+
<Controller
|
|
349
|
+
name="status"
|
|
350
|
+
control={control}
|
|
351
|
+
render={({ field }) => (
|
|
352
|
+
<FormControlLabel
|
|
353
|
+
sx={{ maxWidth: 'max-content' }}
|
|
354
|
+
control={
|
|
355
|
+
<Switch
|
|
356
|
+
checked={field.value === 'active'}
|
|
357
|
+
onChange={(e) => field.onChange(e.target.checked ? 'active' : 'inactive')}
|
|
358
|
+
/>
|
|
389
359
|
}
|
|
390
|
-
|
|
391
|
-
min: 0,
|
|
392
|
-
max: isPercentage ? 100 : 999999,
|
|
393
|
-
step: isPercentage ? 10 : 1,
|
|
394
|
-
}}
|
|
360
|
+
label={watchedValues.status === 'active' ? t('admin.vendor.enabled') : t('admin.vendor.disabled')}
|
|
395
361
|
/>
|
|
396
362
|
)}
|
|
397
363
|
/>
|
|
398
364
|
</Stack>
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
name="status"
|
|
402
|
-
control={control}
|
|
403
|
-
render={({ field }) => (
|
|
404
|
-
<FormControlLabel
|
|
405
|
-
control={
|
|
406
|
-
<Switch
|
|
407
|
-
checked={field.value === 'active'}
|
|
408
|
-
onChange={(e) => field.onChange(e.target.checked ? 'active' : 'inactive')}
|
|
409
|
-
/>
|
|
410
|
-
}
|
|
411
|
-
label={watchedValues.status === 'active' ? t('admin.vendor.enabled') : t('admin.vendor.disabled')}
|
|
412
|
-
/>
|
|
413
|
-
)}
|
|
414
|
-
/>
|
|
415
|
-
</Stack>
|
|
416
|
-
</DrawerForm>
|
|
365
|
+
</DrawerForm>
|
|
366
|
+
</FormProvider>
|
|
417
367
|
);
|
|
418
368
|
}
|
|
@@ -16,14 +16,11 @@ import VendorCreate from './create';
|
|
|
16
16
|
interface Vendor {
|
|
17
17
|
id: string;
|
|
18
18
|
vendor_key: string;
|
|
19
|
+
vendor_type: string;
|
|
19
20
|
name: string;
|
|
20
21
|
description: string;
|
|
21
22
|
app_url: string;
|
|
22
|
-
webhook_path: string;
|
|
23
|
-
default_commission_rate: number;
|
|
24
|
-
default_commission_type: 'percentage' | 'fixed_amount';
|
|
25
23
|
status: 'active' | 'inactive';
|
|
26
|
-
order_create_params: Record<string, any>;
|
|
27
24
|
metadata: Record<string, any>;
|
|
28
25
|
created_at: string;
|
|
29
26
|
updated_at: string;
|
|
@@ -95,7 +92,17 @@ export default function VendorsList() {
|
|
|
95
92
|
filter: true,
|
|
96
93
|
customBodyRenderLite: (_: string, index: number) => {
|
|
97
94
|
const item = data.list[index] as Vendor;
|
|
98
|
-
return
|
|
95
|
+
return (
|
|
96
|
+
<InfoCard
|
|
97
|
+
name={item.name}
|
|
98
|
+
description={
|
|
99
|
+
<Typography variant="body2" color="text.secondary" sx={{ textTransform: 'capitalize' }}>
|
|
100
|
+
{item.vendor_type}
|
|
101
|
+
</Typography>
|
|
102
|
+
}
|
|
103
|
+
logo={undefined}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
99
106
|
},
|
|
100
107
|
},
|
|
101
108
|
},
|
|
@@ -114,21 +121,6 @@ export default function VendorsList() {
|
|
|
114
121
|
},
|
|
115
122
|
},
|
|
116
123
|
},
|
|
117
|
-
{
|
|
118
|
-
label: t('admin.vendor.commission'),
|
|
119
|
-
name: 'commission',
|
|
120
|
-
options: {
|
|
121
|
-
filter: true,
|
|
122
|
-
customBodyRenderLite: (_: string, index: number) => {
|
|
123
|
-
const item = data.list[index] as Vendor;
|
|
124
|
-
const commissionText =
|
|
125
|
-
item.default_commission_type === 'percentage'
|
|
126
|
-
? `${item.default_commission_rate}%`
|
|
127
|
-
: `${item.default_commission_rate}`;
|
|
128
|
-
return <Typography variant="body2">{commissionText}</Typography>;
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
124
|
{
|
|
133
125
|
label: t('common.status'),
|
|
134
126
|
name: 'status',
|
|
@@ -197,7 +189,7 @@ export default function VendorsList() {
|
|
|
197
189
|
return (
|
|
198
190
|
<>
|
|
199
191
|
{/* 供应商服务公钥展示 */}
|
|
200
|
-
{
|
|
192
|
+
{window.blocklet.appPk && (
|
|
201
193
|
<Box
|
|
202
194
|
sx={{
|
|
203
195
|
display: 'flex',
|
|
@@ -237,7 +229,7 @@ export default function VendorsList() {
|
|
|
237
229
|
}}>
|
|
238
230
|
<Chip
|
|
239
231
|
sx={{ backgroundColor: 'grey.200', color: 'text.secondary' }}
|
|
240
|
-
label={
|
|
232
|
+
label={window.blocklet.appPk}
|
|
241
233
|
variant="outlined"
|
|
242
234
|
size="small"
|
|
243
235
|
/>
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
3
|
import {
|
|
4
|
+
api,
|
|
4
5
|
CreditTransactionsList,
|
|
5
6
|
CustomerInvoiceList,
|
|
6
|
-
TxLink,
|
|
7
|
-
api,
|
|
8
7
|
formatBNStr,
|
|
9
8
|
formatTime,
|
|
10
9
|
hasDelegateTxHash,
|
|
10
|
+
TxLink,
|
|
11
11
|
useMobile,
|
|
12
12
|
} from '@blocklet/payment-react';
|
|
13
13
|
import type { TPaymentCurrency, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
@@ -25,31 +25,32 @@ import {
|
|
|
25
25
|
Button,
|
|
26
26
|
CircularProgress,
|
|
27
27
|
Divider,
|
|
28
|
-
Stack,
|
|
29
|
-
Typography,
|
|
30
|
-
Link as MuiLink,
|
|
31
28
|
IconButton,
|
|
29
|
+
Link as MuiLink,
|
|
30
|
+
Stack,
|
|
32
31
|
Tooltip,
|
|
32
|
+
Typography,
|
|
33
33
|
useTheme,
|
|
34
34
|
} from '@mui/material';
|
|
35
|
-
import { useRequest } from 'ahooks';
|
|
36
|
-
import { Link, useNavigate, useParams } from 'react-router-dom';
|
|
37
35
|
import { styled } from '@mui/system';
|
|
38
36
|
import { BN, fromUnitToToken } from '@ocap/util';
|
|
37
|
+
import { useRequest } from 'ahooks';
|
|
39
38
|
import { useCallback, useRef } from 'react';
|
|
39
|
+
import { Link, useNavigate, useParams } from 'react-router-dom';
|
|
40
40
|
import Currency from '../../../components/currency';
|
|
41
41
|
import CustomerLink from '../../../components/customer/link';
|
|
42
|
+
import InfoMetric from '../../../components/info-metric';
|
|
42
43
|
import InfoRow from '../../../components/info-row';
|
|
44
|
+
import InfoRowGroup from '../../../components/info-row-group';
|
|
43
45
|
import SubscriptionDescription from '../../../components/subscription/description';
|
|
44
46
|
import SubscriptionItemList from '../../../components/subscription/items';
|
|
45
47
|
import SubscriptionMetrics from '../../../components/subscription/metrics';
|
|
46
48
|
import SubscriptionActions, { ActionMethods } from '../../../components/subscription/portal/actions';
|
|
47
|
-
import
|
|
49
|
+
import VendorServiceList from '../../../components/subscription/vendor-service-list';
|
|
48
50
|
import { useSessionContext } from '../../../contexts/session';
|
|
49
|
-
import
|
|
50
|
-
import { useUnpaidInvoicesCheckForSubscription, usePendingAmountForSubscription } from '../../../hooks/subscription';
|
|
51
|
+
import { usePendingAmountForSubscription, useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
|
|
51
52
|
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
52
|
-
import
|
|
53
|
+
import { canChangePaymentMethod } from '../../../libs/util';
|
|
53
54
|
|
|
54
55
|
const fetchData = (id: string | undefined): Promise<TSubscriptionExpanded> => {
|
|
55
56
|
return api.get(`/api/subscriptions/${id}`).then((res) => res.data);
|
|
@@ -691,6 +692,20 @@ export default function CustomerSubscriptionDetail() {
|
|
|
691
692
|
<SubscriptionItemList data={data.items} currency={data.paymentCurrency} mode="customer" />
|
|
692
693
|
</Box>
|
|
693
694
|
</Box>
|
|
695
|
+
{(() => {
|
|
696
|
+
const vendorServices = data.items?.map((item) => item.price?.product?.vendor_config || []).flat();
|
|
697
|
+
|
|
698
|
+
if (!vendorServices || vendorServices.length === 0) return null;
|
|
699
|
+
|
|
700
|
+
return (
|
|
701
|
+
<>
|
|
702
|
+
<Divider />
|
|
703
|
+
<Box className="section">
|
|
704
|
+
<VendorServiceList vendorServices={vendorServices} subscriptionId={id} />
|
|
705
|
+
</Box>
|
|
706
|
+
</>
|
|
707
|
+
);
|
|
708
|
+
})()}
|
|
694
709
|
<Divider />
|
|
695
710
|
{isCredit ? (
|
|
696
711
|
<Box className="section">
|