payment-kit 1.18.34 → 1.18.36

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.
@@ -25,6 +25,8 @@ import {
25
25
  import { getSubscriptionPaymentAddress, calculateRecommendedRechargeAmount } from '../libs/subscription';
26
26
  import { expandLineItems } from '../libs/session';
27
27
  import { handleNotificationPreferenceChange } from '../queues/notification';
28
+ import { getEndpointAndSpaceDid } from '../libs/did-space';
29
+ import { spaceQueue } from '../queues/space';
28
30
 
29
31
  const router = Router();
30
32
  const auth = authenticate<Customer>({ component: true, roles: ['owner', 'admin'] });
@@ -172,6 +174,51 @@ router.get('/me', sessionMiddleware(), async (req, res) => {
172
174
  }
173
175
  });
174
176
 
177
+ router.post('/sync-to-space', sessionMiddleware(), async (req, res) => {
178
+ if (!req.user) {
179
+ return res.status(403).json({ error: 'Unauthorized' });
180
+ }
181
+ try {
182
+ const userDid = req.user.did;
183
+ const jobId = `space-${userDid}`;
184
+ const { endpoint } = await getEndpointAndSpaceDid(userDid);
185
+ if (endpoint) {
186
+ const mainTask = await spaceQueue.get(jobId);
187
+ if (mainTask) {
188
+ return res.json({
189
+ success: true,
190
+ message: 'Billing data sync already in progress',
191
+ });
192
+ }
193
+ spaceQueue.push({
194
+ id: jobId,
195
+ job: {
196
+ type: 'customer',
197
+ data: {
198
+ id: userDid,
199
+ },
200
+ },
201
+ delay: 60, // delay 1min
202
+ });
203
+ logger.info('Queued billing sync to DID Space for user:', { did: req.user.did });
204
+
205
+ return res.json({
206
+ success: true,
207
+ message: 'Billing data sync will start soon',
208
+ });
209
+ }
210
+ return res.json({
211
+ success: false,
212
+ message: 'No endpoint found for the user',
213
+ });
214
+ } catch (error) {
215
+ return res.json({
216
+ success: false,
217
+ message: error.message,
218
+ });
219
+ }
220
+ });
221
+
175
222
  // get overdue invoices
