payment-kit 1.19.0 → 1.19.2

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 (139) hide show
  1. package/api/src/crons/index.ts +8 -0
  2. package/api/src/index.ts +4 -0
  3. package/api/src/libs/credit-grant.ts +146 -0
  4. package/api/src/libs/env.ts +1 -0
  5. package/api/src/libs/invoice.ts +4 -3
  6. package/api/src/libs/notification/template/base.ts +388 -2
  7. package/api/src/libs/notification/template/customer-credit-grant-granted.ts +149 -0
  8. package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +151 -0
  9. package/api/src/libs/notification/template/customer-credit-insufficient.ts +254 -0
  10. package/api/src/libs/notification/template/subscription-canceled.ts +193 -202
  11. package/api/src/libs/notification/template/subscription-refund-succeeded.ts +215 -237
  12. package/api/src/libs/notification/template/subscription-renewed.ts +130 -200
  13. package/api/src/libs/notification/template/subscription-succeeded.ts +100 -202
  14. package/api/src/libs/notification/template/subscription-trial-start.ts +142 -188
  15. package/api/src/libs/notification/template/subscription-trial-will-end.ts +146 -174
  16. package/api/src/libs/notification/template/subscription-upgraded.ts +96 -192
  17. package/api/src/libs/notification/template/subscription-will-canceled.ts +94 -135
  18. package/api/src/libs/notification/template/subscription-will-renew.ts +220 -245
  19. package/api/src/libs/payment.ts +69 -0
  20. package/api/src/libs/queue/index.ts +3 -2
  21. package/api/src/libs/session.ts +8 -0
  22. package/api/src/libs/subscription.ts +74 -3
  23. package/api/src/libs/util.ts +3 -1
  24. package/api/src/libs/ws.ts +23 -1
  25. package/api/src/locales/en.ts +33 -0
  26. package/api/src/locales/zh.ts +31 -0
  27. package/api/src/queues/credit-consume.ts +728 -0
  28. package/api/src/queues/credit-grant.ts +572 -0
  29. package/api/src/queues/notification.ts +173 -128
  30. package/api/src/queues/payment.ts +210 -122
  31. package/api/src/queues/subscription.ts +179 -0
  32. package/api/src/routes/checkout-sessions.ts +157 -9
  33. package/api/src/routes/connect/shared.ts +3 -2
  34. package/api/src/routes/credit-grants.ts +241 -0
  35. package/api/src/routes/credit-transactions.ts +208 -0
  36. package/api/src/routes/customers.ts +34 -5
  37. package/api/src/routes/index.ts +8 -0
  38. package/api/src/routes/meter-events.ts +347 -0
  39. package/api/src/routes/meters.ts +219 -0
  40. package/api/src/routes/payment-currencies.ts +20 -2
  41. package/api/src/routes/payment-links.ts +1 -1
  42. package/api/src/routes/payment-methods.ts +14 -2
  43. package/api/src/routes/prices.ts +43 -0
  44. package/api/src/routes/pricing-table.ts +13 -7
  45. package/api/src/routes/products.ts +63 -4
  46. package/api/src/routes/settings.ts +1 -1
  47. package/api/src/routes/subscriptions.ts +4 -0
  48. package/api/src/routes/webhook-endpoints.ts +0 -3
  49. package/api/src/store/migrations/20250610-billing-credit.ts +43 -0
  50. package/api/src/store/models/credit-grant.ts +486 -0
  51. package/api/src/store/models/credit-transaction.ts +268 -0
  52. package/api/src/store/models/customer.ts +8 -0
  53. package/api/src/store/models/index.ts +52 -1
  54. package/api/src/store/models/meter-event.ts +423 -0
  55. package/api/src/store/models/meter.ts +176 -0
  56. package/api/src/store/models/payment-currency.ts +66 -14
  57. package/api/src/store/models/price.ts +6 -0
  58. package/api/src/store/models/product.ts +2 -2
  59. package/api/src/store/models/subscription.ts +24 -0
  60. package/api/src/store/models/types.ts +28 -2
  61. package/api/tests/libs/subscription.spec.ts +53 -0
  62. package/blocklet.yml +9 -1
  63. package/package.json +4 -4
  64. package/scripts/sdk.js +233 -1
  65. package/src/app.tsx +10 -0
  66. package/src/components/collapse.tsx +11 -1
  67. package/src/components/conditional-section.tsx +87 -0
  68. package/src/components/customer/credit-grant-item-list.tsx +99 -0
  69. package/src/components/customer/credit-overview.tsx +246 -0
  70. package/src/components/customer/form.tsx +7 -3
  71. package/src/components/invoice/list.tsx +19 -1
  72. package/src/components/metadata/form.tsx +287 -91
  73. package/src/components/meter/actions.tsx +101 -0
  74. package/src/components/meter/add-usage-dialog.tsx +239 -0
  75. package/src/components/meter/events-list.tsx +657 -0
  76. package/src/components/meter/form.tsx +245 -0
  77. package/src/components/meter/products.tsx +264 -0
  78. package/src/components/meter/usage-guide.tsx +174 -0
  79. package/src/components/payment-currency/form.tsx +2 -0
  80. package/src/components/payment-intent/list.tsx +19 -1
  81. package/src/components/payment-link/item.tsx +2 -2
  82. package/src/components/payment-link/preview.tsx +1 -1
  83. package/src/components/payment-link/product-select.tsx +52 -12
  84. package/src/components/payment-method/arcblock.tsx +2 -0
  85. package/src/components/payment-method/base.tsx +2 -0
  86. package/src/components/payment-method/bitcoin.tsx +2 -0
  87. package/src/components/payment-method/ethereum.tsx +2 -0
  88. package/src/components/payment-method/stripe.tsx +2 -0
  89. package/src/components/payouts/list.tsx +19 -1
  90. package/src/components/payouts/portal/list.tsx +6 -11
  91. package/src/components/price/currency-select.tsx +56 -32
  92. package/src/components/price/form.tsx +912 -407
  93. package/src/components/pricing-table/preview.tsx +1 -1
  94. package/src/components/product/add-price.tsx +9 -7
  95. package/src/components/product/create.tsx +7 -4
  96. package/src/components/product/edit-price.tsx +21 -12
  97. package/src/components/product/features.tsx +17 -7
  98. package/src/components/product/form.tsx +100 -90
  99. package/src/components/refund/list.tsx +19 -1
  100. package/src/components/section/header.tsx +5 -18
  101. package/src/components/subscription/items/index.tsx +1 -1
  102. package/src/components/subscription/metrics.tsx +37 -5
  103. package/src/components/subscription/portal/actions.tsx +2 -1
  104. package/src/contexts/products.tsx +26 -9
  105. package/src/hooks/subscription.ts +34 -0
  106. package/src/libs/meter-utils.ts +196 -0
  107. package/src/libs/util.ts +4 -0
  108. package/src/locales/en.tsx +389 -5
  109. package/src/locales/zh.tsx +368 -1
  110. package/src/pages/admin/billing/index.tsx +61 -33
  111. package/src/pages/admin/billing/invoices/detail.tsx +1 -1
  112. package/src/pages/admin/billing/meters/create.tsx +60 -0
  113. package/src/pages/admin/billing/meters/detail.tsx +435 -0
  114. package/src/pages/admin/billing/meters/index.tsx +210 -0
  115. package/src/pages/admin/billing/meters/meter-event.tsx +346 -0
  116. package/src/pages/admin/billing/subscriptions/detail.tsx +47 -14
  117. package/src/pages/admin/customers/customers/credit-grant/detail.tsx +391 -0
  118. package/src/pages/admin/customers/customers/detail.tsx +14 -10
  119. package/src/pages/admin/customers/index.tsx +5 -0
  120. package/src/pages/admin/developers/events/detail.tsx +1 -1
  121. package/src/pages/admin/developers/index.tsx +1 -1
  122. package/src/pages/admin/payments/intents/detail.tsx +1 -1
  123. package/src/pages/admin/payments/payouts/detail.tsx +1 -1
  124. package/src/pages/admin/payments/refunds/detail.tsx +1 -1
  125. package/src/pages/admin/products/index.tsx +3 -2
  126. package/src/pages/admin/products/links/detail.tsx +1 -1
  127. package/src/pages/admin/products/prices/actions.tsx +16 -4
  128. package/src/pages/admin/products/prices/detail.tsx +30 -3
  129. package/src/pages/admin/products/prices/list.tsx +8 -1
  130. package/src/pages/admin/products/pricing-tables/detail.tsx +1 -1
  131. package/src/pages/admin/products/products/create.tsx +233 -57
  132. package/src/pages/admin/products/products/detail.tsx +2 -1
  133. package/src/pages/admin/settings/payment-methods/index.tsx +3 -0
  134. package/src/pages/customer/credit-grant/detail.tsx +308 -0
  135. package/src/pages/customer/index.tsx +44 -9
  136. package/src/pages/customer/recharge/account.tsx +5 -5
  137. package/src/pages/customer/subscription/change-payment.tsx +4 -2
  138. package/src/pages/customer/subscription/detail.tsx +48 -14
  139. package/src/pages/customer/subscription/embed.tsx +1 -1
