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
|
@@ -256,6 +256,8 @@ export default function createQueue<T = any>({ name, onJob, options = defaults }
|
|
|
256
256
|
const jobs = await store.getScheduledJobs();
|
|
257
257
|
for (const x of jobs) {
|
|
258
258
|
if (x.job && x.id) {
|
|
259
|
+
// fix: https://github.com/blocklet/payment-kit/issues/287
|
|
260
|
+
await cancel(x.id);
|
|
259
261
|
push({ job: x.job, id: x.id, persist: false });
|
|
260
262
|
} else {
|
|
261
263
|
logger.info('skip invalid job from db', { job: x });
|
|
@@ -17,7 +17,7 @@ export default function createQueueStore(queue: string) {
|
|
|
17
17
|
},
|
|
18
18
|
getScheduledJobs(): Promise<TJob[]> {
|
|
19
19
|
return Job.findAll({
|
|
20
|
-
where: { queue, delay: { [Op.not]: -1 }, will_run_at: { [Op.lte]: Date.now() } },
|
|
20
|
+
where: { queue, delay: { [Op.not]: -1 }, will_run_at: { [Op.lte]: Date.now() }, cancelled: false },
|
|
21
21
|
order: [['created_at', 'ASC']],
|
|
22
22
|
transaction: null,
|
|
23
23
|
});
|
package/api/src/libs/security.ts
CHANGED
|
@@ -90,6 +90,6 @@ export function authenticate<T extends Model>({ component, roles, record, mine }
|
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
return res.status(
|
|
93
|
+
return res.status(403).json({ error: 'Not authorized to perform this action' });
|
|
94
94
|
};
|
|
95
95
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import querystring from 'querystring';
|
|
2
|
+
|
|
1
3
|
import component from '@blocklet/sdk/lib/component';
|
|
2
4
|
import { BN } from '@ocap/util';
|
|
5
|
+
import type { LiteralUnion } from 'type-fest';
|
|
3
6
|
|
|
4
7
|
import {
|
|
5
8
|
Customer,
|
|
@@ -14,9 +17,22 @@ import {
|
|
|
14
17
|
import dayjs from './dayjs';
|
|
15
18
|
import logger from './logger';
|
|
16
19
|
import { getPriceUintAmountByCurrency, getRecurringPeriod } from './session';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
import { getConnectQueryParam } from './util';
|
|
21
|
+
|
|
22
|
+
export function getCustomerSubscriptionPageUrl({
|
|
23
|
+
subscriptionId,
|
|
24
|
+
locale = 'en',
|
|
25
|
+
userDid,
|
|
26
|
+
}: {
|
|
27
|
+
subscriptionId: string;
|
|
28
|
+
locale: LiteralUnion<'en' | 'zh', string>;
|
|
29
|
+
userDid: string;
|
|
30
|
+
}) {
|
|
31
|
+
return component.getUrl(
|
|
32
|
+
`customer/subscription/${subscriptionId}?locale=${locale}&${querystring.stringify(
|
|
33
|
+
getConnectQueryParam({ userDid })
|
|
34
|
+
)}`
|
|
35
|
+
);
|
|
20
36
|
}
|
|
21
37
|
|
|
22
38
|
// FIXME: make this configurable from preferences
|
package/api/src/libs/util.ts
CHANGED
|
@@ -205,3 +205,36 @@ export function getExplorerLink(
|
|
|
205
205
|
return undefined;
|
|
206
206
|
}
|
|
207
207
|
}
|
|
208
|
+
|
|
209
|
+
type DIDConnectCustomQuery = {
|
|
210
|
+
// 填写用户的 DID
|
|
211
|
+
forceConnected: string;
|
|
212
|
+
sourceAppPid?: string;
|
|
213
|
+
// auto 代表引导切换(进行弹窗提示,用户可选择不切换);
|
|
214
|
+
// disabled 代表不提示(适用于支付页面,允许用户不以当前登录账户来进行支付);
|
|
215
|
+
// required 代表强制要求切换(进行页面跳转强制切换账号)
|
|
216
|
+
// switchAccount?: boolean || 'auto'; // 默认 'auto'
|
|
217
|
+
switchBehavior?: 'auto' | 'disabled' | 'required';
|
|
218
|
+
showClose?: true | false;
|
|
219
|
+
};
|
|
220
|
+
/**
|
|
221
|
+
* @see https://github.com/blocklet/payment-kit/issues/315#issuecomment-1902978895
|
|
222
|
+
* @description
|
|
223
|
+
* @export
|
|
224
|
+
* @param {{ userDid: string }} { userDid }
|
|
225
|
+
* @return {*} {string}
|
|
226
|
+
*/
|
|
227
|
+
export function getConnectQueryParam({ userDid }: { userDid: string }): {
|
|
228
|
+
'__did-connect__': string;
|
|
229
|
+
} {
|
|
230
|
+
const data: DIDConnectCustomQuery = {
|
|
231
|
+
forceConnected: userDid,
|
|
232
|
+
sourceAppPid: env.appPid,
|
|
233
|
+
switchBehavior: 'auto',
|
|
234
|
+
showClose: false,
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
'__did-connect__': Buffer.from(JSON.stringify(data), 'utf8').toString('base64'),
|
|
239
|
+
};
|
|
240
|
+
}
|
package/api/src/locales/en.ts
CHANGED
|
@@ -5,14 +5,17 @@ export default flat({
|
|
|
5
5
|
common: {
|
|
6
6
|
account: 'Account',
|
|
7
7
|
product: 'Product',
|
|
8
|
-
paymentInfo: '
|
|
9
|
-
|
|
8
|
+
paymentInfo: 'Paid',
|
|
9
|
+
refundAmount: 'Refund amount',
|
|
10
|
+
refundPeriod: 'Refund period',
|
|
11
|
+
validityPeriod: 'Service period',
|
|
10
12
|
trialPeriod: 'Trail period',
|
|
11
13
|
viewSubscription: 'View subscription',
|
|
12
14
|
viewInvoice: 'View invoice',
|
|
13
15
|
viewTxHash: 'View transaction',
|
|
14
16
|
trialDuration: 'Trial duration',
|
|
15
17
|
duration: 'Duration',
|
|
18
|
+
permanent: 'Permanent',
|
|
16
19
|
nftAddress: 'NFT Address',
|
|
17
20
|
month: 'month',
|
|
18
21
|
months: 'months',
|
|
@@ -20,6 +23,8 @@ export default flat({
|
|
|
20
23
|
days: 'days',
|
|
21
24
|
minute: 'minute',
|
|
22
25
|
minutes: 'minutes',
|
|
26
|
+
cancellationReason: 'Cancellation reason',
|
|
27
|
+
renewNow: 'Renew now',
|
|
23
28
|
},
|
|
24
29
|
|
|
25
30
|
sendTo: 'Sent to',
|
|
@@ -45,17 +50,28 @@ export default flat({
|
|
|
45
50
|
body: 'Thank you for successfully subscribing to {productName} on {at}. We will be happy to provide you with excellent service, and we wish you a pleasant experience.',
|
|
46
51
|
},
|
|
47
52
|
|
|
53
|
+
oneTimePaymentSucceeded: {
|
|
54
|
+
title: 'Congratulations! Your purchase of {productName} was successful',
|
|
55
|
+
body: 'Thank you for successfully purchasing {productName} on {at}. We will provide you with excellent service, and we wish you a pleasant experience!',
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
subscriptionUpgraded: {
|
|
59
|
+
title: 'Congratulations! Your subscription plan for {productName} has been successfully upgraded',
|
|
60
|
+
body: 'Your subscription plan for {productName} has been successfully upgraded at {at}, thank you for your support, we wish you a pleasant experience!',
|
|
61
|
+
},
|
|
62
|
+
|
|
48
63
|
subscriptionWillRenew: {
|
|
49
64
|
title: '{productName} will be auto-renewed soon',
|
|
50
65
|
body: 'Your subscription to {productName} expires in {willRenewDuration}. Please make sure you have enough balance in your account to auto-renew your subscription when it expires ({at}). We wish you all the best!',
|
|
51
66
|
unableToPayBody:
|
|
52
67
|
'Your subscription to {productName} expires in {willRenewDuration}. <span style="color: red;">Your current balance is {balance}, which is less than {price}</span>, so please make sure you have enough balance in your account to automatically renew your subscription when it expires ({at}). We wish you all the best!',
|
|
53
|
-
renewAmount: '
|
|
68
|
+
renewAmount: 'Renewal amount',
|
|
54
69
|
},
|
|
55
70
|
|
|
56
71
|
subscriptionRenewed: {
|
|
57
|
-
title: '{productName}
|
|
72
|
+
title: '{productName} renewal successful',
|
|
58
73
|
body: 'Your {productName} was successfully renewed on {at}. Thank you for your continued support and trust, have a great day!',
|
|
74
|
+
noExpenseIncurred: 'No charges were incurred during the service period',
|
|
59
75
|
},
|
|
60
76
|
|
|
61
77
|
subscriptionRenewFailed: {
|
|
@@ -80,5 +96,23 @@ export default flat({
|
|
|
80
96
|
txSendFailed: 'Failed to send transaction when try to collect payment.',
|
|
81
97
|
},
|
|
82
98
|
},
|
|
99
|
+
|
|
100
|
+
subscriptionRefundSucceeded: {
|
|
101
|
+
title: '{productName} refund successful',
|
|
102
|
+
body: 'Your subscription to {productName} has been successfully refunded on {at}, with a refund amount of {refundInfo}. If you have any questions, please feel free to contact us.',
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
subscriptWillCanceled: {
|
|
106
|
+
title: '{productName} subscription is about to be automatically canceled',
|
|
107
|
+
body: 'Due to the long period of unsuccessful automatic renewal, your subscription to {productName} will be automatically canceled by the system on {at} ({willCancelDuration} later). Please handle the renewal issue manually in time to avoid any inconvenience. If you have any questions, please feel free to contact us.',
|
|
108
|
+
renewAmount: 'Renewal Amount',
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
subscriptionCanceled: {
|
|
112
|
+
title: '{productName} subscription canceled',
|
|
113
|
+
body: 'Your subscription to {productName} has been canceled on {at}. If you have any questions, please feel free to contact us.',
|
|
114
|
+
adminCanceled: 'Admin canceled',
|
|
115
|
+
adminCanceledAndRefunded: 'Admin canceled and refunded, please check for the refund email',
|
|
116
|
+
},
|
|
83
117
|
},
|
|
84
118
|
});
|
package/api/src/locales/zh.ts
CHANGED
|
@@ -5,14 +5,17 @@ export default flat({
|
|
|
5
5
|
common: {
|
|
6
6
|
account: '账号',
|
|
7
7
|
product: '商品',
|
|
8
|
-
paymentInfo: '
|
|
9
|
-
|
|
8
|
+
paymentInfo: '已支付',
|
|
9
|
+
refundAmount: '退款金额',
|
|
10
|
+
refundPeriod: '退款周期',
|
|
11
|
+
validityPeriod: '服务周期',
|
|
10
12
|
trialPeriod: '试用期',
|
|
11
13
|
viewSubscription: '查看订阅',
|
|
12
14
|
viewInvoice: '查看发票',
|
|
13
15
|
viewTxHash: '查看交易',
|
|
14
16
|
trialDuration: '试用期时长',
|
|
15
17
|
duration: '时长',
|
|
18
|
+
permanent: '永久',
|
|
16
19
|
nftAddress: 'NFT 地址',
|
|
17
20
|
month: '个月',
|
|
18
21
|
months: '个月',
|
|
@@ -20,6 +23,8 @@ export default flat({
|
|
|
20
23
|
days: '天',
|
|
21
24
|
minute: '分钟',
|
|
22
25
|
minutes: '分钟',
|
|
26
|
+
cancellationReason: '取消原因',
|
|
27
|
+
renewNow: '立即续费',
|
|
23
28
|
},
|
|
24
29
|
|
|
25
30
|
sendTo: '发送给',
|
|
@@ -45,6 +50,16 @@ export default flat({
|
|
|
45
50
|
body: '感谢您于 {at} 成功订阅了 {productName}。我们将竭诚为您提供优质的服务,祝您使用愉快!',
|
|
46
51
|
},
|
|
47
52
|
|
|
53
|
+
oneTimePaymentSucceeded: {
|
|
54
|
+
title: '恭喜!您已购买 {productName} 成功',
|
|
55
|
+
body: '感谢您于 {at} 成功购买了 {productName}。我们将竭诚为您提供优质的服务,祝您使用愉快!',
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
subscriptionUpgraded: {
|
|
59
|
+
title: '恭喜!您订阅的 {productName} 套餐已成功变更',
|
|
60
|
+
body: '您订阅的 {productName} 套餐已于 {at} 变更成功,感谢您的支持,祝您使用愉快!',
|
|
61
|
+
},
|
|
62
|
+
|
|
48
63
|
subscriptionWillRenew: {
|
|
49
64
|
title: '{productName} 即将自动续费',
|
|
50
65
|
body: '您订阅的 {productName} 还有 {willRenewDuration} 到期。请确保您的账户余额充足,以便在到期后({at})自动续费。祝您一切顺利!',
|
|
@@ -56,6 +71,7 @@ export default flat({
|
|
|
56
71
|
subscriptionRenewed: {
|
|
57
72
|
title: '{productName} 续费成功',
|
|
58
73
|
body: '您的 {productName} 已于 {at} 成功续费。感谢您的持续支持与信任,祝您使用愉快!',
|
|
74
|
+
noExpenseIncurred: '服务期间未产生费用',
|
|
59
75
|
},
|
|
60
76
|
|
|
61
77
|
subscriptionRenewFailed: {
|
|
@@ -75,5 +91,23 @@ export default flat({
|
|
|
75
91
|
txSendFailed: '扣款交易发送失败',
|
|
76
92
|
},
|
|
77
93
|
},
|
|
94
|
+
|
|
95
|
+
subscriptionRefundSucceeded: {
|
|
96
|
+
title: '{productName} 退款成功',
|
|
97
|
+
body: '您订阅的 {productName} 在 {at} 已退款成功,退款金额为 {refundInfo}。如有任何疑问,请随时与我们联系。',
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
subscriptWillCanceled: {
|
|
101
|
+
title: '{productName} 订阅即将自动取消',
|
|
102
|
+
body: '由于长时间未能自动完成续费,您订阅的 {productName} 将于 {at} ({willCancelDuration}后) 被系统自动取消订阅。请您及时手动处理续费问题,以免影响使用。如有任何疑问,请随时与我们联系。',
|
|
103
|
+
renewAmount: '续费金额',
|
|
104
|
+
},
|
|
105
|
+
|
|
106
|
+
subscriptionCanceled: {
|
|
107
|
+
title: '{productName} 订阅已取消',
|
|
108
|
+
body: '您订阅的 {productName} 在 {at} 已取消。如有任何疑问,请随时与我们联系。',
|
|
109
|
+
adminCanceled: '管理员取消',
|
|
110
|
+
adminCanceledAndRefunded: '管理员取消并已退款,请留意后续的退款邮件',
|
|
111
|
+
},
|
|
78
112
|
},
|
|
79
113
|
});
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/indent */
|
|
1
2
|
import { events } from '../libs/event';
|
|
2
3
|
import logger from '../libs/logger';
|
|
3
4
|
import { Notification } from '../libs/notification';
|
|
4
5
|
import type { BaseEmailTemplate } from '../libs/notification/template/base';
|
|
6
|
+
import {
|
|
7
|
+
OneTimePaymentSucceededEmailTemplate,
|
|
8
|
+
OneTimePaymentSucceededEmailTemplateOptions,
|
|
9
|
+
} from '../libs/notification/template/one-time-payment-succeeded';
|
|
10
|
+
import {
|
|
11
|
+
SubscriptionCanceledEmailTemplate,
|
|
12
|
+
SubscriptionCanceledEmailTemplateOptions,
|
|
13
|
+
} from '../libs/notification/template/subscription-cacceled';
|
|
14
|
+
import {
|
|
15
|
+
SubscriptionRefundSucceededEmailTemplate,
|
|
16
|
+
SubscriptionRefundSucceededEmailTemplateOptions,
|
|
17
|
+
} from '../libs/notification/template/subscription-refund-succeeded';
|
|
5
18
|
import {
|
|
6
19
|
SubscriptionRenewFailedEmailTemplate,
|
|
7
20
|
SubscriptionRenewFailedEmailTemplateOptions,
|
|
@@ -22,16 +35,31 @@ import {
|
|
|
22
35
|
SubscriptionTrailWilEndEmailTemplate,
|
|
23
36
|
SubscriptionTrialWillEndEmailTemplateOptions,
|
|
24
37
|
} from '../libs/notification/template/subscription-trial-will-end';
|
|
38
|
+
import {
|
|
39
|
+
SubscriptionUpgradedEmailTemplate,
|
|
40
|
+
SubscriptionUpgradedEmailTemplateOptions,
|
|
41
|
+
} from '../libs/notification/template/subscription-upgraded';
|
|
42
|
+
import {
|
|
43
|
+
SubscriptionWillCanceledEmailTemplate,
|
|
44
|
+
SubscriptionWillCanceledEmailTemplateOptions,
|
|
45
|
+
} from '../libs/notification/template/subscription-will-canceled';
|
|
25
46
|
import {
|
|
26
47
|
SubscriptionWillRenewEmailTemplate,
|
|
27
48
|
SubscriptionWillRenewEmailTemplateOptions,
|
|
28
49
|
} from '../libs/notification/template/subscription-will-renew';
|
|
29
50
|
import createQueue from '../libs/queue';
|
|
30
|
-
import { EventType, Invoice, Subscription } from '../store/models';
|
|
51
|
+
import { CheckoutSession, EventType, Invoice, Refund, Subscription } from '../store/models';
|
|
31
52
|
|
|
32
53
|
export type NotificationQueueJobOptions = any;
|
|
54
|
+
|
|
55
|
+
export type NotificationQueueJobType =
|
|
56
|
+
| EventType
|
|
57
|
+
| 'customer.subscription.will_renew'
|
|
58
|
+
| 'customer.subscription.trial_will_end'
|
|
59
|
+
| 'customer.subscription.will_canceled';
|
|
60
|
+
|
|
33
61
|
export type NotificationQueueJob = {
|
|
34
|
-
type:
|
|
62
|
+
type: NotificationQueueJobType;
|
|
35
63
|
options: NotificationQueueJobOptions;
|
|
36
64
|
};
|
|
37
65
|
|
|
@@ -39,6 +67,12 @@ function getNotificationTemplate(job: NotificationQueueJob): BaseEmailTemplate {
|
|
|
39
67
|
if (job.type === 'customer.subscription.started') {
|
|
40
68
|
return new SubscriptionSucceededEmailTemplate(job.options as SubscriptionSucceededEmailTemplateOptions);
|
|
41
69
|
}
|
|
70
|
+
if (job.type === 'checkout.session.completed') {
|
|
71
|
+
return new OneTimePaymentSucceededEmailTemplate(job.options as OneTimePaymentSucceededEmailTemplateOptions);
|
|
72
|
+
}
|
|
73
|
+
if (job.type === 'customer.subscription.upgraded') {
|
|
74
|
+
return new SubscriptionUpgradedEmailTemplate(job.options as SubscriptionUpgradedEmailTemplateOptions);
|
|
75
|
+
}
|
|
42
76
|
if (job.type === 'customer.subscription.renewed') {
|
|
43
77
|
return new SubscriptionRenewedEmailTemplate(job.options as SubscriptionRenewedEmailTemplateOptions);
|
|
44
78
|
}
|
|
@@ -54,6 +88,15 @@ function getNotificationTemplate(job: NotificationQueueJob): BaseEmailTemplate {
|
|
|
54
88
|
if (job.type === 'customer.subscription.trial_will_end') {
|
|
55
89
|
return new SubscriptionTrailWilEndEmailTemplate(job.options as SubscriptionTrialWillEndEmailTemplateOptions);
|
|
56
90
|
}
|
|
91
|
+
if (job.type === 'customer.subscription.will_canceled') {
|
|
92
|
+
return new SubscriptionWillCanceledEmailTemplate(job.options as SubscriptionWillCanceledEmailTemplateOptions);
|
|
93
|
+
}
|
|
94
|
+
if (job.type === 'customer.subscription.canceled') {
|
|
95
|
+
return new SubscriptionCanceledEmailTemplate(job.options as SubscriptionCanceledEmailTemplateOptions);
|
|
96
|
+
}
|
|
97
|
+
if (job.type === 'refund.succeeded') {
|
|
98
|
+
return new SubscriptionRefundSucceededEmailTemplate(job.options as SubscriptionRefundSucceededEmailTemplateOptions);
|
|
99
|
+
}
|
|
57
100
|
|
|
58
101
|
throw new Error(`Unknown job type: ${job.type}`);
|
|
59
102
|
}
|
|
@@ -87,6 +130,7 @@ export const notificationQueue = createQueue<NotificationQueueJob>({
|
|
|
87
130
|
*/
|
|
88
131
|
// eslint-disable-next-line require-await
|
|
89
132
|
export async function startNotificationQueue() {
|
|
133
|
+
// 试用期开始
|
|
90
134
|
events.on('customer.subscription.trial_start', (subscription: Subscription) => {
|
|
91
135
|
notificationQueue.push({
|
|
92
136
|
job: {
|
|
@@ -112,6 +156,18 @@ export async function startNotificationQueue() {
|
|
|
112
156
|
}
|
|
113
157
|
});
|
|
114
158
|
|
|
159
|
+
// 一次性购买成功
|
|
160
|
+
events.on('checkout.session.completed', (checkoutSession: CheckoutSession) => {
|
|
161
|
+
notificationQueue.push({
|
|
162
|
+
job: {
|
|
163
|
+
type: 'checkout.session.completed',
|
|
164
|
+
options: {
|
|
165
|
+
checkoutSessionId: checkoutSession.id,
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
115
171
|
events.on('customer.subscription.renewed', (subscription: Subscription) => {
|
|
116
172
|
notificationQueue.push({
|
|
117
173
|
job: {
|
|
@@ -138,4 +194,37 @@ export async function startNotificationQueue() {
|
|
|
138
194
|
});
|
|
139
195
|
}
|
|
140
196
|
});
|
|
197
|
+
|
|
198
|
+
events.on('customer.subscription.upgraded', (subscription: Subscription) => {
|
|
199
|
+
notificationQueue.push({
|
|
200
|
+
job: {
|
|
201
|
+
type: 'customer.subscription.upgraded',
|
|
202
|
+
options: {
|
|
203
|
+
subscriptionId: subscription.id,
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
events.on('refund.succeeded', (refund: Refund) => {
|
|
210
|
+
notificationQueue.push({
|
|
211
|
+
job: {
|
|
212
|
+
type: 'refund.succeeded',
|
|
213
|
+
options: {
|
|
214
|
+
refundId: refund.id,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
events.on('customer.subscription.canceled', (subscription: Subscription) => {
|
|
221
|
+
notificationQueue.push({
|
|
222
|
+
job: {
|
|
223
|
+
type: 'customer.subscription.canceled',
|
|
224
|
+
options: {
|
|
225
|
+
subscriptionId: subscription.id,
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
});
|
|
229
|
+
});
|
|
141
230
|
}
|
|
@@ -16,8 +16,8 @@ export default {
|
|
|
16
16
|
action: 'setup',
|
|
17
17
|
authPrincipal: false,
|
|
18
18
|
claims: {
|
|
19
|
-
authPrincipal: async ({ extraParams }: CallbackArgs) => {
|
|
20
|
-
const { paymentMethod } = await ensureSetupIntent(extraParams.checkoutSessionId);
|
|
19
|
+
authPrincipal: async ({ extraParams, userDid }: CallbackArgs) => {
|
|
20
|
+
const { paymentMethod } = await ensureSetupIntent(extraParams.checkoutSessionId, userDid);
|
|
21
21
|
return getAuthPrincipalClaim(paymentMethod, 'subscribe');
|
|
22
22
|
},
|
|
23
23
|
},
|
|
@@ -133,7 +133,7 @@ export async function ensurePaymentIntent(checkoutSessionId: string, userDid?: s
|
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
export async function ensureSetupIntent(checkoutSessionId: string, userDid
|
|
136
|
+
export async function ensureSetupIntent(checkoutSessionId: string, userDid: string) {
|
|
137
137
|
const checkoutSession = await ensureCheckoutSession(checkoutSessionId);
|
|
138
138
|
|
|
139
139
|
let customerId;
|
|
@@ -357,7 +357,7 @@ export async function ensureInvoiceAndItems({
|
|
|
357
357
|
applyCredit = true,
|
|
358
358
|
}: {
|
|
359
359
|
customer: Customer;
|
|
360
|
-
|
|
360
|
+
currency: PaymentCurrency;
|
|
361
361
|
subscription?: Subscription;
|
|
362
362
|
props: TInvoice;
|
|
363
363
|
lineItems: TLineItemExpanded[];
|
|
@@ -314,6 +314,11 @@ export class Subscription extends Model<InferAttributes<Subscription>, InferCrea
|
|
|
314
314
|
}
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
// 发射 canceled 事件
|
|
318
|
+
if (model.status === 'canceled' && model.previous('status') !== model.status) {
|
|
319
|
+
createEvent('Subscription', 'customer.subscription.canceled', model, options).catch(console.error);
|
|
320
|
+
}
|
|
321
|
+
|
|
317
322
|
createStatusEvent(
|
|
318
323
|
'Subscription',
|
|
319
324
|
'customer.subscription',
|
|
@@ -422,6 +422,7 @@ export type EventType = LiteralUnion<
|
|
|
422
422
|
| 'customer.subscription.trial_end'
|
|
423
423
|
| 'customer.subscription.started'
|
|
424
424
|
| 'customer.subscription.updated'
|
|
425
|
+
| 'customer.subscription.canceled'
|
|
425
426
|
| 'customer.tax_id.created'
|
|
426
427
|
| 'customer.tax_id.deleted'
|
|
427
428
|
| 'customer.tax_id.updated'
|
|
@@ -514,6 +515,8 @@ export type EventType = LiteralUnion<
|
|
|
514
515
|
| 'recipient.updated'
|
|
515
516
|
| 'refund.created'
|
|
516
517
|
| 'refund.updated'
|
|
518
|
+
| 'refund.succeeded'
|
|
519
|
+
| 'refund.canceled'
|
|
517
520
|
| 'reporting.report_run.failed'
|
|
518
521
|
| 'reporting.report_run.succeeded'
|
|
519
522
|
| 'reporting.report_type.updated'
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.138",
|
|
4
4
|
"scripts": {
|
|
5
|
-
"dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev",
|
|
5
|
+
"dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
7
7
|
"lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
|
|
8
8
|
"lint:fix": "npm run lint -- --fix",
|
|
@@ -42,31 +42,31 @@
|
|
|
42
42
|
]
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@abtnode/cron": "1.16.23
|
|
46
|
-
"@arcblock/did": "^1.18.
|
|
45
|
+
"@abtnode/cron": "1.16.23",
|
|
46
|
+
"@arcblock/did": "^1.18.110",
|
|
47
47
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
48
|
-
"@arcblock/did-connect": "^2.9.
|
|
49
|
-
"@arcblock/did-util": "^1.18.
|
|
50
|
-
"@arcblock/jwt": "^1.18.
|
|
51
|
-
"@arcblock/ux": "^2.9.
|
|
52
|
-
"@blocklet/logger": "1.16.23
|
|
53
|
-
"@blocklet/payment-react": "1.13.
|
|
54
|
-
"@blocklet/sdk": "1.16.23
|
|
55
|
-
"@blocklet/ui-react": "^2.9.
|
|
56
|
-
"@blocklet/uploader": "^0.0.
|
|
57
|
-
"@mui/icons-material": "^5.15.
|
|
58
|
-
"@mui/lab": "^5.0.0-alpha.
|
|
59
|
-
"@mui/material": "^5.15.
|
|
60
|
-
"@mui/styles": "^5.15.
|
|
61
|
-
"@mui/system": "^5.15.
|
|
62
|
-
"@ocap/asset": "^1.18.
|
|
63
|
-
"@ocap/client": "^1.18.
|
|
64
|
-
"@ocap/mcrypto": "^1.18.
|
|
65
|
-
"@ocap/util": "^1.18.
|
|
66
|
-
"@ocap/wallet": "^1.18.
|
|
48
|
+
"@arcblock/did-connect": "^2.9.29",
|
|
49
|
+
"@arcblock/did-util": "^1.18.110",
|
|
50
|
+
"@arcblock/jwt": "^1.18.110",
|
|
51
|
+
"@arcblock/ux": "^2.9.29",
|
|
52
|
+
"@blocklet/logger": "1.16.23",
|
|
53
|
+
"@blocklet/payment-react": "1.13.138",
|
|
54
|
+
"@blocklet/sdk": "1.16.23",
|
|
55
|
+
"@blocklet/ui-react": "^2.9.29",
|
|
56
|
+
"@blocklet/uploader": "^0.0.73",
|
|
57
|
+
"@mui/icons-material": "^5.15.8",
|
|
58
|
+
"@mui/lab": "^5.0.0-alpha.164",
|
|
59
|
+
"@mui/material": "^5.15.7",
|
|
60
|
+
"@mui/styles": "^5.15.8",
|
|
61
|
+
"@mui/system": "^5.15.8",
|
|
62
|
+
"@ocap/asset": "^1.18.110",
|
|
63
|
+
"@ocap/client": "^1.18.110",
|
|
64
|
+
"@ocap/mcrypto": "^1.18.110",
|
|
65
|
+
"@ocap/util": "^1.18.110",
|
|
66
|
+
"@ocap/wallet": "^1.18.110",
|
|
67
67
|
"@stripe/react-stripe-js": "^2.4.0",
|
|
68
|
-
"@stripe/stripe-js": "^2.
|
|
69
|
-
"ahooks": "^3.7.
|
|
68
|
+
"@stripe/stripe-js": "^2.4.0",
|
|
69
|
+
"ahooks": "^3.7.10",
|
|
70
70
|
"axios": "^0.27.2",
|
|
71
71
|
"body-parser": "^1.20.2",
|
|
72
72
|
"cls-hooked": "^4.2.2",
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"express": "^4.18.2",
|
|
79
79
|
"express-async-errors": "^3.1.1",
|
|
80
80
|
"express-history-api-fallback": "^2.2.1",
|
|
81
|
-
"fastq": "^1.
|
|
81
|
+
"fastq": "^1.17.1",
|
|
82
82
|
"flat": "^5.0.2",
|
|
83
|
-
"google-libphonenumber": "^3.2.
|
|
83
|
+
"google-libphonenumber": "^3.2.34",
|
|
84
84
|
"iframe-resizer-react": "^1.1.0",
|
|
85
|
-
"joi": "^17.
|
|
86
|
-
"json-stable-stringify": "^1.1.
|
|
85
|
+
"joi": "^17.12.1",
|
|
86
|
+
"json-stable-stringify": "^1.1.1",
|
|
87
87
|
"lodash": "^4.17.21",
|
|
88
88
|
"morgan": "^1.10.0",
|
|
89
89
|
"nanoid": "3",
|
|
@@ -92,32 +92,32 @@
|
|
|
92
92
|
"pretty-ms-i18n": "^1.0.3",
|
|
93
93
|
"react": "^18.2.0",
|
|
94
94
|
"react-dom": "^18.2.0",
|
|
95
|
-
"react-error-boundary": "^4.0.
|
|
96
|
-
"react-hook-form": "^7.
|
|
95
|
+
"react-error-boundary": "^4.0.12",
|
|
96
|
+
"react-hook-form": "^7.50.1",
|
|
97
97
|
"react-international-phone": "^3.1.2",
|
|
98
|
-
"react-router-dom": "^6.
|
|
98
|
+
"react-router-dom": "^6.22.0",
|
|
99
99
|
"rimraf": "^3.0.2",
|
|
100
|
-
"sequelize": "^6.
|
|
100
|
+
"sequelize": "^6.36.0",
|
|
101
101
|
"sql-where-parser": "^2.2.1",
|
|
102
102
|
"sqlite3": "^5.1.7",
|
|
103
103
|
"stripe": "^13.11.0",
|
|
104
104
|
"typewriter-effect": "^2.21.0",
|
|
105
|
-
"ufo": "^1.
|
|
106
|
-
"umzug": "^3.
|
|
105
|
+
"ufo": "^1.4.0",
|
|
106
|
+
"umzug": "^3.6.1",
|
|
107
107
|
"use-bus": "^2.5.2",
|
|
108
108
|
"validator": "^13.11.0"
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
|
-
"@abtnode/types": "1.16.23
|
|
111
|
+
"@abtnode/types": "1.16.23",
|
|
112
112
|
"@arcblock/eslint-config-ts": "^0.2.4",
|
|
113
|
-
"@blocklet/payment-types": "1.13.
|
|
113
|
+
"@blocklet/payment-types": "1.13.138",
|
|
114
114
|
"@types/cookie-parser": "^1.4.6",
|
|
115
115
|
"@types/cors": "^2.8.17",
|
|
116
116
|
"@types/dotenv-flow": "^3.3.3",
|
|
117
117
|
"@types/express": "^4.17.21",
|
|
118
|
-
"@types/node": "^18.19.
|
|
119
|
-
"@types/react": "^18.2.
|
|
120
|
-
"@types/react-dom": "^18.2.
|
|
118
|
+
"@types/node": "^18.19.14",
|
|
119
|
+
"@types/react": "^18.2.55",
|
|
120
|
+
"@types/react-dom": "^18.2.18",
|
|
121
121
|
"@vitejs/plugin-react": "^2.2.0",
|
|
122
122
|
"bumpp": "^8.2.1",
|
|
123
123
|
"cross-env": "^7.0.3",
|
|
@@ -130,10 +130,10 @@
|
|
|
130
130
|
"prettier": "^2.8.8",
|
|
131
131
|
"prettier-plugin-import-sort": "^0.0.7",
|
|
132
132
|
"ts-jest": "^29.1.2",
|
|
133
|
-
"ts-node": "^10.9.
|
|
134
|
-
"type-fest": "^4.
|
|
133
|
+
"ts-node": "^10.9.2",
|
|
134
|
+
"type-fest": "^4.10.2",
|
|
135
135
|
"typescript": "^4.9.5",
|
|
136
|
-
"vite": "^4.5.
|
|
136
|
+
"vite": "^4.5.2",
|
|
137
137
|
"vite-plugin-blocklet": "^0.7.2",
|
|
138
138
|
"vite-plugin-node-polyfills": "^0.7.0",
|
|
139
139
|
"vite-plugin-svgr": "^2.4.0",
|
|
@@ -149,5 +149,5 @@
|
|
|
149
149
|
"parser": "typescript"
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
|
-
"gitHead": "
|
|
152
|
+
"gitHead": "2865a4ed4a06029cd97a4f0914c1c3c61b5182cb"
|
|
153
153
|
}
|
package/src/contexts/session.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { useContext } from 'react';
|
|
|
3
3
|
|
|
4
4
|
const { SessionProvider, SessionContext, SessionConsumer, withSession } = createAuthServiceSessionContext();
|
|
5
5
|
|
|
6
|
-
export function useSessionContext()
|
|
7
|
-
return useContext(SessionContext);
|
|
6
|
+
export function useSessionContext() {
|
|
7
|
+
return useContext<import('@arcblock/did-connect/lib/types').SessionContext>(SessionContext);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export { SessionProvider, SessionContext, SessionConsumer, withSession };
|
|
@@ -158,7 +158,7 @@ export default function CreatePaymentLink() {
|
|
|
158
158
|
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
|
|
159
159
|
{t('common.preview')}
|
|
160
160
|
</Typography>
|
|
161
|
-
{stashed && <PaymentLinkPreview id={`plink_${session
|
|
161
|
+
{stashed && <PaymentLinkPreview id={`plink_${session?.user?.did}`} version={stashed} />}
|
|
162
162
|
</Stack>
|
|
163
163
|
</Stack>
|
|
164
164
|
</FormProvider>
|
|
@@ -132,7 +132,7 @@ export default function CreatePricingTable() {
|
|
|
132
132
|
<Typography variant="h6" sx={{ mb: 2, fontWeight: 600 }}>
|
|
133
133
|
{t('common.preview')}
|
|
134
134
|
</Typography>
|
|
135
|
-
{stashed && <PricingTablePreview id={`prctbl_${session
|
|
135
|
+
{stashed && <PricingTablePreview id={`prctbl_${session?.user?.did}`} version={stashed} />}
|
|
136
136
|
</Box>
|
|
137
137
|
</Stack>
|
|
138
138
|
</ProductsProvider>
|