@voyage_ai/v402-web-ts 0.1.1 → 0.1.3

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.
@@ -1,5 +1,64 @@
1
- // src/react/hooks/useWalletStore.ts
2
- import { useSyncExternalStore } from "react";
1
+ var __getOwnPropNames = Object.getOwnPropertyNames;
2
+ var __esm = (fn, res) => function __init() {
3
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
+ };
5
+
6
+ // src/types/common.ts
7
+ var PROD_BACK_URL, DEV_BACK_URL;
8
+ var init_common = __esm({
9
+ "src/types/common.ts"() {
10
+ "use strict";
11
+ PROD_BACK_URL = "https://v402.onvoyage.ai/api/pay";
12
+ DEV_BACK_URL = "http://localhost:3000/api/pay";
13
+ }
14
+ });
15
+
16
+ // src/types/svm.ts
17
+ import { z } from "zod";
18
+ import { ExactSvmPayloadSchema } from "x402/types";
19
+ var SolanaNetworkSchema, SolanaPaymentPayloadSchema;
20
+ var init_svm = __esm({
21
+ "src/types/svm.ts"() {
22
+ "use strict";
23
+ SolanaNetworkSchema = z.enum([
24
+ "solana-devnet",
25
+ "solana",
26
+ "solana-mainnet"
27
+ // Alias for mainnet
28
+ ]);
29
+ SolanaPaymentPayloadSchema = z.object({
30
+ x402Version: z.literal(1),
31
+ scheme: z.literal("exact"),
32
+ network: SolanaNetworkSchema,
33
+ payload: ExactSvmPayloadSchema
34
+ });
35
+ }
36
+ });
37
+
38
+ // src/types/evm.ts
39
+ import { z as z2 } from "zod";
40
+ import { ExactEvmPayloadSchema } from "x402/types";
41
+ var EvmNetworkSchema, EvmPaymentPayloadSchema;
42
+ var init_evm = __esm({
43
+ "src/types/evm.ts"() {
44
+ "use strict";
45
+ EvmNetworkSchema = z2.enum([
46
+ "ethereum",
47
+ "sepolia",
48
+ "base",
49
+ "base-sepolia",
50
+ "polygon",
51
+ "arbitrum",
52
+ "optimism"
53
+ ]);
54
+ EvmPaymentPayloadSchema = z2.object({
55
+ x402Version: z2.literal(1),
56
+ scheme: z2.literal("exact"),
57
+ network: EvmNetworkSchema,
58
+ payload: ExactEvmPayloadSchema
59
+ });
60
+ }
61
+ });
3
62
 
4
63
  // src/types/index.ts
