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.
- package/api/src/crons/index.ts +8 -0
- package/api/src/index.ts +4 -0
- package/api/src/libs/credit-grant.ts +146 -0
- package/api/src/libs/env.ts +1 -0
- package/api/src/libs/invoice.ts +4 -3
- package/api/src/libs/notification/template/base.ts +388 -2
- package/api/src/libs/notification/template/customer-credit-grant-granted.ts +149 -0
- package/api/src/libs/notification/template/customer-credit-grant-low-balance.ts +151 -0
- package/api/src/libs/notification/template/customer-credit-insufficient.ts +254 -0
- package/api/src/libs/notification/template/subscription-canceled.ts +193 -202
- package/api/src/libs/notification/template/subscription-refund-succeeded.ts +215 -237
- package/api/src/libs/notification/template/subscription-renewed.ts +130 -200
- package/api/src/libs/notification/template/subscription-succeeded.ts +100 -202
- package/api/src/libs/notification/template/subscription-trial-start.ts +142 -188
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +146 -174
- package/api/src/libs/notification/template/subscription-upgraded.ts +96 -192
- package/api/src/libs/notification/template/subscription-will-canceled.ts +94 -135
- package/api/src/libs/notification/template/subscription-will-renew.ts +220 -245
- package/api/src/libs/payment.ts +69 -0
- package/api/src/libs/queue/index.ts +3 -2
- package/api/src/libs/session.ts +8 -0
- package/api/src/libs/subscription.ts +74 -3
- package/api/src/libs/util.ts +3 -1
- package/api/src/libs/ws.ts +23 -1
- package/api/src/locales/en.ts +33 -0
- package/api/src/locales/zh.ts +31 -0
- package/api/src/queues/credit-consume.ts +728 -0
- package/api/src/queues/credit-grant.ts +572 -0
- package/api/src/queues/notification.ts +173 -128
- package/api/src/queues/payment.ts +210 -122
- package/api/src/queues/subscription.ts +179 -0
- package/api/src/routes/checkout-sessions.ts +157 -9
- package/api/src/routes/connect/shared.ts +3 -2
- package/api/src/routes/credit-grants.ts +241 -0
- package/api/src/routes/credit-transactions.ts +208 -0
- package/api/src/routes/customers.ts +34 -5
- package/api/src/routes/index.ts +8 -0
- package/api/src/routes/meter-events.ts +347 -0
- package/api/src/routes/meters.ts +219 -0
- package/api/src/routes/payment-currencies.ts +20 -2
- package/api/src/routes/payment-links.ts +1 -1
- package/api/src/routes/payment-methods.ts +14 -2
- package/api/src/routes/prices.ts +43 -0
- package/api/src/routes/pricing-table.ts +13 -7
- package/api/src/routes/products.ts +63 -4
- package/api/src/routes/settings.ts +1 -1
- package/api/src/routes/subscriptions.ts +4 -0
- package/api/src/routes/webhook-endpoints.ts +0 -3
- package/api/src/store/migrations/20250610-billing-credit.ts +43 -0
- package/api/src/store/models/credit-grant.ts +486 -0
- package/api/src/store/models/credit-transaction.ts +268 -0
- package/api/src/store/models/customer.ts +8 -0
- package/api/src/store/models/index.ts +52 -1
- package/api/src/store/models/meter-event.ts +423 -0
- package/api/src/store/models/meter.ts +176 -0
- package/api/src/store/models/payment-currency.ts +66 -14
- package/api/src/store/models/price.ts +6 -0
- package/api/src/store/models/product.ts +2 -2
- package/api/src/store/models/subscription.ts +24 -0
- package/api/src/store/models/types.ts +28 -2
- package/api/tests/libs/subscription.spec.ts +53 -0
- package/blocklet.yml +9 -1
- package/package.json +4 -4
- package/scripts/sdk.js +233 -1
- package/src/app.tsx +10 -0
- package/src/components/collapse.tsx +11 -1
- package/src/components/conditional-section.tsx +87 -0
- package/src/components/customer/credit-grant-item-list.tsx +99 -0
- package/src/components/customer/credit-overview.tsx +246 -0
- package/src/components/customer/form.tsx +7 -3
- package/src/components/invoice/list.tsx +19 -1
- package/src/components/metadata/form.tsx +287 -91
- package/src/components/meter/actions.tsx +101 -0
- package/src/components/meter/add-usage-dialog.tsx +239 -0
- package/src/components/meter/events-list.tsx +657 -0
- package/src/components/meter/form.tsx +245 -0
- package/src/components/meter/products.tsx +264 -0
- package/src/components/meter/usage-guide.tsx +174 -0
- package/src/components/payment-currency/form.tsx +2 -0
- package/src/components/payment-intent/list.tsx +19 -1
- package/src/components/payment-link/item.tsx +2 -2
- package/src/components/payment-link/preview.tsx +1 -1
- package/src/components/payment-link/product-select.tsx +52 -12
- package/src/components/payment-method/arcblock.tsx +2 -0
- package/src/components/payment-method/base.tsx +2 -0
- package/src/components/payment-method/bitcoin.tsx +2 -0
- package/src/components/payment-method/ethereum.tsx +2 -0
- package/src/components/payment-method/stripe.tsx +2 -0
- package/src/components/payouts/list.tsx +19 -1
- package/src/components/payouts/portal/list.tsx +6 -11
- package/src/components/price/currency-select.tsx +56 -32
- package/src/components/price/form.tsx +912 -407
- package/src/components/pricing-table/preview.tsx +1 -1
- package/src/components/product/add-price.tsx +9 -7
- package/src/components/product/create.tsx +7 -4
- package/src/components/product/edit-price.tsx +21 -12
- package/src/components/product/features.tsx +17 -7
- package/src/components/product/form.tsx +100 -90
- package/src/components/refund/list.tsx +19 -1
- package/src/components/section/header.tsx +5 -18
- package/src/components/subscription/items/index.tsx +1 -1
- package/src/components/subscription/metrics.tsx +37 -5
- package/src/components/subscription/portal/actions.tsx +2 -1
- package/src/contexts/products.tsx +26 -9
- package/src/hooks/subscription.ts +34 -0
- package/src/libs/meter-utils.ts +196 -0
- package/src/libs/util.ts +4 -0
- package/src/locales/en.tsx +389 -5
- package/src/locales/zh.tsx +368 -1
- package/src/pages/admin/billing/index.tsx +61 -33
- package/src/pages/admin/billing/invoices/detail.tsx +1 -1
- package/src/pages/admin/billing/meters/create.tsx +60 -0
- package/src/pages/admin/billing/meters/detail.tsx +435 -0
- package/src/pages/admin/billing/meters/index.tsx +210 -0
- package/src/pages/admin/billing/meters/meter-event.tsx +346 -0
- package/src/pages/admin/billing/subscriptions/detail.tsx +47 -14
- package/src/pages/admin/customers/customers/credit-grant/detail.tsx +391 -0
- package/src/pages/admin/customers/customers/detail.tsx +14 -10
- package/src/pages/admin/customers/index.tsx +5 -0
- package/src/pages/admin/developers/events/detail.tsx +1 -1
- package/src/pages/admin/developers/index.tsx +1 -1
- package/src/pages/admin/payments/intents/detail.tsx +1 -1
- package/src/pages/admin/payments/payouts/detail.tsx +1 -1
- package/src/pages/admin/payments/refunds/detail.tsx +1 -1
- package/src/pages/admin/products/index.tsx +3 -2
- package/src/pages/admin/products/links/detail.tsx +1 -1
- package/src/pages/admin/products/prices/actions.tsx +16 -4
- package/src/pages/admin/products/prices/detail.tsx +30 -3
- package/src/pages/admin/products/prices/list.tsx +8 -1
- package/src/pages/admin/products/pricing-tables/detail.tsx +1 -1
- package/src/pages/admin/products/products/create.tsx +233 -57
- package/src/pages/admin/products/products/detail.tsx +2 -1
- package/src/pages/admin/settings/payment-methods/index.tsx +3 -0
- package/src/pages/customer/credit-grant/detail.tsx +308 -0
- package/src/pages/customer/index.tsx +44 -9
- package/src/pages/customer/recharge/account.tsx +5 -5
- package/src/pages/customer/subscription/change-payment.tsx +4 -2
- package/src/pages/customer/subscription/detail.tsx +48 -14
- package/src/pages/customer/subscription/embed.tsx +1 -1
|
@@ -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 {
|
|
13
|
-
import {
|
|
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 {
|
|
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
|
|
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
|
|
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<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
66
|
-
|
|
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
|
-
//
|
|
71
|
-
const
|
|
72
|
-
|
|
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
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
112
|
-
subscriptionId: subscription.id,
|
|
113
|
-
locale,
|
|
114
|
-
userDid,
|
|
115
|
-
});
|
|
97
|
+
const paymentInfo = `${paymentAmount} ${paymentCurrency.symbol}`;
|
|
116
98
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
|
148
|
+
const canPay = paymentDetail.balance >= paymentDetail.price;
|
|
166
149
|
if (canPay && !this.options.required) {
|
|
167
|
-
// 当余额足够支付并且本封邮件不是必须发送时,可以不发送邮件
|
|
168
150
|
return null;
|
|
169
151
|
}
|
|
170
152
|
|
|
171
|
-
|
|
153
|
+
// 构建字段
|
|
154
|
+
const commonFields = this.buildCommonFields(userDid, productName, locale);
|
|
172
155
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
type: 'text',
|
|
217
|
+
data: {
|
|
218
|
+
type: 'plain',
|
|
219
|
+
text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
|
|
279
220
|
},
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
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;
|