@@ -1,28 +1,15 @@
1
1
  /* eslint-disable @typescript-eslint/brace-style */
2
2
  /* eslint-disable @typescript-eslint/indent */
3
3
  import pWaitFor from 'p-wait-for';
4
- import prettyMsI18n from 'pretty-ms-i18n';
5
4
  import isEmpty from 'lodash/isEmpty';
6
5
 
7
- import { getPaymentAmountForCycleSubscription, getCustomerSubscriptionPageUrl } from '../../subscription';
8
- import { getUserLocale } from '../../../integrations/blocklet/notification';
6
+ import { getPaymentAmountForCycleSubscription } from '../../subscription';
9
7
  import { translate } from '../../../locales';
10
- import {
11
- CheckoutSession,
12
- Customer,
13
- NftMintItem,
14
- PaymentIntent,
15
- PaymentMethod,
16
- Subscription,
17
- PaymentCurrency,
18
- Invoice,
19
- NFTMintChainType,
20
- } from '../../../store/models';
21
- import { getCustomerInvoicePageUrl, getOneTimeProductInfo } from '../../invoice';
22
- import { getMainProductName } from '../../product';
23
- import { formatTime, getPrettyMsI18nLocale } from '../../time';
24
- import { formatCurrencyInfo, getExplorerLink, getSubscriptionNotificationCustomActions } from '../../util';
25
- import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
8
+ import { CheckoutSession, NftMintItem, PaymentIntent, Invoice, NFTMintChainType } from '../../../store/models';
9
+ import { getOneTimeProductInfo } from '../../invoice';
10
+ import { formatTime } from '../../time';
11
+ import { formatCurrencyInfo, getExplorerLink } from '../../util';
12
+ import { BaseSubscriptionEmailTemplate, BaseEmailTemplateType } from './base';
26
13
 
