payment-kit 1.18.56 → 1.19.1

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 (214) hide show
  1. package/.eslintrc.js +6 -0
  2. package/api/src/crons/index.ts +8 -0
  3. package/api/src/index.ts +4 -0
  4. package/api/src/libs/credit-grant.ts +146 -0
  5. package/api/src/libs/env.ts +1 -0
  6. package/api/src/libs/invoice.ts +4 -3
  7. package/api/src/libs/notification/template/base.ts +388 -2
  8. package/api/src/libs/notification/template/customer-credit-grant-granted.ts +149 -0
  9. package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +151 -0
  10. package/api/src/libs/notification/template/customer-credit-insufficient.ts +254 -0
  11. package/api/src/libs/notification/template/subscription-canceled.ts +193 -202
  12. package/api/src/libs/notification/template/subscription-refund-succeeded.ts +215 -237
  13. package/api/src/libs/notification/template/subscription-renewed.ts +130 -200
  14. package/api/src/libs/notification/template/subscription-succeeded.ts +100 -202
  15. package/api/src/libs/notification/template/subscription-trial-start.ts +142 -188
  16. package/api/src/libs/notification/template/subscription-trial-will-end.ts +146 -174
  17. package/api/src/libs/notification/template/subscription-upgraded.ts +96 -192
  18. package/api/src/libs/notification/template/subscription-will-canceled.ts +94 -135
  19. package/api/src/libs/notification/template/subscription-will-renew.ts +220 -245
  20. package/api/src/libs/payment.ts +69 -0
  21. package/api/src/libs/queue/index.ts +3 -2
  22. package/api/src/libs/session.ts +8 -0
  23. package/api/src/libs/subscription.ts +74 -3
  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 +715 -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/index.ts +8 -0
  37. package/api/src/routes/meter-events.ts +347 -0
  38. package/api/src/routes/meters.ts +219 -0
  39. package/api/src/routes/payment-currencies.ts +14 -2
  40. package/api/src/routes/payment-links.ts +1 -1
  41. package/api/src/routes/payment-methods.ts +14 -2
  42. package/api/src/routes/prices.ts +43 -0
  43. package/api/src/routes/pricing-table.ts +13 -7
  44. package/api/src/routes/products.ts +63 -4
  45. package/api/src/routes/settings.ts +1 -1
  46. package/api/src/routes/subscriptions.ts +4 -0
  47. package/api/src/store/migrations/20250610-billing-credit.ts +43 -0
  48. package/api/src/store/models/credit-grant.ts +486 -0
  49. package/api/src/store/models/credit-transaction.ts +268 -0
  50. package/api/src/store/models/customer.ts +8 -0
  51. package/api/src/store/models/index.ts +52 -1
  52. package/api/src/store/models/meter-event.ts +423 -0
  53. package/api/src/store/models/meter.ts +176 -0
  54. package/api/src/store/models/payment-currency.ts +66 -14
  55. package/api/src/store/models/price.ts +6 -0
  56. package/api/src/store/models/product.ts +2 -2
  57. package/api/src/store/models/subscription.ts +24 -0
  58. package/api/src/store/models/types.ts +28 -2
  59. package/api/tests/libs/subscription.spec.ts +53 -0
  60. package/blocklet.yml +9 -1
  61. package/package.json +57 -58
  62. package/scripts/sdk.js +233 -1
  63. package/src/app.tsx +10 -0
  64. package/src/components/actions.tsx +22 -9
  65. package/src/components/balance-list.tsx +40 -12
  66. package/src/components/collapse.tsx +33 -15
  67. package/src/components/copyable.tsx +8 -7
  68. package/src/components/currency.tsx +15 -7
  69. package/src/components/customer/actions.tsx +1 -5
  70. package/src/components/customer/credit-grant-item-list.tsx +99 -0
  71. package/src/components/customer/credit-overview.tsx +233 -0
  72. package/src/components/customer/form.tsx +7 -2
  73. package/src/components/customer/link.tsx +4 -12
  74. package/src/components/customer/notification-preference.tsx +18 -9
  75. package/src/components/customer/overdraft-protection.tsx +112 -41
  76. package/src/components/drawer-form.tsx +42 -18
  77. package/src/components/error.tsx +1 -5
  78. package/src/components/event/list.tsx +9 -10
  79. package/src/components/filter-toolbar.tsx +20 -19
  80. package/src/components/info-card.tsx +32 -18
  81. package/src/components/info-metric.tsx +16 -6
  82. package/src/components/info-row-group.tsx +1 -7
  83. package/src/components/info-row.tsx +30 -24
  84. package/src/components/invoice/action.tsx +1 -7
  85. package/src/components/invoice/list.tsx +34 -26
  86. package/src/components/invoice/recharge.tsx +5 -7
  87. package/src/components/invoice/table.tsx +17 -12
  88. package/src/components/layout/user.tsx +1 -1
  89. package/src/components/metadata/form.tsx +290 -94
  90. package/src/components/metadata/list.tsx +11 -3
  91. package/src/components/meter/actions.tsx +101 -0
  92. package/src/components/meter/add-usage-dialog.tsx +239 -0
  93. package/src/components/meter/events-list.tsx +657 -0
  94. package/src/components/meter/form.tsx +245 -0
  95. package/src/components/meter/products.tsx +264 -0
  96. package/src/components/meter/usage-guide.tsx +174 -0
  97. package/src/components/passport/actions.tsx +9 -4
  98. package/src/components/payment-currency/add.tsx +16 -3
  99. package/src/components/payment-currency/form.tsx +14 -6
  100. package/src/components/payment-intent/actions.tsx +24 -16
  101. package/src/components/payment-intent/list.tsx +30 -9
  102. package/src/components/payment-link/actions.tsx +1 -5
  103. package/src/components/payment-link/after-pay.tsx +4 -2
  104. package/src/components/payment-link/before-pay.tsx +14 -4
  105. package/src/components/payment-link/item.tsx +27 -6
  106. package/src/components/payment-link/preview.tsx +9 -9
  107. package/src/components/payment-link/product-select.tsx +69 -15
  108. package/src/components/payment-method/arcblock.tsx +8 -1
  109. package/src/components/payment-method/base.tsx +8 -1
  110. package/src/components/payment-method/bitcoin.tsx +8 -1
  111. package/src/components/payment-method/ethereum.tsx +8 -1
  112. package/src/components/payment-method/evm-rpc-input.tsx +11 -7
  113. package/src/components/payment-method/form.tsx +2 -7
  114. package/src/components/payment-method/stripe.tsx +2 -0
  115. package/src/components/payouts/actions.tsx +1 -5
  116. package/src/components/payouts/list.tsx +30 -10
  117. package/src/components/payouts/portal/list.tsx +11 -9
  118. package/src/components/price/currency-select.tsx +63 -32
  119. package/src/components/price/form.tsx +895 -370
  120. package/src/components/price/upsell-select.tsx +10 -2
  121. package/src/components/price/upsell.tsx +7 -2
  122. package/src/components/pricing-table/actions.tsx +1 -5
  123. package/src/components/pricing-table/customer-settings.tsx +5 -1
  124. package/src/components/pricing-table/payment-settings.tsx +14 -4
  125. package/src/components/pricing-table/preview.tsx +9 -9
  126. package/src/components/pricing-table/price-item.tsx +6 -1
  127. package/src/components/pricing-table/product-item.tsx +6 -1
  128. package/src/components/pricing-table/product-settings.tsx +17 -4
  129. package/src/components/product/actions.tsx +1 -5
  130. package/src/components/product/add-price.tsx +9 -7
  131. package/src/components/product/create.tsx +8 -9
  132. package/src/components/product/cross-sell-select.tsx +5 -1
  133. package/src/components/product/cross-sell.tsx +7 -2
  134. package/src/components/product/edit-price.tsx +21 -12
  135. package/src/components/product/features.tsx +26 -6
  136. package/src/components/product/form.tsx +115 -72
  137. package/src/components/progress-bar.tsx +1 -1
  138. package/src/components/refund/actions.tsx +1 -7
  139. package/src/components/refund/list.tsx +31 -18
  140. package/src/components/section/header.tsx +12 -14
  141. package/src/components/subscription/actions/cancel.tsx +22 -5
  142. package/src/components/subscription/actions/index.tsx +9 -10
  143. package/src/components/subscription/actions/pause.tsx +32 -6
  144. package/src/components/subscription/actions/slash-stake.tsx +5 -3
  145. package/src/components/subscription/description.tsx +12 -8
  146. package/src/components/subscription/items/index.tsx +31 -16
  147. package/src/components/subscription/items/usage-records.tsx +19 -5
  148. package/src/components/subscription/list.tsx +5 -7
  149. package/src/components/subscription/metrics.tsx +62 -15
  150. package/src/components/subscription/portal/actions.tsx +78 -71
  151. package/src/components/subscription/portal/cancel.tsx +10 -3
  152. package/src/components/subscription/portal/list.tsx +48 -26
  153. package/src/components/uploader.tsx +5 -13
  154. package/src/components/webhook/attempts.tsx +51 -16
  155. package/src/components/webhook/request-info.tsx +8 -6
  156. package/src/contexts/products.tsx +27 -10
  157. package/src/hooks/subscription.ts +34 -0
  158. package/src/libs/meter-utils.ts +196 -0
  159. package/src/libs/util.ts +4 -0
  160. package/src/locales/en.tsx +385 -4
  161. package/src/locales/zh.tsx +364 -0
  162. package/src/pages/admin/billing/index.tsx +61 -33
  163. package/src/pages/admin/billing/invoices/detail.tsx +49 -13
  164. package/src/pages/admin/billing/meters/create.tsx +60 -0
  165. package/src/pages/admin/billing/meters/detail.tsx +435 -0
  166. package/src/pages/admin/billing/meters/index.tsx +210 -0
  167. package/src/pages/admin/billing/meters/meter-event.tsx +346 -0
  168. package/src/pages/admin/billing/subscriptions/detail.tsx +90 -25
  169. package/src/pages/admin/customers/customers/credit-grant/detail.tsx +391 -0
  170. package/src/pages/admin/customers/customers/detail.tsx +67 -14
  171. package/src/pages/admin/customers/customers/index.tsx +6 -1
  172. package/src/pages/admin/customers/index.tsx +5 -0
  173. package/src/pages/admin/developers/events/detail.tsx +37 -11
  174. package/src/pages/admin/developers/index.tsx +1 -1
  175. package/src/pages/admin/developers/webhooks/detail.tsx +41 -11
  176. package/src/pages/admin/index.tsx +15 -2
  177. package/src/pages/admin/overview.tsx +107 -19
  178. package/src/pages/admin/payments/intents/detail.tsx +58 -14
  179. package/src/pages/admin/payments/payouts/detail.tsx +63 -15
  180. package/src/pages/admin/payments/refunds/detail.tsx +58 -14
  181. package/src/pages/admin/products/index.tsx +11 -4
  182. package/src/pages/admin/products/links/create.tsx +22 -4
  183. package/src/pages/admin/products/links/detail.tsx +43 -14
  184. package/src/pages/admin/products/passports/index.tsx +23 -4
  185. package/src/pages/admin/products/prices/actions.tsx +16 -9
  186. package/src/pages/admin/products/prices/detail.tsx +73 -14
  187. package/src/pages/admin/products/prices/list.tsx +15 -3
  188. package/src/pages/admin/products/pricing-tables/create.tsx +45 -12
  189. package/src/pages/admin/products/pricing-tables/detail.tsx +45 -14
  190. package/src/pages/admin/products/products/create.tsx +233 -54
  191. package/src/pages/admin/products/products/detail.tsx +74 -18
  192. package/src/pages/admin/settings/index.tsx +8 -1
  193. package/src/pages/admin/settings/payment-methods/index.tsx +87 -19
  194. package/src/pages/admin/settings/vault-config/edit-form.tsx +42 -28
  195. package/src/pages/admin/settings/vault-config/index.tsx +57 -10
  196. package/src/pages/customer/credit-grant/detail.tsx +308 -0
  197. package/src/pages/customer/index.tsx +76 -17
  198. package/src/pages/customer/invoice/detail.tsx +63 -14
  199. package/src/pages/customer/invoice/past-due.tsx +11 -3
  200. package/src/pages/customer/payout/detail.tsx +56 -13
  201. package/src/pages/customer/recharge/account.tsx +78 -18
  202. package/src/pages/customer/recharge/subscription.tsx +86 -25
  203. package/src/pages/customer/refund/list.tsx +60 -24
  204. package/src/pages/customer/subscription/change-payment.tsx +17 -6
  205. package/src/pages/customer/subscription/change-plan.tsx +34 -7
  206. package/src/pages/customer/subscription/detail.tsx +134 -34
  207. package/src/pages/customer/subscription/embed.tsx +25 -5
  208. package/src/pages/home.tsx +26 -4
  209. package/src/pages/integrations/donations/edit-form.tsx +25 -9
  210. package/src/pages/integrations/donations/index.tsx +26 -9
  211. package/src/pages/integrations/donations/preview.tsx +59 -15
  212. package/src/pages/integrations/index.tsx +10 -1
  213. package/src/pages/integrations/overview.tsx +78 -17
  214. package/vite.config.ts +60 -30
