@voyage_ai/v402-web-ts 0.1.0 → 0.1.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/README.md +639 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +585 -223
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +585 -223
- package/dist/index.mjs.map +1 -1
- package/dist/react/components/PaymentButton.tsx +12 -5
- package/dist/react/components/WalletConnect.tsx +47 -20
- package/dist/react/hooks/useWallet.ts +35 -8
- package/dist/react/index.d.mts +2 -2
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +653 -33
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +655 -35
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/store/walletStore.ts +29 -0
- package/dist/react/styles/inline-styles.ts +227 -0
- package/dist/react/styles.css +8 -1
- package/package.json +8 -6
package/dist/react/index.js
CHANGED
|
@@ -45,7 +45,7 @@ var import_react = require("react");
|
|
|
45
45
|
var import_types3 = require("x402/types");
|
|
46
46
|
|
|
47
47
|
// src/types/common.ts
|
|
48
|
-
var PROD_BACK_URL = "https://v402.onvoyage.ai/api";
|
|
48
|
+
var PROD_BACK_URL = "https://v402.onvoyage.ai/api/pay";
|
|
49
49
|
|
|
50
50
|
// src/types/svm.ts
|
|
51
51
|
var import_zod = require("zod");
|
|
@@ -234,6 +234,40 @@ function onAccountsChanged(callback) {
|
|
|
234
234
|
ethereum.removeListener?.("accountsChanged", handler);
|
|
235
235
|
};
|
|
236
236
|
}
|
|
237
|
+
function onChainChanged(callback) {
|
|
238
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
239
|
+
return () => {
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
const ethereum = window.ethereum;
|
|
243
|
+
const handler = (chainId) => {
|
|
244
|
+
console.log("\u{1F504} Chain changed to:", chainId);
|
|
245
|
+
callback(chainId);
|
|
246
|
+
};
|
|
247
|
+
ethereum.on("chainChanged", handler);
|
|
248
|
+
return () => {
|
|
249
|
+
ethereum.removeListener?.("chainChanged", handler);
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function onWalletDisconnect(callback) {
|
|
253
|
+
if (typeof window === "undefined") {
|
|
254
|
+
return () => {
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const solana = window.solana;
|
|
258
|
+
if (!solana) {
|
|
259
|
+
return () => {
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const handler = () => {
|
|
263
|
+
console.log("\u{1F50C} Solana wallet disconnected");
|
|
264
|
+
callback();
|
|
265
|
+
};
|
|
266
|
+
solana.on("disconnect", handler);
|
|
267
|
+
return () => {
|
|
268
|
+
solana.removeListener?.("disconnect", handler);
|
|
269
|
+
};
|
|
270
|
+
}
|
|
237
271
|
|
|
238
272
|
// src/services/svm/payment-header.ts
|
|
239
273
|
var import_web3 = require("@solana/web3.js");
|
|
@@ -322,7 +356,14 @@ async function createSvmPaymentHeader(params) {
|
|
|
322
356
|
if (typeof wallet?.signTransaction !== "function") {
|
|
323
357
|
throw new Error("Connected wallet does not support signTransaction");
|
|
324
358
|
}
|
|
325
|
-
|
|
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
|
+
}
|
|
326
367
|
const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
|
|
327
368
|
const paymentPayload = {
|
|
328
369
|
x402Version,
|
|
@@ -338,7 +379,7 @@ async function createSvmPaymentHeader(params) {
|
|
|
338
379
|
function getDefaultSolanaRpcUrl(network) {
|
|
339
380
|
const normalized = network.toLowerCase();
|
|
340
381
|
if (normalized === "solana" || normalized === "solana-mainnet") {
|
|
341
|
-
return "https://
|
|
382
|
+
return "https://cathee-fu8ezd-fast-mainnet.helius-rpc.com";
|
|
342
383
|
} else if (normalized === "solana-devnet") {
|
|
343
384
|
return "https://api.devnet.solana.com";
|
|
344
385
|
}
|
|
@@ -356,6 +397,26 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
356
397
|
return initialResponse;
|
|
357
398
|
}
|
|
358
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
|
+
}
|
|
359
420
|
const x402Version = rawResponse.x402Version;
|
|
360
421
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
361
422
|
const selectedRequirements = parsedPaymentRequirements.find(
|
|
@@ -375,13 +436,22 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
375
436
|
);
|
|
376
437
|
}
|
|
377
438
|
}
|
|
378
|
-
const effectiveRpcUrl = rpcUrl || getDefaultSolanaRpcUrl(network);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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
|
+
}
|
|
385
455
|
const newInit = {
|
|
386
456
|
...requestInit,
|
|
387
457
|
method: requestInit?.method || "POST",
|
|
@@ -391,7 +461,38 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
391
461
|
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
392
462
|
}
|
|
393
463
|
};
|
|
394
|
-
|
|
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;
|
|
395
496
|
}
|
|
396
497
|
|
|
397
498
|
// src/services/evm/payment-header.ts
|
|
@@ -404,6 +505,34 @@ async function createEvmPaymentHeader(params) {
|
|
|
404
505
|
if (!paymentRequirements?.asset) {
|
|
405
506
|
throw new Error("Missing asset (token contract) in payment requirements");
|
|
406
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
|
+
}
|
|
407
536
|
const now = Math.floor(Date.now() / 1e3);
|
|
408
537
|
const nonceBytes = import_ethers.ethers.randomBytes(32);
|
|
409
538
|
const nonceBytes32 = import_ethers.ethers.hexlify(nonceBytes);
|
|
@@ -432,7 +561,14 @@ async function createEvmPaymentHeader(params) {
|
|
|
432
561
|
validBefore: String(now + (paymentRequirements.maxTimeoutSeconds || 3600)),
|
|
433
562
|
nonce: nonceBytes32
|
|
434
563
|
};
|
|
435
|
-
|
|
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
|
+
}
|
|
436
572
|
const headerPayload = {
|
|
437
573
|
x402_version: x402Version,
|
|
438
574
|
x402Version,
|
|
@@ -483,6 +619,26 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
483
619
|
return initialResponse;
|
|
484
620
|
}
|
|
485
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
|
+
}
|
|
486
642
|
const x402Version = rawResponse.x402Version;
|
|
487
643
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
488
644
|
const selectedRequirements = parsedPaymentRequirements.find(
|
|
@@ -503,19 +659,81 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
503
659
|
}
|
|
504
660
|
}
|
|
505
661
|
const targetChainId = getChainIdFromNetwork(selectedRequirements.network);
|
|
506
|
-
|
|
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) {
|
|
507
717
|
try {
|
|
718
|
+
console.log(`\u{1F504} Attempting to switch to chain ${targetChainId}...`);
|
|
508
719
|
await wallet.switchChain(`0x${targetChainId.toString(16)}`);
|
|
720
|
+
console.log(`\u2705 Switch attempted successfully`);
|
|
509
721
|
} catch (error) {
|
|
510
|
-
console.warn("Failed to switch chain:", error);
|
|
722
|
+
console.warn("\u26A0\uFE0F Failed to switch chain (best effort):", error);
|
|
511
723
|
}
|
|
512
724
|
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
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
|
+
}
|
|
519
737
|
const newInit = {
|
|
520
738
|
...requestInit,
|
|
521
739
|
method: requestInit?.method || "POST",
|
|
@@ -525,7 +743,38 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
525
743
|
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
526
744
|
}
|
|
527
745
|
};
|
|
528
|
-
|
|
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;
|
|
529
778
|
}
|
|
530
779
|
|
|
531
780
|
// src/utils/payment-helpers.ts
|
|
@@ -566,7 +815,8 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
|
|
|
566
815
|
}
|
|
567
816
|
response = await handleSvmPayment(endpoint, {
|
|
568
817
|
wallet: solana,
|
|
569
|
-
network: "solana
|
|
818
|
+
network: "solana"
|
|
819
|
+
// Will use backend's network configuration
|
|
570
820
|
});
|
|
571
821
|
} else if (networkType === "evm" /* EVM */) {
|
|
572
822
|
if (!window.ethereum) {
|
|
@@ -578,12 +828,24 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
|
|
|
578
828
|
address: await signer.getAddress(),
|
|
579
829
|
signTypedData: async (domain, types, message) => {
|
|
580
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
|
+
});
|
|
581
843
|
}
|
|
582
844
|
};
|
|
583
|
-
const network = endpoint.includes("sepolia") ? "base-sepolia" : "base";
|
|
584
845
|
response = await handleEvmPayment(endpoint, {
|
|
585
846
|
wallet,
|
|
586
|
-
network
|
|
847
|
+
network: "base"
|
|
848
|
+
// Will use backend's network configuration
|
|
587
849
|
});
|
|
588
850
|
} else {
|
|
589
851
|
throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
|
|
@@ -646,6 +908,138 @@ function getNetworkDisplayName(network) {
|
|
|
646
908
|
return displayNames[network.toLowerCase()] || network;
|
|
647
909
|
}
|
|
648
910
|
|
|
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
|
+
|
|
649
1043
|
// src/react/store/walletStore.ts
|
|
650
1044
|
var WalletStore = class {
|
|
651
1045
|
constructor() {
|
|
@@ -677,6 +1071,29 @@ var WalletStore = class {
|
|
|
677
1071
|
}
|
|
678
1072
|
}
|
|
679
1073
|
});
|
|
1074
|
+
onChainChanged(() => {
|
|
1075
|
+
const connectedType = getConnectedNetworkType();
|
|
1076
|
+
if (connectedType === "evm" /* EVM */) {
|
|
1077
|
+
console.log("\u26A0\uFE0F Network changed detected - disconnecting wallet");
|
|
1078
|
+
disconnectWallet();
|
|
1079
|
+
this.setState({
|
|
1080
|
+
address: null,
|
|
1081
|
+
networkType: null,
|
|
1082
|
+
error: "Network changed. Please reconnect your wallet."
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
});
|
|
1086
|
+
onWalletDisconnect(() => {
|
|
1087
|
+
const connectedType = getConnectedNetworkType();
|
|
1088
|
+
if (connectedType === "solana" /* SOLANA */ || connectedType === "svm" /* SVM */) {
|
|
1089
|
+
console.log("\u26A0\uFE0F Solana wallet disconnected");
|
|
1090
|
+
disconnectWallet();
|
|
1091
|
+
this.setState({
|
|
1092
|
+
address: null,
|
|
1093
|
+
networkType: null
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
});
|
|
680
1097
|
}
|
|
681
1098
|
async autoReconnect() {
|
|
682
1099
|
if (!isWalletManuallyDisconnected()) {
|
|
@@ -844,6 +1261,189 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
844
1261
|
|
|
845
1262
|
// src/react/components/WalletConnect.tsx
|
|
846
1263
|
var import_react4 = __toESM(require("react"));
|
|
1264
|
+
|
|
1265
|
+
// src/react/styles/inline-styles.ts
|
|
1266
|
+
var isDarkMode = () => {
|
|
1267
|
+
if (typeof window === "undefined") return false;
|
|
1268
|
+
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
|
|
1269
|
+
};
|
|
1270
|
+
var colors = {
|
|
1271
|
+
// Light mode
|
|
1272
|
+
light: {
|
|
1273
|
+
background: "#fafafa",
|
|
1274
|
+
cardBg: "#ffffff",
|
|
1275
|
+
text: "#0a0a0a",
|
|
1276
|
+
textSecondary: "#737373",
|
|
1277
|
+
primary: "#000000",
|
|
1278
|
+
primaryHover: "#262626",
|
|
1279
|
+
danger: "#ef4444",
|
|
1280
|
+
dangerHover: "#dc2626",
|
|
1281
|
+
success: "#10b981",
|
|
1282
|
+
successHover: "#059669",
|
|
1283
|
+
disabled: "#e5e5e5",
|
|
1284
|
+
disabledText: "#a3a3a3",
|
|
1285
|
+
errorBg: "#fef2f2",
|
|
1286
|
+
errorText: "#dc2626"
|
|
1287
|
+
},
|
|
1288
|
+
// Dark mode
|
|
1289
|
+
dark: {
|
|
1290
|
+
background: "#0a0a0a",
|
|
1291
|
+
cardBg: "#171717",
|
|
1292
|
+
text: "#fafafa",
|
|
1293
|
+
textSecondary: "#a3a3a3",
|
|
1294
|
+
primary: "#ffffff",
|
|
1295
|
+
primaryHover: "#e5e5e5",
|
|
1296
|
+
danger: "#f87171",
|
|
1297
|
+
dangerHover: "#ef4444",
|
|
1298
|
+
success: "#34d399",
|
|
1299
|
+
successHover: "#10b981",
|
|
1300
|
+
disabled: "#262626",
|
|
1301
|
+
disabledText: "#525252",
|
|
1302
|
+
errorBg: "#1c1917",
|
|
1303
|
+
errorText: "#f87171"
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
var getColors = () => {
|
|
1307
|
+
return isDarkMode() ? colors.dark : colors.light;
|
|
1308
|
+
};
|
|
1309
|
+
var containerStyle = {
|
|
1310
|
+
width: "100%",
|
|
1311
|
+
maxWidth: "420px",
|
|
1312
|
+
margin: "0 auto"
|
|
1313
|
+
};
|
|
1314
|
+
var getSectionStyle = () => {
|
|
1315
|
+
const c = getColors();
|
|
1316
|
+
return {
|
|
1317
|
+
padding: "1.5rem",
|
|
1318
|
+
background: c.cardBg,
|
|
1319
|
+
borderRadius: "12px"
|
|
1320
|
+
};
|
|
1321
|
+
};
|
|
1322
|
+
var getTitleStyle = () => {
|
|
1323
|
+
const c = getColors();
|
|
1324
|
+
return {
|
|
1325
|
+
margin: "0 0 1.25rem 0",
|
|
1326
|
+
fontSize: "1.125rem",
|
|
1327
|
+
fontWeight: 600,
|
|
1328
|
+
color: c.text,
|
|
1329
|
+
letterSpacing: "-0.01em"
|
|
1330
|
+
};
|
|
1331
|
+
};
|
|
1332
|
+
var buttonsContainerStyle = {
|
|
1333
|
+
display: "flex",
|
|
1334
|
+
flexDirection: "column",
|
|
1335
|
+
gap: "0.75rem"
|
|
1336
|
+
};
|
|
1337
|
+
var walletOptionStyle = {
|
|
1338
|
+
display: "flex",
|
|
1339
|
+
flexDirection: "column",
|
|
1340
|
+
gap: "0.5rem"
|
|
1341
|
+
};
|
|
1342
|
+
var baseButtonStyle = {
|
|
1343
|
+
padding: "0.875rem 1.25rem",
|
|
1344
|
+
fontSize: "0.9375rem",
|
|
1345
|
+
fontWeight: 500,
|
|
1346
|
+
border: "none",
|
|
1347
|
+
borderRadius: "8px",
|
|
1348
|
+
cursor: "pointer",
|
|
1349
|
+
transition: "background-color 0.15s ease, opacity 0.15s ease",
|
|
1350
|
+
outline: "none",
|
|
1351
|
+
letterSpacing: "-0.01em"
|
|
1352
|
+
};
|
|
1353
|
+
var getConnectButtonStyle = (isDisabled, isHovered) => {
|
|
1354
|
+
const c = getColors();
|
|
1355
|
+
return {
|
|
1356
|
+
...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
|
|
1361
|
+
};
|
|
1362
|
+
};
|
|
1363
|
+
var getDisconnectButtonStyle = (isHovered) => {
|
|
1364
|
+
const c = getColors();
|
|
1365
|
+
return {
|
|
1366
|
+
...baseButtonStyle,
|
|
1367
|
+
background: isHovered ? c.dangerHover : c.danger,
|
|
1368
|
+
color: "#ffffff"
|
|
1369
|
+
};
|
|
1370
|
+
};
|
|
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
|
+
var getInstallLinkStyle = (isHovered) => {
|
|
1383
|
+
const c = getColors();
|
|
1384
|
+
return {
|
|
1385
|
+
display: "inline-block",
|
|
1386
|
+
padding: "0.5rem",
|
|
1387
|
+
fontSize: "0.8125rem",
|
|
1388
|
+
color: c.textSecondary,
|
|
1389
|
+
textDecoration: isHovered ? "underline" : "none",
|
|
1390
|
+
textAlign: "center",
|
|
1391
|
+
fontWeight: 500
|
|
1392
|
+
};
|
|
1393
|
+
};
|
|
1394
|
+
var walletAddressStyle = {
|
|
1395
|
+
display: "flex",
|
|
1396
|
+
flexDirection: "column",
|
|
1397
|
+
gap: "0.5rem",
|
|
1398
|
+
marginBottom: "1rem"
|
|
1399
|
+
};
|
|
1400
|
+
var getLabelStyle = () => {
|
|
1401
|
+
const c = getColors();
|
|
1402
|
+
return {
|
|
1403
|
+
fontSize: "0.8125rem",
|
|
1404
|
+
color: c.textSecondary,
|
|
1405
|
+
fontWeight: 500,
|
|
1406
|
+
textTransform: "uppercase",
|
|
1407
|
+
letterSpacing: "0.05em"
|
|
1408
|
+
};
|
|
1409
|
+
};
|
|
1410
|
+
var getAddressStyle = () => {
|
|
1411
|
+
const c = getColors();
|
|
1412
|
+
return {
|
|
1413
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
1414
|
+
fontSize: "0.9375rem",
|
|
1415
|
+
fontWeight: 500,
|
|
1416
|
+
color: c.text,
|
|
1417
|
+
letterSpacing: "-0.01em"
|
|
1418
|
+
};
|
|
1419
|
+
};
|
|
1420
|
+
var walletActionsStyle = {
|
|
1421
|
+
margin: "1rem 0"
|
|
1422
|
+
};
|
|
1423
|
+
var getHintStyle = () => {
|
|
1424
|
+
const c = getColors();
|
|
1425
|
+
return {
|
|
1426
|
+
marginTop: "1rem",
|
|
1427
|
+
fontSize: "0.8125rem",
|
|
1428
|
+
color: c.textSecondary,
|
|
1429
|
+
textAlign: "center",
|
|
1430
|
+
lineHeight: "1.5"
|
|
1431
|
+
};
|
|
1432
|
+
};
|
|
1433
|
+
var getErrorStyle = () => {
|
|
1434
|
+
const c = getColors();
|
|
1435
|
+
return {
|
|
1436
|
+
marginTop: "1rem",
|
|
1437
|
+
padding: "0.75rem 1rem",
|
|
1438
|
+
background: c.errorBg,
|
|
1439
|
+
color: c.errorText,
|
|
1440
|
+
borderRadius: "8px",
|
|
1441
|
+
fontSize: "0.8125rem",
|
|
1442
|
+
fontWeight: 500
|
|
1443
|
+
};
|
|
1444
|
+
};
|
|
1445
|
+
|
|
1446
|
+
// src/react/components/WalletConnect.tsx
|
|
847
1447
|
function WalletConnect({
|
|
848
1448
|
supportedNetworks = ["solana" /* SOLANA */, "evm" /* EVM */],
|
|
849
1449
|
className = "",
|
|
@@ -851,6 +1451,8 @@ function WalletConnect({
|
|
|
851
1451
|
onDisconnect
|
|
852
1452
|
}) {
|
|
853
1453
|
const { address, networkType, isConnecting, error, connect, disconnect } = useWallet();
|
|
1454
|
+
const [hoveredButton, setHoveredButton] = (0, import_react4.useState)(null);
|
|
1455
|
+
const [hoveredLink, setHoveredLink] = (0, import_react4.useState)(null);
|
|
854
1456
|
const handleConnect = async (network) => {
|
|
855
1457
|
try {
|
|
856
1458
|
await connect(network);
|
|
@@ -861,14 +1463,16 @@ function WalletConnect({
|
|
|
861
1463
|
disconnect();
|
|
862
1464
|
onDisconnect?.();
|
|
863
1465
|
};
|
|
864
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", {
|
|
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) => {
|
|
865
1467
|
const installed = isWalletInstalled(network);
|
|
866
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", { key: network,
|
|
1468
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ import_react4.default.createElement(
|
|
867
1469
|
"button",
|
|
868
1470
|
{
|
|
869
|
-
|
|
1471
|
+
style: getConnectButtonStyle(isConnecting || !installed, hoveredButton === network),
|
|
870
1472
|
onClick: () => handleConnect(network),
|
|
871
|
-
disabled: isConnecting || !installed
|
|
1473
|
+
disabled: isConnecting || !installed,
|
|
1474
|
+
onMouseEnter: () => setHoveredButton(network),
|
|
1475
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
872
1476
|
},
|
|
873
1477
|
isConnecting ? "Connecting..." : getNetworkDisplayName(network)
|
|
874
1478
|
), !installed && /* @__PURE__ */ import_react4.default.createElement(
|
|
@@ -877,11 +1481,22 @@ function WalletConnect({
|
|
|
877
1481
|
href: getWalletInstallUrl(network),
|
|
878
1482
|
target: "_blank",
|
|
879
1483
|
rel: "noopener noreferrer",
|
|
880
|
-
|
|
1484
|
+
style: getInstallLinkStyle(hoveredLink === network),
|
|
1485
|
+
onMouseEnter: () => setHoveredLink(network),
|
|
1486
|
+
onMouseLeave: () => setHoveredLink(null)
|
|
881
1487
|
},
|
|
882
1488
|
"Install Wallet"
|
|
883
1489
|
));
|
|
884
|
-
})), error && /* @__PURE__ */ import_react4.default.createElement("p", {
|
|
1490
|
+
})), error && /* @__PURE__ */ import_react4.default.createElement("p", { style: getErrorStyle() }, error), /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "To switch accounts, please change it in your wallet extension")) : /* @__PURE__ */ import_react4.default.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ import_react4.default.createElement("div", { style: walletAddressStyle }, /* @__PURE__ */ import_react4.default.createElement("span", { style: getLabelStyle() }, "Connected ", networkType && `(${getNetworkDisplayName(networkType)})`), /* @__PURE__ */ import_react4.default.createElement("span", { style: getAddressStyle() }, formatAddress(address))), /* @__PURE__ */ import_react4.default.createElement("div", { style: walletActionsStyle }, /* @__PURE__ */ import_react4.default.createElement(
|
|
1491
|
+
"button",
|
|
1492
|
+
{
|
|
1493
|
+
style: getDisconnectButtonStyle(hoveredButton === "disconnect"),
|
|
1494
|
+
onClick: handleDisconnect,
|
|
1495
|
+
onMouseEnter: () => setHoveredButton("disconnect"),
|
|
1496
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
1497
|
+
},
|
|
1498
|
+
"Disconnect"
|
|
1499
|
+
)), /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "Switch account in your wallet to change address")));
|
|
885
1500
|
}
|
|
886
1501
|
|
|
887
1502
|
// src/react/components/PaymentButton.tsx
|
|
@@ -898,6 +1513,7 @@ function PaymentButton({
|
|
|
898
1513
|
}) {
|
|
899
1514
|
const { networkType } = useWallet();
|
|
900
1515
|
const { isProcessing, setIsProcessing, setResult, setError, error } = usePayment();
|
|
1516
|
+
const [isHovered, setIsHovered] = (0, import_react5.useState)(false);
|
|
901
1517
|
const handleClick = async () => {
|
|
902
1518
|
if (!networkType) {
|
|
903
1519
|
const errorMsg = "Please connect wallet first";
|
|
@@ -921,15 +1537,19 @@ function PaymentButton({
|
|
|
921
1537
|
onFinish?.();
|
|
922
1538
|
}
|
|
923
1539
|
};
|
|
1540
|
+
const isDisabled = disabled || isProcessing || !networkType;
|
|
924
1541
|
return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(
|
|
925
1542
|
"button",
|
|
926
1543
|
{
|
|
927
|
-
|
|
1544
|
+
style: getPayButtonStyle(isDisabled, isHovered),
|
|
1545
|
+
className,
|
|
928
1546
|
onClick: handleClick,
|
|
929
|
-
disabled:
|
|
1547
|
+
disabled: isDisabled,
|
|
1548
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1549
|
+
onMouseLeave: () => setIsHovered(false)
|
|
930
1550
|
},
|
|
931
1551
|
isProcessing ? "Processing..." : children
|
|
932
|
-
), error && /* @__PURE__ */ import_react5.default.createElement("p", {
|
|
1552
|
+
), error && /* @__PURE__ */ import_react5.default.createElement("p", { style: getErrorStyle() }, error));
|
|
933
1553
|
}
|
|
934
1554
|
// Annotate the CommonJS export names for ESM import in node:
|
|
935
1555
|
0 && (module.exports = {
|