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.
- package/api/src/index.ts +1 -1
- package/api/src/integrations/blocklet/user.ts +2 -2
- package/api/src/integrations/ethereum/token.ts +4 -5
- package/api/src/integrations/stripe/handlers/invoice.ts +1 -1
- package/api/src/integrations/stripe/handlers/payment-intent.ts +1 -1
- package/api/src/integrations/stripe/resource.ts +1 -1
- package/api/src/integrations/stripe/setup.ts +1 -1
- package/api/src/libs/auth.ts +7 -6
- package/api/src/libs/env.ts +1 -1
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +1 -0
- package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
- package/api/src/libs/payment.ts +2 -3
- package/api/src/libs/refund.ts +1 -1
- package/api/src/libs/remote-signer.ts +93 -0
- package/api/src/libs/security.ts +1 -1
- package/api/src/libs/subscription.ts +4 -7
- package/api/src/libs/util.ts +1 -1
- package/api/src/libs/vendor-util/adapters/didnames-adapter.ts +17 -9
- package/api/src/libs/vendor-util/adapters/launcher-adapter.ts +11 -6
- package/api/src/queues/payment.ts +2 -2
- package/api/src/queues/payout.ts +1 -1
- package/api/src/queues/refund.ts +2 -2
- package/api/src/queues/subscription.ts +1 -1
- package/api/src/queues/usage-record.ts +1 -1
- package/api/src/queues/vendors/status-check.ts +1 -1
- package/api/src/queues/webhook.ts +1 -1
- package/api/src/routes/auto-recharge-configs.ts +1 -1
- package/api/src/routes/checkout-sessions.ts +4 -6
- package/api/src/routes/connect/collect-batch.ts +1 -1
- package/api/src/routes/connect/collect.ts +1 -1
- package/api/src/routes/connect/pay.ts +1 -1
- package/api/src/routes/connect/recharge-account.ts +1 -1
- package/api/src/routes/connect/recharge.ts +1 -1
- package/api/src/routes/connect/shared.ts +32 -23
- package/api/src/routes/customers.ts +1 -1
- package/api/src/routes/integrations/stripe.ts +1 -1
- package/api/src/routes/meter-events.ts +9 -12
- package/api/src/routes/payment-currencies.ts +1 -1
- package/api/src/routes/payment-intents.ts +2 -2
- package/api/src/routes/payouts.ts +1 -1
- package/api/src/routes/products.ts +1 -0
- package/api/src/store/models/types.ts +1 -1
- package/api/tests/setup.ts +11 -0
- package/api/third.d.ts +0 -2
- package/blocklet.yml +1 -1
- package/jest.config.js +2 -2
- package/package.json +26 -26
- package/src/components/invoice/table.tsx +2 -2
- package/src/components/subscription/payment-method-info.tsx +1 -1
- package/src/libs/util.ts +1 -1
- 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
|
|
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,
|
|
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
|
|
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
|
package/api/src/libs/auth.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
|
|
3
3
|
import AuthStorage from '@arcblock/did-connect-storage-nedb';
|
|
4
|
-
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
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
|
|
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
|
|
28
|
+
export const blocklet = new BlockletService();
|
|
28
29
|
|
|
29
30
|
export async function getVaultAddress() {
|
|
30
31
|
try {
|
package/api/src/libs/env.ts
CHANGED
|
@@ -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
|
|
package/api/src/libs/payment.ts
CHANGED
|
@@ -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':
|
|
394
|
+
'x-gas-payer-sig': await wallet.signJWT({ txHash }),
|
|
396
395
|
'x-gas-payer-pk': wallet.publicKey,
|
|
397
396
|
},
|
|
398
397
|
};
|
package/api/src/libs/refund.ts
CHANGED
|
@@ -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
|
+
}
|
package/api/src/libs/security.ts
CHANGED
|
@@ -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]:
|
|
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',
|
package/api/src/libs/util.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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({
|
|
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 = (
|
|
21
|
-
|
|
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 });
|
package/api/src/queues/payout.ts
CHANGED
|
@@ -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
|
|
package/api/src/queues/refund.ts
CHANGED
|
@@ -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
|
|
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
|
-
|
|
275
|
-
|
|
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(
|
|
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 =
|
|
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({
|
|
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 }> {
|
|
@@ -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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
package/blocklet.yml
CHANGED
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: '
|
|
10
|
-
globalTeardown: '
|
|
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.
|
|
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.
|
|
48
|
-
"@arcblock/did": "^1.
|
|
49
|
-
"@arcblock/did-connect-react": "^3.1.
|
|
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.
|
|
52
|
-
"@arcblock/jwt": "^1.
|
|
53
|
-
"@arcblock/ux": "^3.1.
|
|
54
|
-
"@arcblock/validator": "^1.
|
|
55
|
-
"@blocklet/did-space-js": "^1.1.
|
|
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.
|
|
58
|
-
"@blocklet/logger": "^1.16.
|
|
59
|
-
"@blocklet/payment-broker-client": "1.
|
|
60
|
-
"@blocklet/payment-react": "1.
|
|
61
|
-
"@blocklet/payment-vendor": "1.
|
|
62
|
-
"@blocklet/sdk": "^1.16.
|
|
63
|
-
"@blocklet/ui-react": "^3.1.
|
|
64
|
-
"@blocklet/uploader": "^0.
|
|
65
|
-
"@blocklet/xss": "^0.
|
|
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.
|
|
71
|
-
"@ocap/client": "^1.
|
|
72
|
-
"@ocap/mcrypto": "^1.
|
|
73
|
-
"@ocap/util": "^1.
|
|
74
|
-
"@ocap/wallet": "^1.
|
|
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.
|
|
129
|
+
"@abtnode/types": "^1.16.54-beta-20251029-055649-a9143beb",
|
|
130
130
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
131
|
-
"@blocklet/payment-types": "1.
|
|
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": "
|
|
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
|
|
package/src/libs/util.ts
CHANGED
|
@@ -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;
|