payment-kit 1.13.136 → 1.13.138
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/base.ts +7 -33
- package/api/src/crons/index.ts +7 -0
- package/api/src/crons/interface/base.ts +17 -0
- package/api/src/crons/subscription-trail-will-end.ts +29 -1
- package/api/src/crons/subscription-will-canceled.ts +48 -0
- package/api/src/crons/subscription-will-renew.ts +29 -2
- package/api/src/libs/invoice.ts +20 -6
- package/api/src/libs/notification/template/one-time-payment-succeeded.ts +245 -0
- package/api/src/libs/notification/template/subscription-cacceled.ts +241 -0
- package/api/src/libs/notification/template/subscription-refund-succeeded.ts +286 -0
- package/api/src/libs/notification/template/subscription-renew-failed.ts +17 -6
- package/api/src/libs/notification/template/subscription-renewed.ts +27 -6
- package/api/src/libs/notification/template/subscription-succeeded.ts +14 -5
- package/api/src/libs/notification/template/subscription-trial-start.ts +13 -4
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +7 -3
- package/api/src/libs/notification/template/subscription-upgraded.ts +261 -0
- package/api/src/libs/notification/template/subscription-will-canceled.ts +225 -0
- package/api/src/libs/notification/template/subscription-will-renew.ts +30 -3
- package/api/src/libs/product.ts +24 -0
- package/api/src/libs/queue/index.ts +2 -0
- package/api/src/libs/queue/store.ts +1 -1
- package/api/src/libs/security.ts +1 -1
- package/api/src/libs/subscription.ts +19 -3
- package/api/src/libs/util.ts +33 -0
- package/api/src/locales/en.ts +38 -4
- package/api/src/locales/zh.ts +36 -2
- package/api/src/queues/notification.ts +91 -2
- package/api/src/routes/connect/setup.ts +2 -2
- package/api/src/routes/connect/shared.ts +2 -2
- package/api/src/store/models/subscription.ts +5 -0
- package/api/src/store/models/types.ts +3 -0
- package/blocklet.yml +1 -1
- package/package.json +44 -44
- package/src/contexts/session.ts +2 -2
- package/src/pages/admin/payments/links/create.tsx +1 -1
- package/src/pages/admin/products/pricing-tables/create.tsx +1 -1
- package/src/pages/customer/invoice.tsx +12 -3
- package/src/pages/customer/subscription/update.tsx +1 -1
- package/api/src/crons/interface/diff.ts +0 -9
package/api/src/crons/base.ts
CHANGED
|
@@ -4,26 +4,17 @@ import pAll from 'p-all';
|
|
|
4
4
|
|
|
5
5
|
import { notificationCronConcurrency } from '../libs/env';
|
|
6
6
|
import logger from '../libs/logger';
|
|
7
|
-
import
|
|
8
|
-
import { NotificationQueueJob, notificationQueue } from '../queues/notification';
|
|
7
|
+
import { NotificationQueueJobType, notificationQueue } from '../queues/notification';
|
|
9
8
|
import type { Subscription } from '../store/models';
|
|
10
|
-
import type { Diff } from './interface/
|
|
9
|
+
import type { Diff, Task } from './interface/base';
|
|
11
10
|
|
|
12
11
|
export interface BaseSubscriptionScheduleNotificationSubscription extends Subscription {
|
|
13
12
|
diffs: Diff[];
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
export type BaseSubscriptionScheduleNotificationEventType =
|
|
17
|
-
| 'customer.subscription.trial_will_end'
|
|
18
|
-
| 'customer.subscription.will_renew';
|
|
15
|
+
export type BaseSubscriptionScheduleNotificationEventType = NotificationQueueJobType;
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
id: string;
|
|
22
|
-
job: { type: NotificationQueueJob['type']; options: Options };
|
|
23
|
-
delay: number;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export abstract class BaseSubscriptionScheduleNotification {
|
|
17
|
+
export abstract class BaseSubscriptionScheduleNotification<Options extends any> {
|
|
27
18
|
readonly start: number;
|
|
28
19
|
readonly end: number;
|
|
29
20
|
abstract eventType: BaseSubscriptionScheduleNotificationEventType;
|
|
@@ -154,30 +145,13 @@ export abstract class BaseSubscriptionScheduleNotification {
|
|
|
154
145
|
});
|
|
155
146
|
}
|
|
156
147
|
|
|
157
|
-
|
|
158
|
-
subscriptions: BaseSubscriptionScheduleNotificationSubscription[]
|
|
159
|
-
): Promise<void> {
|
|
160
|
-
const type = this.eventType;
|
|
148
|
+
abstract getTask({ subscription, diff }: { subscription: Subscription; diff: Diff }): Task<Options>;
|
|
161
149
|
|
|
150
|
+
async addTaskToQueue(subscriptions: BaseSubscriptionScheduleNotificationSubscription[]): Promise<void> {
|
|
162
151
|
const tasks: Task<Options>[] = [];
|
|
163
152
|
for (const subscription of subscriptions) {
|
|
164
153
|
for (const diff of subscription.diffs) {
|
|
165
|
-
const task: Task<Options> = {
|
|
166
|
-
id: `${subscription.id}.${type}.${diff.value}.${diff.unit}`,
|
|
167
|
-
job: {
|
|
168
|
-
type,
|
|
169
|
-
options: {
|
|
170
|
-
subscriptionId: subscription.id,
|
|
171
|
-
willRenewValue: diff.value,
|
|
172
|
-
willRenewUnit: diff.unit,
|
|
173
|
-
required: !!diff.required,
|
|
174
|
-
} as Options,
|
|
175
|
-
},
|
|
176
|
-
delay: dayjs(subscription.current_period_end * 1000)
|
|
177
|
-
.subtract(diff.value, diff.unit)
|
|
178
|
-
.diff(this.start, 's'),
|
|
179
|
-
};
|
|
180
|
-
|
|
154
|
+
const task: Task<Options> = this.getTask({ subscription, diff });
|
|
181
155
|
tasks.push(task);
|
|
182
156
|
}
|
|
183
157
|
}
|
package/api/src/crons/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import logger from '../libs/logger';
|
|
|
5
5
|
import { startSubscriptionQueue } from '../queues/subscription';
|
|
6
6
|
import { CheckoutSession } from '../store/models';
|
|
7
7
|
import { SubscriptionTrailWillEndSchedule } from './subscription-trail-will-end';
|
|
8
|
+
import { SubscriptionWillCanceledSchedule } from './subscription-will-canceled';
|
|
8
9
|
import { SubscriptionWillRenewSchedule } from './subscription-will-renew';
|
|
9
10
|
|
|
10
11
|
function init() {
|
|
@@ -23,6 +24,12 @@ function init() {
|
|
|
23
24
|
fn: () => new SubscriptionTrailWillEndSchedule().run(),
|
|
24
25
|
options: { runOnInit: true },
|
|
25
26
|
},
|
|
27
|
+
{
|
|
28
|
+
name: 'customer.subscription.will_canceled',
|
|
29
|
+
time: notificationCronTime,
|
|
30
|
+
fn: () => new SubscriptionWillCanceledSchedule().run(),
|
|
31
|
+
options: { runOnInit: true },
|
|
32
|
+
},
|
|
26
33
|
{
|
|
27
34
|
name: 'subscription.schedule.retry',
|
|
28
35
|
time: subscriptionCronTime,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ManipulateType } from 'dayjs';
|
|
2
|
+
|
|
3
|
+
import type { NotificationQueueJob } from '../../queues/notification';
|
|
4
|
+
|
|
5
|
+
export interface Diff {
|
|
6
|
+
value: number;
|
|
7
|
+
unit: ManipulateType;
|
|
8
|
+
|
|
9
|
+
// 这个 job 必须跑吗?
|
|
10
|
+
required?: false | true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type Task<Options> = {
|
|
14
|
+
id: string;
|
|
15
|
+
job: { type: NotificationQueueJob['type']; options: Options };
|
|
16
|
+
delay: number;
|
|
17
|
+
};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
1
2
|
/* eslint-disable no-console */
|
|
2
3
|
import { Op } from 'sequelize';
|
|
3
4
|
|
|
5
|
+
import type { SubscriptionTrialWillEndEmailTemplateOptions } from '../libs/notification/template/subscription-trial-will-end';
|
|
4
6
|
import { Subscription } from '../store/models';
|
|
5
7
|
import { BaseSubscriptionScheduleNotification, BaseSubscriptionScheduleNotificationEventType } from './base';
|
|
8
|
+
import type { Diff, Task } from './interface/base';
|
|
6
9
|
|
|
7
|
-
export class SubscriptionTrailWillEndSchedule extends BaseSubscriptionScheduleNotification {
|
|
10
|
+
export class SubscriptionTrailWillEndSchedule extends BaseSubscriptionScheduleNotification<SubscriptionTrialWillEndEmailTemplateOptions> {
|
|
8
11
|
override eventType: BaseSubscriptionScheduleNotificationEventType = 'customer.subscription.trial_will_end';
|
|
9
12
|
|
|
10
13
|
async getSubscriptions(): Promise<Subscription[]> {
|
|
@@ -28,4 +31,29 @@ export class SubscriptionTrailWillEndSchedule extends BaseSubscriptionScheduleNo
|
|
|
28
31
|
|
|
29
32
|
return subscriptions;
|
|
30
33
|
}
|
|
34
|
+
|
|
35
|
+
override getTask({
|
|
36
|
+
subscription,
|
|
37
|
+
diff,
|
|
38
|
+
}: {
|
|
39
|
+
subscription: Subscription;
|
|
40
|
+
diff: Diff;
|
|
41
|
+
}): Task<SubscriptionTrialWillEndEmailTemplateOptions> {
|
|
42
|
+
const type = this.eventType;
|
|
43
|
+
return {
|
|
44
|
+
id: `${subscription.id}.${type}.${diff.value}.${diff.unit}`,
|
|
45
|
+
job: {
|
|
46
|
+
type,
|
|
47
|
+
options: {
|
|
48
|
+
subscriptionId: subscription.id,
|
|
49
|
+
willRenewValue: diff.value,
|
|
50
|
+
willRenewUnit: diff.unit,
|
|
51
|
+
required: !!diff.required,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
delay: dayjs(subscription.current_period_end * 1000)
|
|
55
|
+
.subtract(diff.value, diff.unit)
|
|
56
|
+
.diff(this.start, 's'),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
31
59
|
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
|
|
4
|
+
import type { SubscriptionWillCanceledEmailTemplateOptions } from '../libs/notification/template/subscription-will-canceled';
|
|
5
|
+
import { Subscription } from '../store/models';
|
|
6
|
+
import { BaseSubscriptionScheduleNotification, BaseSubscriptionScheduleNotificationEventType } from './base';
|
|
7
|
+
import type { Diff, Task } from './interface/base';
|
|
8
|
+
|
|
9
|
+
export class SubscriptionWillCanceledSchedule extends BaseSubscriptionScheduleNotification<SubscriptionWillCanceledEmailTemplateOptions> {
|
|
10
|
+
override eventType: BaseSubscriptionScheduleNotificationEventType = 'customer.subscription.will_canceled';
|
|
11
|
+
|
|
12
|
+
async getSubscriptions(): Promise<Subscription[]> {
|
|
13
|
+
const subscriptions = await Subscription.findAll({
|
|
14
|
+
where: {
|
|
15
|
+
status: 'past_due',
|
|
16
|
+
},
|
|
17
|
+
attributes: ['id', 'current_period_start', 'current_period_end'],
|
|
18
|
+
raw: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return subscriptions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
override getTask({
|
|
25
|
+
subscription,
|
|
26
|
+
diff,
|
|
27
|
+
}: {
|
|
28
|
+
subscription: Subscription;
|
|
29
|
+
diff: Diff;
|
|
30
|
+
}): Task<SubscriptionWillCanceledEmailTemplateOptions> {
|
|
31
|
+
const type = this.eventType;
|
|
32
|
+
return {
|
|
33
|
+
id: `${subscription.id}.${type}.${diff.value}.${diff.unit}`,
|
|
34
|
+
job: {
|
|
35
|
+
type,
|
|
36
|
+
options: {
|
|
37
|
+
subscriptionId: subscription.id,
|
|
38
|
+
willCancelValue: diff.value,
|
|
39
|
+
willCancelUnit: diff.unit,
|
|
40
|
+
required: !!diff.required,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
delay: dayjs(subscription.current_period_end * 1000)
|
|
44
|
+
.subtract(diff.value, diff.unit)
|
|
45
|
+
.diff(this.start, 's'),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
2
|
import { Op } from 'sequelize';
|
|
3
3
|
|
|
4
|
+
import type { SubscriptionWillRenewEmailTemplateOptions } from '../libs/notification/template/subscription-will-renew';
|
|
4
5
|
import { Subscription } from '../store/models';
|
|
5
6
|
import { BaseSubscriptionScheduleNotification, BaseSubscriptionScheduleNotificationEventType } from './base';
|
|
7
|
+
import type { Diff, Task } from './interface/base';
|
|
6
8
|
|
|
7
|
-
export class SubscriptionWillRenewSchedule extends BaseSubscriptionScheduleNotification {
|
|
9
|
+
export class SubscriptionWillRenewSchedule extends BaseSubscriptionScheduleNotification<SubscriptionWillRenewEmailTemplateOptions> {
|
|
8
10
|
override eventType: BaseSubscriptionScheduleNotificationEventType = 'customer.subscription.will_renew';
|
|
9
11
|
|
|
10
12
|
async getSubscriptions(): Promise<Subscription[]> {
|
|
@@ -26,4 +28,29 @@ export class SubscriptionWillRenewSchedule extends BaseSubscriptionScheduleNotif
|
|
|
26
28
|
|
|
27
29
|
return subscriptions;
|
|
28
30
|
}
|
|
31
|
+
|
|
32
|
+
override getTask({
|
|
33
|
+
subscription,
|
|
34
|
+
diff,
|
|
35
|
+
}: {
|
|
36
|
+
subscription: Subscription;
|
|
37
|
+
diff: Diff;
|
|
38
|
+
}): Task<SubscriptionWillRenewEmailTemplateOptions> {
|
|
39
|
+
const type = this.eventType;
|
|
40
|
+
return {
|
|
41
|
+
id: `${subscription.id}.${type}.${diff.value}.${diff.unit}`,
|
|
42
|
+
job: {
|
|
43
|
+
type,
|
|
44
|
+
options: {
|
|
45
|
+
subscriptionId: subscription.id,
|
|
46
|
+
willRenewValue: diff.value,
|
|
47
|
+
willRenewUnit: diff.unit,
|
|
48
|
+
required: !!diff.required,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
delay: dayjs(subscription.current_period_end * 1000)
|
|
52
|
+
.subtract(diff.value, diff.unit)
|
|
53
|
+
.diff(this.start, 's'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
29
56
|
}
|
package/api/src/libs/invoice.ts
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
|
+
import querystring from 'querystring';
|
|
2
|
+
|
|
1
3
|
import { component } from '@blocklet/sdk';
|
|
2
4
|
import type { LiteralUnion } from 'type-fest';
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { getConnectQueryParam } from './util';
|
|
7
|
+
|
|
8
|
+
export function getCustomerInvoicePageUrl({
|
|
9
|
+
invoiceId,
|
|
10
|
+
userDid,
|
|
11
|
+
locale = 'en',
|
|
12
|
+
action = '',
|
|
13
|
+
}: {
|
|
14
|
+
userDid: string;
|
|
15
|
+
invoiceId: string;
|
|
16
|
+
locale: LiteralUnion<'en' | 'zh', string>;
|
|
17
|
+
action?: LiteralUnion<'pay', string>;
|
|
18
|
+
}) {
|
|
19
|
+
return component.getUrl(
|
|
20
|
+
`customer/invoice/${invoiceId}?locale=${locale}&action=${action}&${querystring.stringify(
|
|
21
|
+
getConnectQueryParam({ userDid })
|
|
22
|
+
)}`
|
|
23
|
+
);
|
|
10
24
|
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/brace-style */
|
|
2
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
3
|
+
import { fromUnitToToken } from '@ocap/util';
|
|
4
|
+
import pWaitFor from 'p-wait-for';
|
|
5
|
+
|
|
6
|
+
import { getUserLocale } from '../../../integrations/blocklet/notification';
|
|
7
|
+
import { translate } from '../../../locales';
|
|
8
|
+
import { CheckoutSession, Customer, NftMintItem, PaymentIntent, PaymentMethod } from '../../../store/models';
|
|
9
|
+
import { PaymentCurrency } from '../../../store/models/payment-currency';
|
|
10
|
+
import { getMainProductNameByCheckoutSession } from '../../product';
|
|
11
|
+
import { formatTime } from '../../time';
|
|
12
|
+
import { getExplorerLink } from '../../util';
|
|
13
|
+
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
|
|
14
|
+
|
|
15
|
+
export interface OneTimePaymentSucceededEmailTemplateOptions {
|
|
16
|
+
checkoutSessionId: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface OneTimePaymentSucceededEmailTemplateContext {
|
|
20
|
+
locale: string;
|
|
21
|
+
productName: string;
|
|
22
|
+
at: string;
|
|
23
|
+
|
|
24
|
+
nftMintItem: NftMintItem | undefined;
|
|
25
|
+
chainHost: string | undefined;
|
|
26
|
+
userDid: string;
|
|
27
|
+
paymentInfo: string;
|
|
28
|
+
|
|
29
|
+
viewSubscriptionLink: string;
|
|
30
|
+
viewInvoiceLink: string;
|
|
31
|
+
viewTxHashLink: string | undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @see https://github.com/blocklet/payment-kit/issues/236#issue-2007698930
|
|
36
|
+
* @description
|
|
37
|
+
* @export
|
|
38
|
+
* @class OneTimePaymentSucceededEmailTemplate
|
|
39
|
+
* @implements {BaseEmailTemplate<OneTimePaymentSucceededEmailTemplateContext>}
|
|
40
|
+
*/
|
|
41
|
+
export class OneTimePaymentSucceededEmailTemplate
|
|
42
|
+
implements BaseEmailTemplate<OneTimePaymentSucceededEmailTemplateContext>
|
|
43
|
+
{
|
|
44
|
+
options: OneTimePaymentSucceededEmailTemplateOptions;
|
|
45
|
+
|
|
46
|
+
constructor(options: OneTimePaymentSucceededEmailTemplateOptions) {
|
|
47
|
+
this.options = options;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async getContext(): Promise<OneTimePaymentSucceededEmailTemplateContext> {
|
|
51
|
+
const cs: CheckoutSession | null = await CheckoutSession.findByPk(this.options.checkoutSessionId);
|
|
52
|
+
if (!cs) {
|
|
53
|
+
throw new Error(`CheckoutSession(${this.options.checkoutSessionId}) not found`);
|
|
54
|
+
}
|
|
55
|
+
if (cs.mode !== 'payment') {
|
|
56
|
+
throw new Error(`CheckoutSession(${this.options.checkoutSessionId}) mode must be payment`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const customer = await Customer.findByPk(cs.customer_id);
|
|
60
|
+
if (!customer) {
|
|
61
|
+
throw new Error(`Customer not found: ${cs.customer_id}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
await pWaitFor(
|
|
65
|
+
async () => {
|
|
66
|
+
const checkoutSession = await CheckoutSession.findOne({
|
|
67
|
+
where: {
|
|
68
|
+
id: cs.id,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return Boolean(['disabled', 'minted', 'sent', 'error'].includes(checkoutSession?.nft_mint_status as string));
|
|
73
|
+
},
|
|
74
|
+
{ timeout: 1000 * 10, interval: 1000 }
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
const checkoutSession = (await CheckoutSession.findByPk(cs.id)) as CheckoutSession;
|
|
78
|
+
const paymentCurrency = (await PaymentCurrency.findOne({
|
|
79
|
+
where: {
|
|
80
|
+
id: checkoutSession.currency_id,
|
|
81
|
+
},
|
|
82
|
+
})) as PaymentCurrency;
|
|
83
|
+
|
|
84
|
+
const userDid: string = customer.did;
|
|
85
|
+
const locale = await getUserLocale(userDid);
|
|
86
|
+
const productName = await getMainProductNameByCheckoutSession(checkoutSession);
|
|
87
|
+
const at: string = formatTime(checkoutSession.created_at);
|
|
88
|
+
|
|
89
|
+
const paymentInfo: string = `${fromUnitToToken(checkoutSession?.amount_total, paymentCurrency.decimal)} ${
|
|
90
|
+
paymentCurrency.symbol
|
|
91
|
+
}`;
|
|
92
|
+
const hasNft: boolean = checkoutSession?.nft_mint_status === 'minted';
|
|
93
|
+
const nftMintItem: NftMintItem | undefined = hasNft
|
|
94
|
+
? // @ts-expect-error
|
|
95
|
+
checkoutSession?.nft_mint_details?.[paymentMethod.type]
|
|
96
|
+
: undefined;
|
|
97
|
+
|
|
98
|
+
const paymentIntent = await PaymentIntent.findByPk(checkoutSession!.payment_intent_id);
|
|
99
|
+
const paymentMethod: PaymentMethod | null = await PaymentMethod.findByPk(paymentIntent!.payment_method_id);
|
|
100
|
+
// @ts-expect-error
|
|
101
|
+
const chainHost: string | undefined = paymentMethod?.settings?.[paymentMethod.type]?.api_host;
|
|
102
|
+
const viewSubscriptionLink = '';
|
|
103
|
+
const viewInvoiceLink = '';
|
|
104
|
+
|
|
105
|
+
// @ts-expect-error
|
|
106
|
+
const txHash: string | undefined = paymentIntent?.payment_details?.[paymentMethod.type]?.tx_hash;
|
|
107
|
+
const viewTxHashLink: string | undefined = txHash && getExplorerLink(chainHost, txHash as string, 'tx');
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
locale,
|
|
111
|
+
productName,
|
|
112
|
+
at,
|
|
113
|
+
|
|
114
|
+
userDid,
|
|
115
|
+
nftMintItem,
|
|
116
|
+
chainHost,
|
|
117
|
+
paymentInfo,
|
|
118
|
+
|
|
119
|
+
viewSubscriptionLink,
|
|
120
|
+
viewInvoiceLink,
|
|
121
|
+
viewTxHashLink,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async getTemplate(): Promise<BaseEmailTemplateType> {
|
|
126
|
+
const {
|
|
127
|
+
locale,
|
|
128
|
+
productName,
|
|
129
|
+
at,
|
|
130
|
+
nftMintItem,
|
|
131
|
+
chainHost,
|
|
132
|
+
userDid,
|
|
133
|
+
paymentInfo,
|
|
134
|
+
viewSubscriptionLink,
|
|
135
|
+
viewInvoiceLink,
|
|
136
|
+
viewTxHashLink,
|
|
137
|
+
} = await this.getContext();
|
|
138
|
+
|
|
139
|
+
const template: BaseEmailTemplateType = {
|
|
140
|
+
title: `${translate('notification.oneTimePaymentSucceeded.title', locale, {
|
|
141
|
+
productName: `(${productName})`,
|
|
142
|
+
})}`,
|
|
143
|
+
body: `${translate('notification.oneTimePaymentSucceeded.body', locale, {
|
|
144
|
+
at,
|
|
145
|
+
productName: `(${productName})`,
|
|
146
|
+
})}`,
|
|
147
|
+
// @ts-expect-error
|
|
148
|
+
attachments: [
|
|
149
|
+
nftMintItem &&
|
|
150
|
+
chainHost && {
|
|
151
|
+
type: 'asset',
|
|
152
|
+
data: {
|
|
153
|
+
chainHost,
|
|
154
|
+
did: nftMintItem.address,
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
type: 'section',
|
|
159
|
+
fields: [
|
|
160
|
+
{
|
|
161
|
+
type: 'text',
|
|
162
|
+
data: {
|
|
163
|
+
type: 'plain',
|
|
164
|
+
color: '#9397A1',
|
|
165
|
+
text: translate('notification.common.account', locale),
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
type: 'text',
|
|
170
|
+
data: {
|
|
171
|
+
type: 'plain',
|
|
172
|
+
text: userDid,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'text',
|
|
177
|
+
data: {
|
|
178
|
+
type: 'plain',
|
|
179
|
+
color: '#9397A1',
|
|
180
|
+
text: translate('notification.common.product', locale),
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'text',
|
|
185
|
+
data: {
|
|
186
|
+
type: 'plain',
|
|
187
|
+
text: productName,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
type: 'text',
|
|
192
|
+
data: {
|
|
193
|
+
type: 'plain',
|
|
194
|
+
color: '#9397A1',
|
|
195
|
+
text: translate('notification.common.paymentInfo', locale),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
type: 'text',
|
|
200
|
+
data: {
|
|
201
|
+
type: 'plain',
|
|
202
|
+
text: paymentInfo,
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
type: 'text',
|
|
207
|
+
data: {
|
|
208
|
+
type: 'plain',
|
|
209
|
+
color: '#9397A1',
|
|
210
|
+
text: translate('notification.common.validityPeriod', locale),
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
type: 'text',
|
|
215
|
+
data: {
|
|
216
|
+
type: 'plain',
|
|
217
|
+
text: translate('notification.common.permanent', locale),
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
].filter(Boolean),
|
|
221
|
+
},
|
|
222
|
+
].filter(Boolean),
|
|
223
|
+
// @ts-ignore
|
|
224
|
+
actions: [
|
|
225
|
+
viewSubscriptionLink && {
|
|
226
|
+
name: 'viewSubscription',
|
|
227
|
+
title: translate('notification.common.viewSubscription', locale),
|
|
228
|
+
link: viewSubscriptionLink,
|
|
229
|
+
},
|
|
230
|
+
viewInvoiceLink && {
|
|
231
|
+
name: 'viewSubscription',
|
|
232
|
+
title: translate('notification.common.viewInvoice', locale),
|
|
233
|
+
link: viewInvoiceLink,
|
|
234
|
+
},
|
|
235
|
+
viewTxHashLink && {
|
|
236
|
+
name: 'viewTxHash',
|
|
237
|
+
title: translate('notification.common.viewTxHash', locale),
|
|
238
|
+
link: viewTxHashLink as string,
|
|
239
|
+
},
|
|
240
|
+
].filter(Boolean),
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
return template;
|
|
244
|
+
}
|
|
245
|
+
}
|