payment-kit 1.18.39 → 1.18.41

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.
Files changed (38) hide show
  1. package/api/src/libs/subscription.ts +107 -97
  2. package/api/src/routes/checkout-sessions.ts +3 -1
  3. package/api/src/routes/connect/subscribe.ts +4 -0
  4. package/api/src/routes/subscriptions.ts +3 -1
  5. package/blocklet.yml +1 -1
  6. package/package.json +24 -24
  7. package/scripts/sdk.js +27 -1
  8. package/src/components/customer/link.tsx +6 -0
  9. package/src/components/info-card.tsx +2 -2
  10. package/src/components/info-metric.tsx +1 -1
  11. package/src/components/metadata/list.tsx +4 -2
  12. package/src/components/subscription/description.tsx +8 -6
  13. package/src/components/subscription/portal/actions.tsx +105 -36
  14. package/src/components/subscription/portal/list.tsx +152 -74
  15. package/src/components/subscription/status.tsx +17 -5
  16. package/src/locales/en.tsx +12 -7
  17. package/src/locales/zh.tsx +6 -2
  18. package/src/pages/admin/billing/invoices/detail.tsx +1 -5
  19. package/src/pages/admin/billing/subscriptions/detail.tsx +1 -5
  20. package/src/pages/admin/customers/customers/detail.tsx +1 -5
  21. package/src/pages/admin/developers/events/detail.tsx +1 -5
  22. package/src/pages/admin/developers/index.tsx +1 -1
  23. package/src/pages/admin/developers/webhooks/detail.tsx +1 -3
  24. package/src/pages/admin/overview.tsx +4 -4
  25. package/src/pages/admin/payments/intents/detail.tsx +1 -5
  26. package/src/pages/admin/payments/payouts/detail.tsx +1 -5
  27. package/src/pages/admin/payments/refunds/detail.tsx +1 -5
  28. package/src/pages/admin/products/links/detail.tsx +1 -5
  29. package/src/pages/admin/products/prices/detail.tsx +1 -5
  30. package/src/pages/admin/products/pricing-tables/detail.tsx +1 -5
  31. package/src/pages/admin/products/products/detail.tsx +1 -5
  32. package/src/pages/admin/settings/payment-methods/index.tsx +1 -1
  33. package/src/pages/customer/index.tsx +67 -138
  34. package/src/pages/customer/payout/detail.tsx +37 -49
  35. package/src/pages/customer/subscription/change-payment.tsx +2 -35
  36. package/src/pages/customer/subscription/detail.tsx +1 -5
  37. package/src/pages/integrations/donations/index.tsx +1 -1
  38. package/src/pages/integrations/overview.tsx +1 -1
