@voyage_ai/v402-web-ts 0.1.0 → 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/README.md +639 -1
- package/dist/index.d.mts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +600 -229
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +600 -229
- package/dist/index.mjs.map +1 -1
- package/dist/react/components/WalletConnect.tsx +47 -20
- package/dist/react/hooks/usePaymentInfo.ts +38 -4
- package/dist/react/hooks/useWallet.ts +35 -8
- package/dist/react/index.d.mts +16 -55
- package/dist/react/index.d.ts +16 -55
- package/dist/react/index.js +270 -399
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +272 -400
- package/dist/react/index.mjs.map +1 -1
- package/dist/react/index.ts +5 -9
- package/dist/react/store/walletStore.ts +29 -0
- package/dist/react/styles/inline-styles.ts +238 -0
- package/dist/react/styles.css +8 -1
- package/package.json +8 -6
- package/dist/react/components/PaymentButton.tsx +0 -112
package/dist/react/index.js
CHANGED
|
@@ -30,7 +30,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/react/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
-
PaymentButton: () => PaymentButton,
|
|
34
33
|
WalletConnect: () => WalletConnect,
|
|
35
34
|
usePayment: () => usePayment,
|
|
36
35
|
usePaymentInfo: () => usePaymentInfo,
|
|
@@ -45,7 +44,7 @@ var import_react = require("react");
|
|
|
45
44
|
var import_types3 = require("x402/types");
|
|
46
45
|
|
|
47
46
|
// src/types/common.ts
|
|
48
|
-
var PROD_BACK_URL = "https://v402.onvoyage.ai/api";
|
|
47
|
+
var PROD_BACK_URL = "https://v402.onvoyage.ai/api/pay";
|
|
49
48
|
|
|
50
49
|
// src/types/svm.ts
|
|
51
50
|
var import_zod = require("zod");
|
|
@@ -234,300 +233,48 @@ function onAccountsChanged(callback) {
|
|
|
234
233
|
ethereum.removeListener?.("accountsChanged", handler);
|
|
235
234
|
};
|
|
236
235
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
async function createSvmPaymentHeader(params) {
|
|
242
|
-
const { wallet, paymentRequirements, x402Version, rpcUrl } = params;
|
|
243
|
-
const connection = new import_web3.Connection(rpcUrl, "confirmed");
|
|
244
|
-
const feePayer = paymentRequirements?.extra?.feePayer;
|
|
245
|
-
if (typeof feePayer !== "string" || !feePayer) {
|
|
246
|
-
throw new Error("Missing facilitator feePayer in payment requirements (extra.feePayer).");
|
|
247
|
-
}
|
|
248
|
-
const feePayerPubkey = new import_web3.PublicKey(feePayer);
|
|
249
|
-
const walletAddress = wallet?.publicKey?.toString() || wallet?.address;
|
|
250
|
-
if (!walletAddress) {
|
|
251
|
-
throw new Error("Missing connected Solana wallet address or publicKey");
|
|
252
|
-
}
|
|
253
|
-
const userPubkey = new import_web3.PublicKey(walletAddress);
|
|
254
|
-
if (!paymentRequirements?.payTo) {
|
|
255
|
-
throw new Error("Missing payTo in payment requirements");
|
|
256
|
-
}
|
|
257
|
-
const destination = new import_web3.PublicKey(paymentRequirements.payTo);
|
|
258
|
-
const instructions = [];
|
|
259
|
-
instructions.push(
|
|
260
|
-
import_web3.ComputeBudgetProgram.setComputeUnitLimit({
|
|
261
|
-
units: 7e3
|
|
262
|
-
// Sufficient for SPL token transfer
|
|
263
|
-
})
|
|
264
|
-
);
|
|
265
|
-
instructions.push(
|
|
266
|
-
import_web3.ComputeBudgetProgram.setComputeUnitPrice({
|
|
267
|
-
microLamports: 1
|
|
268
|
-
// Minimal price
|
|
269
|
-
})
|
|
270
|
-
);
|
|
271
|
-
if (!paymentRequirements.asset) {
|
|
272
|
-
throw new Error("Missing token mint for SPL transfer");
|
|
273
|
-
}
|
|
274
|
-
const mintPubkey = new import_web3.PublicKey(paymentRequirements.asset);
|
|
275
|
-
const mintInfo = await connection.getAccountInfo(mintPubkey, "confirmed");
|
|
276
|
-
const programId = mintInfo?.owner?.toBase58() === import_spl_token.TOKEN_2022_PROGRAM_ID.toBase58() ? import_spl_token.TOKEN_2022_PROGRAM_ID : import_spl_token.TOKEN_PROGRAM_ID;
|
|
277
|
-
const mint = await (0, import_spl_token.getMint)(connection, mintPubkey, void 0, programId);
|
|
278
|
-
const sourceAta = await (0, import_spl_token.getAssociatedTokenAddress)(
|
|
279
|
-
mintPubkey,
|
|
280
|
-
userPubkey,
|
|
281
|
-
false,
|
|
282
|
-
programId
|
|
283
|
-
);
|
|
284
|
-
const destinationAta = await (0, import_spl_token.getAssociatedTokenAddress)(
|
|
285
|
-
mintPubkey,
|
|
286
|
-
destination,
|
|
287
|
-
false,
|
|
288
|
-
programId
|
|
289
|
-
);
|
|
290
|
-
const sourceAtaInfo = await connection.getAccountInfo(sourceAta, "confirmed");
|
|
291
|
-
if (!sourceAtaInfo) {
|
|
292
|
-
throw new Error(
|
|
293
|
-
`User does not have an Associated Token Account for ${paymentRequirements.asset}. Please create one first or ensure you have the required token.`
|
|
294
|
-
);
|
|
295
|
-
}
|
|
296
|
-
const destAtaInfo = await connection.getAccountInfo(destinationAta, "confirmed");
|
|
297
|
-
if (!destAtaInfo) {
|
|
298
|
-
throw new Error(
|
|
299
|
-
`Destination does not have an Associated Token Account for ${paymentRequirements.asset}. The receiver must create their token account before receiving payments.`
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
const amount = BigInt(paymentRequirements.maxAmountRequired);
|
|
303
|
-
instructions.push(
|
|
304
|
-
(0, import_spl_token.createTransferCheckedInstruction)(
|
|
305
|
-
sourceAta,
|
|
306
|
-
mintPubkey,
|
|
307
|
-
destinationAta,
|
|
308
|
-
userPubkey,
|
|
309
|
-
amount,
|
|
310
|
-
mint.decimals,
|
|
311
|
-
[],
|
|
312
|
-
programId
|
|
313
|
-
)
|
|
314
|
-
);
|
|
315
|
-
const { blockhash } = await connection.getLatestBlockhash("confirmed");
|
|
316
|
-
const message = new import_web3.TransactionMessage({
|
|
317
|
-
payerKey: feePayerPubkey,
|
|
318
|
-
recentBlockhash: blockhash,
|
|
319
|
-
instructions
|
|
320
|
-
}).compileToV0Message();
|
|
321
|
-
const transaction = new import_web3.VersionedTransaction(message);
|
|
322
|
-
if (typeof wallet?.signTransaction !== "function") {
|
|
323
|
-
throw new Error("Connected wallet does not support signTransaction");
|
|
324
|
-
}
|
|
325
|
-
const userSignedTx = await wallet.signTransaction(transaction);
|
|
326
|
-
const serializedTransaction = Buffer.from(userSignedTx.serialize()).toString("base64");
|
|
327
|
-
const paymentPayload = {
|
|
328
|
-
x402Version,
|
|
329
|
-
scheme: paymentRequirements.scheme,
|
|
330
|
-
network: paymentRequirements.network,
|
|
331
|
-
payload: {
|
|
332
|
-
transaction: serializedTransaction
|
|
333
|
-
}
|
|
334
|
-
};
|
|
335
|
-
const paymentHeader = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
|
|
336
|
-
return paymentHeader;
|
|
337
|
-
}
|
|
338
|
-
function getDefaultSolanaRpcUrl(network) {
|
|
339
|
-
const normalized = network.toLowerCase();
|
|
340
|
-
if (normalized === "solana" || normalized === "solana-mainnet") {
|
|
341
|
-
return "https://api.mainnet-beta.solana.com";
|
|
342
|
-
} else if (normalized === "solana-devnet") {
|
|
343
|
-
return "https://api.devnet.solana.com";
|
|
344
|
-
}
|
|
345
|
-
throw new Error(`Unsupported Solana network: ${network}`);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// src/services/svm/payment-handler.ts
|
|
349
|
-
async function handleSvmPayment(endpoint, config, requestInit) {
|
|
350
|
-
const { wallet, network, rpcUrl, maxPaymentAmount } = config;
|
|
351
|
-
const initialResponse = await fetch(endpoint, {
|
|
352
|
-
...requestInit,
|
|
353
|
-
method: requestInit?.method || "POST"
|
|
354
|
-
});
|
|
355
|
-
if (initialResponse.status !== 402) {
|
|
356
|
-
return initialResponse;
|
|
357
|
-
}
|
|
358
|
-
const rawResponse = await initialResponse.json();
|
|
359
|
-
const x402Version = rawResponse.x402Version;
|
|
360
|
-
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
361
|
-
const selectedRequirements = parsedPaymentRequirements.find(
|
|
362
|
-
(req) => req.scheme === "exact" && SolanaNetworkSchema.safeParse(req.network.toLowerCase()).success
|
|
363
|
-
);
|
|
364
|
-
if (!selectedRequirements) {
|
|
365
|
-
console.error(
|
|
366
|
-
"\u274C No suitable Solana payment requirements found. Available networks:",
|
|
367
|
-
parsedPaymentRequirements.map((req) => req.network)
|
|
368
|
-
);
|
|
369
|
-
throw new Error("No suitable Solana payment requirements found");
|
|
370
|
-
}
|
|
371
|
-
if (maxPaymentAmount && maxPaymentAmount > BigInt(0)) {
|
|
372
|
-
if (BigInt(selectedRequirements.maxAmountRequired) > maxPaymentAmount) {
|
|
373
|
-
throw new Error(
|
|
374
|
-
`Payment amount ${selectedRequirements.maxAmountRequired} exceeds maximum allowed ${maxPaymentAmount}`
|
|
375
|
-
);
|
|
376
|
-
}
|
|
236
|
+
function onChainChanged(callback) {
|
|
237
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
238
|
+
return () => {
|
|
239
|
+
};
|
|
377
240
|
}
|
|
378
|
-
const
|
|
379
|
-
const
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
x402Version,
|
|
383
|
-
rpcUrl: effectiveRpcUrl
|
|
384
|
-
});
|
|
385
|
-
const newInit = {
|
|
386
|
-
...requestInit,
|
|
387
|
-
method: requestInit?.method || "POST",
|
|
388
|
-
headers: {
|
|
389
|
-
...requestInit?.headers || {},
|
|
390
|
-
"X-PAYMENT": paymentHeader,
|
|
391
|
-
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
392
|
-
}
|
|
393
|
-
};
|
|
394
|
-
return await fetch(endpoint, newInit);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
// src/services/evm/payment-header.ts
|
|
398
|
-
var import_ethers = require("ethers");
|
|
399
|
-
async function createEvmPaymentHeader(params) {
|
|
400
|
-
const { wallet, paymentRequirements, x402Version, chainId } = params;
|
|
401
|
-
if (!paymentRequirements?.payTo) {
|
|
402
|
-
throw new Error("Missing payTo in payment requirements");
|
|
403
|
-
}
|
|
404
|
-
if (!paymentRequirements?.asset) {
|
|
405
|
-
throw new Error("Missing asset (token contract) in payment requirements");
|
|
406
|
-
}
|
|
407
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
408
|
-
const nonceBytes = import_ethers.ethers.randomBytes(32);
|
|
409
|
-
const nonceBytes32 = import_ethers.ethers.hexlify(nonceBytes);
|
|
410
|
-
const domain = {
|
|
411
|
-
name: paymentRequirements.extra?.name || "USDC",
|
|
412
|
-
version: paymentRequirements.extra?.version || "2",
|
|
413
|
-
chainId,
|
|
414
|
-
verifyingContract: paymentRequirements.asset
|
|
415
|
-
};
|
|
416
|
-
const types = {
|
|
417
|
-
TransferWithAuthorization: [
|
|
418
|
-
{ name: "from", type: "address" },
|
|
419
|
-
{ name: "to", type: "address" },
|
|
420
|
-
{ name: "value", type: "uint256" },
|
|
421
|
-
{ name: "validAfter", type: "uint256" },
|
|
422
|
-
{ name: "validBefore", type: "uint256" },
|
|
423
|
-
{ name: "nonce", type: "bytes32" }
|
|
424
|
-
]
|
|
425
|
-
};
|
|
426
|
-
const authorization = {
|
|
427
|
-
from: wallet.address,
|
|
428
|
-
to: paymentRequirements.payTo,
|
|
429
|
-
value: paymentRequirements.maxAmountRequired,
|
|
430
|
-
validAfter: "0",
|
|
431
|
-
// Effective immediately
|
|
432
|
-
validBefore: String(now + (paymentRequirements.maxTimeoutSeconds || 3600)),
|
|
433
|
-
nonce: nonceBytes32
|
|
434
|
-
};
|
|
435
|
-
const signature = await wallet.signTypedData(domain, types, authorization);
|
|
436
|
-
const headerPayload = {
|
|
437
|
-
x402_version: x402Version,
|
|
438
|
-
x402Version,
|
|
439
|
-
scheme: paymentRequirements.scheme,
|
|
440
|
-
network: paymentRequirements.network,
|
|
441
|
-
payload: {
|
|
442
|
-
signature,
|
|
443
|
-
authorization: {
|
|
444
|
-
from: authorization.from,
|
|
445
|
-
to: authorization.to,
|
|
446
|
-
value: String(authorization.value),
|
|
447
|
-
valid_after: authorization.validAfter,
|
|
448
|
-
validAfter: authorization.validAfter,
|
|
449
|
-
valid_before: authorization.validBefore,
|
|
450
|
-
validBefore: authorization.validBefore,
|
|
451
|
-
nonce: authorization.nonce
|
|
452
|
-
}
|
|
453
|
-
}
|
|
241
|
+
const ethereum = window.ethereum;
|
|
242
|
+
const handler = (chainId) => {
|
|
243
|
+
console.log("\u{1F504} Chain changed to:", chainId);
|
|
244
|
+
callback(chainId);
|
|
454
245
|
};
|
|
455
|
-
|
|
456
|
-
return
|
|
457
|
-
|
|
458
|
-
function getChainIdFromNetwork(network) {
|
|
459
|
-
const chainIdMap = {
|
|
460
|
-
"ethereum": 1,
|
|
461
|
-
"sepolia": 11155111,
|
|
462
|
-
"base": 8453,
|
|
463
|
-
"base-sepolia": 84532,
|
|
464
|
-
"polygon": 137,
|
|
465
|
-
"arbitrum": 42161,
|
|
466
|
-
"optimism": 10
|
|
246
|
+
ethereum.on("chainChanged", handler);
|
|
247
|
+
return () => {
|
|
248
|
+
ethereum.removeListener?.("chainChanged", handler);
|
|
467
249
|
};
|
|
468
|
-
const chainId = chainIdMap[network.toLowerCase()];
|
|
469
|
-
if (!chainId) {
|
|
470
|
-
throw new Error(`Unknown network: ${network}`);
|
|
471
|
-
}
|
|
472
|
-
return chainId;
|
|
473
250
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const initialResponse = await fetch(endpoint, {
|
|
479
|
-
...requestInit,
|
|
480
|
-
method: requestInit?.method || "POST"
|
|
481
|
-
});
|
|
482
|
-
if (initialResponse.status !== 402) {
|
|
483
|
-
return initialResponse;
|
|
484
|
-
}
|
|
485
|
-
const rawResponse = await initialResponse.json();
|
|
486
|
-
const x402Version = rawResponse.x402Version;
|
|
487
|
-
const parsedPaymentRequirements = rawResponse.accepts || [];
|
|
488
|
-
const selectedRequirements = parsedPaymentRequirements.find(
|
|
489
|
-
(req) => req.scheme === "exact" && EvmNetworkSchema.safeParse(req.network.toLowerCase()).success
|
|
490
|
-
);
|
|
491
|
-
if (!selectedRequirements) {
|
|
492
|
-
console.error(
|
|
493
|
-
"\u274C No suitable EVM payment requirements found. Available networks:",
|
|
494
|
-
parsedPaymentRequirements.map((req) => req.network)
|
|
495
|
-
);
|
|
496
|
-
throw new Error("No suitable EVM payment requirements found");
|
|
497
|
-
}
|
|
498
|
-
if (maxPaymentAmount && maxPaymentAmount > BigInt(0)) {
|
|
499
|
-
if (BigInt(selectedRequirements.maxAmountRequired) > maxPaymentAmount) {
|
|
500
|
-
throw new Error(
|
|
501
|
-
`Payment amount ${selectedRequirements.maxAmountRequired} exceeds maximum allowed ${maxPaymentAmount}`
|
|
502
|
-
);
|
|
503
|
-
}
|
|
251
|
+
function onWalletDisconnect(callback) {
|
|
252
|
+
if (typeof window === "undefined") {
|
|
253
|
+
return () => {
|
|
254
|
+
};
|
|
504
255
|
}
|
|
505
|
-
const
|
|
506
|
-
if (
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
} catch (error) {
|
|
510
|
-
console.warn("Failed to switch chain:", error);
|
|
511
|
-
}
|
|
256
|
+
const solana = window.solana;
|
|
257
|
+
if (!solana) {
|
|
258
|
+
return () => {
|
|
259
|
+
};
|
|
512
260
|
}
|
|
513
|
-
const
|
|
514
|
-
wallet
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
...requestInit,
|
|
521
|
-
method: requestInit?.method || "POST",
|
|
522
|
-
headers: {
|
|
523
|
-
...requestInit?.headers || {},
|
|
524
|
-
"X-PAYMENT": paymentHeader,
|
|
525
|
-
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE"
|
|
526
|
-
}
|
|
261
|
+
const handler = () => {
|
|
262
|
+
console.log("\u{1F50C} Solana wallet disconnected");
|
|
263
|
+
callback();
|
|
264
|
+
};
|
|
265
|
+
solana.on("disconnect", handler);
|
|
266
|
+
return () => {
|
|
267
|
+
solana.removeListener?.("disconnect", handler);
|
|
527
268
|
};
|
|
528
|
-
return await fetch(endpoint, newInit);
|
|
529
269
|
}
|
|
530
270
|
|
|
271
|
+
// src/services/svm/payment-header.ts
|
|
272
|
+
var import_web3 = require("@solana/web3.js");
|
|
273
|
+
var import_spl_token = require("@solana/spl-token");
|
|
274
|
+
|
|
275
|
+
// src/services/evm/payment-header.ts
|
|
276
|
+
var import_ethers = require("ethers");
|
|
277
|
+
|
|
531
278
|
// src/utils/payment-helpers.ts
|
|
532
279
|
var import_ethers2 = require("ethers");
|
|
533
280
|
function parsePaymentRequired(response) {
|
|
@@ -553,62 +300,6 @@ function getSupportedNetworkTypes(paymentRequirements) {
|
|
|
553
300
|
});
|
|
554
301
|
return Array.from(networkTypes);
|
|
555
302
|
}
|
|
556
|
-
async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
|
|
557
|
-
endpoint = `${endpoint}/${merchantId}`;
|
|
558
|
-
let response;
|
|
559
|
-
if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
|
|
560
|
-
const solana = window.solana;
|
|
561
|
-
if (!solana) {
|
|
562
|
-
throw new Error("\u8BF7\u5B89\u88C5 Phantom \u94B1\u5305");
|
|
563
|
-
}
|
|
564
|
-
if (!solana.isConnected) {
|
|
565
|
-
await solana.connect();
|
|
566
|
-
}
|
|
567
|
-
response = await handleSvmPayment(endpoint, {
|
|
568
|
-
wallet: solana,
|
|
569
|
-
network: "solana-devnet"
|
|
570
|
-
});
|
|
571
|
-
} else if (networkType === "evm" /* EVM */) {
|
|
572
|
-
if (!window.ethereum) {
|
|
573
|
-
throw new Error("\u8BF7\u5B89\u88C5 MetaMask \u94B1\u5305");
|
|
574
|
-
}
|
|
575
|
-
const provider = new import_ethers2.ethers.BrowserProvider(window.ethereum);
|
|
576
|
-
const signer = await provider.getSigner();
|
|
577
|
-
const wallet = {
|
|
578
|
-
address: await signer.getAddress(),
|
|
579
|
-
signTypedData: async (domain, types, message) => {
|
|
580
|
-
return await signer.signTypedData(domain, types, message);
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
const network = endpoint.includes("sepolia") ? "base-sepolia" : "base";
|
|
584
|
-
response = await handleEvmPayment(endpoint, {
|
|
585
|
-
wallet,
|
|
586
|
-
network
|
|
587
|
-
});
|
|
588
|
-
} else {
|
|
589
|
-
throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
|
|
590
|
-
}
|
|
591
|
-
return response;
|
|
592
|
-
}
|
|
593
|
-
async function handlePayment(endpoint, networkType, callbacks) {
|
|
594
|
-
try {
|
|
595
|
-
callbacks?.onStart?.();
|
|
596
|
-
const response = await makePayment(networkType, "", endpoint);
|
|
597
|
-
if (!response.ok) {
|
|
598
|
-
const errorText = await response.text();
|
|
599
|
-
throw new Error(`\u8BF7\u6C42\u5931\u8D25 (${response.status}): ${errorText}`);
|
|
600
|
-
}
|
|
601
|
-
const result = await response.json();
|
|
602
|
-
callbacks?.onSuccess?.(result);
|
|
603
|
-
return result;
|
|
604
|
-
} catch (err) {
|
|
605
|
-
const errorMessage = err.message || "\u652F\u4ED8\u5931\u8D25";
|
|
606
|
-
callbacks?.onError?.(errorMessage);
|
|
607
|
-
throw err;
|
|
608
|
-
} finally {
|
|
609
|
-
callbacks?.onFinish?.();
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
303
|
|
|
613
304
|
// src/utils/network.ts
|
|
614
305
|
var NETWORK_TYPE_MAP = {
|
|
@@ -632,6 +323,7 @@ var NETWORK_TYPE_MAP = {
|
|
|
632
323
|
};
|
|
633
324
|
function getNetworkDisplayName(network) {
|
|
634
325
|
const displayNames = {
|
|
326
|
+
"evm": "EVM",
|
|
635
327
|
"ethereum": "Ethereum",
|
|
636
328
|
"sepolia": "Sepolia Testnet",
|
|
637
329
|
"base": "Base",
|
|
@@ -677,6 +369,29 @@ var WalletStore = class {
|
|
|
677
369
|
}
|
|
678
370
|
}
|
|
679
371
|
});
|
|
372
|
+
onChainChanged(() => {
|
|
373
|
+
const connectedType = getConnectedNetworkType();
|
|
374
|
+
if (connectedType === "evm" /* EVM */) {
|
|
375
|
+
console.log("\u26A0\uFE0F Network changed detected - disconnecting wallet");
|
|
376
|
+
disconnectWallet();
|
|
377
|
+
this.setState({
|
|
378
|
+
address: null,
|
|
379
|
+
networkType: null,
|
|
380
|
+
error: "Network changed. Please reconnect your wallet."
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
onWalletDisconnect(() => {
|
|
385
|
+
const connectedType = getConnectedNetworkType();
|
|
386
|
+
if (connectedType === "solana" /* SOLANA */ || connectedType === "svm" /* SVM */) {
|
|
387
|
+
console.log("\u26A0\uFE0F Solana wallet disconnected");
|
|
388
|
+
disconnectWallet();
|
|
389
|
+
this.setState({
|
|
390
|
+
address: null,
|
|
391
|
+
networkType: null
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
});
|
|
680
395
|
}
|
|
681
396
|
async autoReconnect() {
|
|
682
397
|
if (!isWalletManuallyDisconnected()) {
|
|
@@ -801,7 +516,7 @@ function usePayment() {
|
|
|
801
516
|
|
|
802
517
|
// src/react/hooks/usePaymentInfo.ts
|
|
803
518
|
var import_react3 = require("react");
|
|
804
|
-
function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
519
|
+
function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL, additionalParams) {
|
|
805
520
|
const [paymentInfo, setPaymentInfo] = (0, import_react3.useState)(null);
|
|
806
521
|
const [supportedNetworks, setSupportedNetworks] = (0, import_react3.useState)([]);
|
|
807
522
|
const [isLoading, setIsLoading] = (0, import_react3.useState)(true);
|
|
@@ -810,8 +525,17 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
810
525
|
setIsLoading(true);
|
|
811
526
|
setError(null);
|
|
812
527
|
try {
|
|
813
|
-
|
|
814
|
-
const
|
|
528
|
+
const fullEndpoint = `${endpoint}/${merchantId}`;
|
|
529
|
+
const requestInit = {
|
|
530
|
+
method: "POST",
|
|
531
|
+
...additionalParams && Object.keys(additionalParams).length > 0 ? {
|
|
532
|
+
body: JSON.stringify(additionalParams),
|
|
533
|
+
headers: {
|
|
534
|
+
"Content-Type": "application/json"
|
|
535
|
+
}
|
|
536
|
+
} : {}
|
|
537
|
+
};
|
|
538
|
+
const response = await fetch(fullEndpoint, requestInit);
|
|
815
539
|
if (response.status === 402) {
|
|
816
540
|
const body = await response.json();
|
|
817
541
|
const payment = parsePaymentRequired(body);
|
|
@@ -832,7 +556,7 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
832
556
|
};
|
|
833
557
|
(0, import_react3.useEffect)(() => {
|
|
834
558
|
fetchPaymentInfo();
|
|
835
|
-
}, [endpoint]);
|
|
559
|
+
}, [endpoint, merchantId]);
|
|
836
560
|
return {
|
|
837
561
|
paymentInfo,
|
|
838
562
|
supportedNetworks,
|
|
@@ -844,6 +568,187 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
|
|
|
844
568
|
|
|
845
569
|
// src/react/components/WalletConnect.tsx
|
|
846
570
|
var import_react4 = __toESM(require("react"));
|
|
571
|
+
|
|
572
|
+
// src/react/styles/inline-styles.ts
|
|
573
|
+
var isDarkMode = () => {
|
|
574
|
+
if (typeof window === "undefined") return false;
|
|
575
|
+
return window.matchMedia?.("(prefers-color-scheme: dark)").matches ?? false;
|
|
576
|
+
};
|
|
577
|
+
var colors = {
|
|
578
|
+
// Light mode
|
|
579
|
+
light: {
|
|
580
|
+
background: "#fafafa",
|
|
581
|
+
cardBg: "#ffffff",
|
|
582
|
+
text: "#0a0a0a",
|
|
583
|
+
textSecondary: "#737373",
|
|
584
|
+
primary: "#000000",
|
|
585
|
+
primaryHover: "#262626",
|
|
586
|
+
danger: "#ef4444",
|
|
587
|
+
dangerHover: "#dc2626",
|
|
588
|
+
success: "#10b981",
|
|
589
|
+
successHover: "#059669",
|
|
590
|
+
disabled: "#e5e5e5",
|
|
591
|
+
disabledText: "#a3a3a3",
|
|
592
|
+
errorBg: "#fef2f2",
|
|
593
|
+
errorText: "#dc2626"
|
|
594
|
+
},
|
|
595
|
+
// Dark mode
|
|
596
|
+
dark: {
|
|
597
|
+
background: "#0a0a0a",
|
|
598
|
+
cardBg: "#171717",
|
|
599
|
+
text: "#fafafa",
|
|
600
|
+
textSecondary: "#a3a3a3",
|
|
601
|
+
primary: "#ffffff",
|
|
602
|
+
primaryHover: "#e5e5e5",
|
|
603
|
+
danger: "#f87171",
|
|
604
|
+
dangerHover: "#ef4444",
|
|
605
|
+
success: "#34d399",
|
|
606
|
+
successHover: "#10b981",
|
|
607
|
+
disabled: "#262626",
|
|
608
|
+
disabledText: "#525252",
|
|
609
|
+
errorBg: "#1c1917",
|
|
610
|
+
errorText: "#f87171"
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
var getColors = () => {
|
|
614
|
+
return isDarkMode() ? colors.dark : colors.light;
|
|
615
|
+
};
|
|
616
|
+
var containerStyle = {
|
|
617
|
+
width: "100%",
|
|
618
|
+
maxWidth: "420px",
|
|
619
|
+
margin: "0 auto"
|
|
620
|
+
};
|
|
621
|
+
var getSectionStyle = () => {
|
|
622
|
+
const c = getColors();
|
|
623
|
+
return {
|
|
624
|
+
padding: "1.5rem",
|
|
625
|
+
background: c.cardBg,
|
|
626
|
+
borderRadius: "12px"
|
|
627
|
+
};
|
|
628
|
+
};
|
|
629
|
+
var getTitleStyle = () => {
|
|
630
|
+
const c = getColors();
|
|
631
|
+
return {
|
|
632
|
+
margin: "0 0 1.25rem 0",
|
|
633
|
+
fontSize: "1.125rem",
|
|
634
|
+
fontWeight: 600,
|
|
635
|
+
color: c.text,
|
|
636
|
+
letterSpacing: "-0.01em"
|
|
637
|
+
};
|
|
638
|
+
};
|
|
639
|
+
var buttonsContainerStyle = {
|
|
640
|
+
display: "flex",
|
|
641
|
+
flexDirection: "column",
|
|
642
|
+
gap: "0.75rem"
|
|
643
|
+
};
|
|
644
|
+
var walletOptionStyle = {
|
|
645
|
+
display: "flex",
|
|
646
|
+
flexDirection: "column",
|
|
647
|
+
gap: "0.5rem"
|
|
648
|
+
};
|
|
649
|
+
var baseButtonStyle = {
|
|
650
|
+
padding: "0.875rem 1.25rem",
|
|
651
|
+
fontSize: "0.9375rem",
|
|
652
|
+
fontWeight: 500,
|
|
653
|
+
border: "none",
|
|
654
|
+
borderRadius: "8px",
|
|
655
|
+
cursor: "pointer",
|
|
656
|
+
transition: "background-color 0.15s ease, opacity 0.15s ease",
|
|
657
|
+
outline: "none",
|
|
658
|
+
letterSpacing: "-0.01em"
|
|
659
|
+
};
|
|
660
|
+
var getConnectButtonStyle = (isDisabled, isHovered) => {
|
|
661
|
+
const c = getColors();
|
|
662
|
+
const darkMode = isDarkMode();
|
|
663
|
+
if (isDisabled) {
|
|
664
|
+
return {
|
|
665
|
+
...baseButtonStyle,
|
|
666
|
+
background: c.disabled,
|
|
667
|
+
color: c.disabledText,
|
|
668
|
+
cursor: "not-allowed",
|
|
669
|
+
border: darkMode ? "1px solid #404040" : "1px solid #d4d4d4"
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
return {
|
|
673
|
+
...baseButtonStyle,
|
|
674
|
+
background: isHovered ? c.primaryHover : c.primary,
|
|
675
|
+
color: darkMode ? "#000000" : "#ffffff",
|
|
676
|
+
cursor: "pointer"
|
|
677
|
+
};
|
|
678
|
+
};
|
|
679
|
+
var getDisconnectButtonStyle = (isHovered) => {
|
|
680
|
+
const c = getColors();
|
|
681
|
+
return {
|
|
682
|
+
...baseButtonStyle,
|
|
683
|
+
background: isHovered ? c.dangerHover : c.danger,
|
|
684
|
+
color: "#ffffff"
|
|
685
|
+
};
|
|
686
|
+
};
|
|
687
|
+
var getInstallLinkStyle = (isHovered) => {
|
|
688
|
+
const c = getColors();
|
|
689
|
+
return {
|
|
690
|
+
display: "inline-block",
|
|
691
|
+
padding: "0.5rem",
|
|
692
|
+
fontSize: "0.8125rem",
|
|
693
|
+
color: c.textSecondary,
|
|
694
|
+
textDecoration: isHovered ? "underline" : "none",
|
|
695
|
+
textAlign: "center",
|
|
696
|
+
fontWeight: 500
|
|
697
|
+
};
|
|
698
|
+
};
|
|
699
|
+
var walletAddressStyle = {
|
|
700
|
+
display: "flex",
|
|
701
|
+
flexDirection: "column",
|
|
702
|
+
gap: "0.5rem",
|
|
703
|
+
marginBottom: "1rem"
|
|
704
|
+
};
|
|
705
|
+
var getLabelStyle = () => {
|
|
706
|
+
const c = getColors();
|
|
707
|
+
return {
|
|
708
|
+
fontSize: "0.8125rem",
|
|
709
|
+
color: c.textSecondary,
|
|
710
|
+
fontWeight: 500,
|
|
711
|
+
textTransform: "uppercase",
|
|
712
|
+
letterSpacing: "0.05em"
|
|
713
|
+
};
|
|
714
|
+
};
|
|
715
|
+
var getAddressStyle = () => {
|
|
716
|
+
const c = getColors();
|
|
717
|
+
return {
|
|
718
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
719
|
+
fontSize: "0.9375rem",
|
|
720
|
+
fontWeight: 500,
|
|
721
|
+
color: c.text,
|
|
722
|
+
letterSpacing: "-0.01em"
|
|
723
|
+
};
|
|
724
|
+
};
|
|
725
|
+
var walletActionsStyle = {
|
|
726
|
+
margin: "1rem 0"
|
|
727
|
+
};
|
|
728
|
+
var getHintStyle = () => {
|
|
729
|
+
const c = getColors();
|
|
730
|
+
return {
|
|
731
|
+
marginTop: "1rem",
|
|
732
|
+
fontSize: "0.8125rem",
|
|
733
|
+
color: c.textSecondary,
|
|
734
|
+
textAlign: "center",
|
|
735
|
+
lineHeight: "1.5"
|
|
736
|
+
};
|
|
737
|
+
};
|
|
738
|
+
var getErrorStyle = () => {
|
|
739
|
+
const c = getColors();
|
|
740
|
+
return {
|
|
741
|
+
marginTop: "1rem",
|
|
742
|
+
padding: "0.75rem 1rem",
|
|
743
|
+
background: c.errorBg,
|
|
744
|
+
color: c.errorText,
|
|
745
|
+
borderRadius: "8px",
|
|
746
|
+
fontSize: "0.8125rem",
|
|
747
|
+
fontWeight: 500
|
|
748
|
+
};
|
|
749
|
+
};
|
|
750
|
+
|
|
751
|
+
// src/react/components/WalletConnect.tsx
|
|
847
752
|
function WalletConnect({
|
|
848
753
|
supportedNetworks = ["solana" /* SOLANA */, "evm" /* EVM */],
|
|
849
754
|
className = "",
|
|
@@ -851,6 +756,8 @@ function WalletConnect({
|
|
|
851
756
|
onDisconnect
|
|
852
757
|
}) {
|
|
853
758
|
const { address, networkType, isConnecting, error, connect, disconnect } = useWallet();
|
|
759
|
+
const [hoveredButton, setHoveredButton] = (0, import_react4.useState)(null);
|
|
760
|
+
const [hoveredLink, setHoveredLink] = (0, import_react4.useState)(null);
|
|
854
761
|
const handleConnect = async (network) => {
|
|
855
762
|
try {
|
|
856
763
|
await connect(network);
|
|
@@ -861,14 +768,16 @@ function WalletConnect({
|
|
|
861
768
|
disconnect();
|
|
862
769
|
onDisconnect?.();
|
|
863
770
|
};
|
|
864
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", {
|
|
771
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { style: { ...containerStyle, ...className ? {} : {} }, className }, !address ? /* @__PURE__ */ import_react4.default.createElement("div", { style: getSectionStyle() }, /* @__PURE__ */ import_react4.default.createElement("h3", { style: getTitleStyle() }, "Connect Wallet"), supportedNetworks.length === 0 ? /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "Please install a supported wallet extension") : /* @__PURE__ */ import_react4.default.createElement("div", { style: buttonsContainerStyle }, supportedNetworks.map((network) => {
|
|
865
772
|
const installed = isWalletInstalled(network);
|
|
866
|
-
return /* @__PURE__ */ import_react4.default.createElement("div", { key: network,
|
|
773
|
+
return /* @__PURE__ */ import_react4.default.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ import_react4.default.createElement(
|
|
867
774
|
"button",
|
|
868
775
|
{
|
|
869
|
-
|
|
776
|
+
style: getConnectButtonStyle(isConnecting || !installed, hoveredButton === network),
|
|
870
777
|
onClick: () => handleConnect(network),
|
|
871
|
-
disabled: isConnecting || !installed
|
|
778
|
+
disabled: isConnecting || !installed,
|
|
779
|
+
onMouseEnter: () => setHoveredButton(network),
|
|
780
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
872
781
|
},
|
|
873
782
|
isConnecting ? "Connecting..." : getNetworkDisplayName(network)
|
|
874
783
|
), !installed && /* @__PURE__ */ import_react4.default.createElement(
|
|
@@ -877,63 +786,25 @@ function WalletConnect({
|
|
|
877
786
|
href: getWalletInstallUrl(network),
|
|
878
787
|
target: "_blank",
|
|
879
788
|
rel: "noopener noreferrer",
|
|
880
|
-
|
|
789
|
+
style: getInstallLinkStyle(hoveredLink === network),
|
|
790
|
+
onMouseEnter: () => setHoveredLink(network),
|
|
791
|
+
onMouseLeave: () => setHoveredLink(null)
|
|
881
792
|
},
|
|
882
793
|
"Install Wallet"
|
|
883
794
|
));
|
|
884
|
-
})), error && /* @__PURE__ */ import_react4.default.createElement("p", {
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
// src/react/components/PaymentButton.tsx
|
|
888
|
-
var import_react5 = __toESM(require("react"));
|
|
889
|
-
function PaymentButton({
|
|
890
|
-
endpoint,
|
|
891
|
-
className = "",
|
|
892
|
-
disabled = false,
|
|
893
|
-
onSuccess,
|
|
894
|
-
onError,
|
|
895
|
-
onStart,
|
|
896
|
-
onFinish,
|
|
897
|
-
children = "Pay Now"
|
|
898
|
-
}) {
|
|
899
|
-
const { networkType } = useWallet();
|
|
900
|
-
const { isProcessing, setIsProcessing, setResult, setError, error } = usePayment();
|
|
901
|
-
const handleClick = async () => {
|
|
902
|
-
if (!networkType) {
|
|
903
|
-
const errorMsg = "Please connect wallet first";
|
|
904
|
-
setError(errorMsg);
|
|
905
|
-
onError?.(errorMsg);
|
|
906
|
-
return;
|
|
907
|
-
}
|
|
908
|
-
try {
|
|
909
|
-
onStart?.();
|
|
910
|
-
setIsProcessing(true);
|
|
911
|
-
setError(null);
|
|
912
|
-
const result = await handlePayment(endpoint, networkType);
|
|
913
|
-
setResult(result);
|
|
914
|
-
onSuccess?.(result);
|
|
915
|
-
} catch (err) {
|
|
916
|
-
const errorMsg = err.message || "Payment failed";
|
|
917
|
-
setError(errorMsg);
|
|
918
|
-
onError?.(errorMsg);
|
|
919
|
-
} finally {
|
|
920
|
-
setIsProcessing(false);
|
|
921
|
-
onFinish?.();
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(
|
|
795
|
+
})), 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(
|
|
925
796
|
"button",
|
|
926
797
|
{
|
|
927
|
-
|
|
928
|
-
onClick:
|
|
929
|
-
|
|
798
|
+
style: getDisconnectButtonStyle(hoveredButton === "disconnect"),
|
|
799
|
+
onClick: handleDisconnect,
|
|
800
|
+
onMouseEnter: () => setHoveredButton("disconnect"),
|
|
801
|
+
onMouseLeave: () => setHoveredButton(null)
|
|
930
802
|
},
|
|
931
|
-
|
|
932
|
-
),
|
|
803
|
+
"Disconnect"
|
|
804
|
+
)), /* @__PURE__ */ import_react4.default.createElement("p", { style: getHintStyle() }, "Switch account in your wallet to change address")));
|
|
933
805
|
}
|
|
934
806
|
// Annotate the CommonJS export names for ESM import in node:
|
|
935
807
|
0 && (module.exports = {
|
|
936
|
-
PaymentButton,
|
|
937
808
|
WalletConnect,
|
|
938
809
|
usePayment,
|
|
939
810
|
usePaymentInfo,
|