payment-kit 1.13.88 → 1.13.90

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.
@@ -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.update({ status: subscription.trail_end ? 'trialing' : 'active' });
22
+ await subscription.start();
23
23
  logger.info('subscription become active on stripe intent succeeded', subscription.id);
24
24
  }
25
25
 
@@ -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) {
@@ -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.update({ status: subscription.trail_end ? 'trialing' : 'active' });
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 type { EventType, Invoice, Subscription } from '../store/models';
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, invoice: Invoice) => {
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: invoice?.id,
121
+ invoiceId: subscription.latest_invoice_id,
123
122
  } as SubscriptionRenewedEmailTemplateOptions,
124
123
  },
125
124
  });
126
125
  });
127
126
 
128
- events.on(
129
- 'customer.subscription.renew_failed',
130
- ({ invoice, result }: { invoice: Invoice; result: SufficientForPaymentResult }) => {
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.update({ status: subscription.trail_end ? 'trialing' : 'active' });
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
- events.emit('customer.subscription.renewed', subscription, invoice);
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
- events.emit('customer.subscription.renew_failed', {
416
- invoice,
417
- result: result || { sufficient: false, reason: 'TX_SEND_FAILED' },
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
- events.emit('customer.subscription.renewed', await Subscription.findByPk(invoice.subscription_id), invoice);
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 };
@@ -458,10 +458,6 @@ router.put('/:id', auth, async (req, res) => {
458
458
  updates.proration_behavior = req.body.proration_behavior;
459
459
  }
460
460
 
461
- const lastInvoice = await Invoice.findByPk(subscription.latest_invoice_id);
462
- if (!lastInvoice) {
463
- throw new Error('Subscription should have latest invoice');
464
- }
465
461
  const paymentCurrency = await PaymentCurrency.findByPk(subscription.currency_id);
466
462
  if (!paymentCurrency) {
467
463
  throw new Error('Subscription should have payment currency');
@@ -562,6 +558,11 @@ router.put('/:id', auth, async (req, res) => {
562
558
  // handle proration
563
559
  const prorationBehavior = updates.proration_behavior || subscription.proration_behavior || 'none';
564
560
  if (prorationBehavior === 'create_prorations') {
561
+ const lastInvoice = await Invoice.findByPk(subscription.latest_invoice_id);
562
+ if (!lastInvoice) {
563
+ throw new Error('Subscription should have latest invoice');
564
+ }
565
+
565
566
  // 0. get last invoice, and invoice items, filter invoice items that are in licensed recurring mode
566
567
  const invoiceItems = await InvoiceItem.findAll({ where: { invoice_id: lastInvoice.id } });
567
568
  const invoiceItemsExpanded = await Price.expand(invoiceItems.map((x) => x.toJSON()));
@@ -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
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.13.88
17
+ version: 1.13.90
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.88",
3
+ "version": "1.13.90",
4
4
  "scripts": {
5
5
  "dev": "COMPONENT_STORE_URL=https://test.store.blocklet.dev blocklet dev",
6
6
  "eject": "vite eject",
@@ -42,27 +42,27 @@
42
42
  ]
43
43
  },
44
44
  "dependencies": {
45
- "@abtnode/cron": "1.16.21-beta-e828f413",
46
- "@arcblock/did": "^1.18.106",
45
+ "@abtnode/cron": "1.16.21",
46
+ "@arcblock/did": "^1.18.108",
47
47
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
48
- "@arcblock/did-connect": "^2.8.23",
49
- "@arcblock/did-util": "^1.18.106",
50
- "@arcblock/jwt": "^1.18.106",
51
- "@arcblock/ux": "^2.8.23",
52
- "@blocklet/logger": "1.16.21-beta-e828f413",
53
- "@blocklet/sdk": "1.16.21-beta-e828f413",
54
- "@blocklet/ui-react": "^2.8.23",
55
- "@blocklet/uploader": "^0.0.56",
48
+ "@arcblock/did-connect": "^2.9.6",
49
+ "@arcblock/did-util": "^1.18.108",
50
+ "@arcblock/jwt": "^1.18.108",
51
+ "@arcblock/ux": "^2.9.6",
52
+ "@blocklet/logger": "1.16.21",
53
+ "@blocklet/sdk": "1.16.21",
54
+ "@blocklet/ui-react": "^2.9.6",
55
+ "@blocklet/uploader": "^0.0.62",
56
56
  "@mui/icons-material": "^5.14.19",
57
57
  "@mui/lab": "^5.0.0-alpha.155",
58
58
  "@mui/material": "^5.14.20",
59
59
  "@mui/styles": "^5.14.20",
60
60
  "@mui/system": "^5.14.20",
61
- "@ocap/asset": "^1.18.106",
62
- "@ocap/client": "^1.18.106",
63
- "@ocap/mcrypto": "^1.18.106",
64
- "@ocap/util": "^1.18.106",
65
- "@ocap/wallet": "^1.18.106",
61
+ "@ocap/asset": "^1.18.108",
62
+ "@ocap/client": "^1.18.108",
63
+ "@ocap/mcrypto": "^1.18.108",
64
+ "@ocap/util": "^1.18.108",
65
+ "@ocap/wallet": "^1.18.108",
66
66
  "@stripe/react-stripe-js": "^2.4.0",
67
67
  "@stripe/stripe-js": "^2.2.0",
68
68
  "ahooks": "^3.7.8",
@@ -107,10 +107,10 @@
107
107
  "validator": "^13.11.0"
108
108
  },
109
109
  "devDependencies": {
110
- "@abtnode/types": "1.16.21-beta-e828f413",
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.88",
113
+ "@did-pay/types": "1.13.90",
114
114
  "@types/cookie-parser": "^1.4.6",
115
115
  "@types/cors": "^2.8.17",
116
116
  "@types/dotenv-flow": "^3.3.3",
@@ -134,7 +134,7 @@
134
134
  "type-fest": "^4.8.3",
135
135
  "typescript": "^4.9.5",
136
136
  "vite": "^4.5.1",
137
- "vite-plugin-blocklet": "^0.6.16",
137
+ "vite-plugin-blocklet": "^0.7.2",
138
138
  "vite-plugin-node-polyfills": "^0.7.0",
139
139
  "vite-plugin-svgr": "^2.4.0",
140
140
  "zx": "^7.2.3"
@@ -149,5 +149,5 @@
149
149
  "parser": "typescript"
150
150
  }
151
151
  },
152
- "gitHead": "1c1c92c77043c0c77f0cda969a299b31d06f70e5"
152
+ "gitHead": "da8ffc51e425abacf0bc215606c9e2a747d8cee0"
153
153
  }