@@ -1354,126 +1354,136 @@ export async function slashOverdraftProtectionStake(subscription: Subscription,
1354
1354
 
1355
1355
  let slashAmount = new BN(0);
1356
1356
  const slashInvoices: Invoice[] = [];
1357
- const updatePromises = invoices.map(async (invoice) => {
1357
+ for (const invoice of invoices) {
1358
1358
  if (!invoice.payment_intent_id) {
1359
1359
  await emitAsync(
1360
1360
  'invoice.queued',
1361
1361
  invoice.id,
1362
1362
  { invoiceId: invoice.id, retryOnError: false, justCreate: true },
1363
- {
1364
- sync: true,
1365
- }
1363
+ { sync: true }
1366
1364
  );
1367
1365
  await invoice.reload();
1368
1366
  }
1367
+
1369
1368
  const paymentIntent = await PaymentIntent.findByPk(invoice.payment_intent_id);
1370
1369
  if (paymentIntent) {
1371
- if (paymentIntent.status === 'succeeded' && invoice.status === 'paid') {
1370
+ const isAlreadyPaid = paymentIntent.status === 'succeeded' && invoice.status === 'paid';
1371
+
1372
+ if (!isAlreadyPaid) {
1373
+ const tmp = slashAmount.add(new BN(invoice.amount_remaining));
1374
+ if (tmp.lte(new BN(remainingStake))) {
1375
+ slashAmount = tmp;
1376
+ slashInvoices.push(invoice);
1377
+ } else {
1378
+ break;
1379
+ }
1380
+ } else {
1372
1381
  logger.info('PaymentIntent and Invoice already updated', {
1373
1382
  subscription: subscription.id,
1374
1383
  paymentIntent: paymentIntent.id,
1375
1384
  invoice: invoice.id,
1376
1385
  });
1377
- return;
1378
1386
  }
1379
- const tmp = slashAmount.add(new BN(invoice.amount_remaining));
1380
-
1381
- if (tmp.lte(new BN(remainingStake))) {
1382
- slashAmount = tmp;
1383
- slashInvoices.push(invoice);
1384
- try {
1385
- const stakeEnough = await checkRemainingStake(
1386
- paymentMethod,
1387
- paymentCurrency,
1387
+ }
1388
+ }
1389
+
1390
+ const slashedInvoiceIds: string[] = [];
1391
+ const processInvoice = async (invoice: Invoice): Promise<void> => {
1392
+ const paymentIntent = await PaymentIntent.findByPk(invoice.payment_intent_id);
1393
+ if (!paymentIntent || (paymentIntent.status === 'succeeded' && invoice.status === 'paid')) return;
1394
+
1395
+ try {
1396
+ const stakeEnough = await checkRemainingStake(
1397
+ paymentMethod,
1398
+ paymentCurrency,
1399
+ address,
1400
+ invoice.amount_remaining
1401
+ );
1402
+ if (!stakeEnough.enough) {
1403
+ throw new Error(`Stake slashing aborted because no enough staking for invoice: ${invoice.id}`);
1404
+ }
1405
+ // do the slash
1406
+ const signed = await client.signSlashStakeTx({
1407
+ tx: {
1408
+ itx: {
1388
1409
  address,
1389
- invoice.amount_remaining
1390
- );
1391
- if (!stakeEnough.enough) {
1392
- throw new Error(`Stake slashing aborted because no enough staking for invoice: ${invoice.id}`);
1393
- }
1394
- // do the slash
1395
- const signed = await client.signSlashStakeTx({
1396
- tx: {
1397
- itx: {
1398
- address,
1399
- outputs: [
1400
- {
1401
- owner: wallet.address,
1402
- tokens: [{ address: paymentCurrency.contract, value: invoice.amount_remaining }],
1403
- },
1404
- ],
1405
- message: 'overdraft_exceeded',
1406
- data: {
1407
- typeUrl: 'json',
1408
- // @ts-ignore
1409
- value: {
1410
- appId: wallet.address,
1411
- reason: 'overdraft_exceeded',
1412
- subscriptionId: subscription.id,
1413
- invoiceId: invoice.id,
1414
- paymentIntentId: paymentIntent.id,
1415
- },
1416
- },
1410
+ outputs: [
1411
+ {
1412
+ owner: wallet.address,
1413
+ tokens: [{ address: paymentCurrency.contract, value: invoice.amount_remaining }],
1417
1414
  },
1418
- },
1419
- wallet,
1420
- });
1421
- // @ts-ignore
1422
- const { buffer } = await client.encodeSlashStakeTx({ tx: signed });
1423
- // @ts-ignore
1424
- const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, getGasPayerExtra(buffer));
1425
-
1426
- await paymentIntent.update({
1427
- status: 'succeeded',
1428
- amount_received: invoice.amount_remaining,
1429
- capture_method: 'manual',
1430
- last_payment_error: null,
1431
- payment_details: {
1432
- arcblock: {
1433
- tx_hash: txHash,
1434
- payer:
1435
- subscription.overdraft_protection?.payment_details?.arcblock?.payer ||
1436
- getSubscriptionPaymentAddress(subscription, 'arcblock'),
1437
- type: 'slash',
1415
+ ],
1416
+ message: 'overdraft_exceeded',
1417
+ data: {
1418
+ typeUrl: 'json',
1419
+ // @ts-ignore
1420
+ value: {
1421
+ appId: wallet.address,
1422
+ reason: 'overdraft_exceeded',
1423
+ subscriptionId: subscription.id,
1424
+ invoiceId: invoice.id,
1425
+ paymentIntentId: paymentIntent.id,
1438
1426
  },
1439
1427
  },
1440
- });
1441
- logger.info('PaymentIntent updated after stake slash', {
1442
- subscription: subscription.id,
1443
- paymentIntent: paymentIntent.id,
1444
- status: 'succeeded',
1445
- });
1446
-
1447
- await invoice.update({
1448
- status: 'paid',
1449
- paid: true,
1450
- amount_paid: invoice.amount_remaining,
1451
- amount_remaining: '0',
1452
- attempted: true,
1453
- attempt_count: invoice.attempt_count + 1,
1454
- status_transitions: { ...invoice.status_transitions, paid_at: dayjs().unix() },
1455
- });
1456
- logger.info('Invoice updated after stake slash', {
1457
- subscription: subscription.id,
1458
- invoice: invoice.id,
1459
- status: 'paid',
1460
- });
1461
- } catch (updateError) {
1462
- logger.error('stake slash failed', {
1463
- subscription: subscription.id,
1464
- invoice: invoice.id,
1465
- error: updateError,
1466
- });
1467
- throw updateError;
1468
- }
1469
- }
1428
+ },
1429
+ },
1430
+ wallet,
1431
+ });
1432
+ // @ts-ignore
1433
+ const { buffer } = await client.encodeSlashStakeTx({ tx: signed });
1434
+ // @ts-ignore
1435
+ const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, getGasPayerExtra(buffer));
1436
+
1437
+ await paymentIntent.update({
1438
+ status: 'succeeded',
1439
+ amount_received: invoice.amount_remaining,
1440
+ capture_method: 'manual',
1441
+ last_payment_error: null,
1442
+ payment_details: {
1443
+ arcblock: {
1444
+ tx_hash: txHash,
1445
+ payer:
1446
+ subscription.overdraft_protection?.payment_details?.arcblock?.payer ||
1447
+ getSubscriptionPaymentAddress(subscription, 'arcblock'),
1448
+ type: 'slash',
1449
+ },
1450
+ },
1451
+ });
1452
+ logger.info('PaymentIntent updated after stake slash', {
1453
+ subscription: subscription.id,
1454
+ paymentIntent: paymentIntent.id,
1455
+ status: 'succeeded',
1456
+ });
1457
+
1458
+ await invoice.update({
1459
+ status: 'paid',
1460
+ paid: true,
1461
+ amount_paid: invoice.amount_remaining,
1462
+ amount_remaining: '0',
1463
+ attempted: true,
1464
+ attempt_count: invoice.attempt_count + 1,
1465
+ status_transitions: { ...invoice.status_transitions, paid_at: dayjs().unix() },
1466
+ });
1467
+ logger.info('Invoice updated after stake slash', {
1468
+ subscription: subscription.id,
1469
+ invoice: invoice.id,
1470
+ status: 'paid',
1471
+ });
1472
+ slashedInvoiceIds.push(invoice.id);
1473
+ } catch (updateError) {
1474
+ logger.error('stake slash failed', {
1475
+ subscription: subscription.id,
1476
+ invoice: invoice.id,
1477
+ error: updateError,
1478
+ });
1479
+ throw updateError;
1470
1480
  }
1471
- });
1481
+ };
1472
1482
 
1473
- await Promise.all(updatePromises);
1474
- logger.info(`${slashInvoices.length} invoices updated after stake slash`, {
1483
+ await Promise.all(slashInvoices.map(processInvoice));
1484
+ logger.info(`${slashedInvoiceIds.length} invoices updated after stake slash`, {
1475
1485
  subscription: subscription.id,
1476
- invoices: slashInvoices.map((x) => x.id),
1486
+ invoices: slashedInvoiceIds,
1477
1487
  slashAmount: slashAmount.toString(),
1478
1488
  });
1479
1489
  } catch (error) {
@@ -346,7 +346,9 @@ const SubscriptionDataSchema = Joi.object({
346
346
  color: Joi.string().valid('primary', 'secondary', 'success', 'error', 'warning').optional(),
347
347
  variant: Joi.string().valid('text', 'contained', 'outlined').optional(),
348
348
  text: Joi.object().required(),
349
- link: Joi.string().uri().required(),
349
+ link: Joi.alternatives()
350
+ .try(Joi.string().uri(), Joi.string().pattern(/^\/[^\s]*$/))
351
+ .required(),
350
352
  type: Joi.string().valid('notification', 'custom').optional(),
351
353
  triggerEvents: Joi.array().items(Joi.string()).optional(),
352
354
  })
@@ -111,6 +111,10 @@ export default {
111
111
  });
112
112
  }
113
113
 
114
+ if (claimsList.length === 0) {
115
+ throw new Error('No available claims for your subscription at this time.');
116
+ }
117
+
114
118
  return claimsList;
115
119
  }
116
120
 
@@ -691,7 +691,9 @@ const updateSchema = Joi.object<{
691
691
  color: Joi.string().valid('primary', 'secondary', 'success', 'error', 'warning').optional(),
692
692
  variant: Joi.string().valid('text', 'contained', 'outlined').optional(),
693
693
  text: Joi.object().required(),
694
- link: Joi.string().uri().required(),
694
+ link: Joi.alternatives()
695
+ .try(Joi.string().uri(), Joi.string().pattern(/^\/[^\s]*$/))
696
+ .required(),
695
697
  type: Joi.string().valid('notification', 'custom').optional(),
696
698
  triggerEvents: Joi.array().items(Joi.string()).optional(),
697
699
  })
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.39
17
+ version: 1.18.41
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.18.39",
3
+ "version": "1.18.41",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -44,31 +44,31 @@
44
44
  ]