27
14
  export interface SubscriptionSucceededEmailTemplateOptions {
28
15
  subscriptionId: string;
@@ -50,37 +37,31 @@ interface SubscriptionSucceededEmailTemplateContext {
50
37
  viewTxHashLink: string | undefined;
51
38
  oneTimeProductInfo?: Array<OneTimeProductInfo>;
52
39
  customActions: any[];
40
+ isCreditSubscription: boolean;
53
41
  }
54
42
 
55
- export class SubscriptionSucceededEmailTemplate
56
- implements BaseEmailTemplate<SubscriptionSucceededEmailTemplateContext>
57
- {
43
+ export class SubscriptionSucceededEmailTemplate extends BaseSubscriptionEmailTemplate<SubscriptionSucceededEmailTemplateContext> {
58
44
  options: SubscriptionSucceededEmailTemplateOptions;
59
45
 
60
46
  constructor(options: SubscriptionSucceededEmailTemplateOptions) {
47
+ super();
61
48
  this.options = options;
62
49
  }
63
50
 
64
51
  async getContext(): Promise<SubscriptionSucceededEmailTemplateContext> {
65
- const subscription: Subscription | null = await Subscription.findByPk(this.options.subscriptionId);
66
- if (!subscription) {
67
- throw new Error(`Subscription not found: ${this.options.subscriptionId}`);
68
- }
52
+ // 获取基础订阅数据
53
+ const basicData = await this.getSubscriptionBasicData(this.options.subscriptionId);
54
+ const { subscription, userDid, locale, productName, paymentCurrency, isCreditSubscription } = basicData;
55
+
69
56
  if (subscription.status !== 'active') {
70
57
  throw new Error(`Subscription not active: ${this.options.subscriptionId}`);
71
58
  }
72
59
 
73
- const customer = await Customer.findByPk(subscription.customer_id);
74
- if (!customer) {
75
- throw new Error(`Customer not found: ${subscription.customer_id}`);
76
- }
77
-
60
+ // 等待相关数据准备就绪
78
61
  await pWaitFor(
79
62
  async () => {
80
63
  const invoice = await Invoice.findOne({
81
- where: {
82
- subscription_id: subscription.id,
83
- },
64
+ where: { subscription_id: subscription.id },
84
65
  order: [['created_at', 'ASC']],
85
66
  });
86
67
  const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);
@@ -94,95 +75,81 @@ export class SubscriptionSucceededEmailTemplate
94
75
  );
95
76
 
96
77
  const invoice = (await Invoice.findByPk(subscription.latest_invoice_id)) as Invoice;
97
- const paymentCurrency = (await PaymentCurrency.findOne({
98
- where: {
99
- id: subscription.currency_id,
100
- },
101
- })) as PaymentCurrency;
102
78
 
103
- const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);
79
+ // 获取支付信息
80
+ const paymentInfoResult = await this.getPaymentInfo(
81
+ subscription,
82
+ paymentCurrency,
83
+ isCreditSubscription,
84
+ invoice?.id
85
+ );
86
+
87
+ // 获取链接
88
+ const links = this.generateSubscriptionLinks(
89
+ subscription,
90
+ locale,
91
+ userDid,
92
+ 'customer.subscription.started',
93
+ invoice?.id,
94
+ isCreditSubscription
95
+ );
104
96
 
105
- const userDid: string = customer.did;
106
- const locale = await getUserLocale(userDid);
107
- const productName = await getMainProductName(subscription.id);
108
- const at: string = formatTime(subscription.created_at);
97
+ // 获取 NFT 信息
98
+ const nftInfo = await this.getNftMintInfo(subscription.id);
109
99
 
100
+ // 获取一次性产品信息
110
101
  const oneTimeProductInfo = await getOneTimeProductInfo(subscription.latest_invoice_id as string, paymentCurrency);
111
- const paymentAmount = await getPaymentAmountForCycleSubscription(subscription, paymentCurrency);
112
102
 
113
- const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
114
- const nftMintItem: NftMintItem | undefined = hasNft
115
- ? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
116
- : undefined;
117
- const currentPeriodStart: string = formatTime((subscription.current_period_start || invoice.period_start) * 1000);
118
- const currentPeriodEnd: string = formatTime((subscription.current_period_end || invoice.period_end) * 1000);
119
- const duration: string = prettyMsI18n(
120
- new Date(currentPeriodEnd).getTime() - new Date(currentPeriodStart).getTime(),
121
- {
122
- locale: getPrettyMsI18nLocale(locale),
123
- }
103
+ // 计算支付金额和时间信息
104
+ const paymentAmount = await getPaymentAmountForCycleSubscription(subscription, paymentCurrency);
105
+ const formattedPaymentInfo = formatCurrencyInfo(
106
+ paymentAmount,
107
+ paymentCurrency,
108
+ paymentInfoResult.paymentMethod,
109
+ true
124
110
  );
125
111
 
126
- const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
127
- const paymentInfo: string = formatCurrencyInfo(paymentAmount, paymentCurrency, paymentMethod, true);
128
-
129
- // @FIXME: 获取 chainHost 困难的一批?
130
- const chainHost: string | undefined =
131
- paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.api_host;
132
- const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
133
- subscriptionId: subscription.id,
134
- locale,
135
- userDid,
136
- });
137
- const viewInvoiceLink = getCustomerInvoicePageUrl({
138
- invoiceId: invoice.id,
139
- userDid,
140
- locale,
141
- });
112
+ const periodInfo = this.formatSubscriptionPeriod(
113
+ subscription.current_period_start || invoice.period_start,
114
+ subscription.current_period_end || invoice.period_end,
115
+ locale
116
+ );
142
117
 
118
+ // 获取交易哈希链接
119
+ const checkoutSession = await CheckoutSession.findBySubscriptionId(subscription.id);
143
120
  const paymentIntent = await PaymentIntent.findByPk(invoice.payment_intent_id);
144
121
  const txHash: string | undefined =
145
122
  paymentIntent?.payment_details?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]?.tx_hash;
146
- const viewTxHashLink: string | undefined =
147
- txHash &&
148
- getExplorerLink({
149
- type: 'tx',
150
- did: txHash,
151
- chainHost,
152
- });
153
123
 
154
- const customActions = getSubscriptionNotificationCustomActions(
155
- subscription,
156
- 'customer.subscription.started',
157
- locale
158
- );
124
+ const chainHost: string | undefined =
125
+ paymentInfoResult.paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as NFTMintChainType]
126
+ ?.api_host;
127
+
128
+ const viewTxHashLink: string | undefined = txHash && getExplorerLink({ type: 'tx', did: txHash, chainHost });
159
129
 
