forgelayer-react 1.0.0 → 1.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.
@@ -0,0 +1,121 @@
1
+ import * as React from 'react';
2
+
3
+ // ── Order object returned by /fl/create ──────────────────────────────────────
4
+
5
+ export interface ForgeLayerOrder {
6
+ ok: true;
7
+ address: string;
8
+ chain: string;
9
+ chainName: string;
10
+ token: string;
11
+ amount: number;
12
+ currency: string;
13
+ /** Crypto amount as a string (e.g. "49.99000000") */
14
+ cryptoAmount: string | null;
15
+ /** Unix timestamp (seconds) when the payment window closes */
16
+ expiresAt: number;
17
+ orderId: string;
18
+ sessionKey: string;
19
+ qrUrl: string;
20
+ /** Present when the same address was reused from an existing pending order */
21
+ reused?: boolean;
22
+ }
23
+
24
+ // ── Modal state ───────────────────────────────────────────────────────────────
25
+
26
+ export type ForgeLayerModalState = 'closed' | 'loading' | 'payment' | 'success' | 'expired' | 'error';
27
+
28
+ // ── useForgeLayerCheckout ─────────────────────────────────────────────────────
29
+
30
+ export interface UseForgeLayerCheckoutOptions {
31
+ /** Base path where forgelayer-node is mounted. Default: '/fl' */
32
+ baseUrl?: string;
33
+ /** Status polling interval in milliseconds. Default: 15000 */
34
+ pollInterval?: number;
35
+ /** Called when payment is confirmed */
36
+ onSuccess?: (order: ForgeLayerOrder) => void;
37
+ /** Called when the payment window expires */
38
+ onExpired?: () => void;
39
+ /** Called when an error occurs during address generation */
40
+ onError?: (error: Error) => void;
41
+ /** Called when the modal opens (loading state begins) */
42
+ onOpen?: () => void;
43
+ /** Called when the modal is closed by the user */
44
+ onClose?: () => void;
45
+ }
46
+
47
+ export interface OpenParams {
48
+ amount: number;
49
+ currency?: string;
50
+ chain?: string;
51
+ token?: string;
52
+ orderId?: string;
53
+ /** Minutes before payment expires */
54
+ paymentWindow?: number;
55
+ /** Return same address for this orderId if still pending */
56
+ reuseAddress?: boolean;
57
+ }
58
+
59
+ export interface UseForgeLayerCheckoutResult {
60
+ modalState: ForgeLayerModalState;
61
+ order: ForgeLayerOrder | null;
62
+ /** Seconds remaining in the payment window. Null until a payment is opened. */
63
+ timeLeft: number | null;
64
+ error: string | null;
65
+ /** Start a new checkout session — opens the modal */
66
+ open: (params: OpenParams) => Promise<void>;
67
+ /** Close the modal and reset all state */
68
+ close: () => void;
69
+ }
70
+
71
+ export function useForgeLayerCheckout(options?: UseForgeLayerCheckoutOptions): UseForgeLayerCheckoutResult;
72
+
73
+ // ── ForgeLayerModal ───────────────────────────────────────────────────────────
74
+
75
+ export interface ForgeLayerModalProps {
76
+ modalState: ForgeLayerModalState;
77
+ order: ForgeLayerOrder | null;
78
+ timeLeft: number | null;
79
+ error: string | null;
80
+ onClose: () => void;
81
+ }
82
+
83
+ export function ForgeLayerModal(props: ForgeLayerModalProps): React.ReactElement | null;
84
+
85
+ // ── ForgeLayerButton ──────────────────────────────────────────────────────────
86
+
87
+ export interface ForgeLayerButtonProps {
88
+ // Payment params
89
+ amount: number;
90
+ currency?: string;
91
+ chain?: string;
92
+ token?: string;
93
+ orderId?: string;
94
+ /** Minutes before the payment window expires. Default: 30 */
95
+ paymentWindow?: number;
96
+ /** Return same deposit address for same orderId if still pending */
97
+ reuseAddress?: boolean;
98
+
99
+ // Backend
100
+ /** Base path where forgelayer-node is mounted. Default: '/fl' */
101
+ baseUrl?: string;
102
+ /** Status polling interval in milliseconds. Default: 15000 */
103
+ pollInterval?: number;
104
+
105
+ // Button UI
106
+ /** Button text. Overridden by children if provided. */
107
+ label?: string;
108
+ children?: React.ReactNode;
109
+ className?: string;
110
+ style?: React.CSSProperties;
111
+ disabled?: boolean;
112
+
113
+ // Callbacks
114
+ onSuccess?: (order: ForgeLayerOrder) => void;
115
+ onExpired?: () => void;
116
+ onError?: (error: Error) => void;
117
+ onOpen?: () => void;
118
+ onClose?: () => void;
119
+ }
120
+
121
+ export function ForgeLayerButton(props: ForgeLayerButtonProps): React.ReactElement;
@@ -0,0 +1,446 @@
1
+ // src/ForgeLayerButton.jsx
2
+ import React2 from "react";
3
+
4
+ // src/useForgeLayerCheckout.js
5
+ import { useState, useRef, useCallback, useEffect } from "react";
6
+ function useForgeLayerCheckout({
7
+ baseUrl = "/fl",
8
+ pollInterval = 15e3,
9
+ onSuccess,
10
+ onExpired,
11
+ onError,
12
+ onOpen,
13
+ onClose: onCloseProp
14
+ } = {}) {
15
+ const [modalState, setModalState] = useState("closed");
16
+ const [order, setOrder] = useState(null);
17
+ const [timeLeft, setTimeLeft] = useState(null);
18
+ const [error, setError] = useState(null);
19
+ const pollRef = useRef(null);
20
+ const cdRef = useRef(null);
21
+ const stopTimers = useCallback(() => {
22
+ if (pollRef.current) {
23
+ clearInterval(pollRef.current);
24
+ pollRef.current = null;
25
+ }
26
+ if (cdRef.current) {
27
+ clearInterval(cdRef.current);
28
+ cdRef.current = null;
29
+ }
30
+ }, []);
31
+ useEffect(() => stopTimers, [stopTimers]);
32
+ const close = useCallback(() => {
33
+ stopTimers();
34
+ setModalState("closed");
35
+ setOrder(null);
36
+ setTimeLeft(null);
37
+ setError(null);
38
+ onCloseProp?.();
39
+ }, [stopTimers, onCloseProp]);
40
+ const startCountdown = useCallback((expiresAt) => {
41
+ if (cdRef.current) clearInterval(cdRef.current);
42
+ const tick = () => {
43
+ const rem = expiresAt - Math.floor(Date.now() / 1e3);
44
+ setTimeLeft(rem <= 0 ? 0 : rem);
45
+ if (rem <= 0) clearInterval(cdRef.current);
46
+ };
47
+ tick();
48
+ cdRef.current = setInterval(tick, 1e3);
49
+ }, []);
50
+ const startPolling = useCallback((orderData) => {
51
+ if (pollRef.current) clearInterval(pollRef.current);
52
+ pollRef.current = setInterval(async () => {
53
+ try {
54
+ const res = await fetch(`${baseUrl}/status?session=${encodeURIComponent(orderData.sessionKey)}`);
55
+ const data = await res.json();
56
+ if (!data.ok) return;
57
+ if (data.status === "confirmed") {
58
+ stopTimers();
59
+ setModalState("success");
60
+ onSuccess?.(orderData);
61
+ } else if (data.status === "expired") {
62
+ stopTimers();
63
+ setModalState("expired");
64
+ onExpired?.();
65
+ }
66
+ } catch (_) {
67
+ }
68
+ }, pollInterval);
69
+ }, [baseUrl, pollInterval, stopTimers, onSuccess, onExpired]);
70
+ const open = useCallback(async (params) => {
71
+ stopTimers();
72
+ setModalState("loading");
73
+ setOrder(null);
74
+ setTimeLeft(null);
75
+ setError(null);
76
+ onOpen?.();
77
+ try {
78
+ const res = await fetch(`${baseUrl}/create`, {
79
+ method: "POST",
80
+ headers: { "Content-Type": "application/json" },
81
+ body: JSON.stringify(params)
82
+ });
83
+ const data = await res.json();
84
+ if (!data.ok) throw new Error(data.error || "Failed to generate payment address.");
85
+ setOrder(data);
86
+ setModalState("payment");
87
+ startCountdown(data.expiresAt);
88
+ startPolling(data);
89
+ } catch (e) {
90
+ setError(e.message);
91
+ setModalState("error");
92
+ onError?.(e);
93
+ }
94
+ }, [baseUrl, stopTimers, startCountdown, startPolling, onOpen, onError]);
95
+ return { modalState, order, timeLeft, error, open, close };
96
+ }
97
+
98
+ // src/ForgeLayerModal.jsx
99
+ import React, { useEffect as useEffect2, useState as useState2 } from "react";
100
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
101
+ var MODAL_CSS = `
102
+ @keyframes fl-r-fade{from{opacity:0}to{opacity:1}}
103
+ @keyframes fl-r-up{from{transform:translateY(16px);opacity:0}to{transform:translateY(0);opacity:1}}
104
+ @keyframes fl-r-spin{to{transform:rotate(360deg)}}
105
+ @keyframes fl-r-pulse{0%,100%{opacity:1}50%{opacity:.35}}
106
+ .fl-r-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99999;
107
+ display:flex;align-items:center;justify-content:center;padding:16px;
108
+ animation:fl-r-fade .18s ease}
109
+ .fl-r-modal{background:#fff;border-radius:16px;width:100%;max-width:520px;
110
+ box-shadow:0 24px 64px rgba(0,0,0,.28);overflow:hidden;
111
+ animation:fl-r-up .22s ease;
112
+ font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;
113
+ font-size:14px;color:#111}
114
+ .fl-r-spinner{width:38px;height:38px;border:3px solid #e5e7eb;
115
+ border-top-color:#f7931a;border-radius:50%;
116
+ animation:fl-r-spin .7s linear infinite;margin:0 auto 14px}
117
+ .fl-r-dot-pending{background:#f59e0b;animation:fl-r-pulse 1.6s ease-in-out infinite}
118
+ .fl-r-dot-confirmed{background:#10b981}
119
+ .fl-r-dot-expired{background:#ef4444}
120
+ .fl-r-xbtn:hover{background:#f3f4f6 !important;color:#111 !important}
121
+ .fl-r-cpbtn:hover{background:#f3f4f6 !important}
122
+ .fl-r-cpbtn-copied{border-color:#10b981 !important;color:#059669 !important}
123
+ @media(max-width:460px){.fl-r-grid{flex-direction:column !important;align-items:center !important}}
124
+ `;
125
+ var _cssInjected = false;
126
+ function ensureCSS() {
127
+ if (_cssInjected || typeof document === "undefined") return;
128
+ _cssInjected = true;
129
+ const el = document.createElement("style");
130
+ el.textContent = MODAL_CSS;
131
+ document.head.appendChild(el);
132
+ }
133
+ function fmtTime(secs) {
134
+ const m = Math.floor(secs / 60);
135
+ const s = secs % 60;
136
+ return `${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
137
+ }
138
+ function ForgeLayerModal({ modalState, order, timeLeft, error, onClose }) {
139
+ const [copied, setCopied] = useState2(false);
140
+ useEffect2(() => {
141
+ ensureCSS();
142
+ }, []);
143
+ useEffect2(() => {
144
+ const handler = (e) => {
145
+ if (e.key === "Escape") onClose();
146
+ };
147
+ document.addEventListener("keydown", handler);
148
+ return () => document.removeEventListener("keydown", handler);
149
+ }, [onClose]);
150
+ useEffect2(() => {
151
+ const prev = document.body.style.overflow;
152
+ document.body.style.overflow = "hidden";
153
+ return () => {
154
+ document.body.style.overflow = prev;
155
+ };
156
+ }, []);
157
+ const copyAddress = () => {
158
+ if (!order?.address) return;
159
+ navigator.clipboard?.writeText(order.address).catch(() => {
160
+ });
161
+ setCopied(true);
162
+ setTimeout(() => setCopied(false), 2e3);
163
+ };
164
+ const urgent = timeLeft !== null && timeLeft <= 120;
165
+ return /* @__PURE__ */ jsx(
166
+ "div",
167
+ {
168
+ className: "fl-r-backdrop",
169
+ role: "dialog",
170
+ "aria-modal": "true",
171
+ "aria-label": "Crypto payment",
172
+ onClick: (e) => {
173
+ if (e.target === e.currentTarget) onClose();
174
+ },
175
+ children: /* @__PURE__ */ jsxs("div", { className: "fl-r-modal", children: [
176
+ /* @__PURE__ */ jsxs("div", { style: {
177
+ display: "flex",
178
+ alignItems: "center",
179
+ justifyContent: "space-between",
180
+ padding: "16px 18px",
181
+ borderBottom: "1px solid #e5e7eb",
182
+ background: "#fafafa"
183
+ }, children: [
184
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 9, fontSize: 15, fontWeight: 700 }, children: [
185
+ /* @__PURE__ */ jsx("div", { style: {
186
+ width: 26,
187
+ height: 26,
188
+ background: "#f7931a",
189
+ borderRadius: 6,
190
+ display: "flex",
191
+ alignItems: "center",
192
+ justifyContent: "center",
193
+ color: "#fff",
194
+ fontSize: 10,
195
+ fontWeight: 800,
196
+ flexShrink: 0
197
+ }, children: "FL" }),
198
+ "Pay with Crypto"
199
+ ] }),
200
+ /* @__PURE__ */ jsx(
201
+ "button",
202
+ {
203
+ className: "fl-r-xbtn",
204
+ onClick: onClose,
205
+ "aria-label": "Close",
206
+ style: {
207
+ background: "none",
208
+ border: "none",
209
+ fontSize: 22,
210
+ lineHeight: 1,
211
+ cursor: "pointer",
212
+ color: "#6b7280",
213
+ padding: "4px 8px",
214
+ borderRadius: 5,
215
+ transition: "background .12s"
216
+ },
217
+ children: "\xD7"
218
+ }
219
+ )
220
+ ] }),
221
+ /* @__PURE__ */ jsxs("div", { style: { padding: "20px 18px" }, children: [
222
+ modalState === "loading" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "28px 0" }, children: [
223
+ /* @__PURE__ */ jsx("div", { className: "fl-r-spinner" }),
224
+ /* @__PURE__ */ jsx("p", { style: { color: "#6b7280", fontSize: 13, margin: 0 }, children: "Generating payment address\u2026" })
225
+ ] }),
226
+ modalState === "error" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "28px 0" }, children: [
227
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 48, marginBottom: 12 }, children: "\u26A0\uFE0F" }),
228
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 15, fontWeight: 700, marginBottom: 8 }, children: "Something went wrong" }),
229
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: "#6b7280" }, children: error })
230
+ ] }),
231
+ modalState === "payment" && order && /* @__PURE__ */ jsxs(Fragment, { children: [
232
+ /* @__PURE__ */ jsxs("div", { style: {
233
+ display: "flex",
234
+ alignItems: "center",
235
+ gap: 9,
236
+ padding: "9px 13px",
237
+ borderRadius: 8,
238
+ background: "#f9fafb",
239
+ border: "1px solid #e5e7eb",
240
+ marginBottom: 14,
241
+ fontSize: 13
242
+ }, children: [
243
+ /* @__PURE__ */ jsx(
244
+ "span",
245
+ {
246
+ className: "fl-r-dot-pending",
247
+ style: { width: 8, height: 8, borderRadius: "50%", flexShrink: 0 }
248
+ }
249
+ ),
250
+ /* @__PURE__ */ jsx("span", { children: "Awaiting payment\u2026" }),
251
+ /* @__PURE__ */ jsx("span", { style: {
252
+ marginLeft: "auto",
253
+ fontWeight: 600,
254
+ fontSize: 13,
255
+ color: urgent ? "#ef4444" : "#374151",
256
+ fontVariantNumeric: "tabular-nums"
257
+ }, children: timeLeft !== null ? fmtTime(timeLeft) : "--:--" })
258
+ ] }),
259
+ /* @__PURE__ */ jsxs("div", { style: {
260
+ background: "#fffbeb",
261
+ border: "1px solid #fde68a",
262
+ borderRadius: 8,
263
+ padding: "9px 13px",
264
+ fontSize: 12,
265
+ color: "#92400e",
266
+ marginBottom: 14,
267
+ lineHeight: 1.5
268
+ }, children: [
269
+ /* @__PURE__ */ jsx("strong", { children: "\u26A0 Important:" }),
270
+ " Send only ",
271
+ /* @__PURE__ */ jsx("strong", { children: order.token }),
272
+ " on the",
273
+ " ",
274
+ /* @__PURE__ */ jsx("strong", { children: order.chainName }),
275
+ " network only. Wrong network = permanent loss."
276
+ ] }),
277
+ /* @__PURE__ */ jsxs("div", { className: "fl-r-grid", style: { display: "flex", gap: 18, marginBottom: 14 }, children: [
278
+ /* @__PURE__ */ jsxs("div", { style: { flexShrink: 0, display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }, children: [
279
+ /* @__PURE__ */ jsx(
280
+ "img",
281
+ {
282
+ src: order.qrUrl,
283
+ alt: `Send to ${order.address}`,
284
+ width: 148,
285
+ height: 148,
286
+ style: { border: "1px solid #e5e7eb", borderRadius: 10, display: "block", background: "#f9fafb" }
287
+ }
288
+ ),
289
+ /* @__PURE__ */ jsx("p", { style: { fontSize: 11, color: "#9ca3af", margin: 0 }, children: "Scan with wallet" })
290
+ ] }),
291
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 13 }, children: [
292
+ /* @__PURE__ */ jsxs("div", { children: [
293
+ /* @__PURE__ */ jsx("label", { style: { display: "block", fontSize: 10, fontWeight: 700, color: "#6b7280", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 4 }, children: "Amount to Send" }),
294
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 22, fontWeight: 700, lineHeight: 1.2 }, children: order.cryptoAmount ? `${parseFloat(order.cryptoAmount).toFixed(8).replace(/\.?0+$/, "")} ${order.token}` : `${order.currency} ${parseFloat(order.amount).toFixed(2)}` }),
295
+ order.cryptoAmount && /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: "#6b7280", marginTop: 2 }, children: [
296
+ "\u2248 ",
297
+ order.currency,
298
+ " ",
299
+ parseFloat(order.amount).toFixed(2)
300
+ ] })
301
+ ] }),
302
+ /* @__PURE__ */ jsxs("div", { children: [
303
+ /* @__PURE__ */ jsx("label", { style: { display: "block", fontSize: 10, fontWeight: 700, color: "#6b7280", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 4 }, children: "Deposit Address" }),
304
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "flex-start", gap: 7 }, children: [
305
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: "monospace", fontSize: 12, color: "#374151", wordBreak: "break-all", flex: 1, lineHeight: 1.5 }, children: order.address }),
306
+ /* @__PURE__ */ jsx(
307
+ "button",
308
+ {
309
+ className: `fl-r-cpbtn${copied ? " fl-r-cpbtn-copied" : ""}`,
310
+ onClick: copyAddress,
311
+ style: {
312
+ flexShrink: 0,
313
+ background: "#fff",
314
+ border: "1px solid #d1d5db",
315
+ borderRadius: 6,
316
+ padding: "5px 11px",
317
+ fontSize: 12,
318
+ cursor: "pointer",
319
+ color: "#374151",
320
+ transition: "background .12s",
321
+ whiteSpace: "nowrap"
322
+ },
323
+ children: copied ? "Copied!" : "Copy"
324
+ }
325
+ )
326
+ ] })
327
+ ] }),
328
+ /* @__PURE__ */ jsxs("div", { children: [
329
+ /* @__PURE__ */ jsx("label", { style: { display: "block", fontSize: 10, fontWeight: 700, color: "#6b7280", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 4 }, children: "Network" }),
330
+ /* @__PURE__ */ jsxs("span", { style: { display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 10px", background: "#f3f4f6", borderRadius: 100, fontSize: 12, fontWeight: 500, color: "#374151" }, children: [
331
+ /* @__PURE__ */ jsx("span", { style: { width: 7, height: 7, borderRadius: "50%", background: "#10b981", flexShrink: 0 } }),
332
+ order.chainName,
333
+ " \xB7 ",
334
+ order.token
335
+ ] })
336
+ ] })
337
+ ] })
338
+ ] })
339
+ ] }),
340
+ modalState === "success" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "30px 16px" }, children: [
341
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 52, marginBottom: 14 }, children: "\u2705" }),
342
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 19, fontWeight: 700, color: "#111", marginBottom: 7 }, children: "Payment Confirmed!" }),
343
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: "#6b7280" }, children: "Your payment has been received." })
344
+ ] }),
345
+ modalState === "expired" && /* @__PURE__ */ jsxs("div", { style: { textAlign: "center", padding: "30px 16px" }, children: [
346
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 52, marginBottom: 14 }, children: "\u23F3" }),
347
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 19, fontWeight: 700, color: "#111", marginBottom: 7 }, children: "Payment Expired" }),
348
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: "#6b7280" }, children: "The payment window has closed. Please start a new payment." })
349
+ ] })
350
+ ] }),
351
+ /* @__PURE__ */ jsxs("div", { style: { padding: "10px 18px 14px", textAlign: "center", fontSize: 11, color: "#9ca3af", borderTop: "1px solid #f3f4f6" }, children: [
352
+ "Secured by",
353
+ " ",
354
+ /* @__PURE__ */ jsx("a", { href: "https://forgelayer.io", target: "_blank", rel: "noopener noreferrer", style: { color: "#f7931a", textDecoration: "none" }, children: "ForgeLayer" })
355
+ ] })
356
+ ] })
357
+ }
358
+ );
359
+ }
360
+
361
+ // src/ForgeLayerButton.jsx
362
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
363
+ function ForgeLayerButton({
364
+ // Payment params
365
+ amount,
366
+ currency = "USD",
367
+ chain = "ethereum",
368
+ token = "USDT",
369
+ orderId,
370
+ paymentWindow,
371
+ reuseAddress,
372
+ // Backend base path (proxied or absolute)
373
+ baseUrl = "/fl",
374
+ pollInterval,
375
+ // Button UI
376
+ label,
377
+ children,
378
+ className,
379
+ style,
380
+ disabled,
381
+ // Callbacks
382
+ onSuccess,
383
+ onExpired,
384
+ onError,
385
+ onOpen,
386
+ onClose
387
+ }) {
388
+ const { modalState, order, timeLeft, error, open, close } = useForgeLayerCheckout({
389
+ baseUrl,
390
+ pollInterval,
391
+ onSuccess,
392
+ onExpired,
393
+ onError,
394
+ onOpen,
395
+ onClose
396
+ });
397
+ const isOpen = modalState !== "closed";
398
+ const handleClick = () => {
399
+ open({ amount, currency, chain, token, orderId, paymentWindow, reuseAddress });
400
+ };
401
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
402
+ /* @__PURE__ */ jsx2(
403
+ "button",
404
+ {
405
+ onClick: handleClick,
406
+ disabled: disabled || isOpen,
407
+ className,
408
+ style: {
409
+ display: "inline-flex",
410
+ alignItems: "center",
411
+ gap: 8,
412
+ padding: "12px 24px",
413
+ background: "#f7931a",
414
+ color: "#fff",
415
+ border: "none",
416
+ borderRadius: 8,
417
+ fontSize: 15,
418
+ fontWeight: 600,
419
+ cursor: disabled || isOpen ? "not-allowed" : "pointer",
420
+ opacity: disabled || isOpen ? 0.65 : 1,
421
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
422
+ lineHeight: 1.2,
423
+ transition: "opacity .15s",
424
+ ...style
425
+ },
426
+ children: label ?? children ?? "Pay with Crypto"
427
+ }
428
+ ),
429
+ isOpen && /* @__PURE__ */ jsx2(
430
+ ForgeLayerModal,
431
+ {
432
+ modalState,
433
+ order,
434
+ timeLeft,
435
+ error,
436
+ onClose: close
437
+ }
438
+ )
439
+ ] });
440
+ }
441
+ export {
442
+ ForgeLayerButton,
443
+ ForgeLayerModal,
444
+ useForgeLayerCheckout
445
+ };
446
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/ForgeLayerButton.jsx", "../src/useForgeLayerCheckout.js", "../src/ForgeLayerModal.jsx"],
4
+ "sourcesContent": ["import React from 'react';\nimport { useForgeLayerCheckout } from './useForgeLayerCheckout.js';\nimport { ForgeLayerModal } from './ForgeLayerModal.jsx';\n\n/**\n * Drop-in React checkout button.\n *\n * Usage:\n * <ForgeLayerButton\n * amount={49.99}\n * currency=\"USD\"\n * chain=\"ethereum\"\n * token=\"USDT\"\n * orderId=\"ORDER-123\"\n * onSuccess={(order) => router.push('/thank-you')}\n * >\n * Pay $49.99\n * </ForgeLayerButton>\n */\nexport function ForgeLayerButton({\n // Payment params\n amount,\n currency = 'USD',\n chain = 'ethereum',\n token = 'USDT',\n orderId,\n paymentWindow,\n reuseAddress,\n // Backend base path (proxied or absolute)\n baseUrl = '/fl',\n pollInterval,\n // Button UI\n label,\n children,\n className,\n style,\n disabled,\n // Callbacks\n onSuccess,\n onExpired,\n onError,\n onOpen,\n onClose,\n}) {\n const { modalState, order, timeLeft, error, open, close } = useForgeLayerCheckout({\n baseUrl,\n pollInterval,\n onSuccess,\n onExpired,\n onError,\n onOpen,\n onClose,\n });\n\n const isOpen = modalState !== 'closed';\n\n const handleClick = () => {\n open({ amount, currency, chain, token, orderId, paymentWindow, reuseAddress });\n };\n\n return (\n <>\n <button\n onClick={handleClick}\n disabled={disabled || isOpen}\n className={className}\n style={{\n display: 'inline-flex',\n alignItems: 'center',\n gap: 8,\n padding: '12px 24px',\n background: '#f7931a',\n color: '#fff',\n border: 'none',\n borderRadius: 8,\n fontSize: 15,\n fontWeight: 600,\n cursor: (disabled || isOpen) ? 'not-allowed' : 'pointer',\n opacity: (disabled || isOpen) ? 0.65 : 1,\n fontFamily: '-apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif',\n lineHeight: 1.2,\n transition: 'opacity .15s',\n ...style,\n }}\n >\n {label ?? children ?? 'Pay with Crypto'}\n </button>\n\n {isOpen && (\n <ForgeLayerModal\n modalState={modalState}\n order={order}\n timeLeft={timeLeft}\n error={error}\n onClose={close}\n />\n )}\n </>\n );\n}\n", "'use strict';\nimport { useState, useRef, useCallback, useEffect } from 'react';\n\n/**\n * Core hook \u2014 handles all state and API calls for the checkout flow.\n *\n * modalState values:\n * 'closed' | 'loading' | 'payment' | 'success' | 'expired' | 'error'\n */\nexport function useForgeLayerCheckout({\n baseUrl = '/fl',\n pollInterval = 15_000,\n onSuccess,\n onExpired,\n onError,\n onOpen,\n onClose: onCloseProp,\n} = {}) {\n const [modalState, setModalState] = useState('closed');\n const [order, setOrder] = useState(null);\n const [timeLeft, setTimeLeft] = useState(null);\n const [error, setError] = useState(null);\n\n const pollRef = useRef(null);\n const cdRef = useRef(null);\n\n const stopTimers = useCallback(() => {\n if (pollRef.current) { clearInterval(pollRef.current); pollRef.current = null; }\n if (cdRef.current) { clearInterval(cdRef.current); cdRef.current = null; }\n }, []);\n\n // Cleanup on unmount\n useEffect(() => stopTimers, [stopTimers]);\n\n const close = useCallback(() => {\n stopTimers();\n setModalState('closed');\n setOrder(null);\n setTimeLeft(null);\n setError(null);\n onCloseProp?.();\n }, [stopTimers, onCloseProp]);\n\n const startCountdown = useCallback((expiresAt) => {\n if (cdRef.current) clearInterval(cdRef.current);\n const tick = () => {\n const rem = expiresAt - Math.floor(Date.now() / 1000);\n setTimeLeft(rem <= 0 ? 0 : rem);\n if (rem <= 0) clearInterval(cdRef.current);\n };\n tick();\n cdRef.current = setInterval(tick, 1000);\n }, []);\n\n const startPolling = useCallback((orderData) => {\n if (pollRef.current) clearInterval(pollRef.current);\n pollRef.current = setInterval(async () => {\n try {\n const res = await fetch(`${baseUrl}/status?session=${encodeURIComponent(orderData.sessionKey)}`);\n const data = await res.json();\n if (!data.ok) return;\n if (data.status === 'confirmed') {\n stopTimers();\n setModalState('success');\n onSuccess?.(orderData);\n } else if (data.status === 'expired') {\n stopTimers();\n setModalState('expired');\n onExpired?.();\n }\n } catch (_) {}\n }, pollInterval);\n }, [baseUrl, pollInterval, stopTimers, onSuccess, onExpired]);\n\n const open = useCallback(async (params) => {\n stopTimers();\n setModalState('loading');\n setOrder(null);\n setTimeLeft(null);\n setError(null);\n onOpen?.();\n\n try {\n const res = await fetch(`${baseUrl}/create`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(params),\n });\n const data = await res.json();\n if (!data.ok) throw new Error(data.error || 'Failed to generate payment address.');\n setOrder(data);\n setModalState('payment');\n startCountdown(data.expiresAt);\n startPolling(data);\n } catch (e) {\n setError(e.message);\n setModalState('error');\n onError?.(e);\n }\n }, [baseUrl, stopTimers, startCountdown, startPolling, onOpen, onError]);\n\n return { modalState, order, timeLeft, error, open, close };\n}\n", "import React, { useEffect, useState } from 'react';\n\n// Injected once \u2014 handles animations, hover, and pseudo-elements\n// that can't be done with inline styles.\nconst MODAL_CSS = `\n@keyframes fl-r-fade{from{opacity:0}to{opacity:1}}\n@keyframes fl-r-up{from{transform:translateY(16px);opacity:0}to{transform:translateY(0);opacity:1}}\n@keyframes fl-r-spin{to{transform:rotate(360deg)}}\n@keyframes fl-r-pulse{0%,100%{opacity:1}50%{opacity:.35}}\n.fl-r-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:99999;\n display:flex;align-items:center;justify-content:center;padding:16px;\n animation:fl-r-fade .18s ease}\n.fl-r-modal{background:#fff;border-radius:16px;width:100%;max-width:520px;\n box-shadow:0 24px 64px rgba(0,0,0,.28);overflow:hidden;\n animation:fl-r-up .22s ease;\n font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;\n font-size:14px;color:#111}\n.fl-r-spinner{width:38px;height:38px;border:3px solid #e5e7eb;\n border-top-color:#f7931a;border-radius:50%;\n animation:fl-r-spin .7s linear infinite;margin:0 auto 14px}\n.fl-r-dot-pending{background:#f59e0b;animation:fl-r-pulse 1.6s ease-in-out infinite}\n.fl-r-dot-confirmed{background:#10b981}\n.fl-r-dot-expired{background:#ef4444}\n.fl-r-xbtn:hover{background:#f3f4f6 !important;color:#111 !important}\n.fl-r-cpbtn:hover{background:#f3f4f6 !important}\n.fl-r-cpbtn-copied{border-color:#10b981 !important;color:#059669 !important}\n@media(max-width:460px){.fl-r-grid{flex-direction:column !important;align-items:center !important}}\n`;\n\nlet _cssInjected = false;\nfunction ensureCSS() {\n if (_cssInjected || typeof document === 'undefined') return;\n _cssInjected = true;\n const el = document.createElement('style');\n el.textContent = MODAL_CSS;\n document.head.appendChild(el);\n}\n\nfunction fmtTime(secs) {\n const m = Math.floor(secs / 60);\n const s = secs % 60;\n return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;\n}\n\nexport function ForgeLayerModal({ modalState, order, timeLeft, error, onClose }) {\n const [copied, setCopied] = useState(false);\n\n useEffect(() => { ensureCSS(); }, []);\n\n // Escape key\n useEffect(() => {\n const handler = (e) => { if (e.key === 'Escape') onClose(); };\n document.addEventListener('keydown', handler);\n return () => document.removeEventListener('keydown', handler);\n }, [onClose]);\n\n // Lock body scroll\n useEffect(() => {\n const prev = document.body.style.overflow;\n document.body.style.overflow = 'hidden';\n return () => { document.body.style.overflow = prev; };\n }, []);\n\n const copyAddress = () => {\n if (!order?.address) return;\n navigator.clipboard?.writeText(order.address).catch(() => {});\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const urgent = timeLeft !== null && timeLeft <= 120;\n\n return (\n <div\n className=\"fl-r-backdrop\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Crypto payment\"\n onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}\n >\n <div className=\"fl-r-modal\">\n\n {/* \u2500\u2500 Header \u2500\u2500 */}\n <div style={{\n display: 'flex', alignItems: 'center', justifyContent: 'space-between',\n padding: '16px 18px', borderBottom: '1px solid #e5e7eb', background: '#fafafa',\n }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 9, fontSize: 15, fontWeight: 700 }}>\n <div style={{\n width: 26, height: 26, background: '#f7931a', borderRadius: 6,\n display: 'flex', alignItems: 'center', justifyContent: 'center',\n color: '#fff', fontSize: 10, fontWeight: 800, flexShrink: 0,\n }}>FL</div>\n Pay with Crypto\n </div>\n <button\n className=\"fl-r-xbtn\"\n onClick={onClose}\n aria-label=\"Close\"\n style={{\n background: 'none', border: 'none', fontSize: 22, lineHeight: 1,\n cursor: 'pointer', color: '#6b7280', padding: '4px 8px',\n borderRadius: 5, transition: 'background .12s',\n }}\n >\u00D7</button>\n </div>\n\n {/* \u2500\u2500 Body \u2500\u2500 */}\n <div style={{ padding: '20px 18px' }}>\n\n {/* Loading */}\n {modalState === 'loading' && (\n <div style={{ textAlign: 'center', padding: '28px 0' }}>\n <div className=\"fl-r-spinner\" />\n <p style={{ color: '#6b7280', fontSize: 13, margin: 0 }}>\n Generating payment address\u2026\n </p>\n </div>\n )}\n\n {/* Error */}\n {modalState === 'error' && (\n <div style={{ textAlign: 'center', padding: '28px 0' }}>\n <div style={{ fontSize: 48, marginBottom: 12 }}>\u26A0\uFE0F</div>\n <div style={{ fontSize: 15, fontWeight: 700, marginBottom: 8 }}>Something went wrong</div>\n <div style={{ fontSize: 13, color: '#6b7280' }}>{error}</div>\n </div>\n )}\n\n {/* Payment */}\n {modalState === 'payment' && order && (\n <>\n {/* Status bar */}\n <div style={{\n display: 'flex', alignItems: 'center', gap: 9,\n padding: '9px 13px', borderRadius: 8,\n background: '#f9fafb', border: '1px solid #e5e7eb',\n marginBottom: 14, fontSize: 13,\n }}>\n <span\n className=\"fl-r-dot-pending\"\n style={{ width: 8, height: 8, borderRadius: '50%', flexShrink: 0 }}\n />\n <span>Awaiting payment\u2026</span>\n <span style={{\n marginLeft: 'auto', fontWeight: 600, fontSize: 13,\n color: urgent ? '#ef4444' : '#374151',\n fontVariantNumeric: 'tabular-nums',\n }}>\n {timeLeft !== null ? fmtTime(timeLeft) : '--:--'}\n </span>\n </div>\n\n {/* Network warning */}\n <div style={{\n background: '#fffbeb', border: '1px solid #fde68a', borderRadius: 8,\n padding: '9px 13px', fontSize: 12, color: '#92400e',\n marginBottom: 14, lineHeight: 1.5,\n }}>\n <strong>\u26A0 Important:</strong> Send only <strong>{order.token}</strong> on the{' '}\n <strong>{order.chainName}</strong> network only. Wrong network = permanent loss.\n </div>\n\n {/* QR + info grid */}\n <div className=\"fl-r-grid\" style={{ display: 'flex', gap: 18, marginBottom: 14 }}>\n {/* QR side */}\n <div style={{ flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>\n <img\n src={order.qrUrl}\n alt={`Send to ${order.address}`}\n width={148}\n height={148}\n style={{ border: '1px solid #e5e7eb', borderRadius: 10, display: 'block', background: '#f9fafb' }}\n />\n <p style={{ fontSize: 11, color: '#9ca3af', margin: 0 }}>Scan with wallet</p>\n </div>\n\n {/* Info side */}\n <div style={{ flex: 1, minWidth: 0, display: 'flex', flexDirection: 'column', gap: 13 }}>\n {/* Amount */}\n <div>\n <label style={{ display: 'block', fontSize: 10, fontWeight: 700, color: '#6b7280', textTransform: 'uppercase', letterSpacing: '.06em', marginBottom: 4 }}>\n Amount to Send\n </label>\n <div style={{ fontSize: 22, fontWeight: 700, lineHeight: 1.2 }}>\n {order.cryptoAmount\n ? `${parseFloat(order.cryptoAmount).toFixed(8).replace(/\\.?0+$/, '')} ${order.token}`\n : `${order.currency} ${parseFloat(order.amount).toFixed(2)}`}\n </div>\n {order.cryptoAmount && (\n <div style={{ fontSize: 12, color: '#6b7280', marginTop: 2 }}>\n \u2248 {order.currency} {parseFloat(order.amount).toFixed(2)}\n </div>\n )}\n </div>\n\n {/* Address */}\n <div>\n <label style={{ display: 'block', fontSize: 10, fontWeight: 700, color: '#6b7280', textTransform: 'uppercase', letterSpacing: '.06em', marginBottom: 4 }}>\n Deposit Address\n </label>\n <div style={{ display: 'flex', alignItems: 'flex-start', gap: 7 }}>\n <span style={{ fontFamily: 'monospace', fontSize: 12, color: '#374151', wordBreak: 'break-all', flex: 1, lineHeight: 1.5 }}>\n {order.address}\n </span>\n <button\n className={`fl-r-cpbtn${copied ? ' fl-r-cpbtn-copied' : ''}`}\n onClick={copyAddress}\n style={{\n flexShrink: 0, background: '#fff', border: '1px solid #d1d5db',\n borderRadius: 6, padding: '5px 11px', fontSize: 12,\n cursor: 'pointer', color: '#374151',\n transition: 'background .12s', whiteSpace: 'nowrap',\n }}\n >\n {copied ? 'Copied!' : 'Copy'}\n </button>\n </div>\n </div>\n\n {/* Network badge */}\n <div>\n <label style={{ display: 'block', fontSize: 10, fontWeight: 700, color: '#6b7280', textTransform: 'uppercase', letterSpacing: '.06em', marginBottom: 4 }}>\n Network\n </label>\n <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, padding: '3px 10px', background: '#f3f4f6', borderRadius: 100, fontSize: 12, fontWeight: 500, color: '#374151' }}>\n <span style={{ width: 7, height: 7, borderRadius: '50%', background: '#10b981', flexShrink: 0 }} />\n {order.chainName} \u00B7 {order.token}\n </span>\n </div>\n </div>\n </div>\n </>\n )}\n\n {/* Success */}\n {modalState === 'success' && (\n <div style={{ textAlign: 'center', padding: '30px 16px' }}>\n <div style={{ fontSize: 52, marginBottom: 14 }}>\u2705</div>\n <div style={{ fontSize: 19, fontWeight: 700, color: '#111', marginBottom: 7 }}>Payment Confirmed!</div>\n <div style={{ fontSize: 13, color: '#6b7280' }}>Your payment has been received.</div>\n </div>\n )}\n\n {/* Expired */}\n {modalState === 'expired' && (\n <div style={{ textAlign: 'center', padding: '30px 16px' }}>\n <div style={{ fontSize: 52, marginBottom: 14 }}>\u23F3</div>\n <div style={{ fontSize: 19, fontWeight: 700, color: '#111', marginBottom: 7 }}>Payment Expired</div>\n <div style={{ fontSize: 13, color: '#6b7280' }}>\n The payment window has closed. Please start a new payment.\n </div>\n </div>\n )}\n </div>\n\n {/* \u2500\u2500 Footer \u2500\u2500 */}\n <div style={{ padding: '10px 18px 14px', textAlign: 'center', fontSize: 11, color: '#9ca3af', borderTop: '1px solid #f3f4f6' }}>\n Secured by{' '}\n <a href=\"https://forgelayer.io\" target=\"_blank\" rel=\"noopener noreferrer\" style={{ color: '#f7931a', textDecoration: 'none' }}>\n ForgeLayer\n </a>\n </div>\n </div>\n </div>\n );\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["React", "useEffect", "useState", "Fragment", "jsx", "jsxs"]
7
+ }