45
45
  },
46
46
  "dependencies": {
47
- "@abtnode/cron": "^1.16.42",
48
- "@arcblock/did": "^1.20.6",
47
+ "@abtnode/cron": "^1.16.43",
48
+ "@arcblock/did": "^1.20.8",
49
49
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
50
- "@arcblock/did-connect": "^2.13.23",
51
- "@arcblock/did-util": "^1.20.6",
52
- "@arcblock/jwt": "^1.20.6",
53
- "@arcblock/ux": "^2.13.23",
54
- "@arcblock/validator": "^1.20.6",
55
- "@blocklet/did-space-js": "^1.0.49",
56
- "@blocklet/js-sdk": "^1.16.42",
57
- "@blocklet/logger": "^1.16.42",
58
- "@blocklet/payment-react": "1.18.39",
59
- "@blocklet/sdk": "^1.16.42",
60
- "@blocklet/ui-react": "^2.13.23",
61
- "@blocklet/uploader": "^0.1.85",
62
- "@blocklet/xss": "^0.1.34",
50
+ "@arcblock/did-connect": "^2.13.29",
51
+ "@arcblock/did-util": "^1.20.8",
52
+ "@arcblock/jwt": "^1.20.8",
53
+ "@arcblock/ux": "^2.13.29",
54
+ "@arcblock/validator": "^1.20.8",
55
+ "@blocklet/did-space-js": "^1.0.51",
56
+ "@blocklet/js-sdk": "^1.16.43",
57
+ "@blocklet/logger": "^1.16.43",
58
+ "@blocklet/payment-react": "1.18.41",
59
+ "@blocklet/sdk": "^1.16.43",
60
+ "@blocklet/ui-react": "^2.13.29",
61
+ "@blocklet/uploader": "^0.1.90",
62
+ "@blocklet/xss": "^0.1.35",
63
63
  "@mui/icons-material": "^5.16.6",
64
64
  "@mui/lab": "^5.0.0-alpha.173",
65
65
  "@mui/material": "^5.16.6",
66
66
  "@mui/system": "^5.16.6",
67
- "@ocap/asset": "^1.20.6",
68
- "@ocap/client": "^1.20.6",
69
- "@ocap/mcrypto": "^1.20.6",
70
- "@ocap/util": "^1.20.6",
71
- "@ocap/wallet": "^1.20.6",
67
+ "@ocap/asset": "^1.20.8",
68
+ "@ocap/client": "^1.20.8",
69
+ "@ocap/mcrypto": "^1.20.8",
70
+ "@ocap/util": "^1.20.8",
71
+ "@ocap/wallet": "^1.20.8",
72
72
  "@stripe/react-stripe-js": "^2.7.3",
73
73
  "@stripe/stripe-js": "^2.4.0",
74
74
  "ahooks": "^3.8.0",
@@ -121,9 +121,9 @@
121
121
  "web3": "^4.16.0"
122
122
  },