160
130
  return {
161
131
  locale,
162
132
  productName,
163
- at,
164
-
133
+ at: formatTime(subscription.created_at),
165
134
  userDid,
166
- nftMintItem,
135
+ nftMintItem: nftInfo.nftMintItem,
167
136
  chainHost,
168
- paymentInfo,
169
- currentPeriodStart,
170
- currentPeriodEnd,
171
- duration,
172
-
173
- viewSubscriptionLink,
174
- viewInvoiceLink,
137
+ paymentInfo: formattedPaymentInfo,
138
+ currentPeriodStart: periodInfo.currentPeriodStart,
139
+ currentPeriodEnd: periodInfo.currentPeriodEnd,
140
+ duration: periodInfo.duration,
141
+ viewSubscriptionLink: links.viewSubscriptionLink,
142
+ viewInvoiceLink: links.viewInvoiceLink!,
175
143
  viewTxHashLink,
176
144
  oneTimeProductInfo,
177
- customActions,
145
+ customActions: links.customActions,
146
+ isCreditSubscription,
178
147
  };
179
148
  }
180
149
 
181
- getOneTimeProductTemplate(oneTimeProductInfo: Array<OneTimeProductInfo>, locale: string = 'en') {
150
+ private getOneTimeProductTemplate(oneTimeProductInfo: Array<OneTimeProductInfo>, locale: string = 'en') {
182
151
  return [
183
- {
184
- type: 'divider',
185
- },
152
+ { type: 'divider' },
186
153
  {
187
154
  type: 'text',
188
155
  data: {
@@ -203,10 +170,7 @@ export class SubscriptionSucceededEmailTemplate
203
170
  },
204
171
  {
205
172
  type: 'text',
206
- data: {
207
- type: 'plain',
208
- text: x?.productName,
209
- },
173
+ data: { type: 'plain', text: x?.productName },
210
174
  },
211
175
  {
212
176
  type: 'text',
@@ -218,10 +182,7 @@ export class SubscriptionSucceededEmailTemplate
218
182
  },
219
183
  {
220
184
  type: 'text',
221
- data: {
222
- type: 'plain',
223
- text: x?.paymentInfo,
224
- },
185
+ data: { type: 'plain', text: x?.paymentInfo },
225
186
  },
226
187
  {
227
188
  type: 'text',
@@ -235,16 +196,16 @@ export class SubscriptionSucceededEmailTemplate
235
196
  type: 'text',
236
197
  data: {
237
198
  type: 'plain',
238
- text: translate('notification.common.qty', locale, {
239
- count: x.quantity,
240
- }),
199
+ text: translate('notification.common.qty', locale, { count: x.quantity }),
241
200
  },
242
201
  },
243
202
  ]),
244
203
  },
245
204
  ];
