@voyage_ai/v402-web-ts 0.2.0 → 0.3.0
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/README.md +467 -130
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +121 -131
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +123 -139
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +39 -9
- package/dist/react/index.d.ts +39 -9
- package/dist/react/index.js +818 -248
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +836 -257
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/styles.css +1 -1
- package/package.json +9 -2
package/dist/index.d.mts
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { PaymentRequirements } from 'x402/types';
|
|
2
2
|
export { PaymentRequirements, SPLTokenAmount, SettleResponse, VerifyResponse, x402Response } from 'x402/types';
|
|
3
|
-
import { VersionedTransaction } from '@solana/web3.js';
|
|
4
3
|
import { z } from 'zod';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Common types for x402 SDK
|
|
8
7
|
* Framework-agnostic types that work across different wallet implementations
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
9
|
/**
|
|
12
10
|
* Generic wallet adapter interface - works with any wallet provider
|
|
13
|
-
* Compatible with Anza wallet-adapter, Privy, and custom implementations
|
|
11
|
+
* Compatible with Anza wallet-adapter, Privy, @solana/kit, and custom implementations
|
|
14
12
|
*/
|
|
15
13
|
interface WalletAdapter {
|
|
16
14
|
publicKey?: {
|
|
17
15
|
toString(): string;
|
|
18
16
|
};
|
|
19
17
|
address?: string;
|
|
20
|
-
signTransaction: (tx:
|
|
18
|
+
signTransaction: <T>(tx: T) => Promise<T>;
|
|
21
19
|
}
|
|
22
20
|
/**
|
|
23
21
|
* EVM wallet adapter interface
|
|
@@ -235,6 +233,8 @@ declare function getChainId(network: string): number;
|
|
|
235
233
|
*
|
|
236
234
|
* Low-level API: Creates X-PAYMENT header for Solana transactions
|
|
237
235
|
* Use this when you want to build the payment header yourself and handle fetch separately
|
|
236
|
+
*
|
|
237
|
+
* Uses @solana/web3.js and @solana/spl-token packages
|
|
238
238
|
*/
|
|
239
239
|
|
|
240
240
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
import { PaymentRequirements } from 'x402/types';
|
|
2
2
|
export { PaymentRequirements, SPLTokenAmount, SettleResponse, VerifyResponse, x402Response } from 'x402/types';
|
|
3
|
-
import { VersionedTransaction } from '@solana/web3.js';
|
|
4
3
|
import { z } from 'zod';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Common types for x402 SDK
|
|
8
7
|
* Framework-agnostic types that work across different wallet implementations
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
9
|
/**
|
|
12
10
|
* Generic wallet adapter interface - works with any wallet provider
|
|
13
|
-
* Compatible with Anza wallet-adapter, Privy, and custom implementations
|
|
11
|
+
* Compatible with Anza wallet-adapter, Privy, @solana/kit, and custom implementations
|
|
14
12
|
*/
|
|
15
13
|
interface WalletAdapter {
|
|
16
14
|
publicKey?: {
|
|
17
15
|
toString(): string;
|
|
18
16
|
};
|
|
19
17
|
address?: string;
|
|
20
|
-
signTransaction: (tx:
|
|
18
|
+
signTransaction: <T>(tx: T) => Promise<T>;
|
|
21
19
|
}
|
|
22
20
|
/**
|
|
23
21
|
* EVM wallet adapter interface
|
|
@@ -235,6 +233,8 @@ declare function getChainId(network: string): number;
|
|
|
235
233
|
*
|
|
236
234
|
* Low-level API: Creates X-PAYMENT header for Solana transactions
|
|
237
235
|
* Use this when you want to build the payment header yourself and handle fetch separately
|
|
236
|
+
*
|
|
237
|
+
* Uses @solana/web3.js and @solana/spl-token packages
|
|
238
238
|
*/
|
|
239
239
|
|
|
240
240
|
/**
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ var PROD_BACK_URL;
|
|
|
25
25
|
var init_common = __esm({
|
|
26
26
|
"src/types/common.ts"() {
|
|
27
27
|
"use strict";
|
|
28
|
-
PROD_BACK_URL = "https://v402pay.onvoyage.ai/api/pay";
|
|
28
|
+
PROD_BACK_URL = true ? "https://v402pay.onvoyage.ai/api/pay" : "https://v402pay.onvoyage.ai/api/pay";
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
|
|
@@ -231,6 +231,40 @@ var import_spl_token = require("@solana/spl-token");
|
|
|
231
231
|
// src/utils/index.ts
|
|
232
232
|
init_wallet();
|
|
233
233
|
|
|
234
|
+
// src/utils/wallet-discovery.ts
|
|
235
|
+
var evmWallets = /* @__PURE__ */ new Map();
|
|
236
|
+
var evmDiscoveryListeners = /* @__PURE__ */ new Set();
|
|
237
|
+
var evmDiscoveryInitialized = false;
|
|
238
|
+
var currentConnectedWallet = null;
|
|
239
|
+
function initEVMWalletDiscovery() {
|
|
240
|
+
if (typeof window === "undefined" || evmDiscoveryInitialized) return;
|
|
241
|
+
evmDiscoveryInitialized = true;
|
|
242
|
+
window.addEventListener("eip6963:announceProvider", ((event) => {
|
|
243
|
+
const { info, provider } = event.detail;
|
|
244
|
+
evmWallets.set(info.uuid, { info, provider });
|
|
245
|
+
evmDiscoveryListeners.forEach((listener) => listener());
|
|
246
|
+
}));
|
|
247
|
+
window.dispatchEvent(new Event("eip6963:requestProvider"));
|
|
248
|
+
}
|
|
249
|
+
function getWalletProviderForPayment(networkType) {
|
|
250
|
+
if (currentConnectedWallet && currentConnectedWallet.networkType === networkType) {
|
|
251
|
+
return currentConnectedWallet.provider;
|
|
252
|
+
}
|
|
253
|
+
if (typeof window === "undefined") return null;
|
|
254
|
+
switch (networkType) {
|
|
255
|
+
case "evm" /* EVM */:
|
|
256
|
+
return window.ethereum;
|
|
257
|
+
case "solana" /* SOLANA */:
|
|
258
|
+
case "svm" /* SVM */:
|
|
259
|
+
return window.phantom?.solana || window.solana;
|
|
260
|
+
default:
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (typeof window !== "undefined") {
|
|
265
|
+
initEVMWalletDiscovery();
|
|
266
|
+
}
|
|
267
|
+
|
|
234
268
|
// src/services/evm/payment-header.ts
|
|
235
269
|
var import_ethers = require("ethers");
|
|
236
270
|
async function createEvmPaymentHeader(params) {
|
|
@@ -346,6 +380,15 @@ function getChainIdFromNetwork(network) {
|
|
|
346
380
|
|
|
347
381
|
// src/services/evm/payment-handler.ts
|
|
348
382
|
init_types();
|
|
383
|
+
var NETWORK_NAMES = {
|
|
384
|
+
1: "Ethereum Mainnet",
|
|
385
|
+
11155111: "Sepolia Testnet",
|
|
386
|
+
8453: "Base Mainnet",
|
|
387
|
+
84532: "Base Sepolia Testnet",
|
|
388
|
+
137: "Polygon Mainnet",
|
|
389
|
+
42161: "Arbitrum One",
|
|
390
|
+
10: "Optimism Mainnet"
|
|
391
|
+
};
|
|
349
392
|
async function handleEvmPayment(endpoint, config, requestInit) {
|
|
350
393
|
const { wallet, network, maxPaymentAmount } = config;
|
|
351
394
|
const initialResponse = await fetch(endpoint, {
|
|
@@ -356,25 +399,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
356
399
|
return initialResponse;
|
|
357
400
|
}
|
|
358
401
|
const rawResponse = await initialResponse.json();
|
|
359
|
-
|
|
360
|
-
"X-PAYMENT header is required",
|
|
361
|
-
"missing X-PAYMENT header",
|
|
362
|
-
"payment_required"
|
|
363
|
-
];
|
|
364
|
-
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
402
|
+
if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
|
|
365
403
|
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
"invalid_signature": "Invalid payment signature",
|
|
369
|
-
"expired": "Payment authorization has expired",
|
|
370
|
-
"already_used": "This payment has already been used",
|
|
371
|
-
"network_mismatch": "Payment network does not match",
|
|
372
|
-
"invalid_payment": "Invalid payment data",
|
|
373
|
-
"verification_failed": "Payment verification failed"
|
|
374
|
-
};
|
|
375
|
-
const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
376
|
-
const error = new Error(errorMessage);
|
|
377
|
-
throw wrapPaymentError(error);
|
|
404
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
405
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
378
406
|
}
|
|
379
407
|
const x402Version = rawResponse.x402Version;
|
|
380
408
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
@@ -406,19 +434,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
406
434
|
console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
|
|
407
435
|
}
|
|
408
436
|
}
|
|
409
|
-
const networkNames = {
|
|
410
|
-
1: "Ethereum Mainnet",
|
|
411
|
-
11155111: "Sepolia Testnet",
|
|
412
|
-
8453: "Base Mainnet",
|
|
413
|
-
84532: "Base Sepolia Testnet",
|
|
414
|
-
137: "Polygon Mainnet",
|
|
415
|
-
42161: "Arbitrum One",
|
|
416
|
-
10: "Optimism Mainnet"
|
|
417
|
-
};
|
|
418
437
|
if (currentChainId && currentChainId !== targetChainId) {
|
|
419
438
|
if (!wallet.switchChain) {
|
|
420
|
-
const currentNetworkName =
|
|
421
|
-
const targetNetworkName =
|
|
439
|
+
const currentNetworkName = NETWORK_NAMES[currentChainId] || `Chain ${currentChainId}`;
|
|
440
|
+
const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
|
|
422
441
|
const error = new Error(
|
|
423
442
|
`Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
|
|
424
443
|
);
|
|
@@ -430,7 +449,7 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
430
449
|
console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
|
|
431
450
|
} catch (error) {
|
|
432
451
|
console.error("\u274C Failed to switch chain:", error);
|
|
433
|
-
const targetNetworkName =
|
|
452
|
+
const targetNetworkName = NETWORK_NAMES[targetChainId] || selectedRequirements.network;
|
|
434
453
|
const wrappedError = wrapPaymentError(error);
|
|
435
454
|
let finalError;
|
|
436
455
|
if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
|
|
@@ -484,25 +503,10 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
484
503
|
if (retryResponse.status === 402) {
|
|
485
504
|
try {
|
|
486
505
|
const retryData = await retryResponse.json();
|
|
487
|
-
|
|
488
|
-
"X-PAYMENT header is required",
|
|
489
|
-
"missing X-PAYMENT header",
|
|
490
|
-
"payment_required"
|
|
491
|
-
];
|
|
492
|
-
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
506
|
+
if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
|
|
493
507
|
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
494
|
-
const
|
|
495
|
-
|
|
496
|
-
"invalid_signature": "Invalid payment signature",
|
|
497
|
-
"expired": "Payment authorization has expired",
|
|
498
|
-
"already_used": "This payment has already been used",
|
|
499
|
-
"network_mismatch": "Payment network does not match",
|
|
500
|
-
"invalid_payment": "Invalid payment data",
|
|
501
|
-
"verification_failed": "Payment verification failed"
|
|
502
|
-
};
|
|
503
|
-
const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
504
|
-
const error = new Error(errorMessage);
|
|
505
|
-
throw wrapPaymentError(error);
|
|
508
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
509
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
506
510
|
}
|
|
507
511
|
} catch (error) {
|
|
508
512
|
if (error instanceof PaymentOperationError) {
|
|
@@ -533,9 +537,9 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
533
537
|
}
|
|
534
538
|
} : {};
|
|
535
539
|
if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
|
|
536
|
-
const solana =
|
|
540
|
+
const solana = getWalletProviderForPayment(networkType);
|
|
537
541
|
if (!solana) {
|
|
538
|
-
throw new Error("\u8BF7\
|
|
542
|
+
throw new Error("\u8BF7\u5148\u8FDE\u63A5 Solana \u94B1\u5305");
|
|
539
543
|
}
|
|
540
544
|
if (!solana.isConnected) {
|
|
541
545
|
await solana.connect();
|
|
@@ -546,10 +550,11 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
546
550
|
// Will use backend's network configuration
|
|
547
551
|
}, requestInit);
|
|
548
552
|
} else if (networkType === "evm" /* EVM */) {
|
|
549
|
-
|
|
550
|
-
|
|
553
|
+
const ethereum = getWalletProviderForPayment(networkType);
|
|
554
|
+
if (!ethereum) {
|
|
555
|
+
throw new Error("\u8BF7\u5148\u8FDE\u63A5 EVM \u94B1\u5305");
|
|
551
556
|
}
|
|
552
|
-
const provider = new import_ethers2.ethers.BrowserProvider(
|
|
557
|
+
const provider = new import_ethers2.ethers.BrowserProvider(ethereum);
|
|
553
558
|
const signer = await provider.getSigner();
|
|
554
559
|
const wallet = {
|
|
555
560
|
address: await signer.getAddress(),
|
|
@@ -563,7 +568,7 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, ad
|
|
|
563
568
|
},
|
|
564
569
|
// Switch to a different chain
|
|
565
570
|
switchChain: async (chainId) => {
|
|
566
|
-
await
|
|
571
|
+
await ethereum.request({
|
|
567
572
|
method: "wallet_switchEthereumChain",
|
|
568
573
|
params: [{ chainId }]
|
|
569
574
|
});
|
|
@@ -650,6 +655,21 @@ function is402Response(response) {
|
|
|
650
655
|
}
|
|
651
656
|
|
|
652
657
|
// src/utils/payment-error-handler.ts
|
|
658
|
+
var IGNORED_402_ERRORS = [
|
|
659
|
+
"X-PAYMENT header is required",
|
|
660
|
+
"missing X-PAYMENT header",
|
|
661
|
+
"payment_required"
|
|
662
|
+
];
|
|
663
|
+
var PAYMENT_ERROR_MESSAGES = {
|
|
664
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
665
|
+
"invalid_signature": "Invalid payment signature",
|
|
666
|
+
"expired": "Payment authorization has expired",
|
|
667
|
+
"already_used": "This payment has already been used",
|
|
668
|
+
"network_mismatch": "Payment network does not match",
|
|
669
|
+
"invalid_payment": "Invalid payment data",
|
|
670
|
+
"verification_failed": "Payment verification failed",
|
|
671
|
+
"invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
|
|
672
|
+
};
|
|
653
673
|
function parsePaymentError(error) {
|
|
654
674
|
if (!error) {
|
|
655
675
|
return {
|
|
@@ -784,7 +804,7 @@ function wrapPaymentError(error) {
|
|
|
784
804
|
// src/services/svm/payment-header.ts
|
|
785
805
|
async function createSvmPaymentHeader(params) {
|
|
786
806
|
const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
|
|
787
|
-
const connection = new import_web3.Connection(rpcUrl
|
|
807
|
+
const connection = new import_web3.Connection(rpcUrl);
|
|
788
808
|
const feePayer = paymentRequirements?.extra?.feePayer;
|
|
789
809
|
if (typeof feePayer !== "string" || !feePayer) {
|
|
790
810
|
throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
|
|
@@ -798,83 +818,85 @@ async function createSvmPaymentHeader(params) {
|
|
|
798
818
|
if (!paymentRequirements?.payTo) {
|
|
799
819
|
throw new Error("Missing payTo in payment requirements");
|
|
800
820
|
}
|
|
801
|
-
const
|
|
802
|
-
const instructions = [];
|
|
803
|
-
instructions.push(
|
|
804
|
-
import_web3.ComputeBudgetProgram.setComputeUnitLimit({
|
|
805
|
-
units: 7e3
|
|
806
|
-
// Sufficient for SPL token transfer
|
|
807
|
-
})
|
|
808
|
-
);
|
|
809
|
-
instructions.push(
|
|
810
|
-
import_web3.ComputeBudgetProgram.setComputeUnitPrice({
|
|
811
|
-
microLamports: 1
|
|
812
|
-
// Minimal price
|
|
813
|
-
})
|
|
814
|
-
);
|
|
821
|
+
const destinationPubkey = new import_web3.PublicKey(paymentRequirements.payTo);
|
|
815
822
|
if (!paymentRequirements.asset) {
|
|
816
823
|
throw new Error("Missing token mint for SPL transfer");
|
|
817
824
|
}
|
|
818
825
|
const mintPubkey = new import_web3.PublicKey(paymentRequirements.asset);
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
826
|
+
const mintAccountInfo = await connection.getAccountInfo(mintPubkey);
|
|
827
|
+
if (!mintAccountInfo) {
|
|
828
|
+
throw new Error(`Mint account ${mintPubkey.toBase58()} not found`);
|
|
829
|
+
}
|
|
830
|
+
const tokenProgramId = mintAccountInfo.owner.equals(import_spl_token.TOKEN_2022_PROGRAM_ID) ? import_spl_token.TOKEN_2022_PROGRAM_ID : import_spl_token.TOKEN_PROGRAM_ID;
|
|
831
|
+
const mint = await (0, import_spl_token.getMint)(connection, mintPubkey, void 0, tokenProgramId);
|
|
832
|
+
const sourceAta = (0, import_spl_token.getAssociatedTokenAddressSync)(
|
|
823
833
|
mintPubkey,
|
|
824
834
|
userPubkey,
|
|
825
835
|
false,
|
|
826
|
-
|
|
836
|
+
tokenProgramId
|
|
827
837
|
);
|
|
828
|
-
const destinationAta =
|
|
838
|
+
const destinationAta = (0, import_spl_token.getAssociatedTokenAddressSync)(
|
|
829
839
|
mintPubkey,
|
|
830
|
-
|
|
840
|
+
destinationPubkey,
|
|
831
841
|
false,
|
|
832
|
-
|
|
842
|
+
tokenProgramId
|
|
833
843
|
);
|
|
834
|
-
const sourceAtaInfo = await connection.getAccountInfo(sourceAta
|
|
844
|
+
const sourceAtaInfo = await connection.getAccountInfo(sourceAta);
|
|
835
845
|
if (!sourceAtaInfo) {
|
|
836
846
|
throw new Error(
|
|
837
847
|
`User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
|
|
838
848
|
);
|
|
839
849
|
}
|
|
840
|
-
const destAtaInfo = await connection.getAccountInfo(destinationAta
|
|
850
|
+
const destAtaInfo = await connection.getAccountInfo(destinationAta);
|
|
841
851
|
if (!destAtaInfo) {
|
|
842
852
|
throw new Error(
|
|
843
853
|
`Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
|
|
844
854
|
);
|
|
845
855
|
}
|
|
846
|
-
const
|
|
847
|
-
|
|
856
|
+
const instructions = [
|
|
857
|
+
import_web3.ComputeBudgetProgram.setComputeUnitLimit({
|
|
858
|
+
units: 7e3
|
|
859
|
+
// Sufficient for SPL token transfer
|
|
860
|
+
}),
|
|
861
|
+
import_web3.ComputeBudgetProgram.setComputeUnitPrice({
|
|
862
|
+
microLamports: 1
|
|
863
|
+
// Minimal price
|
|
864
|
+
}),
|
|
848
865
|
(0, import_spl_token.createTransferCheckedInstruction)(
|
|
849
866
|
sourceAta,
|
|
850
867
|
mintPubkey,
|
|
851
868
|
destinationAta,
|
|
852
869
|
userPubkey,
|
|
853
|
-
|
|
870
|
+
BigInt(paymentRequirements.maxAmountRequired),
|
|
854
871
|
mint.decimals,
|
|
855
872
|
[],
|
|
856
|
-
|
|
873
|
+
tokenProgramId
|
|
857
874
|
)
|
|
858
|
-
|
|
859
|
-
const { blockhash } = await connection.getLatestBlockhash(
|
|
860
|
-
const
|
|
875
|
+
];
|
|
876
|
+
const { blockhash } = await connection.getLatestBlockhash();
|
|
877
|
+
const messageV0 = new import_web3.TransactionMessage({
|
|
861
878
|
payerKey: feePayerPubkey,
|
|
862
879
|
recentBlockhash: blockhash,
|
|
863
880
|
instructions
|
|
864
881
|
}).compileToV0Message();
|
|
865
|
-
const transaction = new import_web3.VersionedTransaction(
|
|
882
|
+
const transaction = new import_web3.VersionedTransaction(messageV0);
|
|
866
883
|
if (typeof wallet?.signTransaction !== "function") {
|
|
867
884
|
throw new Error("Connected wallet does not support signTransaction");
|
|
868
885
|
}
|
|
869
|
-
let
|
|
886
|
+
let signedTransaction;
|
|
870
887
|
try {
|
|
871
|
-
|
|
888
|
+
signedTransaction = await wallet.signTransaction(transaction);
|
|
872
889
|
console.log("\u2705 Transaction signed successfully");
|
|
873
890
|
} catch (error) {
|
|
874
891
|
console.error("\u274C Failed to sign transaction:", error);
|
|
875
892
|
throw wrapPaymentError(error);
|
|
876
893
|
}
|
|
877
|
-
const
|
|
894
|
+
const serializedBytes = signedTransaction.serialize();
|
|
895
|
+
let binary = "";
|
|
896
|
+
for (let i = 0; i < serializedBytes.length; i++) {
|
|
897
|
+
binary += String.fromCharCode(serializedBytes[i]);
|
|
898
|
+
}
|
|
899
|
+
const serializedTransaction = btoa(binary);
|
|
878
900
|
const paymentPayload = {
|
|
879
901
|
x402Version,
|
|
880
902
|
scheme: paymentRequirements.scheme,
|
|
@@ -883,7 +905,7 @@ async function createSvmPaymentHeader(params) {
|
|
|
883
905
|
transaction: serializedTransaction
|
|
884
906
|
}
|
|
885
907
|
};
|
|
886
|
-
const paymentHeader =
|
|
908
|
+
const paymentHeader = btoa(JSON.stringify(paymentPayload));
|
|
887
909
|
return paymentHeader;
|
|
888
910
|
}
|
|
889
911
|
function getDefaultSolanaRpcUrl(network) {
|
|
@@ -899,7 +921,7 @@ function getDefaultSolanaRpcUrl(network) {
|
|
|
899
921
|
// src/services/svm/payment-handler.ts
|
|
900
922
|
init_types();
|
|
901
923
|
async function handleSvmPayment(endpoint, config, requestInit) {
|
|
902
|
-
const { wallet,
|
|
924
|
+
const { wallet, rpcUrl, maxPaymentAmount } = config;
|
|
903
925
|
const initialResponse = await fetch(endpoint, {
|
|
904
926
|
...requestInit,
|
|
905
927
|
method: requestInit?.method || "POST"
|
|
@@ -908,26 +930,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
908
930
|
return initialResponse;
|
|
909
931
|
}
|
|
910
932
|
const rawResponse = await initialResponse.json();
|
|
911
|
-
|
|
912
|
-
"X-PAYMENT header is required",
|
|
913
|
-
"missing X-PAYMENT header",
|
|
914
|
-
"payment_required"
|
|
915
|
-
];
|
|
916
|
-
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
933
|
+
if (rawResponse.error && !IGNORED_402_ERRORS.includes(rawResponse.error)) {
|
|
917
934
|
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
"invalid_signature": "Invalid payment signature",
|
|
921
|
-
"expired": "Payment authorization has expired",
|
|
922
|
-
"already_used": "This payment has already been used",
|
|
923
|
-
"network_mismatch": "Payment network does not match",
|
|
924
|
-
"invalid_payment": "Invalid payment data",
|
|
925
|
-
"verification_failed": "Payment verification failed",
|
|
926
|
-
"invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
|
|
927
|
-
};
|
|
928
|
-
const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
929
|
-
const error = new Error(errorMessage);
|
|
930
|
-
throw wrapPaymentError(error);
|
|
935
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
936
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
931
937
|
}
|
|
932
938
|
const x402Version = rawResponse.x402Version;
|
|
933
939
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
@@ -977,26 +983,10 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
977
983
|
if (retryResponse.status === 402) {
|
|
978
984
|
try {
|
|
979
985
|
const retryData = await retryResponse.json();
|
|
980
|
-
|
|
981
|
-
"X-PAYMENT header is required",
|
|
982
|
-
"missing X-PAYMENT header",
|
|
983
|
-
"payment_required"
|
|
984
|
-
];
|
|
985
|
-
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
986
|
+
if (retryData.error && !IGNORED_402_ERRORS.includes(retryData.error)) {
|
|
986
987
|
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
987
|
-
const
|
|
988
|
-
|
|
989
|
-
"invalid_signature": "Invalid payment signature",
|
|
990
|
-
"expired": "Payment authorization has expired",
|
|
991
|
-
"already_used": "This payment has already been used",
|
|
992
|
-
"network_mismatch": "Payment network does not match",
|
|
993
|
-
"invalid_payment": "Invalid payment data",
|
|
994
|
-
"verification_failed": "Payment verification failed",
|
|
995
|
-
"invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
|
|
996
|
-
};
|
|
997
|
-
const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
998
|
-
const error = new Error(errorMessage);
|
|
999
|
-
throw wrapPaymentError(error);
|
|
988
|
+
const errorMessage = PAYMENT_ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
989
|
+
throw wrapPaymentError(new Error(errorMessage));
|
|
1000
990
|
}
|
|
1001
991
|
} catch (error) {
|
|
1002
992
|
if (error instanceof PaymentOperationError) {
|