5
64
  import {
@@ -9,53 +68,18 @@ import {
9
68
  SettleResponseSchema,
10
69
  SupportedPaymentKindSchema,
11
70
  SupportedPaymentKindsResponseSchema,
12
- ExactSvmPayloadSchema as ExactSvmPayloadSchema2,
13
- ExactEvmPayloadSchema as ExactEvmPayloadSchema2,
14
71
  SupportedSVMNetworks,
15
72
  SvmNetworkToChainId
16
73
  } from "x402/types";
17
-
18
- // src/types/common.ts
19
- var PROD_BACK_URL = "https://v402.onvoyage.ai/api/pay";
20
-
21
- // src/types/svm.ts
22
- import { z } from "zod";
23
- import { ExactSvmPayloadSchema } from "x402/types";
24
- var SolanaNetworkSchema = z.enum([
25
- "solana-devnet",
26
- "solana",
27
- "solana-mainnet"
28
- // Alias for mainnet
29
- ]);
30
- var SolanaPaymentPayloadSchema = z.object({
31
- x402Version: z.literal(1),
32
- scheme: z.literal("exact"),
33
- network: SolanaNetworkSchema,
34
- payload: ExactSvmPayloadSchema
35
- });
36
-
37
- // src/types/evm.ts
38
- import { z as z2 } from "zod";
39
- import { ExactEvmPayloadSchema } from "x402/types";
40
- var EvmNetworkSchema = z2.enum([
41
- "ethereum",
42
- "sepolia",
43
- "base",
44
- "base-sepolia",
45
- "polygon",
46
- "arbitrum",
47
- "optimism"
48
- ]);
49
- var EvmPaymentPayloadSchema = z2.object({
50
- x402Version: z2.literal(1),
51
- scheme: z2.literal("exact"),
52
- network: EvmNetworkSchema,
53
- payload: ExactEvmPayloadSchema
74
+ var init_types = __esm({
75
+ "src/types/index.ts"() {
76
+ "use strict";
77
+ init_svm();
78
+ init_evm();
79
+ }
54
80
  });
55
81
 
56
82
  // src/utils/wallet.ts
57
- var WALLET_DISCONNECTED_KEY = "wallet_manually_disconnected";
58
- var CONNECTED_NETWORK_TYPE_KEY = "connected_network_type";
59
83
  function isWalletInstalled(networkType) {
60
84
  if (typeof window === "undefined") {
61
85
  return false;
@@ -76,22 +100,50 @@ function formatAddress(address) {
76
100
  }
77
101
  return `${address.slice(0, 6)}...${address.slice(-4)}`;
78
102
  }
79
- function markWalletDisconnected() {
103
+ function getDisconnectedNetworks() {
104
+ if (typeof window === "undefined") {
105
+ return {};
106
+ }
107
+ try {
108
+ const cached = localStorage.getItem(WALLET_DISCONNECTED_NETWORKS_KEY);
109
+ return cached ? JSON.parse(cached) : {};
110
+ } catch (error) {
111
+ return {};
112
+ }
113
+ }
114
+ function markWalletDisconnected(networkType) {
80
115
  if (typeof window !== "undefined") {
81
- localStorage.setItem(WALLET_DISCONNECTED_KEY, "true");
82
- localStorage.removeItem(CONNECTED_NETWORK_TYPE_KEY);
116
+ if (networkType) {
117
+ const disconnected = getDisconnectedNetworks();
118
+ disconnected[networkType] = true;
119
+ localStorage.setItem(WALLET_DISCONNECTED_NETWORKS_KEY, JSON.stringify(disconnected));
120
+ } else {
121
+ localStorage.setItem(WALLET_DISCONNECTED_KEY, "true");
122
+ localStorage.removeItem(CONNECTED_NETWORK_TYPE_KEY);
123
+ }
83
124
  }
84
125
  }
85
- function clearWalletDisconnection() {
126
+ function clearWalletDisconnection(networkType) {
86
127
  if (typeof window !== "undefined") {
87
- localStorage.removeItem(WALLET_DISCONNECTED_KEY);
128
+ if (networkType) {
129
+ const disconnected = getDisconnectedNetworks();
130
+ delete disconnected[networkType];
131
+ localStorage.setItem(WALLET_DISCONNECTED_NETWORKS_KEY, JSON.stringify(disconnected));
132
+ } else {
133
+ localStorage.removeItem(WALLET_DISCONNECTED_KEY);
134
+ }
88
135
  }
89
136
  }
90
- function isWalletManuallyDisconnected() {
137
+ function isWalletManuallyDisconnected(networkType) {
91
138
  if (typeof window === "undefined") {
92
139
  return false;
93
140
  }
94
- return localStorage.getItem(WALLET_DISCONNECTED_KEY) === "true";
141
+ if (networkType) {
142
+ const disconnected = getDisconnectedNetworks();
143
+ return disconnected[networkType] === true;
144
+ } else {
145
+ return localStorage.getItem(WALLET_DISCONNECTED_KEY) === "true";
146
+ }
95
147
  }
96
148
  function saveConnectedNetworkType(networkType) {
97
149
  if (typeof window !== "undefined") {
@@ -116,8 +168,60 @@ function getWalletInstallUrl(networkType) {
116
168
  return "#";
117
169
  }
118
170
  }
171
+ function getAllWalletAddresses() {
172
+ if (typeof window === "undefined") {
173
+ return {};
174
+ }
175
+ try {
176
+ const cached = localStorage.getItem(WALLET_ADDRESSES_KEY);
177
+ return cached ? JSON.parse(cached) : {};
178
+ } catch (error) {
179
+ console.error("Failed to parse wallet addresses cache:", error);
180
+ return {};
181
+ }
182
+ }
183
+ function saveWalletAddress(networkType, address) {
184
+ if (typeof window === "undefined") {
185
+ return;
186
+ }
187
+ const addresses = getAllWalletAddresses();
188
+ addresses[networkType] = address;
189
+ localStorage.setItem(WALLET_ADDRESSES_KEY, JSON.stringify(addresses));
190
+ }
191
+ function getCachedWalletAddress(networkType) {
192
+ const addresses = getAllWalletAddresses();
193
+ return addresses[networkType] || null;
194
+ }
195
+ function removeWalletAddress(networkType) {
196
+ if (typeof window === "undefined") {
197
+ return;
198
+ }
199
+ const addresses = getAllWalletAddresses();
200
+ delete addresses[networkType];
201
+ localStorage.setItem(WALLET_ADDRESSES_KEY, JSON.stringify(addresses));
202
+ }
203
+ var WALLET_DISCONNECTED_KEY, WALLET_DISCONNECTED_NETWORKS_KEY, CONNECTED_NETWORK_TYPE_KEY, WALLET_ADDRESSES_KEY;
204
+ var init_wallet = __esm({
205
+ "src/utils/wallet.ts"() {
206
+ "use strict";
207
+ WALLET_DISCONNECTED_KEY = "wallet_manually_disconnected";
208
+ WALLET_DISCONNECTED_NETWORKS_KEY = "wallet_disconnected_networks";
209
+ CONNECTED_NETWORK_TYPE_KEY = "connected_network_type";
210
+ WALLET_ADDRESSES_KEY = "wallet_addresses_cache";
211
+ }
212
+ });
213
+
214
+ // src/react/index.ts
215
+ import "./styles.css";
216
+
217
+ // src/react/hooks/useWalletStore.ts
218
+ import { useSyncExternalStore } from "react";
219
+
220
+ // src/utils/index.ts
221
+ init_wallet();
119
222
 
120
223
  // src/utils/wallet-connect.ts
224
+ init_wallet();
121
225
  async function connectWallet(networkType) {
122
226
  if (typeof window === "undefined") {
123
227
  throw new Error("\u8BF7\u5728\u6D4F\u89C8\u5668\u73AF\u5883\u4E2D\u4F7F\u7528");
@@ -152,13 +256,11 @@ async function connectWallet(networkType) {
152
256
  default:
153
257
  throw new Error("\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B");
154
258
  }
155
- clearWalletDisconnection();
259
+ clearWalletDisconnection(networkType);
156
260
  saveConnectedNetworkType(networkType);
261
+ saveWalletAddress(networkType, address);
157
262
  return address;
158
263
  }
159
- function disconnectWallet() {
160
- markWalletDisconnected();
161
- }
162
264
  async function getCurrentWallet(networkType) {
163
265
  if (typeof window === "undefined") {
164
266
  return null;
@@ -167,28 +269,36 @@ async function getCurrentWallet(networkType) {
167
269
  if (!type) {
168
270
  return null;
169
271
  }
272
+ const cachedAddress = getCachedWalletAddress(type);
170
273
  try {
274
+ let currentAddress = null;
171
275
  switch (type) {
172
276
  case "evm" /* EVM */: {
173
- if (!window.ethereum) return null;
277
+ if (!window.ethereum) return cachedAddress;
174
278
  const accounts = await window.ethereum.request({
175
279
  method: "eth_accounts",
176
280
  params: []
177
281
  });
178
- return accounts && accounts.length > 0 ? accounts[0] : null;
282
+ currentAddress = accounts && accounts.length > 0 ? accounts[0] : null;
283
+ break;
179
284
  }
180
285
  case "solana" /* SOLANA */:
181
286
  case "svm" /* SVM */: {
182
287
  const solana = window.solana;
183
- if (!solana || !solana.isConnected) return null;
184
- return solana.publicKey?.toString() || null;
288
+ if (!solana || !solana.isConnected) return cachedAddress;
289
+ currentAddress = solana.publicKey?.toString() || null;
290
+ break;
185
291
  }
186
292
  default:
187
- return null;
293
+ return cachedAddress;
294
+ }
295
+ if (currentAddress && currentAddress !== cachedAddress) {
296
+ saveWalletAddress(type, currentAddress);
188
297
  }
298
+ return currentAddress || cachedAddress;
189
299
  } catch (error) {
190
300
  console.error("Failed to get current wallet:", error);
191
- return null;
301
+ return cachedAddress;
192
302
  }
193
303
  }
194
304
  function onAccountsChanged(callback) {
@@ -239,6 +349,18 @@ function onWalletDisconnect(callback) {
239
349
  solana.removeListener?.("disconnect", handler);
240
350
  };
241
351
  }
352
+ async function switchNetwork(networkType) {
353
+ const cachedAddress = getCachedWalletAddress(networkType);
354
+ if (cachedAddress) {
355
+ saveConnectedNetworkType(networkType);
356
+ clearWalletDisconnection(networkType);
357
+ const currentAddress = await getCurrentWallet(networkType);
358
+ if (currentAddress) {
359
+ return currentAddress;
360
+ }
361
+ }
362
+ return null;
363
+ }
242
364
 
243
365
  // src/services/svm/payment-header.ts
244
366
  import {
@@ -330,12 +452,12 @@ async function createSvmPaymentHeader(params) {
330
452
  )
331
453
  );
332
454
  const { blockhash } = await connection.getLatestBlockhash("confirmed");
333
- const message = new TransactionMessage({
455
+ const message2 = new TransactionMessage({
334
456
  payerKey: feePayerPubkey,
335
457
  recentBlockhash: blockhash,
336
458
  instructions
337
459
  }).compileToV0Message();
338
- const transaction = new VersionedTransaction(message);
460
+ const transaction = new VersionedTransaction(message2);
339
461
  if (typeof wallet?.signTransaction !== "function") {
340
462
  throw new Error("Connected wallet does not support signTransaction");
341
463
  }
@@ -370,6 +492,7 @@ function getDefaultSolanaRpcUrl(network) {
370
492
  }
371
493
 
372
494
  // src/services/svm/payment-handler.ts
495
+ init_types();
373
496
  async function handleSvmPayment(endpoint, config, requestInit) {
374
497
  const { wallet, network, rpcUrl, maxPaymentAmount } = config;
375
498
  const initialResponse = await fetch(endpoint, {
@@ -394,7 +517,8 @@ async function handleSvmPayment(endpoint, config, requestInit) {
394
517
  "already_used": "This payment has already been used",
395
518
  "network_mismatch": "Payment network does not match",
396
519
  "invalid_payment": "Invalid payment data",
397
- "verification_failed": "Payment verification failed"
520
+ "verification_failed": "Payment verification failed",
521
+ "invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
398
522
  };
399
523
  const errorMessage = ERROR_MESSAGES[rawResponse.error] || `Payment failed: ${rawResponse.error}`;
400
524
  const error = new Error(errorMessage);
@@ -462,7 +586,8 @@ async function handleSvmPayment(endpoint, config, requestInit) {
462
586
  "already_used": "This payment has already been used",
463
587
  "network_mismatch": "Payment network does not match",
464
588
  "invalid_payment": "Invalid payment data",
465
- "verification_failed": "Payment verification failed"
589
+ "verification_failed": "Payment verification failed",
590
+ "invalid_exact_svm_payload_transaction_simulation_failed": "Transaction simulation failed due to insufficient balance. Please check your wallet balance carefully and ensure you have enough funds to cover the payment and transaction fees."
466
591
  };
467
592
  const errorMessage = ERROR_MESSAGES[retryData.error] || `Payment failed: ${retryData.error}`;
468
593
  const error = new Error(errorMessage);
@@ -592,6 +717,7 @@ function getChainIdFromNetwork(network) {
592
717
  }
593
718
 
594
719
  // src/services/evm/payment-handler.ts
720
+ init_types();
595
721
  async function handleEvmPayment(endpoint, config, requestInit) {
596
722
  const { wallet, network, maxPaymentAmount } = config;
597
723
  const initialResponse = await fetch(endpoint, {
@@ -761,6 +887,7 @@ async function handleEvmPayment(endpoint, config, requestInit) {
761
887
  }
762
888
 
763
889
  // src/utils/payment-helpers.ts
890
+ init_common();
764
891
  import { ethers as ethers2 } from "ethers";
765
892
  function parsePaymentRequired(response) {
766
893
  if (response && typeof response === "object") {
@@ -785,9 +912,15 @@ function getSupportedNetworkTypes(paymentRequirements) {
785
912
  });
786
913
  return Array.from(networkTypes);
787
914
  }
788
- async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
789
- endpoint = `${endpoint}/${merchantId}`;
915
+ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL, additionalParams) {
916
+ const fullEndpoint = `${endpoint}/${merchantId}`;
790
917
  let response;
918
+ const requestInit = additionalParams && Object.keys(additionalParams).length > 0 ? {
919
+ body: JSON.stringify(additionalParams),
920
+ headers: {
921
+ "Content-Type": "application/json"
922
+ }
923
+ } : {};
791
924
  if (networkType === "solana" /* SOLANA */ || networkType === "svm" /* SVM */) {
792
925
  const solana = window.solana;
793
926
  if (!solana) {
@@ -796,11 +929,11 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
796
929
  if (!solana.isConnected) {
797
930
  await solana.connect();
798
931
  }
799
- response = await handleSvmPayment(endpoint, {
932
+ response = await handleSvmPayment(fullEndpoint, {
800
933
  wallet: solana,
801
934
  network: "solana"
802
935
  // Will use backend's network configuration
803
- });
936
+ }, requestInit);
804
937
  } else if (networkType === "evm" /* EVM */) {
805
938
  if (!window.ethereum) {
806
939
  throw new Error("\u8BF7\u5B89\u88C5 MetaMask \u94B1\u5305");
@@ -809,8 +942,8 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
809
942
  const signer = await provider.getSigner();
810
943
  const wallet = {
811
944
  address: await signer.getAddress(),
812
- signTypedData: async (domain, types, message) => {
813
- return await signer.signTypedData(domain, types, message);
945
+ signTypedData: async (domain, types, message2) => {
946
+ return await signer.signTypedData(domain, types, message2);
814
947
  },
815
948
  // Get current chain ID from wallet
816
949
  getChainId: async () => {
@@ -825,35 +958,16 @@ async function makePayment(networkType, merchantId, endpoint = PROD_BACK_URL) {
825
958
  });
826
959
  }
827
960
  };
828
- response = await handleEvmPayment(endpoint, {
961
+ response = await handleEvmPayment(fullEndpoint, {
829
962
  wallet,
830
963
  network: "base"
831
964
  // Will use backend's network configuration
832
- });
965
+ }, requestInit);
833
966
  } else {
834
967
  throw new Error(`\u4E0D\u652F\u6301\u7684\u7F51\u7EDC\u7C7B\u578B: ${networkType}`);
835
968
  }
836
969
  return response;
837
970
  }
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
971
 
858
972
  // src/utils/network.ts
859
973
  var NETWORK_TYPE_MAP = {
@@ -877,6 +991,7 @@ var NETWORK_TYPE_MAP = {
877
991
  };
878
992
  function getNetworkDisplayName(network) {
879
993
  const displayNames = {
994
+ "evm": "EVM",
880
995
  "ethereum": "Ethereum",
881
996
  "sepolia": "Sepolia Testnet",
882
997
  "base": "Base",
@@ -1039,60 +1154,28 @@ var WalletStore = class {
1039
1154
  init() {
1040
1155
  if (this.initialized) return;
1041
1156
  this.initialized = true;
1042
- this.autoReconnect();
1043
1157
  onAccountsChanged((accounts) => {
1044
- const connectedType = getConnectedNetworkType();
1045
- if (connectedType === "evm" /* EVM */) {
1158
+ if (this.state.networkType === "evm" /* EVM */) {
1046
1159
  if (accounts.length === 0) {
1047
1160
  this.setState({ address: null });
1048
- console.log("\u{1F50C} Wallet disconnected");
1049
- } else {
1050
- if (!isWalletManuallyDisconnected()) {
1051
- this.setState({ address: accounts[0] });
1052
- console.log("\u{1F504} Account changed:", accounts[0]);
1053
- }
1161
+ } else if (!isWalletManuallyDisconnected("evm" /* EVM */)) {
1162
+ this.setState({ address: accounts[0] });
1163
+ saveWalletAddress("evm" /* EVM */, accounts[0]);
1054
1164
  }
1055
1165
  }
1056
1166
  });
1057
1167
  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
- });
1168
+ if (this.state.networkType === "evm" /* EVM */) {
1169
+ this.handleDisconnect("evm" /* EVM */, "Chain changed. Please reconnect your wallet.");
1067
1170
  }
1068
1171
  });
1069
1172
  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
- });
1173
+ const svmTypes = ["solana" /* SOLANA */, "svm" /* SVM */];
1174
+ if (this.state.networkType && svmTypes.includes(this.state.networkType)) {
1175
+ this.handleDisconnect(this.state.networkType);
1078
1176
  }
1079
1177
  });
1080
1178
  }
1081
- async autoReconnect() {
1082
- if (!isWalletManuallyDisconnected()) {
1083
- const connectedType = getConnectedNetworkType();
1084
- if (connectedType) {
1085
- const currentAddress = await getCurrentWallet(connectedType);
1086
- if (currentAddress) {
1087
- this.setState({
1088
- address: currentAddress,
1089
- networkType: connectedType
1090
- });
1091
- console.log("\u{1F504} Auto-reconnected wallet:", currentAddress);
1092
- }
1093
- }
1094
- }
1095
- }
1096
1179
  // Get current state
1097
1180
  getState() {
1098
1181
  return this.state;
@@ -1113,18 +1196,32 @@ var WalletStore = class {
1113
1196
  notifyListeners() {
1114
1197
  this.listeners.forEach((listener) => listener());
1115
1198
  }
1199
+ // Handle wallet disconnect (internal helper)
1200
+ handleDisconnect(networkType, error) {
1201
+ removeWalletAddress(networkType);
1202
+ markWalletDisconnected(networkType);
1203
+ if (typeof window !== "undefined") {
1204
+ localStorage.removeItem("connected_network_type");
1205
+ }
1206
+ this.setState({
1207
+ address: null,
1208
+ networkType: null,
1209
+ error: error || null
1210
+ });
1211
+ }
1116
1212
  // Connect wallet
1117
1213
  async connect(type) {
1214
+ if (this.state.address && this.state.networkType && this.state.networkType !== type) {
1215
+ saveWalletAddress(this.state.networkType, this.state.address);
1216
+ }
1118
1217
  this.setState({ isConnecting: true, error: null });
1119
1218
  try {
1120
1219
  const walletAddress = await connectWallet(type);
1121
- console.log("\u2705 Wallet connected:", walletAddress, "Network:", type);
1122
1220
  this.setState({
1123
1221
  address: walletAddress,
1124
1222
  networkType: type,
1125
1223
  isConnecting: false
1126
1224
  });
1127
- console.log("\u{1F4DD} Store state updated");
1128
1225
  } catch (err) {
1129
1226
  this.setState({
1130
1227
  error: err.message || "Failed to connect wallet",
@@ -1133,20 +1230,67 @@ var WalletStore = class {
1133
1230
  throw err;
1134
1231
  }
1135
1232
  }
1233
+ // Switch network (use cached wallet if available)
1234
+ async switchNetwork(type) {
1235
+ if (this.state.address && this.state.networkType) {
1236
+ saveWalletAddress(this.state.networkType, this.state.address);
1237
+ }
1238
+ this.setState({ isConnecting: true, error: null });
1239
+ try {
1240
+ const address = await switchNetwork(type);
1241
+ if (address) {
1242
+ this.setState({
1243
+ address,
1244
+ networkType: type,
1245
+ isConnecting: false
1246
+ });
1247
+ } else {
1248
+ this.setState({
1249
+ address: null,
1250
+ networkType: type,
1251
+ isConnecting: true
1252
+ });
1253
+ await this.connect(type);
1254
+ }
1255
+ } catch (err) {
1256
+ this.setState({
1257
+ error: err.message || "Failed to switch network",
1258
+ isConnecting: false
1259
+ });
1260
+ throw err;
1261
+ }
1262
+ }
1136
1263
  // Disconnect wallet
1137
1264
  disconnect() {
1138
- disconnectWallet();
1139
- this.setState({
1140
- address: null,
1141
- networkType: null,
1142
- error: null
1143
- });
1144
- console.log("\u{1F50C} Wallet disconnected from store");
1265
+ const currentNetwork = this.state.networkType;
1266
+ if (currentNetwork) {
1267
+ this.handleDisconnect(currentNetwork);
1268
+ } else {
1269
+ this.setState({
1270
+ address: null,
1271
+ networkType: null,
1272
+ error: null
1273
+ });
1274
+ }
1145
1275
  }
1146
1276
  // Clear error
1147
1277
  clearError() {
1148
1278
  this.setState({ error: null });
1149
1279
  }
1280
+ // Ensure network matches expected type (for page-specific network requirements)
1281
+ async ensureNetwork(expectedNetwork) {
1282
+ if (isWalletManuallyDisconnected(expectedNetwork)) {
1283
+ return;
1284
+ }
1285
+ if (this.state.networkType === expectedNetwork && this.state.address) {
1286
+ return;
1287
+ }
1288
+ if (this.state.networkType !== expectedNetwork) {
1289
+ await this.switchNetwork(expectedNetwork);
1290
+ } else if (!this.state.address) {
1291
+ await this.connect(expectedNetwork);
1292
+ }
1293
+ }
1150
1294
  };
1151
1295
  var walletStore = new WalletStore();
1152
1296
  if (typeof window !== "undefined") {
@@ -1164,11 +1308,30 @@ function useWallet() {
1164
1308
  return {
1165
1309
  ...state,
1166
1310
  connect: (type) => walletStore.connect(type),
1311
+ switchNetwork: (type) => walletStore.switchNetwork(type),
1312
+ ensureNetwork: (type) => walletStore.ensureNetwork(type),
1167
1313
  disconnect: () => walletStore.disconnect(),
1168
1314
  clearError: () => walletStore.clearError()
1169
1315
  };
1170
1316
  }
1171
1317
 
1318
+ // src/react/hooks/usePageNetwork.ts
1319
+ import { useEffect } from "react";
1320
+ function usePageNetwork(expectedNetwork, options = {}) {
1321
+ const {
1322
+ autoSwitch = true,
1323
+ switchOnMount = true
1324
+ } = options;
1325
+ const wallet = useWallet();
1326
+ useEffect(() => {
1327
+ if (!autoSwitch || !switchOnMount) return;
1328
+ wallet.ensureNetwork(expectedNetwork).catch((err) => {
1329
+ console.error("Failed to ensure network:", err);
1330
+ });
1331
+ }, [expectedNetwork]);
1332
+ return wallet;
1333
+ }
1334
+
1172
1335
  // src/react/hooks/usePayment.ts
1173
1336
  import { useCallback, useState } from "react";
1174
1337
  function usePayment() {
@@ -1200,8 +1363,9 @@ function usePayment() {
1200
1363
  }
1201
1364
 
1202
1365
  // src/react/hooks/usePaymentInfo.ts
1203
- import { useEffect, useState as useState2 } from "react";
1204
- function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
1366
+ import { useEffect as useEffect2, useState as useState2 } from "react";
1367
+ init_common();
1368
+ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL, additionalParams) {
1205
1369
  const [paymentInfo, setPaymentInfo] = useState2(null);
1206
1370
  const [supportedNetworks, setSupportedNetworks] = useState2([]);
1207
1371
  const [isLoading, setIsLoading] = useState2(true);
@@ -1210,8 +1374,17 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
1210
1374
  setIsLoading(true);
1211
1375
  setError(null);
1212
1376
  try {
1213
- endpoint = `${endpoint}/${merchantId}`;
1214
- const response = await fetch(endpoint, { method: "POST" });
1377
+ const fullEndpoint = `${endpoint}/${merchantId}`;
1378
+ const requestInit = {
1379
+ method: "POST",
1380
+ ...additionalParams && Object.keys(additionalParams).length > 0 ? {
1381
+ body: JSON.stringify(additionalParams),
1382
+ headers: {
1383
+ "Content-Type": "application/json"
1384
+ }
1385
+ } : {}
1386
+ };
1387
+ const response = await fetch(fullEndpoint, requestInit);
1215
1388
  if (response.status === 402) {
1216
1389
  const body = await response.json();
1217
1390
  const payment = parsePaymentRequired(body);
@@ -1230,9 +1403,9 @@ function usePaymentInfo(merchantId, endpoint = PROD_BACK_URL) {
1230
1403
  setIsLoading(false);
1231
1404
  }
1232
1405
  };
1233
- useEffect(() => {
1406
+ useEffect2(() => {
1234
1407
  fetchPaymentInfo();
1235
- }, [endpoint]);
1408
+ }, [endpoint, merchantId]);
1236
1409
  return {
1237
1410
  paymentInfo,
1238
1411
  supportedNetworks,
@@ -1335,12 +1508,21 @@ var baseButtonStyle = {
1335
1508
  };
1336
1509
  var getConnectButtonStyle = (isDisabled, isHovered) => {
1337
1510
  const c = getColors();
1511
+ const darkMode = isDarkMode();
1512
+ if (isDisabled) {
1513
+ return {
1514
+ ...baseButtonStyle,
1515
+ background: c.disabled,
1516
+ color: c.disabledText,
1517
+ cursor: "not-allowed",
1518
+ border: darkMode ? "1px solid #404040" : "1px solid #d4d4d4"
1519
+ };
1520
+ }
1338
1521
  return {
1339
1522
  ...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
1523
+ background: isHovered ? c.primaryHover : c.primary,
1524
+ color: darkMode ? "#000000" : "#ffffff",
1525
+ cursor: "pointer"
1344
1526
  };
1345
1527
  };
1346
1528
  var getDisconnectButtonStyle = (isHovered) => {
@@ -1351,17 +1533,6 @@ var getDisconnectButtonStyle = (isHovered) => {
1351
1533
  color: "#ffffff"
1352
1534
  };
1353
1535
  };
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
1536
  var getInstallLinkStyle = (isHovered) => {
1366
1537
  const c = getColors();
1367
1538
  return {
@@ -1446,7 +1617,7 @@ function WalletConnect({
1446
1617
  disconnect();
1447
1618
  onDisconnect?.();
1448
1619
  };
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) => {
1620
+ 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
1621
  const installed = isWalletInstalled(network);
1451
1622
  return /* @__PURE__ */ React.createElement("div", { key: network, style: walletOptionStyle }, /* @__PURE__ */ React.createElement(
1452
1623
  "button",
@@ -1482,61 +1653,571 @@ function WalletConnect({
1482
1653
  )), /* @__PURE__ */ React.createElement("p", { style: getHintStyle() }, "Switch account in your wallet to change address")));
1483
1654
  }
1484
1655
 
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"
1656
+ // src/react/components/V402Checkout.tsx
1657
+ import React3, { useEffect as useEffect3, useState as useState4 } from "react";
1658
+ import { Button, Card, Divider, message, Spin, Tooltip, Typography } from "antd";
1659
+ import {
1660
+ DisconnectOutlined,
1661
+ InfoCircleOutlined,
1662
+ LinkOutlined,
1663
+ LoadingOutlined,
1664
+ LockOutlined,
1665
+ SafetyOutlined
1666
+ } from "@ant-design/icons";
1667
+ init_common();
1668
+
1669
+ // src/react/utils/CryptoIcons.tsx
1670
+ import React2 from "react";
1671
+ var SolanaIcon = ({ width = 16, height = 16, className, style }) => {
1672
+ return /* @__PURE__ */ React2.createElement(
1673
+ "svg",
1674
+ {
1675
+ xmlns: "http://www.w3.org/2000/svg",
1676
+ viewBox: "0 0 16 16",
1677
+ width,
1678
+ height,
1679
+ className,
1680
+ style
1681
+ },
1682
+ /* @__PURE__ */ React2.createElement("desc", null, "Solana SOL Fill Streamline Icon: https://streamlinehq.com"),
1683
+ /* @__PURE__ */ React2.createElement("g", { fill: "none", fillRule: "evenodd" }, /* @__PURE__ */ React2.createElement(
1684
+ "path",
1685
+ {
1686
+ d: "M16 0v16H0V0h16ZM8.395333333333333 15.505333333333333l-0.007333333333333332 0.0013333333333333333 -0.047333333333333324 0.023333333333333334 -0.013333333333333332 0.0026666666666666666 -0.009333333333333332 -0.0026666666666666666 -0.047333333333333324 -0.023333333333333334c-0.006666666666666666 -0.0026666666666666666 -0.012666666666666666 -0.0006666666666666666 -0.016 0.003333333333333333l-0.0026666666666666666 0.006666666666666666 -0.011333333333333334 0.2853333333333333 0.003333333333333333 0.013333333333333332 0.006666666666666666 0.008666666666666666 0.06933333333333333 0.049333333333333326 0.009999999999999998 0.0026666666666666666 0.008 -0.0026666666666666666 0.06933333333333333 -0.049333333333333326 0.008 -0.010666666666666666 0.0026666666666666666 -0.011333333333333334 -0.011333333333333334 -0.2846666666666666c-0.0013333333333333333 -0.006666666666666666 -0.005999999999999999 -0.011333333333333334 -0.011333333333333334 -0.011999999999999999Zm0.17666666666666667 -0.07533333333333334 -0.008666666666666666 0.0013333333333333333 -0.12333333333333332 0.062 -0.006666666666666666 0.006666666666666666 -0.002 0.007333333333333332 0.011999999999999999 0.2866666666666666 0.003333333333333333 0.008 0.005333333333333333 0.004666666666666666 0.134 0.062c0.008 0.0026666666666666666 0.015333333333333332 0 0.019333333333333334 -0.005333333333333333l0.0026666666666666666 -0.009333333333333332 -0.02266666666666667 -0.4093333333333333c-0.002 -0.008 -0.006666666666666666 -0.013333333333333332 -0.013333333333333332 -0.014666666666666665Zm-0.4766666666666666 0.0013333333333333333a0.015333333333333332 0.015333333333333332 0 0 0 -0.018 0.004l-0.004 0.009333333333333332 -0.02266666666666667 0.4093333333333333c0 0.008 0.004666666666666666 0.013333333333333332 0.011333333333333334 0.016l0.009999999999999998 -0.0013333333333333333 0.134 -0.062 0.006666666666666666 -0.005333333333333333 0.0026666666666666666 -0.007333333333333332 0.011333333333333334 -0.2866666666666666 -0.002 -0.008 -0.006666666666666666 -0.006666666666666666 -0.12266666666666666 -0.06133333333333333Z",
1687
+ strokeWidth: "0.6667"
1688
+ }
1689
+ ), /* @__PURE__ */ React2.createElement(
1690
+ "path",
1691
+ {
1692
+ fill: "#000000",
1693
+ d: "M4.862 2.862A0.6666666666666666 0.6666666666666666 0 0 1 5.333333333333333 2.6666666666666665h8.666666666666666a0.6666666666666666 0.6666666666666666 0 0 1 0.47133333333333327 1.138l-2 2A0.6666666666666666 0.6666666666666666 0 0 1 12 6H3.333333333333333a0.6666666666666666 0.6666666666666666 0 0 1 -0.47133333333333327 -1.138l2 -2Zm-2.1166666666666663 4.156666666666666A0.6666666666666666 0.6666666666666666 0 0 1 3.333333333333333 6.666666666666666h8.666666666666666a0.6666666666666666 0.6666666666666666 0 0 1 0.5546666666666666 0.29666666666666663l1.3333333333333333 2A0.6666666666666666 0.6666666666666666 0 0 1 13.333333333333332 10H4.666666666666666a0.6666666666666666 0.6666666666666666 0 0 1 -0.5546666666666666 -0.29666666666666663l-1.3333333333333333 -2a0.6666666666666666 0.6666666666666666 0 0 1 -0.03333333333333333 -0.6846666666666665Zm1.4499999999999997 3.843333333333333A0.6666666666666666 0.6666666666666666 0 0 1 4.666666666666666 10.666666666666666h8.666666666666666a0.6666666666666666 0.6666666666666666 0 0 1 0.47133333333333327 1.138l-2 2A0.6666666666666666 0.6666666666666666 0 0 1 11.333333333333332 14H2.6666666666666665a0.6666666666666666 0.6666666666666666 0 0 1 -0.47133333333333327 -1.138l2 -2Z",
1694
+ strokeWidth: "0.6667"
1695
+ }
1696
+ ))
1697
+ );
1698
+ };
1699
+ var BaseIcon = ({ width = 24, height = 24, className, style }) => {
1700
+ return /* @__PURE__ */ React2.createElement(
1701
+ "svg",
1702
+ {
1703
+ xmlns: "http://www.w3.org/2000/svg",
1704
+ viewBox: "0 0 24 24",
1705
+ fill: "none",
1706
+ stroke: "#000000",
1707
+ strokeLinecap: "round",
1708
+ strokeLinejoin: "round",
1709
+ width,
1710
+ height,
1711
+ className,
1712
+ style
1713
+ },
1714
+ /* @__PURE__ */ React2.createElement("desc", null, "Brand Coinbase Streamline Icon: https://streamlinehq.com"),
1715
+ /* @__PURE__ */ React2.createElement(
1716
+ "path",
1717
+ {
1718
+ d: "M12.95 22c-4.503 0 -8.445 -3.04 -9.61 -7.413 -1.165 -4.373 0.737 -8.988 4.638 -11.25a9.906 9.906 0 0 1 12.008 1.598l-3.335 3.367a5.185 5.185 0 0 0 -7.354 0.013 5.252 5.252 0 0 0 0 7.393 5.185 5.185 0 0 0 7.354 0.013L20 19.088A9.887 9.887 0 0 1 12.95 22z",
1719
+ strokeWidth: "2"
1720
+ }
1721
+ )
1722
+ );
1723
+ };
1724
+ var getNetworkIcon = (network) => {
1725
+ const networkLower = network.toLowerCase();
1726
+ if (networkLower.includes("solana")) {
1727
+ return SolanaIcon;
1728
+ }
1729
+ if (networkLower.includes("base")) {
1730
+ return BaseIcon;
1731
+ }
1732
+ return BaseIcon;
1733
+ };
1734
+
1735
+ // src/react/components/V402Checkout.tsx
1736
+ var { Title, Text } = Typography;
1737
+ var notify = {
1738
+ success: (title, msg) => {
1739
+ message.success(`${title}: ${msg}`);
1740
+ },
1741
+ error: (title, msg) => {
1742
+ message.error(`${title}: ${msg}`);
1743
+ },
1744
+ info: (title, msg) => {
1745
+ message.info(`${title}: ${msg}`);
1746
+ }
1747
+ };
1748
+ function V402Checkout({
1749
+ merchantId,
1750
+ headerInfo = {},
1751
+ isModal = false,
1752
+ onPaymentComplete,
1753
+ additionalParams = {},
1754
+ expectedNetwork
1496
1755
  }) {
1497
- const { networkType } = useWallet();
1498
- const { isProcessing, setIsProcessing, setResult, setError, error } = usePayment();
1499
- const [isHovered, setIsHovered] = useState4(false);
1500
- const handleClick = async () => {
1756
+ const {
1757
+ title = "V402Pay - Make x402Pay Easier",
1758
+ subtitle = "onvoyage.ai",
1759
+ tooltipText = "V402Pay - Accept Crypto Payments Easier"
1760
+ } = headerInfo;
1761
+ let endpoint = DEV_BACK_URL;
1762
+ const {
1763
+ supportedNetworks,
1764
+ isLoading: fetchingPaymentInfo,
1765
+ paymentInfo
1766
+ } = usePaymentInfo(merchantId, endpoint, additionalParams);
1767
+ const targetNetwork = expectedNetwork || supportedNetworks[0];
1768
+ const { address, networkType, disconnect, ensureNetwork } = usePageNetwork(
1769
+ targetNetwork,
1770
+ { autoSwitch: !!targetNetwork, switchOnMount: true }
1771
+ );
1772
+ const { isProcessing, setIsProcessing, result, setResult, error, setError } = usePayment();
1773
+ const [paymentDetails, setPaymentDetails] = useState4(null);
1774
+ const handleDisconnect = () => {
1775
+ disconnect();
1776
+ setResult(null);
1777
+ setError(null);
1778
+ notify.info("Wallet Disconnected", "Your wallet has been disconnected successfully.");
1779
+ };
1780
+ useEffect3(() => {
1781
+ if (paymentInfo && paymentInfo.length > 0) {
1782
+ const firstPayment = paymentInfo[0];
1783
+ const rawAmount = firstPayment.maxAmountRequired?.toString() || "0";
1784
+ const decimals = 6;
1785
+ const humanReadableAmount = (Number(rawAmount) / Math.pow(10, decimals)).toFixed(2);
1786
+ const network = firstPayment.network || "Unknown";
1787
+ const currency = "USDC";
1788
+ setPaymentDetails({
1789
+ amount: humanReadableAmount,
1790
+ currency,
1791
+ network
1792
+ });
1793
+ }
1794
+ }, [paymentInfo]);
1795
+ useEffect3(() => {
1796
+ if (targetNetwork && !fetchingPaymentInfo && ensureNetwork) {
1797
+ ensureNetwork(targetNetwork).catch((err) => {
1798
+ console.error("Failed to ensure network:", err);
1799
+ });
1800
+ }
1801
+ }, [targetNetwork, fetchingPaymentInfo]);
1802
+ const handlePayment = async () => {
1501
1803
  if (!networkType) {
1502
- const errorMsg = "Please connect wallet first";
1503
- setError(errorMsg);
1504
- onError?.(errorMsg);
1804
+ notify.error("Wallet Not Connected", "Please connect your wallet first.");
1505
1805
  return;
1506
1806
  }
1807
+ setResult(null);
1808
+ setError(null);
1809
+ setIsProcessing(true);
1507
1810
  try {
1508
- onStart?.();
1509
- setIsProcessing(true);
1510
- setError(null);
1511
- const result = await handlePayment(endpoint, networkType);
1512
- setResult(result);
1513
- onSuccess?.(result);
1811
+ const response = await makePayment(networkType, merchantId, endpoint, additionalParams);
1812
+ const data = await response.json();
1813
+ setResult(data);
1814
+ notify.success("Payment Successful!", "Your payment has been processed successfully.");
1815
+ if (onPaymentComplete) {
1816
+ onPaymentComplete(data);
1817
+ }
1514
1818
  } catch (err) {
1515
- const errorMsg = err.message || "Payment failed";
1516
- setError(errorMsg);
1517
- onError?.(errorMsg);
1819
+ const errorMessage = err.message || "Payment failed";
1820
+ setError(errorMessage);
1821
+ notify.error("Payment Failed", errorMessage);
1518
1822
  } finally {
1519
1823
  setIsProcessing(false);
1520
- onFinish?.();
1521
1824
  }
1522
1825
  };
1523
- const isDisabled = disabled || isProcessing || !networkType;
1524
- return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
1525
- "button",
1826
+ const getNetworkColor = (network) => {
1827
+ if (network.toLowerCase().includes("solana")) return "#14F195";
1828
+ if (network.toLowerCase().includes("evm") || network.toLowerCase().includes("base")) return "#0052FF";
1829
+ return "#8c8c8c";
1830
+ };
1831
+ const NetworkIcon = paymentDetails ? getNetworkIcon(paymentDetails.network) : null;
1832
+ const networkColor = paymentDetails ? getNetworkColor(paymentDetails.network) : "#8c8c8c";
1833
+ const loadingColor = "#8c8c8c";
1834
+ const hasInvalidCheckoutId = !fetchingPaymentInfo && (!paymentInfo || paymentInfo.length === 0);
1835
+ return /* @__PURE__ */ React3.createElement(
1836
+ "div",
1526
1837
  {
1527
- style: getPayButtonStyle(isDisabled, isHovered),
1528
- className,
1529
- onClick: handleClick,
1530
- disabled: isDisabled,
1531
- onMouseEnter: () => setIsHovered(true),
1532
- onMouseLeave: () => setIsHovered(false)
1838
+ className: isModal ? "bg-white" : "h-screen bg-white flex items-center justify-center p-4 overflow-hidden"
1533
1839
  },
1534
- isProcessing ? "Processing..." : children
1535
- ), error && /* @__PURE__ */ React2.createElement("p", { style: getErrorStyle() }, error));
1840
+ /* @__PURE__ */ React3.createElement(
1841
+ "div",
1842
+ {
1843
+ className: "flex gap-4 items-center justify-center",
1844
+ style: {
1845
+ maxWidth: isProcessing || result || error ? "1200px" : "480px",
1846
+ transition: "max-width 0.4s ease-in-out",
1847
+ width: "100%"
1848
+ }
1849
+ },
1850
+ /* @__PURE__ */ React3.createElement(
1851
+ Card,
1852
+ {
1853
+ className: "flex-shrink-0",
1854
+ style: {
1855
+ border: isModal ? "none" : "1px solid #e8e8e8",
1856
+ borderRadius: isModal ? "0" : "16px",
1857
+ boxShadow: isModal ? "none" : "0 4px 24px rgba(0, 0, 0, 0.06)",
1858
+ maxHeight: isModal ? "calc(100vh - 100px)" : "calc(100vh - 32px)",
1859
+ overflow: "auto",
1860
+ width: isModal ? "100%" : "480px",
1861
+ transition: "all 0.4s ease-in-out",
1862
+ transform: result || error ? "translateX(0)" : "translateX(0)"
1863
+ },
1864
+ styles: { body: { padding: isModal ? "0px" : "32px 24px" } }
1865
+ },
1866
+ /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-3 mb-4" }, /* @__PURE__ */ React3.createElement(
1867
+ "div",
1868
+ {
1869
+ className: "w-12 h-12 rounded-xl flex items-center justify-center",
1870
+ style: {
1871
+ background: hasInvalidCheckoutId ? "#ff4d4f" : paymentDetails ? networkColor : loadingColor,
1872
+ transition: "background 0.3s ease"
1873
+ }
1874
+ },
1875
+ hasInvalidCheckoutId ? /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "20px", color: "white", fontWeight: "bold" } }, "\u2717") : paymentDetails && NetworkIcon ? /* @__PURE__ */ React3.createElement(NetworkIcon, { width: 24, height: 24 }) : /* @__PURE__ */ React3.createElement(LoadingOutlined, { style: { fontSize: "20px", color: "white" }, spin: true })
1876
+ ), /* @__PURE__ */ React3.createElement("div", { className: "flex-1" }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-2" }, /* @__PURE__ */ React3.createElement(Title, { level: 4, style: { margin: 0, fontSize: "18px", fontWeight: 600 } }, title || "Echo Payment OnVoyage"), !hasInvalidCheckoutId && /* @__PURE__ */ React3.createElement(
1877
+ Tooltip,
1878
+ {
1879
+ title: tooltipText,
1880
+ placement: "top"
1881
+ },
1882
+ /* @__PURE__ */ React3.createElement(
1883
+ InfoCircleOutlined,
1884
+ {
1885
+ style: { fontSize: "14px", color: "#8c8c8c", cursor: "help" }
1886
+ }
1887
+ )
1888
+ )), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, subtitle))),
1889
+ /* @__PURE__ */ React3.createElement("div", { className: "text-center mb-5" }, /* @__PURE__ */ React3.createElement("div", { className: "inline-flex items-center justify-center w-12 h-12 rounded-full bg-gray-50 mb-3" }, /* @__PURE__ */ React3.createElement(LockOutlined, { style: { fontSize: "20px", color: "#595959" } })), /* @__PURE__ */ React3.createElement(Title, { level: 3, style: { margin: "0 0 6px 0", fontSize: "20px", fontWeight: 600 } }, "Payment Required"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Pay ", paymentDetails ? `$${paymentDetails.amount} ${paymentDetails.currency}` : "the required amount", " to access")),
1890
+ hasInvalidCheckoutId && /* @__PURE__ */ React3.createElement("div", { className: "text-center py-6" }, /* @__PURE__ */ React3.createElement(
1891
+ "div",
1892
+ {
1893
+ className: "inline-flex items-center justify-center w-16 h-16 rounded-full mb-4",
1894
+ style: {
1895
+ background: "linear-gradient(135deg, #ef4444 0%, #f87171 100%)",
1896
+ boxShadow: "0 4px 20px rgba(239, 68, 68, 0.3)"
1897
+ }
1898
+ },
1899
+ /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "32px", color: "white" } }, "!")
1900
+ ), /* @__PURE__ */ React3.createElement(
1901
+ Title,
1902
+ {
1903
+ level: 4,
1904
+ style: { margin: "0 0 12px 0", fontSize: "18px", fontWeight: 600, color: "#262626" }
1905
+ },
1906
+ "Invalid Checkout ID"
1907
+ ), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c", display: "block", marginBottom: "16px" } }, "The checkout ID you provided is invalid or has expired."), /* @__PURE__ */ React3.createElement(
1908
+ "div",
1909
+ {
1910
+ style: {
1911
+ background: "#fef2f2",
1912
+ padding: "16px",
1913
+ borderRadius: "12px",
1914
+ border: "1px solid #fee2e2",
1915
+ marginTop: "16px"
1916
+ }
1917
+ },
1918
+ /* @__PURE__ */ React3.createElement(Text, { style: {
1919
+ fontSize: "13px",
1920
+ color: "#dc2626",
1921
+ lineHeight: "1.6",
1922
+ fontWeight: 500
1923
+ } }, "Failed to load payment information. Please check your checkout ID.")
1924
+ )),
1925
+ !hasInvalidCheckoutId && fetchingPaymentInfo && /* @__PURE__ */ React3.createElement("div", { className: "text-center py-6" }, /* @__PURE__ */ React3.createElement(Text, { style: { color: "#8c8c8c" } }, "Loading payment information...")),
1926
+ !hasInvalidCheckoutId && !fetchingPaymentInfo && !address && /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(WalletConnect, { supportedNetworks })),
1927
+ !hasInvalidCheckoutId && address && /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
1928
+ "div",
1929
+ {
1930
+ className: "bg-gray-50 rounded-lg p-3 mb-4",
1931
+ style: { border: "1px solid #f0f0f0" }
1932
+ },
1933
+ /* @__PURE__ */ React3.createElement("div", { className: "flex items-center justify-between" }, /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-3 flex-1" }, /* @__PURE__ */ React3.createElement(
1934
+ "div",
1935
+ {
1936
+ className: "w-10 h-10 rounded-full bg-black flex items-center justify-center text-white text-sm font-semibold"
1937
+ },
1938
+ address.slice(0, 2).toUpperCase()
1939
+ ), /* @__PURE__ */ React3.createElement("div", { className: "flex-1 min-w-0" }, /* @__PURE__ */ React3.createElement(Text, { style: {
1940
+ display: "block",
1941
+ fontSize: "12px",
1942
+ color: "#8c8c8c",
1943
+ marginBottom: "2px"
1944
+ } }, "Connected Wallet"), /* @__PURE__ */ React3.createElement(
1945
+ Text,
1946
+ {
1947
+ style: {
1948
+ fontSize: "13px",
1949
+ fontWeight: 600,
1950
+ fontFamily: "Monaco, monospace"
1951
+ }
1952
+ },
1953
+ formatAddress(address)
1954
+ ))), /* @__PURE__ */ React3.createElement(
1955
+ Button,
1956
+ {
1957
+ type: "text",
1958
+ size: "small",
1959
+ icon: /* @__PURE__ */ React3.createElement(DisconnectOutlined, null),
1960
+ onClick: handleDisconnect,
1961
+ style: { color: "#ff4d4f" }
1962
+ }
1963
+ ))
1964
+ ), paymentDetails && /* @__PURE__ */ React3.createElement("div", { className: "bg-gray-50 rounded-lg p-3 mb-4", style: { border: "1px solid #f0f0f0" } }, /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Payment Amount"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "18px", fontWeight: 600 } }, "$", paymentDetails.amount)), /* @__PURE__ */ React3.createElement(Divider, { style: { margin: "6px 0" } }), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Currency"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "14px", fontWeight: 500 } }, paymentDetails.currency)), /* @__PURE__ */ React3.createElement(Divider, { style: { margin: "6px 0" } }), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-center mb-2" }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Network"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "14px", fontWeight: 500 } }, paymentDetails.network)), /* @__PURE__ */ React3.createElement(Divider, { style: { margin: "6px 0" } }), /* @__PURE__ */ React3.createElement("div", { className: "flex justify-between items-start" }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Wallet Address"), /* @__PURE__ */ React3.createElement(Text, { style: {
1965
+ fontSize: "11px",
1966
+ fontWeight: 500,
1967
+ fontFamily: "Monaco, monospace",
1968
+ wordBreak: "break-all",
1969
+ textAlign: "right",
1970
+ maxWidth: "60%",
1971
+ lineHeight: 1.4
1972
+ } }, address))), /* @__PURE__ */ React3.createElement(
1973
+ "div",
1974
+ {
1975
+ className: "flex items-center justify-center gap-2 mb-3 p-2 rounded-lg",
1976
+ style: { background: "#f6ffed", border: "1px solid #d9f7be" }
1977
+ },
1978
+ /* @__PURE__ */ React3.createElement(SafetyOutlined, { style: { color: "#52c41a", fontSize: "13px" } }),
1979
+ /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "12px", color: "#52c41a", fontWeight: 500 } }, "Secure payment powered by v402pay")
1980
+ ), /* @__PURE__ */ React3.createElement(
1981
+ Button,
1982
+ {
1983
+ type: "primary",
1984
+ size: "large",
1985
+ onClick: handlePayment,
1986
+ disabled: isProcessing || !paymentDetails,
1987
+ loading: isProcessing,
1988
+ block: true,
1989
+ style: {
1990
+ height: "44px",
1991
+ fontSize: "14px",
1992
+ fontWeight: 600,
1993
+ borderRadius: "8px",
1994
+ ...!isProcessing && paymentDetails && {
1995
+ background: "#1a1a1a",
1996
+ borderColor: "#1a1a1a"
1997
+ },
1998
+ marginBottom: "10px"
1999
+ }
2000
+ },
2001
+ isProcessing ? "Processing..." : !paymentDetails ? "Loading..." : `Pay $${paymentDetails.amount} ${paymentDetails.currency}`
2002
+ ), /* @__PURE__ */ React3.createElement("div", { className: "text-center" }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#8c8c8c" } }, "Don't have USDC?", " "), /* @__PURE__ */ React3.createElement(
2003
+ "a",
2004
+ {
2005
+ href: "https://faucet.circle.com/",
2006
+ target: "_blank",
2007
+ rel: "noopener noreferrer",
2008
+ className: "text-blue-600 hover:text-blue-700 text-sm font-medium inline-flex items-center gap-1"
2009
+ },
2010
+ "Get it here ",
2011
+ /* @__PURE__ */ React3.createElement(LinkOutlined, { style: { fontSize: "12px" } })
2012
+ )), isModal && result && /* @__PURE__ */ React3.createElement(
2013
+ "div",
2014
+ {
2015
+ className: "mt-4 p-4 rounded-lg",
2016
+ style: { background: "#f6ffed", border: "1px solid #b7eb8f" }
2017
+ },
2018
+ /* @__PURE__ */ React3.createElement("div", { className: "text-center" }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "20px" } }, "\u2713"), /* @__PURE__ */ React3.createElement(Text, { style: {
2019
+ fontSize: "14px",
2020
+ color: "#52c41a",
2021
+ fontWeight: 600,
2022
+ marginLeft: "8px"
2023
+ } }, "Payment Successful!"))
2024
+ ), isModal && error && /* @__PURE__ */ React3.createElement(
2025
+ "div",
2026
+ {
2027
+ className: "mt-4 p-4 rounded-lg",
2028
+ style: { background: "#fff2f0", border: "1px solid #ffccc7" }
2029
+ },
2030
+ /* @__PURE__ */ React3.createElement("div", { className: "text-center mb-3" }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "20px" } }, "\u2717"), /* @__PURE__ */ React3.createElement(Text, { style: {
2031
+ fontSize: "14px",
2032
+ color: "#ff4d4f",
2033
+ fontWeight: 600,
2034
+ marginLeft: "8px",
2035
+ display: "block",
2036
+ marginTop: "4px"
2037
+ } }, "Payment Failed")),
2038
+ /* @__PURE__ */ React3.createElement(Text, { style: {
2039
+ fontSize: "13px",
2040
+ color: "#ff4d4f",
2041
+ display: "block",
2042
+ textAlign: "center"
2043
+ } }, error)
2044
+ ))
2045
+ ),
2046
+ !isModal && (isProcessing || result || error) && /* @__PURE__ */ React3.createElement(
2047
+ Card,
2048
+ {
2049
+ title: /* @__PURE__ */ React3.createElement("div", { className: "flex items-center gap-2" }, isProcessing && !result && !error ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(LoadingOutlined, { style: { color: "#14b8a6", fontSize: "16px" } }), /* @__PURE__ */ React3.createElement(Text, { strong: true, style: { fontSize: "16px", color: "#262626" } }, "Processing Payment")) : result ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { color: "#52c41a", fontSize: "18px" } }, "\u2713"), /* @__PURE__ */ React3.createElement(Text, { strong: true, style: { fontSize: "16px", color: "#262626" } }, "Payment Successful")) : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement("span", { style: { color: "#ff4d4f", fontSize: "18px" } }, "\u2717"), /* @__PURE__ */ React3.createElement(Text, { strong: true, style: { fontSize: "16px", color: "#262626" } }, "Payment Failed"))),
2050
+ extra: !isProcessing && /* @__PURE__ */ React3.createElement(
2051
+ Button,
2052
+ {
2053
+ type: "text",
2054
+ size: "small",
2055
+ onClick: () => {
2056
+ setResult(null);
2057
+ setError(null);
2058
+ }
2059
+ },
2060
+ "Close"
2061
+ ),
2062
+ style: {
2063
+ border: "1px solid #e8e8e8",
2064
+ borderRadius: "16px",
2065
+ boxShadow: "0 4px 24px rgba(0, 0, 0, 0.06)",
2066
+ maxHeight: "calc(100vh - 32px)",
2067
+ width: "480px",
2068
+ animation: "slideInRight 0.4s ease-out"
2069
+ },
2070
+ styles: {
2071
+ body: {
2072
+ padding: "24px",
2073
+ maxHeight: "calc(100vh - 120px)",
2074
+ overflow: "auto"
2075
+ }
2076
+ }
2077
+ },
2078
+ isProcessing && !result && !error && /* @__PURE__ */ React3.createElement("div", { className: "text-center py-10" }, /* @__PURE__ */ React3.createElement("div", { className: "relative inline-block" }, /* @__PURE__ */ React3.createElement(
2079
+ "div",
2080
+ {
2081
+ className: "absolute inset-0 rounded-full blur-xl opacity-40",
2082
+ style: {
2083
+ background: "linear-gradient(135deg, #14b8a6 0%, #06b6d4 100%)",
2084
+ animation: "pulse 2s ease-in-out infinite"
2085
+ }
2086
+ }
2087
+ ), /* @__PURE__ */ React3.createElement(
2088
+ Spin,
2089
+ {
2090
+ indicator: /* @__PURE__ */ React3.createElement(LoadingOutlined, { style: { fontSize: 56, color: "#14b8a6" } })
2091
+ }
2092
+ )), /* @__PURE__ */ React3.createElement("div", { className: "mt-6" }, /* @__PURE__ */ React3.createElement(Text, { strong: true, style: { fontSize: "18px", color: "#262626", letterSpacing: "-0.02em" } }, "Verifying Payment")), /* @__PURE__ */ React3.createElement("div", { className: "mt-2 mb-6" }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c", lineHeight: "1.6" } }, "Please wait while we confirm your transaction")), /* @__PURE__ */ React3.createElement(
2093
+ "div",
2094
+ {
2095
+ className: "mt-4 p-4 rounded-xl",
2096
+ style: {
2097
+ background: "linear-gradient(135deg, #f0fdfa 0%, #ecfeff 100%)",
2098
+ border: "1px solid #ccfbf1"
2099
+ }
2100
+ },
2101
+ /* @__PURE__ */ React3.createElement("div", { className: "flex items-center justify-center gap-2" }, /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "16px" } }, "\u23F1\uFE0F"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "13px", color: "#0f766e", fontWeight: 500 } }, "This may take a few moments"))
2102
+ )),
2103
+ result && /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement("div", { className: "text-center mb-6" }, /* @__PURE__ */ React3.createElement(
2104
+ "div",
2105
+ {
2106
+ className: "inline-flex items-center justify-center w-16 h-16 rounded-full mb-4",
2107
+ style: {
2108
+ background: "linear-gradient(135deg, #10b981 0%, #34d399 100%)",
2109
+ boxShadow: "0 4px 20px rgba(16, 185, 129, 0.3)"
2110
+ }
2111
+ },
2112
+ /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "32px", color: "white" } }, "\u2713")
2113
+ ), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(Text, { strong: true, style: {
2114
+ fontSize: "20px",
2115
+ color: "#262626",
2116
+ display: "block",
2117
+ marginBottom: "8px"
2118
+ } }, "Payment Successful!"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c" } }, "Your transaction has been confirmed"))), /* @__PURE__ */ React3.createElement(Divider, { style: { margin: "20px 0", borderColor: "#f0f0f0" } }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "12px", color: "#8c8c8c", fontWeight: 500 } }, "RESPONSE DATA")), /* @__PURE__ */ React3.createElement(
2119
+ "pre",
2120
+ {
2121
+ style: {
2122
+ background: "#fafafa",
2123
+ padding: "20px",
2124
+ borderRadius: "12px",
2125
+ fontSize: "12px",
2126
+ lineHeight: "1.8",
2127
+ overflow: "auto",
2128
+ margin: 0,
2129
+ fontFamily: "Monaco, Courier New, monospace",
2130
+ whiteSpace: "pre-wrap",
2131
+ wordBreak: "break-word",
2132
+ border: "1px solid #e8e8e8",
2133
+ color: "#262626"
2134
+ }
2135
+ },
2136
+ JSON.stringify(result, null, 2)
2137
+ )),
2138
+ error && /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement("div", { className: "text-center mb-6" }, /* @__PURE__ */ React3.createElement(
2139
+ "div",
2140
+ {
2141
+ className: "inline-flex items-center justify-center w-16 h-16 rounded-full mb-4",
2142
+ style: {
2143
+ background: "linear-gradient(135deg, #ef4444 0%, #f87171 100%)",
2144
+ boxShadow: "0 4px 20px rgba(239, 68, 68, 0.3)"
2145
+ }
2146
+ },
2147
+ /* @__PURE__ */ React3.createElement("span", { style: { fontSize: "32px", color: "white" } }, "\u2717")
2148
+ ), /* @__PURE__ */ React3.createElement("div", null, /* @__PURE__ */ React3.createElement(Text, { strong: true, style: {
2149
+ fontSize: "20px",
2150
+ color: "#262626",
2151
+ display: "block",
2152
+ marginBottom: "8px"
2153
+ } }, "Payment Failed"), /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "14px", color: "#8c8c8c" } }, "Something went wrong with your transaction"))), /* @__PURE__ */ React3.createElement(Divider, { style: { margin: "20px 0", borderColor: "#f0f0f0" } }, /* @__PURE__ */ React3.createElement(Text, { style: { fontSize: "12px", color: "#8c8c8c", fontWeight: 500 } }, "ERROR DETAILS")), /* @__PURE__ */ React3.createElement(
2154
+ "div",
2155
+ {
2156
+ style: {
2157
+ background: "#fef2f2",
2158
+ padding: "20px",
2159
+ borderRadius: "12px",
2160
+ border: "1px solid #fee2e2"
2161
+ }
2162
+ },
2163
+ /* @__PURE__ */ React3.createElement(Text, { style: {
2164
+ fontSize: "14px",
2165
+ color: "#dc2626",
2166
+ lineHeight: "1.6",
2167
+ fontWeight: 500
2168
+ } }, error)
2169
+ ), /* @__PURE__ */ React3.createElement("div", { className: "mt-4 text-center" }, /* @__PURE__ */ React3.createElement(
2170
+ Button,
2171
+ {
2172
+ size: "large",
2173
+ onClick: handlePayment,
2174
+ style: {
2175
+ height: "44px",
2176
+ fontSize: "14px",
2177
+ fontWeight: 600,
2178
+ borderRadius: "8px",
2179
+ background: "#1a1a1a",
2180
+ borderColor: "#1a1a1a",
2181
+ color: "white",
2182
+ paddingLeft: "32px",
2183
+ paddingRight: "32px"
2184
+ }
2185
+ },
2186
+ "Try Again"
2187
+ )))
2188
+ )
2189
+ ),
2190
+ /* @__PURE__ */ React3.createElement("style", { dangerouslySetInnerHTML: {
2191
+ __html: `
2192
+ @keyframes slideInRight {
2193
+ from {
2194
+ opacity: 0;
2195
+ transform: translateX(100px);
2196
+ }
2197
+ to {
2198
+ opacity: 1;
2199
+ transform: translateX(0);
2200
+ }
2201
+ }
2202
+
2203
+ @keyframes pulse {
2204
+ 0%, 100% {
2205
+ transform: scale(1);
2206
+ opacity: 0.4;
2207
+ }
2208
+ 50% {
2209
+ transform: scale(1.1);
2210
+ opacity: 0.6;
2211
+ }
2212
+ }
2213
+ `
2214
+ } })
2215
+ );
1536
2216
  }
1537
2217
  export {
1538
- PaymentButton,
2218
+ V402Checkout,
1539
2219
  WalletConnect,
2220
+ usePageNetwork,
1540
2221
  usePayment,
1541
2222
  usePaymentInfo,
1542
2223
  useWallet