payment-kit 1.13.170 → 1.13.171
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
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import Cron from '@abtnode/cron';
|
|
2
2
|
|
|
3
|
-
import { batchHandleStripeInvoices } from '../integrations/stripe/resource';
|
|
3
|
+
import { batchHandleStripeInvoices, batchHandleStripeSubscriptions } from '../integrations/stripe/resource';
|
|
4
4
|
import {
|
|
5
5
|
expiredSessionCleanupCronTime,
|
|
6
6
|
notificationCronTime,
|
|
7
7
|
stripeInvoiceCronTime,
|
|
8
|
+
stripeSubscriptionCronTime,
|
|
8
9
|
subscriptionCronTime,
|
|
9
10
|
} from '../libs/env';
|
|
10
11
|
import logger from '../libs/logger';
|
|
@@ -57,6 +58,12 @@ function init() {
|
|
|
57
58
|
fn: batchHandleStripeInvoices,
|
|
58
59
|
options: { runOnInit: false },
|
|
59
60
|
},
|
|
61
|
+
{
|
|
62
|
+
name: 'stripe.subscription.sync',
|
|
63
|
+
time: stripeSubscriptionCronTime,
|
|
64
|
+
fn: batchHandleStripeSubscriptions,
|
|
65
|
+
options: { runOnInit: false },
|
|
66
|
+
},
|
|
60
67
|
],
|
|
61
68
|
onError: (error: Error, name: string) => {
|
|
62
69
|
logger.error('run job failed', { name, error: error.message, stack: error.stack });
|
|
@@ -43,7 +43,7 @@ export async function handleSubscriptionEvent(event: TEventExpanded, _: Stripe)
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const fields = ['cancel_at', 'cancel_at_period_end', 'canceled_at'];
|
|
46
|
+
const fields = ['cancel_at', 'cancel_at_period_end', 'canceled_at', 'current_period_start', 'current_period_end'];
|
|
47
47
|
if (subscription.payment_settings?.payment_method_types?.includes('stripe')) {
|
|
48
48
|
fields.push('pause_collection');
|
|
49
49
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
/* eslint-disable no-continue */
|
|
2
|
+
/* eslint-disable no-await-in-loop */
|
|
1
3
|
import env from '@blocklet/sdk/lib/env';
|
|
2
4
|
import merge from 'lodash/merge';
|
|
5
|
+
import omit from 'lodash/omit';
|
|
6
|
+
import pick from 'lodash/pick';
|
|
3
7
|
import { Op } from 'sequelize';
|
|
4
8
|
|
|
5
9
|
import logger from '../../libs/logger';
|
|
@@ -347,15 +351,15 @@ export async function batchHandleStripeInvoices() {
|
|
|
347
351
|
},
|
|
348
352
|
});
|
|
349
353
|
|
|
350
|
-
|
|
354
|
+
for (const invoice of stripeInvoices) {
|
|
351
355
|
const stripeInvoiceId = invoice.metadata?.stripe_id;
|
|
352
356
|
if (!stripeInvoiceId) {
|
|
353
|
-
|
|
357
|
+
continue;
|
|
354
358
|
}
|
|
355
359
|
|
|
356
360
|
const method = stripeMethods.find((m) => m.livemode === invoice.livemode);
|
|
357
361
|
if (!method) {
|
|
358
|
-
|
|
362
|
+
continue;
|
|
359
363
|
}
|
|
360
364
|
|
|
361
365
|
const client = method.getStripeClient();
|
|
@@ -369,8 +373,6 @@ export async function batchHandleStripeInvoices() {
|
|
|
369
373
|
await client.invoices.pay(stripeInvoiceId);
|
|
370
374
|
logger.info('stripe invoice payment requested', { local: invoice.id, stripe: stripeInvoiceId });
|
|
371
375
|
|
|
372
|
-
await sleep(5000);
|
|
373
|
-
|
|
374
376
|
await syncStripeInvoice(invoice);
|
|
375
377
|
|
|
376
378
|
if (invoice.payment_intent_id) {
|
|
@@ -392,5 +394,66 @@ export async function batchHandleStripeInvoices() {
|
|
|
392
394
|
logger.error('stripe invoice finalize error', error);
|
|
393
395
|
}
|
|
394
396
|
}
|
|
397
|
+
|
|
398
|
+
await sleep(5000);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export async function batchHandleStripeSubscriptions() {
|
|
403
|
+
const stripeMethods = await PaymentMethod.findAll({ where: { type: 'stripe' } });
|
|
404
|
+
if (stripeMethods.length === 0) {
|
|
405
|
+
return;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const subscriptions = await Subscription.findAll({
|
|
409
|
+
where: {
|
|
410
|
+
status: { [Op.not]: ['canceled', 'incomplete', 'incomplete_expired'] },
|
|
411
|
+
'payment_details.stripe.subscription_id': { [Op.not]: null },
|
|
412
|
+
},
|
|
395
413
|
});
|
|
414
|
+
|
|
415
|
+
for (const subscription of subscriptions) {
|
|
416
|
+
const subscriptionId = subscription.payment_details?.stripe?.subscription_id;
|
|
417
|
+
if (!subscriptionId) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const method = stripeMethods.find((m) => m.livemode === subscription.livemode);
|
|
422
|
+
if (!method) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const client = method.getStripeClient();
|
|
427
|
+
try {
|
|
428
|
+
const exist = await client.subscriptions.retrieve(subscriptionId);
|
|
429
|
+
if (exist) {
|
|
430
|
+
const fields = [
|
|
431
|
+
'cancel_at',
|
|
432
|
+
'cancel_at_period_end',
|
|
433
|
+
'canceled_at',
|
|
434
|
+
'current_period_start',
|
|
435
|
+
'current_period_end',
|
|
436
|
+
];
|
|
437
|
+
if (subscription.payment_settings?.payment_method_types?.includes('stripe')) {
|
|
438
|
+
fields.push('pause_collection');
|
|
439
|
+
}
|
|
440
|
+
await subscription.update(pick(exist, fields) as any);
|
|
441
|
+
logger.warn('stripe subscription synced', { local: subscription.id, stripe: subscriptionId });
|
|
442
|
+
} else {
|
|
443
|
+
logger.warn('stripe subscription missing', { local: subscription.id, stripe: subscriptionId });
|
|
444
|
+
}
|
|
445
|
+
} catch (error) {
|
|
446
|
+
logger.error('stripe subscription sync error', { error, local: subscription.id, stripe: subscriptionId });
|
|
447
|
+
if (error.message.includes('No such subscription')) {
|
|
448
|
+
await subscription.update({
|
|
449
|
+
payment_details: {
|
|
450
|
+
...subscription.payment_details,
|
|
451
|
+
stripe: omit(subscription.payment_details?.stripe, ['subscription_id']),
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
await sleep(3000);
|
|
458
|
+
}
|
|
396
459
|
}
|
package/api/src/libs/env.ts
CHANGED
|
@@ -4,7 +4,8 @@ export const subscriptionCronTime: string = process.env.SUBSCRIPTION_CRON_TIME |
|
|
|
4
4
|
export const notificationCronTime: string = process.env.NOTIFICATION_CRON_TIME || '0 5 */6 * * *'; // 默认每6个小时执行一次
|
|
5
5
|
export const expiredSessionCleanupCronTime: string = process.env.EXPIRED_SESSION_CLEANUP_CRON_TIME || '0 1 */2 * * *'; // 默认每2个小时执行一次
|
|
6
6
|
export const notificationCronConcurrency: number = Number(process.env.NOTIFICATION_CRON_CONCURRENCY) || 8; // 默认并发数为 8
|
|
7
|
-
export const stripeInvoiceCronTime: string = process.env.
|
|
7
|
+
export const stripeInvoiceCronTime: string = process.env.STRIPE_INVOICE_CRON_TIME || '0 */30 * * * *'; // 默认每 30min 执行一次
|
|
8
|
+
export const stripeSubscriptionCronTime: string = process.env.STRIPE_SUBSCRIPTION_CRON_TIME || '0 10 */8 * * *'; // 默认每 8小时 执行一次
|
|
8
9
|
|
|
9
10
|
export default {
|
|
10
11
|
...env,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { LiteralUnion } from 'type-fest';
|
|
2
2
|
|
|
3
3
|
import { ensurePassportRevoked } from '../integrations/blocklet/passport';
|
|
4
|
+
import { batchHandleStripeSubscriptions } from '../integrations/stripe/resource';
|
|
4
5
|
import dayjs from '../libs/dayjs';
|
|
5
6
|
import { events } from '../libs/event';
|
|
6
7
|
import { getLock } from '../libs/lock';
|
|
@@ -391,6 +392,8 @@ export const startSubscriptionQueue = async () => {
|
|
|
391
392
|
}
|
|
392
393
|
await addSubscriptionJob(x, 'cycle');
|
|
393
394
|
});
|
|
395
|
+
|
|
396
|
+
await batchHandleStripeSubscriptions();
|
|
394
397
|
};
|
|
395
398
|
|
|
396
399
|
export async function addSubscriptionJob(
|
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.171",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "cross-env COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"@arcblock/jwt": "^1.18.110",
|
|
51
51
|
"@arcblock/ux": "^2.9.39",
|
|
52
52
|
"@blocklet/logger": "1.16.23",
|
|
53
|
-
"@blocklet/payment-react": "1.13.
|
|
53
|
+
"@blocklet/payment-react": "1.13.171",
|
|
54
54
|
"@blocklet/sdk": "1.16.23",
|
|
55
55
|
"@blocklet/ui-react": "^2.9.39",
|
|
56
56
|
"@blocklet/uploader": "^0.0.74",
|
|
@@ -110,7 +110,7 @@
|
|
|
110
110
|
"devDependencies": {
|
|
111
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.171",
|
|
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": "88712f035a9b1b2f6cec912520f58066fdb2b97f"
|
|
153
153
|
}
|