payment-kit 1.21.17 → 1.22.1

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.
Files changed (51) hide show
  1. package/api/src/index.ts +1 -1
  2. package/api/src/integrations/blocklet/user.ts +2 -2
  3. package/api/src/integrations/ethereum/token.ts +4 -5
  4. package/api/src/integrations/stripe/handlers/invoice.ts +1 -1
  5. package/api/src/integrations/stripe/handlers/payment-intent.ts +1 -1
  6. package/api/src/integrations/stripe/resource.ts +1 -1
  7. package/api/src/integrations/stripe/setup.ts +1 -1
  8. package/api/src/libs/auth.ts +7 -6
  9. package/api/src/libs/env.ts +1 -1
  10. package/api/src/libs/notification/template/subscription-trial-will-end.ts +1 -0
  11. package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
  12. package/api/src/libs/payment.ts +2 -3
  13. package/api/src/libs/refund.ts +1 -1
  14. package/api/src/libs/remote-signer.ts +93 -0
  15. package/api/src/libs/security.ts +1 -1
  16. package/api/src/libs/subscription.ts +4 -7
  17. package/api/src/libs/util.ts +1 -1
  18. package/api/src/libs/vendor-util/adapters/didnames-adapter.ts +17 -9
  19. package/api/src/libs/vendor-util/adapters/launcher-adapter.ts +11 -6
  20. package/api/src/queues/payment.ts +2 -2
  21. package/api/src/queues/payout.ts +1 -1
  22. package/api/src/queues/refund.ts +2 -2
  23. package/api/src/queues/subscription.ts +1 -1
  24. package/api/src/queues/usage-record.ts +1 -1
  25. package/api/src/queues/vendors/status-check.ts +1 -1
  26. package/api/src/queues/webhook.ts +1 -1
  27. package/api/src/routes/auto-recharge-configs.ts +1 -1
  28. package/api/src/routes/checkout-sessions.ts +4 -6
  29. package/api/src/routes/connect/collect-batch.ts +1 -1
  30. package/api/src/routes/connect/collect.ts +1 -1
  31. package/api/src/routes/connect/pay.ts +1 -1
  32. package/api/src/routes/connect/recharge-account.ts +1 -1
  33. package/api/src/routes/connect/recharge.ts +1 -1
  34. package/api/src/routes/connect/shared.ts +32 -23
  35. package/api/src/routes/customers.ts +1 -1
  36. package/api/src/routes/integrations/stripe.ts +1 -1
  37. package/api/src/routes/meter-events.ts +9 -12
  38. package/api/src/routes/payment-currencies.ts +1 -1
  39. package/api/src/routes/payment-intents.ts +2 -2
  40. package/api/src/routes/payouts.ts +1 -1
  41. package/api/src/routes/products.ts +1 -0
  42. package/api/src/store/models/types.ts +1 -1
  43. package/api/tests/setup.ts +11 -0
  44. package/api/third.d.ts +0 -2
  45. package/blocklet.yml +1 -1
  46. package/jest.config.js +2 -2
  47. package/package.json +26 -26
  48. package/src/components/invoice/table.tsx +2 -2
  49. package/src/components/subscription/payment-method-info.tsx +1 -1
  50. package/src/libs/util.ts +1 -1
  51. package/src/pages/customer/subscription/detail.tsx +1 -1
package/api/src/index.ts CHANGED
@@ -2,7 +2,7 @@ import 'express-async-errors';
2
2
 
3
3
  import path from 'path';
4
4
 
5
- import fallback from '@blocklet/sdk/lib/middlewares/fallback';
5
+ import { fallback } from '@blocklet/sdk/lib/middlewares/fallback';
6
6
  import cookieParser from 'cookie-parser';
7
7
  import cors from 'cors';
8
8
  import dotenv from 'dotenv-flow';
@@ -32,6 +32,6 @@ const handleUserUpdate = async ({ user }: { user: any }) => {
32
32
  }
33
33
  };
34
34
 