246
205
  }
206
+
247
207
  async getTemplate(): Promise<BaseEmailTemplateType> {
208
+ const context = await this.getContext();
248
209
  const {
249
210
  locale,
250
211
  productName,
@@ -261,26 +222,41 @@ export class SubscriptionSucceededEmailTemplate
261
222
  viewTxHashLink,
262
223
  oneTimeProductInfo,
263
224
  customActions,
264
- } = await this.getContext();
225
+ isCreditSubscription,
226
+ } = context;
227
+
265
228
  const hasOneTimeProduct = !isEmpty(oneTimeProductInfo);
266
229
 
230
+ // 构建基础字段
231
+ const commonFields = this.buildCommonFields(userDid, productName, locale);
232
+ const paymentFields = this.buildPaymentField(paymentInfo, locale, isCreditSubscription);
233
+ const periodFields = this.buildPeriodField(
234
+ currentPeriodStart,
235
+ currentPeriodEnd,
236
+ duration,
237
+ locale,
238
+ isCreditSubscription
239
+ );
240
+
241
+ // 构建操作按钮
242
+ const actions = this.buildCommonActions(viewSubscriptionLink, viewInvoiceLink, locale, isCreditSubscription, [
243
+ viewTxHashLink && {
244
+ name: translate('notification.common.viewTxHash', locale),
245
+ title: translate('notification.common.viewTxHash', locale),
246
+ link: viewTxHashLink as string,
247
+ },
248
+ ...customActions,
249
+ ]);
250
+
267
251
  const template: BaseEmailTemplateType = {
268
- title: `${translate('notification.subscriptionSucceed.title', locale, {
269
- productName,
270
- })}`,
271
- body: `${translate('notification.subscriptionSucceed.body', locale, {
272
- at,
273
- productName,
274
- })}`,
252
+ title: translate('notification.subscriptionSucceed.title', locale, { productName }),
253
+ body: translate('notification.subscriptionSucceed.body', locale, { at, productName }),
275
254
  // @ts-expect-error
276
255
  attachments: [
277
256
  nftMintItem &&
278
257
  chainHost && {
279
258
  type: 'asset',
280
- data: {
281
- chainHost,
282
- did: nftMintItem.address,
283
- },
259
+ data: { chainHost, did: nftMintItem.address },
284
260
  },
285
261
  hasOneTimeProduct && {
286
262
  type: 'text',
@@ -291,92 +267,14 @@ export class SubscriptionSucceededEmailTemplate
291
267
  },
292
268
  {
293
269
  type: 'section',
294
- fields: [
295
- {
296
- type: 'text',
297
- data: {
298
- type: 'plain',
299
- color: '#9397A1',
300
- text: translate('notification.common.account', locale),
301
- },
302
- },
303
- {
304
- type: 'text',
305
- data: {
306
- type: 'plain',
307
- text: userDid,
308
- },
309
- },
310
- {
311
- type: 'text',
312
- data: {
313
- type: 'plain',
314
- color: '#9397A1',
315
- text: translate('notification.common.product', locale),
316
- },
317
- },
318
- {
319
- type: 'text',
320
- data: {
321
- type: 'plain',
322
- text: productName,
323
- },
324
- },
325
- {
326
- type: 'text',
327
- data: {
328
- type: 'plain',
329
- color: '#9397A1',
330
- text: translate('notification.common.paymentAmount', locale),
331
- },
332
- },
333
- {
334
- type: 'text',
335
- data: {
336
- type: 'plain',
337
- text: paymentInfo,
338
- },
339
- },
340
- {
341
- type: 'text',
342
- data: {
343
- type: 'plain',
344
- color: '#9397A1',
345
- text: translate('notification.common.validityPeriod', locale),
346
- },
347
- },
348
- {
349
- type: 'text',
350
- data: {
351
- type: 'plain',
352
- text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
353
- },
354
- },
355
- ].filter(Boolean),
270
+ fields: [...commonFields, ...paymentFields, ...periodFields].filter(Boolean),
356
271
  },
357
272
  ...(hasOneTimeProduct
358
273
  ? this.getOneTimeProductTemplate(oneTimeProductInfo as Array<OneTimeProductInfo>, locale)
359
274
  : []),
360
275
  ].filter(Boolean),
361
276
  // @ts-ignore
362
- actions: [
363
- {
364
- name: translate('notification.common.viewSubscription', locale),
365
- title: translate('notification.common.viewSubscription', locale),
366
- link: viewSubscriptionLink,
367
- },
368
- {
369
- name: translate('notification.common.viewInvoice', locale),
370
- title: translate('notification.common.viewInvoice', locale),
371
- link: viewInvoiceLink,
372
- },
373
- viewTxHashLink && {
374
- name: translate('notification.common.viewTxHash', locale),
375
- title: translate('notification.common.viewTxHash', locale),
376
- link: viewTxHashLink as string,
377
- },
378
- ...customActions,
379
- ].filter(Boolean),
277
+ actions: actions.filter(Boolean),
380
278
  };
381
279
 
382
280
  return template;