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
@@ -2,19 +2,14 @@
2
2
  /* eslint-disable @typescript-eslint/indent */
3
3
  import { fromUnitToToken } from '@ocap/util';
4
4
  import type { ManipulateType } from 'dayjs';
5
- import prettyMsI18n from 'pretty-ms-i18n';
6
5
 
7
6
  import { getTokenByAddress } from '../../../integrations/arcblock/stake';
8
- import { getUserLocale } from '../../../integrations/blocklet/notification';
9
7
  import { translate } from '../../../locales';
10
- import { Customer, PaymentMethod, Subscription, PaymentCurrency } from '../../../store/models';
11
8
  import { PaymentDetail } from '../../payment';
12
- import { getMainProductName } from '../../product';
13
- import { getCustomerSubscriptionPageUrl, getPaymentAmountForCycleSubscription } from '../../subscription';
14
- import { formatTime, getPrettyMsI18nLocale, getSimplifyDuration } from '../../time';
15
- import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
9
+ import { getPaymentAmountForCycleSubscription } from '../../subscription';
10
+ import { formatTime, getSimplifyDuration } from '../../time';
16
11
  import dayjs from '../../dayjs';
17
- import { getSubscriptionNotificationCustomActions } from '../../util';
12
+ import { BaseSubscriptionEmailTemplate, BaseEmailTemplateType } from './base';
18
13
 
