payment-kit 1.14.38 → 1.15.0
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/integrations/stripe/resource.ts +2 -2
- package/api/src/libs/api.ts +2 -2
- package/api/src/routes/products.ts +11 -3
- package/api/tests/libs/api.spec.ts +2 -4
- package/blocklet.yml +1 -1
- package/package.json +19 -19
- package/src/components/customer/link.tsx +7 -2
- package/src/components/filter-toolbar.tsx +2 -2
- package/src/components/metadata/form.tsx +2 -2
- package/src/components/price/form.tsx +28 -2
- package/src/components/product/form.tsx +13 -7
- package/src/locales/en.tsx +4 -0
- package/src/locales/zh.tsx +3 -0
- package/src/pages/admin/customers/customers/detail.tsx +14 -2
- package/src/pages/admin/customers/customers/index.tsx +6 -2
- package/src/pages/customer/index.tsx +2 -1
|
@@ -44,7 +44,7 @@ export async function ensureStripeProduct(internal: Product, method: PaymentMeth
|
|
|
44
44
|
attrs.unit_label = internal.unit_label;
|
|
45
45
|
}
|
|
46
46
|
if (internal.statement_descriptor) {
|
|
47
|
-
attrs.statement_descriptor_suffix =
|
|
47
|
+
attrs.statement_descriptor_suffix = '';
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
const product = await client.products.create(attrs);
|
|
@@ -176,7 +176,7 @@ export async function ensureStripePaymentIntent(
|
|
|
176
176
|
enabled: true,
|
|
177
177
|
allow_redirects: 'never',
|
|
178
178
|
},
|
|
179
|
-
statement_descriptor_suffix:
|
|
179
|
+
statement_descriptor_suffix: '',
|
|
180
180
|
metadata: {
|
|
181
181
|
appPid: env.appPid,
|
|
182
182
|
id: internal.id,
|
package/api/src/libs/api.ts
CHANGED
|
@@ -163,13 +163,13 @@ function validateMetadataValue(value: any, helpers: any) {
|
|
|
163
163
|
export const MetadataSchema = Joi.alternatives()
|
|
164
164
|
.try(
|
|
165
165
|
Joi.object()
|
|
166
|
-
.pattern(Joi.string().max(
|
|
166
|
+
.pattern(Joi.string().max(40), Joi.any().custom(validateMetadataValue, 'Custom Validation'))
|
|
167
167
|
.min(0)
|
|
168
168
|
.allow(null),
|
|
169
169
|
Joi.array()
|
|
170
170
|
.items(
|
|
171
171
|
Joi.object({
|
|
172
|
-
key: Joi.string().max(
|
|
172
|
+
key: Joi.string().max(40).required(),
|
|
173
173
|
value: Joi.any().custom(validateMetadataValue, 'Custom Validation').required(),
|
|
174
174
|
})
|
|
175
175
|
)
|
|
@@ -21,11 +21,19 @@ const auth = authenticate<Product>({ component: true, roles: ['owner', 'admin']
|
|
|
21
21
|
const ProductAndPriceSchema = Joi.object({
|
|
22
22
|
name: Joi.string().max(64).empty('').optional(),
|
|
23
23
|
type: Joi.string().valid('service', 'good').empty('').optional(),
|
|
24
|
-
description: Joi.string().max(
|
|
24
|
+
description: Joi.string().max(250).empty('').optional(),
|
|
25
25
|
images: Joi.any().optional(),
|
|
26
26
|
metadata: MetadataSchema,
|
|
27
|
-
statement_descriptor: Joi.string()
|
|
28
|
-
|
|
27
|
+
statement_descriptor: Joi.string()
|
|
28
|
+
.max(22)
|
|
29
|
+
.pattern(/^(?=.*[A-Za-z])[^\u4e00-\u9fa5<>"’\\]*$/)
|
|
30
|
+
.messages({
|
|
31
|
+
'string.pattern.base':
|
|
32
|
+
'statement_descriptor should be at least one letter and cannot include Chinese characters and special characters such as <, >、"、’ or \\',
|
|
33
|
+
})
|
|
34
|
+
.empty('')
|
|
35
|
+
.optional(),
|
|
36
|
+
unit_label: Joi.string().max(12).empty('').optional(),
|
|
29
37
|
nft_factory: Joi.string().max(40).allow(null).empty('').optional(),
|
|
30
38
|
features: Joi.array()
|
|
31
39
|
.items(Joi.object({ name: Joi.string().max(64).empty('').optional() }).unknown(true))
|
|
@@ -177,13 +177,11 @@ describe('MetadataSchema', () => {
|
|
|
177
177
|
|
|
178
178
|
it('should invalidate an object with a key longer than 64 characters', () => {
|
|
179
179
|
const data = {
|
|
180
|
-
['a'.repeat(
|
|
180
|
+
['a'.repeat(41)]: 'value1',
|
|
181
181
|
};
|
|
182
182
|
const { error } = MetadataSchema.validate(data);
|
|
183
183
|
expect(error).toBeDefined();
|
|
184
|
-
expect(error?.details?.[0]?.message).toMatch(
|
|
185
|
-
/"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" is not allowed/
|
|
186
|
-
);
|
|
184
|
+
expect(error?.details?.[0]?.message).toMatch(/"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" is not allowed/);
|
|
187
185
|
});
|
|
188
186
|
|
|
189
187
|
it('should invalidate an array with an object missing the key field', () => {
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -43,34 +43,34 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"@abtnode/cron": "1.16.30",
|
|
46
|
-
"@arcblock/did": "^1.18.
|
|
46
|
+
"@arcblock/did": "^1.18.135",
|
|
47
47
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
48
|
-
"@arcblock/did-connect": "^2.10.
|
|
49
|
-
"@arcblock/did-util": "^1.18.
|
|
50
|
-
"@arcblock/jwt": "^1.18.
|
|
51
|
-
"@arcblock/ux": "
|
|
52
|
-
"@arcblock/validator": "^1.18.
|
|
48
|
+
"@arcblock/did-connect": "^2.10.25",
|
|
49
|
+
"@arcblock/did-util": "^1.18.135",
|
|
50
|
+
"@arcblock/jwt": "^1.18.135",
|
|
51
|
+
"@arcblock/ux": "2.10.24",
|
|
52
|
+
"@arcblock/validator": "^1.18.135",
|
|
53
53
|
"@blocklet/js-sdk": "1.16.30",
|
|
54
54
|
"@blocklet/logger": "1.16.30",
|
|
55
|
-
"@blocklet/payment-react": "1.
|
|
55
|
+
"@blocklet/payment-react": "1.15.0",
|
|
56
56
|
"@blocklet/sdk": "1.16.30",
|
|
57
|
-
"@blocklet/ui-react": "^2.10.
|
|
57
|
+
"@blocklet/ui-react": "^2.10.25",
|
|
58
58
|
"@blocklet/uploader": "^0.1.27",
|
|
59
59
|
"@mui/icons-material": "^5.16.6",
|
|
60
60
|
"@mui/lab": "^5.0.0-alpha.173",
|
|
61
61
|
"@mui/material": "^5.16.6",
|
|
62
62
|
"@mui/styles": "^5.16.6",
|
|
63
63
|
"@mui/system": "^5.16.6",
|
|
64
|
-
"@ocap/asset": "^1.18.
|
|
65
|
-
"@ocap/client": "^1.18.
|
|
66
|
-
"@ocap/mcrypto": "^1.18.
|
|
67
|
-
"@ocap/util": "^1.18.
|
|
68
|
-
"@ocap/wallet": "^1.18.
|
|
64
|
+
"@ocap/asset": "^1.18.135",
|
|
65
|
+
"@ocap/client": "^1.18.135",
|
|
66
|
+
"@ocap/mcrypto": "^1.18.135",
|
|
67
|
+
"@ocap/util": "^1.18.135",
|
|
68
|
+
"@ocap/wallet": "^1.18.135",
|
|
69
69
|
"@react-pdf/renderer": "^3.4.4",
|
|
70
70
|
"@stripe/react-stripe-js": "^2.7.3",
|
|
71
71
|
"@stripe/stripe-js": "^2.4.0",
|
|
72
72
|
"ahooks": "^3.8.0",
|
|
73
|
-
"axios": "^1.7.
|
|
73
|
+
"axios": "^1.7.5",
|
|
74
74
|
"body-parser": "^1.20.2",
|
|
75
75
|
"cls-hooked": "^4.2.2",
|
|
76
76
|
"cookie-parser": "^1.4.6",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"devDependencies": {
|
|
120
120
|
"@abtnode/types": "1.16.30",
|
|
121
121
|
"@arcblock/eslint-config-ts": "^0.3.2",
|
|
122
|
-
"@blocklet/payment-types": "1.
|
|
122
|
+
"@blocklet/payment-types": "1.15.0",
|
|
123
123
|
"@types/cookie-parser": "^1.4.7",
|
|
124
124
|
"@types/cors": "^2.8.17",
|
|
125
125
|
"@types/debug": "^4.1.12",
|
|
@@ -139,13 +139,13 @@
|
|
|
139
139
|
"npm-run-all": "^4.1.5",
|
|
140
140
|
"prettier": "^3.3.3",
|
|
141
141
|
"prettier-plugin-import-sort": "^0.0.7",
|
|
142
|
-
"ts-jest": "^29.2.
|
|
142
|
+
"ts-jest": "^29.2.5",
|
|
143
143
|
"ts-node": "^10.9.2",
|
|
144
144
|
"type-fest": "^4.23.0",
|
|
145
145
|
"typescript": "^4.9.5",
|
|
146
146
|
"vite": "^5.3.5",
|
|
147
147
|
"vite-node": "^2.0.4",
|
|
148
|
-
"vite-plugin-blocklet": "^0.9.
|
|
148
|
+
"vite-plugin-blocklet": "^0.9.3",
|
|
149
149
|
"vite-plugin-node-polyfills": "^0.21.0",
|
|
150
150
|
"vite-plugin-svgr": "^4.2.0",
|
|
151
151
|
"vite-tsconfig-paths": "^4.3.2",
|
|
@@ -161,5 +161,5 @@
|
|
|
161
161
|
"parser": "typescript"
|
|
162
162
|
}
|
|
163
163
|
},
|
|
164
|
-
"gitHead": "
|
|
164
|
+
"gitHead": "a53bb8a9b781e36974899d3fc8a4aad544621a9c"
|
|
165
165
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
2
2
|
import { Link } from 'react-router-dom';
|
|
3
3
|
|
|
4
|
+
import { getCustomerAvatar } from '@blocklet/payment-react';
|
|
4
5
|
import InfoCard from '../info-card';
|
|
5
6
|
|
|
6
7
|
export default function CustomerLink({ customer, linked }: { customer: TCustomer; linked?: boolean }) {
|
|
@@ -11,7 +12,11 @@ export default function CustomerLink({ customer, linked }: { customer: TCustomer
|
|
|
11
12
|
return (
|
|
12
13
|
<Link to={`/admin/customers/${customer.id}`}>
|
|
13
14
|
<InfoCard
|
|
14
|
-
logo={
|
|
15
|
+
logo={getCustomerAvatar(
|
|
16
|
+
customer?.did,
|
|
17
|
+
customer?.updated_at ? new Date(customer.updated_at).toISOString() : '',
|
|
18
|
+
48
|
|
19
|
+
)}
|
|
15
20
|
name={customer.email}
|
|
16
21
|
description={`${customer.did.slice(0, 6)}...${customer.did.slice(-6)}`}
|
|
17
22
|
/>
|
|
@@ -21,7 +26,7 @@ export default function CustomerLink({ customer, linked }: { customer: TCustomer
|
|
|
21
26
|
|
|
22
27
|
return (
|
|
23
28
|
<InfoCard
|
|
24
|
-
logo={
|
|
29
|
+
logo={getCustomerAvatar(customer.did, customer.updated_at ? new Date(customer.updated_at).toISOString() : '', 48)}
|
|
25
30
|
name={customer.email}
|
|
26
31
|
description={<span style={{ wordBreak: 'break-all' }}>{customer?.did}</span>}
|
|
27
32
|
/>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
2
|
-
import { api, useMobile, usePaymentContext } from '@blocklet/payment-react';
|
|
2
|
+
import { api, getCustomerAvatar, useMobile, usePaymentContext } from '@blocklet/payment-react';
|
|
3
3
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
4
4
|
import { Add, Close } from '@mui/icons-material';
|
|
5
5
|
import { Button, Menu, MenuItem } from '@mui/material';
|
|
@@ -300,7 +300,7 @@ function SearchCustomers({ setSearch, search }: Pick<Props, 'setSearch' | 'searc
|
|
|
300
300
|
setShow(null);
|
|
301
301
|
}}>
|
|
302
302
|
<InfoCard
|
|
303
|
-
logo={
|
|
303
|
+
logo={getCustomerAvatar(x?.did, x?.updated_at, 48)}
|
|
304
304
|
name={x.email}
|
|
305
305
|
key={x.id}
|
|
306
306
|
description={`${x.did.slice(0, 6)}...${x.did.slice(-6)}`}
|
|
@@ -75,7 +75,7 @@ export default function MetadataForm({
|
|
|
75
75
|
required: t('payment.checkout.required'),
|
|
76
76
|
maxLength: {
|
|
77
77
|
value: 64,
|
|
78
|
-
message: t('common.maxLength', { len:
|
|
78
|
+
message: t('common.maxLength', { len: 40 }),
|
|
79
79
|
},
|
|
80
80
|
}}
|
|
81
81
|
placeholder="Key"
|
|
@@ -83,7 +83,7 @@ export default function MetadataForm({
|
|
|
83
83
|
// @ts-ignore
|
|
84
84
|
ref={errors?.metadata?.[index]?.key ? errorRef : null}
|
|
85
85
|
inputProps={{
|
|
86
|
-
maxLength:
|
|
86
|
+
maxLength: 40,
|
|
87
87
|
}}
|
|
88
88
|
/>
|
|
89
89
|
<FormInput
|
|
@@ -104,6 +104,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
104
104
|
control,
|
|
105
105
|
setValue,
|
|
106
106
|
formState: { errors },
|
|
107
|
+
trigger,
|
|
107
108
|
} = useFormContext();
|
|
108
109
|
const getFieldError = (name: string) => {
|
|
109
110
|
const names = name?.split('.');
|
|
@@ -111,7 +112,6 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
111
112
|
};
|
|
112
113
|
const { settings, livemode } = usePaymentContext();
|
|
113
114
|
const currencies = useFieldArray({ control, name: getFieldName('currency_options') });
|
|
114
|
-
|
|
115
115
|
const priceLocked = useWatch({ control, name: getFieldName('locked') });
|
|
116
116
|
const isRecurring = useWatch({ control, name: getFieldName('type') }) === 'recurring';
|
|
117
117
|
const isMetered = useWatch({ control, name: getFieldName('recurring.usage_type') }) === 'metered';
|
|
@@ -138,6 +138,11 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
138
138
|
return true;
|
|
139
139
|
};
|
|
140
140
|
|
|
141
|
+
const handleRemoveCurrency = async (index: number) => {
|
|
142
|
+
await currencies.remove(index);
|
|
143
|
+
trigger(getFieldName('recurring.interval_config'));
|
|
144
|
+
};
|
|
145
|
+
|
|
141
146
|
return (
|
|
142
147
|
<Root direction="column" alignItems="flex-start" spacing={2}>
|
|
143
148
|
{isLocked && <Alert severity="info">{t('admin.price.locked')}</Alert>}
|
|
@@ -280,7 +285,7 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
280
285
|
);
|
|
281
286
|
}}
|
|
282
287
|
/>
|
|
283
|
-
<IconButton size="small" disabled={isLocked} onClick={() =>
|
|
288
|
+
<IconButton size="small" disabled={isLocked} onClick={() => handleRemoveCurrency(index)}>
|
|
284
289
|
<DeleteOutlineOutlined color="error" sx={{ opacity: 0.75 }} />
|
|
285
290
|
</IconButton>
|
|
286
291
|
</Stack>
|
|
@@ -322,6 +327,19 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
322
327
|
name={getFieldName('recurring.interval_config')}
|
|
323
328
|
control={control}
|
|
324
329
|
disabled={isLocked}
|
|
330
|
+
rules={{
|
|
331
|
+
validate: (val) => {
|
|
332
|
+
const hasStripe = currencies.fields?.some((x: any) => {
|
|
333
|
+
return !!settings.paymentMethods.find(
|
|
334
|
+
(y) => y?.type === 'stripe' && x?.currency_id === y?.default_currency_id
|
|
335
|
+
);
|
|
336
|
+
});
|
|
337
|
+
if (val === 'hour_1' && hasStripe) {
|
|
338
|
+
return t('admin.price.recurring.stripeTip');
|
|
339
|
+
}
|
|
340
|
+
return true;
|
|
341
|
+
},
|
|
342
|
+
}}
|
|
325
343
|
render={({ field }) => (
|
|
326
344
|
<Box>
|
|
327
345
|
<FormLabel sx={{ color: 'text.primary' }}>{t('admin.price.recurring.interval')}</FormLabel>
|
|
@@ -333,8 +351,10 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
333
351
|
setValue(getFieldName('recurring.interval'), interval);
|
|
334
352
|
setValue(getFieldName('recurring.interval_count'), +count);
|
|
335
353
|
setValue(getFieldName('recurring.interval_config'), e.target.value);
|
|
354
|
+
trigger(getFieldName('recurring.interval_config'));
|
|
336
355
|
}}
|
|
337
356
|
sx={{ width: INPUT_WIDTH }}
|
|
357
|
+
error={!!get(errors, getFieldName('recurring.interval_config'))}
|
|
338
358
|
size="small">
|
|
339
359
|
{!livemode && <MenuItem value="hour_1">{t('common.hourly')}</MenuItem>}
|
|
340
360
|
<MenuItem value="day_1">{t('common.daily')}</MenuItem>
|
|
@@ -345,6 +365,12 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
|
|
|
345
365
|
<MenuItem value="year_1">{t('common.yearly')}</MenuItem>
|
|
346
366
|
<MenuItem value="month_2">{t('common.custom')}</MenuItem>
|
|
347
367
|
</Select>
|
|
368
|
+
{get(errors, getFieldName('recurring.interval_config'))?.message && (
|
|
369
|
+
<Typography color="error" sx={{ fontSize: '0.75rem', mt: 0.5, ml: 1.75 }}>
|
|
370
|
+
{/* @ts-ignore */}
|
|
371
|
+
{get(errors, getFieldName('recurring.interval_config')).message}
|
|
372
|
+
</Typography>
|
|
373
|
+
)}
|
|
348
374
|
</Box>
|
|
349
375
|
)}
|
|
350
376
|
/>
|
|
@@ -57,8 +57,8 @@ export default function ProductForm(props: Props) {
|
|
|
57
57
|
rules={{
|
|
58
58
|
required: t('admin.product.description.required'),
|
|
59
59
|
maxLength: {
|
|
60
|
-
value:
|
|
61
|
-
message: t('common.maxLength', { len:
|
|
60
|
+
value: 250,
|
|
61
|
+
message: t('common.maxLength', { len: 250 }),
|
|
62
62
|
},
|
|
63
63
|
}}
|
|
64
64
|
label={t('admin.product.description.label')}
|
|
@@ -68,21 +68,27 @@ export default function ProductForm(props: Props) {
|
|
|
68
68
|
multiline
|
|
69
69
|
minRows={2}
|
|
70
70
|
maxRows={4}
|
|
71
|
-
inputProps={{ maxLength:
|
|
71
|
+
inputProps={{ maxLength: 250 }}
|
|
72
72
|
/>
|
|
73
73
|
<Collapse trigger={t('admin.product.additional')}>
|
|
74
74
|
<Stack spacing={2} alignItems="flex-start">
|
|
75
75
|
<FormInput
|
|
76
76
|
name="statement_descriptor"
|
|
77
77
|
label={t('admin.product.statement_descriptor.label')}
|
|
78
|
-
rules={{
|
|
79
|
-
|
|
78
|
+
rules={{
|
|
79
|
+
maxLength: { value: 22, message: t('common.maxLength', { len: 22 }) },
|
|
80
|
+
pattern: {
|
|
81
|
+
value: /^(?=.*[A-Za-z])[^\u4e00-\u9fa5<>"’\\]*$/,
|
|
82
|
+
message: t('common.latinOnly'),
|
|
83
|
+
},
|
|
84
|
+
}}
|
|
80
85
|
/>
|
|
81
86
|
<FormInput
|
|
82
87
|
name="unit_label"
|
|
83
88
|
label={t('admin.product.unit_label.label')}
|
|
84
|
-
rules={{
|
|
85
|
-
|
|
89
|
+
rules={{
|
|
90
|
+
maxLength: { value: 12, message: t('common.maxLength', { len: 12 }) },
|
|
91
|
+
}}
|
|
86
92
|
/>
|
|
87
93
|
{!props.simple && <ProductFeatures />}
|
|
88
94
|
{!props.simple && <MetadataForm title={t('common.metadata.label')} />}
|
package/src/locales/en.tsx
CHANGED
|
@@ -18,6 +18,9 @@ export default flat({
|
|
|
18
18
|
exit: 'Exit',
|
|
19
19
|
maxLength: 'Max {len} characters',
|
|
20
20
|
minLength: 'Min {len} characters',
|
|
21
|
+
invalidCharacters: 'Invalid characters',
|
|
22
|
+
latinOnly:
|
|
23
|
+
'At least one letter and cannot include Chinese characters and special characters such as <, >、"、’ or \\',
|
|
21
24
|
loading: 'Loading...',
|
|
22
25
|
},
|
|
23
26
|
admin: {
|
|
@@ -148,6 +151,7 @@ export default flat({
|
|
|
148
151
|
'Metered billing lets you charge customers based on reported usage at the end of each billing period.',
|
|
149
152
|
aggregate: 'Charge for metered usage by',
|
|
150
153
|
intervalCountTip: 'Billing interval must be a positive integer',
|
|
154
|
+
stripeTip: 'Stripe requires the billing period to be greater than or equal to 1 day',
|
|
151
155
|
},
|
|
152
156
|
currency: {
|
|
153
157
|
add: 'Add more currencies',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -18,6 +18,8 @@ export default flat({
|
|
|
18
18
|
exit: '退出',
|
|
19
19
|
maxLength: '最多输入{len}个字符',
|
|
20
20
|
minLength: '最少输入{len}个字符',
|
|
21
|
+
invalidCharacters: '无效字符',
|
|
22
|
+
latinOnly: '至少包含一个字母,并且不能包含中文字符和特殊字符如 <, >、"、’ 或 \\',
|
|
21
23
|
loading: '加载中...',
|
|
22
24
|
},
|
|
23
25
|
admin: {
|
|
@@ -144,6 +146,7 @@ export default flat({
|
|
|
144
146
|
meteredTip: '计量计费允许您根据每个计费周期结束时的报告的使用情况向客户收费。',
|
|
145
147
|
aggregate: '按何种方式收费计量使用',
|
|
146
148
|
intervalCountTip: '计费周期必须是正整数',
|
|
149
|
+
stripeTip: 'Stripe要求计费周期不能为小时',
|
|
147
150
|
},
|
|
148
151
|
currency: {
|
|
149
152
|
add: '添加更多货币',
|
|
@@ -2,7 +2,15 @@
|
|
|
2
2
|
import DidAddress from '@arcblock/ux/lib/DID';
|
|
3
3
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
4
4
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
api,
|
|
7
|
+
formatBNStr,
|
|
8
|
+
formatError,
|
|
9
|
+
formatTime,
|
|
10
|
+
getCustomerAvatar,
|
|
11
|
+
useMobile,
|
|
12
|
+
usePaymentContext,
|
|
13
|
+
} from '@blocklet/payment-react';
|
|
6
14
|
import type { GroupedBN, TCustomerExpanded, TPaymentMethodExpanded } from '@blocklet/payment-types';
|
|
7
15
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
8
16
|
import { Alert, Avatar, Box, Button, CircularProgress, Divider, Stack, Typography } from '@mui/material';
|
|
@@ -196,7 +204,11 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
196
204
|
<Stack direction="row" alignItems="center" spacing={1}>
|
|
197
205
|
<Avatar
|
|
198
206
|
title={data.customer.name}
|
|
199
|
-
src={
|
|
207
|
+
src={getCustomerAvatar(
|
|
208
|
+
data.customer?.did,
|
|
209
|
+
data.customer?.updated_at ? new Date(data.customer.updated_at).toISOString() : '',
|
|
210
|
+
52
|
|
211
|
+
)}
|
|
200
212
|
variant="square"
|
|
201
213
|
sx={{ width: 52, height: 52, borderRadius: 'var(--radius-s, 4px)' }}
|
|
202
214
|
/>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { getDurableData } from '@arcblock/ux/lib/Datatable';
|
|
3
3
|
import DidAddress from '@arcblock/ux/lib/DID';
|
|
4
4
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
5
|
-
import { api, formatTime, Table } from '@blocklet/payment-react';
|
|
5
|
+
import { api, formatTime, getCustomerAvatar, Table } from '@blocklet/payment-react';
|
|
6
6
|
import type { TCustomer } from '@blocklet/payment-types';
|
|
7
7
|
import { Avatar, CircularProgress, Stack, Typography } from '@mui/material';
|
|
8
8
|
import { useEffect, useState } from 'react';
|
|
@@ -60,7 +60,11 @@ export default function CustomersList() {
|
|
|
60
60
|
<Link to={`/admin/customers/${item.id}`}>
|
|
61
61
|
<Stack direction="row" alignItems="center" spacing={1}>
|
|
62
62
|
<Avatar
|
|
63
|
-
src={
|
|
63
|
+
src={getCustomerAvatar(
|
|
64
|
+
item?.did,
|
|
65
|
+
item?.updated_at ? new Date(item.updated_at).toISOString() : '',
|
|
66
|
+
48
|
|
67
|
+
)}
|
|
64
68
|
variant="square"
|
|
65
69
|
sx={{ borderRadius: 'var(--radius-m, 8px)' }}
|
|
66
70
|
/>
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
CustomerInvoiceList,
|
|
6
6
|
formatBNStr,
|
|
7
7
|
formatError,
|
|
8
|
+
getCustomerAvatar,
|
|
8
9
|
getPrefix,
|
|
9
10
|
TruncatedText,
|
|
10
11
|
useMobile,
|
|
@@ -337,7 +338,7 @@ export default function CustomerHome() {
|
|
|
337
338
|
<Box display="flex" alignItems="center" gap={1} flexWrap="wrap" sx={{ mb: 3 }}>
|
|
338
339
|
<Avatar
|
|
339
340
|
title={data?.name}
|
|
340
|
-
src={
|
|
341
|
+
src={getCustomerAvatar(data?.did, data?.updated_at ? new Date(data.updated_at).toISOString() : '', 48)}
|
|
341
342
|
variant="circular"
|
|
342
343
|
sx={{ width: 48, height: 48 }}
|
|
343
344
|
/>
|