@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.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
} from "x402/types";
|
|
17
17
|
|
|
18
18
|
// src/types/common.ts
|
|
19
|
-
var PROD_BACK_URL = "https://v402.onvoyage.ai/api";
|
|
19
|
+
var PROD_BACK_URL = "https://v402.onvoyage.ai/api/pay";
|
|
20
20
|
|
|
21
21
|
// src/types/svm.ts
|
|
22
22
|
import { z } from "zod";
|
|
@@ -205,6 +205,40 @@ function onAccountsChanged(callback) {
|
|
|
205
205
|
ethereum.removeListener?.("accountsChanged", handler);
|
|
206
206
|
};
|
|
207
207
|
}
|
|
208
|
+
function onChainChanged(callback) {
|
|
209
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
210
|
+
return () => {
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
const ethereum = window.ethereum;
|
|
214
|
+
const handler = (chainId) => {
|
|
215
|
+
console.log("\u{1F504} Chain changed to:", chainId);
|
|
216
|
+
callback(chainId);
|
|
217
|
+
};
|
|
218
|
+
ethereum.on("chainChanged", handler);
|
|
219
|
+
return () => {
|
|
220
|
+
ethereum.removeListener?.("chainChanged", handler);
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function onWalletDisconnect(callback) {
|
|
224
|
+
if (typeof window === "undefined") {
|
|
225
|
+
return () => {
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const solana = window.solana;
|
|
229
|
+
if (!solana) {
|
|
230
|
+
return () => {
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
const handler = () => {
|
|
234
|
+
console.log("\u{1F50C} Solana wallet disconnected");
|
|
235
|
+
callback();
|
|
236
|
+
};
|
|
237
|
+
solana.on("disconnect", handler);
|
|
238
|
+
return () => {
|
|
239
|
+
solana.removeListener?.("disconnect", handler);
|
|
240
|
+
};
|
|
241
|
+
}
|
|
208
242
|
|
|
209
243
|
// src/services/svm/payment-header.ts
|
|
210
244
|
import {
|
|
@@ -305,7 +339,14 @@ async function createSvmPaymentHeader(params) {
|
|
|
305
339
|
if (typeof wallet?.signTransaction !== "function") {
|
|
306
340
|
throw new Error("Connected wallet does not support signTransaction");
|
|
307
341
|
}
|
|
308
|
-
|
|
342
|
+
let userSignedTx;
|
|
343
|
+
try {
|
|
344
|
+
userSignedTx = await wallet.signTransaction(transaction);
|
|
345
|
+
console.log("\u2705 Transaction signed successfully");
|
|
346
|
+
} catch (error) {
|
|
347
|
+
console.error("\u274C Failed to sign transaction:", error);
|
|
348
|
+
throw wrapPaymentError(error);
|
|
349
|
+
}
|
|
309
350
|
const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
|
|
310
351
|
const paymentPayload = {
|
|
311
352
|
x402Version,
|
|
@@ -321,7 +362,7 @@ async function createSvmPaymentHeader(params) {
|
|
|
321
362
|
function getDefaultSolanaRpcUrl(network) {
|
|
322
363
|
const normalized = network.toLowerCase();
|
|
323
364
|
if (normalized === "solana" || normalized === "solana-mainnet") {
|
|
324
|
-
return "https://
|
|
365
|
+
return "https://cathee-fu8ezd-fast-mainnet.helius-rpc.com";
|
|
325
366
|
} else if (normalized === "solana-devnet") {
|
|
326
367
|
return "https://api.devnet.solana.com";
|
|
327
368
|
}
|
|
@@ -339,6 +380,26 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
339
380
|
return initialResponse;
|
|
340
381
|
}
|
|
341
382
|
const rawResponse = await initialResponse.json();
|
|
383
|
+
const IGNORED_ERRORS = [
|
|
384
|
+
"X-PAYMENT header is required",
|
|
385
|
+
"missing X-PAYMENT header",
|
|
386
|
+
"payment_required"
|
|
387
|
+
];
|
|
388
|
+
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
389
|
+
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
390
|
+
const ERROR_MESSAGES = {
|
|
391
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
392
|
+
"invalid_signature": "Invalid payment signature",
|
|
393
|
+
"expired": "Payment authorization has expired",
|
|
394
|
+
"already_used": "This payment has already been used",
|
|
395
|
+
"network_mismatch": "Payment network does not match",
|
|
396
|
+
"invalid_payment": "Invalid payment data",
|
|
397
|
+
"verification_failed": "Payment verification failed"
|
|
398
|
+
};
|
|
399
|
+
const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
400
|
+
const error = new Error(errorMessage);
|
|
401
|
+
throw wrapPaymentError(error);
|
|
402
|
+
}
|
|
342
403
|
const x402Version = rawResponse.x402Version;
|
|
343
404
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
344
405
|
const selectedRequirements = parsedPaymentRequirements.find(
|
|
@@ -358,13 +419,22 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
358
419
|
);
|
|
359
420
|
}
|
|
360
421
|
}
|
|
361
|
-
const effectiveRpcUrl = rpcUrl || getDefaultSolanaRpcUrl(network);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
422
|
+
const effectiveRpcUrl = rpcUrl || getDefaultSolanaRpcUrl(selectedRequirements.network);
|
|
423
|
+
console.log(`\u{1F4CD} Using Solana RPC: ${effectiveRpcUrl.substring(0, 40)}...`);
|
|
424
|
+
console.log(`\u{1F4CD} Network from backend: ${selectedRequirements.network}`);
|
|
425
|
+
let paymentHeader;
|
|
426
|
+
try {
|
|
427
|
+
paymentHeader = await createSvmPaymentHeader({
|
|
428
|
+
wallet,
|
|
429
|
+
paymentRequirements: selectedRequirements,
|
|
430
|
+
x402Version,
|
|
431
|
+
rpcUrl: effectiveRpcUrl
|
|
432
|
+
});
|
|
433
|
+
console.log("\u2705 Payment header created successfully");
|
|
434
|
+
} catch (error) {
|
|
435
|
+
console.error("\u274C Failed to create payment header:", error);
|
|
436
|
+
throw wrapPaymentError(error);
|
|
437
|
+
}
|
|
368
438
|
const newInit = {
|
|
369
439
|
...requestInit,
|
|
370
440
|
method: requestInit?.method || "POST",
|
|
@@ -374,7 +444,38 @@ async function handleSvmPayment(endpoint, config, requestInit) {
|
|
|
374
444
|
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
375
445
|
}
|
|
376
446
|
};
|
|
377
|
-
|
|
447
|
+
const retryResponse = await fetch(endpoint, newInit);
|
|
448
|
+
if (retryResponse.status === 402) {
|
|
449
|
+
try {
|
|
450
|
+
const retryData = await retryResponse.json();
|
|
451
|
+
const IGNORED_ERRORS2 = [
|
|
452
|
+
"X-PAYMENT header is required",
|
|
453
|
+
"missing X-PAYMENT header",
|
|
454
|
+
"payment_required"
|
|
455
|
+
];
|
|
456
|
+
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
457
|
+
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
458
|
+
const ERROR_MESSAGES = {
|
|
459
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
460
|
+
"invalid_signature": "Invalid payment signature",
|
|
461
|
+
"expired": "Payment authorization has expired",
|
|
462
|
+
"already_used": "This payment has already been used",
|
|
463
|
+
"network_mismatch": "Payment network does not match",
|
|
464
|
+
"invalid_payment": "Invalid payment data",
|
|
465
|
+
"verification_failed": "Payment verification failed"
|
|
466
|
+
};
|
|
467
|
+
const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
468
|
+
const error = new Error(errorMessage);
|
|
469
|
+
throw wrapPaymentError(error);
|
|
470
|
+
}
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (error instanceof PaymentOperationError) {
|
|
473
|
+
throw error;
|
|
474
|
+
}
|
|
475
|
+
console.warn("\u26A0\uFE0F Could not parse retry 402 response:", error);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return retryResponse;
|
|
378
479
|
}
|
|
379
480
|
|
|
380
481
|
// src/services/evm/payment-header.ts
|
|
@@ -387,6 +488,34 @@ async function createEvmPaymentHeader(params) {
|
|
|
387
488
|
if (!paymentRequirements?.asset) {
|
|
388
489
|
throw new Error("Missing asset (token contract) in payment requirements");
|
|
389
490
|
}
|
|
491
|
+
if (wallet.getChainId) {
|
|
492
|
+
try {
|
|
493
|
+
const currentChainIdHex = await wallet.getChainId();
|
|
494
|
+
const currentChainId = parseInt(currentChainIdHex, 16);
|
|
495
|
+
if (currentChainId !== chainId) {
|
|
496
|
+
const networkNames = {
|
|
497
|
+
1: "Ethereum Mainnet",
|
|
498
|
+
11155111: "Sepolia Testnet",
|
|
499
|
+
8453: "Base Mainnet",
|
|
500
|
+
84532: "Base Sepolia Testnet",
|
|
501
|
+
137: "Polygon Mainnet",
|
|
502
|
+
42161: "Arbitrum One",
|
|
503
|
+
10: "Optimism Mainnet"
|
|
504
|
+
};
|
|
505
|
+
const currentNetworkName = networkNames[currentChainId] || `Chain ${currentChainId}`;
|
|
506
|
+
const targetNetworkName = networkNames[chainId] || `Chain ${chainId}`;
|
|
507
|
+
throw new Error(
|
|
508
|
+
`Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch your wallet to the correct network.`
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
console.log(`\u2705 Chain ID verified: ${chainId}`);
|
|
512
|
+
} catch (error) {
|
|
513
|
+
if (error.message.includes("Network mismatch")) {
|
|
514
|
+
throw wrapPaymentError(error);
|
|
515
|
+
}
|
|
516
|
+
console.warn("Could not verify chainId:", error);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
390
519
|
const now = Math.floor(Date.now() / 1e3);
|
|
391
520
|
const nonceBytes = ethers.randomBytes(32);
|
|
392
521
|
const nonceBytes32 = ethers.hexlify(nonceBytes);
|
|
@@ -415,7 +544,14 @@ async function createEvmPaymentHeader(params) {
|
|
|
415
544
|
validBefore: String(now + (paymentRequirements.maxTimeoutSeconds || 3600)),
|
|
416
545
|
nonce: nonceBytes32
|
|
417
546
|
};
|
|
418
|
-
|
|
547
|
+
let signature;
|
|
548
|
+
try {
|
|
549
|
+
signature = await wallet.signTypedData(domain, types, authorization);
|
|
550
|
+
console.log("\u2705 Signature created successfully");
|
|
551
|
+
} catch (error) {
|
|
552
|
+
console.error("\u274C Failed to create signature:", error);
|
|
553
|
+
throw wrapPaymentError(error);
|
|
554
|
+
}
|
|
419
555
|
const headerPayload = {
|
|
420
556
|
x402_version: x402Version,
|
|
421
557
|
x402Version,
|
|
@@ -466,6 +602,26 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
466
602
|
return initialResponse;
|
|
467
603
|
}
|
|
468
604
|
const rawResponse = await initialResponse.json();
|
|
605
|
+
const IGNORED_ERRORS = [
|
|
606
|
+
"X-PAYMENT header is required",
|
|
607
|
+
"missing X-PAYMENT header",
|
|
608
|
+
"payment_required"
|
|
609
|
+
];
|
|
610
|
+
if (rawResponse.error && !IGNORED_ERRORS.includes(rawResponse.error)) {
|
|
611
|
+
console.error(`\u274C Payment verification failed: ${rawResponse.error}`);
|
|
612
|
+
const ERROR_MESSAGES = {
|
|
613
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
614
|
+
"invalid_signature": "Invalid payment signature",
|
|
615
|
+
"expired": "Payment authorization has expired",
|
|
616
|
+
"already_used": "This payment has already been used",
|
|
617
|
+
"network_mismatch": "Payment network does not match",
|
|
618
|
+
"invalid_payment": "Invalid payment data",
|
|
619
|
+
"verification_failed": "Payment verification failed"
|
|
620
|
+
};
|
|
621
|
+
const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
|
|
622
|
+
const error = new Error(errorMessage);
|
|
623
|
+
throw wrapPaymentError(error);
|
|
624
|
+
}
|
|
469
625
|
const x402Version = rawResponse.x402Version;
|
|
470
626
|
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
471
627
|
const selectedRequirements = parsedPaymentRequirements.find(
|
|
@@ -486,19 +642,81 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
486
642
|
}
|
|
487
643
|
}
|
|
488
644
|
const targetChainId = getChainIdFromNetwork(selectedRequirements.network);
|
|
489
|
-
|
|
645
|
+
let currentChainId;
|
|
646
|
+
if (wallet.getChainId) {
|
|
647
|
+
try {
|
|
648
|
+
const chainIdHex = await wallet.getChainId();
|
|
649
|
+
currentChainId = parseInt(chainIdHex, 16);
|
|
650
|
+
console.log(`\u{1F4CD} Current wallet chain: ${currentChainId}`);
|
|
651
|
+
} catch (error) {
|
|
652
|
+
console.warn("\u26A0\uFE0F Failed to get current chainId:", error);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
const networkNames = {
|
|
656
|
+
1: "Ethereum Mainnet",
|
|
657
|
+
11155111: "Sepolia Testnet",
|
|
658
|
+
8453: "Base Mainnet",
|
|
659
|
+
84532: "Base Sepolia Testnet",
|
|
660
|
+
137: "Polygon Mainnet",
|
|
661
|
+
42161: "Arbitrum One",
|
|
662
|
+
10: "Optimism Mainnet"
|
|
663
|
+
};
|
|
664
|
+
if (currentChainId && currentChainId !== targetChainId) {
|
|
665
|
+
if (!wallet.switchChain) {
|
|
666
|
+
const currentNetworkName = networkNames[currentChainId] || `Chain ${currentChainId}`;
|
|
667
|
+
const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
|
|
668
|
+
const error = new Error(
|
|
669
|
+
`Network mismatch: Your wallet is connected to ${currentNetworkName}, but payment requires ${targetNetworkName}. Please switch to ${targetNetworkName} manually in your wallet.`
|
|
670
|
+
);
|
|
671
|
+
throw wrapPaymentError(error);
|
|
672
|
+
}
|
|
673
|
+
try {
|
|
674
|
+
console.log(`\u{1F504} Switching to chain ${targetChainId}...`);
|
|
675
|
+
await wallet.switchChain(`0x${targetChainId.toString(16)}`);
|
|
676
|
+
console.log(`\u2705 Successfully switched to chain ${targetChainId}`);
|
|
677
|
+
} catch (error) {
|
|
678
|
+
console.error("\u274C Failed to switch chain:", error);
|
|
679
|
+
const targetNetworkName = networkNames[targetChainId] || selectedRequirements.network;
|
|
680
|
+
const wrappedError = wrapPaymentError(error);
|
|
681
|
+
let finalError;
|
|
682
|
+
if (wrappedError.code === "USER_REJECTED" /* USER_REJECTED */) {
|
|
683
|
+
finalError = new PaymentOperationError({
|
|
684
|
+
code: wrappedError.code,
|
|
685
|
+
message: wrappedError.message,
|
|
686
|
+
userMessage: `You rejected the network switch request. Please switch to ${targetNetworkName} manually.`,
|
|
687
|
+
originalError: wrappedError.originalError
|
|
688
|
+
});
|
|
689
|
+
} else {
|
|
690
|
+
finalError = new PaymentOperationError({
|
|
691
|
+
code: "NETWORK_SWITCH_FAILED" /* NETWORK_SWITCH_FAILED */,
|
|
692
|
+
message: wrappedError.message,
|
|
693
|
+
userMessage: `Failed to switch to ${targetNetworkName}. Please switch manually in your wallet.`,
|
|
694
|
+
originalError: wrappedError.originalError
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
throw finalError;
|
|
698
|
+
}
|
|
699
|
+
} else if (wallet.switchChain && !currentChainId) {
|
|
490
700
|
try {
|
|
701
|
+
console.log(`\u{1F504} Attempting to switch to chain ${targetChainId}...`);
|
|
491
702
|
await wallet.switchChain(`0x${targetChainId.toString(16)}`);
|
|
703
|
+
console.log(`\u2705 Switch attempted successfully`);
|
|
492
704
|
} catch (error) {
|
|
493
|
-
console.warn("Failed to switch chain:", error);
|
|
705
|
+
console.warn("\u26A0\uFE0F Failed to switch chain (best effort):", error);
|
|
494
706
|
}
|
|
495
707
|
}
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
708
|
+
let paymentHeader;
|
|
709
|
+
try {
|
|
710
|
+
paymentHeader = await createEvmPaymentHeader({
|
|
711
|
+
wallet,
|
|
712
|
+
paymentRequirements: selectedRequirements,
|
|
713
|
+
x402Version,
|
|
714
|
+
chainId: targetChainId
|
|
715
|
+
});
|
|
716
|
+
} catch (error) {
|
|
717
|
+
console.error("\u274C Failed to create payment header:", error);
|
|
718
|
+
throw wrapPaymentError(error);
|
|
719
|
+
}
|
|
502
720
|
const newInit = {
|
|
503
721
|
...requestInit,
|
|
504
722
|
method: requestInit?.method || "POST",
|
|
@@ -508,7 +726,38 @@ async function handleEvmPayment(endpoint, config, requestInit) {
|
|
|
508
726
|
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
509
727
|
}
|
|
510
728
|
};
|
|
511
|
-
|
|
729
|
+
const retryResponse = await fetch(endpoint, newInit);
|
|
730
|
+
if (retryResponse.status === 402) {
|
|
731
|
+
try {
|
|
732
|
+
const retryData = await retryResponse.json();
|
|
733
|
+
const IGNORED_ERRORS2 = [
|
|
734
|
+
"X-PAYMENT header is required",
|
|
735
|
+
"missing X-PAYMENT header",
|
|
736
|
+
"payment_required"
|
|
737
|
+
];
|
|
738
|
+
if (retryData.error && !IGNORED_ERRORS2.includes(retryData.error)) {
|
|
739
|
+
console.error(`\u274C Payment verification failed: ${retryData.error}`);
|
|
740
|
+
const ERROR_MESSAGES = {
|
|
741
|
+
"insufficient_funds": "Insufficient balance to complete this payment",
|
|
742
|
+
"invalid_signature": "Invalid payment signature",
|
|
743
|
+
"expired": "Payment authorization has expired",
|
|
744
|
+
"already_used": "This payment has already been used",
|
|
745
|
+
"network_mismatch": "Payment network does not match",
|
|
746
|
+
"invalid_payment": "Invalid payment data",
|
|
747
|
+
"verification_failed": "Payment verification failed"
|
|
748
|
+
};
|
|
749
|
+
const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
|
|
750
|
+
const error = new Error(errorMessage);
|
|
751
|
+
throw wrapPaymentError(error);
|
|
752
|
+
}
|
|
753
|
+
} catch (error) {
|
|
754
|
+
if (error instanceof PaymentOperationError) {
|
|
755
|
+
throw error;
|
|
756
|
+
}
|
|
757
|
+
console.warn("\u26A0\uFE0F Could not parse retry 402 response:", error);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return retryResponse;
|
|
512
761
|
}
|
|
513
762
|
|
|
514
763
|
// src/utils/payment-helpers.ts
|
|
@@ -549,7 +798,8 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
|
|
|
549
798
|
}
|
|
550
799
|
response = await handleSvmPayment(endpoint, {
|
|
551
800
|
wallet: solana,
|
|
552
|
-
network: "solana
|
|
801
|
+
network: "solana"
|
|
802
|
+
// Will use backend's network configuration
|
|
553
803
|
});
|
|
554
804
|
} else if (networkType === "evm" /* EVM */) {
|
|
555
805
|
if (!window.ethereum) {
|
|
@@ -561,12 +811,24 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
|
|
|
561
811
|
address: await signer.getAddress(),
|
|
562
812
|
signTypedData: async (domain, types, message) => {
|
|
563
813
|
return await signer.signTypedData(domain, types, message);
|
|
814
|
+
},
|
|
815
|
+
// Get current chain ID from wallet
|
|
816
|
+
getChainId: async () => {
|
|
817
|
+
const network = await provider.getNetwork();
|
|
818
|
+
return `0x${network.chainId.toString(16)}`;
|
|
819
|
+
},
|
|
820
|
+
// Switch to a different chain
|
|
821
|
+
switchChain: async (chainId) => {
|
|
822
|
+
await window.ethereum.request({
|
|
823
|
+
method: "wallet_switchEthereumChain",
|
|
824
|
+
params: [{ chainId }]
|
|
825
|
+
});
|
|
564
826
|
}
|
|
565
827
|
};
|
|
566
|
-
const network = endpoint.includes("sepolia") ? "base-sepolia" : "base";
|
|
567
828
|
response = await handleEvmPayment(endpoint, {
|
|
568
829
|
wallet,
|
|
569
|
-
network
|
|
830
|
+
network: "base"
|
|
831
|
+
// Will use backend's network configuration
|
|
570
832
|
});
|
|
571
833
|
} else {
|
|
572
834
|
throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
|
|
@@ -629,6 +891,138 @@ function getNetworkDisplayName(network) {
|
|
|
629
891
|
return displayNames[network.toLowerCase()] || network;
|
|
630
892
|
}
|
|
631
893
|
|
|
894
|
+
// src/utils/payment-error-handler.ts
|
|
895
|
+
function parsePaymentError(error) {
|
|
896
|
+
if (!error) {
|
|
897
|
+
return {
|
|
898
|
+
code: "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
899
|
+
message: "Unknown error occurred",
|
|
900
|
+
userMessage: "An unknown error occurred. Please try again.",
|
|
901
|
+
originalError: error
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const errorMessage = error.message || error.toString();
|
|
905
|
+
const errorCode = error.code;
|
|
906
|
+
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")) {
|
|
907
|
+
return {
|
|
908
|
+
code: "USER_REJECTED" /* USER_REJECTED */,
|
|
909
|
+
message: "User rejected the transaction",
|
|
910
|
+
userMessage: "You rejected the signature request. Please try again if you want to proceed.",
|
|
911
|
+
originalError: error
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
if (errorMessage.includes("chainId") && (errorMessage.includes("must match") || errorMessage.includes("does not match"))) {
|
|
915
|
+
const match = errorMessage.match(/chainId.*?"(\d+)".*?active.*?"(\d+)"/i) || errorMessage.match(/chain (\d+).*?chain (\d+)/i);
|
|
916
|
+
if (match) {
|
|
917
|
+
const [, requestedChain, activeChain] = match;
|
|
918
|
+
return {
|
|
919
|
+
code: "CHAIN_ID_MISMATCH" /* CHAIN_ID_MISMATCH */,
|
|
920
|
+
message: `Network mismatch (wallet is on different chain): Requested ${requestedChain}, but wallet is on ${activeChain}`,
|
|
921
|
+
userMessage: `Your wallet is on the wrong network. Please switch to the correct network and try again.`,
|
|
922
|
+
originalError: error
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
return {
|
|
926
|
+
code: "CHAIN_ID_MISMATCH" /* CHAIN_ID_MISMATCH */,
|
|
927
|
+
message: "Network mismatch (wallet selected network does not match)",
|
|
928
|
+
userMessage: "Your wallet is on the wrong network. Please switch to the correct network.",
|
|
929
|
+
originalError: error
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
if (errorMessage.includes("Network mismatch") || errorMessage.includes("Wrong network") || errorMessage.includes("Incorrect network")) {
|
|
933
|
+
return {
|
|
934
|
+
code: "NETWORK_MISMATCH" /* NETWORK_MISMATCH */,
|
|
935
|
+
message: errorMessage,
|
|
936
|
+
userMessage: "Please switch your wallet to the correct network.",
|
|
937
|
+
originalError: error
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
if (errorMessage.includes("locked") || errorMessage.includes("Wallet is locked")) {
|
|
941
|
+
return {
|
|
942
|
+
code: "WALLET_LOCKED" /* WALLET_LOCKED */,
|
|
943
|
+
message: "Wallet is locked",
|
|
944
|
+
userMessage: "Please unlock your wallet and try again.",
|
|
945
|
+
originalError: error
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
if (errorMessage.includes("insufficient") && (errorMessage.includes("balance") || errorMessage.includes("funds"))) {
|
|
949
|
+
return {
|
|
950
|
+
code: "INSUFFICIENT_BALANCE" /* INSUFFICIENT_BALANCE */,
|
|
951
|
+
message: "Insufficient balance",
|
|
952
|
+
userMessage: "You don't have enough balance to complete this payment.",
|
|
953
|
+
originalError: error
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
if (errorMessage.includes("Failed to switch") || errorMessage.includes("switch chain")) {
|
|
957
|
+
return {
|
|
958
|
+
code: "NETWORK_SWITCH_FAILED" /* NETWORK_SWITCH_FAILED */,
|
|
959
|
+
message: errorMessage,
|
|
960
|
+
userMessage: "Failed to switch network. Please switch manually in your wallet.",
|
|
961
|
+
originalError: error
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
if (errorMessage.includes("not connected") || errorMessage.includes("No wallet") || errorMessage.includes("Connect wallet")) {
|
|
965
|
+
return {
|
|
966
|
+
code: "WALLET_NOT_CONNECTED" /* WALLET_NOT_CONNECTED */,
|
|
967
|
+
message: "Wallet not connected",
|
|
968
|
+
userMessage: "Please connect your wallet first.",
|
|
969
|
+
originalError: error
|
|
970
|
+
};
|
|
971
|
+
}
|
|
972
|
+
if (errorMessage.includes("No suitable") || errorMessage.includes("payment requirements") || errorMessage.includes("Missing payTo") || errorMessage.includes("Missing asset")) {
|
|
973
|
+
return {
|
|
974
|
+
code: "INVALID_PAYMENT_REQUIREMENTS" /* INVALID_PAYMENT_REQUIREMENTS */,
|
|
975
|
+
message: errorMessage,
|
|
976
|
+
userMessage: "Invalid payment configuration. Please contact support.",
|
|
977
|
+
originalError: error
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
if (errorMessage.includes("exceeds maximum")) {
|
|
981
|
+
return {
|
|
982
|
+
code: "AMOUNT_EXCEEDED" /* AMOUNT_EXCEEDED */,
|
|
983
|
+
message: errorMessage,
|
|
984
|
+
userMessage: "Payment amount exceeds the maximum allowed.",
|
|
985
|
+
originalError: error
|
|
986
|
+
};
|
|
987
|
+
}
|
|
988
|
+
if (errorMessage.includes("signature") || errorMessage.includes("sign") || errorCode === "UNKNOWN_ERROR") {
|
|
989
|
+
return {
|
|
990
|
+
code: "SIGNATURE_FAILED" /* SIGNATURE_FAILED */,
|
|
991
|
+
message: errorMessage,
|
|
992
|
+
userMessage: "Failed to sign the transaction. Please try again.",
|
|
993
|
+
originalError: error
|
|
994
|
+
};
|
|
995
|
+
}
|
|
996
|
+
return {
|
|
997
|
+
code: "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
|
|
998
|
+
message: errorMessage,
|
|
999
|
+
userMessage: "An unexpected error occurred. Please try again or contact support.",
|
|
1000
|
+
originalError: error
|
|
1001
|
+
};
|
|
1002
|
+
}
|
|
1003
|
+
var PaymentOperationError = class _PaymentOperationError extends Error {
|
|
1004
|
+
constructor(paymentError) {
|
|
1005
|
+
super(paymentError.message);
|
|
1006
|
+
this.name = "PaymentOperationError";
|
|
1007
|
+
this.code = paymentError.code;
|
|
1008
|
+
this.userMessage = paymentError.userMessage;
|
|
1009
|
+
this.originalError = paymentError.originalError;
|
|
1010
|
+
if (Error.captureStackTrace) {
|
|
1011
|
+
Error.captureStackTrace(this, _PaymentOperationError);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Get a formatted error message for logging
|
|
1016
|
+
*/
|
|
1017
|
+
toLogString() {
|
|
1018
|
+
return `[${this.code}] ${this.message} | User Message: ${this.userMessage}`;
|
|
1019
|
+
}
|
|
1020
|
+
};
|
|
1021
|
+
function wrapPaymentError(error) {
|
|
1022
|
+
const parsedError = parsePaymentError(error);
|
|
1023
|
+
return new PaymentOperationError(parsedError);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
632
1026
|
// src/react/store/walletStore.ts
|
|
633
1027
|
var WalletStore = class {
|
|
634
1028
|
constructor() {
|
|
@@ -660,6 +1054,29 @@ var WalletStore = class {
|
|
|
660
1054
|
}
|
|
661
1055
|
}
|
|
662
1056
|
});
|
|
1057
|
+
onChainChanged(() => {
|
|
1058
|
+
const connectedType = getConnectedNetworkType();
|
|
1059
|
+
if (connectedType === "evm" /* EVM */) {
|
|
1060
|
+
console.log("\u26A0\uFE0F Network changed detected - disconnecting wallet");
|
|
1061
|
+
disconnectWallet();
|
|
1062
|
+
this.setState({
|
|
1063
|
+
address: null,
|
|
1064
|
+
networkType: null,
|
|
1065
|
+
error: "Network changed. Please reconnect your wallet."
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
onWalletDisconnect(() => {
|
|
1070
|
+
const connectedType = getConnectedNetworkType();
|
|
1071
|
+
if (connectedType === "solana" /* SOLANA */ || connectedType === "svm" /* SVM */) {
|
|
1072
|
+
console.log("\u26A0\uFE0F Solana wallet disconnected");
|
|
1073
|
+
disconnectWallet();
|
|
1074
|
+
this.setState({
|
|
1075
|
+
address: null,
|
|
1076
|
+
networkType: null
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
663
1080
|
}
|
|
664
1081
|
async autoReconnect() {
|
|
665
1082
|
if (!isWalletManuallyDisconnected()) {
|
|
@@ -826,7 +1243,190 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
826
1243
|
}
|
|
827
1244
|
|
|
828
1245
|
// src/react/components/WalletConnect.tsx
|
|
829
|
-
import React from "react";
|
|
1246
|
+
import React, { useState as useState3 } from "react";
|
|
1247
|
+
|
|
1248
|
+
// src/react/styles/inline-styles.ts
|
|
1249
|
+
var isDarkMode = () => {
|
|
1250
|
+
if (typeof window === "undefined") return false;
|
|
1251
|
+
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
|
|
1252
|
+
};
|
|
1253
|
+
var colors = {
|
|
1254
|
+
// Light mode
|
|
1255
|
+
light: {
|
|
1256
|
+
background: "#fafafa",
|
|
1257
|
+
cardBg: "#ffffff",
|
|
1258
|
+
text: "#0a0a0a",
|
|
1259
|
+
textSecondary: "#737373",
|
|
1260
|
+
primary: "#000000",
|
|
1261
|
+
primaryHover: "#262626",
|
|
1262
|
+
danger: "#ef4444",
|
|
1263
|
+
dangerHover: "#dc2626",
|
|
1264
|
+
success: "#10b981",
|
|
1265
|
+
successHover: "#059669",
|
|
1266
|
+
disabled: "#e5e5e5",
|
|
1267
|
+
disabledText: "#a3a3a3",
|
|
1268
|
+
errorBg: "#fef2f2",
|
|
1269
|
+
errorText: "#dc2626"
|
|
1270
|
+
},
|
|
1271
|
+
// Dark mode
|
|
1272
|
+
dark: {
|
|
1273
|
+
background: "#0a0a0a",
|
|
1274
|
+
cardBg: "#171717",
|
|
1275
|
+
text: "#fafafa",
|
|
1276
|
+
textSecondary: "#a3a3a3",
|
|
1277
|
+
primary: "#ffffff",
|
|
1278
|
+
primaryHover: "#e5e5e5",
|
|
1279
|
+
danger: "#f87171",
|
|
1280
|
+
dangerHover: "#ef4444",
|
|
1281
|
+
success: "#34d399",
|
|
1282
|
+
successHover: "#10b981",
|
|
1283
|
+
disabled: "#262626",
|
|
1284
|
+
disabledText: "#525252",
|
|
1285
|
+
errorBg: "#1c1917",
|
|
1286
|
+
errorText: "#f87171"
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
var getColors = () => {
|
|
1290
|
+
return isDarkMode() ? colors.dark : colors.light;
|
|
1291
|
+
};
|
|
1292
|
+
var containerStyle = {
|
|
1293
|
+
width: "100%",
|
|
1294
|
+
maxWidth: "420px",
|
|
1295
|
+
margin: "0 auto"
|
|
1296
|
+
};
|
|
1297
|
+
var getSectionStyle = () => {
|
|
1298
|
+
const c = getColors();
|
|
1299
|
+
return {
|
|
1300
|
+
padding: "1.5rem",
|
|
1301
|
+
background: c.cardBg,
|
|
1302
|
+
borderRadius: "12px"
|
|
1303
|
+
};
|
|
1304
|
+
};
|
|
1305
|
+
var getTitleStyle = () => {
|
|
1306
|
+
const c = getColors();
|
|
1307
|
+
return {
|
|
1308
|
+
margin: "0 0 1.25rem 0",
|
|
1309
|
+
fontSize: "1.125rem",
|
|
1310
|
+
fontWeight: 600,
|
|
1311
|
+
color: c.text,
|
|
1312
|
+
letterSpacing: "-0.01em"
|
|
1313
|
+
};
|
|
1314
|
+
};
|
|
1315
|
+
var buttonsContainerStyle = {
|
|
1316
|
+
display: "flex",
|
|
1317
|
+
flexDirection: "column",
|
|
1318
|
+
gap: "0.75rem"
|
|
1319
|
+
};
|
|
1320
|
+
var walletOptionStyle = {
|
|
1321
|
+
display: "flex",
|
|
1322
|
+
flexDirection: "column",
|
|
1323
|
+
gap: "0.5rem"
|
|
1324
|
+
};
|
|
1325
|
+
var baseButtonStyle = {
|
|
1326
|
+
padding: "0.875rem 1.25rem",
|
|
1327
|
+
fontSize: "0.9375rem",
|
|
1328
|
+
fontWeight: 500,
|
|
1329
|
+
border: "none",
|
|
1330
|
+
borderRadius: "8px",
|
|
1331
|
+
cursor: "pointer",
|
|
1332
|
+
transition: "background-color 0.15s ease, opacity 0.15s ease",
|
|
1333
|
+
outline: "none",
|
|
1334
|
+
letterSpacing: "-0.01em"
|
|
1335
|
+
};
|
|
1336
|
+
var getConnectButtonStyle = (isDisabled, isHovered) => {
|
|
1337
|
+
const c = getColors();
|
|
1338
|
+
return {
|
|
1339
|
+
...baseButtonStyle,
|
|
1340
|
+
background: isDisabled ? c.disabled : isHovered ? c.primaryHover : c.primary,
|
|
1341
|
+
color: isDarkMode() ? "#000000" : "#ffffff",
|
|
1342
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
1343
|
+
opacity: isDisabled ? 0.5 : 1
|
|
1344
|
+
};
|
|
1345
|
+
};
|
|
1346
|
+
var getDisconnectButtonStyle = (isHovered) => {
|
|
1347
|
+
const c = getColors();
|
|
1348
|
+
return {
|
|
1349
|
+
...baseButtonStyle,
|
|
1350
|
+
background: isHovered ? c.dangerHover : c.danger,
|
|
1351
|
+
color: "#ffffff"
|
|
1352
|
+
};
|
|
1353
|
+
};
|
|
1354
|
+
var getPayButtonStyle = (isDisabled, isHovered) => {
|
|
1355
|
+
const c = getColors();
|
|
1356
|
+
return {
|
|
1357
|
+
...baseButtonStyle,
|
|
1358
|
+
background: isDisabled ? c.disabled : isHovered ? c.successHover : c.success,
|
|
1359
|
+
color: "#ffffff",
|
|
1360
|
+
width: "100%",
|
|
1361
|
+
cursor: isDisabled ? "not-allowed" : "pointer",
|
|
1362
|
+
opacity: isDisabled ? 0.5 : 1
|
|
1363
|
+
};
|
|
1364
|
+
};
|
|
1365
|
+
var getInstallLinkStyle = (isHovered) => {
|
|
1366
|
+
const c = getColors();
|
|
1367
|
+
return {
|
|
1368
|
+
display: "inline-block",
|
|
1369
|
+
padding: "0.5rem",
|
|
1370
|
+
fontSize: "0.8125rem",
|
|
1371
|
+
color: c.textSecondary,
|
|
1372
|
+
textDecoration: isHovered ? "underline" : "none",
|
|
1373
|
+
textAlign: "center",
|
|
1374
|
+
fontWeight: 500
|
|
1375
|
+
};
|
|
1376
|
+
};
|
|
1377
|
+
var walletAddressStyle = {
|
|
1378
|
+
display: "flex",
|
|
1379
|
+
flexDirection: "column",
|
|
1380
|
+
gap: "0.5rem",
|
|
1381
|
+
marginBottom: "1rem"
|
|
1382
|
+
};
|
|
1383
|
+
var getLabelStyle = () => {
|
|
1384
|
+
const c = getColors();
|
|
1385
|
+
return {
|
|
1386
|
+
fontSize: "0.8125rem",
|
|
1387
|
+
color: c.textSecondary,
|
|
1388
|
+
fontWeight: 500,
|
|
1389
|
+
textTransform: "uppercase",
|
|
1390
|
+
letterSpacing: "0.05em"
|
|
1391
|
+
};
|
|
1392
|
+
};
|
|
1393
|
+
var getAddressStyle = () => {
|
|
1394
|
+
const c = getColors();
|
|
1395
|
+
return {
|
|
1396
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
1397
|
+
fontSize: "0.9375rem",
|
|
1398
|
+
fontWeight: 500,
|
|
1399
|
+
color: c.text,
|
|
1400
|
+
letterSpacing: "-0.01em"
|
|
1401
|
+
};
|
|
1402
|
+
};
|
|
1403
|
+
var walletActionsStyle = {
|
|
1404
|
+
margin: "1rem 0"
|
|
1405
|
+
};
|
|
1406
|
+
var getHintStyle = () => {
|
|
1407
|
+
const c = getColors();
|
|
1408
|
+
return {
|
|
1409
|
+
marginTop: "1rem",
|
|
1410
|
+
fontSize: "0.8125rem",
|
|
1411
|
+
color: c.textSecondary,
|
|
1412
|
+
textAlign: "center",
|
|
1413
|
+
lineHeight: "1.5"
|
|
1414
|
+
};
|
|
1415
|
+
};
|
|
1416
|
+
var getErrorStyle = () => {
|
|
1417
|
+
const c = getColors();
|
|
1418
|
+
return {
|
|
1419
|
+
marginTop: "1rem",
|
|
1420
|
+
padding: "0.75rem 1rem",
|
|
1421
|
+
background: c.errorBg,
|
|
1422
|
+
color: c.errorText,
|
|
1423
|
+
borderRadius: "8px",
|
|
1424
|
+
fontSize: "0.8125rem",
|
|
1425
|
+
fontWeight: 500
|
|
1426
|
+
};
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
// src/react/components/WalletConnect.tsx
|
|
830
1430
|
function WalletConnect({
|
|
831
1431
|
supportedNetworks = ["solana" /* SOLANA */, "evm" /* EVM */],
|
|
832
1432
|
className = "",
|
|
@@ -834,6 +1434,8 @@ function WalletConnect({
|
|
|
834
1434
|
onDisconnect
|
|
835
1435
|
}) {
|
|
836
1436
|
const { address, networkType, isConnecting, error, connect, disconnect } = useWallet();
|
|
1437
|
+
const [hoveredButton, setHoveredButton] = useState3(null);
|
|
1438
|
+
const [hoveredLink, setHoveredLink] = useState3(null);
|
|
837
1439
|
const handleConnect = async (network) => {
|
|
838
1440
|
try {
|
|
839
1441
|
await connect(network);
|
|
@@ -844,14 +1446,16 @@ function WalletConnect({
|
|
|
844
1446
|
disconnect();
|
|
845
1447
|
onDisconnect?.();
|
|
846
1448
|
};
|
|
847
|
-
return /* @__PURE__ */ React.createElement("div", {
|
|
1449
|
+
return /* @__PURE__ */ React.createElement("div", { style: { ...containerStyle, ...className ? {} : {} }, className }, !address ? /* @__PURE__ */ React.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ React.createElement("h3", { style: getTitleStyle() }, "Connect Wallet"), supportedNetworks.length === 0 ? /* @__PURE__ */ React.createElement("p", { style: getHintStyle() }, "No payment required") : /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, supportedNetworks.map((network) => {
|
|
848
1450
|
const installed = isWalletInstalled(network);
|
|
849
|
-
return /* @__PURE__ */ React.createElement("div", { key: network,
|
|
1451
|
+
return /* @__PURE__ */ React.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ React.createElement(
|
|
850
1452
|
"button",
|
|
851
1453
|
{
|
|
852
|
-
|
|
1454
|
+
style: getConnectButtonStyle(isConnecting || !installed, hoveredButton === network),
|
|
853
1455
|
onClick: () => handleConnect(network),
|
|
854
|
-
disabled: isConnecting || !installed
|
|
1456
|
+
disabled: isConnecting || !installed,
|
|
1457
|
+
onMouseEnter: () => setHoveredButton(network),
|
|
1458
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
855
1459
|
},
|
|
856
1460
|
isConnecting ? "Connecting..." : getNetworkDisplayName(network)
|
|
857
1461
|
), !installed && /* @__PURE__ */ React.createElement(
|
|
@@ -860,15 +1464,26 @@ function WalletConnect({
|
|
|
860
1464
|
href: getWalletInstallUrl(network),
|
|
861
1465
|
target: "_blank",
|
|
862
1466
|
rel: "noopener noreferrer",
|
|
863
|
-
|
|
1467
|
+
style: getInstallLinkStyle(hoveredLink === network),
|
|
1468
|
+
onMouseEnter: () => setHoveredLink(network),
|
|
1469
|
+
onMouseLeave: () => setHoveredLink(null)
|
|
864
1470
|
},
|
|
865
1471
|
"Install Wallet"
|
|
866
1472
|
));
|
|
867
|
-
})), error && /* @__PURE__ */ React.createElement("p", {
|
|
1473
|
+
})), error && /* @__PURE__ */ React.createElement("p", { style: getErrorStyle() }, error), /* @__PURE__ */ React.createElement("p", { style: getHintStyle() }, "To switch accounts, please change it in your wallet extension")) : /* @__PURE__ */ React.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ React.createElement("div", { style: walletAddressStyle }, /* @__PURE__ */ React.createElement("span", { style: getLabelStyle() }, "Connected ", networkType && `(${getNetworkDisplayName(networkType)})`), /* @__PURE__ */ React.createElement("span", { style: getAddressStyle() }, formatAddress(address))), /* @__PURE__ */ React.createElement("div", { style: walletActionsStyle }, /* @__PURE__ */ React.createElement(
|
|
1474
|
+
"button",
|
|
1475
|
+
{
|
|
1476
|
+
style: getDisconnectButtonStyle(hoveredButton === "disconnect"),
|
|
1477
|
+
onClick: handleDisconnect,
|
|
1478
|
+
onMouseEnter: () => setHoveredButton("disconnect"),
|
|
1479
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
1480
|
+
},
|
|
1481
|
+
"Disconnect"
|
|
1482
|
+
)), /* @__PURE__ */ React.createElement("p", { style: getHintStyle() }, "Switch account in your wallet to change address")));
|
|
868
1483
|
}
|
|
869
1484
|
|
|
870
1485
|
// src/react/components/PaymentButton.tsx
|
|
871
|
-
import React2 from "react";
|
|
1486
|
+
import React2, { useState as useState4 } from "react";
|
|
872
1487
|
function PaymentButton({
|
|
873
1488
|
endpoint,
|
|
874
1489
|
className = "",
|
|
@@ -881,6 +1496,7 @@ function PaymentButton({
|
|
|
881
1496
|
}) {
|
|
882
1497
|
const { networkType } = useWallet();
|
|
883
1498
|
const { isProcessing, setIsProcessing, setResult, setError, error } = usePayment();
|
|
1499
|
+
const [isHovered, setIsHovered] = useState4(false);
|
|
884
1500
|
const handleClick = async () => {
|
|
885
1501
|
if (!networkType) {
|
|
886
1502
|
const errorMsg = "Please connect wallet first";
|
|
@@ -904,15 +1520,19 @@ function PaymentButton({
|
|
|
904
1520
|
onFinish?.();
|
|
905
1521
|
}
|
|
906
1522
|
};
|
|
1523
|
+
const isDisabled = disabled || isProcessing || !networkType;
|
|
907
1524
|
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
|
|
908
1525
|
"button",
|
|
909
1526
|
{
|
|
910
|
-
|
|
1527
|
+
style: getPayButtonStyle(isDisabled, isHovered),
|
|
1528
|
+
className,
|
|
911
1529
|
onClick: handleClick,
|
|
912
|
-
disabled:
|
|
1530
|
+
disabled: isDisabled,
|
|
1531
|
+
onMouseEnter: () => setIsHovered(true),
|
|
1532
|
+
onMouseLeave: () => setIsHovered(false)
|
|
913
1533
|
},
|
|
914
1534
|
isProcessing ? "Processing..." : children
|
|
915
|
-
), error && /* @__PURE__ */ React2.createElement("p", {
|
|
1535
|
+
), error && /* @__PURE__ */ React2.createElement("p", { style: getErrorStyle() }, error));
|
|
916
1536
|
}
|
|
917
1537
|
export {
|
|
918
1538
|
PaymentButton,
|