@@ -4,17 +4,10 @@ import { fromUnitToToken } from '@ocap/util';
4
4
  import type { ManipulateType } from 'dayjs';
5
5
 
6
6
  import dayjs from '../../dayjs';
7
- import { getUserLocale } from '../../../integrations/blocklet/notification';
8
7
  import { translate } from '../../../locales';
9
- import { Customer, Invoice, PaymentMethod, Subscription } from '../../../store/models';
10
- import { PaymentCurrency } from '../../../store/models/payment-currency';
11
- import { getCustomerInvoicePageUrl } from '../../invoice';
12
- import logger from '../../logger';
13
- import { getMainProductName } from '../../product';
14
- import { getCustomerSubscriptionPageUrl } from '../../subscription';
8
+ import { Invoice, PaymentCurrency, PaymentMethod } from '../../../store/models';
15
9
  import { formatTime, getSimplifyDuration } from '../../time';
16
- import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
17
- import { getSubscriptionNotificationCustomActions } from '../../util';
10
+ import { BaseSubscriptionEmailTemplate, BaseEmailTemplateType } from './base';
18
11
 
19
12
  export interface SubscriptionWillCanceledEmailTemplateOptions {
20
13
  subscriptionId: string;
@@ -29,35 +22,32 @@ interface SubscriptionWillCanceledEmailTemplateContext {
29
22
  at: string;
30
23
  cancelReason: string;
31
24
  body: string;
32
-
33
25
  userDid: string;
34
26
  paymentInfo: string;
35
-
36
27
  viewSubscriptionLink: string;
37
- viewInvoiceLink: string;
28
+ viewInvoiceLink?: string;
38
29
  customActions: any[];
39
30
  needRenew: boolean;
31
+ isCreditSubscription: boolean;
40
32
  }
41
33
 
42
- export class SubscriptionWillCanceledEmailTemplate
43
- implements BaseEmailTemplate<SubscriptionWillCanceledEmailTemplateContext>
44
- {
34
+ export class SubscriptionWillCanceledEmailTemplate extends BaseSubscriptionEmailTemplate<SubscriptionWillCanceledEmailTemplateContext> {
45
35
  options: SubscriptionWillCanceledEmailTemplateOptions;
46
36
 
47
37
  constructor(options: SubscriptionWillCanceledEmailTemplateOptions) {
38
+ super();
48
39
  this.options = options;
49
40
  }
50
41
 
51
42
  async getContext(): Promise<SubscriptionWillCanceledEmailTemplateContext> {
52
43
  if (!this.options.required) {
53
- logger.error('SubscriptionWillCanceledEmailTemplate: This execution will be skipped', this.options);
54
44
  throw new Error('SubscriptionWillCanceledEmailTemplate: This execution will be skipped');
55
45
  }
56
46
 
57
- const subscription: Subscription | null = await Subscription.findByPk(this.options.subscriptionId);
58
- if (!subscription) {
59
- throw new Error(`Subscription(${this.options.subscriptionId}) not found`);
60
- }
47
+ // 获取基础订阅数据
48
+ const basicData = await this.getSubscriptionBasicData(this.options.subscriptionId);
49
+ const { subscription, userDid, locale, productName, isCreditSubscription } = basicData;
50
+
61
51
  if (subscription.isImmutable()) {
62
52
  throw new Error(`Subscription(${this.options.subscriptionId}) is immutable, no need to send notification`);
63
53
  }
@@ -73,15 +63,9 @@ export class SubscriptionWillCanceledEmailTemplate
73
63
  throw new Error(`Subscription(${this.options.subscriptionId}) is already canceled, no need to send notification`);
74
64
  }
75
65
 
76
- const customer = await Customer.findByPk(subscription.customer_id);
77
- if (!customer) {
78
- throw new Error(`Customer not found: ${subscription.customer_id}`);
79
- }
80
-
66
+ // 获取发票和支付信息
81
67
  const invoice = await Invoice.findOne({
82
- where: {
83
- id: subscription.latest_invoice_id,
84
- },
68
+ where: { id: subscription.latest_invoice_id },
85
69
  include: [
86
70
  { model: PaymentCurrency, as: 'paymentCurrency' },
87
71
  { model: PaymentMethod, as: 'paymentMethod' },
@@ -91,15 +75,24 @@ export class SubscriptionWillCanceledEmailTemplate
91
75
  throw new Error(`Invoice(${subscription.latest_invoice_id}) not found`);
92
76
  }
93
77
 
94
- const userDid = customer.did;
95
- const locale = await getUserLocale(userDid);
96
- const productName = await getMainProductName(subscription.id);
97
- const at: string = formatTime(cancelAt * 1000);
98
- const willCancelDuration: string = getSimplifyDuration((cancelAt - now) * 1000, locale);
78
+ // 获取链接
79
+ const links = this.generateSubscriptionLinks(
80
+ subscription,
81
+ locale,
82
+ userDid,
83
+ 'customer.subscription.will_canceled',
84
+ subscription.latest_invoice_id,
85
+ isCreditSubscription
86
+ );
87
+
88
+ // 计算时间和取消原因
89
+ const at = formatTime(cancelAt * 1000);
90
+ const willCancelDuration = getSimplifyDuration((cancelAt - now) * 1000, locale);
91
+
99
92
  // @ts-ignore
100
- const paymentInfo: string = `${fromUnitToToken(+invoice.total, invoice?.paymentCurrency?.decimal)} ${invoice?.paymentCurrency?.symbol}${invoice?.paymentMethod ? `(${invoice?.paymentMethod.name})` : ''}`;
93
+ const paymentInfo = `${fromUnitToToken(+invoice.total, invoice?.paymentCurrency?.decimal)} ${invoice?.paymentCurrency?.symbol}${invoice?.paymentMethod ? `(${invoice?.paymentMethod.name})` : ''}`;
101
94
 
102
- let body: string = translate('notification.subscriptWillCanceled.body', locale, {
95
+ let body = translate('notification.subscriptWillCanceled.body', locale, {
103
96
  productName,
104
97
  willCancelDuration,
105
98
  at,
@@ -119,6 +112,7 @@ export class SubscriptionWillCanceledEmailTemplate
119
112
  canceled_at: formatTime(subscription.canceled_at ? subscription.canceled_at * 1000 : dayjs().unix()),
120
113
  }
121
114
  );
115
+
122
116
  if (subscription.status === 'past_due' || subscription.cancelation_details?.reason === 'payment_failed') {
123
117
  body = translate('notification.subscriptWillCanceled.pastDue', locale, {
124
118
  productName,
@@ -128,41 +122,24 @@ export class SubscriptionWillCanceledEmailTemplate
128
122
  needRenew = true;
129
123
  }
130
124
 
131
- const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
132
- subscriptionId: subscription.id,
133
- locale,
134
- userDid,
135
- });
136
- const viewInvoiceLink = getCustomerInvoicePageUrl({
137
- invoiceId: invoice.id,
138
- userDid,
139
- locale,
140
- action: 'pay',
141
- });
142
-
143
- const customActions = getSubscriptionNotificationCustomActions(
144
- subscription,
145
- 'customer.subscription.will_canceled',
146
- locale
147
- );
148
125
  return {
149
126
  locale,
150
127
  productName,
151
128
  at,
152
129
  body,
153
130
  cancelReason,
154
-
155
131
  userDid,
156
132
  paymentInfo,
157
-
158
- viewSubscriptionLink,
159
- viewInvoiceLink,
160
- customActions,
133
+ viewSubscriptionLink: links.viewSubscriptionLink,
134
+ viewInvoiceLink: links.viewInvoiceLink,
135
+ customActions: links.customActions,
161
136
  needRenew,
137
+ isCreditSubscription,
162
138
  };
163
139
  }
164
140
 
165
141
  async getTemplate(): Promise<BaseEmailTemplateType | null> {
142
+ const context = await this.getContext();
166
143
  const {
167
144
  locale,
168
145
  productName,
@@ -172,109 +149,91 @@ export class SubscriptionWillCanceledEmailTemplate
172
149
  needRenew,
173
150
  userDid,
174
151
  paymentInfo,
175
-
176
152
  viewSubscriptionLink,
177
153
  viewInvoiceLink,
178
154
  customActions,
179
- } = await this.getContext();
155
+ isCreditSubscription,
156
+ } = context;
180
157
 
181
158
  // 如果当前时间大于订阅终止时间,那么不发送通知
182
159
  if (dayjs().utc().isAfter(dayjs.utc(at))) {
183
160
  return null;
184
161
  }
185
162
 
186
- const template: BaseEmailTemplateType = {
187
- title: `${translate('notification.subscriptWillCanceled.title', locale, {
188
- productName,
189
- })}`,
190
- body,
191
- // @ts-expect-error
192
- attachments: [
193
- {
194
- type: 'section',
195
- fields: [
196
- {
197
- type: 'text',
198
- data: {
199
- type: 'plain',
200
- color: '#9397A1',
201
- text: translate('notification.common.account', locale),
202
- },
203
- },
204
- {
205
- type: 'text',
206
- data: {
207
- type: 'plain',
208
- text: userDid,
209
- },
210
- },
211
- {
212
- type: 'text',
213
- data: {
214
- type: 'plain',
215
- color: '#9397A1',
216
- text: translate('notification.common.product', locale),
217
- },
218
- },
219
- {
220
- type: 'text',
221
- data: {
222
- type: 'plain',
223
- text: productName,
224
- },
225
- },
226
- ...(needRenew
227
- ? [
228
- {
229
- type: 'text',
230
- data: {
231
- type: 'plain',
232
- color: '#9397A1',
233
- text: translate('notification.common.paymentAmount', locale),
234
- },
235
- },
236
- {
237
- type: 'text',
238
- data: {
239
- type: 'plain',
240
- text: paymentInfo,
241
- },
242
- },
243
- ]
244
- : []),
163
+ // 构建字段
164
+ const commonFields = this.buildCommonFields(userDid, productName, locale);
165
+
166
+ // 支付金额字段(仅在需要续费时显示)
167
+ const paymentAmountFields =
168
+ needRenew && !isCreditSubscription
169
+ ? [
245
170
  {
246
171
  type: 'text',
247
172
  data: {
248
173
  type: 'plain',
249
174
  color: '#9397A1',
250
- text: translate('notification.subscriptWillCanceled.cancelReason', locale),
175
+ text: translate('notification.common.paymentAmount', locale),
251
176
  },
252
177
  },
253
178
  {
254
179
  type: 'text',
255
180
  data: {
256
181
  type: 'plain',
257
- text: cancelReason,
182
+ text: paymentInfo,
258
183
  },
259
184
  },
260
- ].filter(Boolean),
185
+ ]
186
+ : [];
187
+
188
+ // 取消原因字段
189
+ const cancelReasonFields = [
190
+ {
191
+ type: 'text',
192
+ data: {
193
+ type: 'plain',
194
+ color: '#9397A1',
195
+ text: translate('notification.subscriptWillCanceled.cancelReason', locale),
261
196
  },
262
- ].filter(Boolean),
263
- // @ts-ignore
264
- actions: [
265
- viewSubscriptionLink && {
266
- name: translate('notification.common.viewSubscription', locale),
267
- title: translate('notification.common.viewSubscription', locale),
268
- link: viewSubscriptionLink,
197
+ },
198
+ {
199
+ type: 'text',
200
+ data: {
201
+ type: 'plain',
202
+ text: cancelReason,
203
+ },
204
+ },
205
+ ];
206
+
207
+ // 构建操作按钮
208
+ const actions = [
209
+ viewSubscriptionLink && {
210
+ name: translate('notification.common.viewSubscription', locale),
211
+ title: translate('notification.common.viewSubscription', locale),
212
+ link: viewSubscriptionLink,
213
+ },
214
+ viewInvoiceLink &&
215
+ needRenew && {
216
+ name: translate('notification.common.renewNow', locale),
217
+ title: translate('notification.common.renewNow', locale),
218
+ link: viewInvoiceLink,
219
+ },
220
+ ...customActions,
221
+ ].filter(Boolean);
222
+
223
+ const template: BaseEmailTemplateType = {
224
+ title: translate('notification.subscriptWillCanceled.title', locale, {
225
+ productName,
226
+ }),
227
+ body,
228
+ // @ts-expect-error
229
+ attachments: [
230
+ {
231
+ type: 'section',
232
+ fields: [...commonFields, ...paymentAmountFields, ...cancelReasonFields].filter(Boolean),
269
233
  },
270
- viewInvoiceLink &&
271
- needRenew && {
272
- name: translate('notification.common.renewNow', locale),
273
- title: translate('notification.common.renewNow', locale),
274
- link: viewInvoiceLink,
275
- },
276
- ...customActions,
277
234
  ].filter(Boolean),
235
+ // @ts-ignore
236
+ actions,
278
237
  };
279
238
 
280
239
  return template;