19
14
  export interface SubscriptionTrialWillEndEmailTemplateOptions {
20
15
  subscriptionId: string;
@@ -23,38 +18,36 @@ export interface SubscriptionTrialWillEndEmailTemplateOptions {
23
18
  required?: false | true;
24
19
  }
25
20
 
26
- interface SubscriptionTrialWilEndEmailTemplateContext {
21
+ interface SubscriptionTrialWillEndEmailTemplateContext {
27
22
  locale: string;
28
23
  productName: string;
29
24
  at: string;
30
25
  willEndDuration: string;
31
26
  paymentDetail: PaymentDetail;
32
-
33
27
  userDid: string;
34
28
  paymentInfo: string;
35
29
  currentPeriodStart: string;
36
30
  currentPeriodEnd: string;
37
31
  duration: string;
38
-
39
32
  viewSubscriptionLink: string;
40
- paymentMethod: PaymentMethod | null;
41
33
  customActions: any[];
34
+ isCreditSubscription: boolean;
35
+ isStripe: boolean;
42
36
  }
43
37
 
44
- export class SubscriptionTrialWilEndEmailTemplate
45
- implements BaseEmailTemplate<SubscriptionTrialWilEndEmailTemplateContext>
46
- {
38
+ export class SubscriptionTrialWillEndEmailTemplate extends BaseSubscriptionEmailTemplate<SubscriptionTrialWillEndEmailTemplateContext> {
47
39
  options: SubscriptionTrialWillEndEmailTemplateOptions;
48
40
 
49
41
  constructor(options: SubscriptionTrialWillEndEmailTemplateOptions) {
42
+ super();
50
43
  this.options = options;
51
44
  }
52
45
 
53
- async getContext(): Promise<SubscriptionTrialWilEndEmailTemplateContext> {
54
- const subscription: Subscription | null = await Subscription.findByPk(this.options.subscriptionId);
55
- if (!subscription) {
56
- throw new Error(`Subscription not found: ${this.options.subscriptionId}`);
57
- }
46
+ async getContext(): Promise<SubscriptionTrialWillEndEmailTemplateContext> {
47
+ // 获取基础订阅数据
48
+ const basicData = await this.getSubscriptionBasicData(this.options.subscriptionId);
49
+ const { subscription, userDid, locale, productName, paymentCurrency, isCreditSubscription } = basicData;
50
+
58
51
  if (subscription.status !== 'trialing') {
59
52
  throw new Error(`Subscription not trialing: ${this.options.subscriptionId}`);
60
53
  }
@@ -62,61 +55,51 @@ export class SubscriptionTrialWilEndEmailTemplate
62
55
  throw new Error(`Subscription is scheduled to cancel: ${this.options.subscriptionId}`);
63
56
  }
64
57
 
65
- const customer = await Customer.findByPk(subscription.customer_id);
66
- if (!customer) {
67
- throw new Error(`Customer not found: ${subscription.customer_id}`);
58
+ if (!subscription.trial_end) {
59
+ throw new Error(`Subscription has no trial end: ${subscription.id}`);
68
60
  }
69
61
 
70
- // const invoice = (await Invoice.findByPk(subscription.latest_invoice_id)) as Invoice;
71
- const paymentCurrency = (await PaymentCurrency.findOne({
72
- where: {
73
- id: subscription.currency_id,
74
- },
75
- })) as PaymentCurrency;
76
-
77
- const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
62
+ // 获取支付信息
63
+ const paymentInfoResult = await this.getPaymentInfo(subscription, paymentCurrency, isCreditSubscription);
64
+ const { paymentMethod } = paymentInfoResult;
78
65
 
79
66
  if (!paymentMethod) {
80
67
  throw new Error(`Payment method not found: ${paymentCurrency.payment_method_id}`);
81
68
  }
82
69
 
83
- const userDid = customer.did;
84
- const locale = await getUserLocale(userDid);
85
- const productName = await getMainProductName(subscription.id);
86
- if (!subscription.trial_end) {
87
- throw new Error(`Subscription has no trial end: ${subscription.id}`);
88
- }
89
-
90
- const at: string = formatTime((subscription.trial_end as number) * 1000);
70
+ // 获取链接
71
+ const links = this.generateSubscriptionLinks(
72
+ subscription,
73
+ locale,
74
+ userDid,
75
+ 'customer.subscription.trial_will_end',
76
+ undefined,
77
+ isCreditSubscription
78
+ );
91
79
 
92
- const willEndDuration: string = getSimplifyDuration((subscription.trial_end! - dayjs().unix()) * 1000, locale);
80
+ // 计算时间信息
81
+ const at = formatTime((subscription.trial_end as number) * 1000);
82
+ const willEndDuration = getSimplifyDuration((subscription.trial_end! - dayjs().unix()) * 1000, locale);
93
83
 
84
+ // 获取支付详情
94
85
  const paymentAmount = await getPaymentAmountForCycleSubscription(subscription, paymentCurrency);
95
86
  const paymentDetail = { price: paymentAmount, balance: 0, symbol: paymentCurrency.symbol };
96
- // @ts-ignore
97
- const paymentAddress = subscription.payment_details?.[paymentMethod.type]?.payer ?? undefined;
98
- const balance = await getTokenByAddress(paymentAddress, paymentMethod, paymentCurrency);
99
- paymentDetail.balance = +fromUnitToToken(balance, paymentCurrency.decimal);
100
87
 
101
- const paymentInfo: string = `${paymentAmount} ${paymentCurrency.symbol}`;
102
- const currentPeriodStart: string = formatTime((subscription.trial_start as number) * 1000);
103
- const currentPeriodEnd: string = formatTime((subscription.trial_end as number) * 1000);
104
- const duration: string = prettyMsI18n(
105
- new Date(currentPeriodEnd).getTime() - new Date(currentPeriodStart).getTime(),
106
- {
107
- locale: getPrettyMsI18nLocale(locale),
108
- }
109
- );
88
+ // 获取余额信息(非 Stripe 支付方式)
89
+ const isStripe = paymentMethod?.type === 'stripe';
90
+ if (!isStripe && !isCreditSubscription) {
91
+ // @ts-ignore
92
+ const paymentAddress = subscription.payment_details?.[paymentMethod.type]?.payer ?? undefined;
93
+ const balance = await getTokenByAddress(paymentAddress, paymentMethod, paymentCurrency);
94
+ paymentDetail.balance = +fromUnitToToken(balance, paymentCurrency.decimal);
95
+ }
110
96
 
111
- const viewSubscriptionLink = getCustomerSubscriptionPageUrl({
112
- subscriptionId: subscription.id,
113
- locale,
114
- userDid,
115
- });
97
+ const paymentInfo = `${paymentAmount} ${paymentCurrency.symbol}`;
116
98
 
117
- const customActions = getSubscriptionNotificationCustomActions(
118
- subscription,
119
- 'customer.subscription.trial_will_end',
99
+ // 计算周期信息
100
+ const periodInfo = this.formatSubscriptionPeriod(
101
+ subscription.trial_start as number,
102
+ subscription.trial_end as number,
120
103
  locale
121
104
  );
122
105
 
@@ -126,169 +109,158 @@ export class SubscriptionTrialWilEndEmailTemplate
126
109
  at,
127
110
  willEndDuration,
128
111
  paymentDetail,
129
-
130
112
  userDid,
131
113
  paymentInfo,
132
- currentPeriodStart,
133
- currentPeriodEnd,
134
- duration,
135
-
136
- viewSubscriptionLink,
137
- paymentMethod,
138
- customActions,
114
+ currentPeriodStart: periodInfo.currentPeriodStart,
115
+ currentPeriodEnd: periodInfo.currentPeriodEnd,
116
+ duration: periodInfo.duration,
117
+ viewSubscriptionLink: links.viewSubscriptionLink,
118
+ customActions: links.customActions,
119
+ isCreditSubscription,
120
+ isStripe,
139
121
  };
140
122
  }
141
123
 
142
124
  async getTemplate(): Promise<BaseEmailTemplateType | null> {
125
+ const context = await this.getContext();
143
126
  const {
144
127
  locale,
145
128
  productName,
146
129
  at,
147
130
  willEndDuration,
148
131
  paymentDetail,
149
-
150
132
  userDid,
151
133
  paymentInfo,
152
134
  currentPeriodStart,
153
135
  currentPeriodEnd,
154
136
  duration,
155
- paymentMethod,
156
137
  viewSubscriptionLink,
157
138
  customActions,
158
- } = await this.getContext();
139
+ isCreditSubscription,
140
+ isStripe,
141
+ } = context;
159
142
 
160
143
  // 如果当前时间大于试用结束时间,那么不发送通知
161
144
  if (dayjs().utc().isAfter(dayjs.utc(at))) {
162
145
  return null;
163
146
  }
164
147
 
165
- const canPay: boolean = paymentDetail.balance >= paymentDetail.price;
148
+ const canPay = paymentDetail.balance >= paymentDetail.price;
166
149
  if (canPay && !this.options.required) {
167
- // 当余额足够支付并且本封邮件不是必须发送时,可以不发送邮件
168
150
  return null;
169
151
  }
170
152
 
171
- const isStripe = paymentMethod?.type === 'stripe';
153
+ // 构建字段
154
+ const commonFields = this.buildCommonFields(userDid, productName, locale);
172
155
 
173
- const template: BaseEmailTemplateType = {
174
- title: `${translate('notification.subscriptionTrialWillEnd.title', locale, {
175
- productName,
176
- willEndDuration,
177
- })}`,
178
- body:
179
- canPay || isStripe
180
- ? `${translate('notification.subscriptionTrialWillEnd.body', locale, {
181
- at,
182
- productName,
183
- willEndDuration,
184
- })}`
185
- : `${translate('notification.subscriptionTrialWillEnd.unableToPayBody', locale, {
186
- at,
187
- productName,
188
- willEndDuration,
189
- balance: `${paymentDetail.balance} ${paymentDetail.symbol}`,
190
- price: `${paymentDetail.price} ${paymentDetail.symbol}`,
191
- })}`,
192
- // @ts-expect-error
193
- attachments: [
194
- {
195
- type: 'section',
196
- fields: [
197
- {
198
- type: 'text',
199
- data: {
200
- type: 'plain',
201
- color: '#9397A1',
202
- text: translate('notification.common.account', locale),
203
- },
204
- },
205
- {
206
- type: 'text',
207
- data: {
208
- type: 'plain',
209
- text: userDid,
210
- },
211
- },
212
- {
213
- type: 'text',
214
- data: {
215
- type: 'plain',
216
- color: '#9397A1',
217
- text: translate('notification.common.product', locale),
218
- },
156
+ // 支付金额字段
157
+ const paymentAmountFields = isCreditSubscription
158
+ ? []
159
+ : [
160
+ {
161
+ type: 'text',
162
+ data: {
163
+ type: 'plain',
164
+ color: '#9397A1',
165
+ text: translate('notification.common.paymentAmount', locale),
219
166
  },
220
- {
221
- type: 'text',
222
- data: {
223
- type: 'plain',
224
- text: productName,
225
- },
167
+ },
168
+ {
169
+ type: 'text',
170
+ data: {
171
+ type: 'plain',
172
+ text: paymentInfo,
226
173
  },
174
+ },
175
+ ];
176
+
177
+ // 余额不足提醒字段
178
+ const insufficientBalanceFields =
179
+ !canPay && !isStripe && !isCreditSubscription
180
+ ? [
227
181
  {
228
182
  type: 'text',
229
183
  data: {
230
184
  type: 'plain',
231
185
  color: '#9397A1',
232
- text: translate('notification.common.paymentAmount', locale),
186
+ text: translate('notification.common.balanceReminder', locale),
233
187
  },
234
188
  },
235
189
  {
236
190
  type: 'text',
237
191
  data: {
238
192
  type: 'plain',
239
- text: paymentInfo,
193
+ color: '#FF0000',
194
+ text: translate('notification.subscriptionTrialWillEnd.unableToPayReason', locale, {
195
+ balance: `${paymentDetail.balance} ${paymentDetail.symbol}`,
196
+ price: `${paymentDetail.price} ${paymentDetail.symbol}`,
197
+ }),
240
198
  },
241
199
  },
242
- ...(!canPay && !isStripe
243
- ? [
244
- {
245
- type: 'text',
246
- data: {
247
- type: 'plain',
248
- color: '#9397A1',
249
- text: translate('notification.common.balanceReminder', locale),
250
- },
251
- },
252
- {
253
- type: 'text',
254
- data: {
255
- type: 'plain',
256
- color: '#FF0000',
257
- text: translate('notification.subscriptionTrialWillEnd.unableToPayReason', locale, {
258
- balance: `${paymentDetail.balance} ${paymentDetail.symbol}`,
259
- price: `${paymentDetail.price} ${paymentDetail.symbol}`,
260
- }),
261
- },
262
- },
263
- ]
264
- : []),
265
- {
266
- type: 'text',
267
- data: {
268
- type: 'plain',
269
- color: '#9397A1',
270
- text: translate('notification.common.trialPeriod', locale),
271
- },
200
+ ]
201
+ : [];
202
+
203
+ // 试用期字段
204
+ const trialPeriodFields = isCreditSubscription
205
+ ? []
206
+ : [
207
+ {
208
+ type: 'text',
209
+ data: {
210
+ type: 'plain',
211
+ color: '#9397A1',
212
+ text: translate('notification.common.trialPeriod', locale),
272
213
  },
273
- {
274
- type: 'text',
275
- data: {
276
- type: 'plain',
277
- text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
278
- },
214
+ },
215
+ {
216
+ type: 'text',
217
+ data: {
218
+ type: 'plain',
219
+ text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
279
220
  },
280
- ].filter(Boolean),
281
- },
282
- ].filter(Boolean),
283
- // @ts-ignore
284
- actions: [
221
+ },
222
+ ];
223
+
224
+ // 构建操作按钮
225
+ const actions = [
226
+ {
227
+ name: translate('notification.common.viewSubscription', locale),
228
+ title: translate('notification.common.viewSubscription', locale),
229
+ link: viewSubscriptionLink,
230
+ },
231
+ ...customActions,
232
+ ].filter(Boolean);
233
+
234
+ const template: BaseEmailTemplateType = {
235
+ title: translate('notification.subscriptionTrialWillEnd.title', locale, {
236
+ productName,
237
+ willEndDuration,
238
+ }),
239
+ body:
240
+ canPay || isStripe
241
+ ? translate('notification.subscriptionTrialWillEnd.body', locale, {
242
+ at,
243
+ productName,
244
+ willEndDuration,
245
+ })
246
+ : translate('notification.subscriptionTrialWillEnd.unableToPayBody', locale, {
247
+ at,
248
+ productName,
249
+ willEndDuration,
250
+ balance: `${paymentDetail.balance} ${paymentDetail.symbol}`,
251
+ price: `${paymentDetail.price} ${paymentDetail.symbol}`,
252
+ }),
253
+ // @ts-expect-error
254
+ attachments: [
285
255
  {
286
- name: translate('notification.common.viewSubscription', locale),
287
- title: translate('notification.common.viewSubscription', locale),
288
- link: viewSubscriptionLink,
256
+ type: 'section',
257
+ fields: [...commonFields, ...paymentAmountFields, ...insufficientBalanceFields, ...trialPeriodFields].filter(
258
+ Boolean
259
+ ),
289
260
  },
290
- ...customActions,
291
261
  ].filter(Boolean),
262
+ // @ts-ignore
263
+ actions,
292
264
  };
293
265
 
294
266
  return template;