@moneymq/react 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,1525 @@
1
+ // src/provider.tsx
2
+ import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useMemo as useMemo3, useState as useState3 } from "react";
3
+ import { ConnectionProvider, WalletProvider } from "@solana/wallet-adapter-react";
4
+ import { PhantomWalletAdapter, SolflareWalletAdapter } from "@solana/wallet-adapter-wallets";
5
+
6
+ // src/wallet-modal-provider.tsx
7
+ import { createContext, useContext, useState as useState2, useCallback as useCallback2, useMemo as useMemo2 } from "react";
8
+
9
+ // src/wallet-modal.tsx
10
+ import { useCallback, useEffect, useMemo, useState } from "react";
11
+ import { useWallet } from "@solana/wallet-adapter-react";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ var overlayStyle = {
14
+ position: "fixed",
15
+ inset: 0,
16
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
17
+ backdropFilter: "blur(4px)",
18
+ display: "flex",
19
+ alignItems: "center",
20
+ justifyContent: "center",
21
+ zIndex: 9999,
22
+ animation: "fadeIn 150ms ease-out"
23
+ };
24
+ var modalStyle = {
25
+ backgroundColor: "#18181b",
26
+ borderRadius: "1rem",
27
+ padding: "1.5rem",
28
+ width: "100%",
29
+ maxWidth: "400px",
30
+ maxHeight: "90vh",
31
+ overflow: "auto",
32
+ border: "1px solid #27272a",
33
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)",
34
+ animation: "slideUp 150ms ease-out"
35
+ };
36
+ var headerStyle = {
37
+ display: "flex",
38
+ alignItems: "center",
39
+ gap: "0.75rem",
40
+ marginBottom: "0.5rem"
41
+ };
42
+ var logoStyle = {
43
+ width: "2.5rem",
44
+ height: "2.5rem",
45
+ borderRadius: "0.5rem",
46
+ objectFit: "contain"
47
+ };
48
+ var titleStyle = {
49
+ fontSize: "1.25rem",
50
+ fontWeight: 600,
51
+ color: "#fafafa",
52
+ margin: 0
53
+ };
54
+ var descriptionStyle = {
55
+ fontSize: "0.875rem",
56
+ color: "#a1a1aa",
57
+ marginBottom: "1.5rem"
58
+ };
59
+ var walletListStyle = {
60
+ display: "flex",
61
+ flexDirection: "column",
62
+ gap: "0.5rem"
63
+ };
64
+ var walletButtonStyle = {
65
+ display: "flex",
66
+ alignItems: "center",
67
+ gap: "0.75rem",
68
+ width: "100%",
69
+ padding: "0.875rem 1rem",
70
+ backgroundColor: "#27272a",
71
+ border: "1px solid #3f3f46",
72
+ borderRadius: "0.75rem",
73
+ cursor: "pointer",
74
+ transition: "all 150ms",
75
+ textAlign: "left"
76
+ };
77
+ var walletIconStyle = {
78
+ width: "2rem",
79
+ height: "2rem",
80
+ borderRadius: "0.375rem"
81
+ };
82
+ var walletNameStyle = {
83
+ flex: 1,
84
+ fontSize: "0.9375rem",
85
+ fontWeight: 500,
86
+ color: "#fafafa"
87
+ };
88
+ var walletTagStyle = {
89
+ fontSize: "0.75rem",
90
+ padding: "0.25rem 0.5rem",
91
+ borderRadius: "9999px",
92
+ backgroundColor: "#3f3f46",
93
+ color: "#a1a1aa"
94
+ };
95
+ var closeButtonStyle = {
96
+ position: "absolute",
97
+ top: "1rem",
98
+ right: "1rem",
99
+ padding: "0.5rem",
100
+ backgroundColor: "transparent",
101
+ border: "none",
102
+ borderRadius: "0.375rem",
103
+ cursor: "pointer",
104
+ color: "#71717a",
105
+ transition: "color 150ms"
106
+ };
107
+ var noWalletsStyle = {
108
+ textAlign: "center",
109
+ padding: "2rem 1rem",
110
+ color: "#a1a1aa",
111
+ fontSize: "0.875rem"
112
+ };
113
+ function WalletModal({ visible, onClose, branding }) {
114
+ const { wallets, select, connecting } = useWallet();
115
+ const [hoveredWallet, setHoveredWallet] = useState(null);
116
+ const accentColor = branding?.accentColor || "#ec4899";
117
+ const sortedWallets = useMemo(() => {
118
+ const installed = [];
119
+ const notInstalled = [];
120
+ for (const wallet of wallets) {
121
+ if (wallet.readyState === "Installed") {
122
+ installed.push(wallet);
123
+ } else if (wallet.readyState === "Loadable" || wallet.readyState === "NotDetected") {
124
+ notInstalled.push(wallet);
125
+ }
126
+ }
127
+ return [...installed, ...notInstalled];
128
+ }, [wallets]);
129
+ const handleSelect = useCallback((wallet) => {
130
+ select(wallet.adapter.name);
131
+ onClose();
132
+ }, [select, onClose]);
133
+ useEffect(() => {
134
+ const handleEscape = (e) => {
135
+ if (e.key === "Escape") onClose();
136
+ };
137
+ if (visible) {
138
+ document.addEventListener("keydown", handleEscape);
139
+ document.body.style.overflow = "hidden";
140
+ }
141
+ return () => {
142
+ document.removeEventListener("keydown", handleEscape);
143
+ document.body.style.overflow = "";
144
+ };
145
+ }, [visible, onClose]);
146
+ if (!visible) return null;
147
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
148
+ /* @__PURE__ */ jsx("style", { children: `
149
+ @keyframes fadeIn {
150
+ from { opacity: 0; }
151
+ to { opacity: 1; }
152
+ }
153
+ @keyframes slideUp {
154
+ from { opacity: 0; transform: translateY(10px); }
155
+ to { opacity: 1; transform: translateY(0); }
156
+ }
157
+ ` }),
158
+ /* @__PURE__ */ jsx("div", { style: overlayStyle, onClick: onClose, children: /* @__PURE__ */ jsxs(
159
+ "div",
160
+ {
161
+ style: { ...modalStyle, position: "relative" },
162
+ onClick: (e) => e.stopPropagation(),
163
+ children: [
164
+ /* @__PURE__ */ jsx(
165
+ "button",
166
+ {
167
+ style: closeButtonStyle,
168
+ onClick: onClose,
169
+ onMouseEnter: (e) => e.currentTarget.style.color = "#fafafa",
170
+ onMouseLeave: (e) => e.currentTarget.style.color = "#71717a",
171
+ children: /* @__PURE__ */ jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx("path", { d: "M18 6L6 18M6 6l12 12" }) })
172
+ }
173
+ ),
174
+ /* @__PURE__ */ jsxs("div", { style: headerStyle, children: [
175
+ branding?.logo && /* @__PURE__ */ jsx("img", { src: branding.logo, alt: "", style: logoStyle }),
176
+ /* @__PURE__ */ jsx("h2", { style: titleStyle, children: branding?.title || "Connect Wallet" })
177
+ ] }),
178
+ /* @__PURE__ */ jsx("p", { style: descriptionStyle, children: branding?.description || "Select a wallet to connect and pay securely." }),
179
+ /* @__PURE__ */ jsx("div", { style: walletListStyle, children: sortedWallets.length === 0 ? /* @__PURE__ */ jsx("div", { style: noWalletsStyle, children: "No wallets found. Please install a Solana wallet extension." }) : sortedWallets.map((wallet) => {
180
+ const isInstalled = wallet.readyState === "Installed";
181
+ const isHovered = hoveredWallet === wallet.adapter.name;
182
+ return /* @__PURE__ */ jsxs(
183
+ "button",
184
+ {
185
+ style: {
186
+ ...walletButtonStyle,
187
+ backgroundColor: isHovered ? "#3f3f46" : "#27272a",
188
+ borderColor: isHovered ? accentColor : "#3f3f46"
189
+ },
190
+ onClick: () => handleSelect(wallet),
191
+ onMouseEnter: () => setHoveredWallet(wallet.adapter.name),
192
+ onMouseLeave: () => setHoveredWallet(null),
193
+ disabled: connecting,
194
+ children: [
195
+ /* @__PURE__ */ jsx(
196
+ "img",
197
+ {
198
+ src: wallet.adapter.icon,
199
+ alt: wallet.adapter.name,
200
+ style: walletIconStyle
201
+ }
202
+ ),
203
+ /* @__PURE__ */ jsx("span", { style: walletNameStyle, children: wallet.adapter.name }),
204
+ isInstalled ? /* @__PURE__ */ jsx("span", { style: { ...walletTagStyle, backgroundColor: accentColor + "20", color: accentColor }, children: "Detected" }) : /* @__PURE__ */ jsx("span", { style: walletTagStyle, children: "Install" })
205
+ ]
206
+ },
207
+ wallet.adapter.name
208
+ );
209
+ }) })
210
+ ]
211
+ }
212
+ ) })
213
+ ] });
214
+ }
215
+
216
+ // src/wallet-modal-provider.tsx
217
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
218
+ var WalletModalContext = createContext({
219
+ visible: false,
220
+ setVisible: () => {
221
+ }
222
+ });
223
+ var BrandingContext = createContext(void 0);
224
+ function useWalletModal() {
225
+ return useContext(WalletModalContext);
226
+ }
227
+ function useBranding() {
228
+ return useContext(BrandingContext);
229
+ }
230
+ function CustomWalletModalProvider({ children, branding }) {
231
+ const [visible, setVisible] = useState2(false);
232
+ const handleClose = useCallback2(() => setVisible(false), []);
233
+ const contextValue = useMemo2(
234
+ () => ({ visible, setVisible }),
235
+ [visible]
236
+ );
237
+ return /* @__PURE__ */ jsx2(BrandingContext.Provider, { value: branding, children: /* @__PURE__ */ jsxs2(WalletModalContext.Provider, { value: contextValue, children: [
238
+ children,
239
+ /* @__PURE__ */ jsx2(WalletModal, { visible, onClose: handleClose, branding })
240
+ ] }) });
241
+ }
242
+
243
+ // src/provider.tsx
244
+ import { jsx as jsx3 } from "react/jsx-runtime";
245
+ var MoneyMQContext = createContext2(null);
246
+ var SandboxContext = createContext2({
247
+ isSandboxMode: false,
248
+ sandboxAccounts: []
249
+ });
250
+ var DEFAULT_RPC_URL = "https://api.devnet.solana.com";
251
+ var SANDBOX_HASH_PREFIX = "SURFNETxSAFEHASH";
252
+ function normalizeRpcUrl(url) {
253
+ return url.replace("0.0.0.0", "localhost").replace("127.0.0.1", "localhost");
254
+ }
255
+ async function getRpcUrl(apiUrl) {
256
+ try {
257
+ console.log("[MoneyMQ] Fetching config from:", `${apiUrl}/config`);
258
+ const response = await fetch(`${apiUrl}/config`);
259
+ const config = await response.json();
260
+ const rawRpcUrl = config.x402?.validator?.rpcUrl || DEFAULT_RPC_URL;
261
+ console.log("[MoneyMQ] Raw RPC URL from config:", rawRpcUrl);
262
+ const normalizedUrl = normalizeRpcUrl(rawRpcUrl);
263
+ console.log("[MoneyMQ] Normalized RPC URL:", normalizedUrl);
264
+ return normalizedUrl;
265
+ } catch (err) {
266
+ console.error("[MoneyMQ] Error fetching config:", err);
267
+ return DEFAULT_RPC_URL;
268
+ }
269
+ }
270
+ async function checkSandboxMode(rpcUrl) {
271
+ try {
272
+ console.log("[MoneyMQ] Checking sandbox mode with RPC:", rpcUrl);
273
+ const response = await fetch(rpcUrl, {
274
+ method: "POST",
275
+ headers: { "Content-Type": "application/json" },
276
+ body: JSON.stringify({
277
+ jsonrpc: "2.0",
278
+ id: 1,
279
+ method: "getLatestBlockhash",
280
+ params: [{ commitment: "finalized" }]
281
+ })
282
+ });
283
+ const data = await response.json();
284
+ const blockhash = data?.result?.value?.blockhash || "";
285
+ const isSandbox = blockhash.startsWith(SANDBOX_HASH_PREFIX);
286
+ console.log("[MoneyMQ] Blockhash:", blockhash, "| Sandbox:", isSandbox);
287
+ return isSandbox;
288
+ } catch (err) {
289
+ console.error("[MoneyMQ] Error checking sandbox mode:", err);
290
+ return false;
291
+ }
292
+ }
293
+ async function fetchTokenBalance(rpcUrl, tokenAccountAddress) {
294
+ try {
295
+ const response = await fetch(rpcUrl, {
296
+ method: "POST",
297
+ headers: { "Content-Type": "application/json" },
298
+ body: JSON.stringify({
299
+ jsonrpc: "2.0",
300
+ id: 1,
301
+ method: "getAccountInfo",
302
+ params: [tokenAccountAddress, { encoding: "jsonParsed" }]
303
+ })
304
+ });
305
+ const data = await response.json();
306
+ if (data.result?.value?.data?.parsed?.info?.tokenAmount) {
307
+ return parseFloat(data.result.value.data.parsed.info.tokenAmount.uiAmountString || "0");
308
+ }
309
+ return 0;
310
+ } catch {
311
+ return 0;
312
+ }
313
+ }
314
+ async function fetchSandboxAccounts(apiUrl, rpcUrl) {
315
+ try {
316
+ console.log("[MoneyMQ] Fetching sandbox accounts from:", `${apiUrl}/sandbox/accounts`);
317
+ const response = await fetch(`${apiUrl}/sandbox/accounts`);
318
+ if (!response.ok) {
319
+ console.log("[MoneyMQ] Sandbox accounts fetch failed:", response.status);
320
+ return [];
321
+ }
322
+ const data = await response.json();
323
+ if (data.solana?.userAccounts) {
324
+ const accounts = await Promise.all(
325
+ data.solana.userAccounts.map(async (acc) => {
326
+ let usdcBalance = 0;
327
+ if (acc.stablecoins?.usdc) {
328
+ usdcBalance = await fetchTokenBalance(rpcUrl, acc.stablecoins.usdc);
329
+ }
330
+ return {
331
+ id: acc.address,
332
+ name: acc.label,
333
+ address: acc.address,
334
+ secretKeyHex: acc.secretKeyHex,
335
+ stablecoins: acc.stablecoins,
336
+ usdcBalance
337
+ };
338
+ })
339
+ );
340
+ console.log("[MoneyMQ] Sandbox accounts loaded:", accounts.length);
341
+ return accounts;
342
+ }
343
+ return [];
344
+ } catch (err) {
345
+ console.error("[MoneyMQ] Error fetching sandbox accounts:", err);
346
+ return [];
347
+ }
348
+ }
349
+ function useMoneyMQ() {
350
+ const client = useContext2(MoneyMQContext);
351
+ if (!client) {
352
+ throw new Error("useMoneyMQ must be used within a MoneyMQProvider");
353
+ }
354
+ return client;
355
+ }
356
+ function useSandbox() {
357
+ return useContext2(SandboxContext);
358
+ }
359
+ function MoneyMQProvider({
360
+ children,
361
+ client,
362
+ branding
363
+ }) {
364
+ const [rpcEndpoint, setRpcEndpoint] = useState3(null);
365
+ const [isSandboxMode, setIsSandboxMode] = useState3(false);
366
+ const [sandboxAccounts, setSandboxAccounts] = useState3([]);
367
+ useEffect2(() => {
368
+ async function initialize() {
369
+ const rpcUrl = await getRpcUrl(client.config.url);
370
+ setRpcEndpoint(rpcUrl);
371
+ const isSandbox = await checkSandboxMode(rpcUrl);
372
+ setIsSandboxMode(isSandbox);
373
+ if (isSandbox) {
374
+ const accounts = await fetchSandboxAccounts(client.config.url, rpcUrl);
375
+ setSandboxAccounts(accounts);
376
+ }
377
+ }
378
+ initialize();
379
+ }, [client.config.url]);
380
+ const wallets = useMemo3(
381
+ () => [
382
+ new PhantomWalletAdapter(),
383
+ new SolflareWalletAdapter()
384
+ ],
385
+ []
386
+ );
387
+ const sandboxContextValue = useMemo3(
388
+ () => ({ isSandboxMode, sandboxAccounts }),
389
+ [isSandboxMode, sandboxAccounts]
390
+ );
391
+ if (!rpcEndpoint) {
392
+ return null;
393
+ }
394
+ return /* @__PURE__ */ jsx3(MoneyMQContext.Provider, { value: client, children: /* @__PURE__ */ jsx3(SandboxContext.Provider, { value: sandboxContextValue, children: /* @__PURE__ */ jsx3(ConnectionProvider, { endpoint: rpcEndpoint, children: /* @__PURE__ */ jsx3(WalletProvider, { wallets, autoConnect: true, children: /* @__PURE__ */ jsx3(CustomWalletModalProvider, { branding, children }) }) }) }) });
395
+ }
396
+
397
+ // src/pay-button.tsx
398
+ import { forwardRef, useEffect as useEffect3, useState as useState5 } from "react";
399
+
400
+ // src/payment-modal.tsx
401
+ import { useCallback as useCallback3, useState as useState4 } from "react";
402
+ import { useWallet as useWallet2 } from "@solana/wallet-adapter-react";
403
+ import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react";
404
+ import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
405
+ function encodeFormData(data) {
406
+ const params = new URLSearchParams();
407
+ const addParams = (obj, prefix = "") => {
408
+ for (const [key, value] of Object.entries(obj)) {
409
+ const paramKey = prefix ? `${prefix}[${key}]` : key;
410
+ if (value === null || value === void 0) {
411
+ continue;
412
+ } else if (typeof value === "object" && !Array.isArray(value)) {
413
+ addParams(value, paramKey);
414
+ } else {
415
+ params.append(paramKey, String(value));
416
+ }
417
+ }
418
+ };
419
+ addParams(data);
420
+ return params.toString();
421
+ }
422
+ function normalizeRpcUrl2(url) {
423
+ return url.replace("0.0.0.0", "localhost").replace("127.0.0.1", "localhost");
424
+ }
425
+ async function makeRequestWith402Handling(url, method, body, secretKeyHex, rpcUrl, headers = {}) {
426
+ console.log(`[MoneyMQ] Making ${method} request to ${url}`);
427
+ const formData = body ? encodeFormData(body) : void 0;
428
+ let response = await fetch(url, {
429
+ method,
430
+ headers: {
431
+ "Content-Type": "application/x-www-form-urlencoded",
432
+ ...headers
433
+ },
434
+ body: formData
435
+ });
436
+ let data = await response.json();
437
+ console.log(`[MoneyMQ] Response status: ${response.status}`, data);
438
+ if (response.status === 402) {
439
+ console.log("[MoneyMQ] \u{1F4B3} 402 Payment Required - processing payment...");
440
+ const paymentRequirements = data?.payment_requirements || data?.error?.payment_requirements || [];
441
+ if (paymentRequirements.length === 0) {
442
+ console.warn("[MoneyMQ] \u26A0\uFE0F No payment requirements found in 402 response");
443
+ throw new Error("Payment required but no payment requirements provided");
444
+ }
445
+ console.log("[MoneyMQ] Payment requirements:", paymentRequirements);
446
+ const { createPaymentHeader, selectPaymentRequirements } = await import("x402/client");
447
+ const { createSigner } = await import("x402-fetch");
448
+ const signer = await createSigner("solana", secretKeyHex, {
449
+ svmConfig: rpcUrl
450
+ });
451
+ const selectedPaymentRequirement = selectPaymentRequirements(paymentRequirements, "solana", "exact");
452
+ console.log(`[MoneyMQ] \u{1F4B0} Creating payment for ${selectedPaymentRequirement.network}...`);
453
+ const paymentHeaderValue = await createPaymentHeader(
454
+ signer,
455
+ 1,
456
+ // x402Version
457
+ selectedPaymentRequirement,
458
+ {
459
+ svmConfig: {
460
+ rpcUrl
461
+ }
462
+ }
463
+ );
464
+ console.log("[MoneyMQ] \u2705 Payment header created, retrying request...");
465
+ response = await fetch(url, {
466
+ method,
467
+ headers: {
468
+ "Content-Type": "application/x-www-form-urlencoded",
469
+ "X-Payment": paymentHeaderValue,
470
+ ...headers
471
+ },
472
+ body: formData
473
+ });
474
+ data = await response.json();
475
+ console.log(`[MoneyMQ] Retry response status: ${response.status}`, data);
476
+ if (!response.ok) {
477
+ throw new Error(data.error?.message || "Request failed after payment");
478
+ }
479
+ } else if (!response.ok) {
480
+ throw new Error(data.error?.message || "Request failed");
481
+ }
482
+ return data;
483
+ }
484
+ async function createSandboxPayment(apiUrl, rpcUrl, amount, currency, recipient, senderAddress, secretKeyHex, productName) {
485
+ console.log("[MoneyMQ] Creating sandbox payment...", { amount, currency, recipient, senderAddress });
486
+ const paymentIntent = await makeRequestWith402Handling(
487
+ `${apiUrl}/v1/payment_intents`,
488
+ "POST",
489
+ {
490
+ amount: Math.round(amount * 100),
491
+ // Convert to cents (Stripe-style)
492
+ currency: currency.toLowerCase(),
493
+ customer: senderAddress,
494
+ description: productName ? `Purchase - ${productName}` : "Payment",
495
+ metadata: {
496
+ sender_address: senderAddress,
497
+ recipient_address: recipient
498
+ }
499
+ },
500
+ secretKeyHex,
501
+ rpcUrl
502
+ );
503
+ console.log("[MoneyMQ] Payment intent created:", paymentIntent);
504
+ console.log("[MoneyMQ] Confirming payment intent:", paymentIntent.id);
505
+ const confirmedIntent = await makeRequestWith402Handling(
506
+ `${apiUrl}/v1/payment_intents/${paymentIntent.id}/confirm`,
507
+ "POST",
508
+ {},
509
+ secretKeyHex,
510
+ rpcUrl
511
+ );
512
+ console.log("[MoneyMQ] Payment intent confirmed:", confirmedIntent);
513
+ return confirmedIntent.id;
514
+ }
515
+ var truncateAddress = (address) => {
516
+ if (address.length <= 12) return address;
517
+ return `${address.slice(0, 4)}...${address.slice(-4)}`;
518
+ };
519
+ function PaymentModal({
520
+ visible,
521
+ onClose,
522
+ amount,
523
+ currency,
524
+ recipient,
525
+ productName,
526
+ onSuccess,
527
+ onError,
528
+ accentColor = "#ec4899"
529
+ }) {
530
+ const [isSending, setIsSending] = useState4(false);
531
+ const [copiedSender, setCopiedSender] = useState4(false);
532
+ const [copiedRecipient, setCopiedRecipient] = useState4(false);
533
+ const [showDetails, setShowDetails] = useState4(false);
534
+ const [selectedWallet, setSelectedWallet] = useState4(null);
535
+ const [selectedPaymentMethod, setSelectedPaymentMethod] = useState4(null);
536
+ const { publicKey, connected, disconnect, wallets, select, wallet: connectedWallet } = useWallet2();
537
+ const branding = useBranding();
538
+ const { isSandboxMode, sandboxAccounts } = useSandbox();
539
+ const client = useMoneyMQ();
540
+ const copyToClipboard = (text, type) => {
541
+ navigator.clipboard.writeText(text);
542
+ if (type === "sender") {
543
+ setCopiedSender(true);
544
+ setTimeout(() => setCopiedSender(false), 2e3);
545
+ } else {
546
+ setCopiedRecipient(true);
547
+ setTimeout(() => setCopiedRecipient(false), 2e3);
548
+ }
549
+ };
550
+ const handleSelectWallet = (wallet) => {
551
+ setSelectedWallet(wallet);
552
+ setSelectedPaymentMethod({ type: "browser_extension", wallet });
553
+ select(wallet.adapter.name);
554
+ };
555
+ const handleSelectSandboxAccount = (account) => {
556
+ setSelectedPaymentMethod({ type: "sandbox_account", sandboxAccount: account });
557
+ setSelectedWallet(null);
558
+ };
559
+ const handleDisconnect = async () => {
560
+ await disconnect();
561
+ setSelectedWallet(null);
562
+ setSelectedPaymentMethod(null);
563
+ };
564
+ const availableWallets = wallets.filter(
565
+ (wallet) => wallet.readyState === "Installed" || wallet.readyState === "Loadable"
566
+ );
567
+ const displayedSandboxAccounts = sandboxAccounts.slice(0, 3);
568
+ const currentWalletIcon = connectedWallet?.adapter.icon || selectedWallet?.adapter.icon;
569
+ const currentWalletName = connectedWallet?.adapter.name || selectedWallet?.adapter.name;
570
+ const getCurrentSelectionDisplay = () => {
571
+ if (connected && publicKey) {
572
+ return {
573
+ icon: currentWalletIcon,
574
+ name: currentWalletName,
575
+ address: publicKey.toBase58(),
576
+ type: "browser_extension"
577
+ };
578
+ }
579
+ if (selectedPaymentMethod?.type === "sandbox_account" && selectedPaymentMethod.sandboxAccount) {
580
+ const name = selectedPaymentMethod.sandboxAccount.name;
581
+ return {
582
+ icon: null,
583
+ name: name.charAt(0).toUpperCase() + name.slice(1),
584
+ address: selectedPaymentMethod.sandboxAccount.address,
585
+ type: "sandbox_account"
586
+ };
587
+ }
588
+ return null;
589
+ };
590
+ const currentSelection = getCurrentSelectionDisplay();
591
+ const handlePay = useCallback3(async () => {
592
+ if (!recipient) return;
593
+ let senderAddress;
594
+ let secretKeyHex;
595
+ if (selectedPaymentMethod?.type === "sandbox_account" && selectedPaymentMethod.sandboxAccount) {
596
+ senderAddress = selectedPaymentMethod.sandboxAccount.address;
597
+ secretKeyHex = selectedPaymentMethod.sandboxAccount.secretKeyHex;
598
+ } else if (publicKey) {
599
+ senderAddress = publicKey.toBase58();
600
+ } else {
601
+ return;
602
+ }
603
+ setIsSending(true);
604
+ try {
605
+ if (selectedPaymentMethod?.type === "sandbox_account" && secretKeyHex) {
606
+ const apiUrl = normalizeRpcUrl2(client.config.url);
607
+ let rpcUrl = "http://localhost:8899";
608
+ try {
609
+ const configResponse = await fetch(`${apiUrl}/config`);
610
+ const config = await configResponse.json();
611
+ rpcUrl = normalizeRpcUrl2(config.x402?.validator?.rpcUrl || rpcUrl);
612
+ } catch {
613
+ console.log("[MoneyMQ] Using default RPC URL");
614
+ }
615
+ const paymentId = await createSandboxPayment(
616
+ apiUrl,
617
+ rpcUrl,
618
+ amount,
619
+ currency,
620
+ recipient,
621
+ senderAddress,
622
+ secretKeyHex,
623
+ productName
624
+ );
625
+ setIsSending(false);
626
+ onSuccess?.(paymentId);
627
+ onClose();
628
+ return;
629
+ }
630
+ const event = new CustomEvent("moneymq:payment-initiated", {
631
+ detail: {
632
+ amount,
633
+ currency,
634
+ recipient,
635
+ sender: senderAddress,
636
+ paymentMethod: selectedPaymentMethod?.type || "browser_extension"
637
+ },
638
+ bubbles: true
639
+ });
640
+ window.dispatchEvent(event);
641
+ const signature = await new Promise((resolve, reject) => {
642
+ const handleSuccess = (e) => {
643
+ const customEvent = e;
644
+ cleanup();
645
+ resolve(customEvent.detail.signature);
646
+ };
647
+ const handleError = (e) => {
648
+ const customEvent = e;
649
+ cleanup();
650
+ reject(customEvent.detail);
651
+ };
652
+ const cleanup = () => {
653
+ window.removeEventListener("moneymq:payment-success", handleSuccess);
654
+ window.removeEventListener("moneymq:payment-error", handleError);
655
+ };
656
+ window.addEventListener("moneymq:payment-success", handleSuccess);
657
+ window.addEventListener("moneymq:payment-error", handleError);
658
+ setTimeout(() => {
659
+ cleanup();
660
+ reject(new Error("Payment timeout"));
661
+ }, 6e4);
662
+ });
663
+ setIsSending(false);
664
+ onSuccess?.(signature);
665
+ onClose();
666
+ } catch (err) {
667
+ console.error("Payment failed:", err);
668
+ setIsSending(false);
669
+ onError?.(err instanceof Error ? err : new Error(String(err)));
670
+ }
671
+ }, [publicKey, recipient, amount, currency, onSuccess, onError, onClose, selectedPaymentMethod, client.config.url, productName]);
672
+ const canPay = (connected && publicKey || selectedPaymentMethod?.type === "sandbox_account") && recipient && !isSending;
673
+ if (!visible) return null;
674
+ const WalletIcon = () => /* @__PURE__ */ jsxs3("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
675
+ /* @__PURE__ */ jsx4("path", { d: "M21 12V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2h14a2 2 0 002-2v-5z" }),
676
+ /* @__PURE__ */ jsx4("path", { d: "M16 12h.01", strokeWidth: "2", strokeLinecap: "round" })
677
+ ] });
678
+ const SandboxIcon = () => /* @__PURE__ */ jsx4("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: /* @__PURE__ */ jsx4("path", { d: "M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714c0 .597.237 1.17.659 1.591L19.8 15.3M14.25 3.104c.251.023.501.05.75.082M19.8 15.3l-1.57.393A9.065 9.065 0 0 1 12 15a9.065 9.065 0 0 1-6.23.693L5 15.5m14.8-.2a2.25 2.25 0 0 1 .775 2.646l-.972 2.916a2.25 2.25 0 0 1-2.134 1.538H6.532a2.25 2.25 0 0 1-2.135-1.538l-.971-2.916A2.25 2.25 0 0 1 4.2 15.3", strokeLinecap: "round", strokeLinejoin: "round" }) });
679
+ return /* @__PURE__ */ jsxs3(Fragment2, { children: [
680
+ /* @__PURE__ */ jsx4("style", { children: `
681
+ @keyframes spin {
682
+ from { transform: rotate(0deg); }
683
+ to { transform: rotate(360deg); }
684
+ }
685
+ ` }),
686
+ /* @__PURE__ */ jsx4(
687
+ "div",
688
+ {
689
+ style: {
690
+ position: "fixed",
691
+ inset: 0,
692
+ zIndex: 9998,
693
+ backgroundColor: "rgba(0, 0, 0, 0.6)",
694
+ backdropFilter: "blur(8px)"
695
+ },
696
+ onClick: onClose
697
+ }
698
+ ),
699
+ /* @__PURE__ */ jsx4(
700
+ "div",
701
+ {
702
+ style: {
703
+ position: "fixed",
704
+ inset: 0,
705
+ zIndex: 9999,
706
+ display: "flex",
707
+ alignItems: "center",
708
+ justifyContent: "center",
709
+ padding: "1rem"
710
+ },
711
+ onClick: onClose,
712
+ children: /* @__PURE__ */ jsxs3(
713
+ "div",
714
+ {
715
+ style: {
716
+ width: "100%",
717
+ maxWidth: "380px",
718
+ backgroundColor: "#2c2c2e",
719
+ borderRadius: "1rem",
720
+ overflow: "hidden",
721
+ boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.5)"
722
+ },
723
+ onClick: (e) => e.stopPropagation(),
724
+ children: [
725
+ /* @__PURE__ */ jsxs3(
726
+ "div",
727
+ {
728
+ style: {
729
+ display: "flex",
730
+ alignItems: "center",
731
+ justifyContent: "space-between",
732
+ padding: "1rem 1.25rem",
733
+ borderBottom: "1px solid #3a3a3c"
734
+ },
735
+ children: [
736
+ branding?.logo ? /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
737
+ /* @__PURE__ */ jsx4(
738
+ "img",
739
+ {
740
+ src: branding.logo,
741
+ alt: "Logo",
742
+ style: { height: "24px", width: "auto", filter: "invert(1)" }
743
+ }
744
+ ),
745
+ isSandboxMode && /* @__PURE__ */ jsx4("span", { style: { fontSize: "0.75rem", fontWeight: 600, color: "#ff9f0a" }, children: "| SANDBOX" })
746
+ ] }) : /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "0.5rem" }, children: [
747
+ /* @__PURE__ */ jsxs3("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [
748
+ /* @__PURE__ */ jsx4("circle", { cx: "12", cy: "12", r: "10", fill: accentColor }),
749
+ /* @__PURE__ */ jsx4("path", { d: "M8 12l2.5 2.5L16 9", stroke: "white", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
750
+ ] }),
751
+ /* @__PURE__ */ jsx4("span", { style: { fontSize: "1.125rem", fontWeight: 600, color: "#fff" }, children: "Pay" }),
752
+ isSandboxMode && /* @__PURE__ */ jsx4("span", { style: { fontSize: "0.75rem", fontWeight: 600, color: "#ff9f0a" }, children: "| SANDBOX" })
753
+ ] }),
754
+ /* @__PURE__ */ jsx4(
755
+ "button",
756
+ {
757
+ onClick: onClose,
758
+ style: {
759
+ padding: "0.375rem 0.75rem",
760
+ fontSize: "0.875rem",
761
+ fontWeight: 500,
762
+ color: "#0a84ff",
763
+ background: "none",
764
+ border: "none",
765
+ cursor: "pointer"
766
+ },
767
+ children: "Cancel"
768
+ }
769
+ )
770
+ ]
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsx4("div", { style: { padding: "0.75rem" }, children: currentSelection ? /* @__PURE__ */ jsx4(
774
+ "div",
775
+ {
776
+ style: {
777
+ backgroundColor: "#3a3a3c",
778
+ borderRadius: "0.75rem",
779
+ padding: "0.875rem 1rem",
780
+ marginBottom: "0.5rem"
781
+ },
782
+ children: /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "0.75rem" }, children: [
783
+ /* @__PURE__ */ jsx4(
784
+ "div",
785
+ {
786
+ style: {
787
+ width: "40px",
788
+ height: "40px",
789
+ borderRadius: "0.5rem",
790
+ backgroundColor: currentSelection.icon ? "transparent" : "#636366",
791
+ display: "flex",
792
+ alignItems: "center",
793
+ justifyContent: "center",
794
+ color: "#fff",
795
+ overflow: "hidden"
796
+ },
797
+ children: currentSelection.icon ? /* @__PURE__ */ jsx4(
798
+ "img",
799
+ {
800
+ src: currentSelection.icon,
801
+ alt: currentSelection.name || "Wallet",
802
+ style: { width: "40px", height: "40px", borderRadius: "0.5rem" }
803
+ }
804
+ ) : currentSelection.type === "sandbox_account" ? /* @__PURE__ */ jsx4(SandboxIcon, {}) : /* @__PURE__ */ jsx4(WalletIcon, {})
805
+ }
806
+ ),
807
+ /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
808
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem", marginBottom: "0.125rem" }, children: [
809
+ /* @__PURE__ */ jsxs3("span", { style: { fontSize: "0.75rem", color: "#8e8e93" }, children: [
810
+ "From ",
811
+ truncateAddress(currentSelection.address)
812
+ ] }),
813
+ /* @__PURE__ */ jsx4(
814
+ "button",
815
+ {
816
+ onClick: () => copyToClipboard(currentSelection.address, "sender"),
817
+ style: {
818
+ padding: 0,
819
+ background: "none",
820
+ border: "none",
821
+ cursor: "pointer",
822
+ color: copiedSender ? "#30d158" : "#8e8e93",
823
+ display: "flex",
824
+ alignItems: "center"
825
+ },
826
+ children: /* @__PURE__ */ jsxs3("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
827
+ /* @__PURE__ */ jsx4("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
828
+ /* @__PURE__ */ jsx4("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
829
+ ] })
830
+ }
831
+ )
832
+ ] }),
833
+ /* @__PURE__ */ jsx4(
834
+ "span",
835
+ {
836
+ style: {
837
+ fontSize: "0.9375rem",
838
+ fontWeight: 500,
839
+ color: "#fff"
840
+ },
841
+ children: currentSelection.name
842
+ }
843
+ )
844
+ ] }),
845
+ /* @__PURE__ */ jsx4(
846
+ "button",
847
+ {
848
+ onClick: handleDisconnect,
849
+ style: {
850
+ fontSize: "0.75rem",
851
+ fontWeight: 500,
852
+ color: "#ff453a",
853
+ background: "none",
854
+ border: "none",
855
+ cursor: "pointer",
856
+ padding: "0.25rem 0.5rem"
857
+ },
858
+ children: "unlink"
859
+ }
860
+ )
861
+ ] })
862
+ }
863
+ ) : /* @__PURE__ */ jsxs3(Menu, { as: "div", style: { position: "relative", marginBottom: "0.5rem" }, children: [
864
+ /* @__PURE__ */ jsxs3(
865
+ MenuButton,
866
+ {
867
+ style: {
868
+ width: "100%",
869
+ backgroundColor: "#3a3a3c",
870
+ borderRadius: "0.75rem",
871
+ padding: "0.875rem 1rem",
872
+ border: "none",
873
+ cursor: "pointer",
874
+ display: "flex",
875
+ alignItems: "center",
876
+ gap: "0.75rem",
877
+ textAlign: "left"
878
+ },
879
+ children: [
880
+ /* @__PURE__ */ jsx4(
881
+ "div",
882
+ {
883
+ style: {
884
+ width: "40px",
885
+ height: "40px",
886
+ borderRadius: "0.5rem",
887
+ backgroundColor: currentWalletIcon ? "transparent" : "#636366",
888
+ display: "flex",
889
+ alignItems: "center",
890
+ justifyContent: "center",
891
+ color: "#fff",
892
+ overflow: "hidden",
893
+ flexShrink: 0
894
+ },
895
+ children: currentWalletIcon ? /* @__PURE__ */ jsx4(
896
+ "img",
897
+ {
898
+ src: currentWalletIcon,
899
+ alt: currentWalletName || "Wallet",
900
+ style: { width: "40px", height: "40px", borderRadius: "0.5rem" }
901
+ }
902
+ ) : /* @__PURE__ */ jsx4(WalletIcon, {})
903
+ }
904
+ ),
905
+ /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
906
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "0.75rem", color: "#8e8e93", marginBottom: "0.125rem" }, children: "From" }),
907
+ /* @__PURE__ */ jsx4(
908
+ "span",
909
+ {
910
+ style: {
911
+ fontSize: "0.9375rem",
912
+ fontWeight: 500,
913
+ color: "#0a84ff"
914
+ },
915
+ children: selectedWallet ? selectedWallet.adapter.name : "Connect Wallet"
916
+ }
917
+ )
918
+ ] }),
919
+ /* @__PURE__ */ jsx4(
920
+ "svg",
921
+ {
922
+ width: "16",
923
+ height: "16",
924
+ viewBox: "0 0 16 16",
925
+ fill: "#8e8e93",
926
+ style: { flexShrink: 0 },
927
+ children: /* @__PURE__ */ jsx4("path", { fillRule: "evenodd", d: "M5.22 10.22a.75.75 0 0 1 1.06 0L8 11.94l1.72-1.72a.75.75 0 1 1 1.06 1.06l-2.25 2.25a.75.75 0 0 1-1.06 0l-2.25-2.25a.75.75 0 0 1 0-1.06ZM10.78 5.78a.75.75 0 0 1-1.06 0L8 4.06 6.28 5.78a.75.75 0 0 1-1.06-1.06l2.25-2.25a.75.75 0 0 1 1.06 0l2.25 2.25a.75.75 0 0 1 0 1.06Z", clipRule: "evenodd" })
928
+ }
929
+ )
930
+ ]
931
+ }
932
+ ),
933
+ /* @__PURE__ */ jsxs3(
934
+ MenuItems,
935
+ {
936
+ anchor: "bottom start",
937
+ style: {
938
+ backgroundColor: "#2c2c2e",
939
+ borderRadius: "0.75rem",
940
+ padding: "0.25rem",
941
+ boxShadow: "0 10px 25px rgba(0, 0, 0, 0.4)",
942
+ zIndex: 1e4,
943
+ outline: "none",
944
+ border: "1px solid #48484a",
945
+ maxHeight: "300px",
946
+ overflowY: "auto",
947
+ width: "var(--button-width)",
948
+ marginTop: "0.25rem"
949
+ },
950
+ children: [
951
+ isSandboxMode && displayedSandboxAccounts.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
952
+ displayedSandboxAccounts.map((account) => /* @__PURE__ */ jsx4(MenuItem, { children: ({ focus }) => /* @__PURE__ */ jsxs3(
953
+ "button",
954
+ {
955
+ onClick: () => handleSelectSandboxAccount(account),
956
+ style: {
957
+ display: "flex",
958
+ alignItems: "center",
959
+ gap: "0.75rem",
960
+ padding: "0.875rem 1rem",
961
+ backgroundColor: focus ? "#3a3a3c" : "transparent",
962
+ borderRadius: "0.5rem",
963
+ border: "none",
964
+ cursor: "pointer",
965
+ width: "100%",
966
+ textAlign: "left",
967
+ transition: "background-color 150ms"
968
+ },
969
+ children: [
970
+ /* @__PURE__ */ jsx4(
971
+ "div",
972
+ {
973
+ style: {
974
+ width: "40px",
975
+ height: "40px",
976
+ borderRadius: "0.5rem",
977
+ backgroundColor: "#636366",
978
+ display: "flex",
979
+ alignItems: "center",
980
+ justifyContent: "center",
981
+ overflow: "hidden",
982
+ flexShrink: 0,
983
+ color: "#fff"
984
+ },
985
+ children: /* @__PURE__ */ jsx4(SandboxIcon, {})
986
+ }
987
+ ),
988
+ /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
989
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem", marginBottom: "0.125rem" }, children: [
990
+ /* @__PURE__ */ jsxs3("span", { style: { fontSize: "0.75rem", color: "#8e8e93" }, children: [
991
+ "From ",
992
+ truncateAddress(account.address)
993
+ ] }),
994
+ /* @__PURE__ */ jsx4(
995
+ "span",
996
+ {
997
+ role: "button",
998
+ tabIndex: 0,
999
+ onClick: (e) => {
1000
+ e.stopPropagation();
1001
+ navigator.clipboard.writeText(account.address);
1002
+ },
1003
+ onKeyDown: (e) => {
1004
+ if (e.key === "Enter" || e.key === " ") {
1005
+ e.stopPropagation();
1006
+ navigator.clipboard.writeText(account.address);
1007
+ }
1008
+ },
1009
+ style: {
1010
+ padding: 0,
1011
+ background: "none",
1012
+ border: "none",
1013
+ cursor: "pointer",
1014
+ color: "#8e8e93",
1015
+ display: "flex",
1016
+ alignItems: "center"
1017
+ },
1018
+ children: /* @__PURE__ */ jsxs3("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1019
+ /* @__PURE__ */ jsx4("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
1020
+ /* @__PURE__ */ jsx4("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
1021
+ ] })
1022
+ }
1023
+ )
1024
+ ] }),
1025
+ /* @__PURE__ */ jsx4(
1026
+ "span",
1027
+ {
1028
+ style: {
1029
+ fontSize: "0.9375rem",
1030
+ fontWeight: 500,
1031
+ color: "#fff"
1032
+ },
1033
+ children: account.name.charAt(0).toUpperCase() + account.name.slice(1)
1034
+ }
1035
+ )
1036
+ ] }),
1037
+ account.usdcBalance !== void 0 && /* @__PURE__ */ jsxs3(
1038
+ "span",
1039
+ {
1040
+ style: {
1041
+ fontSize: "0.6875rem",
1042
+ color: "#ff9f0a",
1043
+ backgroundColor: "rgba(255, 159, 10, 0.15)",
1044
+ padding: "0.25rem 0.5rem",
1045
+ borderRadius: "0.25rem",
1046
+ flexShrink: 0
1047
+ },
1048
+ children: [
1049
+ account.usdcBalance.toLocaleString(),
1050
+ " USDC"
1051
+ ]
1052
+ }
1053
+ )
1054
+ ]
1055
+ }
1056
+ ) }, account.id)),
1057
+ availableWallets.length > 0 && /* @__PURE__ */ jsx4("div", { style: { height: "1px", backgroundColor: "#48484a", margin: "0.25rem 0" } })
1058
+ ] }),
1059
+ availableWallets.length > 0 ? availableWallets.map((wallet) => /* @__PURE__ */ jsx4(MenuItem, { children: ({ focus }) => /* @__PURE__ */ jsxs3(
1060
+ "button",
1061
+ {
1062
+ onClick: () => handleSelectWallet(wallet),
1063
+ style: {
1064
+ display: "flex",
1065
+ alignItems: "center",
1066
+ gap: "0.75rem",
1067
+ padding: "0.875rem 1rem",
1068
+ backgroundColor: focus ? "#3a3a3c" : "transparent",
1069
+ borderRadius: "0.5rem",
1070
+ border: "none",
1071
+ cursor: "pointer",
1072
+ width: "100%",
1073
+ textAlign: "left",
1074
+ transition: "background-color 150ms"
1075
+ },
1076
+ children: [
1077
+ /* @__PURE__ */ jsx4(
1078
+ "div",
1079
+ {
1080
+ style: {
1081
+ width: "40px",
1082
+ height: "40px",
1083
+ borderRadius: "0.5rem",
1084
+ backgroundColor: "#636366",
1085
+ display: "flex",
1086
+ alignItems: "center",
1087
+ justifyContent: "center",
1088
+ overflow: "hidden",
1089
+ flexShrink: 0
1090
+ },
1091
+ children: wallet.adapter.icon ? /* @__PURE__ */ jsx4(
1092
+ "img",
1093
+ {
1094
+ src: wallet.adapter.icon,
1095
+ alt: wallet.adapter.name,
1096
+ style: {
1097
+ width: "40px",
1098
+ height: "40px",
1099
+ borderRadius: "0.5rem"
1100
+ }
1101
+ }
1102
+ ) : /* @__PURE__ */ jsx4(WalletIcon, {})
1103
+ }
1104
+ ),
1105
+ /* @__PURE__ */ jsxs3("div", { style: { flex: 1, minWidth: 0 }, children: [
1106
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "0.75rem", color: "#8e8e93", marginBottom: "0.125rem" }, children: "Browser Extension" }),
1107
+ /* @__PURE__ */ jsx4(
1108
+ "span",
1109
+ {
1110
+ style: {
1111
+ fontSize: "0.9375rem",
1112
+ fontWeight: 500,
1113
+ color: "#fff"
1114
+ },
1115
+ children: wallet.adapter.name
1116
+ }
1117
+ )
1118
+ ] }),
1119
+ wallet.readyState === "Installed" && /* @__PURE__ */ jsx4(
1120
+ "span",
1121
+ {
1122
+ style: {
1123
+ fontSize: "0.6875rem",
1124
+ color: "#30d158",
1125
+ backgroundColor: "rgba(48, 209, 88, 0.15)",
1126
+ padding: "0.25rem 0.5rem",
1127
+ borderRadius: "0.25rem",
1128
+ flexShrink: 0
1129
+ },
1130
+ children: "Detected"
1131
+ }
1132
+ )
1133
+ ]
1134
+ }
1135
+ ) }, wallet.adapter.name)) : !isSandboxMode || displayedSandboxAccounts.length === 0 ? /* @__PURE__ */ jsx4(
1136
+ "div",
1137
+ {
1138
+ style: {
1139
+ textAlign: "center",
1140
+ padding: "1.5rem",
1141
+ color: "#8e8e93",
1142
+ fontSize: "0.875rem"
1143
+ },
1144
+ children: "No wallets detected"
1145
+ }
1146
+ ) : null
1147
+ ]
1148
+ }
1149
+ )
1150
+ ] }) }),
1151
+ /* @__PURE__ */ jsxs3(
1152
+ "div",
1153
+ {
1154
+ style: {
1155
+ padding: "1rem 1.25rem 1.25rem",
1156
+ borderTop: "1px solid #3a3a3c"
1157
+ },
1158
+ children: [
1159
+ /* @__PURE__ */ jsxs3("div", { style: { marginBottom: "1rem" }, children: [
1160
+ /* @__PURE__ */ jsx4("div", { style: { fontSize: "0.875rem", color: "#8e8e93", marginBottom: "0.25rem" }, children: productName ? `Pay ${productName}` : "Total" }),
1161
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1162
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "baseline", gap: "0.375rem" }, children: [
1163
+ /* @__PURE__ */ jsx4("span", { style: { fontSize: "2rem", fontWeight: 600, color: "#fff" }, children: amount.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }),
1164
+ /* @__PURE__ */ jsx4("span", { style: { fontSize: "1rem", fontWeight: 500, color: "#8e8e93" }, children: currency })
1165
+ ] }),
1166
+ /* @__PURE__ */ jsx4(
1167
+ "button",
1168
+ {
1169
+ onClick: () => setShowDetails(!showDetails),
1170
+ style: {
1171
+ width: "22px",
1172
+ height: "22px",
1173
+ borderRadius: "50%",
1174
+ border: "1.5px solid #8e8e93",
1175
+ background: "none",
1176
+ color: "#8e8e93",
1177
+ fontSize: "0.8125rem",
1178
+ fontWeight: 600,
1179
+ fontStyle: "italic",
1180
+ fontFamily: "Georgia, serif",
1181
+ cursor: "pointer",
1182
+ display: "flex",
1183
+ alignItems: "center",
1184
+ justifyContent: "center",
1185
+ padding: 0,
1186
+ transition: "all 150ms"
1187
+ },
1188
+ title: "Show payment details",
1189
+ children: "i"
1190
+ }
1191
+ )
1192
+ ] }),
1193
+ showDetails && /* @__PURE__ */ jsx4(
1194
+ "div",
1195
+ {
1196
+ style: {
1197
+ marginTop: "0.75rem",
1198
+ padding: "0.625rem 0.75rem",
1199
+ backgroundColor: "#3a3a3c",
1200
+ borderRadius: "0.5rem",
1201
+ fontSize: "0.8125rem"
1202
+ },
1203
+ children: /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between" }, children: [
1204
+ /* @__PURE__ */ jsx4("span", { style: { color: "#8e8e93" }, children: "Recipient" }),
1205
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", alignItems: "center", gap: "0.375rem" }, children: [
1206
+ /* @__PURE__ */ jsx4(
1207
+ "span",
1208
+ {
1209
+ style: {
1210
+ color: "#fff",
1211
+ fontFamily: "ui-monospace, monospace",
1212
+ fontSize: "0.75rem"
1213
+ },
1214
+ children: truncateAddress(recipient)
1215
+ }
1216
+ ),
1217
+ /* @__PURE__ */ jsx4(
1218
+ "button",
1219
+ {
1220
+ onClick: () => copyToClipboard(recipient, "recipient"),
1221
+ style: {
1222
+ padding: 0,
1223
+ background: "none",
1224
+ border: "none",
1225
+ cursor: "pointer",
1226
+ color: copiedRecipient ? "#30d158" : "#8e8e93",
1227
+ display: "flex",
1228
+ alignItems: "center"
1229
+ },
1230
+ children: /* @__PURE__ */ jsxs3("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1231
+ /* @__PURE__ */ jsx4("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
1232
+ /* @__PURE__ */ jsx4("path", { d: "M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1" })
1233
+ ] })
1234
+ }
1235
+ )
1236
+ ] })
1237
+ ] })
1238
+ }
1239
+ )
1240
+ ] }),
1241
+ /* @__PURE__ */ jsx4(
1242
+ "button",
1243
+ {
1244
+ onClick: handlePay,
1245
+ disabled: !canPay,
1246
+ style: {
1247
+ width: "100%",
1248
+ padding: "1rem",
1249
+ borderRadius: "0.75rem",
1250
+ border: "none",
1251
+ fontSize: "1.0625rem",
1252
+ fontWeight: 600,
1253
+ cursor: canPay ? "pointer" : "not-allowed",
1254
+ backgroundColor: canPay ? accentColor : "#48484a",
1255
+ color: canPay ? "#fff" : "#8e8e93",
1256
+ display: "flex",
1257
+ alignItems: "center",
1258
+ justifyContent: "center",
1259
+ gap: "0.5rem",
1260
+ transition: "opacity 150ms"
1261
+ },
1262
+ children: isSending ? /* @__PURE__ */ jsxs3(Fragment2, { children: [
1263
+ /* @__PURE__ */ jsx4(
1264
+ "div",
1265
+ {
1266
+ style: {
1267
+ width: "1.25rem",
1268
+ height: "1.25rem",
1269
+ border: "2px solid currentColor",
1270
+ borderTopColor: "transparent",
1271
+ borderRadius: "50%",
1272
+ animation: "spin 1s linear infinite"
1273
+ }
1274
+ }
1275
+ ),
1276
+ /* @__PURE__ */ jsx4("span", { children: "Processing..." })
1277
+ ] }) : /* @__PURE__ */ jsxs3(Fragment2, { children: [
1278
+ /* @__PURE__ */ jsx4("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx4("path", { fillRule: "evenodd", d: "M12.516 2.17a.75.75 0 0 0-1.032 0 11.209 11.209 0 0 1-7.877 3.08.75.75 0 0 0-.722.515A12.74 12.74 0 0 0 2.25 9.75c0 5.942 4.064 10.933 9.563 12.348a.749.749 0 0 0 .374 0c5.499-1.415 9.563-6.406 9.563-12.348 0-1.39-.223-2.73-.635-3.985a.75.75 0 0 0-.722-.516 11.209 11.209 0 0 1-7.877-3.08ZM10.28 10.22a.75.75 0 0 0-1.06 1.06l2.25 2.25a.75.75 0 0 0 1.06 0l3.75-3.75a.75.75 0 0 0-1.06-1.06l-3.22 3.22-1.72-1.72Z", clipRule: "evenodd" }) }),
1279
+ /* @__PURE__ */ jsxs3("span", { children: [
1280
+ "Pay ",
1281
+ amount.toLocaleString(void 0, { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
1282
+ " ",
1283
+ currency
1284
+ ] })
1285
+ ] })
1286
+ }
1287
+ )
1288
+ ]
1289
+ }
1290
+ )
1291
+ ]
1292
+ }
1293
+ )
1294
+ }
1295
+ )
1296
+ ] });
1297
+ }
1298
+
1299
+ // src/pay-button.tsx
1300
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1301
+ var baseStyle = {
1302
+ position: "relative",
1303
+ display: "inline-flex",
1304
+ alignItems: "center",
1305
+ justifyContent: "center",
1306
+ gap: "0.5rem",
1307
+ borderRadius: "0.5rem",
1308
+ fontSize: "0.875rem",
1309
+ fontWeight: 600,
1310
+ padding: "0.625rem 1rem",
1311
+ cursor: "pointer",
1312
+ transition: "all 200ms",
1313
+ border: "none"
1314
+ };
1315
+ var solidStyle = {
1316
+ ...baseStyle,
1317
+ backgroundColor: "#ec4899",
1318
+ color: "white",
1319
+ boxShadow: "0 1px 2px 0 rgb(0 0 0 / 0.05)"
1320
+ };
1321
+ var outlineStyle = {
1322
+ ...baseStyle,
1323
+ backgroundColor: "transparent",
1324
+ color: "#ec4899",
1325
+ border: "1px solid #ec4899"
1326
+ };
1327
+ var PayButton = forwardRef(
1328
+ function PayButton2({
1329
+ priceId,
1330
+ price: priceObject,
1331
+ product: productObject,
1332
+ onSuccess,
1333
+ onError,
1334
+ variant = "solid",
1335
+ children,
1336
+ disabled
1337
+ }, ref) {
1338
+ const client = useMoneyMQ();
1339
+ const [isModalOpen, setIsModalOpen] = useState5(false);
1340
+ const [isHovered, setIsHovered] = useState5(false);
1341
+ const hasPriceObject = priceObject !== void 0;
1342
+ const [isLoading, setIsLoading] = useState5(!hasPriceObject);
1343
+ const [error, setError] = useState5(null);
1344
+ const [amount, setAmount] = useState5(hasPriceObject ? priceObject.unit_amount / 100 : 0);
1345
+ const [currency, setCurrency] = useState5(hasPriceObject ? priceObject.currency.toUpperCase() : "USDC");
1346
+ const [recipient, setRecipient] = useState5("");
1347
+ const [productName, setProductName] = useState5(productObject?.name);
1348
+ useEffect3(() => {
1349
+ async function fetchPaymentDetails() {
1350
+ setIsLoading(true);
1351
+ setError(null);
1352
+ try {
1353
+ const apiUrl = client.config.url;
1354
+ const configResponse = await fetch(`${apiUrl}/config`);
1355
+ if (!configResponse.ok) {
1356
+ throw new Error(`Failed to fetch config: ${configResponse.status}`);
1357
+ }
1358
+ const config = await configResponse.json();
1359
+ if (config.x402?.payoutAccount?.address) {
1360
+ setRecipient(config.x402.payoutAccount.address);
1361
+ }
1362
+ if (hasPriceObject) {
1363
+ setAmount(priceObject.unit_amount / 100);
1364
+ setCurrency(priceObject.currency.toUpperCase());
1365
+ if (productObject) {
1366
+ setProductName(productObject.name);
1367
+ } else if (priceObject.product) {
1368
+ try {
1369
+ const productResponse = await fetch(`${apiUrl}/v1/products/${priceObject.product}`);
1370
+ if (productResponse.ok) {
1371
+ const product = await productResponse.json();
1372
+ setProductName(product.name);
1373
+ }
1374
+ } catch {
1375
+ }
1376
+ }
1377
+ } else if (priceId) {
1378
+ const priceResponse = await fetch(`${apiUrl}/v1/prices/${priceId}`);
1379
+ if (!priceResponse.ok) {
1380
+ throw new Error(`Failed to fetch price: ${priceResponse.status}`);
1381
+ }
1382
+ const price = await priceResponse.json();
1383
+ setAmount(price.unit_amount / 100);
1384
+ setCurrency(price.currency.toUpperCase());
1385
+ if (price.product) {
1386
+ try {
1387
+ const productResponse = await fetch(`${apiUrl}/v1/products/${price.product}`);
1388
+ if (productResponse.ok) {
1389
+ const product = await productResponse.json();
1390
+ setProductName(product.name);
1391
+ }
1392
+ } catch {
1393
+ }
1394
+ }
1395
+ } else {
1396
+ throw new Error("Either priceId or price object is required");
1397
+ }
1398
+ } catch (err) {
1399
+ console.error("[PayButton] Error fetching payment details:", err);
1400
+ const errorMessage = err instanceof Error ? err.message : "Failed to load payment details";
1401
+ setError(errorMessage);
1402
+ onError?.(new Error(errorMessage));
1403
+ } finally {
1404
+ setIsLoading(false);
1405
+ }
1406
+ }
1407
+ fetchPaymentDetails();
1408
+ }, [priceId, priceObject, productObject, client.config.url, onError, hasPriceObject]);
1409
+ const handleClick = () => {
1410
+ if (!isLoading && !error) {
1411
+ setIsModalOpen(true);
1412
+ }
1413
+ };
1414
+ const handlePaymentSuccess = (signature) => {
1415
+ const payment = {
1416
+ id: `pay_${Date.now()}`,
1417
+ amount,
1418
+ currency,
1419
+ status: "completed",
1420
+ signature
1421
+ };
1422
+ onSuccess?.(payment);
1423
+ };
1424
+ const handlePaymentError = (error2) => {
1425
+ onError?.(error2);
1426
+ };
1427
+ const isDisabled = disabled || isLoading || !!error;
1428
+ const buttonStyle = {
1429
+ ...variant === "outline" ? outlineStyle : solidStyle,
1430
+ backgroundColor: variant === "solid" ? isHovered && !isDisabled ? "#db2777" : "#ec4899" : isHovered && !isDisabled ? "rgba(236, 72, 153, 0.1)" : "transparent",
1431
+ opacity: isDisabled ? 0.5 : 1,
1432
+ cursor: isDisabled ? "not-allowed" : "pointer"
1433
+ };
1434
+ return /* @__PURE__ */ jsxs4(Fragment3, { children: [
1435
+ /* @__PURE__ */ jsx5(
1436
+ "button",
1437
+ {
1438
+ ref,
1439
+ onClick: handleClick,
1440
+ disabled: isDisabled,
1441
+ style: buttonStyle,
1442
+ title: error || void 0,
1443
+ onMouseEnter: () => setIsHovered(true),
1444
+ onMouseLeave: () => setIsHovered(false),
1445
+ children: isLoading ? /* @__PURE__ */ jsx5("span", { children: "Loading..." }) : error ? /* @__PURE__ */ jsx5("span", { children: "Error" }) : children || /* @__PURE__ */ jsx5("span", { children: "Pay" })
1446
+ }
1447
+ ),
1448
+ /* @__PURE__ */ jsx5(
1449
+ PaymentModal,
1450
+ {
1451
+ visible: isModalOpen,
1452
+ onClose: () => setIsModalOpen(false),
1453
+ amount,
1454
+ currency,
1455
+ recipient,
1456
+ productName,
1457
+ onSuccess: handlePaymentSuccess,
1458
+ onError: handlePaymentError
1459
+ }
1460
+ )
1461
+ ] });
1462
+ }
1463
+ );
1464
+
1465
+ // src/use-payment.ts
1466
+ import { useCallback as useCallback4, useState as useState6 } from "react";
1467
+ import { useWallet as useWallet3 } from "@solana/wallet-adapter-react";
1468
+ function usePayment() {
1469
+ const [isPending, setIsPending] = useState6(false);
1470
+ const [lastPayment, setLastPayment] = useState6(null);
1471
+ const { publicKey, connected } = useWallet3();
1472
+ const { setVisible } = useWalletModal();
1473
+ const pay = useCallback4(async (priceId) => {
1474
+ if (!connected || !publicKey) {
1475
+ setVisible(true);
1476
+ return null;
1477
+ }
1478
+ setIsPending(true);
1479
+ try {
1480
+ const event = new CustomEvent("moneymq:pay", {
1481
+ detail: { priceId, publicKey: publicKey.toBase58() },
1482
+ bubbles: true
1483
+ });
1484
+ window.dispatchEvent(event);
1485
+ const result = await new Promise((resolve, reject) => {
1486
+ const handleSuccess = (e) => {
1487
+ const customEvent = e;
1488
+ cleanup();
1489
+ resolve(customEvent.detail);
1490
+ };
1491
+ const handleError = (e) => {
1492
+ const customEvent = e;
1493
+ cleanup();
1494
+ reject(customEvent.detail);
1495
+ };
1496
+ const cleanup = () => {
1497
+ window.removeEventListener("moneymq:payment-success", handleSuccess);
1498
+ window.removeEventListener("moneymq:payment-error", handleError);
1499
+ };
1500
+ window.addEventListener("moneymq:payment-success", handleSuccess);
1501
+ window.addEventListener("moneymq:payment-error", handleError);
1502
+ setTimeout(() => {
1503
+ cleanup();
1504
+ reject(new Error("Payment timeout"));
1505
+ }, 6e4);
1506
+ });
1507
+ setLastPayment(result);
1508
+ return result;
1509
+ } catch {
1510
+ return null;
1511
+ } finally {
1512
+ setIsPending(false);
1513
+ }
1514
+ }, [connected, publicKey, setVisible]);
1515
+ return { pay, isPending, lastPayment };
1516
+ }
1517
+ export {
1518
+ MoneyMQProvider,
1519
+ PayButton,
1520
+ PaymentModal,
1521
+ useMoneyMQ,
1522
+ usePayment,
1523
+ useSandbox
1524
+ };
1525
+ //# sourceMappingURL=index.mjs.map