payment-kit 1.13.89 → 1.13.91
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/integrations/stripe/handlers/setup-intent.ts +1 -1
- package/api/src/libs/audit.ts +2 -2
- package/api/src/libs/session.ts +2 -1
- package/api/src/queues/invoice.ts +1 -1
- package/api/src/queues/notification.ts +8 -9
- package/api/src/queues/payment.ts +12 -13
- package/api/src/routes/connect/collect.ts +6 -2
- package/api/src/routes/customers.ts +5 -0
- package/api/src/store/models/subscription.ts +21 -0
- package/api/src/store/models/types.ts +0 -3
- package/blocklet.yml +1 -1
- package/package.json +3 -3
|
@@ -19,7 +19,7 @@ export async function handleSetupIntentEvent(event: TEventExpanded, _: Stripe) {
|
|
|
19
19
|
|
|
20
20
|
if (event.type === 'setup_intent.succeeded') {
|
|
21
21
|
if (subscription.status === 'incomplete') {
|
|
22
|
-
await subscription.
|
|
22
|
+
await subscription.start();
|
|
23
23
|
logger.info('subscription become active on stripe intent succeeded', subscription.id);
|
|
24
24
|
}
|
|
25
25
|
|
package/api/src/libs/audit.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { events } from './event';
|
|
|
7
7
|
|
|
8
8
|
const API_VERSION = '2023-09-05';
|
|
9
9
|
|
|
10
|
-
export async function createEvent(scope: string, type: LiteralUnion<EventType, string>, model: any, options: any) {
|
|
10
|
+
export async function createEvent(scope: string, type: LiteralUnion<EventType, string>, model: any, options: any = {}) {
|
|
11
11
|
// console.log('createEvent', scope, type, model, options);
|
|
12
12
|
const data: any = {
|
|
13
13
|
object: model.dataValues,
|
|
@@ -44,7 +44,7 @@ export async function createStatusEvent(
|
|
|
44
44
|
prefix: string,
|
|
45
45
|
config: Record<string, string>,
|
|
46
46
|
model: any,
|
|
47
|
-
options: any
|
|
47
|
+
options: any = {}
|
|
48
48
|
) {
|
|
49
49
|
// console.log('createStatusEvent', scope, prefix, config, model, options);
|
|
50
50
|
if (options.fields.includes('status') === false) {
|
package/api/src/libs/session.ts
CHANGED
|
@@ -298,6 +298,7 @@ export function getFastCheckoutAmount(
|
|
|
298
298
|
}
|
|
299
299
|
|
|
300
300
|
const { total, renew } = getCheckoutAmount(items, currencyId, includeFreeTrial);
|
|
301
|
+
|
|
301
302
|
if (mode === 'payment') {
|
|
302
303
|
return total;
|
|
303
304
|
}
|
|
@@ -307,7 +308,7 @@ export function getFastCheckoutAmount(
|
|
|
307
308
|
}
|
|
308
309
|
|
|
309
310
|
if (mode === 'subscription') {
|
|
310
|
-
return new BN(total).add(new BN(renew).mul(new BN(minimumCycle - 1))).toString();
|
|
311
|
+
return new BN(total).add(new BN(renew).mul(new BN(includeFreeTrial ? minimumCycle : minimumCycle - 1))).toString();
|
|
311
312
|
}
|
|
312
313
|
|
|
313
314
|
return '0';
|
|
@@ -58,7 +58,7 @@ export const handleInvoice = async (job: InvoiceJob) => {
|
|
|
58
58
|
if (invoice.subscription_id) {
|
|
59
59
|
const subscription = await Subscription.findByPk(invoice.subscription_id);
|
|
60
60
|
if (subscription && subscription.status === 'incomplete') {
|
|
61
|
-
await subscription.
|
|
61
|
+
await subscription.start();
|
|
62
62
|
logger.info('invoice subscription updated', subscription.id);
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -26,9 +26,8 @@ import {
|
|
|
26
26
|
SubscriptionWillRenewEmailTemplate,
|
|
27
27
|
SubscriptionWillRenewEmailTemplateOptions,
|
|
28
28
|
} from '../libs/notification/template/subscription-will-renew';
|
|
29
|
-
import type { SufficientForPaymentResult } from '../libs/payment';
|
|
30
29
|
import createQueue from '../libs/queue';
|
|
31
|
-
import
|
|
30
|
+
import { EventType, Invoice, Subscription } from '../store/models';
|
|
32
31
|
|
|
33
32
|
export type NotificationQueueJobOptions = any;
|
|
34
33
|
export type NotificationQueueJob = {
|
|
@@ -113,30 +112,30 @@ export async function startNotificationQueue() {
|
|
|
113
112
|
}
|
|
114
113
|
});
|
|
115
114
|
|
|
116
|
-
events.on('customer.subscription.renewed', (subscription: Subscription
|
|
115
|
+
events.on('customer.subscription.renewed', (subscription: Subscription) => {
|
|
117
116
|
notificationQueue.push({
|
|
118
117
|
job: {
|
|
119
118
|
type: 'customer.subscription.renewed',
|
|
120
119
|
options: {
|
|
121
120
|
subscriptionId: subscription.id,
|
|
122
|
-
invoiceId:
|
|
121
|
+
invoiceId: subscription.latest_invoice_id,
|
|
123
122
|
} as SubscriptionRenewedEmailTemplateOptions,
|
|
124
123
|
},
|
|
125
124
|
});
|
|
126
125
|
});
|
|
127
126
|
|
|
128
|
-
events.on(
|
|
129
|
-
|
|
130
|
-
(
|
|
127
|
+
events.on('customer.subscription.renew_failed', async (subscription: Subscription) => {
|
|
128
|
+
const invoice = await Invoice.findByPk(subscription.latest_invoice_id);
|
|
129
|
+
if (invoice && subscription.metadata.renew_failed_reason) {
|
|
131
130
|
notificationQueue.push({
|
|
132
131
|
job: {
|
|
133
132
|
type: 'customer.subscription.renew_failed',
|
|
134
133
|
options: {
|
|
135
134
|
invoice,
|
|
136
|
-
result,
|
|
135
|
+
result: subscription.metadata.renew_failed_reason,
|
|
137
136
|
},
|
|
138
137
|
},
|
|
139
138
|
});
|
|
140
139
|
}
|
|
141
|
-
);
|
|
140
|
+
});
|
|
142
141
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ensureStakedForGas } from '../integrations/blockchain/stake';
|
|
2
|
+
import { createEvent } from '../libs/audit';
|
|
2
3
|
import { wallet } from '../libs/auth';
|
|
3
4
|
import dayjs from '../libs/dayjs';
|
|
4
5
|
import CustomError from '../libs/error';
|
|
@@ -60,14 +61,7 @@ export const handlePaymentSucceed = async (paymentIntent: PaymentIntent, invoice
|
|
|
60
61
|
const subscription = await Subscription.findByPk(invoice.subscription_id);
|
|
61
62
|
if (subscription) {
|
|
62
63
|
if (subscription.status === 'incomplete') {
|
|
63
|
-
await subscription.
|
|
64
|
-
|
|
65
|
-
if (subscription.trail_end) {
|
|
66
|
-
events.emit('customer.subscription.trial_start', subscription);
|
|
67
|
-
} else {
|
|
68
|
-
events.emit('customer.subscription.started', subscription);
|
|
69
|
-
}
|
|
70
|
-
|
|
64
|
+
await subscription.start();
|
|
71
65
|
logger.info(`Subscription ${subscription.id} updated on payment done ${invoice.id}`);
|
|
72
66
|
} else if (subscription.status === 'past_due') {
|
|
73
67
|
if (subscription.cancel_at_period_end && subscription.cancelation_details?.reason === 'payment_failed') {
|
|
@@ -82,7 +76,7 @@ export const handlePaymentSucceed = async (paymentIntent: PaymentIntent, invoice
|
|
|
82
76
|
}
|
|
83
77
|
|
|
84
78
|
if (invoice.billing_reason === 'subscription_cycle') {
|
|
85
|
-
|
|
79
|
+
createEvent('Subscription', 'customer.subscription.renewed', subscription).catch(console.error);
|
|
86
80
|
}
|
|
87
81
|
}
|
|
88
82
|
|
|
@@ -412,10 +406,15 @@ export const handlePayment = async (job: PaymentJob) => {
|
|
|
412
406
|
|
|
413
407
|
// 只有在重试次数超过阈值的时候才发送邮件,不然邮件频率太高了,初次邮件时首次失败 6 小时后
|
|
414
408
|
if (attemptCount >= MIN_RETRY_MAIL && invoice.billing_reason === 'subscription_cycle') {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
409
|
+
const subscription = await Subscription.findByPk(invoice.subscription_id);
|
|
410
|
+
if (subscription) {
|
|
411
|
+
await subscription.update({
|
|
412
|
+
metadata: Object.assign(subscription.metadata, {
|
|
413
|
+
renew_failed_reason: result || { sufficient: false, reason: 'TX_SEND_FAILED' },
|
|
414
|
+
}),
|
|
415
|
+
});
|
|
416
|
+
createEvent('Subscription', 'customer.subscription.renew_failed', subscription);
|
|
417
|
+
}
|
|
419
418
|
}
|
|
420
419
|
|
|
421
420
|
const updates = await handlePaymentFailed(paymentIntent, invoice, error);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Transaction, TransferV3Tx } from '@ocap/client';
|
|
2
2
|
import { fromAddress } from '@ocap/wallet';
|
|
3
3
|
|
|
4
|
+
import { createEvent } from '../../libs/audit';
|
|
4
5
|
import type { CallbackArgs } from '../../libs/auth';
|
|
5
6
|
import { wallet } from '../../libs/auth';
|
|
6
|
-
import { events } from '../../libs/event';
|
|
7
7
|
import { getGasPayerExtra } from '../../libs/payment';
|
|
8
8
|
import { getTxMetadata } from '../../libs/util';
|
|
9
9
|
import { invoiceQueue } from '../../queues/invoice';
|
|
@@ -101,7 +101,11 @@ export default {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
if (invoice.subscription_id) {
|
|
104
|
-
|
|
104
|
+
createEvent(
|
|
105
|
+
'Subscription',
|
|
106
|
+
'customer.subscription.renewed',
|
|
107
|
+
await Subscription.findByPk(invoice.subscription_id)
|
|
108
|
+
);
|
|
105
109
|
}
|
|
106
110
|
|
|
107
111
|
return { hash: txHash };
|
|
@@ -24,10 +24,12 @@ const authPortal = authenticate<Customer>({
|
|
|
24
24
|
const schema = Joi.object<{
|
|
25
25
|
page: number;
|
|
26
26
|
pageSize: number;
|
|
27
|
+
did?: string;
|
|
27
28
|
livemode?: boolean;
|
|
28
29
|
}>({
|
|
29
30
|
page: Joi.number().integer().min(1).default(1),
|
|
30
31
|
pageSize: Joi.number().integer().min(1).max(100).default(20),
|
|
32
|
+
did: Joi.string().empty(''),
|
|
31
33
|
livemode: Joi.boolean().empty(''),
|
|
32
34
|
});
|
|
33
35
|
router.get('/', auth, async (req, res) => {
|
|
@@ -37,6 +39,9 @@ router.get('/', auth, async (req, res) => {
|
|
|
37
39
|
if (typeof query.livemode === 'boolean') {
|
|
38
40
|
where.livemode = query.livemode;
|
|
39
41
|
}
|
|
42
|
+
if (query.did) {
|
|
43
|
+
where.did = query.did;
|
|
44
|
+
}
|
|
40
45
|
|
|
41
46
|
try {
|
|
42
47
|
const { rows: list, count } = await Customer.findAndCountAll({
|
|
@@ -4,6 +4,7 @@ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes,
|
|
|
4
4
|
import type { LiteralUnion } from 'type-fest';
|
|
5
5
|
|
|
6
6
|
import { createCustomEvent, createEvent, createStatusEvent } from '../../libs/audit';
|
|
7
|
+
import logger from '../../libs/logger';
|
|
7
8
|
import { createIdGenerator } from '../../libs/util';
|
|
8
9
|
import { Invoice } from './invoice';
|
|
9
10
|
import type { PaymentDetails, PaymentSettings, PriceRecurring } from './types';
|
|
@@ -358,6 +359,26 @@ export class Subscription extends Model<InferAttributes<Subscription>, InferCrea
|
|
|
358
359
|
public isActive() {
|
|
359
360
|
return ['active', 'trialing'].includes(this.status);
|
|
360
361
|
}
|
|
362
|
+
|
|
363
|
+
public async start() {
|
|
364
|
+
if (this.isActive()) {
|
|
365
|
+
logger.warn(`subscription already started: ${this.id}`);
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
if (this.isImmutable()) {
|
|
370
|
+
logger.warn(`subscription is immutable: ${this.id}`);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (this.trail_end) {
|
|
375
|
+
await this.update({ status: 'trialing' });
|
|
376
|
+
createEvent('Subscription', 'customer.subscription.trial_start', this).catch(console.error);
|
|
377
|
+
} else {
|
|
378
|
+
await this.update({ status: 'active' });
|
|
379
|
+
createEvent('Subscription', 'customer.subscription.started', this).catch(console.error);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
361
382
|
}
|
|
362
383
|
|
|
363
384
|
export type TSubscription = InferAttributes<Subscription>;
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.13.
|
|
3
|
+
"version": "1.13.91",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"@abtnode/types": "1.16.21",
|
|
111
111
|
"@arcblock/eslint-config": "^0.2.4",
|
|
112
112
|
"@arcblock/eslint-config-ts": "^0.2.4",
|
|
113
|
-
"@did-pay/types": "1.13.
|
|
113
|
+
"@did-pay/types": "1.13.91",
|
|
114
114
|
"@types/cookie-parser": "^1.4.6",
|
|
115
115
|
"@types/cors": "^2.8.17",
|
|
116
116
|
"@types/dotenv-flow": "^3.3.3",
|
|
@@ -149,5 +149,5 @@
|
|
|
149
149
|
"parser": "typescript"
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
|
-
"gitHead": "
|
|
152
|
+
"gitHead": "970a705a07b5b34cb04f54c5e76797c96c500d81"
|
|
153
153
|
}
|