35
- export function initUserHandler() {
36
- notification.on('user.updated', handleUserUpdate);
35
+ export async function initUserHandler() {
36
+ await notification.on('user.updated', handleUserUpdate);
37
37
  }
@@ -1,6 +1,7 @@
1
1
  import { JsonRpcProvider, TransactionReceipt, ethers } from 'ethers';
2
2
 
3
3
  import { ethWallet } from '../../libs/auth';
4
+ import { RemoteSigner } from '../../libs/remote-signer';
4
5
  import { getApproveFunction } from './contract';
5
6
  import erc20Abi from './erc20-abi.json';
6
7
 
@@ -92,12 +93,11 @@ export async function transferErc20FromUser(
92
93
  user: string,
93
94
  amount: string
94
95
  ): Promise<TransactionReceipt> {
95
- const wallet = new ethers.Wallet(ethWallet.secretKey);
96
- const signer = wallet.connect(provider);
96
+ const signer = new RemoteSigner(ethWallet, provider);
97
97
  const contract = new ethers.Contract(contractAddress, erc20Abi, signer);
98
98
 
99
99
  // @ts-ignore
100
- const res = await contract.transferFrom(user, wallet.address, amount);
100
+ const res = await contract.transferFrom(user, ethWallet.address, amount);
101
101
 
102
102
  // Wait for the transaction to be mined
103
103
  const receipt = await res.wait();
@@ -111,8 +111,7 @@ export async function sendErc20ToUser(
111
111
  user: string,
112
112
  amount: string
113
113
  ): Promise<TransactionReceipt> {
114
- const wallet = new ethers.Wallet(ethWallet.secretKey);
115
- const signer = wallet.connect(provider);
114
+ const signer = new RemoteSigner(ethWallet, provider);
116
115
  const contract = new ethers.Contract(contractAddress, erc20Abi, signer);
117
116
 
118
117
  // @ts-ignore
@@ -1,4 +1,4 @@
1
- import env from '@blocklet/sdk/lib/env';
1
+ import { env } from '@blocklet/sdk/lib/env';
2
2
  import merge from 'lodash/merge';
3
3
  import pick from 'lodash/pick';
4
4
  import pWaitFor from 'p-wait-for';
@@ -1,4 +1,4 @@
1
- import env from '@blocklet/sdk/lib/env';
1
+ import { env } from '@blocklet/sdk/lib/env';
2
2
  import merge from 'lodash/merge';
3
3
  import pick from 'lodash/pick';
4
4
  import pWaitFor from 'p-wait-for';
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-continue */
2
2
  /* eslint-disable no-await-in-loop */
3
- import env from '@blocklet/sdk/lib/env';
3
+ import { env } from '@blocklet/sdk/lib/env';
4
4
  import merge from 'lodash/merge';
5
5
  import omit from 'lodash/omit';
6
6
  import pick from 'lodash/pick';
@@ -1,6 +1,6 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
 
3
- import env from '@blocklet/sdk/lib/env';
3
+ import { env } from '@blocklet/sdk/lib/env';
4
4
 
5
5
  import Stripe from 'stripe';
6
6
  import logger from '../../libs/logger';
@@ -1,10 +1,11 @@
1
1
  import path from 'path';
2
2
 
3
3
  import AuthStorage from '@arcblock/did-connect-storage-nedb';
4
- import AuthService from '@blocklet/sdk/service/auth';
5
- import getWallet from '@blocklet/sdk/lib/wallet';
6
- import WalletAuthenticator from '@blocklet/sdk/lib/wallet-authenticator';
7
- import WalletHandler from '@blocklet/sdk/lib/wallet-handler';
4
+ // @ts-ignore
5
+ import { BlockletService } from '@blocklet/sdk/service/auth';
6
+ import { getWallet } from '@blocklet/sdk/lib/wallet';
7
+ import { WalletAuthenticator } from '@blocklet/sdk/lib/wallet-authenticator';
8
+ import { WalletHandlers } from '@blocklet/sdk/lib/wallet-handler';
8
9
  import type { Request } from 'express';
9
10
  import type { LiteralUnion } from 'type-fest';
10
11
  import type { WalletObject } from '@ocap/wallet';
@@ -15,7 +16,7 @@ import logger from './logger';
15
16
  export const wallet: WalletObject = getWallet();
16
17
  export const ethWallet: WalletObject = getWallet('ethereum');
17
18
  export const authenticator = new WalletAuthenticator();
18
- export const handlers = new WalletHandler({
19
+ export const handlers = new WalletHandlers({
19
20
  authenticator,
20
21
  tokenStorage: new AuthStorage({
21
22
  dbPath: path.join(env.dataDir, 'auth.db'),
@@ -24,7 +25,7 @@ export const handlers = new WalletHandler({
24
25
  }),
25
26
  });
26
27
 
27
- export const blocklet = new AuthService();
28
+ export const blocklet = new BlockletService();
28
29
 
29
30
  export async function getVaultAddress() {
30
31
  try {
@@ -1,4 +1,4 @@
1
- import env from '@blocklet/sdk/lib/env';
1
+ import { env } from '@blocklet/sdk/lib/env';
2
2
 
3
3
  export const paymentStatCronTime: string = '0 1 0 * * *'; // 默认每天一次,计算前一天的
4
4
  export const subscriptionCronTime: string = process.env.SUBSCRIPTION_CRON_TIME || '0 */30 * * * *'; // 默认每 30 min 行一次
@@ -92,6 +92,7 @@ export class SubscriptionTrialWillEndEmailTemplate extends BaseSubscriptionEmail
92
92
  const paymentAddress = subscription.payment_details?.[paymentMethod.type]?.payer ?? undefined;
93
93
  const balance = await getTokenByAddress(paymentAddress, paymentMethod, paymentCurrency);
94
94
 
95
+ // @ts-ignore
95
96
  paymentDetail.balance = +fromUnitToToken(balance, paymentCurrency.decimal);
96
97
  }
97
98
 
@@ -100,7 +100,7 @@ export class SubscriptionWillRenewEmailTemplate extends BaseSubscriptionEmailTem
100
100
  const paymentAddress = getSubscriptionPaymentAddress(subscription, paymentInfoResult.paymentMethod!.type);
101
101
  const balance = await getTokenByAddress(paymentAddress, paymentInfoResult.paymentMethod!, paymentCurrency);
102
102
 
103
- paymentDetail.balanceFormatted = fromUnitToToken(balance, paymentCurrency.decimal);
103
+ paymentDetail.balanceFormatted = fromUnitToToken(balance || '0', paymentCurrency.decimal);
104
104
  paymentDetail.balance = +paymentDetail.balanceFormatted;
105
105
  }
106
106
 
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable @typescript-eslint/indent */
2
2
  import { isEthereumDid } from '@arcblock/did';
3
3
  import { toDelegateAddress } from '@arcblock/did-util';
4
- import { sign } from '@arcblock/jwt';
5
4
  import { getWalletDid } from '@blocklet/sdk/lib/did';
6
5
  import type { DelegateState, TokenLimit } from '@ocap/client';
7
6
  import { toTxHash } from '@ocap/mcrypto';
@@ -384,7 +383,7 @@ export function isCreditSufficientForPayment(args: {
384
383
  return { sufficient: true, balance };
385
384
  }
386
385
 
387
- export function getGasPayerExtra(txBuffer: Buffer, headers?: { [key: string]: string }) {
386
+ export async function getGasPayerExtra(txBuffer: Buffer, headers?: { [key: string]: string }) {
388
387
  if (headers && headers['x-gas-payer-sig'] && headers['x-gas-payer-pk']) {
389
388
  return { headers };
390
389
  }
@@ -392,7 +391,7 @@ export function getGasPayerExtra(txBuffer: Buffer, headers?: { [key: string]: st
392
391
  const txHash = toTxHash(txBuffer);
393
392
  return {
394
393
  headers: {
395
- 'x-gas-payer-sig': sign(wallet.address, wallet.secretKey, { txHash }),
394
+ 'x-gas-payer-sig': await wallet.signJWT({ txHash }),
396
395
  'x-gas-payer-pk': wallet.publicKey,
397
396
  },
398
397
  };
@@ -50,7 +50,7 @@ export async function getRefundAmountSetup({
50
50
  paymentIntentId,
51
51
  });
52
52
  return {
53
- amount: paymentIntent.amount_received,
53
+ amount: paymentIntent.amount_received || '0',
54
54
  totalAmount: paymentIntent.amount_received,
55
55
  };
56
56
  }
@@ -0,0 +1,93 @@
1
+ import { ethers, JsonRpcProvider, TransactionRequest } from 'ethers';
2
+ import type { WalletObject } from '@ocap/wallet';
3
+
4
+ /**
5
+ * Remote Signer that uses remote signing service for Ethereum transactions
6
+ * Implements required methods from AbstractSigner
7
+ */
8
+ export class RemoteSigner extends ethers.AbstractSigner {
9
+ private wallet: WalletObject;
10
+
11
+ constructor(wallet: WalletObject, provider?: JsonRpcProvider) {
12
+ super(provider);
13
+ this.wallet = wallet;
14
+ }
15
+
16
+ /**
17
+ * Returns the address of the signer
18
+ * Required by AbstractSigner
19
+ */
20
+ getAddress(): Promise<string> {
21
+ return Promise.resolve(this.wallet.address);
22
+ }
23
+
24
+ /**
25
+ * Signs a transaction and returns the signed serialized transaction
26
+ * Required by AbstractSigner
27
+ */
28
+ async signTransaction(transaction: TransactionRequest): Promise<string> {
29
+ // Populate transaction fields (nonce, gasLimit, chainId, etc.)
30
+ const populatedTx = await this.populateTransaction(transaction);
31
+
32
+ // Remove 'from' field as it's not allowed in unsigned transactions
33
+ // The 'from' address is derived from the signature
34
+ const { from, ...txWithoutFrom } = populatedTx;
35
+
36
+ // Serialize unsigned transaction
37
+ const unsignedTx = ethers.Transaction.from(txWithoutFrom);
38
+ const serialized = unsignedTx.unsignedSerialized;
39
+
40
+ // Hash and sign via remote service using signETH (Ethereum-specific signing)
41
+ const hash = ethers.keccak256(serialized);
42
+ const signature = await this.wallet.signETH(hash, false); // hashBeforeSign=false because we already hashed
43
+
44
+ // Attach signature and return signed transaction
45
+ unsignedTx.signature = ethers.Signature.from(signature);
46
+ return unsignedTx.serialized;
47
+ }
48
+
49
+ /**
50
+ * Signs a message and returns the signature
51
+ * Required for message signing operations
52
+ */
53
+ async signMessage(message: string | Uint8Array): Promise<string> {
54
+ // Convert message to bytes if it's a string
55
+ const messageBytes = typeof message === 'string' ? ethers.toUtf8Bytes(message) : message;
56
+
57
+ // Calculate the Ethereum signed message hash
58
+ const messageHash = ethers.hashMessage(messageBytes);
59
+
60
+ // Sign via remote service using signETH (Ethereum-specific signing)
61
+ const signature = await this.wallet.signETH(messageHash, false); // hashBeforeSign=false because hashMessage already hashed
62
+
63
+ // Return signature as hex string
64
+ return ethers.hexlify(signature);
65
+ }
66
+
67
+ /**
68
+ * Signs typed data (EIP-712)
69
+ * Optional but useful for signing structured data
70
+ */
71
+ async signTypedData(
72
+ domain: ethers.TypedDataDomain,
73
+ types: Record<string, ethers.TypedDataField[]>,
74
+ value: Record<string, any>
75
+ ): Promise<string> {
76
+ // Calculate the EIP-712 hash
77
+ const hash = ethers.TypedDataEncoder.hash(domain, types, value);
78
+
79
+ // Sign via remote service using signETH (Ethereum-specific signing)
80
+ const signature = await this.wallet.signETH(hash, false); // hashBeforeSign=false because hash is already computed
81
+
82
+ // Return signature as hex string
83
+ return ethers.hexlify(signature);
84
+ }
85
+
86
+ /**
87
+ * Connects the signer to a new provider
88
+ * Required by AbstractSigner
89
+ */
90
+ connect(provider: JsonRpcProvider): RemoteSigner {
91
+ return new RemoteSigner(this.wallet, provider);
92
+ }
93
+ }
@@ -1,6 +1,6 @@
1
1
  import { auth } from '@blocklet/sdk/lib/middlewares';
2
2
  import { getVerifyData, verify } from '@blocklet/sdk/lib/util/verify-sign';
3
- import getWallet from '@blocklet/sdk/lib/wallet';
3
+ import { getWallet } from '@blocklet/sdk/lib/wallet';
4
4
  import type { NextFunction, Request, Response } from 'express';
5
5
  import type { Model } from 'sequelize';
6
6
 
@@ -1043,22 +1043,19 @@ export async function getSubscriptionStakeAmountSetup(
1043
1043
  logger.info('getSubscriptionStakeAmountSetup failed, no inputs', { txHash, info });
1044
1044
  return null;
1045
1045
  }
1046
- const amountRes: { [key: string]: BN } = {};
1046
+ const amountRes: { [key: string]: string } = {};
1047
1047
  // calculate stake amount for each address
1048
1048
  inputs.forEach((input: any) => {
1049
1049
  const { tokens } = input;
1050
1050
  tokens.forEach((token: any) => {
1051
1051
  const { address, value } = token;
1052
1052
  if (amountRes[address]) {
1053
- amountRes[address] = amountRes[address].add(new BN(value));
1053
+ amountRes[address] = new BN(amountRes[address]).add(new BN(value)).toString();
1054
1054
  } else {
1055
- amountRes[address] = new BN(value);
1055
+ amountRes[address] = new BN(value).toString();
1056
1056
  }
1057
1057
  });
1058
1058
  });
1059
- Object.keys(amountRes).forEach((address) => {
1060
- amountRes[address] = amountRes[address].toString();
1061
- });
1062
1059
  logger.info('get subscription stake amount setup success', { txHash, amountRes });
1063
1060
  return amountRes;
1064
1061
  }
@@ -1489,7 +1486,7 @@ export async function slashOverdraftProtectionStake(subscription: Subscription,
1489
1486
  // @ts-ignore
1490
1487
  const { buffer } = await client.encodeSlashStakeTx({ tx: signed });
1491
1488
  // @ts-ignore
1492
- const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, getGasPayerExtra(buffer));
1489
+ const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, await getGasPayerExtra(buffer));
1493
1490
 
1494
1491
  await paymentIntent.update({
1495
1492
  status: 'succeeded',
@@ -2,7 +2,7 @@ import crypto from 'crypto';
2
2
  import { Readable } from 'stream';
3
3
  import { buffer } from 'node:stream/consumers';
4
4
  import { getUrl } from '@blocklet/sdk/lib/component';
5
- import env from '@blocklet/sdk/lib/env';
5
+ import { env } from '@blocklet/sdk/lib/env';
6
6
  import { getWalletDid } from '@blocklet/sdk/lib/did';
7
7
  import { toStakeAddress } from '@arcblock/did-util';
8
8
  import { customAlphabet } from 'nanoid';
@@ -94,7 +94,8 @@ export class DidnamesAdapter implements VendorAdapter {
94
94
  });
95
95
 
96
96
  // Generate bindDomainCap for this domain
97
- const bindDomainCap = this.generateBindCap({
97
+ // eslint-disable-next-line no-await-in-loop
98
+ const bindDomainCap = await this.generateBindCap({
98
99
  domain,
99
100
  checkoutSessionId: orderData.checkoutSessionId,
100
101
  });
@@ -115,7 +116,8 @@ export class DidnamesAdapter implements VendorAdapter {
115
116
  };
116
117
 
117
118
  try {
118
- const { headers, body } = VendorAuth.signRequestWithHeaders(updatedOrderData);
119
+ // eslint-disable-next-line no-await-in-loop
120
+ const { headers, body } = await VendorAuth.signRequestWithHeaders(updatedOrderData);
119
121
 
120
122
  // eslint-disable-next-line no-await-in-loop
121
123
  const response = await fetch(url, {
@@ -198,7 +200,13 @@ export class DidnamesAdapter implements VendorAdapter {
198
200
  /**
199
201
  * Generate bindDomainCap (binding capability) for domain authorization
200
202
  */
201
- private generateBindCap({ domain, checkoutSessionId }: { domain: string; checkoutSessionId: string }): any {
203
+ private async generateBindCap({
204
+ domain,
205
+ checkoutSessionId,
206
+ }: {
207
+ domain: string;
208
+ checkoutSessionId: string;
209
+ }): Promise<any> {
202
210
  const now = Math.floor(Date.now() / 1000);
203
211
  const expireInMinutes = 30;
204
212
 
@@ -210,7 +218,7 @@ export class DidnamesAdapter implements VendorAdapter {
210
218
  nonce: uuidV4(),
211
219
  };
212
220
 
213
- const signature = toBase58(wallet.sign(stableStringify(cap) || ''));
221
+ const signature = toBase58(await wallet.sign(stableStringify(cap) || ''));
214
222
 
215
223
  return {
216
224
  cap,
@@ -345,7 +353,7 @@ export class DidnamesAdapter implements VendorAdapter {
345
353
  customParams: params.customParams,
346
354
  };
347
355
 
348
- const { headers, body } = VendorAuth.signRequestWithHeaders(returnRequest);
356
+ const { headers, body } = await VendorAuth.signRequestWithHeaders(returnRequest);
349
357
  const url = formatVendorUrl(vendorConfig, '/api/vendor/return');
350
358
  logger.info('submitting domain return to DID Names', {
351
359
  url,
@@ -449,7 +457,7 @@ export class DidnamesAdapter implements VendorAdapter {
449
457
  async getOrder(vendor: ProductVendor, orderId: string): Promise<any> {
450
458
  const url = formatVendorUrl(vendor, `/api/vendor/orders/${orderId}`);
451
459
 
452
- const { headers } = VendorAuth.signRequestWithHeaders({});
460
+ const { headers } = await VendorAuth.signRequestWithHeaders({});
453
461
  const response = await fetch(url, { method: 'GET', headers });
454
462
 
455
463
  if (!response.ok) {
@@ -474,7 +482,7 @@ export class DidnamesAdapter implements VendorAdapter {
474
482
  orderId,
475
483
  });
476
484
 
477
- const { headers } = VendorAuth.signRequestWithHeaders({});
485
+ const { headers } = await VendorAuth.signRequestWithHeaders({});
478
486
  const response = await fetch(url, { method: 'GET', headers });
479
487
 
480
488
  logger.info('didnames order status response', {
@@ -499,9 +507,9 @@ export class DidnamesAdapter implements VendorAdapter {
499
507
  return data;
500
508
  }
501
509
 
502
- connectTest(): Promise<any> {
510
+ async connectTest(): Promise<any> {
503
511
  const url = formatVendorUrl(this.vendorConfig!, '/api/vendor/health');
504
- const { headers } = VendorAuth.signRequestWithHeaders({});
512
+ const { headers } = await VendorAuth.signRequestWithHeaders({});
505
513
  return fetch(url, { headers }).then((res) => res.json());
506
514
  }
507
515
  }
@@ -17,8 +17,13 @@ import {
17
17
  } from './types';
18
18
  import { formatVendorUrl } from './util';
19
19
 
20
- const doRequestVendorData = (vendor: ProductVendor, orderId: string, url: string, options: { shortUrl: boolean }) => {
21
- const { headers } = VendorAuth.signRequestWithHeaders({});
20
+ const doRequestVendorData = async (
21
+ vendor: ProductVendor,
22
+ orderId: string,
23
+ url: string,
24
+ options: { shortUrl: boolean }
25
+ ) => {
26
+ const { headers } = await VendorAuth.signRequestWithHeaders({});
22
27
  const name = vendor?.name;
23
28
  const key = vendor?.vendor_key;
24
29
  const { shortUrl } = options;
@@ -124,7 +129,7 @@ export class LauncherAdapter implements VendorAdapter {
124
129
  };
125
130
  }
126
131
 
127
- const { headers, body } = VendorAuth.signRequestWithHeaders(orderData);
132
+ const { headers, body } = await VendorAuth.signRequestWithHeaders(orderData);
128
133
  const url = formatVendorUrl(vendorConfig, '/api/vendor/deliveries');
129
134
  const response = await fetch(url, {
130
135
  method: 'POST',
@@ -207,7 +212,7 @@ export class LauncherAdapter implements VendorAdapter {
207
212
  });
208
213
 
209
214
  const vendorConfig = await this.getVendorConfig();
210
- const { headers, body } = VendorAuth.signRequestWithHeaders(params);
215
+ const { headers, body } = await VendorAuth.signRequestWithHeaders(params);
211
216
 
212
217
  const response = await fetch(formatVendorUrl(vendorConfig, '/api/vendor/return'), {
213
218
  method: 'POST',
@@ -268,9 +273,9 @@ export class LauncherAdapter implements VendorAdapter {
268
273
  return doRequestVendorData(vendor, orderId, url, options);
269
274
  }
270
275
 
271
- connectTest(): Promise<any> {
276
+ async connectTest(): Promise<any> {
272
277
  const url = formatVendorUrl(this.vendorConfig!, '/api/vendor/health');
273
- const { headers } = VendorAuth.signRequestWithHeaders({});
278
+ const { headers } = await VendorAuth.signRequestWithHeaders({});
274
279
  return fetch(url, { headers }).then((res) => res.json());
275
280
  }
276
281
  }
@@ -754,7 +754,7 @@ const handleStakeSlash = async (
754
754
  // @ts-ignore
755
755
  const { buffer } = await client.encodeSlashStakeTx({ tx: signed });
756
756
  // @ts-ignore
757
- const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, getGasPayerExtra(buffer));
757
+ const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, await getGasPayerExtra(buffer));
758
758
  logger.info('Stake slashing done', {
759
759
  subscription: subscription.id,
760
760
  amount: slashAmount,
@@ -973,7 +973,7 @@ export const handlePayment = async (job: PaymentJob) => {
973
973
  const txHash = await client.sendTransferV2Tx(
974
974
  // @ts-ignore
975
975
  { tx: signed, wallet, delegator: result.delegator },
976
- getGasPayerExtra(buffer)
976
+ await getGasPayerExtra(buffer)
977
977
  );
978
978
  logger.info('PaymentIntent txHash', { txHash });
979
979
  logger.info('PaymentIntent capture done', { id: paymentIntent.id, txHash });
@@ -82,7 +82,7 @@ async function processArcblockPayout(payout: Payout, paymentMethod: PaymentMetho
82
82
  // @ts-ignore
83
83
  const { buffer } = await client.encodeTransferV2Tx({ tx: signed });
84
84
  // @ts-ignore
85
- const txHash = await client.sendTransferV2Tx({ tx: signed, wallet }, getGasPayerExtra(buffer));
85
+ const txHash = await client.sendTransferV2Tx({ tx: signed, wallet }, await getGasPayerExtra(buffer));
86
86
 
87
87
  logger.info('Payout completed', { id: payout.id, txHash });
88
88
 
@@ -167,7 +167,7 @@ const handleRefundJob = async (
167
167
  // @ts-ignore
168
168
  const { buffer } = await client.encodeTransferV2Tx({ tx: signed });
169
169
  // @ts-ignore
170
- const txHash = await client.sendTransferV2Tx({ tx: signed, wallet }, getGasPayerExtra(buffer));
170
+ const txHash = await client.sendTransferV2Tx({ tx: signed, wallet }, await getGasPayerExtra(buffer));
171
171
 
172
172
  logger.info('refund transfer done', { id: refund.id, txHash });
173
173
  await refund.update({
@@ -366,7 +366,7 @@ const handleStakeReturnJob = async (
366
366
  // @ts-ignore
367
367
  const { buffer } = await client.encodeReturnStakeTx({ tx: signed });
368
368
  // @ts-ignore
369
- const txHash = await client.sendReturnStakeTx({ tx: signed, wallet }, getGasPayerExtra(buffer));
369
+ const txHash = await client.sendReturnStakeTx({ tx: signed, wallet }, await getGasPayerExtra(buffer));
370
370
  logger.info('stake return done', { id: refund.id, txHash });
371
371
  await refund.update({
372
372
  status: 'succeeded',
@@ -743,7 +743,7 @@ export const handleStakeSlashAfterCancel = async (subscription: Subscription, fo
743
743
  // @ts-ignore
744
744
  const { buffer } = await client.encodeSlashStakeTx({ tx: signed });
745
745
  // @ts-ignore
746
- const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, getGasPayerExtra(buffer));
746
+ const txHash = await client.sendSlashStakeTx({ tx: signed, wallet }, await getGasPayerExtra(buffer));
747
747
  logger.info('Stake slashing done', {
748
748
  subscription: subscription.id,
749
749
  amount: invoice.amount_remaining,
@@ -108,7 +108,7 @@ export const doHandleUsageRecord = async (job: UsageRecordJob) => {
108
108
  rawQuantity,
109
109
  quantity,
110
110
 
111
- unitAmount: fromUnitToToken(unitAmount, currency.decimal),
111
+ unitAmount: fromUnitToToken(unitAmount || '0', currency.decimal),
112
112
  totalAmount: fromUnitToToken(totalAmount.toString(), currency.decimal),
113
113
  threshold: fromUnitToToken(threshold.toString(), currency.decimal),
114
114
  });
@@ -103,7 +103,7 @@ export const handleVendorStatusCheck = async (job: VendorStatusCheckJob) => {
103
103
  '/api/vendor/status',
104
104
  vendor.order_id
105
105
  );
106
- const { headers } = VendorAuth.signRequestWithHeaders({});
106
+ const { headers } = await VendorAuth.signRequestWithHeaders({});
107
107
 
108
108
  const result = await fetch(serverStatusUrl, { headers });
109
109
  const data = await result.json();
@@ -63,7 +63,7 @@ export const handleWebhook = async (job: WebhookJob) => {
63
63
  headers: {
64
64
  'x-app-id': wallet.address,
65
65
  'x-app-pk': wallet.publicKey,
66
- 'x-component-sig': sign(json),
66
+ 'x-component-sig': await sign(json),
67
67
  'x-component-did': process.env.BLOCKLET_COMPONENT_DID as string,
68
68
  },
69
69
  });
@@ -3,7 +3,7 @@ import Joi from 'joi';
3
3
 
4
4
  import { CustomError } from '@blocklet/error';
5
5
  import { Op } from 'sequelize';
6
- import sessionMiddleware from '@blocklet/sdk/lib/middlewares/session';
6
+ import { sessionMiddleware } from '@blocklet/sdk/lib/middlewares/session';
7
7
  import { BN, fromTokenToUnit, fromUnitToToken } from '@ocap/util';
8
8
  import { trimDecimals } from '../libs/math-utils';
9
9
  import {
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable consistent-return */
2
2
  import { isValid } from '@arcblock/did';
3
3
  import { getUrl } from '@blocklet/sdk/lib/component';
4
- import sessionMiddleware from '@blocklet/sdk/lib/middlewares/session';
4
+ import { sessionMiddleware } from '@blocklet/sdk/lib/middlewares/session';
5
5
  import { BN, fromUnitToToken } from '@ocap/util';
6
6
  import { NextFunction, Request, Response, Router } from 'express';
7
7
  import Joi from 'joi';
@@ -390,7 +390,7 @@ export async function calculateAndUpdateAmount(
390
390
  amount,
391
391
  });
392
392
 
393
- if (checkoutSession.mode === 'payment' && amount.total < 0) {
393
+ if (checkoutSession.mode === 'payment' && new BN(amount.total || '0').lt(new BN('0'))) {
394
394
  throw new Error('Payment amount should be greater or equal to 0');
395
395
  }
396
396
 
@@ -2149,10 +2149,6 @@ router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async
2149
2149
  return res.status(400).json({ error: 'Payment method not found' });
2150
2150
  }
2151
2151
 
2152
- if (paymentMethod.type !== 'arcblock') {
2153
- return res.status(400).json({ error: 'Payment method not supported for fast checkout' });
2154
- }
2155
-
2156
2152
  // Validate checkout session ownership if it was created for a specific customer
2157
2153
  if (checkoutSession.customer_id && checkoutSession.metadata?.createdBy) {
2158
2154
  const createdByDid = checkoutSession.metadata.createdBy;
@@ -2355,6 +2351,8 @@ router.post('/:id/fast-checkout-confirm', user, ensureCheckoutSessionOpen, async
2355
2351
  };
2356
2352
  canFastPay = true;
2357
2353
  }
2354
+ } else if (paymentMethod.type !== 'arcblock') {
2355
+ return res.status(400).json({ error: 'Payment method not supported for fast checkout' });
2358
2356
  }
2359
2357
 
2360
2358
  logger.info('Checkout session submitted successfully', {
@@ -136,7 +136,7 @@ export default {
136
136
  const txHash = await client.sendTransferV3Tx(
137
137
  // @ts-ignore
138
138
  { tx, wallet: fromAddress(userDid) },
139
- getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
139
+ await getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
140
140
  );
141
141
  await afterTxExecution({ tx_hash: txHash, payer: userDid, type: 'transfer' });
142
142
 
@@ -155,7 +155,7 @@ export default {
155
155
  const txHash = await client.sendTransferV3Tx(
156
156
  // @ts-ignore
157
157
  { tx, wallet: fromAddress(userDid) },
158
- getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
158
+ await getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
159
159
  );
160
160
 
161
161
  await afterTxExecution({
@@ -115,7 +115,7 @@ export default {
115
115
  const txHash = await client.sendTransferV3Tx(
116
116
  // @ts-ignore
117
117
  { tx, wallet: fromAddress(userDid) },
118
- getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
118
+ await getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
119
119
  );
120
120
 
121
121
  await paymentIntent.update({
@@ -149,7 +149,7 @@ export default {
149
149
  const txHash = await client.sendTransferV3Tx(
150
150
  // @ts-ignore
151
151
  { tx, wallet: fromAddress(userDid) },
152
- getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
152
+ await getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
153
153
  );
154
154
 
155
155
  await afterTxExecution({
@@ -128,7 +128,7 @@ export default {
128
128
  const txHash = await client.sendTransferV3Tx(
129
129
  // @ts-ignore
130
130
  { tx, wallet: fromAddress(userDid) },
131
- getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
131
+ await getGasPayerExtra(buffer, client.pickGasPayerHeaders(request))
132
132
  );
133
133
 
134
134
  logger.info('Recharge successful', {
@@ -270,11 +270,12 @@ export async function ensureSetupIntent(checkoutSessionId: string, skipInvoice?:
270
270
  invoice = await Invoice.findByPk(checkoutSession.invoice_id);
271
271
  } else {
272
272
  // Get discount information for this checkout session
273
- let discountInfo: { appliedDiscounts: string[]; discountBreakdown: Array<{ amount: string; discount: string }> } = {
274
- appliedDiscounts: [],
275
- discountBreakdown: []
276
- };
277
-
273
+ let discountInfo: { appliedDiscounts: string[]; discountBreakdown: Array<{ amount: string; discount: string }> } =
274
+ {
275
+ appliedDiscounts: [],
276
+ discountBreakdown: [],
277
+ };
278
+
278
279
  try {
279
280
  discountInfo = await getDiscountRecordsForCheckout({
280
281
  checkoutSessionId: checkoutSession.id,
@@ -462,13 +463,13 @@ export async function ensureInvoiceForCheckout({
462
463
  const trialEnd = Number(checkoutSession.subscription_data?.trial_end || 0);
463
464
  const now = dayjs().unix();
464
465
  const invoiceItems = lineItems || (await Price.expand(checkoutSession.line_items, { product: true }));
465
-
466
+
466
467
  // Get discount records for this checkout session
467
- let discountInfo: { appliedDiscounts: string[]; discountBreakdown: Array<{ amount: string; discount: string }> } = {
468
- appliedDiscounts: [],
469
- discountBreakdown: []
468
+ let discountInfo: { appliedDiscounts: string[]; discountBreakdown: Array<{ amount: string; discount: string }> } = {
469
+ appliedDiscounts: [],
470
+ discountBreakdown: [],
470
471
  };
471
-
472
+
472
473
  // Prepare discount configuration for getCheckoutAmount
473
474
  let discountConfig;
474
475
  try {
@@ -476,7 +477,7 @@ export async function ensureInvoiceForCheckout({
476
477
  checkoutSessionId: checkoutSession.id,
477
478
  customerId: customer.id,
478
479
  });
479
-
480
+
480
481
  // Apply discount if we have discount records
481
482
  if (discountInfo.appliedDiscounts.length > 0 && checkoutSession.discounts?.length) {
482
483
  const firstDiscount = checkoutSession.discounts[0];
@@ -495,8 +496,13 @@ export async function ensureInvoiceForCheckout({
495
496
  error: error.message,
496
497
  });
497
498
  }
498
-
499
- const totalAmount = await getCheckoutAmount(invoiceItems, checkoutSession.currency_id, trialInDays > 0 || trialEnd > now, discountConfig);
499
+
500
+ const totalAmount = await getCheckoutAmount(
501
+ invoiceItems,
502
+ checkoutSession.currency_id,
503
+ trialInDays > 0 || trialEnd > now,
504
+ discountConfig
505
+ );
500
506
  const { invoice, items } = await ensureInvoiceAndItems({
501
507
  customer,
502
508
  currency: currency as PaymentCurrency,
@@ -530,16 +536,16 @@ export async function ensureInvoiceForCheckout({
530
536
  custom_fields: checkoutSession.invoice_creation?.invoice_data?.custom_fields || [],
531
537
  footer: checkoutSession.invoice_creation?.invoice_data?.footer || '',
532
538
  metadata,
533
-
539
+
534
540
  discounts: discountInfo.appliedDiscounts,
535
541
  total_discount_amounts: discountInfo.discountBreakdown,
536
-
542
+
537
543
  ...(props || {}),
538
544
  } as unknown as Invoice,
539
545
  });
540
-
541
- logger.info('Invoice created for checkoutSession', {
542
- checkoutSessionId: checkoutSession.id,
546
+
547
+ logger.info('Invoice created for checkoutSession', {
548
+ checkoutSessionId: checkoutSession.id,
543
549
  invoiceId: invoice.id,
544
550
  hasDiscounts: discountInfo.appliedDiscounts.length > 0,
545
551
  discountCount: discountInfo.appliedDiscounts.length,
@@ -696,7 +702,7 @@ export async function ensureAccountRecharge(customerId: string, currencyId: stri
696
702
  }
697
703
 
698
704
  const receiverAddress = rechargeAddress || customer.did;
699
-
705
+
700
706
  return {
701
707
  paymentCurrency: paymentCurrency as PaymentCurrency,
702
708
  paymentMethod: paymentMethod as PaymentMethod,
@@ -1084,7 +1090,8 @@ export async function getTokenRequirements({
1084
1090
  const tokenRequirements: { address: string; value: string }[] = [];
1085
1091
  let amount = await getFastCheckoutAmount({ items, mode, currencyId: paymentCurrency.id, trialing: !!trialing });
1086
1092
 
1087
- const addStakeRequired = requiredStake && ((paymentMethod.type === 'arcblock' && mode !== 'delegation') || mode === 'setup');
1093
+ const addStakeRequired =
1094
+ requiredStake && ((paymentMethod.type === 'arcblock' && mode !== 'delegation') || mode === 'setup');
1088
1095
 
1089
1096
  if (!addStakeRequired && amount === '0') {
1090
1097
  return tokenRequirements;
@@ -1123,7 +1130,10 @@ export async function getTokenRequirements({
1123
1130
  if (exist) {
1124
1131
  exist.value = new BN(exist.value).add(staking.licensed).add(staking.metered).toString();
1125
1132
  } else {
1126
- tokenRequirements.push({ address: paymentCurrency.contract as string, value: staking.licensed.add(staking.metered).toString() });
1133
+ tokenRequirements.push({
1134
+ address: paymentCurrency.contract as string,
1135
+ value: staking.licensed.add(staking.metered).toString(),
1136
+ });
1127
1137
  }
1128
1138
  }
1129
1139
 
@@ -1408,7 +1418,7 @@ async function executeSingleTransaction(
1408
1418
  const { buffer } = await client[`encode${type}Tx`]({ tx });
1409
1419
  return client[`send${type}Tx`](
1410
1420
  { tx, wallet: fromPublicKey(userPk, toTypeInfo(userDid)) },
1411
- getGasPayerExtra(buffer, gasPayerHeaders)
1421
+ await getGasPayerExtra(buffer, gasPayerHeaders)
1412
1422
  );
1413
1423
  }
1414
1424
 
@@ -1531,7 +1541,6 @@ export async function updateStripeSubscriptionAfterChangePayment(setupIntent: Se
1531
1541
  }
1532
1542
  }
1533
1543
 
1534
-
1535
1544
  export async function ensureAutoRechargeAuthorization(
1536
1545
  autoRechargeConfigId: string
1537
1546
  ): Promise<{ autoRechargeConfig: AutoRechargeConfig; paymentMethod: PaymentMethod; paymentCurrency: PaymentCurrency }> {
@@ -1,4 +1,4 @@
1
- import sessionMiddleware from '@blocklet/sdk/lib/middlewares/session';
1
+ import { sessionMiddleware } from '@blocklet/sdk/lib/middlewares/session';
2
2
  import { Router } from 'express';
3
3
  import Joi from 'joi';
4
4
  import pick from 'lodash/pick';
@@ -1,4 +1,4 @@
1
- import env from '@blocklet/sdk/lib/env';
1
+ import { env } from '@blocklet/sdk/lib/env';
2
2
  import express, { NextFunction, Request, Response, Router } from 'express';
3
3
  import get from 'lodash/get';
4
4
 
@@ -278,7 +278,7 @@ router.post('/', auth, async (req, res) => {
278
278
  value: fromTokenToUnit(value, paymentCurrency.decimal).toString(),
279
279
  },
280
280
  identifier: req.body.identifier,
281
- livemode: !!req.livemode,
281
+ livemode: meter.livemode,
282
282
  processed: false,
283
283
  status: 'pending' as MeterEventStatus,
284
284
  attempt_count: 0,
@@ -312,12 +312,15 @@ router.post('/', auth, async (req, res) => {
312
312
 
313
313
  router.get('/pending-amount', authMine, async (req, res) => {
314
314
  try {
315
- const where: any = {
316
- status: 'requires_action',
315
+ const params: any = {
316
+ status: ['requires_action', 'requires_capture'],
317
317
  livemode: !!req.livemode,
318
318
  };
319
319
  if (req.query.subscription_id) {
320
- where['payload.subscription_id'] = req.query.subscription_id;
320
+ params.subscriptionId = req.query.subscription_id;
321
+ }
322
+ if (req.query.currency_id) {
323
+ params.currencyId = req.query.currency_id;
321
324
  }
322
325
  if (req.query.customer_id) {
323
326
  if (typeof req.query.customer_id !== 'string') {
@@ -327,15 +330,9 @@ router.get('/pending-amount', authMine, async (req, res) => {
327
330
  if (!customer) {
328
331
  return res.status(404).json({ error: 'Customer not found' });
329
332
  }
330
- where['payload.customer_id'] = customer.id;
333
+ params.customerId = customer.id;
331
334
  }
332
- const [summary] = await MeterEvent.getPendingAmounts({
333
- subscriptionId: req.query.subscription_id as string,
334
- livemode: !!req.livemode,
335
- currencyId: req.query.currency_id as string,
336
- status: ['requires_action', 'requires_capture'],
337
- customerId: req.query.customer_id as string,
338
- });
335
+ const [summary] = await MeterEvent.getPendingAmounts(params);
339
336
  return res.json(summary);
340
337
  } catch (err) {
341
338
  logger.error('Error getting meter event pending amount', err);
@@ -5,7 +5,7 @@ import { InferAttributes, Op, WhereOptions } from 'sequelize';
5
5
  import Joi from 'joi';
6
6
  import pick from 'lodash/pick';
7
7
  import { getUrl } from '@blocklet/sdk';
8
- import sessionMiddleware from '@blocklet/sdk/lib/middlewares/session';
8
+ import { sessionMiddleware } from '@blocklet/sdk/lib/middlewares/session';
9
9
  import { fetchErc20Meta } from '../integrations/ethereum/token';
10
10
  import logger from '../libs/logger';
11
11
  import { authenticate } from '../libs/security';
@@ -362,10 +362,10 @@ router.get('/:id/refundable-amount', authPortal, async (req, res) => {
362
362
  if (payouts.length > 0) {
363
363
  let totalPayoutAmount = new BN('0');
364
364
  payouts.forEach((payout) => {
365
- totalPayoutAmount = totalPayoutAmount.add(new BN(payout.amount));
365
+ totalPayoutAmount = totalPayoutAmount.add(new BN(payout.amount || '0'));
366
366
  });
367
367
 
368
- result.amount = result.amount.sub(totalPayoutAmount);
368
+ result.amount = new BN(result.amount || '0').sub(totalPayoutAmount).toString();
369
369
  }
370
370
  res.json(result);
371
371
  } else {
@@ -3,7 +3,7 @@ import { Router } from 'express';
3
3
  import Joi from 'joi';
4
4
  import pick from 'lodash/pick';
5
5
 
6
- import sessionMiddleware from '@blocklet/sdk/lib/middlewares/session';
6
+ import { sessionMiddleware } from '@blocklet/sdk/lib/middlewares/session';
7
7
  import type { WhereOptions } from 'sequelize';
8
8
  import { createListParamSchema, getOrder, getWhereFromKvQuery, MetadataSchema } from '../libs/api';
9
9
  import { authenticate } from '../libs/security';
@@ -166,6 +166,7 @@ export async function createProductAndPrices(payload: any) {
166
166
  // @ts-ignore
167
167
  ['preset', 'maximum', 'minimum'].forEach((key: keyof CustomUnitAmount) => {
168
168
  if (newPrice.custom_unit_amount?.[key]) {
169
+ // @ts-ignore
169
170
  newPrice.custom_unit_amount[key] = fromTokenToUnit(
170
171
  newPrice.custom_unit_amount[key] as string,
171
172
  currency.decimal
@@ -137,7 +137,7 @@ export type SimpleCustomField = {
137
137
  };
138
138
 
139
139
  export type DiscountAmount = {
140
- amount: number;
140
+ amount: string;
141
141
  discount?: string;
142
142
  promotion_code?: string;
143
143
  coupon?: string;
@@ -0,0 +1,11 @@
1
+ import { fromRandom } from '@ocap/wallet';
2
+ import { types } from '@ocap/mcrypto';
3
+
4
+ // Create a test wallet
5
+ const wallet = fromRandom({
6
+ role: types.RoleType.ROLE_APPLICATION,
7
+ });
8
+
9
+ // Set missing environment variables that @blocklet/sdk jest-setup.js doesn't provide
10
+ process.env.BLOCKLET_APP_PK = wallet.publicKey;
11
+ process.env.BLOCKLET_APP_EK = wallet.secretKey; // EK (Encryption Key) uses secretKey
package/api/third.d.ts CHANGED
@@ -2,8 +2,6 @@ declare module 'vite-plugin-blocklet';
2
2
 
3
3
  declare module '@blocklet/sdk/service/notification';
4
4
 
5
- declare module '@blocklet/sdk/service/auth';
6
-
7
5
  declare module 'express-history-api-fallback';
8
6
 
9
7
  declare module 'express-async-errors';
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.21.17
17
+ version: 1.22.1
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/jest.config.js CHANGED
@@ -6,8 +6,8 @@ module.exports = {
6
6
  coverageDirectory: 'coverage',
7
7
  restoreMocks: true,
8
8
  clearMocks: true,
9
- globalSetup: '@blocklet/sdk/lib/util/jest-setup.js',
10
- globalTeardown: '@blocklet/sdk/lib/util/jest-teardown.js',
9
+ globalSetup: '../../tools/jest-setup.js',
10
+ globalTeardown: '../../tools/jest-teardown.js',
11
11
  transform: {
12
12
  '^.+\\.ts?$': 'ts-jest',
13
13
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.21.17",
3
+ "version": "1.22.1",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
@@ -44,34 +44,34 @@
44
44
  ]
45
45
  },
46
46
  "dependencies": {
47
- "@abtnode/cron": "^1.16.53-beta-20251011-054719-4ed2f6b7",
48
- "@arcblock/did": "^1.25.6",
49
- "@arcblock/did-connect-react": "^3.1.46",
47
+ "@abtnode/cron": "^1.16.54-beta-20251029-055649-a9143beb",
48
+ "@arcblock/did": "^1.26.3",
49
+ "@arcblock/did-connect-react": "^3.1.53",
50
50
  "@arcblock/did-connect-storage-nedb": "^1.8.0",
51
- "@arcblock/did-util": "^1.25.6",
52
- "@arcblock/jwt": "^1.25.6",
53
- "@arcblock/ux": "^3.1.46",
54
- "@arcblock/validator": "^1.25.6",
55
- "@blocklet/did-space-js": "^1.1.32",
51
+ "@arcblock/did-util": "^1.26.3",
52
+ "@arcblock/jwt": "^1.26.3",
53
+ "@arcblock/ux": "^3.1.53",
54
+ "@arcblock/validator": "^1.26.3",
55
+ "@blocklet/did-space-js": "^1.1.35",
56
56
  "@blocklet/error": "^0.2.5",
57
- "@blocklet/js-sdk": "^1.16.53-beta-20251011-054719-4ed2f6b7",
58
- "@blocklet/logger": "^1.16.53-beta-20251011-054719-4ed2f6b7",
59
- "@blocklet/payment-broker-client": "1.21.17",
60
- "@blocklet/payment-react": "1.21.17",
61
- "@blocklet/payment-vendor": "1.21.17",
62
- "@blocklet/sdk": "^1.16.53-beta-20251011-054719-4ed2f6b7",
63
- "@blocklet/ui-react": "^3.1.46",
64
- "@blocklet/uploader": "^0.2.15",
65
- "@blocklet/xss": "^0.2.12",
57
+ "@blocklet/js-sdk": "^1.16.54-beta-20251029-055649-a9143beb",
58
+ "@blocklet/logger": "^1.16.54-beta-20251029-055649-a9143beb",
59
+ "@blocklet/payment-broker-client": "1.22.1",
60
+ "@blocklet/payment-react": "1.22.1",
61
+ "@blocklet/payment-vendor": "1.22.1",
62
+ "@blocklet/sdk": "^1.16.54-beta-20251029-055649-a9143beb",
63
+ "@blocklet/ui-react": "^3.1.53",
64
+ "@blocklet/uploader": "^0.3.1",
65
+ "@blocklet/xss": "^0.3.1",
66
66
  "@mui/icons-material": "^7.1.2",
67
67
  "@mui/lab": "7.0.0-beta.14",
68
68
  "@mui/material": "^7.1.2",
69
69
  "@mui/system": "^7.1.1",
70
- "@ocap/asset": "^1.25.6",
71
- "@ocap/client": "^1.25.6",
72
- "@ocap/mcrypto": "^1.25.6",
73
- "@ocap/util": "^1.25.6",
74
- "@ocap/wallet": "^1.25.6",
70
+ "@ocap/asset": "^1.26.3",
71
+ "@ocap/client": "^1.26.3",
72
+ "@ocap/mcrypto": "^1.26.3",
73
+ "@ocap/util": "^1.26.3",
74
+ "@ocap/wallet": "^1.26.3",
75
75
  "@stripe/react-stripe-js": "^2.9.0",
76
76
  "@stripe/stripe-js": "^2.4.0",
77
77
  "ahooks": "^3.8.5",
@@ -126,9 +126,9 @@
126
126
  "web3": "^4.16.0"
127
127
  },
128
128
  "devDependencies": {
129
- "@abtnode/types": "^1.16.53-beta-20251011-054719-4ed2f6b7",
129
+ "@abtnode/types": "^1.16.54-beta-20251029-055649-a9143beb",
130
130
  "@arcblock/eslint-config-ts": "^0.3.3",
131
- "@blocklet/payment-types": "1.21.17",
131
+ "@blocklet/payment-types": "1.22.1",
132
132
  "@types/cookie-parser": "^1.4.9",
133
133
  "@types/cors": "^2.8.19",
134
134
  "@types/debug": "^4.1.12",
@@ -175,5 +175,5 @@
175
175
  "parser": "typescript"
176
176
  }
177
177
  },
178
- "gitHead": "a823bc05e706681ee70451437b0460aba909c253"
178
+ "gitHead": "8db720c3902e307a6d869ff706ae4857f88cf867"
179
179
  }
@@ -56,9 +56,9 @@ export function getAppliedBalance(invoice: TInvoiceExpanded) {
56
56
  invoice.ending_token_balance &&
57
57
  invoice.ending_token_balance[invoice.paymentCurrency.id]
58
58
  ) {
59
- const starting = toBN(invoice.starting_token_balance[invoice.paymentCurrency.id]);
59
+ const starting = toBN(invoice.starting_token_balance[invoice.paymentCurrency.id] || '0');
60
60
 
61
- const ending = toBN(invoice.ending_token_balance[invoice.paymentCurrency.id]);
61
+ const ending = toBN(invoice.ending_token_balance[invoice.paymentCurrency.id] || '0');
62
62
  return ending.sub(starting).toString();
63
63
  }
64
64
 
@@ -197,7 +197,7 @@ export default function PaymentMethodInfo({
197
197
 
198
198
  return (
199
199
  <Typography variant="body2" sx={{ color: 'text.secondary' }}>
200
- -
200
+ {type || '-'}
201
201
  </Typography>
202
202
  );
203
203
  };
package/src/libs/util.ts CHANGED
@@ -252,7 +252,7 @@ export function stringToColor(str: string) {
252
252
 
253
253
  // @ts-ignore
254
254
  const hash = Hasher.SHA3.hash256(str).slice(-12);
255
-
255
+ // @ts-ignore
256
256
  const index = Math.abs(hexToNumber(hash)) % colors.length;
257
257
  return colors[index];
258
258
  }
@@ -145,7 +145,7 @@ export default function CustomerSubscriptionDetail() {
145
145
  );
146
146
  const estimateAmount = +fromUnitToToken(cycleAmount.amount, data.paymentCurrency?.decimal);
147
147
 
148
- const remainingStake = +fromUnitToToken(overdraftProtection?.unused, data.paymentCurrency?.decimal);
148
+ const remainingStake = +fromUnitToToken(overdraftProtection?.unused || '0', data.paymentCurrency?.decimal);
149
149
  const formatEstimatedDuration = (cycles: number) => {
150
150
  if (!data?.pending_invoice_item_interval) return '';
151
151
  const { interval, interval_count: intervalCount } = data.pending_invoice_item_interval;