176
223
  router.get('/:id/overdue/invoices', sessionMiddleware(), async (req, res) => {
177
224
  if (!req.user) {
@@ -298,7 +298,12 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
298
298
  'subscription_cancel',
299
299
  'subscription',
300
300
  'manual',
301
- 'upcoming'
301
+ 'upcoming',
302
+ 'slash_stake',
303
+ 'stake',
304
+ 'overdraft_protection',
305
+ 'stake_overdraft_protection',
306
+ 'recharge'
302
307
  ),
303
308
  },
304
309
  custom_fields: {
@@ -469,8 +474,12 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
469
474
  createdAt: 'created_at',
470
475
  updatedAt: 'updated_at',
471
476
  hooks: {
472
- afterCreate: (model: Invoice, options) =>
473
- createEvent('Invoice', 'invoice.created', model, options).catch(console.error),
477
+ afterCreate: (model: Invoice, options) => {
478
+ createEvent('Invoice', 'invoice.created', model, options).catch(console.error);
479
+ if (model.status === 'paid') {
480
+ createEvent('Invoice', 'invoice.paid', model, options).catch(console.error);
481
+ }
482
+ },
474
483
  afterUpdate: (model: Invoice, options) => {
475
484
  createEvent('Invoice', 'invoice.updated', model, options).catch(console.error);
476
485
  createStatusEvent(
@@ -13,8 +13,14 @@ import {
13
13
  getSubscriptionNotificationCustomActions,
14
14
  isUserInBlocklist,
15
15
  api,
16
+ md5,
17
+ safeJsonParse,
18
+ getExplorerLink,
19
+ resolveAddressChainTypes,
20
+ formatCurrencyInfo,
21
+ getExplorerTxUrl,
16
22
  } from '../../src/libs/util';
17
- import type { Subscription, PaymentMethod } from '../../src/store/models';
23
+ import type { Subscription, PaymentMethod, PaymentCurrency } from '../../src/store/models';
18
24
 
19
25
  import { blocklet } from '../../src/libs/auth';
20
26
  import logger from '../../src/libs/logger';
@@ -653,3 +659,211 @@ describe('isUserInBlocklist', () => {
653
659
  expect(result).toBe(true);
654
660
  });
655
661
  });
662
+
663
+ describe('md5', () => {
664
+ it('should generate a consistent hash for the same input', () => {
665
+ const input = 'test-string';
666
+ const result1 = md5(input);
667
+ const result2 = md5(input);
668
+ expect(result1).toBe(result2);
669
+ });
670
+
671
+ it('should generate different hashes for different inputs', () => {
672
+ const result1 = md5('test-string-1');
673
+ const result2 = md5('test-string-2');
674
+ expect(result1).not.toBe(result2);
675
+ });
676
+
677
+ it('should return a 32 character hexadecimal string', () => {
678
+ const result = md5('test-string');
679
+ expect(result).toMatch(/^[0-9a-f]{32}$/);
680
+ });
681
+ });
682
+
683
+ describe('safeJsonParse', () => {
684
+ it('should parse valid JSON strings', () => {
685
+ const input = '{"key":"value","number":123}';
686
+ const result = safeJsonParse(input, null);
687
+ expect(result).toEqual({ key: 'value', number: 123 });
688
+ });
689
+
690
+ it('should return the default value when parsing invalid JSON', () => {
691
+ const input = '{invalid json}';
692
+ const defaultValue = { default: true };
693
+ const result = safeJsonParse(input, defaultValue);
694
+ expect(result).toEqual(defaultValue);
695
+ });
696
+
697
+ it('should return the input when parsing fails and no default value is provided', () => {
698
+ const input = '{invalid json}';
699
+ const result = safeJsonParse(input, undefined);
700
+ expect(result).toBe(input);
701
+ });
702
+ });
703
+
704
+ describe('getExplorerLink', () => {
705
+ it('should return undefined when chainHost is not provided', () => {
706
+ const result = getExplorerLink({
707
+ type: 'asset',
708
+ did: 'did:example:123',
709
+ chainHost: undefined,
710
+ });
711
+ expect(result).toBeUndefined();
712
+ });
713
+
714
+ it('should construct a proper asset URL with the given parameters', () => {
715
+ const result = getExplorerLink({
716
+ type: 'asset',
717
+ did: 'did:example:123',
718
+ chainHost: 'https://chain.example.com',
719
+ });
720
+ expect(result).toBe('https://chain.example.com/explorer/assets/did:example:123');
721
+ });
722
+
723
+ it('should construct a proper account URL with the given parameters', () => {
724
+ const result = getExplorerLink({
725
+ type: 'account',
726
+ did: 'did:example:123',
727
+ chainHost: 'https://chain.example.com',
728
+ });
729
+ expect(result).toBe('https://chain.example.com/explorer/accounts/did:example:123');
730
+ });
731
+
732
+ it('should append query parameters when provided', () => {
733
+ const result = getExplorerLink({
734
+ type: 'tx',
735
+ did: 'txhash123',
736
+ chainHost: 'https://chain.example.com',
737
+ queryParams: { foo: 'bar', baz: 'qux' },
738
+ });
739
+ expect(result).toBe('https://chain.example.com/explorer/txs/txhash123?foo=bar&baz=qux');
740
+ });
741
+
742
+ it('should construct a default URL when type is not recognized', () => {
743
+ const result = getExplorerLink({
744
+ type: 'unknown',
745
+ did: 'did:example:123',
746
+ chainHost: 'https://chain.example.com',
747
+ });
748
+ expect(result).toBe('https://chain.example.com/');
749
+ });
750
+
751
+ it('should handle invalid chain host URLs', () => {
752
+ const result = getExplorerLink({
753
+ type: 'asset',
754
+ did: 'did:example:123',
755
+ chainHost: 'invalid-url',
756
+ });
757
+ expect(result).toBeUndefined();
758
+ });
759
+ });
760
+
761
+ describe('resolveAddressChainTypes', () => {
762
+ it('should return ethereum, base, and arcblock for Ethereum addresses', () => {
763
+ const result = resolveAddressChainTypes('0x71C7656EC7ab88b098defB751B7401B5f6d8976F');
764
+ expect(result).toEqual(['ethereum', 'base', 'arcblock']);
765
+ });
766
+
767
+ it('should return arcblock for non-Ethereum addresses', () => {
768
+ const result = resolveAddressChainTypes('did:abt:z1muQ3xqHQK2uiACHyChikobsiY5kLqtShA');
769
+ expect(result).toEqual(['arcblock']);
770
+ });
771
+ });
772
+
773
+ describe('formatCurrencyInfo', () => {
774
+ it('should format amount with currency symbol when isToken is true', () => {
775
+ const paymentCurrency = { symbol: 'ETH', decimal: 18 } as PaymentCurrency;
776
+ const result = formatCurrencyInfo('10', paymentCurrency, null, true);
777
+ expect(result).toBe('10 ETH');
778
+ });
779
+
780
+ it('should use fromUnitToToken to format amount when isToken is false', () => {
781
+ const paymentCurrency = { symbol: 'ETH', decimal: 18 } as PaymentCurrency;
782
+ // Mock the fromUnitToToken function to return a predictable value
783
+ // In a real test, you might use jest.mock to mock this dependency
784
+ // For this example, we'll assume fromUnitToToken converts correctly
785
+ const result = formatCurrencyInfo('1000000000000000000', paymentCurrency, null);
786
+ // In reality, this would be '1 ETH'
787
+ expect(result).toContain('ETH');
788
+ });
789
+
790
+ it('should include payment method name for non-arcblock payment methods', () => {
791
+ const paymentCurrency = { symbol: 'USD', decimal: 2 } as PaymentCurrency;
792
+ const paymentMethod = { type: 'stripe', name: 'Credit Card' } as PaymentMethod;
793
+ const result = formatCurrencyInfo('1000', paymentCurrency, paymentMethod, true);
794
+ expect(result).toBe('1000 USD (Credit Card)');
795
+ });
796
+
797
+ it('should not include payment method name for arcblock payment methods', () => {
798
+ const paymentCurrency = { symbol: 'ABT', decimal: 18 } as PaymentCurrency;
799
+ const paymentMethod = { type: 'arcblock', name: 'ArcBlock' } as PaymentMethod;
800
+ const result = formatCurrencyInfo('5', paymentCurrency, paymentMethod, true);
801
+ expect(result).toBe('5 ABT');
802
+ });
803
+
804
+ it('should handle undefined or empty amount', () => {
805
+ const paymentCurrency = { symbol: 'ETH', decimal: 18 } as PaymentCurrency;
806
+ const result = formatCurrencyInfo('', paymentCurrency, null, true);
807
+ expect(result).toBe('0 ETH');
808
+ });
809
+
810
+ it('should use default values when currency information is missing', () => {
811
+ const paymentCurrency = {} as PaymentCurrency;
812
+ const result = formatCurrencyInfo('10', paymentCurrency, null, true);
813
+ expect(result).toBe('10 ');
814
+ });
815
+ });
816
+
817
+ describe('getExplorerTxUrl', () => {
818
+ it('should return empty string when explorerHost or txHash is not provided', () => {
819
+ const result1 = getExplorerTxUrl({
820
+ explorerHost: '',
821
+ txHash: 'hash123',
822
+ type: 'ethereum',
823
+ });
824
+ expect(result1).toBe('');
825
+
826
+ const result2 = getExplorerTxUrl({
827
+ explorerHost: 'https://explorer.example.com',
828
+ txHash: '',
829
+ type: 'ethereum',
830
+ });
831
+ expect(result2).toBe('');
832
+ });
833
+
834
+ it('should construct a proper URL for arcblock type', () => {
835
+ const result = getExplorerTxUrl({
836
+ explorerHost: 'https://explorer.example.com',
837
+ txHash: 'hash123',
838
+ type: 'arcblock',
839
+ });
840
+ expect(result).toBe('https://explorer.example.com/txs/hash123');
841
+ });
842
+
843
+ it('should construct a proper URL for non-arcblock types', () => {
844
+ const result = getExplorerTxUrl({
845
+ explorerHost: 'https://explorer.example.com',
846
+ txHash: 'hash123',
847
+ type: 'ethereum',
848
+ });
849
+ expect(result).toBe('https://explorer.example.com/tx/hash123');
850
+ });
851
+
852
+ it('should handle different blockchain types correctly', () => {
853
+ const testCases = [
854
+ { type: 'ethereum', expected: '/tx/' },
855
+ { type: 'base', expected: '/tx/' },
856
+ { type: 'bitcoin', expected: '/tx/' },
857
+ { type: 'arcblock', expected: '/txs/' },
858
+ ];
859
+
860
+ testCases.forEach(({ type, expected }) => {
861
+ const result = getExplorerTxUrl({
862
+ explorerHost: 'https://explorer.example.com',
863
+ txHash: 'hash123',
864
+ type: type as any,
865
+ });
866
+ expect(result).toContain(expected);
867
+ });
868
+ });
869
+ });
package/blocklet.yml CHANGED
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.18.34
17
+ version: 1.18.36
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
@@ -71,6 +71,7 @@ capabilities:
71
71
  navigation: true
72
72
  clusterMode: false
73
73
  component: true
74
+ didSpace: requiredOnConnect
74
75
  screenshots:
75
76
  - setting.png
76
77
  - payment.png
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.18.34",
3
+ "version": "1.18.36",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -47,18 +47,19 @@
47
47
  "@abtnode/cron": "^1.16.42",
48
48
  "@arcblock/did": "^1.20.2",
49
49
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
50
- "@arcblock/did-connect": "^2.13.12",
50
+ "@arcblock/did-connect": "^2.13.13",
51
51
  "@arcblock/did-util": "^1.20.2",
52
52
  "@arcblock/jwt": "^1.20.2",
53
- "@arcblock/ux": "^2.13.12",
53
+ "@arcblock/ux": "^2.13.13",
54
54
  "@arcblock/validator": "^1.20.2",
55
+ "@blocklet/did-space-js": "^1.0.48",
55
56
  "@blocklet/js-sdk": "^1.16.42",
56
57
  "@blocklet/logger": "^1.16.42",
57
- "@blocklet/payment-react": "1.18.34",
58
+ "@blocklet/payment-react": "1.18.36",
58
59
  "@blocklet/sdk": "^1.16.42",
59
- "@blocklet/ui-react": "^2.13.12",
60
- "@blocklet/uploader": "^0.1.83",
61
- "@blocklet/xss": "^0.1.32",
60
+ "@blocklet/ui-react": "^2.13.13",
61
+ "@blocklet/uploader": "^0.1.84",
62
+ "@blocklet/xss": "^0.1.33",
62
63
  "@mui/icons-material": "^5.16.6",
63
64
  "@mui/lab": "^5.0.0-alpha.173",
64
65
  "@mui/material": "^5.16.6",
@@ -122,7 +123,7 @@
122
123
  "devDependencies": {
123
124
  "@abtnode/types": "^1.16.42",
124
125
  "@arcblock/eslint-config-ts": "^0.3.3",
125
- "@blocklet/payment-types": "1.18.34",
126
+ "@blocklet/payment-types": "1.18.36",
126
127
  "@types/cookie-parser": "^1.4.7",
127
128
  "@types/cors": "^2.8.17",
128
129
  "@types/debug": "^4.1.12",
@@ -168,5 +169,5 @@
168
169
  "parser": "typescript"
169
170
  }
170
171
  },
171
- "gitHead": "0ddb7c08956891409919c8027b56b77eba1726d4"
172
+ "gitHead": "98872e06bac0c437b53a6648ce4487e9f6d2336b"
172
173
  }
package/scripts/sdk.js CHANGED
@@ -69,16 +69,72 @@ const checkoutModule = {
69
69
  subscription_data: {
70
70
  no_stake: true,
71
71
  },
72
+ });
73
+ console.log('createBatchSubscription', checkoutSession);
74
+ return checkoutSession;
75
+ },
76
+
77
+ // 批量订阅 + 免质押 + 自定义表单规则
78
+ async createBatchSubscriptionWithCustomField() {
79
+ const checkoutSession = await payment.checkout.sessions.create({
80
+ mode: 'subscription',
81
+ line_items: [
82
+ {
83
+ price_id: 'price_fQFIS12yi0JR3KePLmitjrhA',
84
+ quantity: 1,
85
+ subscription_data: {
86
+ metadata: { test: 'test price_fQFIS12yi0JR3KePLmitjrhA' },
87
+ },
88
+ },
89
+ {
90
+ price_id: 'price_PXyI9Duz99eqty1AqbaEc73u',
91
+ quantity: 1,
92
+ subscription_data: {
93
+ metadata: { test: 'test price_PXyI9Duz99eqty1AqbaEc73u' },
94
+ },
95
+ },
96
+ ],
97
+ enable_subscription_grouping: true,
98
+ subscription_data: {
99
+ no_stake: true,
100
+ },
101
+ phone_number_collection: {
102
+ enabled: true,
103
+ },
104
+ billing_address_collection: 'required',
72
105
  metadata: {
73
106
  page_info: {
74
107
  form_purpose_description: {
75
108
  en: 'Information collected helps us process your payment and deliver our services.',
76
109
  zh: '收集的信息帮助我们处理您的付款并提供服务。',
77
110
  },
111
+ field_validation: {
112
+ customer_name: {
113
+ pattern: '^[a-zA-Z\\s]{2,50}$',
114
+ pattern_message: {
115
+ en: 'Name should only contain 2-50 letters and spaces',
116
+ zh: '姓名应只包含2-50个字母和空格',
117
+ },
118
+ },
119
+ customer_email: {
120
+ pattern: '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$',
121
+ pattern_message: {
122
+ en: 'Please enter a valid email address',
123
+ zh: '请输入有效的电子邮件地址',
124
+ },
125
+ },
126
+ 'billing_address.line1': {
127
+ pattern: '^.{5,100}$',
128
+ pattern_message: {
129
+ en: 'Address should be 5-100 characters',
130
+ zh: '地址应为5-100个字符',
131
+ },
132
+ },
133
+ },
78
134
  },
79
135
  },
80
136
  });
81
- console.log('createBatchSubscription', checkoutSession);
137
+ console.log('createBatchSubscriptionWithCustomField', checkoutSession);
82
138
  return checkoutSession;
83
139
  },
