@voyage_ai/v402-web-ts 0.1.1 → 0.1.2

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.
@@ -30,7 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/react/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- PaymentButton: () => PaymentButton,
34
33
  WalletConnect: () => WalletConnect,
35
34
  usePayment: () => usePayment,
36
35
  usePaymentInfo: () => usePaymentInfo,
@@ -272,510 +271,9 @@ function onWalletDisconnect(callback) {
272
271
  // src/services/svm/payment-header.ts
273
272
  var import_web3 = require("@solana/web3.js");
274
273
  var import_spl_token = require("@solana/spl-token");
275
- async function createSvmPaymentHeader(params) {
276
- const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
277
- const connection = new import_web3.Connection(rpcUrl, "confirmed");
278
- const feePayer = paymentRequirements?.extra?.feePayer;
279
- if (typeof feePayer !== "string" || !feePayer) {
280
- throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
281
- }
282
- const feePayerPubkey = new import_web3.PublicKey(feePayer);
283
- const walletAddress = wallet?.publicKey?.toString() || wallet?.address;
284
- if (!walletAddress) {
285
- throw new Error("Missing connected Solana wallet address or publicKey");
286
- }
287
- const userPubkey = new import_web3.PublicKey(walletAddress);
288
- if (!paymentRequirements?.payTo) {
289
- throw new Error("Missing payTo in payment requirements");
290
- }
291
- const destination = new import_web3.PublicKey(paymentRequirements.payTo);
292
- const instructions = [];
293
- instructions.push(
294
- import_web3.ComputeBudgetProgram.setComputeUnitLimit({
295
- units: 7e3
296
- // Sufficient for SPL token transfer
297
- })
298
- );
299
- instructions.push(
300
- import_web3.ComputeBudgetProgram.setComputeUnitPrice({
301
- microLamports: 1
302
- // Minimal price
303
- })
304
- );
305
- if (!paymentRequirements.asset) {
306
- throw new Error("Missing token mint for SPL transfer");
307
- }
308
- const mintPubkey = new import_web3.PublicKey(paymentRequirements.asset);
309
- const mintInfo = await connection.getAccountInfo(mintPubkey, "confirmed");
310
- const programId = mintInfo?.owner?.toBase58() === import_spl_token.TOKEN_2022_PROGRAM_ID.toBase58() ? import_spl_token.TOKEN_2022_PROGRAM_ID : import_spl_token.TOKEN_PROGRAM_ID;
311
- const mint = await (0, import_spl_token.getMint)(connection, mintPubkey, void 0, programId);
312
- const sourceAta = await (0, import_spl_token.getAssociatedTokenAddress)(
313
- mintPubkey,
314
- userPubkey,
315
- false,
316
- programId
317
- );
318
- const destinationAta = await (0, import_spl_token.getAssociatedTokenAddress)(
319
- mintPubkey,
320
- destination,
321
- false,
322
- programId
323
- );
324
- const sourceAtaInfo = await connection.getAccountInfo(sourceAta, "confirmed");
325
- if (!sourceAtaInfo) {
326
- throw new Error(
327
- `User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
328
- );
329
- }
330
- const destAtaInfo = await connection.getAccountInfo(destinationAta, "confirmed");
331
- if (!destAtaInfo) {
332
- throw new Error(
333
- `Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
334
- );
335
- }
336
- const amount = BigInt(paymentRequirements.maxAmountRequired);
337
- instructions.push(
338
- (0, import_spl_token.createTransferCheckedInstruction)(
339
- sourceAta,
340
- mintPubkey,
341
- destinationAta,
342
- userPubkey,
343
- amount,
344
- mint.decimals,
345
- [],
346
- programId
347
- )
348
- );
349
- const { blockhash } = await connection.getLatestBlockhash("confirmed");
350
- const message = new import_web3.TransactionMessage({
351
- payerKey: feePayerPubkey,
352
- recentBlockhash: blockhash,
353
- instructions
354
- }).compileToV0Message();
355
- const transaction = new import_web3.VersionedTransaction(message);
356
- if (typeof wallet?.signTransaction !== "function") {
357
- throw new Error("Connected wallet does not support signTransaction");
358
- }
359
- let userSignedTx;
360
- try {
361
- userSignedTx = await wallet.signTransaction(transaction);
362
- console.log("\u2705 Transaction signed successfully");
363
- } catch (error) {
364
- console.error("\u274C Failed to sign transaction:", error);
365
- throw wrapPaymentError(error);
366
- }
367
- const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
368
- const paymentPayload = {
369
- x402Version,
370
- scheme: paymentRequirements.scheme,
371
- network: paymentRequirements.network,
372
- payload: {
373
- transaction: serializedTransaction
374
- }
375
- };
376
- const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
377
- return paymentHeader;
378
- }
379
- function getDefaultSolanaRpcUrl(network) {
380
- const normalized = network.toLowerCase();
381
- if (normalized === "solana" || normalized === "solana-mainnet") {
382
- return "https://cathee-fu8ezd-fast-mainnet.helius-rpc.com";
383
- } else if (normalized === "solana-devnet") {
384
- return "https://api.devnet.solana.com";
385
- }
386
- throw new Error(`Unsupported Solana network: ${network}`);
387
- }
388
-
389
- // src/services/svm/payment-handler.ts
390
- async function handleSvmPayment(endpoint, config, requestInit) {
391
- const { wallet, network, rpcUrl, maxPaymentAmount } = config;
392
- const initialResponse = await fetch(endpoint, {
393
- ...requestInit,
394
- method: requestInit?.method || "POST"
395
- });
396
- if (initialResponse.status !== 402) {
397
- return initialResponse;
398
- }
399
- const rawResponse = await initialResponse.json();
400
- const IGNORED_ERRORS = [
401
- "X-PAYMENT header is required",
402
- "missing X-PAYMENT header",
403
- "payment_required"
404
- ];
405
- if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
406
- console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
407
- const ERROR_MESSAGES = {
408
- "insufficient_funds": "Insufficient balance to complete this payment",
409
- "invalid_signature": "Invalid payment signature",
410
- "expired": "Payment authorization has expired",
411
- "already_used": "This payment has already been used",
412
- "network_mismatch": "Payment network does not match",
413
- "invalid_payment": "Invalid payment data",
414
- "verification_failed": "Payment verification failed"
415
- };
416
- const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
417
- const error = new Error(errorMessage);
418
- throw wrapPaymentError(error);
419
- }
420
- const x402Version = rawResponse.x402Version;
421
- const parsedPaymentRequirements = rawResponse.accepts || [];
422
- const selectedRequirements = parsedPaymentRequirements.find(
423
- (req) => req.scheme === "exact" && SolanaNetworkSchema.safeParse(req.network.toLowerCase()).success
424
- );
425
- if (!selectedRequirements) {
426
- console.error(
427
- "\u274C No suitable Solana payment requirements found. Available networks:",
428
- parsedPaymentRequirements.map((req) => req.network)
429
- );
430
- throw new Error("No suitable Solana payment requirements found");
431
- }
432
- if (maxPaymentAmount && maxPaymentAmount > BigInt(0)) {
433
- if (BigInt(selectedRequirements.maxAmountRequired) > maxPaymentAmount) {
434
- throw new Error(
435
- `Payment amount ${selectedRequirements.maxAmountRequired} exceeds maximum allowed ${maxPaymentAmount}`
436
- );
437
- }
438
- }
439
- const effectiveRpcUrl = rpcUrl || getDefaultSolanaRpcUrl(selectedRequirements.network);
440
- console.log(`\u{1F4CD} Using Solana RPC: ${effectiveRpcUrl.substring(0, 40)}...`);
441
- console.log(`\u{1F4CD} Network from backend: ${selectedRequirements.network}`);
442
- let paymentHeader;
443
- try {
444
- paymentHeader = await createSvmPaymentHeader({
445
- wallet,
446
- paymentRequirements: selectedRequirements,
447
- x402Version,
448
- rpcUrl: effectiveRpcUrl
449
- });
450
- console.log("\u2705 Payment header created successfully");
451
- } catch (error) {
452
- console.error("\u274C Failed to create payment header:", error);
453
- throw wrapPaymentError(error);
454
- }
455
- const newInit = {
456
- ...requestInit,
457
- method: requestInit?.method || "POST",
458
- headers: {
459
- ...requestInit?.headers || {},
460
- "X-PAYMENT": paymentHeader,
461
- "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
462
- }
463
- };
464
- const retryResponse = await fetch(endpoint, newInit);
465
- if (retryResponse.status === 402) {
466
- try {
467
- const retryData = await retryResponse.json();
468
- const IGNORED_ERRORS2 = [
469
- "X-PAYMENT header is required",
470
- "missing X-PAYMENT header",
471
- "payment_required"
472
- ];
473
- if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
474
- console.error(`\u274C Payment verification failed: ${retryData.error}`);
475
- const ERROR_MESSAGES = {
476
- "insufficient_funds": "Insufficient balance to complete this payment",
477
- "invalid_signature": "Invalid payment signature",
478
- "expired": "Payment authorization has expired",
479
- "already_used": "This payment has already been used",
480
- "network_mismatch": "Payment network does not match",
481
- "invalid_payment": "Invalid payment data",
482
- "verification_failed": "Payment verification failed"
483
- };
484
- const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
485
- const error = new Error(errorMessage);
486
- throw wrapPaymentError(error);
487
- }
488
- } catch (error) {
489
- if (error instanceof PaymentOperationError) {
490
- throw error;
491
- }
492
- console.warn("\u26A0\uFE0F Could not parse retry 402 response:", error);
493
- }
494
- }
495
- return retryResponse;
496
- }
497
274
 