123
123
  "devDependencies": {
124
- "@abtnode/types": "^1.16.42",
124
+ "@abtnode/types": "^1.16.43",
125
125
  "@arcblock/eslint-config-ts": "^0.3.3",
126
- "@blocklet/payment-types": "1.18.39",
126
+ "@blocklet/payment-types": "1.18.41",
127
127
  "@types/cookie-parser": "^1.4.7",
128
128
  "@types/cors": "^2.8.17",
129
129
  "@types/debug": "^4.1.12",
@@ -169,5 +169,5 @@
169
169
  "parser": "typescript"
170
170
  }
171
171
  },
172
- "gitHead": "9e9a5c18fc160094505e902e995f5a5a052d544f"
172
+ "gitHead": "4232e09576dd8aa8dcfb0e4dd5a1bbbb50480b7f"
173
173
  }
package/scripts/sdk.js CHANGED
@@ -196,6 +196,32 @@ const checkoutModule = {
196
196
  console.log('createCheckoutWithSettingId', checkoutSession);
197
197
  return { setting, checkoutSession };
198
198
  },
199
+
200
+ async createCheckoutWithServiceAction() {
201
+ const checkoutSession = await payment.checkout.sessions.create({
202
+ mode: 'subscription',
203
+ line_items: [{ price_id: 'price_fQFIS12yi0JR3KePLmitjrhA', quantity: 1 }],
204
+ subscription_data: {
205
+ service_actions: [
206
+ {
207
+ text: {
208
+ zh: '全链接测试',
209
+ en: 'Full Link Test',
210
+ },
211
+ link: 'https://baidu.com',
212
+ },
213
+ {
214
+ text: {
215
+ zh: '相对路径访问',
216
+ en: 'Relative Path Access',
217
+ },
218
+ link: '/.well-known/service/user/notifications',
219
+ },
220
+ ],
221
+ },
222
+ });
223
+ console.log('createCheckoutWithServiceAction', checkoutSession);
224
+ },
199
225
  };
