payment-kit 1.13.64 → 1.13.66
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/index.ts +4 -0
- package/api/src/integrations/blocklet/notification.ts +5 -3
- package/api/src/jobs/notification.ts +142 -0
- package/api/src/jobs/payment.ts +14 -0
- package/api/src/jobs/subscription.ts +2 -2
- package/api/src/libs/audit.ts +3 -1
- package/api/src/libs/env.ts +3 -0
- package/api/src/libs/event.ts +10 -1
- package/api/src/libs/invoice.ts +5 -0
- package/api/src/libs/notification/index.ts +23 -0
- package/api/src/libs/notification/template/base.ts +12 -0
- package/api/src/libs/notification/template/subscription-renew-failed.ts +286 -0
- package/api/src/libs/notification/template/subscription-renewed.ts +259 -0
- package/api/src/libs/notification/template/subscription-succeeded.ts +279 -0
- package/api/src/libs/notification/template/subscription-trial-start.ts +267 -0
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +250 -0
- package/api/src/libs/notification/template/subscription-will-renew.ts +232 -0
- package/api/src/libs/payment.ts +100 -3
- package/api/src/libs/product.ts +19 -0
- package/api/src/libs/queue/index.ts +13 -0
- package/api/src/libs/subscription.ts +5 -0
- package/api/src/libs/time.ts +17 -0
- package/api/src/libs/util.ts +39 -0
- package/api/src/locales/en.ts +67 -0
- package/api/src/locales/zh.ts +64 -0
- package/api/src/routes/connect/collect.ts +6 -0
- package/api/src/schedule/index.ts +28 -0
- package/api/src/schedule/interface/diff.ts +9 -0
- package/api/src/schedule/subscription-trail-will-end.ts +197 -0
- package/api/src/schedule/subscription-will-renew.ts +195 -0
- package/api/src/store/models/subscription.ts +30 -12
- package/api/src/store/models/types.ts +13 -12
- package/api/third.d.ts +2 -0
- package/blocklet.yml +1 -1
- package/package.json +23 -21
- package/src/app.tsx +2 -0
- package/src/components/invoice/action.tsx +25 -7
- package/src/components/invoice/list.tsx +19 -4
- package/src/components/portal/invoice/list.tsx +1 -1
- package/src/components/portal/subscription/list.tsx +6 -5
- package/src/components/subscription/items/index.tsx +8 -4
- package/src/libs/util.ts +2 -2
- package/src/locales/en.tsx +5 -1
- package/src/locales/zh.tsx +5 -1
- package/src/pages/checkout/pricing-table.tsx +1 -1
- package/src/pages/customer/index.tsx +13 -2
- package/src/pages/customer/invoice.tsx +5 -4
- package/src/pages/customer/subscription/index.tsx +163 -0
- package/tsconfig.api.json +6 -1
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/brace-style */
|
|
2
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
3
|
+
import { toDid } from '@ocap/util';
|
|
4
|
+
import pWaitFor from 'p-wait-for';
|
|
5
|
+
import prettyMsI18n from 'pretty-ms-i18n';
|
|
6
|
+
|
|
7
|
+
import { getUserLocale } from '../../../integrations/blocklet/notification';
|
|
8
|
+
import { translate } from '../../../locales';
|
|
9
|
+
import { CheckoutSession, Customer, Invoice, NftMintItem, PaymentMethod, Subscription } from '../../../store/models';
|
|
10
|
+
import { PaymentCurrency } from '../../../store/models/payment-currency';
|
|
11
|
+
import { getCustomerInvoicePageUrl } from '../../invoice';
|
|
12
|
+
import { getMainProductName } from '../../product';
|
|
13
|
+
import { getCustomerSubscriptionPageUrl } from '../../subscription';
|
|
14
|
+
import { formatTime, getPrettyMsI18nLocale } from '../../time';
|
|
15
|
+
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
|
|
16
|
+
|
|
17
|
+
export interface SubscriptionTrailStartEmailTemplateOptions {
|
|
18
|
+
subscriptionId: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface SubscriptionTrailStartEmailTemplateContext {
|
|
22
|
+
locale: string;
|
|
23
|
+
productName: string;
|
|
24
|
+
subscriptionTrialEnd: string;
|
|
25
|
+
|
|
26
|
+
nftMintItem: NftMintItem | undefined;
|
|
27
|
+
chainHost: string | undefined;
|
|
28
|
+
userDid: string;
|
|
29
|
+
paymentInfo: string;
|
|
30
|
+
currentPeriodStart: string;
|
|
31
|
+
currentPeriodEnd: string;
|
|
32
|
+
duration: string;
|
|
33
|
+
|
|
34
|
+
viewSubscriptionLink: string;
|
|
35
|
+
viewInvoiceLink: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class SubscriptionTrailStartEmailTemplate
|
|
39
|
+
implements BaseEmailTemplate<SubscriptionTrailStartEmailTemplateContext>
|
|
40
|
+
{
|
|
41
|
+
options: SubscriptionTrailStartEmailTemplateOptions;
|
|
42
|
+
|
|
43
|
+
constructor(options: SubscriptionTrailStartEmailTemplateOptions) {
|
|
44
|
+
this.options = options;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getContext(): Promise<SubscriptionTrailStartEmailTemplateContext> {
|
|
48
|
+
const subscription: Subscription | null = await Subscription.findByPk(this.options.subscriptionId);
|
|
49
|
+
if (!subscription) {
|
|
50
|
+
throw new Error(`Subscription not found: ${this.options.subscriptionId}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const customer = await Customer.findByPk(subscription.customer_id);
|
|
54
|
+
if (!customer) {
|
|
55
|
+
throw new Error(`Customer not found: ${subscription.customer_id}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
await pWaitFor(
|
|
59
|
+
async () => {
|
|
60
|
+
const checkoutSession = await CheckoutSession.findOne({
|
|
61
|
+
where: {
|
|
62
|
+
subscription_id: subscription.id,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return ['minted', 'sent', 'error'].includes(checkoutSession?.nft_mint_status as string);
|
|
67
|
+
},
|
|
68
|
+
{ timeout: 1000 * 10, interval: 1000 }
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const invoice: Invoice = (await Invoice.findOne({
|
|
72
|
+
where: {
|
|
73
|
+
subscription_id: subscription.id,
|
|
74
|
+
total: 0,
|
|
75
|
+
},
|
|
76
|
+
})) as Invoice;
|
|
77
|
+
|
|
78
|
+
const paymentCurrency = (await PaymentCurrency.findOne({
|
|
79
|
+
where: {
|
|
80
|
+
id: subscription.currency_id,
|
|
81
|
+
},
|
|
82
|
+
})) as PaymentCurrency;
|
|
83
|
+
|
|
84
|
+
const checkoutSession = await CheckoutSession.findOne({
|
|
85
|
+
where: {
|
|
86
|
+
subscription_id: subscription.id,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
const locale = await getUserLocale(customer.did);
|
|
91
|
+
const productName = await getMainProductName(subscription.id);
|
|
92
|
+
const subscriptionTrialEnd: string = formatTime((subscription.trail_end as number) * 1000);
|
|
93
|
+
|
|
94
|
+
const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
|
|
95
|
+
const nftMintItem: NftMintItem | undefined = hasNft
|
|
96
|
+
? checkoutSession?.nft_mint_details?.[checkoutSession?.nft_mint_details?.type as 'arcblock' | 'ethereum']
|
|
97
|
+
: undefined;
|
|
98
|
+
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(invoice.default_payment_method_id);
|
|
99
|
+
const chainHost: string | undefined =
|
|
100
|
+
paymentMethod?.settings?.[checkoutSession?.nft_mint_details?.type as 'arcblock' | 'ethereum']?.api_host;
|
|
101
|
+
const paymentInfo: string = `0 ${paymentCurrency.symbol}`;
|
|
102
|
+
const currentPeriodStart: string = formatTime((subscription.trail_start as number) * 1000);
|
|
103
|
+
const currentPeriodEnd: string = formatTime((subscription.trail_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
|
+
);
|
|
110
|
+
|
|
111
|
+
const viewSubscriptionLink = getCustomerSubscriptionPageUrl(subscription.id, locale);
|
|
112
|
+
const viewInvoiceLink = getCustomerInvoicePageUrl(invoice.id, locale);
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
locale,
|
|
116
|
+
productName,
|
|
117
|
+
subscriptionTrialEnd,
|
|
118
|
+
|
|
119
|
+
nftMintItem,
|
|
120
|
+
chainHost,
|
|
121
|
+
userDid: customer.did,
|
|
122
|
+
paymentInfo,
|
|
123
|
+
currentPeriodStart,
|
|
124
|
+
currentPeriodEnd,
|
|
125
|
+
duration,
|
|
126
|
+
|
|
127
|
+
viewSubscriptionLink,
|
|
128
|
+
viewInvoiceLink,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async getTemplate(): Promise<BaseEmailTemplateType> {
|
|
133
|
+
const {
|
|
134
|
+
locale,
|
|
135
|
+
productName,
|
|
136
|
+
subscriptionTrialEnd,
|
|
137
|
+
|
|
138
|
+
nftMintItem,
|
|
139
|
+
chainHost,
|
|
140
|
+
userDid,
|
|
141
|
+
paymentInfo,
|
|
142
|
+
currentPeriodStart,
|
|
143
|
+
currentPeriodEnd,
|
|
144
|
+
duration,
|
|
145
|
+
|
|
146
|
+
viewSubscriptionLink,
|
|
147
|
+
viewInvoiceLink,
|
|
148
|
+
} = await this.getContext();
|
|
149
|
+
|
|
150
|
+
const template: BaseEmailTemplateType = {
|
|
151
|
+
title: `${translate('notification.subscriptionTrialStart.title', locale, {
|
|
152
|
+
productName: `(${productName})`,
|
|
153
|
+
})}`,
|
|
154
|
+
body: `${translate('notification.subscriptionTrialStart.body', locale, {
|
|
155
|
+
subscriptionTrialEnd,
|
|
156
|
+
productName: `(${productName})`,
|
|
157
|
+
trialDuration: duration,
|
|
158
|
+
})}`,
|
|
159
|
+
// @ts-expect-error
|
|
160
|
+
attachments: [
|
|
161
|
+
nftMintItem &&
|
|
162
|
+
chainHost && {
|
|
163
|
+
type: 'asset',
|
|
164
|
+
data: {
|
|
165
|
+
chainHost,
|
|
166
|
+
did: nftMintItem.address,
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
type: 'section',
|
|
171
|
+
fields: [
|
|
172
|
+
{
|
|
173
|
+
type: 'text',
|
|
174
|
+
data: {
|
|
175
|
+
type: 'plain',
|
|
176
|
+
color: '#9397A1',
|
|
177
|
+
text: translate('notification.common.account', locale),
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: 'text',
|
|
182
|
+
data: {
|
|
183
|
+
type: 'plain',
|
|
184
|
+
text: userDid,
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
type: 'text',
|
|
189
|
+
data: {
|
|
190
|
+
type: 'plain',
|
|
191
|
+
color: '#9397A1',
|
|
192
|
+
text: translate('notification.common.product', locale),
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: 'text',
|
|
197
|
+
data: {
|
|
198
|
+
type: 'plain',
|
|
199
|
+
text: productName,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
type: 'text',
|
|
204
|
+
data: {
|
|
205
|
+
type: 'plain',
|
|
206
|
+
color: '#9397A1',
|
|
207
|
+
text: translate('notification.common.paymentInfo', locale),
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
type: 'text',
|
|
212
|
+
data: {
|
|
213
|
+
type: 'plain',
|
|
214
|
+
text: paymentInfo,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
type: 'text',
|
|
219
|
+
data: {
|
|
220
|
+
type: 'plain',
|
|
221
|
+
color: '#9397A1',
|
|
222
|
+
text: translate('notification.common.trialPeriod', locale),
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
type: 'text',
|
|
227
|
+
data: {
|
|
228
|
+
type: 'plain',
|
|
229
|
+
text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
nftMintItem && {
|
|
233
|
+
type: 'text',
|
|
234
|
+
data: {
|
|
235
|
+
type: 'plain',
|
|
236
|
+
color: '#9397A1',
|
|
237
|
+
text: translate('notification.common.nftAddress', locale),
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
nftMintItem && {
|
|
241
|
+
type: 'text',
|
|
242
|
+
data: {
|
|
243
|
+
type: 'plain',
|
|
244
|
+
text: toDid(nftMintItem.address),
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
].filter(Boolean),
|
|
248
|
+
},
|
|
249
|
+
].filter(Boolean),
|
|
250
|
+
// @ts-ignore
|
|
251
|
+
actions: [
|
|
252
|
+
{
|
|
253
|
+
name: 'viewSubscription',
|
|
254
|
+
title: translate('notification.common.viewSubscription', locale),
|
|
255
|
+
link: viewSubscriptionLink,
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'viewSubscription',
|
|
259
|
+
title: translate('notification.common.viewInvoice', locale),
|
|
260
|
+
link: viewInvoiceLink,
|
|
261
|
+
},
|
|
262
|
+
].filter(Boolean),
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return template;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/brace-style */
|
|
2
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
3
|
+
import { fromUnitToToken } from '@ocap/util';
|
|
4
|
+
import type { ManipulateType } from 'dayjs';
|
|
5
|
+
import prettyMsI18n from 'pretty-ms-i18n';
|
|
6
|
+
|
|
7
|
+
import { getUserLocale } from '../../../integrations/blocklet/notification';
|
|
8
|
+
import { translate } from '../../../locales';
|
|
9
|
+
import { Customer, Invoice, Subscription } from '../../../store/models';
|
|
10
|
+
import { PaymentCurrency } from '../../../store/models/payment-currency';
|
|
11
|
+
import { PaymentDetail, getPaymentDetail } from '../../payment';
|
|
12
|
+
import { getMainProductName } from '../../product';
|
|
13
|
+
import { getCustomerSubscriptionPageUrl } from '../../subscription';
|
|
14
|
+
import { formatTime, getPrettyMsI18nLocale } from '../../time';
|
|
15
|
+
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
|
|
16
|
+
|
|
17
|
+
export interface SubscriptionTrialWillEndEmailTemplateOptions {
|
|
18
|
+
subscriptionId: string;
|
|
19
|
+
willRenewValue: number;
|
|
20
|
+
willRenewUnit: ManipulateType;
|
|
21
|
+
required?: false | true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
interface SubscriptionTrailWilEndEmailTemplateContext {
|
|
25
|
+
locale: string;
|
|
26
|
+
productName: string;
|
|
27
|
+
at: string;
|
|
28
|
+
willRenewDuration: string;
|
|
29
|
+
paymentDetail: PaymentDetail;
|
|
30
|
+
|
|
31
|
+
userDid: string;
|
|
32
|
+
paymentInfo: string;
|
|
33
|
+
currentPeriodStart: string;
|
|
34
|
+
currentPeriodEnd: string;
|
|
35
|
+
duration: string;
|
|
36
|
+
|
|
37
|
+
viewSubscriptionLink: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export class SubscriptionTrailWilEndEmailTemplate
|
|
41
|
+
implements BaseEmailTemplate<SubscriptionTrailWilEndEmailTemplateContext>
|
|
42
|
+
{
|
|
43
|
+
options: SubscriptionTrialWillEndEmailTemplateOptions;
|
|
44
|
+
|
|
45
|
+
constructor(options: SubscriptionTrialWillEndEmailTemplateOptions) {
|
|
46
|
+
this.options = options;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async getContext(): Promise<SubscriptionTrailWilEndEmailTemplateContext> {
|
|
50
|
+
const subscription: Subscription | null = await Subscription.findByPk(this.options.subscriptionId);
|
|
51
|
+
if (!subscription) {
|
|
52
|
+
throw new Error(`Subscription not found: ${this.options.subscriptionId}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const customer = await Customer.findByPk(subscription.customer_id);
|
|
56
|
+
if (!customer) {
|
|
57
|
+
throw new Error(`Customer not found: ${subscription.customer_id}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const invoice = (await Invoice.findByPk(subscription.latest_invoice_id)) as Invoice;
|
|
61
|
+
const paymentCurrency = (await PaymentCurrency.findOne({
|
|
62
|
+
where: {
|
|
63
|
+
id: subscription.currency_id,
|
|
64
|
+
},
|
|
65
|
+
})) as PaymentCurrency;
|
|
66
|
+
|
|
67
|
+
const locale = await getUserLocale(customer.did);
|
|
68
|
+
const productName = await getMainProductName(subscription.id);
|
|
69
|
+
const at: string = formatTime((subscription.trail_end as number) * 1000);
|
|
70
|
+
const willRenewDuration: string =
|
|
71
|
+
locale === 'en' ? this.getWillRenewDuration(locale) : this.getWillRenewDuration(locale).replaceAll(' ', '');
|
|
72
|
+
|
|
73
|
+
const userDid = customer.did;
|
|
74
|
+
const paymentDetail: PaymentDetail = await getPaymentDetail(userDid, invoice);
|
|
75
|
+
const paymentInfo: string = `${fromUnitToToken(+invoice.total, paymentCurrency.decimal)} ${paymentCurrency.symbol}`;
|
|
76
|
+
const currentPeriodStart: string = formatTime((subscription.trail_start as number) * 1000);
|
|
77
|
+
const currentPeriodEnd: string = formatTime((subscription.trail_end as number) * 1000);
|
|
78
|
+
const duration: string = prettyMsI18n(
|
|
79
|
+
new Date(currentPeriodEnd).getTime() - new Date(currentPeriodStart).getTime(),
|
|
80
|
+
{
|
|
81
|
+
locale: getPrettyMsI18nLocale(locale),
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const viewSubscriptionLink = getCustomerSubscriptionPageUrl(subscription.id, locale);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
locale,
|
|
89
|
+
productName,
|
|
90
|
+
at,
|
|
91
|
+
willRenewDuration,
|
|
92
|
+
paymentDetail,
|
|
93
|
+
|
|
94
|
+
userDid,
|
|
95
|
+
paymentInfo,
|
|
96
|
+
currentPeriodStart,
|
|
97
|
+
currentPeriodEnd,
|
|
98
|
+
duration,
|
|
99
|
+
|
|
100
|
+
viewSubscriptionLink,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getWillRenewDuration(locale: string): string {
|
|
105
|
+
if (this.options.willRenewUnit === 'M') {
|
|
106
|
+
if (this.options.willRenewValue > 1) {
|
|
107
|
+
return `${this.options.willRenewValue} ${translate('notification.common.months', locale)}`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return `${this.options.willRenewValue} ${translate('notification.common.month', locale)}`;
|
|
111
|
+
}
|
|
112
|
+
if (this.options.willRenewUnit === 'd') {
|
|
113
|
+
if (this.options.willRenewValue > 1) {
|
|
114
|
+
return `${this.options.willRenewValue} ${translate('notification.common.days', locale)}`;
|
|
115
|
+
}
|
|
116
|
+
return `${this.options.willRenewValue} ${translate('notification.common.day', locale)}`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (this.options.willRenewUnit === 'm') {
|
|
120
|
+
if (this.options.willRenewValue > 1) {
|
|
121
|
+
return `${this.options.willRenewValue} ${translate('notification.common.minutes', locale)}`;
|
|
122
|
+
}
|
|
123
|
+
return `${this.options.willRenewValue} ${translate('notification.common.minute', locale)}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return `${this.options.willRenewValue} ${this.options.willRenewUnit}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async getTemplate(): Promise<BaseEmailTemplateType | null> {
|
|
130
|
+
const {
|
|
131
|
+
locale,
|
|
132
|
+
productName,
|
|
133
|
+
at,
|
|
134
|
+
willRenewDuration,
|
|
135
|
+
paymentDetail,
|
|
136
|
+
|
|
137
|
+
userDid,
|
|
138
|
+
paymentInfo,
|
|
139
|
+
currentPeriodStart,
|
|
140
|
+
currentPeriodEnd,
|
|
141
|
+
duration,
|
|
142
|
+
|
|
143
|
+
viewSubscriptionLink,
|
|
144
|
+
} = await this.getContext();
|
|
145
|
+
|
|
146
|
+
const canPay: boolean = paymentDetail.balance >= paymentDetail.price;
|
|
147
|
+
if (canPay && !this.options.required) {
|
|
148
|
+
// 当余额足够支付并且本封邮件不是必须发送时,可以不发送邮件
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const template: BaseEmailTemplateType = {
|
|
153
|
+
title: `${translate('notification.subscriptionTrialWillEnd.title', locale, {
|
|
154
|
+
productName: `(${productName})`,
|
|
155
|
+
willRenewDuration,
|
|
156
|
+
})}`,
|
|
157
|
+
body: canPay
|
|
158
|
+
? `${translate('notification.subscriptionTrialWillEnd.body', locale, {
|
|
159
|
+
at,
|
|
160
|
+
productName: `(${productName})`,
|
|
161
|
+
willRenewDuration,
|
|
162
|
+
})}`
|
|
163
|
+
: `${translate('notification.subscriptionTrialWillEnd.unableToPayBody', locale, {
|
|
164
|
+
at,
|
|
165
|
+
productName: `(${productName})`,
|
|
166
|
+
willRenewDuration,
|
|
167
|
+
balance: `${paymentDetail.balance} ${paymentDetail.symbol}`,
|
|
168
|
+
price: `${paymentDetail.price} ${paymentDetail.symbol}`,
|
|
169
|
+
})}`,
|
|
170
|
+
// @ts-expect-error
|
|
171
|
+
attachments: [
|
|
172
|
+
{
|
|
173
|
+
type: 'section',
|
|
174
|
+
fields: [
|
|
175
|
+
{
|
|
176
|
+
type: 'text',
|
|
177
|
+
data: {
|
|
178
|
+
type: 'plain',
|
|
179
|
+
color: '#9397A1',
|
|
180
|
+
text: translate('notification.common.account', locale),
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'text',
|
|
185
|
+
data: {
|
|
186
|
+
type: 'plain',
|
|
187
|
+
text: userDid,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: 'text',
|
|
192
|
+
data: {
|
|
193
|
+
type: 'plain',
|
|
194
|
+
color: '#9397A1',
|
|
195
|
+
text: translate('notification.common.product', locale),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: 'text',
|
|
200
|
+
data: {
|
|
201
|
+
type: 'plain',
|
|
202
|
+
text: productName,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: 'text',
|
|
207
|
+
data: {
|
|
208
|
+
type: 'plain',
|
|
209
|
+
color: '#9397A1',
|
|
210
|
+
text: translate('notification.common.paymentInfo', locale),
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
data: {
|
|
216
|
+
type: 'plain',
|
|
217
|
+
text: paymentInfo,
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
type: 'text',
|
|
222
|
+
data: {
|
|
223
|
+
type: 'plain',
|
|
224
|
+
color: '#9397A1',
|
|
225
|
+
text: translate('notification.common.trialPeriod', locale),
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
type: 'text',
|
|
230
|
+
data: {
|
|
231
|
+
type: 'plain',
|
|
232
|
+
text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
].filter(Boolean),
|
|
236
|
+
},
|
|
237
|
+
].filter(Boolean),
|
|
238
|
+
// @ts-ignore
|
|
239
|
+
actions: [
|
|
240
|
+
{
|
|
241
|
+
name: 'viewSubscription',
|
|
242
|
+
title: translate('notification.common.viewSubscription', locale),
|
|
243
|
+
link: viewSubscriptionLink,
|
|
244
|
+
},
|
|
245
|
+
].filter(Boolean),
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
return template;
|
|
249
|
+
}
|
|
250
|
+
}
|