84
140
 
@@ -469,7 +525,7 @@ const testModules = {
469
525
 
470
526
  async function runTest() {
471
527
  payment.environments.setTestMode(true);
472
- await testModules.checkout.createBatchSubscription();
528
+ await testModules.checkout.createBatchSubscriptionWithCustomField();
473
529
  }
474
530
 
475
531
  async function main() {
@@ -1,17 +1,13 @@
1
1
  import type { TCustomer } from '@blocklet/payment-types';
2
2
  import { Link } from 'react-router-dom';
3
-
3
+ import UserCard from '@arcblock/ux/lib/UserCard';
4
4
  import { getCustomerAvatar } from '@blocklet/payment-react';
5
- import DID from '@arcblock/ux/lib/DID';
6
- import { Box, Typography } from '@mui/material';
7
- import InfoCard from '../info-card';
8
5
 
9
6
  export default function CustomerLink({
10
7
  customer,
11
8
  linked,
12
9
  linkTo,
13
10
  size,
14
- tooltip,
15
11
  }: {
16
12
  customer: TCustomer;
17
13
  linked?: boolean;
@@ -22,61 +18,40 @@ export default function CustomerLink({
22
18
  if (!customer) {
23
19
  return null;
24
20
  }
21
+ const CustomerCard = (
22
+ // @ts-ignore
23
+ <UserCard
24
+ did={customer?.did}
25
+ showHoverCard
26
+ sx={{
27
+ border: 'none',
28
+ p: 0,
29
+ minWidth: 0,
30
+ }}
31
+ avatarProps={{
32
+ size: size === 'small' ? 24 : 40,
33
+ }}
34
+ showDid={size !== 'small'}
35
+ {...(customer.metadata.anonymous === true
36
+ ? {
37
+ user: {
38
+ fullName: customer.name || customer.email,
39
+ did: customer.did,
40
+ email: customer.email,
41
+ avatar: getCustomerAvatar(
42
+ customer?.did,
43
+ customer?.updated_at ? new Date(customer.updated_at).toISOString() : ''
44
+ ),
45
+ },
46
+ }
47
+ : {})}
48
+ />
49
+ );
25
50
  if (linked) {
26
- return (
27
- <Link to={linkTo || `/admin/customers/${customer.id}`}>
28
- <Box sx={{ '.info-card-wrapper': { cursor: 'pointer' }, '.info-card': { minWidth: 0 } }}>
29
- {/* @ts-ignore */}
30
- <InfoCard
31
- logo={getCustomerAvatar(
32
- customer?.did,
33
- customer?.updated_at ? new Date(customer.updated_at).toISOString() : '',
34
- size === 'small' ? 24 : 48
35
- )}
36
- name={
37
- <Typography
38
- sx={{ maxWidth: 208, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
39
- className="customer-link-name">
40
- {customer.name || customer.email}
41
- </Typography>
42
- }
43
- {...(size === 'small'
44
- ? { tooltip: tooltip ? <DID did={customer?.did} /> : false, size: 24 }
45
- : {
46
- description: <DID did={customer?.did} compact responsive={false} sx={{ whiteSpace: 'nowrap' }} />,
47
- size: 48,
48
- })}
49
- />
50
- </Box>
51
- </Link>
52
- );
51
+ return <Link to={linkTo || `/admin/customers/${customer.id}`}>{CustomerCard}</Link>;
53
52
  }
54
53
 
55
- return (
56
- <Box sx={{ '.info-card': { minWidth: 0 } }}>
57
- {/* @ts-ignore */}
58
- <InfoCard
59
- logo={getCustomerAvatar(
60
- customer.did,
61
- customer.updated_at ? new Date(customer.updated_at).toISOString() : '',
62
- size === 'small' ? 24 : 48
63
- )}
64
- name={
65
- <Typography
66
- sx={{ maxWidth: 320, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}
67
- className="customer-link-name">
68
- {customer.name || customer.email}
69
- </Typography>
70
- }
71
- {...(size === 'small'
72
- ? { tooltip: tooltip ? <DID did={customer?.did} /> : false, size: 24 }
73
- : {
74
- description: <DID did={customer?.did} compact responsive={false} sx={{ whiteSpace: 'nowrap' }} />,
75
- size: 48,
76
- })}
77
- />
78
- </Box>
79
- );
54
+ return CustomerCard;
80
55
  }
81
56
 
82
57
  CustomerLink.defaultProps = {
@@ -282,7 +282,7 @@ export default function InvoiceList({
282
282
  options: {
283
283
  customBodyRenderLite: (_: string, index: number) => {
284
284
  const item = data.list[index] as TInvoiceExpanded;
285
- return <CustomerLink customer={item.customer} />;
285
+ return <CustomerLink customer={item.customer} size="small" />;
286
286
  },
287
287
  },
288
288
  });
@@ -15,6 +15,8 @@ import { useLocalStorageState } from 'ahooks';
15
15
  import { useEffect, useState } from 'react';
16
16
  import { Link } from 'react-router-dom';
17
17
 
18
+ import DID from '@arcblock/ux/lib/DID';
19
+ import ShortenLabel from '@arcblock/ux/lib/UserCard/Content/shorten-label';
18
20
  import { debounce, getAppInfo } from '../../libs/util';
19
21
  import CustomerLink from '../customer/link';
20
22
  import FilterToolbar from '../filter-toolbar';
@@ -202,8 +204,12 @@ export default function PayoutList({ customer_id, payment_intent_id, status, fea
202
204
  if (appInfo) {
203
205
  return (
204
206
  <InfoCard
205
- name={appInfo.name}
206
- description={`${item.destination.slice(0, 6)}...${item.destination.slice(-6)}`}
207
+ name={
208
+ <ShortenLabel sx={{ fontWeight: 500 }} maxLength={30}>
209
+ {appInfo.name}
210
+ </ShortenLabel>
211
+ }
212
+ description={<DID did={item.destination} compact responsive={false} sx={{ whiteSpace: 'nowrap' }} />}
207
213
  logo={appInfo.avatar}
208
214
  size={40}
209
215
  />
package/src/libs/util.ts CHANGED
@@ -3,14 +3,12 @@
3
3
  /* eslint-disable @typescript-eslint/indent */
4
4
  import { formatCheckoutHeadlines, formatPrice, getPrefix, getPriceCurrencyOptions } from '@blocklet/payment-react';
5
5
  import type {
6
- ChainType,
7
6
  LineItem,
8
7
  PriceRecurring,
9
8
  TInvoiceExpanded,
10
9
  TLineItemExpanded,
11
10
  TPaymentCurrency,
12
11
  TPaymentLinkExpanded,
13
- TPaymentMethod,
14
12
  TPaymentMethodExpanded,
15
13
  TPrice,
16
14
  TProductExpanded,
@@ -350,17 +348,6 @@ export function getAppInfo(address: string): { name: string; avatar: string; typ
350
348
  return null;
351
349
  }
352
350
 
353
- export function getTokenBalanceLink(method: TPaymentMethod, address: string) {
354
- const explorerHost = (method?.settings?.[method?.type as ChainType] as any)?.explorer_host || '';
355
- if (method.type === 'arcblock' && address) {
356
- return joinURL(explorerHost, 'accounts', address, 'tokens');
357
- }
358
- if (['ethereum', 'base'].includes(method.type) && address) {
359
- return joinURL(explorerHost, 'address', address);
360
- }
361
- return '';
362
- }
363
-
364
351
  export function isWillCanceled(subscription: TSubscriptionExpanded) {
365
352
  const now = Date.now() / 1000;
366
353
  if (
@@ -211,8 +211,8 @@ export default function CustomerDetail(props: { id: string }) {
211
211
  52
212
212
  )}
213
213
  alt={data.customer.name}
214
- variant="square"
215
- sx={{ width: 52, height: 52, borderRadius: 'var(--radius-s, 4px)' }}
214
+ variant="circular"
215
+ sx={{ width: 52, height: 52 }}
216
216
  />
217
217
  <Typography variant="h2" sx={{ fontWeight: 600 }}>
218
218
  {data.customer.name}
@@ -75,9 +75,8 @@ export default function CustomersList() {
75
75
  item?.updated_at ? new Date(item.updated_at).toISOString() : '',
76
76
  48
77
77
  )}
78
- variant="square"
78
+ variant="circular"
79
79
  alt={item?.name}
80
- sx={{ borderRadius: 'var(--radius-m, 8px)' }}
81
80
  />
82
81
  <Typography sx={{ wordBreak: 'break-all' }}>{item.name}</Typography>
83
82
  </Stack>