payment-kit 1.13.50 → 1.13.52

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.
@@ -1,10 +1,11 @@
1
+ import { isEthereumDid, isValid } from '@arcblock/did';
1
2
  // import pick from 'lodash/pick';
2
3
  import { formatFactoryState, preMintFromFactory } from '@ocap/asset';
3
4
  import merge from 'lodash/merge';
4
5
 
5
6
  import { wallet } from '../../libs/auth';
6
7
  import logger from '../../libs/logger';
7
- import { CheckoutSession, PaymentCurrency, PaymentIntent, PaymentMethod, Subscription } from '../../store/models';
8
+ import { CheckoutSession, PaymentIntent, PaymentMethod, Subscription } from '../../store/models';
8
9
  import { sendNftNotification } from '../blocklet/notification';
9
10
 
10
11
  export async function mintNftForCheckoutSession(id: string) {
@@ -34,13 +35,23 @@ export async function mintNftForCheckoutSession(id: string) {
34
35
  return;
35
36
  }
36
37
 
38
+ const { factory, inputs } = checkoutSession.nft_mint_settings as any;
39
+ if (isValid(factory) === false) {
40
+ logger.warn('checkoutSession nft mint settings invalid', { id, factory });
41
+ return;
42
+ }
43
+
44
+ // TODO: we only support minting from arcblock chain now
45
+ if (isEthereumDid(factory)) {
46
+ logger.warn('nft mint not supported for factory', { id, factory });
47
+ return;
48
+ }
49
+
37
50
  // TODO: we may need retry here when the chain is temporarily inaccessible
38
- const currency = await PaymentCurrency.findByPk(checkoutSession.currency_id);
39
- const method = await PaymentMethod.findByPk(currency?.payment_method_id);
40
- if (method?.type === 'arcblock') {
51
+ const method = await PaymentMethod.findOne({ where: { livemode: checkoutSession.livemode, type: 'arcblock' } });
52
+ if (method) {
41
53
  const client = method.getOcapClient();
42
54
  const nftOwner = checkoutSession.customer_did;
43
- const { factory, inputs } = checkoutSession.nft_mint_settings as any;
44
55
 
45
56
  const [{ state: factoryState }, { state: appState }] = await Promise.all([
46
57
  client.getFactoryState({ address: factory }),
@@ -118,9 +129,5 @@ export async function mintNftForCheckoutSession(id: string) {
118
129
  factoryState.name
119
130
  );
120
131
  logger.info('nft sent for checkoutSession', { id, nftOwner });
121
-
122
- return;
123
132
  }
124
-
125
- logger.warn('nft mint not supported for payment method', { id, type: method?.type });
126
133
  }
@@ -38,8 +38,6 @@ export async function syncStripPayment(paymentIntent: PaymentIntent) {
38
38
  const client = await method.getStripeClient();
39
39
  const stripeIntent = await client.paymentIntents.retrieve(paymentIntent.metadata.stripe_id);
40
40
  if (stripeIntent) {
41
- const justSucceed = stripeIntent.status === 'succeeded' && paymentIntent.status !== 'succeeded';
42
-
43
41
  // @ts-ignore
44
42
  await paymentIntent.update({
45
43
  amount: String(stripeIntent.amount),
@@ -50,9 +48,7 @@ export async function syncStripPayment(paymentIntent: PaymentIntent) {
50
48
  });
51
49
  logger.info('stripe payment intent synced', { locale: paymentIntent.id, remote: stripeIntent.id });
52
50
 
53
- if (justSucceed) {
54
- await handlePaymentSucceed(paymentIntent);
55
- }
51
+ await handlePaymentSucceed(paymentIntent);
56
52
  }
57
53
  }
58
54
 
@@ -107,6 +103,9 @@ export async function handleStripePaymentCreated(event: TEventExpanded, client:
107
103
  }
108
104
 
109
105
  logger.info('stripe payment intent mirrored', { locale: paymentIntent.id, remote: stripeIntent.id });
106
+ if (stripeIntent.status === 'succeeded') {
107
+ await handlePaymentSucceed(paymentIntent);
108
+ }
110
109
  }
111
110
  }
112
111
 
@@ -37,17 +37,19 @@ export const handlePaymentSucceed = async (paymentIntent: PaymentIntent) => {
37
37
  return;
38
38
  }
39
39
 
40
- await invoice.update({
41
- paid: true,
42
- status: 'paid',
43
- amount_due: '0',
44
- amount_paid: paymentIntent.amount,
45
- amount_remaining: '0',
46
- attempt_count: invoice.attempt_count + 1,
47
- attempted: true,
48
- status_transitions: { ...invoice.status_transitions, paid_at: dayjs().unix() },
49
- });
50
- logger.info(`Invoice ${invoice.id} updated on payment done: ${paymentIntent.id}`);
40
+ if (invoice.status !== 'paid') {
41
+ await invoice.update({
42
+ paid: true,
43
+ status: 'paid',
44
+ amount_due: '0',
45
+ amount_paid: paymentIntent.amount,
46
+ amount_remaining: '0',
47
+ attempt_count: invoice.attempt_count + 1,
48
+ attempted: true,
49
+ status_transitions: { ...invoice.status_transitions, paid_at: dayjs().unix() },
50
+ });
51
+ logger.info(`Invoice ${invoice.id} updated on payment done: ${paymentIntent.id}`);
52
+ }
51
53
 
52
54
  if (invoice.subscription_id) {
53
55
  const subscription = await Subscription.findByPk(invoice.subscription_id);
@@ -56,6 +58,7 @@ export const handlePaymentSucceed = async (paymentIntent: PaymentIntent) => {
56
58
  await subscription.update({ status: subscription.trail_end ? 'trialing' : 'active' });
57
59
  logger.info(`Subscription ${subscription.id} updated on payment done ${invoice.id}`);
58
60
  } else {
61
+ // FIXME: possible error here
59
62
  await subscription.update({ status: 'active' });
60
63
  logger.info(`Subscription ${subscription.id} moved to active after payment done ${paymentIntent.id}`);
61
64
  }
@@ -169,7 +169,9 @@ export function getSubscriptionCycleAmount(items: TLineItemExpanded[], currency:
169
169
  export function expandLineItems(items: any[], products: Product[], prices: Price[]) {
170
170
  items.forEach((item) => {
171
171
  item.price = prices.find((x) => x.id === item.price_id);
172
- item.price.product = products.find((x) => x.id === item.price.product_id);
172
+ if (item.price) {
173
+ item.price.product = products.find((x) => x.id === item.price.product_id);
174
+ }
173
175
  });
174
176
 
175
177
  return items;
@@ -1,13 +1,16 @@
1
1
  /* eslint-disable consistent-return */
2
+ import { isValid } from '@arcblock/did';
2
3
  import { getUrl } from '@blocklet/sdk/lib/component';
3
4
  import userMiddleware from '@blocklet/sdk/lib/middlewares/user';
4
5
  import { NextFunction, Request, Response, Router } from 'express';
6
+ import Joi from 'joi';
5
7
  import cloneDeep from 'lodash/cloneDeep';
6
8
  import merge from 'lodash/merge';
7
9
  import omit from 'lodash/omit';
8
10
  import pick from 'lodash/pick';
9
11
  import sortBy from 'lodash/sortBy';
10
12
  import uniq from 'lodash/uniq';
13
+ import type { WhereOptions } from 'sequelize';
11
14
 
12
15
  import { checkPassportForPaymentLink } from '../integrations/blocklet/passport';
13
16
  import { handleStripePaymentSucceed } from '../integrations/stripe/handlers/payment-intent';
@@ -22,6 +25,7 @@ import { isDelegationSufficientForPayment } from '../libs/payment';
22
25
  import { authenticate } from '../libs/security';
23
26
  import {
24
27
  canUpsell,
28
+ expandLineItems,
25
29
  getCheckoutAmount,
26
30
  getCheckoutMode,
27
31
  getFastCheckoutAmount,
@@ -957,4 +961,94 @@ router.delete('/:id/cross-sell', user, ensureCheckoutSessionOpen, async (req, re
957
961
  }
958
962
  });
959
963
 
964
+ const schema = Joi.object<{
965
+ page: number;
966
+ pageSize: number;
967
+ status?: string;
968
+ payment_status?: string;
969
+ nft_mint_status?: string;
970
+ customer_id?: string;
971
+ customer_did?: string;
972
+ payment_intent_id?: string;
973
+ subscription_id?: string;
974
+ livemode?: boolean;
975
+ }>({
976
+ page: Joi.number().integer().min(1).default(1),
977
+ pageSize: Joi.number().integer().min(1).max(100).default(20),
978
+ status: Joi.string().empty(''),
979
+ payment_status: Joi.string().empty(''),
980
+ nft_mint_status: Joi.string().empty(''),
981
+ customer_id: Joi.string().empty(''),
982
+ customer_did: Joi.string().empty(''),
983
+ payment_intent_id: Joi.string().empty(''),
984
+ subscription_id: Joi.string().empty(''),
985
+ livemode: Joi.boolean().empty(''),
986
+ });
987
+ router.get('/', auth, async (req, res) => {
988
+ const { page, pageSize, livemode, ...query } = await schema.validateAsync(req.query, {
989
+ stripUnknown: false,
990
+ allowUnknown: true,
991
+ });
992
+ const where: WhereOptions<CheckoutSession> = {};
993
+
994
+ ['status', 'payment_status', 'nft_mint_status'].forEach((key) => {
995
+ // @ts-ignore
996
+ if (query[key]) {
997
+ // @ts-ignore
998
+ where[key] = query[key].split(',').map((x: string) => x.trim()).filter(Boolean); // prettier-ignore
999
+ }
1000
+ });
1001
+ if (query.customer_id) {
1002
+ where.customer_id = query.customer_id;
1003
+ }
1004
+ if (query.payment_intent_id) {
1005
+ where.payment_intent_id = query.payment_intent_id;
1006
+ }
1007
+ if (query.subscription_id) {
1008
+ where.subscription_id = query.subscription_id;
1009
+ }
1010
+ if (query.customer_did && isValid(query.customer_did)) {
1011
+ const customer = await Customer.findOne({ where: { did: query.customer_did } });
1012
+ if (customer) {
1013
+ where.customer_id = customer.id;
1014
+ } else {
1015
+ res.json({ count: 0, list: [] });
1016
+ return;
1017
+ }
1018
+ }
1019
+ if (typeof livemode === 'boolean') {
1020
+ where.livemode = livemode;
1021
+ }
1022
+
1023
+ Object.keys(query)
1024
+ .filter((x) => x.startsWith('metadata.'))
1025
+ .forEach((key: string) => {
1026
+ // @ts-ignore
1027
+ where[key] = query[key];
1028
+ });
1029
+
1030
+ try {
1031
+ const { rows: list, count } = await CheckoutSession.findAndCountAll({
1032
+ where,
1033
+ order: [['created_at', 'DESC']],
1034
+ offset: (page - 1) * pageSize,
1035
+ limit: pageSize,
1036
+ include: [],
1037
+ });
1038
+
1039
+ const condition = { where: { livemode: !!req.livemode } };
1040
+ const products = (await Product.findAll(condition)).map((x) => x.toJSON());
1041
+ const prices = (await Price.findAll(condition)).map((x) => x.toJSON());
1042
+ const docs = list.map((x) => x.toJSON());
1043
+
1044
+ // @ts-ignore
1045
+ docs.forEach((x) => expandLineItems(x.line_items, products, prices));
1046
+
1047
+ res.json({ count, list: docs });
1048
+ } catch (err) {
1049
+ console.error(err);
1050
+ res.json({ count: 0, list: [] });
1051
+ }
1052
+ });
1053
+
960
1054
  export default router;
@@ -118,7 +118,8 @@ router.get('/:id', authPortal, async (req, res) => {
118
118
  let subscription;
119
119
 
120
120
  if (doc) {
121
- if (doc.status !== 'succeeded' && doc.metadata?.stripe_id) {
121
+ const shouldSync = doc.status !== 'succeeded' || req.query.sync === '1';
122
+ if (doc.metadata?.stripe_id && shouldSync) {
122
123
  await syncStripPayment(doc);
123
124
  }
124
125
 
@@ -272,7 +272,7 @@ export type PaymentDetails = {
272
272
  export type NftMintSettings = {
273
273
  enabled: boolean;
274
274
  behavior?: LiteralUnion<'per_customer' | 'per_checkout_session', string>;
275
- factory?: string;
275
+ factory?: string; // the factory address determines which chain to mint on
276
276
  inputs?: Record<string, string>;
277
277
  };
278
278
 
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.50
17
+ version: 1.13.52
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.50",
3
+ "version": "1.13.52",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev",
6
6
  "eject": "vite eject",
@@ -42,14 +42,14 @@
42
42
  "dependencies": {
43
43
  "@arcblock/did": "^1.18.95",
44
44
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
45
- "@arcblock/did-connect": "^2.8.7",
45
+ "@arcblock/did-connect": "^2.8.8",
46
46
  "@arcblock/did-util": "^1.18.95",
47
47
  "@arcblock/jwt": "^1.18.95",
48
- "@arcblock/ux": "^2.8.7",
48
+ "@arcblock/ux": "^2.8.8",
49
49
  "@blocklet/logger": "1.16.17",
50
50
  "@blocklet/sdk": "1.16.17",
51
- "@blocklet/ui-react": "^2.8.7",
52
- "@blocklet/uploader": "^0.0.33",
51
+ "@blocklet/ui-react": "^2.8.8",
52
+ "@blocklet/uploader": "^0.0.34",
53
53
  "@mui/icons-material": "^5.14.16",
54
54
  "@mui/lab": "^5.0.0-alpha.151",
55
55
  "@mui/material": "^5.14.16",
@@ -104,7 +104,7 @@
104
104
  "@abtnode/types": "1.16.17",
105
105
  "@arcblock/eslint-config": "^0.2.4",
106
106
  "@arcblock/eslint-config-ts": "^0.2.4",
107
- "@did-pay/types": "1.13.50",
107
+ "@did-pay/types": "1.13.52",
108
108
  "@types/cookie-parser": "^1.4.5",
109
109
  "@types/cors": "^2.8.15",
110
110
  "@types/dotenv-flow": "^3.3.2",
@@ -141,5 +141,5 @@
141
141
  "parser": "typescript"
142
142
  }
143
143
  },
144
- "gitHead": "7bdfa2019c557823145e8d262c887b9066c90d04"
144
+ "gitHead": "f51b870fb8b6d9d37641055d389550b16aded800"
145
145
  }