200
226
 
201
227
  const paymentModule = {
@@ -515,7 +541,6 @@ const subscriptionModule = {
515
541
  },
516
542
  };
517
543
 
518
- // 测试模块注册
519
544
  const testModules = {
520
545
  checkout: checkoutModule,
521
546
  payment: paymentModule,
@@ -523,6 +548,7 @@ const testModules = {
523
548
  subscription: subscriptionModule,
524
549
  };
525
550
 
551
+ // 测试入口
526
552
  async function runTest() {
527
553
  payment.environments.setTestMode(true);
528
554
  await testModules.checkout.createBatchSubscriptionWithCustomField();
@@ -2,18 +2,21 @@ 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 { InfoType, UserCardProps } from '@arcblock/ux/lib/UserCard/types';
5
6
 
6
7
  export default function CustomerLink({
7
8
  customer,
8
9
  linked,
9
10
  linkTo,
10
11
  size,
12
+ cardProps,
11
13
  }: {
12
14
  customer: TCustomer;
13
15
  linked?: boolean;
14
16
  linkTo?: string;
15
17
  size?: 'default' | 'small';
16
18
  tooltip?: boolean;
19
+ cardProps?: UserCardProps;
17
20
  }) {
18
21
  if (!customer) {
19
22
  return null;
@@ -31,6 +34,7 @@ export default function CustomerLink({
31
34
  avatarProps={{
32
35
  size: size === 'small' ? 24 : 40,
33
36
  }}
37
+ popupInfoType={InfoType.Minimal}
34
38
  showDid={size !== 'small'}
35
39
  {...(customer.metadata.anonymous === true
36
40
  ? {
@@ -45,6 +49,7 @@ export default function CustomerLink({
45
49
  },
46
50
  }
47
51
  : {})}
52
+ {...cardProps}
48
53
  />
49
54
  );
50
55
  if (linked) {
@@ -59,4 +64,5 @@ CustomerLink.defaultProps = {
59
64
  linkTo: '',
60
65
  size: 'default',
61
66
  tooltip: true,
67
+ cardProps: {},
62
68
  };
@@ -41,11 +41,11 @@ export default function InfoCard(props: Props) {
41
41
  wordBreak: getWordBreakStyle(props.name),
42
42
  minWidth: 140,
43
43
  }}>
44
- <Typography variant="body1" color="text.primary" component="div">
44
+ <Typography variant="body2" color="text.primary" component="div">
45
45
  {props.name}
46
46
  </Typography>
47
47
  {props.description && (
48
- <Typography variant="subtitle1" color="text.secondary">
48
+ <Typography variant="subtitle2" color="text.secondary">
49
49
  {props.description}
50
50
  </Typography>
51
51
  )}
@@ -16,7 +16,7 @@ export default function InfoMetric(props: Props) {
16
16
  return (
17
17
  <>
18
18
  <Stack direction="column" alignItems="flex-start">
19
- <Typography component="div" variant="body1" mb={1} color="text.primary" sx={{ fontWeight: 500 }}>
19
+ <Typography component="div" variant="subtitle2" mb={1} color="text.primary">
20
20
  {props.label}
21
21
  {!!props.tip && (
22
22
  <Tooltip title={props.tip}>
@@ -18,10 +18,12 @@ export default function MetadataList({
18
18
  if (isEmpty(data)) {
19
19
  return (
20
20
  <Box sx={{ textAlign: 'center' }}>
21
- <Typography color="text.primary" fontWeight={500}>
21
+ <Typography variant="subtitle1" color="text.primary">
22
22
  {t('common.metadata.empty')}
23
23
  </Typography>
24
- <Typography color="text.lighter">{t('common.metadata.emptyTip')}</Typography>
24
+ <Typography variant="body2" color="text.lighter">
25
+ {t('common.metadata.emptyTip')}
26
+ </Typography>
25
27
  <Button sx={{ color: 'text.link' }} onClick={handleEditMetadata}>
26
28
  {t('common.add')}
27
29
  </Button>
@@ -5,17 +5,18 @@ import { Stack, Tooltip, Typography } from '@mui/material';
5
5
 
6
6
  type Props = {
7
7
  subscription: TSubscriptionExpanded;
8
- variant?: 'body1' | 'h5' | 'h4' | 'h3' | 'h2' | 'h1';
8
+ variant?: 'subtitle1' | 'subtitle2' | 'body1' | 'body2' | 'h5' | 'h4' | 'h3' | 'h2' | 'h1';
9
9
  hideSubscription?: boolean;
10
+ maxLength?: number;
10
11
  };
11
12
 
12
- export default function SubscriptionDescription({ subscription, variant, hideSubscription }: Props) {
13
+ export default function SubscriptionDescription({ subscription, variant, hideSubscription, maxLength = 80 }: Props) {
13
14
  const { isMobile } = useMobile();
14
15
  if (subscription.description) {
15
16
  return (
16
17
  <Stack direction="row" alignItems="center" spacing={1}>
17
- <Typography variant={variant} fontWeight={600} className="subscription-description">
18
- <TruncatedText text={subscription.description} maxLength={80} useWidth />
18
+ <Typography variant={variant} className="subscription-description">
19
+ <TruncatedText text={subscription.description} maxLength={maxLength} useWidth />
19
20
  </Typography>
20
21
  {!hideSubscription && !isMobile && (
21
22
  <Tooltip title={formatSubscriptionProduct(subscription.items)}>
@@ -27,8 +28,8 @@ export default function SubscriptionDescription({ subscription, variant, hideSub
27
28
  }
28
29
 
29
30
  return (
30
- <Typography variant={variant} fontWeight={600}>
31
- {formatSubscriptionProduct(subscription.items)}
31
+ <Typography variant={variant}>
32
+ <TruncatedText text={formatSubscriptionProduct(subscription.items)} maxLength={maxLength} useWidth />
32
33
  </Typography>
33
34
  );
34
35
  }
@@ -36,4 +37,5 @@ export default function SubscriptionDescription({ subscription, variant, hideSub
36
37
  SubscriptionDescription.defaultProps = {
37
38
  variant: 'body1',
38
39
  hideSubscription: false,
40
+ maxLength: 80,
39
41
  };