498
275
  // src/services/evm/payment-header.ts
499
276
  var import_ethers = require("ethers");
500
- async function createEvmPaymentHeader(params) {
501
- const { wallet, paymentRequirements, x402Version, chainId } = params;
502
- if (!paymentRequirements?.payTo) {
503
- throw new Error("Missing payTo in payment requirements");
504
- }
505
- if (!paymentRequirements?.asset) {
506
- throw new Error("Missing asset (token contract) in payment requirements");
507
- }
508
- if (wallet.getChainId) {
509
- try {
510
- const currentChainIdHex = await wallet.getChainId();
511
- const currentChainId = parseInt(currentChainIdHex, 16);
512
- if (currentChainId !== chainId) {
513
- const networkNames = {
514
- 1: "Ethereum Mainnet",
515
- 11155111: "Sepolia Testnet",
516
- 8453: "Base Mainnet",
517
- 84532: "Base Sepolia Testnet",
518
- 137: "Polygon Mainnet",
519
- 42161: "Arbitrum One",
520
- 10: "Optimism Mainnet"
521
- };
522
- const currentNetworkName = networkNames[currentChainId] || `Chain ${currentChainId}`;
523
- const targetNetworkName = networkNames[chainId] || `Chain ${chainId}`;
524
- throw new Error(
525
- `Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch your wallet to the correct network.`
526
- );
527
- }
528
- console.log(`\u2705 Chain ID verified: ${chainId}`);
529
- } catch (error) {
530
- if (error.message.includes("Network mismatch")) {
531
- throw wrapPaymentError(error);
532
- }
533
- console.warn("Could not verify chainId:", error);
534
- }
535
- }
536
- const now = Math.floor(Date.now() / 1e3);
537
- const nonceBytes = import_ethers.ethers.randomBytes(32);
538
- const nonceBytes32 = import_ethers.ethers.hexlify(nonceBytes);
539
- const domain = {
540
- name: paymentRequirements.extra?.name || "USDC",
541
- version: paymentRequirements.extra?.version || "2",
542
- chainId,
543
- verifyingContract: paymentRequirements.asset
544
- };
545
- const types = {
546
- TransferWithAuthorization: [
547
- { name: "from", type: "address" },
548
- { name: "to", type: "address" },
549
- { name: "value", type: "uint256" },
550
- { name: "validAfter", type: "uint256" },
551
- { name: "validBefore", type: "uint256" },
552
- { name: "nonce", type: "bytes32" }
553
- ]
554
- };
555
- const authorization = {
556
- from: wallet.address,
557
- to: paymentRequirements.payTo,
558
- value: paymentRequirements.maxAmountRequired,
559
- validAfter: "0",
560
- // Effective immediately
561
- validBefore: String(now + (paymentRequirements.maxTimeoutSeconds || 3600)),
562
- nonce: nonceBytes32
563
- };
564
- let signature;
565
- try {
566
- signature = await wallet.signTypedData(domain, types, authorization);
567
- console.log("\u2705 Signature created successfully");
568
- } catch (error) {
569
- console.error("\u274C Failed to create signature:", error);
570
- throw wrapPaymentError(error);
571
- }
572
- const headerPayload = {
573
- x402_version: x402Version,
574
- x402Version,
575
- scheme: paymentRequirements.scheme,
576
- network: paymentRequirements.network,
577
- payload: {
578
- signature,
579
- authorization: {
580
- from: authorization.from,
581
- to: authorization.to,
582
- value: String(authorization.value),
583
- valid_after: authorization.validAfter,
584
- validAfter: authorization.validAfter,
585
- valid_before: authorization.validBefore,
586
- validBefore: authorization.validBefore,
587
- nonce: authorization.nonce
588
- }
589
- }
590
- };
591
- const paymentHeader = btoa(JSON.stringify(headerPayload));
592
- return paymentHeader;
593
- }
594
- function getChainIdFromNetwork(network) {
595
- const chainIdMap = {
596
- "ethereum": 1,
597
- "sepolia": 11155111,
598
- "base": 8453,
599
- "base-sepolia": 84532,
600
- "polygon": 137,
601
- "arbitrum": 42161,
602
- "optimism": 10
603
- };
604
- const chainId = chainIdMap[network.toLowerCase()];
605
- if (!chainId) {
606
- throw new Error(`Unknown network: ${network}`);
607
- }
608
- return chainId;
609
- }
610
-
611
- // src/services/evm/payment-handler.ts
612
- async function handleEvmPayment(endpoint, config, requestInit) {
613
- const { wallet, network, maxPaymentAmount } = config;
614
- const initialResponse = await fetch(endpoint, {
615
- ...requestInit,
616
- method: requestInit?.method || "POST"
617
- });
618
- if (initialResponse.status !== 402) {
619
- return initialResponse;
620
- }
621
- const rawResponse = await initialResponse.json();
622
- const IGNORED_ERRORS = [
623
- "X-PAYMENT header is required",
624
- "missing X-PAYMENT header",
625
- "payment_required"
626
- ];
627
- if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
628
- console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
629
- const ERROR_MESSAGES = {
630
- "insufficient_funds": "Insufficient balance to complete this payment",
631
- "invalid_signature": "Invalid payment signature",
632
- "expired": "Payment authorization has expired",
633
- "already_used": "This payment has already been used",
634
- "network_mismatch": "Payment network does not match",
635
- "invalid_payment": "Invalid payment data",
636
- "verification_failed": "Payment verification failed"
637
- };
638
- const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
639
- const error = new Error(errorMessage);
640
- throw wrapPaymentError(error);
641
- }
642
- const x402Version = rawResponse.x402Version;
643
- const parsedPaymentRequirements = rawResponse.accepts || [];
644
- const selectedRequirements = parsedPaymentRequirements.find(
645
- (req) => req.scheme === "exact" && EvmNetworkSchema.safeParse(req.network.toLowerCase()).success
646
- );
647
- if (!selectedRequirements) {
648
- console.error(
649
- "\u274C No suitable EVM payment requirements found. Available networks:",
650
- parsedPaymentRequirements.map((req) => req.network)
651
- );
652
- throw new Error("No suitable EVM payment requirements found");
653
- }
654
- if (maxPaymentAmount && maxPaymentAmount > BigInt(0)) {
655
- if (BigInt(selectedRequirements.maxAmountRequired) > maxPaymentAmount) {
656
- throw new Error(
657
- `Payment amount ${selectedRequirements.maxAmountRequired} exceeds maximum allowed ${maxPaymentAmount}`
658
- );
659
- }
660
- }
661
- const targetChainId = getChainIdFromNetwork(selectedRequirements.network);
662
- let currentChainId;
663
- if (wallet.getChainId) {
664
- try {
665
- const chainIdHex = await wallet.getChainId();
666
- currentChainId = parseInt(chainIdHex, 16);
667
- console.log(`\u{1F4CD} Current wallet chain: ${currentChainId}`);
668
- } catch (error) {
669
- console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
670
- }
671
- }
672
- const networkNames = {
673
- 1: "Ethereum Mainnet",
674
- 11155111: "Sepolia Testnet",
675
- 8453: "Base Mainnet",
676
- 84532: "Base Sepolia Testnet",
677
- 137: "Polygon Mainnet",
678
- 42161: "Arbitrum One",
679
- 10: "Optimism Mainnet"
680
- };
681
- if (currentChainId && currentChainId !== targetChainId) {
682
- if (!wallet.switchChain) {
683
- const currentNetworkName = networkNames[currentChainId] || `Chain ${currentChainId}`;
684
- const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
685
- const error = new Error(
686
- `Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
687
- );
688
- throw wrapPaymentError(error);
689
- }
690
- try {
691
- console.log(`\u{1F504} Switching to chain ${targetChainId}...`);
692
- await wallet.switchChain(`0x${targetChainId.toString(16)}`);
693
- console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
694
- } catch (error) {
695
- console.error("\u274C Failed to switch chain:", error);
696
- const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
697
- const wrappedError = wrapPaymentError(error);
698
- let finalError;
699
- if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
700
- finalError = new PaymentOperationError({
701
- code: wrappedError.code,
702
- message: wrappedError.message,
703
- userMessage: `You rejected the network switch request. Please switch to ${targetNetworkName} manually.`,
704
- originalError: wrappedError.originalError
705
- });
706
- } else {
707
- finalError = new PaymentOperationError({
708
- code: "NETWORK_SWITCH_FAILED" /* NETWORK_SWITCH_FAILED */,
709
- message: wrappedError.message,
710
- userMessage: `Failed to switch to ${targetNetworkName}. Please switch manually in your wallet.`,
711
- originalError: wrappedError.originalError
712
- });
713
- }
714
- throw finalError;
715
- }
716
- } else if (wallet.switchChain && !currentChainId) {
717
- try {
718
- console.log(`\u{1F504} Attempting to switch to chain ${targetChainId}...`);
719
- await wallet.switchChain(`0x${targetChainId.toString(16)}`);
720
- console.log(`\u2705 Switch attempted successfully`);
721
- } catch (error) {
722
- console.warn("\u26A0\uFE0F Failed to switch chain (best effort):", error);
723
- }
724
- }
725
- let paymentHeader;
726
- try {
727
- paymentHeader = await createEvmPaymentHeader({
728
- wallet,
729
- paymentRequirements: selectedRequirements,
730
- x402Version,
731
- chainId: targetChainId
732
- });
733
- } catch (error) {
734
- console.error("\u274C Failed to create payment header:", error);
735
- throw wrapPaymentError(error);
736
- }
737
- const newInit = {
738
- ...requestInit,
739
- method: requestInit?.method || "POST",
740
- headers: {
741
- ...requestInit?.headers || {},
742
- "X-PAYMENT": paymentHeader,
743
- "Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
744
- }
745
- };
746
- const retryResponse = await fetch(endpoint, newInit);
747
- if (retryResponse.status === 402) {
748
- try {
749
- const retryData = await retryResponse.json();
750
- const IGNORED_ERRORS2 = [
751
- "X-PAYMENT header is required",
752
- "missing X-PAYMENT header",
753
- "payment_required"
754
- ];
755
- if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
756
- console.error(`\u274C Payment verification failed: ${retryData.error}`);
757
- const ERROR_MESSAGES = {
758
- "insufficient_funds": "Insufficient balance to complete this payment",
759
- "invalid_signature": "Invalid payment signature",
760
- "expired": "Payment authorization has expired",
761
- "already_used": "This payment has already been used",
762
- "network_mismatch": "Payment network does not match",
763
- "invalid_payment": "Invalid payment data",
764
- "verification_failed": "Payment verification failed"
765
- };
766
- const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
767
- const error = new Error(errorMessage);
768
- throw wrapPaymentError(error);
769
- }
770
- } catch (error) {
771
- if (error instanceof PaymentOperationError) {
772
- throw error;
773
- }
774
- console.warn("\u26A0\uFE0F Could not parse retry 402 response:", error);
775
- }
776
- }
777
- return retryResponse;
778
- }
779
277
 
780
278
  // src/utils/payment-helpers.ts
781
279
  var import_ethers2 = require("ethers");
@@ -802,75 +300,6 @@ function getSupportedNetworkTypes(paymentRequirements) {
802
300
  });
803
301
  return Array.from(networkTypes);
804
302
  }
805
- async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
806
- endpoint = `${endpoint}/${merchantId}`;
807
- let response;
808
- if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
809
- const solana = window.solana;
810
- if (!solana) {
811
- throw new Error("\u8BF7\u5B89\u88C5 Phantom \u94B1\u5305");
812
- }
813
- if (!solana.isConnected) {
814
- await solana.connect();
815
- }
816
- response = await handleSvmPayment(endpoint, {
817
- wallet: solana,
818
- network: "solana"
819
- // Will use backend's network configuration
820
- });
821
- } else if (networkType === "evm" /* EVM */) {
822
- if (!window.ethereum) {
823
- throw new Error("\u8BF7\u5B89\u88C5 MetaMask \u94B1\u5305");
824
- }
825
- const provider = new import_ethers2.ethers.BrowserProvider(window.ethereum);
826
- const signer = await provider.getSigner();
827
- const wallet = {
828
- address: await signer.getAddress(),
829
- signTypedData: async (domain, types, message) => {
830
- return await signer.signTypedData(domain, types, message);
831
- },
832
- // Get current chain ID from wallet
833
- getChainId: async () => {
834
- const network = await provider.getNetwork();
835
- return `0x${network.chainId.toString(16)}`;
836
- },
837
- // Switch to a different chain
838
- switchChain: async (chainId) => {
839
- await window.ethereum.request({
840
- method: "wallet_switchEthereumChain",
841
- params: [{ chainId }]
842
- });
843
- }
844
- };
845
- response = await handleEvmPayment(endpoint, {
846
- wallet,
847
- network: "base"
848
- // Will use backend's network configuration
849
- });
850
- } else {
851
- throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
852
- }
853
- return response;
854
- }
855
- async function handlePayment(endpoint, networkType, callbacks) {
856
- try {
857
- callbacks?.onStart?.();
858
- const response = await makePayment(networkType, "", endpoint);
859
- if (!response.ok) {
860
- const errorText = await response.text();
861
- throw new Error(`\u8BF7\u6C42\u5931\u8D25 (${response.status}): ${errorText}`);
862
- }
863
- const result = await response.json();
864
- callbacks?.onSuccess?.(result);
865
- return result;
866
- } catch (err) {
867
- const errorMessage = err.message || "\u652F\u4ED8\u5931\u8D25";
868
- callbacks?.onError?.(errorMessage);
869
- throw err;
870
- } finally {
871
- callbacks?.onFinish?.();
872
- }
873
- }
874
303
 
875
304
  // src/utils/network.ts
876
305
  var NETWORK_TYPE_MAP = {
@@ -894,6 +323,7 @@ var NETWORK_TYPE_MAP = {
894
323
  };
895
324
  function getNetworkDisplayName(network) {
896
325
  const displayNames = {
326
+ "evm": "EVM",
897
327
  "ethereum": "Ethereum",
898
328
  "sepolia": "Sepolia Testnet",
899
329
  "base": "Base",
@@ -908,138 +338,6 @@ function getNetworkDisplayName(network) {
908
338
  return displayNames[network.toLowerCase()] || network;
909
339
  }
910
340
 
911
- // src/utils/payment-error-handler.ts
912
- function parsePaymentError(error) {
913
- if (!error) {
914
- return {
915
- code: "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
916
- message: "Unknown error occurred",
917
- userMessage: "An unknown error occurred. Please try again.",
918
- originalError: error
919
- };
920
- }
921
- const errorMessage = error.message || error.toString();
922
- const errorCode = error.code;
923
- if (errorCode === 4001 || errorCode === "ACTION_REJECTED" || errorMessage.includes("User rejected") || errorMessage.includes("user rejected") || errorMessage.includes("User denied") || errorMessage.includes("user denied") || errorMessage.includes("ethers-user-denied")) {
924
- return {
925
- code: "USER_REJECTED" /* USER_REJECTED */,
926
- message: "User rejected the transaction",
927
- userMessage: "You rejected the signature request. Please try again if you want to proceed.",
928
- originalError: error
929
- };
930
- }
931
- if (errorMessage.includes("chainId") && (errorMessage.includes("must match") || errorMessage.includes("does not match"))) {
932
- const match = errorMessage.match(/chainId.*?"(\d+)".*?active.*?"(\d+)"/i) || errorMessage.match(/chain (\d+).*?chain (\d+)/i);
933
- if (match) {
934
- const [, requestedChain, activeChain] = match;
935
- return {
936
- code: "CHAIN_ID_MISMATCH" /* CHAIN_ID_MISMATCH */,
937
- message: `Network mismatch (wallet is on different chain): Requested ${requestedChain}, but wallet is on ${activeChain}`,
938
- userMessage: `Your wallet is on the wrong network. Please switch to the correct network and try again.`,
939
- originalError: error
940
- };
941
- }
942
- return {
943
- code: "CHAIN_ID_MISMATCH" /* CHAIN_ID_MISMATCH */,
944
- message: "Network mismatch (wallet selected network does not match)",
945
- userMessage: "Your wallet is on the wrong network. Please switch to the correct network.",
946
- originalError: error
947
- };
948
- }
949
- if (errorMessage.includes("Network mismatch") || errorMessage.includes("Wrong network") || errorMessage.includes("Incorrect network")) {
950
- return {
951
- code: "NETWORK_MISMATCH" /* NETWORK_MISMATCH */,
952
- message: errorMessage,
953
- userMessage: "Please switch your wallet to the correct network.",
954
- originalError: error
955
- };
956
- }
957
- if (errorMessage.includes("locked") || errorMessage.includes("Wallet is locked")) {
958
- return {
959
- code: "WALLET_LOCKED" /* WALLET_LOCKED */,
960
- message: "Wallet is locked",
961
- userMessage: "Please unlock your wallet and try again.",
962
- originalError: error
963
- };
964
- }
965
- if (errorMessage.includes("insufficient") && (errorMessage.includes("balance") || errorMessage.includes("funds"))) {
966
- return {
967
- code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */,
968
- message: "Insufficient balance",
969
- userMessage: "You don't have enough balance to complete this payment.",
970
- originalError: error
971
- };
972
- }
973
- if (errorMessage.includes("Failed to switch") || errorMessage.includes("switch chain")) {
974
- return {
975
- code: "NETWORK_SWITCH_FAILED" /* NETWORK_SWITCH_FAILED */,
976
- message: errorMessage,
977
- userMessage: "Failed to switch network. Please switch manually in your wallet.",
978
- originalError: error
979
- };
980
- }
981
- if (errorMessage.includes("not connected") || errorMessage.includes("No wallet") || errorMessage.includes("Connect wallet")) {
982
- return {
983
- code: "WALLET_NOT_CONNECTED" /* WALLET_NOT_CONNECTED */,
984
- message: "Wallet not connected",
985
- userMessage: "Please connect your wallet first.",
986
- originalError: error
987
- };
988
- }
989
- if (errorMessage.includes("No suitable") || errorMessage.includes("payment requirements") || errorMessage.includes("Missing payTo") || errorMessage.includes("Missing asset")) {
990
- return {
991
- code: "INVALID_PAYMENT_REQUIREMENTS" /* INVALID_PAYMENT_REQUIREMENTS */,
992
- message: errorMessage,
993
- userMessage: "Invalid payment configuration. Please contact support.",
994
- originalError: error
995
- };
996
- }
997
- if (errorMessage.includes("exceeds maximum")) {
998
- return {
999
- code: "AMOUNT_EXCEEDED" /* AMOUNT_EXCEEDED */,
1000
- message: errorMessage,
1001
- userMessage: "Payment amount exceeds the maximum allowed.",
1002
- originalError: error
1003
- };
1004
- }
1005
- if (errorMessage.includes("signature") || errorMessage.includes("sign") || errorCode === "UNKNOWN_ERROR") {
1006
- return {
1007
- code: "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
1008
- message: errorMessage,
1009
- userMessage: "Failed to sign the transaction. Please try again.",
1010
- originalError: error
1011
- };
1012
- }
1013
- return {
1014
- code: "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
1015
- message: errorMessage,
1016
- userMessage: "An unexpected error occurred. Please try again or contact support.",
1017
- originalError: error
1018
- };
1019
- }
1020
- var PaymentOperationError = class _PaymentOperationError extends Error {
1021
- constructor(paymentError) {
1022
- super(paymentError.message);
1023
- this.name = "PaymentOperationError";
1024
- this.code = paymentError.code;
1025
- this.userMessage = paymentError.userMessage;
1026
- this.originalError = paymentError.originalError;
1027
- if (Error.captureStackTrace) {
1028
- Error.captureStackTrace(this, _PaymentOperationError);
1029
- }
1030
- }
1031
- /**
1032
- * Get a formatted error message for logging
1033
- */
1034
- toLogString() {
1035
- return `[${this.code}] ${this.message} | User Message: ${this.userMessage}`;
1036
- }
1037
- };
1038
- function wrapPaymentError(error) {
1039
- const parsedError = parsePaymentError(error);
1040
- return new PaymentOperationError(parsedError);
1041
- }
1042
-
1043
341
  // src/react/store/walletStore.ts
1044
342
  var WalletStore = class {
1045
343
  constructor() {
@@ -1218,7 +516,7 @@ function usePayment() {
1218
516
 
1219
517
  // src/react/hooks/usePaymentInfo.ts
1220
518
  var import_react3 = require("react");
1221
- function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
519
+ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL, additionalParams) {
1222
520
  const [paymentInfo, setPaymentInfo] = (0, import_react3.useState)(null);
1223
521
  const [supportedNetworks, setSupportedNetworks] = (0, import_react3.useState)([]);
1224
522
  const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
@@ -1227,8 +525,17 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
1227
525
  setIsLoading(true);
1228
526
  setError(null);
1229
527
  try {
1230
- endpoint = `${endpoint}/${merchantId}`;
1231
- const response = await fetch(endpoint, { method: "POST" });
528
+ const fullEndpoint = `${endpoint}/${merchantId}`;
529
+ const requestInit = {
530
+ method: "POST",
531
+ ...additionalParams && Object.keys(additionalParams).length > 0 ? {
532
+ body: JSON.stringify(additionalParams),
533
+ headers: {
534
+ "Content-Type": "application/json"
535
+ }
536
+ } : {}
537
+ };
538
+ const response = await fetch(fullEndpoint, requestInit);
1232
539
  if (response.status === 402) {
1233
540
  const body = await response.json();
1234
541
  const payment = parsePaymentRequired(body);
@@ -1249,7 +556,7 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
1249
556
  };
1250
557
  (0, import_react3.useEffect)(() => {
1251
558
  fetchPaymentInfo();
1252
- }, [endpoint]);
559
+ }, [endpoint, merchantId]);
1253
560
  return {
1254
561
  paymentInfo,
1255
562
  supportedNetworks,
@@ -1352,12 +659,21 @@ var baseButtonStyle = {
1352
659
  };
1353
660
  var getConnectButtonStyle = (isDisabled, isHovered) => {
1354
661
  const c = getColors();
662
+ const darkMode = isDarkMode();
663
+ if (isDisabled) {
664
+ return {
665
+ ...baseButtonStyle,
666
+ background: c.disabled,
667
+ color: c.disabledText,
668
+ cursor: "not-allowed",
669
+ border: darkMode ? "1px solid #404040" : "1px solid #d4d4d4"
670
+ };
671
+ }
1355
672
  return {
1356
673
  ...baseButtonStyle,
1357
- background: isDisabled ? c.disabled : isHovered ? c.primaryHover : c.primary,
1358
- color: isDarkMode() ? "#000000" : "#ffffff",
1359
- cursor: isDisabled ? "not-allowed" : "pointer",
1360
- opacity: isDisabled ? 0.5 : 1
674
+ background: isHovered ? c.primaryHover : c.primary,
675
+ color: darkMode ? "#000000" : "#ffffff",
676
+ cursor: "pointer"
1361
677
  };
1362
678
  };
1363
679
  var getDisconnectButtonStyle = (isHovered) => {
@@ -1368,17 +684,6 @@ var getDisconnectButtonStyle = (isHovered) => {
1368
684
  color: "#ffffff"
1369
685
  };
1370
686
  };
1371
- var getPayButtonStyle = (isDisabled, isHovered) => {
1372
- const c = getColors();
1373
- return {
1374
- ...baseButtonStyle,
1375
- background: isDisabled ? c.disabled : isHovered ? c.successHover : c.success,
1376
- color: "#ffffff",
1377
- width: "100%",
1378
- cursor: isDisabled ? "not-allowed" : "pointer",
1379
- opacity: isDisabled ? 0.5 : 1
1380
- };
1381
- };
1382
687
  var getInstallLinkStyle = (isHovered) => {
1383
688
  const c = getColors();
1384
689
  return {
@@ -1463,7 +768,7 @@ function WalletConnect({
1463
768
  disconnect();
1464
769
  onDisconnect?.();
1465
770
  };
1466
- return /* @__PURE__ */ import_react4.default.createElement("div", { style: { ...containerStyle, ...className ? {} : {} }, className }, !address ? /* @__PURE__ */ import_react4.default.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ import_react4.default.createElement("h3", { style: getTitleStyle() }, "Connect Wallet"), supportedNetworks.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "No payment required") : /* @__PURE__ */ import_react4.default.createElement("div", { style: buttonsContainerStyle }, supportedNetworks.map((network) => {
771
+ return /* @__PURE__ */ import_react4.default.createElement("div", { style: { ...containerStyle, ...className ? {} : {} }, className }, !address ? /* @__PURE__ */ import_react4.default.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ import_react4.default.createElement("h3", { style: getTitleStyle() }, "Connect Wallet"), supportedNetworks.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "Please install a supported wallet extension") : /* @__PURE__ */ import_react4.default.createElement("div", { style: buttonsContainerStyle }, supportedNetworks.map((network) => {
1467
772
  const installed = isWalletInstalled(network);
1468
773
  return /* @__PURE__ */ import_react4.default.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ import_react4.default.createElement(
1469
774
  "button",
@@ -1498,62 +803,8 @@ function WalletConnect({
1498
803
  "Disconnect"
1499
804
  )), /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "Switch account in your wallet to change address")));
1500
805
  }
1501
-
1502
- // src/react/components/PaymentButton.tsx
1503
- var import_react5 = __toESM(require("react"));
1504
- function PaymentButton({
1505
- endpoint,
1506
- className = "",
1507
- disabled = false,
1508
- onSuccess,
1509
- onError,
1510
- onStart,
1511
- onFinish,
1512
- children = "Pay Now"
1513
- }) {
1514
- const { networkType } = useWallet();
1515
- const { isProcessing, setIsProcessing, setResult, setError, error } = usePayment();
1516
- const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
1517
- const handleClick = async () => {
1518
- if (!networkType) {
1519
- const errorMsg = "Please connect wallet first";
1520
- setError(errorMsg);
1521
- onError?.(errorMsg);
1522
- return;
1523
- }
1524
- try {
1525
- onStart?.();
1526
- setIsProcessing(true);
1527
- setError(null);
1528
- const result = await handlePayment(endpoint, networkType);
1529
- setResult(result);
1530
- onSuccess?.(result);
1531
- } catch (err) {
1532
- const errorMsg = err.message || "Payment failed";
1533
- setError(errorMsg);
1534
- onError?.(errorMsg);
1535
- } finally {
1536
- setIsProcessing(false);
1537
- onFinish?.();
1538
- }
1539
- };
1540
- const isDisabled = disabled || isProcessing || !networkType;
1541
- return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(
1542
- "button",
1543
- {
1544
- style: getPayButtonStyle(isDisabled, isHovered),
1545
- className,
1546
- onClick: handleClick,
1547
- disabled: isDisabled,
1548
- onMouseEnter: () => setIsHovered(true),
1549
- onMouseLeave: () => setIsHovered(false)
1550
- },
1551
- isProcessing ? "Processing..." : children
1552
- ), error && /* @__PURE__ */ import_react5.default.createElement("p", { style: getErrorStyle() }, error));
1553
- }
1554
806
  // Annotate the CommonJS export names for ESM import in node:
1555
807
  0 && (module.exports = {
1556
- PaymentButton,
1557
808
  WalletConnect,
1558
809
  usePayment,
1559
810
  usePaymentInfo,