@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.
- package/dist/index.d.mts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +17 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +17 -8
- package/dist/index.mjs.map +1 -1
- package/dist/react/components/WalletConnect.tsx +1 -1
- package/dist/react/hooks/usePaymentInfo.ts +38 -4
- package/dist/react/index.d.mts +15 -54
- package/dist/react/index.d.ts +15 -54
- package/dist/react/index.js +28 -777
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +28 -776
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/index.ts +5 -9
- package/dist/react/styles/inline-styles.ts +15 -4
- package/package.json +1 -1
- package/dist/react/components/PaymentButton.tsx +0 -119
package/dist/react/index.mjs
CHANGED
|
@@ -255,510 +255,9 @@ import {
|
|
|
255
255
|
TOKEN_2022_PROGRAM_ID,
|
|
256
256
|
TOKEN_PROGRAM_ID
|
|
257
257
|
} from "@solana/spl-token";
|
|
258
|
-
async function createSvmPaymentHeader(params) {
|
|
259
|
-
const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
|
|
260
|
-
const connection = new Connection(rpcUrl, "confirmed");
|
|
261
|
-
const feePayer = paymentRequirements?.extra?.feePayer;
|
|
262
|
-
if (typeof feePayer !== "string" || !feePayer) {
|
|
263
|
-
throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
|
|
264
|
-
}
|
|
265
|
-
const feePayerPubkey = new PublicKey(feePayer);
|
|
266
|
-
const walletAddress = wallet?.publicKey?.toString() || wallet?.address;
|
|
267
|
-
if (!walletAddress) {
|
|
268
|
-
throw new Error("Missing connected Solana wallet address or publicKey");
|
|
269
|
-
}
|
|
270
|
-
const userPubkey = new PublicKey(walletAddress);
|
|
271
|
-
if (!paymentRequirements?.payTo) {
|
|
272
|
-
throw new Error("Missing payTo in payment requirements");
|
|
273
|
-
}
|
|
274
|
-
const destination = new PublicKey(paymentRequirements.payTo);
|
|
275
|
-
const instructions = [];
|
|
276
|
-
instructions.push(
|
|
277
|
-
ComputeBudgetProgram.setComputeUnitLimit({
|
|
278
|
-
units: 7e3
|
|
279
|
-
// Sufficient for SPL token transfer
|
|
280
|
-
})
|
|
281
|
-
);
|
|
282
|
-
instructions.push(
|
|
283
|
-
ComputeBudgetProgram.setComputeUnitPrice({
|
|
284
|
-
microLamports: 1
|
|
285
|
-
// Minimal price
|
|
286
|
-
})
|
|
287
|
-
);
|
|
288
|
-
if (!paymentRequirements.asset) {
|
|
289
|
-
throw new Error("Missing token mint for SPL transfer");
|
|
290
|
-
}
|
|
291
|
-
const mintPubkey = new PublicKey(paymentRequirements.asset);
|
|
292
|
-
const mintInfo = await connection.getAccountInfo(mintPubkey, "confirmed");
|
|
293
|
-
const programId = mintInfo?.owner?.toBase58() === TOKEN_2022_PROGRAM_ID.toBase58() ? TOKEN_2022_PROGRAM_ID : TOKEN_PROGRAM_ID;
|
|
294
|
-
const mint = await getMint(connection, mintPubkey, void 0, programId);
|
|
295
|
-
const sourceAta = await getAssociatedTokenAddress(
|
|
296
|
-
mintPubkey,
|
|
297
|
-
userPubkey,
|
|
298
|
-
false,
|
|
299
|
-
programId
|
|
300
|
-
);
|
|
301
|
-
const destinationAta = await getAssociatedTokenAddress(
|
|
302
|
-
mintPubkey,
|
|
303
|
-
destination,
|
|
304
|
-
false,
|
|
305
|
-
programId
|
|
306
|
-
);
|
|
307
|
-
const sourceAtaInfo = await connection.getAccountInfo(sourceAta, "confirmed");
|
|
308
|
-
if (!sourceAtaInfo) {
|
|
309
|
-
throw new Error(
|
|
310
|
-
`User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
|
|
311
|
-
);
|
|
312
|
-
}
|
|
313
|
-
const destAtaInfo = await connection.getAccountInfo(destinationAta, "confirmed");
|
|
314
|
-
if (!destAtaInfo) {
|
|
315
|
-
throw new Error(
|
|
316
|
-
`Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
|
|
317
|
-
);
|
|
318
|
-
}
|
|
319
|
-
const amount = BigInt(paymentRequirements.maxAmountRequired);
|
|
320
|
-
instructions.push(
|
|
321
|
-
createTransferCheckedInstruction(
|
|
322
|
-
sourceAta,
|
|
323
|
-
mintPubkey,
|
|
324
|
-
destinationAta,
|
|
325
|
-
userPubkey,
|
|
326
|
-
amount,
|
|
327
|
-
mint.decimals,
|
|
328
|
-
[],
|
|
329
|
-
programId
|
|
330
|
-
)
|
|
331
|
-
);
|
|
332
|
-
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
333
|
-
const message = new TransactionMessage({
|
|
334
|
-
payerKey: feePayerPubkey,
|
|
335
|
-
recentBlockhash: blockhash,
|
|
336
|
-
instructions
|
|
337
|
-
}).compileToV0Message();
|
|
338
|
-
const transaction = new VersionedTransaction(message);
|
|
339
|
-
if (typeof wallet?.signTransaction !== "function") {
|
|
340
|
-
throw new Error("Connected wallet does not support signTransaction");
|
|
341
|
-
}
|
|
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
|
-
}
|
|
350
|
-
const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
|
|
351
|
-
const paymentPayload = {
|
|
352
|
-
x402Version,
|
|
353
|
-
scheme: paymentRequirements.scheme,
|
|
354
|
-
network: paymentRequirements.network,
|
|
355
|
-
payload: {
|
|
356
|
-
transaction: serializedTransaction
|
|
357
|
-
}
|
|
358
|
-
};
|
|
359
|
-
const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
360
|
-
return paymentHeader;
|
|
361
|
-
}
|
|
362
|
-
function getDefaultSolanaRpcUrl(network) {
|
|
363
|
-
const normalized = network.toLowerCase();
|
|
364
|
-
if (normalized === "solana" || normalized === "solana-mainnet") {
|
|
365
|
-
return "https://cathee-fu8ezd-fast-mainnet.helius-rpc.com";
|
|
366
|
-
} else if (normalized === "solana-devnet") {
|
|
367
|
-
return "https://api.devnet.solana.com";
|
|
368
|
-
}
|
|
369
|
-
throw new Error(`Unsupported Solana network: ${network}`);
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
// src/services/svm/payment-handler.ts
|
|
373
|
-
async function handleSvmPayment(endpoint, config, requestInit) {
|
|
374
|
-
const { wallet, network, rpcUrl, maxPaymentAmount } = config;
|
|
375
|
-
const initialResponse = await fetch(endpoint, {
|
|
376
|
-
...requestInit,
|
|
377
|
-
method: requestInit?.method || "POST"
|
|
378
|
-
});
|
|
379
|
-
if (initialResponse.status !== 402) {
|
|
380
|
-
return initialResponse;
|
|
381
|
-
}
|
|
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
|
-
}
|
|
403
|
-
const x402Version = rawResponse.x402Version;
|
|
404
|
-
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
405
|
-
const selectedRequirements = parsedPaymentRequirements.find(
|
|
406
|
-
(req) => req.scheme === "exact" && SolanaNetworkSchema.safeParse(req.network.toLowerCase()).success
|
|
407
|
-
);
|
|
408
|
-
if (!selectedRequirements) {
|
|
409
|
-
console.error(
|
|
410
|
-
"\u274C No suitable Solana payment requirements found. Available networks:",
|
|
411
|
-
parsedPaymentRequirements.map((req) => req.network)
|
|
412
|
-
);
|
|
413
|
-
throw new Error("No suitable Solana payment requirements found");
|
|
414
|
-
}
|
|
415
|
-
if (maxPaymentAmount && maxPaymentAmount > BigInt(0)) {
|
|
416
|
-
if (BigInt(selectedRequirements.maxAmountRequired) > maxPaymentAmount) {
|
|
417
|
-
throw new Error(
|
|
418
|
-
`Payment amount ${selectedRequirements.maxAmountRequired} exceeds maximum allowed ${maxPaymentAmount}`
|
|
419
|
-
);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
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
|
-
}
|
|
438
|
-
const newInit = {
|
|
439
|
-
...requestInit,
|
|
440
|
-
method: requestInit?.method || "POST",
|
|
441
|
-
headers: {
|
|
442
|
-
...requestInit?.headers || {},
|
|
443
|
-
"X-PAYMENT": paymentHeader,
|
|
444
|
-
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
445
|
-
}
|
|
446
|
-
};
|
|
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;
|
|
479
|
-
}
|
|
480
258
|
|
|
481
259
|
// src/services/evm/payment-header.ts
|
|
482
260
|
import { ethers } from "ethers";
|
|
483
|
-
async function createEvmPaymentHeader(params) {
|
|
484
|
-
const { wallet, paymentRequirements, x402Version, chainId } = params;
|
|
485
|
-
if (!paymentRequirements?.payTo) {
|
|
486
|
-
throw new Error("Missing payTo in payment requirements");
|
|
487
|
-
}
|
|
488
|
-
if (!paymentRequirements?.asset) {
|
|
489
|
-
throw new Error("Missing asset (token contract) in payment requirements");
|
|
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
|
-
}
|
|
519
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
520
|
-
const nonceBytes = ethers.randomBytes(32);
|
|
521
|
-
const nonceBytes32 = ethers.hexlify(nonceBytes);
|
|
522
|
-
const domain = {
|
|
523
|
-
name: paymentRequirements.extra?.name || "USDC",
|
|
524
|
-
version: paymentRequirements.extra?.version || "2",
|
|
525
|
-
chainId,
|
|
526
|
-
verifyingContract: paymentRequirements.asset
|
|
527
|
-
};
|
|
528
|
-
const types = {
|
|
529
|
-
TransferWithAuthorization: [
|
|
530
|
-
{ name: "from", type: "address" },
|
|
531
|
-
{ name: "to", type: "address" },
|
|
532
|
-
{ name: "value", type: "uint256" },
|
|
533
|
-
{ name: "validAfter", type: "uint256" },
|
|
534
|
-
{ name: "validBefore", type: "uint256" },
|
|
535
|
-
{ name: "nonce", type: "bytes32" }
|
|
536
|
-
]
|
|
537
|
-
};
|
|
538
|
-
const authorization = {
|
|
539
|
-
from: wallet.address,
|
|
540
|
-
to: paymentRequirements.payTo,
|
|
541
|
-
value: paymentRequirements.maxAmountRequired,
|
|
542
|
-
validAfter: "0",
|
|
543
|
-
// Effective immediately
|
|
544
|
-
validBefore: String(now + (paymentRequirements.maxTimeoutSeconds || 3600)),
|
|
545
|
-
nonce: nonceBytes32
|
|
546
|
-
};
|
|
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
|
-
}
|
|
555
|
-
const headerPayload = {
|
|
556
|
-
x402_version: x402Version,
|
|
557
|
-
x402Version,
|
|
558
|
-
scheme: paymentRequirements.scheme,
|
|
559
|
-
network: paymentRequirements.network,
|
|
560
|
-
payload: {
|
|
561
|
-
signature,
|
|
562
|
-
authorization: {
|
|
563
|
-
from: authorization.from,
|
|
564
|
-
to: authorization.to,
|
|
565
|
-
value: String(authorization.value),
|
|
566
|
-
valid_after: authorization.validAfter,
|
|
567
|
-
validAfter: authorization.validAfter,
|
|
568
|
-
valid_before: authorization.validBefore,
|
|
569
|
-
validBefore: authorization.validBefore,
|
|
570
|
-
nonce: authorization.nonce
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
const paymentHeader = btoa(JSON.stringify(headerPayload));
|
|
575
|
-
return paymentHeader;
|
|
576
|
-
}
|
|
577
|
-
function getChainIdFromNetwork(network) {
|
|
578
|
-
const chainIdMap = {
|
|
579
|
-
"ethereum": 1,
|
|
580
|
-
"sepolia": 11155111,
|
|
581
|
-
"base": 8453,
|
|
582
|
-
"base-sepolia": 84532,
|
|
583
|
-
"polygon": 137,
|
|
584
|
-
"arbitrum": 42161,
|
|
585
|
-
"optimism": 10
|
|
586
|
-
};
|
|
587
|
-
const chainId = chainIdMap[network.toLowerCase()];
|
|
588
|
-
if (!chainId) {
|
|
589
|
-
throw new Error(`Unknown network: ${network}`);
|
|
590
|
-
}
|
|
591
|
-
return chainId;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// src/services/evm/payment-handler.ts
|
|
595
|
-
async function handleEvmPayment(endpoint, config, requestInit) {
|
|
596
|
-
const { wallet, network, maxPaymentAmount } = config;
|
|
597
|
-
const initialResponse = await fetch(endpoint, {
|
|
598
|
-
...requestInit,
|
|
599
|
-
method: requestInit?.method || "POST"
|
|
600
|
-
});
|
|
601
|
-
if (initialResponse.status !== 402) {
|
|
602
|
-
return initialResponse;
|
|
603
|
-
}
|
|
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
|
-
}
|
|
625
|
-
const x402Version = rawResponse.x402Version;
|
|
626
|
-
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
627
|
-
const selectedRequirements = parsedPaymentRequirements.find(
|
|
628
|
-
(req) => req.scheme === "exact" && EvmNetworkSchema.safeParse(req.network.toLowerCase()).success
|
|
629
|
-
);
|
|
630
|
-
if (!selectedRequirements) {
|
|
631
|
-
console.error(
|
|
632
|
-
"\u274C No suitable EVM payment requirements found. Available networks:",
|
|
633
|
-
parsedPaymentRequirements.map((req) => req.network)
|
|
634
|
-
);
|
|
635
|
-
throw new Error("No suitable EVM payment requirements found");
|
|
636
|
-
}
|
|
637
|
-
if (maxPaymentAmount && maxPaymentAmount > BigInt(0)) {
|
|
638
|
-
if (BigInt(selectedRequirements.maxAmountRequired) > maxPaymentAmount) {
|
|
639
|
-
throw new Error(
|
|
640
|
-
`Payment amount ${selectedRequirements.maxAmountRequired} exceeds maximum allowed ${maxPaymentAmount}`
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
const targetChainId = getChainIdFromNetwork(selectedRequirements.network);
|
|
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) {
|
|
700
|
-
try {
|
|
701
|
-
console.log(`\u{1F504} Attempting to switch to chain ${targetChainId}...`);
|
|
702
|
-
await wallet.switchChain(`0x${targetChainId.toString(16)}`);
|
|
703
|
-
console.log(`\u2705 Switch attempted successfully`);
|
|
704
|
-
} catch (error) {
|
|
705
|
-
console.warn("\u26A0\uFE0F Failed to switch chain (best effort):", error);
|
|
706
|
-
}
|
|
707
|
-
}
|
|
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
|
-
}
|
|
720
|
-
const newInit = {
|
|
721
|
-
...requestInit,
|
|
722
|
-
method: requestInit?.method || "POST",
|
|
723
|
-
headers: {
|
|
724
|
-
...requestInit?.headers || {},
|
|
725
|
-
"X-PAYMENT": paymentHeader,
|
|
726
|
-
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
727
|
-
}
|
|
728
|
-
};
|
|
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;
|
|
761
|
-
}
|
|
762
261
|
|
|
763
262
|
// src/utils/payment-helpers.ts
|
|
764
263
|
import { ethers as ethers2 } from "ethers";
|
|
@@ -785,75 +284,6 @@ function getSupportedNetworkTypes(paymentRequirements) {
|
|
|
785
284
|
});
|
|
786
285
|
return Array.from(networkTypes);
|
|
787
286
|
}
|
|
788
|
-
async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
|
|
789
|
-
endpoint = `${endpoint}/${merchantId}`;
|
|
790
|
-
let response;
|
|
791
|
-
if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
|
|
792
|
-
const solana = window.solana;
|
|
793
|
-
if (!solana) {
|
|
794
|
-
throw new Error("\u8BF7\u5B89\u88C5 Phantom \u94B1\u5305");
|
|
795
|
-
}
|
|
796
|
-
if (!solana.isConnected) {
|
|
797
|
-
await solana.connect();
|
|
798
|
-
}
|
|
799
|
-
response = await handleSvmPayment(endpoint, {
|
|
800
|
-
wallet: solana,
|
|
801
|
-
network: "solana"
|
|
802
|
-
// Will use backend's network configuration
|
|
803
|
-
});
|
|
804
|
-
} else if (networkType === "evm" /* EVM */) {
|
|
805
|
-
if (!window.ethereum) {
|
|
806
|
-
throw new Error("\u8BF7\u5B89\u88C5 MetaMask \u94B1\u5305");
|
|
807
|
-
}
|
|
808
|
-
const provider = new ethers2.BrowserProvider(window.ethereum);
|
|
809
|
-
const signer = await provider.getSigner();
|
|
810
|
-
const wallet = {
|
|
811
|
-
address: await signer.getAddress(),
|
|
812
|
-
signTypedData: async (domain, types, message) => {
|
|
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
|
-
});
|
|
826
|
-
}
|
|
827
|
-
};
|
|
828
|
-
response = await handleEvmPayment(endpoint, {
|
|
829
|
-
wallet,
|
|
830
|
-
network: "base"
|
|
831
|
-
// Will use backend's network configuration
|
|
832
|
-
});
|
|
833
|
-
} else {
|
|
834
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
|
|
835
|
-
}
|
|
836
|
-
return response;
|
|
837
|
-
}
|
|
838
|
-
async function handlePayment(endpoint, networkType, callbacks) {
|
|
839
|
-
try {
|
|
840
|
-
callbacks?.onStart?.();
|
|
841
|
-
const response = await makePayment(networkType, "", endpoint);
|
|
842
|
-
if (!response.ok) {
|
|
843
|
-
const errorText = await response.text();
|
|
844
|
-
throw new Error(`\u8BF7\u6C42\u5931\u8D25 (${response.status}): ${errorText}`);
|
|
845
|
-
}
|
|
846
|
-
const result = await response.json();
|
|
847
|
-
callbacks?.onSuccess?.(result);
|
|
848
|
-
return result;
|
|
849
|
-
} catch (err) {
|
|
850
|
-
const errorMessage = err.message || "\u652F\u4ED8\u5931\u8D25";
|
|
851
|
-
callbacks?.onError?.(errorMessage);
|
|
852
|
-
throw err;
|
|
853
|
-
} finally {
|
|
854
|
-
callbacks?.onFinish?.();
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
287
|
|
|
858
288
|
// src/utils/network.ts
|
|
859
289
|
var NETWORK_TYPE_MAP = {
|
|
@@ -877,6 +307,7 @@ var NETWORK_TYPE_MAP = {
|
|
|
877
307
|
};
|
|
878
308
|
function getNetworkDisplayName(network) {
|
|
879
309
|
const displayNames = {
|
|
310
|
+
"evm": "EVM",
|
|
880
311
|
"ethereum": "Ethereum",
|
|
881
312
|
"sepolia": "Sepolia Testnet",
|
|
882
313
|
"base": "Base",
|
|
@@ -891,138 +322,6 @@ function getNetworkDisplayName(network) {
|
|
|
891
322
|
return displayNames[network.toLowerCase()] || network;
|
|
892
323
|
}
|
|
893
324
|
|
|
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
|
-
|
|
1026
325
|
// src/react/store/walletStore.ts
|
|
1027
326
|
var WalletStore = class {
|
|
1028
327
|
constructor() {
|
|
@@ -1201,7 +500,7 @@ function usePayment() {
|
|
|
1201
500
|
|
|
1202
501
|
// src/react/hooks/usePaymentInfo.ts
|
|
1203
502
|
import { useEffect, useState as useState2 } from "react";
|
|
1204
|
-
function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
503
|
+
function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL, additionalParams) {
|
|
1205
504
|
const [paymentInfo, setPaymentInfo] = useState2(null);
|
|
1206
505
|
const [supportedNetworks, setSupportedNetworks] = useState2([]);
|
|
1207
506
|
const [isLoading, setIsLoading] = useState2(true);
|
|
@@ -1210,8 +509,17 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
1210
509
|
setIsLoading(true);
|
|
1211
510
|
setError(null);
|
|
1212
511
|
try {
|
|
1213
|
-
|
|
1214
|
-
const
|
|
512
|
+
const fullEndpoint = `${endpoint}/${merchantId}`;
|
|
513
|
+
const requestInit = {
|
|
514
|
+
method: "POST",
|
|
515
|
+
...additionalParams && Object.keys(additionalParams).length > 0 ? {
|
|
516
|
+
body: JSON.stringify(additionalParams),
|
|
517
|
+
headers: {
|
|
518
|
+
"Content-Type": "application/json"
|
|
519
|
+
}
|
|
520
|
+
} : {}
|
|
521
|
+
};
|
|
522
|
+
const response = await fetch(fullEndpoint, requestInit);
|
|
1215
523
|
if (response.status === 402) {
|
|
1216
524
|
const body = await response.json();
|
|
1217
525
|
const payment = parsePaymentRequired(body);
|
|
@@ -1232,7 +540,7 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
1232
540
|
};
|
|
1233
541
|
useEffect(() => {
|
|
1234
542
|
fetchPaymentInfo();
|
|
1235
|
-
}, [endpoint]);
|
|
543
|
+
}, [endpoint, merchantId]);
|
|
1236
544
|
return {
|
|
1237
545
|
paymentInfo,
|
|
1238
546
|
supportedNetworks,
|
|
@@ -1335,12 +643,21 @@ var baseButtonStyle = {
|
|
|
1335
643
|
};
|
|
1336
644
|
var getConnectButtonStyle = (isDisabled, isHovered) => {
|
|
1337
645
|
const c = getColors();
|
|
646
|
+
const darkMode = isDarkMode();
|
|
647
|
+
if (isDisabled) {
|
|
648
|
+
return {
|
|
649
|
+
...baseButtonStyle,
|
|
650
|
+
background: c.disabled,
|
|
651
|
+
color: c.disabledText,
|
|
652
|
+
cursor: "not-allowed",
|
|
653
|
+
border: darkMode ? "1px solid #404040" : "1px solid #d4d4d4"
|
|
654
|
+
};
|
|
655
|
+
}
|
|
1338
656
|
return {
|
|
1339
657
|
...baseButtonStyle,
|
|
1340
|
-
background:
|
|
1341
|
-
color:
|
|
1342
|
-
cursor:
|
|
1343
|
-
opacity: isDisabled ? 0.5 : 1
|
|
658
|
+
background: isHovered ? c.primaryHover : c.primary,
|
|
659
|
+
color: darkMode ? "#000000" : "#ffffff",
|
|
660
|
+
cursor: "pointer"
|
|
1344
661
|
};
|
|
1345
662
|
};
|
|
1346
663
|
var getDisconnectButtonStyle = (isHovered) => {
|
|
@@ -1351,17 +668,6 @@ var getDisconnectButtonStyle = (isHovered) => {
|
|
|
1351
668
|
color: "#ffffff"
|
|
1352
669
|
};
|
|
1353
670
|
};
|
|
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
671
|
var getInstallLinkStyle = (isHovered) => {
|
|
1366
672
|
const c = getColors();
|
|
1367
673
|
return {
|
|
@@ -1446,7 +752,7 @@ function WalletConnect({
|
|
|
1446
752
|
disconnect();
|
|
1447
753
|
onDisconnect?.();
|
|
1448
754
|
};
|
|
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() }, "
|
|
755
|
+
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() }, "Please install a supported wallet extension") : /* @__PURE__ */ React.createElement("div", { style: buttonsContainerStyle }, supportedNetworks.map((network) => {
|
|
1450
756
|
const installed = isWalletInstalled(network);
|
|
1451
757
|
return /* @__PURE__ */ React.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ React.createElement(
|
|
1452
758
|
"button",
|
|
@@ -1481,61 +787,7 @@ function WalletConnect({
|
|
|
1481
787
|
"Disconnect"
|
|
1482
788
|
)), /* @__PURE__ */ React.createElement("p", { style: getHintStyle() }, "Switch account in your wallet to change address")));
|
|
1483
789
|
}
|
|
1484
|
-
|
|
1485
|
-
// src/react/components/PaymentButton.tsx
|
|
1486
|
-
import React2, { useState as useState4 } from "react";
|
|
1487
|
-
function PaymentButton({
|
|
1488
|
-
endpoint,
|
|
1489
|
-
className = "",
|
|
1490
|
-
disabled = false,
|
|
1491
|
-
onSuccess,
|
|
1492
|
-
onError,
|
|
1493
|
-
onStart,
|
|
1494
|
-
onFinish,
|
|
1495
|
-
children = "Pay Now"
|
|
1496
|
-
}) {
|
|
1497
|
-
const { networkType } = useWallet();
|
|
1498
|
-
const { isProcessing, setIsProcessing, setResult, setError, error } = usePayment();
|
|
1499
|
-
const [isHovered, setIsHovered] = useState4(false);
|
|
1500
|
-
const handleClick = async () => {
|
|
1501
|
-
if (!networkType) {
|
|
1502
|
-
const errorMsg = "Please connect wallet first";
|
|
1503
|
-
setError(errorMsg);
|
|
1504
|
-
onError?.(errorMsg);
|
|
1505
|
-
return;
|
|
1506
|
-
}
|
|
1507
|
-
try {
|
|
1508
|
-
onStart?.();
|
|
1509
|
-
setIsProcessing(true);
|
|
1510
|
-
setError(null);
|
|
1511
|
-
const result = await handlePayment(endpoint, networkType);
|
|
1512
|
-
setResult(result);
|
|
1513
|
-
onSuccess?.(result);
|
|
1514
|
-
} catch (err) {
|
|
1515
|
-
const errorMsg = err.message || "Payment failed";
|
|
1516
|
-
setError(errorMsg);
|
|
1517
|
-
onError?.(errorMsg);
|
|
1518
|
-
} finally {
|
|
1519
|
-
setIsProcessing(false);
|
|
1520
|
-
onFinish?.();
|
|
1521
|
-
}
|
|
1522
|
-
};
|
|
1523
|
-
const isDisabled = disabled || isProcessing || !networkType;
|
|
1524
|
-
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
|
|
1525
|
-
"button",
|
|
1526
|
-
{
|
|
1527
|
-
style: getPayButtonStyle(isDisabled, isHovered),
|
|
1528
|
-
className,
|
|
1529
|
-
onClick: handleClick,
|
|
1530
|
-
disabled: isDisabled,
|
|
1531
|
-
onMouseEnter: () => setIsHovered(true),
|
|
1532
|
-
onMouseLeave: () => setIsHovered(false)
|
|
1533
|
-
},
|
|
1534
|
-
isProcessing ? "Processing..." : children
|
|
1535
|
-
), error && /* @__PURE__ */ React2.createElement("p", { style: getErrorStyle() }, error));
|
|
1536
|
-
}
|
|
1537
790
|
export {
|
|
1538
|
-
PaymentButton,
|
|
1539
791
|
WalletConnect,
|
|
1540
792
|
usePayment,
|
|
1541
793
|
usePaymentInfo,
|