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
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/brace-style */
|
|
2
2
|
/* eslint-disable @typescript-eslint/indent */
|
|
3
|
-
import prettyMsI18n from 'pretty-ms-i18n';
|
|
4
|
-
|
|
5
|
-
import { getUserLocale } from '../../../integrations/blocklet/notification';
|
|
6
3
|
import { translate } from '../../../locales';
|
|
7
|
-
import {
|
|
4
|
+
import { PaymentIntent, PaymentMethod, Refund, Subscription } from '../../../store/models';
|
|
8
5
|
import { Invoice } from '../../../store/models/invoice';
|
|
9
6
|
import { PaymentCurrency } from '../../../store/models/payment-currency';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { formatCurrencyInfo, getExplorerLink, getSubscriptionNotificationCustomActions } from '../../util';
|
|
14
|
-
import type { BaseEmailTemplate, BaseEmailTemplateType } from './base';
|
|
7
|
+
import { formatTime } from '../../time';
|
|
8
|
+
import { formatCurrencyInfo, getExplorerLink } from '../../util';
|
|
9
|
+
import { BaseSubscriptionEmailTemplate, BaseEmailTemplateType } from './base';
|
|
15
10
|
|
|
16
11
|
export interface SubscriptionRefundSucceededEmailTemplateOptions {
|
|
17
12
|
refundId: string;
|
|
@@ -21,7 +16,6 @@ interface SubscriptionRefundSucceededEmailTemplateContext {
|
|
|
21
16
|
locale: string;
|
|
22
17
|
productName: string;
|
|
23
18
|
at: string;
|
|
24
|
-
|
|
25
19
|
chainHost: string | undefined;
|
|
26
20
|
userDid: string;
|
|
27
21
|
paymentInfo: string;
|
|
@@ -32,24 +26,23 @@ interface SubscriptionRefundSucceededEmailTemplateContext {
|
|
|
32
26
|
unusedPeriodStart?: string;
|
|
33
27
|
unusedPeriodEnd?: string;
|
|
34
28
|
unusedDuration: string;
|
|
35
|
-
|
|
36
29
|
viewSubscriptionLink: string;
|
|
37
30
|
viewTxHashLink: string | undefined;
|
|
38
31
|
refund: Refund;
|
|
39
32
|
customActions: any[];
|
|
33
|
+
isCreditSubscription: boolean;
|
|
40
34
|
}
|
|
41
35
|
|
|
42
|
-
export class SubscriptionRefundSucceededEmailTemplate
|
|
43
|
-
implements BaseEmailTemplate<SubscriptionRefundSucceededEmailTemplateContext>
|
|
44
|
-
{
|
|
36
|
+
export class SubscriptionRefundSucceededEmailTemplate extends BaseSubscriptionEmailTemplate<SubscriptionRefundSucceededEmailTemplateContext> {
|
|
45
37
|
options: SubscriptionRefundSucceededEmailTemplateOptions;
|
|
46
38
|
|
|
47
39
|
constructor(options: SubscriptionRefundSucceededEmailTemplateOptions) {
|
|
40
|
+
super();
|
|
48
41
|
this.options = options;
|
|
49
42
|
}
|
|
50
43
|
|
|
51
44
|
async getContext(): Promise<SubscriptionRefundSucceededEmailTemplateContext> {
|
|
52
|
-
const refund
|
|
45
|
+
const refund = await Refund.findByPk(this.options.refundId);
|
|
53
46
|
if (!refund) {
|
|
54
47
|
throw new Error(`Refund not found: ${this.options.refundId}`);
|
|
55
48
|
}
|
|
@@ -57,69 +50,80 @@ export class SubscriptionRefundSucceededEmailTemplate
|
|
|
57
50
|
throw new Error(`Refund not succeeded: ${this.options.refundId}`);
|
|
58
51
|
}
|
|
59
52
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}
|
|
53
|
+
// 获取基础订阅数据
|
|
54
|
+
const basicData = await this.getSubscriptionBasicData(refund.subscription_id!);
|
|
55
|
+
const { userDid, locale, productName, isCreditSubscription } = basicData;
|
|
64
56
|
|
|
57
|
+
// 获取支付相关信息
|
|
65
58
|
const paymentIntent = await PaymentIntent.findByPk(refund.payment_intent_id);
|
|
66
|
-
const paymentCurrency =
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
})) as PaymentCurrency;
|
|
71
|
-
|
|
72
|
-
const userDid: string = customer.did;
|
|
73
|
-
const locale = await getUserLocale(userDid);
|
|
74
|
-
const productName = await getMainProductName(refund.subscription_id!);
|
|
75
|
-
const at: string = formatTime(refund.created_at);
|
|
59
|
+
const paymentCurrency = await PaymentCurrency.findByPk(refund.currency_id);
|
|
60
|
+
if (!paymentCurrency) {
|
|
61
|
+
throw new Error(`PaymentCurrency not found: ${refund.currency_id}`);
|
|
62
|
+
}
|
|
76
63
|
|
|
64
|
+
// 获取发票和周期信息
|
|
77
65
|
let invoice = null;
|
|
78
66
|
let currentPeriodStart;
|
|
79
67
|
let currentPeriodEnd;
|
|
80
|
-
let duration;
|
|
68
|
+
let duration = '';
|
|
81
69
|
let unusedPeriodStart;
|
|
82
70
|
let unusedPeriodEnd;
|
|
83
|
-
let unusedDuration;
|
|
71
|
+
let unusedDuration = '';
|
|
72
|
+
|
|
84
73
|
if (refund.type === 'refund') {
|
|
85
|
-
invoice =
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
invoice = await Invoice.findByPk(refund.invoice_id);
|
|
75
|
+
if (invoice) {
|
|
76
|
+
const periodInfo = this.formatSubscriptionPeriod(invoice.period_start, invoice.period_end, locale);
|
|
77
|
+
currentPeriodStart = periodInfo.currentPeriodStart;
|
|
78
|
+
currentPeriodEnd = periodInfo.currentPeriodEnd;
|
|
79
|
+
duration = periodInfo.duration;
|
|
91
80
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
81
|
+
// 处理未使用期间
|
|
82
|
+
if (refund?.metadata?.unused_period_start && refund?.metadata?.unused_period_end) {
|
|
83
|
+
const unusedPeriodInfo = this.formatSubscriptionPeriod(
|
|
84
|
+
refund.metadata.unused_period_start,
|
|
85
|
+
refund.metadata.unused_period_end,
|
|
86
|
+
locale
|
|
87
|
+
);
|
|
88
|
+
unusedPeriodStart = unusedPeriodInfo.currentPeriodStart;
|
|
89
|
+
unusedPeriodEnd = unusedPeriodInfo.currentPeriodEnd;
|
|
90
|
+
unusedDuration = unusedPeriodInfo.duration;
|
|
91
|
+
}
|
|
98
92
|
}
|
|
99
93
|
}
|
|
100
94
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
);
|
|
95
|
+
// 获取支付方式
|
|
96
|
+
const paymentMethod = await PaymentMethod.findByPk(refund.payment_method_id || invoice?.default_payment_method_id);
|
|
104
97
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
paymentMethod
|
|
109
|
-
);
|
|
110
|
-
const refundInfo: string = formatCurrencyInfo(refund.amount, paymentCurrency, paymentMethod);
|
|
98
|
+
// 格式化支付和退款信息
|
|
99
|
+
const paymentInfo = formatCurrencyInfo(paymentIntent?.amount_received || '0', paymentCurrency, paymentMethod);
|
|
100
|
+
const refundInfo = formatCurrencyInfo(refund.amount, paymentCurrency, paymentMethod);
|
|
111
101
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
102
|
+
// 获取链接
|
|
103
|
+
let customActions: any[] = [];
|
|
104
|
+
let viewSubscriptionLink = '';
|
|
105
|
+
if (refund?.subscription_id) {
|
|
106
|
+
const subscription = await Subscription.findByPk(refund.subscription_id);
|
|
107
|
+
if (subscription) {
|
|
108
|
+
const links = this.generateSubscriptionLinks(
|
|
109
|
+
subscription,
|
|
110
|
+
locale,
|
|
111
|
+
userDid,
|
|
112
|
+
'refund.succeeded',
|
|
113
|
+
undefined,
|
|
114
|
+
isCreditSubscription
|
|
115
|
+
);
|
|
116
|
+
viewSubscriptionLink = links.viewSubscriptionLink;
|
|
117
|
+
customActions = links.customActions;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
119
120
|
|
|
121
|
+
// 获取交易哈希链接
|
|
122
|
+
// @ts-expect-error
|
|
123
|
+
const chainHost = paymentMethod?.settings?.[paymentMethod?.type]?.api_host;
|
|
120
124
|
// @ts-expect-error
|
|
121
|
-
const txHash
|
|
122
|
-
const viewTxHashLink
|
|
125
|
+
const txHash = refund?.payment_details?.[paymentMethod?.type]?.tx_hash;
|
|
126
|
+
const viewTxHashLink =
|
|
123
127
|
txHash &&
|
|
124
128
|
getExplorerLink({
|
|
125
129
|
type: 'tx',
|
|
@@ -127,17 +131,12 @@ export class SubscriptionRefundSucceededEmailTemplate
|
|
|
127
131
|
chainHost,
|
|
128
132
|
});
|
|
129
133
|
|
|
130
|
-
|
|
131
|
-
if (refund?.subscription_id) {
|
|
132
|
-
const subscription = await Subscription.findByPk(refund.subscription_id);
|
|
133
|
-
customActions = getSubscriptionNotificationCustomActions(subscription!, 'refund.succeeded', locale);
|
|
134
|
-
}
|
|
134
|
+
const at = formatTime(refund.created_at);
|
|
135
135
|
|
|
136
136
|
return {
|
|
137
137
|
locale,
|
|
138
138
|
productName,
|
|
139
139
|
at,
|
|
140
|
-
|
|
141
140
|
userDid,
|
|
142
141
|
chainHost,
|
|
143
142
|
paymentInfo,
|
|
@@ -148,20 +147,20 @@ export class SubscriptionRefundSucceededEmailTemplate
|
|
|
148
147
|
unusedPeriodStart,
|
|
149
148
|
unusedPeriodEnd,
|
|
150
149
|
unusedDuration,
|
|
151
|
-
|
|
152
150
|
viewSubscriptionLink,
|
|
153
151
|
viewTxHashLink,
|
|
154
152
|
refund,
|
|
155
153
|
customActions,
|
|
154
|
+
isCreditSubscription,
|
|
156
155
|
};
|
|
157
156
|
}
|
|
158
157
|
|
|
159
158
|
async getTemplate(): Promise<BaseEmailTemplateType> {
|
|
159
|
+
const context = await this.getContext();
|
|
160
160
|
const {
|
|
161
161
|
locale,
|
|
162
162
|
productName,
|
|
163
163
|
at,
|
|
164
|
-
|
|
165
164
|
userDid,
|
|
166
165
|
paymentInfo,
|
|
167
166
|
refundInfo,
|
|
@@ -171,217 +170,196 @@ export class SubscriptionRefundSucceededEmailTemplate
|
|
|
171
170
|
unusedPeriodStart,
|
|
172
171
|
unusedPeriodEnd,
|
|
173
172
|
unusedDuration,
|
|
174
|
-
|
|
175
173
|
viewSubscriptionLink,
|
|
176
174
|
viewTxHashLink,
|
|
177
175
|
refund,
|
|
178
|
-
|
|
176
|
+
isCreditSubscription,
|
|
177
|
+
} = context;
|
|
179
178
|
|
|
179
|
+
// 如果是质押返还类型,使用特殊模板
|
|
180
180
|
if (refund.type === 'stake_return') {
|
|
181
|
-
return
|
|
182
|
-
title: `${translate('notification.subscriptionStakeReturnSucceeded.title', locale, {
|
|
183
|
-
productName,
|
|
184
|
-
})}`,
|
|
185
|
-
body: `${translate('notification.subscriptionStakeReturnSucceeded.body', locale, {
|
|
186
|
-
at,
|
|
187
|
-
productName,
|
|
188
|
-
refundInfo,
|
|
189
|
-
})}`,
|
|
190
|
-
attachments: [
|
|
191
|
-
{
|
|
192
|
-
type: 'section',
|
|
193
|
-
fields: [
|
|
194
|
-
{
|
|
195
|
-
type: 'text',
|
|
196
|
-
data: {
|
|
197
|
-
type: 'plain',
|
|
198
|
-
color: '#9397A1',
|
|
199
|
-
text: translate('notification.common.account', locale),
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
type: 'text',
|
|
204
|
-
data: {
|
|
205
|
-
type: 'plain',
|
|
206
|
-
text: userDid,
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
{
|
|
210
|
-
type: 'text',
|
|
211
|
-
data: {
|
|
212
|
-
type: 'plain',
|
|
213
|
-
color: '#9397A1',
|
|
214
|
-
text: translate('notification.common.product', locale),
|
|
215
|
-
},
|
|
216
|
-
},
|
|
217
|
-
{
|
|
218
|
-
type: 'text',
|
|
219
|
-
data: {
|
|
220
|
-
type: 'plain',
|
|
221
|
-
text: productName,
|
|
222
|
-
},
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
type: 'text',
|
|
226
|
-
data: {
|
|
227
|
-
type: 'plain',
|
|
228
|
-
color: '#9397A1',
|
|
229
|
-
text: translate('notification.common.returnAmount', locale),
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
type: 'text',
|
|
234
|
-
data: {
|
|
235
|
-
type: 'plain',
|
|
236
|
-
text: refundInfo,
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
],
|
|
240
|
-
},
|
|
241
|
-
],
|
|
242
|
-
// @ts-ignore
|
|
243
|
-
actions: [
|
|
244
|
-
{
|
|
245
|
-
name: translate('notification.common.viewSubscription', locale),
|
|
246
|
-
title: translate('notification.common.viewSubscription', locale),
|
|
247
|
-
link: viewSubscriptionLink,
|
|
248
|
-
},
|
|
249
|
-
viewTxHashLink && {
|
|
250
|
-
name: translate('notification.common.viewTxHash', locale),
|
|
251
|
-
title: translate('notification.common.viewTxHash', locale),
|
|
252
|
-
link: viewTxHashLink as string,
|
|
253
|
-
},
|
|
254
|
-
].filter(Boolean),
|
|
255
|
-
};
|
|
181
|
+
return this.getStakeReturnTemplate(context);
|
|
256
182
|
}
|
|
257
183
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
fields: [
|
|
272
|
-
{
|
|
273
|
-
type: 'text',
|
|
274
|
-
data: {
|
|
275
|
-
type: 'plain',
|
|
276
|
-
color: '#9397A1',
|
|
277
|
-
text: translate('notification.common.account', locale),
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
type: 'text',
|
|
282
|
-
data: {
|
|
283
|
-
type: 'plain',
|
|
284
|
-
text: userDid,
|
|
285
|
-
},
|
|
286
|
-
},
|
|
287
|
-
{
|
|
288
|
-
type: 'text',
|
|
289
|
-
data: {
|
|
290
|
-
type: 'plain',
|
|
291
|
-
color: '#9397A1',
|
|
292
|
-
text: translate('notification.common.product', locale),
|
|
293
|
-
},
|
|
184
|
+
// 构建字段
|
|
185
|
+
const commonFields = this.buildCommonFields(userDid, productName, locale);
|
|
186
|
+
|
|
187
|
+
// 支付和退款金额字段
|
|
188
|
+
const amountFields = isCreditSubscription
|
|
189
|
+
? []
|
|
190
|
+
: [
|
|
191
|
+
{
|
|
192
|
+
type: 'text',
|
|
193
|
+
data: {
|
|
194
|
+
type: 'plain',
|
|
195
|
+
color: '#9397A1',
|
|
196
|
+
text: translate('notification.common.paymentAmount', locale),
|
|
294
197
|
},
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
type: 'text',
|
|
201
|
+
data: {
|
|
202
|
+
type: 'plain',
|
|
203
|
+
text: paymentInfo,
|
|
301
204
|
},
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: 'text',
|
|
208
|
+
data: {
|
|
209
|
+
type: 'plain',
|
|
210
|
+
color: '#9397A1',
|
|
211
|
+
text: translate('notification.common.refundAmount', locale),
|
|
309
212
|
},
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
type: 'text',
|
|
216
|
+
data: {
|
|
217
|
+
type: 'plain',
|
|
218
|
+
text: refundInfo,
|
|
316
219
|
},
|
|
220
|
+
},
|
|
221
|
+
];
|
|
222
|
+
|
|
223
|
+
// 有效期字段
|
|
224
|
+
const validityPeriodFields =
|
|
225
|
+
currentPeriodStart && currentPeriodEnd && !isCreditSubscription
|
|
226
|
+
? [
|
|
317
227
|
{
|
|
318
228
|
type: 'text',
|
|
319
229
|
data: {
|
|
320
230
|
type: 'plain',
|
|
321
231
|
color: '#9397A1',
|
|
322
|
-
text: translate('notification.common.
|
|
232
|
+
text: translate('notification.common.validityPeriod', locale),
|
|
323
233
|
},
|
|
324
234
|
},
|
|
325
235
|
{
|
|
326
236
|
type: 'text',
|
|
327
237
|
data: {
|
|
328
238
|
type: 'plain',
|
|
329
|
-
text:
|
|
239
|
+
text: `${currentPeriodStart} ~ ${currentPeriodEnd}(${duration})`,
|
|
330
240
|
},
|
|
331
241
|
},
|
|
242
|
+
]
|
|
243
|
+
: [];
|
|
244
|
+
|
|
245
|
+
// 退款期间字段
|
|
246
|
+
const refundPeriodFields =
|
|
247
|
+
unusedPeriodStart && unusedPeriodEnd && unusedDuration && !isCreditSubscription
|
|
248
|
+
? [
|
|
332
249
|
{
|
|
333
250
|
type: 'text',
|
|
334
251
|
data: {
|
|
335
252
|
type: 'plain',
|
|
336
253
|
color: '#9397A1',
|
|
337
|
-
text: translate('notification.common.
|
|
254
|
+
text: translate('notification.common.refundPeriod', locale),
|
|
338
255
|
},
|
|
339
256
|
},
|
|
340
257
|
{
|
|
341
258
|
type: 'text',
|
|
342
259
|
data: {
|
|
343
260
|
type: 'plain',
|
|
344
|
-
text: `${
|
|
261
|
+
text: `${unusedPeriodStart} ~ ${unusedPeriodEnd}(${unusedDuration})`,
|
|
345
262
|
},
|
|
346
263
|
},
|
|
264
|
+
]
|
|
265
|
+
: [];
|
|
347
266
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
267
|
+
// 构建操作按钮
|
|
268
|
+
const actions = [
|
|
269
|
+
{
|
|
270
|
+
name: translate('notification.common.viewSubscription', locale),
|
|
271
|
+
title: translate('notification.common.viewSubscription', locale),
|
|
272
|
+
link: viewSubscriptionLink,
|
|
273
|
+
},
|
|
274
|
+
viewTxHashLink && {
|
|
275
|
+
name: translate('notification.common.viewTxHash', locale),
|
|
276
|
+
title: translate('notification.common.viewTxHash', locale),
|
|
277
|
+
link: viewTxHashLink,
|
|
278
|
+
},
|
|
279
|
+
].filter(Boolean);
|
|
280
|
+
|
|
281
|
+
const template: BaseEmailTemplateType = {
|
|
282
|
+
title: translate('notification.subscriptionRefundSucceeded.title', locale, {
|
|
283
|
+
productName,
|
|
284
|
+
}),
|
|
285
|
+
body: translate('notification.subscriptionRefundSucceeded.body', locale, {
|
|
286
|
+
at,
|
|
287
|
+
productName,
|
|
288
|
+
refundInfo,
|
|
289
|
+
}),
|
|
290
|
+
// @ts-expect-error
|
|
291
|
+
attachments: [
|
|
372
292
|
{
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
link: viewSubscriptionLink,
|
|
376
|
-
},
|
|
377
|
-
viewTxHashLink && {
|
|
378
|
-
name: translate('notification.common.viewTxHash', locale),
|
|
379
|
-
title: translate('notification.common.viewTxHash', locale),
|
|
380
|
-
link: viewTxHashLink as string,
|
|
293
|
+
type: 'section',
|
|
294
|
+
fields: [...commonFields, ...amountFields, ...validityPeriodFields, ...refundPeriodFields].filter(Boolean),
|
|
381
295
|
},
|
|
382
296
|
].filter(Boolean),
|
|
297
|
+
// @ts-ignore
|
|
298
|
+
actions,
|
|
383
299
|
};
|
|
384
300
|
|
|
385
301
|
return template;
|
|
386
302
|
}
|
|
303
|
+
|
|
304
|
+
private getStakeReturnTemplate(context: SubscriptionRefundSucceededEmailTemplateContext): BaseEmailTemplateType {
|
|
305
|
+
const { locale, productName, at, userDid, refundInfo, viewSubscriptionLink, viewTxHashLink, isCreditSubscription } =
|
|
306
|
+
context;
|
|
307
|
+
|
|
308
|
+
// 构建字段
|
|
309
|
+
const commonFields = this.buildCommonFields(userDid, productName, locale);
|
|
310
|
+
|
|
311
|
+
// 返还金额字段
|
|
312
|
+
const returnAmountFields = isCreditSubscription
|
|
313
|
+
? []
|
|
314
|
+
: [
|
|
315
|
+
{
|
|
316
|
+
type: 'text',
|
|
317
|
+
data: {
|
|
318
|
+
type: 'plain',
|
|
319
|
+
color: '#9397A1',
|
|
320
|
+
text: translate('notification.common.returnAmount', locale),
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
type: 'text',
|
|
325
|
+
data: {
|
|
326
|
+
type: 'plain',
|
|
327
|
+
text: refundInfo,
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
// 构建操作按钮
|
|
333
|
+
const actions = [
|
|
334
|
+
{
|
|
335
|
+
name: translate('notification.common.viewSubscription', locale),
|
|
336
|
+
title: translate('notification.common.viewSubscription', locale),
|
|
337
|
+
link: viewSubscriptionLink,
|
|
338
|
+
},
|
|
339
|
+
viewTxHashLink && {
|
|
340
|
+
name: translate('notification.common.viewTxHash', locale),
|
|
341
|
+
title: translate('notification.common.viewTxHash', locale),
|
|
342
|
+
link: viewTxHashLink,
|
|
343
|
+
},
|
|
344
|
+
].filter(Boolean);
|
|
345
|
+
|
|
346
|
+
return {
|
|
347
|
+
title: translate('notification.subscriptionStakeReturnSucceeded.title', locale, {
|
|
348
|
+
productName,
|
|
349
|
+
}),
|
|
350
|
+
body: translate('notification.subscriptionStakeReturnSucceeded.body', locale, {
|
|
351
|
+
at,
|
|
352
|
+
productName,
|
|
353
|
+
refundInfo,
|
|
354
|
+
}),
|
|
355
|
+
attachments: [
|
|
356
|
+
{
|
|
357
|
+
type: 'section',
|
|
358
|
+
fields: [...commonFields, ...returnAmountFields].filter(Boolean),
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
// @ts-ignore
|
|
362
|
+
actions,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
387
365
|
}
|