UNPKG

@cedros/pay-react

Version:

React frontend library for Cedros Pay - unified Stripe and Solana x402 payments

1,638 lines 58.6 kB
import { jsxs as w, jsx as u, Fragment as me } from "react/jsx-runtime"; import G, { useState as U, useCallback as T, useMemo as F, useRef as ke, useEffect as Y } from "react"; import { ConnectionProvider as De, WalletProvider as qe, useWallet as ye } from "@solana/wallet-adapter-react"; import { u as $e, c as Re, o as ge, q as Ie, e as he, g as ie, h as be, i as ce, r as Se, P as ze, S as Ue, m as je, s as Oe, t as Te, v as fe, w as Fe } from "./styles-DFcRS8Uu.mjs"; import { C as Bt, E as Nt, b as Mt, a as _t, d as At, B as Wt, y as Lt, f as Dt, A as qt, D as $t, l as zt, k as Ut, z as jt, p as Ot, j as Ft, x as Ht, F as Kt, n as Qt } from "./styles-DFcRS8Uu.mjs"; import { a as le, u as ee, g as D, f as Z } from "./CedrosContext-CFEXGwQg.mjs"; import { k as Yt, C as Vt, j as Xt, i as Jt, L as Zt, b as er, R as tr, l as rr, W as nr, h as sr, d as or, c as ar, m as ir, r as cr, v as lr } from "./CedrosContext-CFEXGwQg.mjs"; import { clusterApiUrl as He } from "@solana/web3.js"; import { WalletReadyState as Ee } from "@solana/wallet-adapter-base"; import { WalletIcon as Ke } from "@solana/wallet-adapter-react-ui"; import "@solana/wallet-adapter-wallets"; function Qe(e) { switch (e) { case "mainnet-beta": return "https://api.mainnet-beta.solana.com"; case "devnet": return "https://api.devnet.solana.com"; case "testnet": return "https://api.testnet.solana.com"; default: return "https://api.mainnet-beta.solana.com"; } } function Ge(e) { try { const r = new URL(e); return `${r.protocol}//${r.host}`; } catch { return e; } } function Ye(e = {}) { const { solanaCluster: r = "mainnet-beta", solanaEndpoint: t, customRpcProviders: o = [], allowUnsafeScripts: f = !1, additionalScriptSrc: i = [], additionalConnectSrc: h = [], additionalFrameSrc: x = [], includeStripe: m = !0 } = e, l = ["'self'"]; f && l.push("'unsafe-inline'", "'unsafe-eval'"), m && l.push("https://js.stripe.com"), l.push(...i); const s = ["'self'"]; m && s.push("https://api.stripe.com", "https://*.stripe.com"); const b = Qe(r); if (s.push(b), s.push("https://*.solana.com"), t) { const d = Ge(t); s.includes(d) || s.push(d); } o.forEach((d) => { s.includes(d) || s.push(d); }), s.push(...h); const c = ["'self'"]; return m && c.push("https://js.stripe.com", "https://checkout.stripe.com"), c.push(...x), { scriptSrc: l, connectSrc: s, frameSrc: c }; } function Ve(e, r = "header") { const { scriptSrc: t, connectSrc: o, frameSrc: f } = e; switch (r) { case "header": case "meta": case "nextjs": case "nginx": { const i = []; return t.length > 0 && i.push(`script-src ${t.join(" ")}`), o.length > 0 && i.push(`connect-src ${o.join(" ")}`), f.length > 0 && i.push(`frame-src ${f.join(" ")}`), i.join("; "); } case "helmet": { const i = {}; return t.length > 0 && (i.scriptSrc = t), o.length > 0 && (i.connectSrc = o), f.length > 0 && (i.frameSrc = f), i; } case "directives": return { scriptSrc: t, connectSrc: o, frameSrc: f }; default: throw new Error(`Unknown CSP format: ${r}`); } } function ht(e = {}, r = "header") { const t = Ye(e); return Ve(t, r); } const bt = { HELIUS: "https://*.helius-rpc.com", QUICKNODE: "https://*.quicknode.pro", ALCHEMY: "https://*.alchemy.com", ANKR: "https://rpc.ankr.com", TRITON: "https://*.rpcpool.com" }, St = { /** * Mainnet production with custom RPC (recommended) */ MAINNET_CUSTOM_RPC: (e) => ({ solanaCluster: "mainnet-beta", solanaEndpoint: e, allowUnsafeScripts: !1 }), /** * Mainnet with Next.js (requires unsafe-inline/eval) */ MAINNET_NEXTJS: (e) => ({ solanaCluster: "mainnet-beta", solanaEndpoint: e, allowUnsafeScripts: !0 }), /** * Devnet for testing */ DEVNET: () => ({ solanaCluster: "devnet", allowUnsafeScripts: !0 }), /** * Crypto-only payments (no Stripe) */ CRYPTO_ONLY: (e) => ({ solanaCluster: "mainnet-beta", solanaEndpoint: e, includeStripe: !1 }), /** * Stripe-only payments (no Solana) */ STRIPE_ONLY: () => ({ solanaCluster: "mainnet-beta", includeStripe: !0, // Don't include Solana RPC endpoints customRpcProviders: [] }) }, Xe = ({ resource: e, items: r, label: t, cardLabel: o, cryptoLabel: f, showCard: i = !0, showCrypto: h = !0, onPaymentAttempt: x, onPaymentSuccess: m, onPaymentError: l, onStripeSuccess: s, onCryptoSuccess: b, onStripeError: c, onCryptoError: d, customerEmail: n, successUrl: a, cancelUrl: p, metadata: C, couponCode: P, autoDetectWallets: y = !0, testPageUrl: B, hideMessages: N = !1, renderModal: E }) => { const I = le(), [v, k] = U(!1), { status: q, processPayment: R, processCartCheckout: $ } = $e(), { isCartMode: _, effectiveResource: z } = Re(e, r), { t: O } = ge(), L = t || O("ui.purchase"), j = o || O("ui.card"), ue = f || O("ui.usdc_solana"), re = T(async () => { if (y && i) { const { detectSolanaWallets: A } = await import("./walletDetection-JZR3UCOa.mjs"); if (!A()) { const K = _ ? void 0 : z, Q = _ && r ? Ie(r) : void 0; he("stripe", K, Q), x && x("stripe"), ie("stripe", K, Q); let M; _ && r ? M = await $( r, a, p, C, n, P ) : z && (M = await R( z, a, p, C, n, P )), M && M.success && M.transactionId ? (be("stripe", M.transactionId, K, Q), s ? s(M.transactionId) : m && m(M.transactionId)) : M && !M.success && M.error && (ce("stripe", M.error, K, Q), c ? c(M.error) : l && l(M.error)); return; } } k(!0); }, [y, i, _, r, z, $, R, a, p, C, n, P, m, l, s, c, x]), te = F(() => _ && r ? `purchase-cart-${r.map((A) => A.resource).join("-")}` : `purchase-${z || "unknown"}`, [_, r, z]), ne = F( () => Se(te, re), [te, re] ), H = q === "loading", se = { isOpen: v, onClose: () => k(!1), resource: _ ? void 0 : z, items: _ ? r : void 0, cardLabel: j, cryptoLabel: ue, showCard: i, showCrypto: h, onPaymentAttempt: x, onPaymentSuccess: (A) => { k(!1), m?.(A); }, onPaymentError: (A) => { k(!1), l?.(A); }, onStripeSuccess: (A) => { k(!1), s?.(A); }, onCryptoSuccess: (A) => { k(!1), b?.(A); }, onStripeError: (A) => { k(!1), c?.(A); }, onCryptoError: (A) => { k(!1), d?.(A); }, customerEmail: n, successUrl: a, cancelUrl: p, metadata: C, couponCode: P, testPageUrl: B, hideMessages: N }; return /* @__PURE__ */ w("div", { className: I.unstyled ? "" : I.className, style: I.unstyled ? {} : I.style, children: [ /* @__PURE__ */ u( "button", { onClick: ne, disabled: H, className: I.unstyled ? "" : "cedros-theme__button cedros-theme__stripe", style: { width: "100%", cursor: H ? "not-allowed" : "pointer", opacity: H ? 0.6 : 1 }, type: "button", children: H ? O("ui.processing") : L } ), E ? E(se) : /* @__PURE__ */ u(ze, { ...se }) ] }); }; function wt(e) { const { resource: r, items: t, checkout: o = {}, display: f = {}, callbacks: i = {}, advanced: h = {} } = e, { config: x, walletPool: m } = ee(), l = le(), { isCartMode: s } = Re(r, t), b = G.useMemo(() => ({ marginTop: "0.5rem", fontSize: "0.875rem", color: l.tokens.surfaceText, opacity: 0.7, textAlign: "center" }), [l.tokens.surfaceText]), c = G.useMemo( () => h.wallets && h.wallets.length > 0 ? h.wallets : m.getAdapters(), [h.wallets, m] ), d = G.useMemo( () => t ? Ie(t) : 0, [t] ), n = G.useMemo( () => i.onPaymentSuccess ? (k) => i.onPaymentSuccess({ transactionId: k, method: "stripe" }) : void 0, [i.onPaymentSuccess] ), a = G.useMemo( () => i.onPaymentSuccess ? (k) => i.onPaymentSuccess({ transactionId: k, method: "crypto" }) : void 0, [i.onPaymentSuccess] ), p = G.useMemo( () => i.onPaymentError ? (k) => i.onPaymentError({ message: k, method: "stripe" }) : void 0, [i.onPaymentError] ), C = G.useMemo( () => i.onPaymentError ? (k) => i.onPaymentError({ message: k, method: "crypto" }) : void 0, [i.onPaymentError] ), P = x.solanaEndpoint ?? He(x.solanaCluster); if (!r && (!t || t.length === 0)) return D().error('CedrosPay: Must provide either "resource" or "items" prop'), /* @__PURE__ */ u("div", { className: f.className, style: { color: l.tokens.errorText }, children: "Configuration error: No resource or items provided" }); const y = f.showCard ?? !0, B = f.showCrypto ?? !0, N = f.showPurchaseButton ?? !1, E = f.layout ?? "vertical", I = f.hideMessages ?? !1, v = h.autoDetectWallets ?? !0; return /* @__PURE__ */ u("div", { className: l.unstyled ? f.className : l.className, style: l.unstyled ? {} : l.style, children: /* @__PURE__ */ u(De, { endpoint: P, children: /* @__PURE__ */ u(qe, { wallets: c, autoConnect: !1, children: /* @__PURE__ */ u("div", { className: l.unstyled ? f.className : `cedros-theme__pay ${f.className || ""}`, children: /* @__PURE__ */ w("div", { className: l.unstyled ? "" : `cedros-theme__pay-content cedros-theme__pay-content--${E}`, children: [ N ? /* @__PURE__ */ u( Xe, { resource: s ? void 0 : r || t?.[0]?.resource, items: s ? t : void 0, label: f.purchaseLabel, cardLabel: f.cardLabel, cryptoLabel: f.cryptoLabel, showCard: y, showCrypto: B, onPaymentAttempt: i.onPaymentAttempt, onPaymentSuccess: n, onPaymentError: p, onStripeSuccess: n, onCryptoSuccess: a, onStripeError: p, onCryptoError: C, customerEmail: o.customerEmail, successUrl: o.successUrl, cancelUrl: o.cancelUrl, metadata: o.metadata, couponCode: o.couponCode, autoDetectWallets: v, testPageUrl: h.testPageUrl, hideMessages: I, renderModal: f.renderModal } ) : /* @__PURE__ */ w(me, { children: [ y && /* @__PURE__ */ u( Ue, { resource: s ? void 0 : r || t?.[0]?.resource, items: s ? t : void 0, customerEmail: o.customerEmail, successUrl: o.successUrl, cancelUrl: o.cancelUrl, metadata: o.metadata, couponCode: o.couponCode, label: f.cardLabel, onAttempt: i.onPaymentAttempt, onSuccess: n, onError: p } ), B && /* @__PURE__ */ u( je, { resource: s ? void 0 : r || t?.[0]?.resource, items: s ? t : void 0, metadata: o.metadata, couponCode: o.couponCode, label: f.cryptoLabel, onAttempt: i.onPaymentAttempt, onSuccess: a, onError: C, testPageUrl: h.testPageUrl, hideMessages: I } ) ] }), s && t && t.length > 1 && !I && /* @__PURE__ */ w("div", { style: b, children: [ "Checking out ", d, " items" ] }) ] }) }) }) }) }); } function Je() { const { subscriptionManager: e } = ee(), [r, t] = U({ status: "idle", error: null, sessionId: null, subscriptionStatus: null, expiresAt: null }), o = T( async (x) => { t((l) => ({ ...l, status: "loading", error: null })); const m = await e.processSubscription(x); return t((l) => ({ ...l, status: m.success ? "success" : "error", error: m.success ? null : m.error || "Subscription failed", sessionId: m.success && m.transactionId || null })), m; }, [e] ), f = T( async (x) => { t((m) => ({ ...m, status: "checking", error: null })); try { const m = await e.checkSubscriptionStatus(x); return t((l) => ({ ...l, status: m.active ? "success" : "idle", subscriptionStatus: m.status, expiresAt: m.expiresAt || m.currentPeriodEnd || null })), m; } catch (m) { const l = m instanceof Error ? m.message : "Failed to check subscription status"; throw t((s) => ({ ...s, status: "error", error: l })), m; } }, [e] ), i = T( async (x, m, l) => { t((s) => ({ ...s, status: "loading", error: null })); try { const s = await e.requestSubscriptionQuote( x, m, l ); return t((b) => ({ ...b, status: "idle" })), s; } catch (s) { const b = s instanceof Error ? s.message : "Failed to get subscription quote"; throw t((c) => ({ ...c, status: "error", error: b })), s; } }, [e] ), h = T(() => { t({ status: "idle", error: null, sessionId: null, subscriptionStatus: null, expiresAt: null }); }, []); return { ...r, processSubscription: o, checkStatus: f, requestQuote: i, reset: h }; } function Ct({ resource: e, interval: r, intervalDays: t, trialDays: o, successUrl: f, cancelUrl: i, metadata: h, customerEmail: x, couponCode: m, label: l, disabled: s = !1, onAttempt: b, onSuccess: c, onError: d, className: n = "" }) { const { status: a, error: p, sessionId: C, processSubscription: P } = Je(), y = le(), { t: B, translations: N } = ge(), E = l || B("ui.subscribe"), I = y.unstyled ? n : `${y.className} cedros-theme__stripe-button ${n}`.trim(), v = p && typeof p != "string" ? p?.code ?? null : null, q = p ? typeof p == "string" ? p : ((L) => { if (!L || !N) return ""; const j = N.errors[L]; return j ? j.action ? `${j.message} ${j.action}` : j.message : ""; })(v) : null, R = T(async () => { D().debug("[SubscribeButton] executeSubscription:", { resource: e, interval: r, intervalDays: t, trialDays: o, couponCode: m }), he("stripe", e), b && b("stripe"), ie("stripe", e); const L = await P({ resource: e, interval: r, intervalDays: t, trialDays: o, customerEmail: x, metadata: h, couponCode: m, successUrl: f, cancelUrl: i }); L.success && L.transactionId ? (be("stripe", L.transactionId, e), c && c(L.transactionId)) : !L.success && L.error && (ce("stripe", L.error, e), d && d(L.error)); }, [ e, r, t, o, x, h, m, f, i, P, b, c, d ]), $ = F(() => `subscribe-${e}-${r}`, [e, r]), _ = F( () => Se($, R), [$, R] ), z = a === "loading", O = s || z; return /* @__PURE__ */ w("div", { className: I, style: y.unstyled ? {} : y.style, children: [ /* @__PURE__ */ u( "button", { onClick: _, disabled: O, className: y.unstyled ? n : "cedros-theme__button cedros-theme__stripe", type: "button", children: z ? B("ui.processing") : E } ), q && /* @__PURE__ */ u("div", { className: y.unstyled ? "" : "cedros-theme__error", children: q }), C && /* @__PURE__ */ u("div", { className: y.unstyled ? "" : "cedros-theme__success", children: B("ui.redirecting_to_checkout") }) ] }); } function Ze() { const { subscriptionManager: e, x402Manager: r, walletManager: t } = ee(), { publicKey: o, signTransaction: f } = ye(), [i, h] = U({ status: "idle", error: null, sessionId: null, subscriptionStatus: null, expiresAt: null }), [x, m] = U(null), l = T(() => { if (!o) { const n = "Wallet not connected"; return h((a) => ({ ...a, status: "error", error: n })), { valid: !1, error: n }; } if (!f) { const n = "Wallet does not support signing"; return h((a) => ({ ...a, status: "error", error: n })), { valid: !1, error: n }; } return { valid: !0 }; }, [o, f]), s = T( async (n) => { if (!o) return h((a) => ({ ...a, status: "error", error: "Wallet not connected" })), null; h((a) => ({ ...a, status: "checking", error: null })); try { const a = await e.checkSubscriptionStatus({ resource: n, userId: o.toString() }); return h((p) => ({ ...p, status: a.active ? "success" : "idle", subscriptionStatus: a.status, expiresAt: a.expiresAt || a.currentPeriodEnd || null })), a; } catch (a) { const p = Z(a, "Failed to check subscription status"); return h((C) => ({ ...C, status: "error", error: p })), null; } }, [o, e] ), b = T( async (n, a, p) => { h((C) => ({ ...C, status: "loading", error: null })); try { const C = await e.requestSubscriptionQuote( n, a, p ); return m(C), h((P) => ({ ...P, status: "idle" })), C; } catch (C) { const P = Z(C, "Failed to get subscription quote"); return h((y) => ({ ...y, status: "error", error: P })), null; } }, [e] ), c = T( async (n, a, p) => { const C = l(); if (!C.valid) return { success: !1, error: C.error }; h((P) => ({ ...P, status: "loading", error: null })); try { const P = await e.requestSubscriptionQuote( n, a, p ); m(P); const y = P.requirement; if (!r.validateRequirement(y)) throw new Error("Invalid subscription quote received from server"); const B = !!y.extra?.feePayer; let N; if (B) { const { transaction: E, blockhash: I } = await r.buildGaslessTransaction({ resourceId: n, userWallet: o.toString(), feePayer: y.extra.feePayer, couponCode: p?.couponCode }), v = t.deserializeTransaction(E), k = await t.partiallySignTransaction({ transaction: v, signTransaction: f, blockhash: I }); N = await r.submitGaslessTransaction({ resource: n, partialTx: k, couponCode: p?.couponCode, resourceType: "regular", requirement: y }); } else { const E = await t.buildTransaction({ requirement: y, payerPublicKey: o }), I = await t.signTransaction({ transaction: E, signTransaction: f }), v = t.buildPaymentPayload({ requirement: y, signedTx: I, payerPublicKey: o }); N = await r.submitPayment({ resource: n, payload: v, couponCode: p?.couponCode, resourceType: "regular" }); } if (N.success) { const E = await e.checkSubscriptionStatus({ resource: n, userId: o.toString() }); h({ status: "success", error: null, sessionId: N.transactionId || null, subscriptionStatus: E.status, expiresAt: E.expiresAt || E.currentPeriodEnd || null }); } else h((E) => ({ ...E, status: "error", error: N.error || "Subscription payment failed" })); return N; } catch (P) { const y = Z(P, "Subscription payment failed"); return h((B) => ({ ...B, status: "error", error: y })), { success: !1, error: y }; } }, [ l, e, r, t, o, f ] ), d = T(() => { h({ status: "idle", error: null, sessionId: null, subscriptionStatus: null, expiresAt: null }), m(null); }, []); return { ...i, quote: x, checkStatus: s, requestQuote: b, processPayment: c, reset: d }; } function vt({ resource: e, interval: r, intervalDays: t, couponCode: o, label: f, disabled: i = !1, onAttempt: h, onSuccess: x, onError: m, className: l = "", testPageUrl: s, hideMessages: b = !1, autoCheckStatus: c = !0 }) { const { connected: d, connecting: n, connect: a, disconnect: p, select: C, wallets: P, wallet: y, publicKey: B } = ye(), { status: N, error: E, subscriptionStatus: I, expiresAt: v, checkStatus: k, processPayment: q } = Ze(), R = le(), { solanaError: $ } = ee(), { t: _, translations: z } = ge(), O = f || _("ui.subscribe_with_crypto"), L = ke(q), j = ke(k); Y(() => { L.current = q, j.current = k; }, [q, k]); const ue = E && typeof E != "string" ? E?.code ?? null : null, re = $ && typeof $ != "string" ? $?.code ?? null : null, te = (S) => { if (!S || !z) return ""; const W = z.errors[S]; return W ? W.action ? `${W.message} ${W.action}` : W.message : ""; }, ne = E ? typeof E == "string" ? E : te(ue) : null, H = $ ? typeof $ == "string" ? $ : te(re) : null, se = F( () => P.map((S) => `${S.adapter.name}-${S.readyState}`).join(","), [P] ), A = F( () => P.filter( ({ readyState: S }) => S === Ee.Installed || S === Ee.Loadable ), // eslint-disable-next-line react-hooks/exhaustive-deps [se] ); Y(() => { c && d && B && (D().debug("[CryptoSubscribeButton] Auto-checking subscription status"), j.current(e)); }, [c, d, B, e]), Y(() => { N === "success" && I === "active" && (be("crypto", "subscription-active", e), x && x("subscription-active")); }, [N, I, e, x]), Y(() => { N === "error" && E && (ce("crypto", E, e), m && m(E)); }, [N, E, e, m]); const K = typeof window < "u" && window.top !== window.self, [Q, M] = U(!1), [we, de] = U(!1), [Ce, X] = U(!1), J = $; Y(() => { let S = !1; return S || (async () => { if (we && y && !d && !n) { D().debug( "[CryptoSubscribeButton] Wallet detected, attempting auto-connect:", y.adapter.name ), de(!1), Te(y.adapter.name); try { await a(), S || D().debug("[CryptoSubscribeButton] Auto-connect successful"); } catch (pe) { if (!S) { D().error("[CryptoSubscribeButton] Auto-connect failed:", pe); const Le = pe instanceof Error ? pe.message : "Failed to connect wallet"; fe(Le, y.adapter.name), X(!1); } } } })(), () => { S = !0; }; }, [y, we, d, n, a]), Y(() => { d && Ce && B && y && (Oe(y.adapter.name, B.toString()), D().debug("[CryptoSubscribeButton] Processing pending subscription payment"), X(!1), M(!1), ie("crypto", e), L.current(e, r, { couponCode: o, intervalDays: t })); }, [d, Ce, B, y, e, r, o, t]); const ve = T(async () => { if (D().debug("[CryptoSubscribeButton] executeSubscriptionFlow called", { connected: d, wallet: y?.adapter.name, resource: e, interval: r }), he("crypto", e), h && h("crypto"), J) { D().error("[CryptoSubscribeButton] Solana dependencies missing:", J), ce("crypto", J, e), m && m(J); return; } if (K) { const S = s || window.location.href; window.open(S, "_blank", "noopener,noreferrer"); return; } if (d) ie("crypto", e), await q(e, r, { couponCode: o, intervalDays: t }); else { X(!0); try { if (y) D().debug( "[CryptoSubscribeButton] Wallet already selected, connecting:", y.adapter.name ), Te(y.adapter.name), await a(); else { if (D().debug("[CryptoSubscribeButton] No wallet selected, showing selector"), A.length === 0) { X(!1); const S = "No wallets available"; throw fe(S), new Error(S); } M(!0); } } catch (S) { X(!1); const W = S instanceof Error ? S.message : "Failed to connect wallet"; D().error("[CryptoSubscribeButton] Connection error:", W), fe(W, y?.adapter.name); } } }, [ d, y, e, r, o, t, K, s, A, a, q, J, h, m ]), xe = F(() => `crypto-subscribe-${e}-${r}`, [e, r]), Ne = F( () => Se(xe, ve, { cooldownMs: 200, deduplicationWindowMs: 0 }), [xe, ve] ), Pe = N === "loading" || N === "checking", oe = I === "active" || I === "trialing", Me = i || Pe || n || !!J || oe; let ae = O; if (Pe) ae = _("ui.processing"); else if (oe && v) { const S = new Date(v).toLocaleDateString(); ae = `${_("ui.subscribed_until")} ${S}`; } else oe && (ae = _("ui.subscribed")); const _e = T(async () => { try { de(!1), d && await p(), C(null), M(!0); } catch (S) { D().error("Failed to change wallet:", S); } }, [d, p, C]), Ae = T( (S) => { D().debug("[CryptoSubscribeButton] Wallet clicked:", S), M(!1), C(S), de(!0); }, [C] ), We = T(async () => { try { await p(), X(!1), typeof window < "u" && window.localStorage && window.localStorage.removeItem("walletName"); } catch (S) { D().error("Failed to disconnect wallet:", S); } }, [p]); return /* @__PURE__ */ w( "div", { className: R.unstyled ? l : `${R.className} cedros-theme__crypto-button ${l || ""}`, style: R.unstyled ? {} : R.style, children: [ /* @__PURE__ */ u( "button", { onClick: Ne, disabled: Me, className: R.unstyled ? l : "cedros-theme__button cedros-theme__crypto", type: "button", children: ae } ), Q && !b && /* @__PURE__ */ u( "div", { className: "cedros-modal-overlay", style: { position: "fixed", top: 0, left: 0, right: 0, bottom: 0, backgroundColor: R.tokens.modalOverlay, display: "flex", alignItems: "center", justifyContent: "center", zIndex: 9999, padding: "1rem" }, onClick: () => M(!1), children: /* @__PURE__ */ w( "div", { className: "cedros-modal-content", style: { backgroundColor: R.tokens.modalBackground, borderRadius: "12px", padding: "2rem", maxWidth: "400px", width: "100%", boxShadow: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)", border: `1px solid ${R.tokens.modalBorder}` }, onClick: (S) => S.stopPropagation(), children: [ /* @__PURE__ */ w( "div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "1.5rem" }, children: [ /* @__PURE__ */ u( "h3", { style: { margin: 0, fontSize: "1.25rem", fontWeight: 600, color: R.tokens.surfaceText }, children: _("wallet.select_wallet") } ), /* @__PURE__ */ u( "button", { onClick: () => M(!1), style: Fe(R.tokens.surfaceText), "aria-label": "Close modal", type: "button", children: "×" } ) ] } ), /* @__PURE__ */ u("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem" }, children: A.map((S) => /* @__PURE__ */ w( "button", { onClick: () => Ae(S.adapter.name), style: { width: "100%", padding: "1rem", backgroundColor: R.tokens.surfaceBackground, border: `1px solid ${R.tokens.surfaceBorder}`, borderRadius: "0.5rem", cursor: "pointer", fontSize: "1rem", textAlign: "left", color: R.tokens.surfaceText, display: "flex", alignItems: "center", gap: "1rem", transition: "all 0.2s ease" }, onMouseEnter: (W) => { W.currentTarget.style.backgroundColor = R.tokens.modalBackground, W.currentTarget.style.borderColor = R.tokens.surfaceText, W.currentTarget.style.transform = "translateY(-2px)"; }, onMouseLeave: (W) => { W.currentTarget.style.backgroundColor = R.tokens.surfaceBackground, W.currentTarget.style.borderColor = R.tokens.surfaceBorder, W.currentTarget.style.transform = "translateY(0)"; }, type: "button", children: [ /* @__PURE__ */ u(Ke, { wallet: S, style: { width: "24px", height: "24px" } }), /* @__PURE__ */ u("span", { style: { fontWeight: 500 }, children: S.adapter.name }) ] }, S.adapter.name )) }) ] } ) } ), d && !b && !Q && /* @__PURE__ */ w( "div", { style: { display: "flex", justifyContent: "space-between", marginTop: "0.5rem", fontSize: "0.75rem", color: R.tokens.surfaceText, opacity: 0.7 }, children: [ /* @__PURE__ */ u( "button", { onClick: _e, style: { background: "none", border: "none", padding: 0, color: "inherit", textDecoration: "none", cursor: "pointer", fontSize: "inherit" }, type: "button", children: _("wallet.change") } ), /* @__PURE__ */ u( "button", { onClick: We, style: { background: "none", border: "none", padding: 0, color: "inherit", textDecoration: "none", cursor: "pointer", fontSize: "inherit" }, type: "button", children: _("ui.disconnect") } ) ] } ), !b && H && /* @__PURE__ */ u("div", { className: R.unstyled ? "" : "cedros-theme__error", children: H }), !b && ne && /* @__PURE__ */ u("div", { className: R.unstyled ? "" : "cedros-theme__error", children: ne }), !b && oe && /* @__PURE__ */ u("div", { className: R.unstyled ? "" : "cedros-theme__success", children: _("ui.subscription_active") }) ] } ); } function et() { const { subscriptionChangeManager: e } = ee(), [r, t] = U({ status: "idle", error: null, subscription: null, changePreview: null }), o = T( async (s, b) => { t((c) => ({ ...c, status: "loading", error: null })); try { const c = await e.getDetails(s, b); return t((d) => ({ ...d, status: "success", subscription: c })), c; } catch (c) { const d = c instanceof Error ? c.message : "Failed to load subscription"; return t((n) => ({ ...n, status: "error", error: d })), null; } }, [e] ), f = T( async (s, b, c, d) => { t((n) => ({ ...n, status: "loading", error: null })); try { const n = { currentResource: s, newResource: b, userId: c, newInterval: d }, a = await e.previewChange(n); return t((p) => ({ ...p, status: "idle", changePreview: a })), a; } catch (n) { const a = n instanceof Error ? n.message : "Failed to preview change"; return t((p) => ({ ...p, status: "error", error: a })), null; } }, [e] ), i = T( async (s) => { const { subscription: b } = r; if (!b) return t((c) => ({ ...c, status: "error", error: "No subscription loaded" })), null; t((c) => ({ ...c, status: "loading", error: null })); try { const c = { currentResource: b.resource, newResource: s.newResource, userId: b.id, // Use subscription ID as user identifier newInterval: s.newInterval, prorationBehavior: s.prorationBehavior, immediate: s.immediate }, d = await e.changeSubscription(c); return d.success ? t((n) => ({ ...n, status: "success", subscription: n.subscription ? { ...n.subscription, resource: d.newResource, interval: d.newInterval, status: d.status } : null, changePreview: null })) : t((n) => ({ ...n, status: "error", error: d.error || "Failed to change subscription" })), d; } catch (c) { const d = c instanceof Error ? c.message : "Failed to change subscription"; return t((n) => ({ ...n, status: "error", error: d })), null; } }, [e, r.subscription] ), h = T( async (s) => { const { subscription: b } = r; if (!b) return t((c) => ({ ...c, status: "error", error: "No subscription loaded" })), null; t((c) => ({ ...c, status: "loading", error: null })); try { const c = { resource: b.resource, userId: b.id, immediate: s }, d = await e.cancel(c); if (d.success) { const n = s ? "canceled" : b.status; t((a) => ({ ...a, status: "success", subscription: a.subscription ? { ...a.subscription, status: n, cancelAtPeriodEnd: !s } : null })); } else t((n) => ({ ...n, status: "error", error: d.error || "Failed to cancel subscription" })); return d; } catch (c) { const d = c instanceof Error ? c.message : "Failed to cancel subscription"; return t((n) => ({ ...n, status: "error", error: d })), null; } }, [e, r.subscription] ), x = T( async (s, b) => { t((c) => ({ ...c, status: "loading", error: null })); try { const c = await e.getBillingPortalUrl({ userId: s, returnUrl: b }); return window.location.href = c.url, c; } catch (c) { const d = c instanceof Error ? c.message : "Failed to open billing portal"; return t((n) => ({ ...n, status: "error", error: d })), null; } }, [e] ), m = T(() => { t((s) => ({ ...s, changePreview: null })); }, []), l = T(() => { t({ status: "idle", error: null, subscription: null, changePreview: null }); }, []); return { ...r, loadSubscription: o, previewChange: f, changeSubscription: i, cancelSubscription: h, openBillingPortal: x, clearPreview: m, reset: l }; } const g = { container: { padding: "24px", backgroundColor: "#fff", borderRadius: "8px", border: "1px solid #e5e7eb", fontFamily: "system-ui, -apple-system, sans-serif" }, error: { padding: "12px 16px", backgroundColor: "#fef2f2", border: "1px solid #fecaca", borderRadius: "6px", color: "#dc2626", marginBottom: "16px" }, loading: { padding: "24px", textAlign: "center", color: "#6b7280" }, details: { marginBottom: "24px" }, title: { margin: "0 0 16px 0", fontSize: "18px", fontWeight: 600, color: "#111827" }, detailRow: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "8px 0", borderBottom: "1px solid #f3f4f6" }, label: { color: "#6b7280", fontSize: "14px" }, value: { color: "#111827", fontSize: "14px", fontWeight: 500 }, statusBadge: { padding: "4px 8px", borderRadius: "4px", color: "#fff", fontSize: "12px", fontWeight: 500, textTransform: "capitalize" }, cancelNotice: { marginTop: "12px", padding: "8px 12px", backgroundColor: "#fef3c7", border: "1px solid #fcd34d", borderRadius: "6px", color: "#92400e", fontSize: "13px" }, prorationPreview: { padding: "16px", backgroundColor: "#f9fafb", borderRadius: "8px", marginBottom: "24px" }, previewTitle: { margin: "0 0 12px 0", fontSize: "16px", fontWeight: 600, color: "#111827" }, previewDetails: { marginBottom: "16px" }, previewRow: { display: "flex", justifyContent: "space-between", padding: "6px 0", fontSize: "14px", color: "#4b5563" }, previewTotal: { borderTop: "1px solid #e5e7eb", marginTop: "8px", paddingTop: "12px", fontWeight: 600, color: "#111827" }, previewActions: { display: "flex", gap: "12px", justifyContent: "flex-end" }, cancelButton: { padding: "8px 16px", backgroundColor: "#fff", border: "1px solid #d1d5db", borderRadius: "6px", color: "#374151", cursor: "pointer", fontSize: "14px" }, confirmButton: { padding: "8px 16px", backgroundColor: "#3b82f6", border: "none", borderRadius: "6px", color: "#fff", cursor: "pointer", fontSize: "14px", fontWeight: 500 }, plansSection: { marginBottom: "24px" }, plansTitle: { margin: "0 0 12px 0", fontSize: "16px", fontWeight: 600, color: "#111827" }, plansList: { display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "16px" }, planCard: { padding: "16px", backgroundColor: "#fff", border: "1px solid #e5e7eb", borderRadius: "8px", textAlign: "center" }, currentPlan: { borderColor: "#3b82f6", backgroundColor: "#eff6ff" }, planName: { fontSize: "16px", fontWeight: 600, color: "#111827", marginBottom: "4px" }, planPrice: { fontSize: "14px", color: "#6b7280", marginBottom: "8px" }, planDescription: { fontSize: "12px", color: "#9ca3af", marginBottom: "12px" }, currentBadge: { display: "inline-block", padding: "4px 8px", backgroundColor: "#3b82f6", color: "#fff", borderRadius: "4px", fontSize: "12px", fontWeight: 500 }, changePlanButton: { padding: "8px 16px", backgroundColor: "#f3f4f6", border: "1px solid #d1d5db", borderRadius: "6px", color: "#374151", cursor: "pointer", fontSize: "14px", width: "100%" }, actions: { display: "flex", gap: "12px", justifyContent: "flex-end", paddingTop: "16px", borderTop: "1px solid #e5e7eb" }, portalButton: { padding: "10px 20px", backgroundColor: "#3b82f6", border: "none", borderRadius: "6px", color: "#fff", cursor: "pointer", fontSize: "14px", fontWeight: 500 }, cancelSubscriptionButton: { padding: "10px 20px", backgroundColor: "#fff", border: "1px solid #ef4444", borderRadius: "6px", color: "#ef4444", cursor: "pointer", fontSize: "14px" } }; function V(e, r) { return new Intl.NumberFormat("en-US", { style: "currency", currency: r.toUpperCase() }).format(e / 100); } function Be(e) { return new Date(e).toLocaleDateString("en-US", { year: "numeric", month: "long", day: "numeric" }); } function tt(e) { switch (e) { case "active": return "#22c55e"; case "trialing": return "#3b82f6"; case "past_due": return "#f59e0b"; case "canceled": case "expired": return "#ef4444"; default: return "#6b7280"; } } function rt({ preview: e, onConfirm: r, onCancel: t, isLoading: o }) { const f = e.immediateAmount < 0; return /* @__PURE__ */ w("div", { className: "cedros-proration-preview", style: g.prorationPreview, children: [ /* @__PURE__ */ u("h4", { style: g.previewTitle, children: "Change Preview" }), /* @__PURE__ */ w("div", { style: g.previewDetails, children: [ /* @__PURE__ */ w("div", { style: g.previewRow, children: [ /* @__PURE__ */ u("span", { children: "Current plan:" }), /* @__PURE__ */ w("span", { children: [ V(e.currentPlanPrice, e.currency), "/period" ] }) ] }), /* @__PURE__ */ w("div", { style: g.previewRow, children: [ /* @__PURE__ */ u("span", { children: "New plan:" }), /* @__PURE__ */ w("span", { children: [ V(e.newPlanPrice, e.currency), "/period" ] }) ] }), /* @__PURE__ */ w("div", { style: g.previewRow, children: [ /* @__PURE__ */ u("span", { children: "Days remaining:" }), /* @__PURE__ */ w("span", { children: [ e.daysRemaining, " days" ] }) ] }), e.prorationDetails && /* @__PURE__ */ w(me, { children: [ /* @__PURE__ */ w("div", { style: g.previewRow, children: [ /* @__PURE__ */ u("span", { children: "Unused credit:" }), /* @__PURE__ */ w("span", { children: [ "-", V(e.prorationDetails.unusedCredit, e.currency) ] }) ] }), /* @__PURE__ */ w("div", { style: g.previewRow, children: [ /* @__PURE__ */ u("span", { children: "New plan cost:" }), /* @__PURE__ */ u("span", { children: V(e.prorationDetails.newPlanCost, e.currency) }) ] }) ] }), /* @__PURE__ */ w("div", { style: { ...g.previewRow, ...g.previewTotal }, children: [ /* @__PURE__ */ u("span", { children: f ? "Credit to account:" : "Amount due now:" }), /* @__PURE__ */ u("span", { style: { color: f ? "#22c55e" : "#ef4444" }, children: V(Math.abs(e.immediateAmount), e.currency) }) ] }), /* @__PURE__ */ w("div", { style: g.previewRow, children: [ /* @__PURE__ */ u("span", { children: "Effective date:" }), /* @__PURE__ */ u("span", { children: Be(e.effectiveDate) }) ] }) ] }), /* @__PURE__ */ w("div", { style: g.previewActions, children: [ /* @__PURE__ */ u("button", { onClick: t, style: g.cancelButton, disabled: o, children: "Cancel" }), /* @__PURE__ */ u("button", { onClick: r, style: g.confirmButton, disabled: o, children: o ? "Processing..." : "Confirm Change" }) ] }) ] }); } function xt({ resource: e, userId: r, availablePlans: t = [], onSubscriptionChanged: o, onSubscriptionCanceled: f, billingPortalReturnUrl: i, showBillingPortal: h = !1, className: x, style: m }) { const { subscription: l, changePreview: s, status: b, error: c, loadSubscription: d, previewChange: n, changeSubscription: a, cancelSubscription: p, openBillingPortal: C, clearPreview: P } = et(); Y(() => { d(e, r); }, [e, r, d]); const y = T( async (v, k) => { await n(e, v, r, k); }, [e, r, n] ), B = T(async () => { if (!s) return; const v = t.find( (q) => q.price === s.newPlanPrice && q.currency === s.currency ); (await a({ newResource: v?.resource || e, newInterval: v?.interval, immediate: !0 }))?.success && v && o?.(v.resource, v.interval); }, [s, t, e, a, o]), N = T( async (v) => { (await p(v))?.success && f?.(); }, [p, f] ), E = T(() => { C(r, i); }, [r, i, C]), I = b === "loading"; return /* @__PURE__ */ w("div", { className: `cedros-subscription-panel ${x || ""}`, style: { ...g.container, ...m }, children: [ c && /* @__PURE__ */ u("div", { className: "cedros-subscription-error", style: g.error, children: c }), I && !l && /* @__PURE__ */ u("div", { className: "cedros-subscription-loading", style: g.loading, children: "Loading subscription..." }), l && /* @__PURE__ */ w(me, { children: [ /* @__PURE__ */ w("div", { className: "cedros-subscription-details", style: g.details, children: [ /* @__PURE__ */ u("h3", { style: g.title, children: "Current Subscription" }), /* @__PURE__ */ w("div", { style: g.detailRow, children: [ /* @__PURE__ */ u("span", { style: g.label, children: "Plan:" }), /* @__PURE__ */ u("span", { style: g.value, children: l.resource }) ] }), /* @__PURE__ */ w("div", { style: g.detailRow, children: [ /* @__PURE__ */ u("span", { style: g.label, children: "Status:" }), /* @__PURE__ */ u( "span", { style: { ...g.statusBadge, backgroundColor: tt(l.status) }, children: l.status } ) ] }), /* @__PURE__ */ w("div", { style: g.detailRow, children: [ /* @__PURE__ */ u("span", { style: g.label, children: "Price:" }), /* @__PURE__ */ w("span", { style: g.value, children: [ V(l.pricePerPeriod, l.currency), "/", l.interval ] }) ] }), /* @__PURE__ */ w("div", { style: g.detailRow, children: [ /* @__PURE__ */ u("span", { style: g.label, children: "Current period ends:" }), /* @__PURE__ */ u("span", { style: g.value, children: Be(l.currentPeriodEnd) }) ] }), l.cancelAtPeriodEnd && /* @__PURE__ */ u("div", { style: g.cancelNotice, children: "Subscription will cancel at end of current period" }) ] }), s && /* @__PURE__ */ u( rt, { preview: s, onConfirm: B, onCancel: P, isLoading: I } ), t.length > 0 && !s && /* @__PURE__ */ w("div", { className: "cedros-available-plans", style: g.plansSection, children: [ /* @__PURE__ */ u("h4", { style: g.plansTitle, children: "Available Plans" }), /* @__PURE__ */ u("div", { style: g.plansList, children: t.map((v) => { const k = v.resource === l.resource; return /* @__PURE__ */ w( "div", { style: { ...g.planCard, ...k ? g.currentPlan : {} }, children: [ /* @__PURE__ */ u("div", { style: g.planName, children: v.name }), /* @__PURE__ */ w("div", { style: g.planPrice, children: [ V(v.price, v.currency), "/", v.interval ] }), v.description && /* @__PURE__ */ u("div", { style: g.planDescription, children: v.description }), k ? /* @__PURE__ */ u("span", { style: g.currentBadge, children: "Current Plan" }) : /* @__PURE__ */ u( "button", { onClick: () => y(v.resource, v.interval), style: g.changePlanButton, disabled: I, children: v.price > l.pricePerPeriod ? "Upgrade" : "Downgrade" } ) ] }, v.resource ); }) }) ] }), /* @__PURE__ */ w("div", { className: "cedros-subscription-actions", style: g.actions, children: [ h && l.paymentMethod === "stripe" && /* @__PURE__ */ u("button", { onClick: E, style: g.portalButton, disabled: I, children: "Manage Billing" }), l.status === "active" && !l.cancelAtPeriodEnd && /* @__PURE__ */ u( "button", { onClick: () => N(!1), style: g.cancelSubscriptionButton, disabled: I, children: "Cancel Subscription" } ) ] }) ] }) ] }); } function Pt() { const { x402Manager: e, walletManager: r } = ee(), { publicKey: t, signTransaction: o } = ye(), [f, i] = U({ status: "idle", error: null, transactionId: null }), [h, x] = U(null), [m, l] = U(null), s = T( async (n) => { try { i((p) => ({ ...p, status: "loading" })); const a = await e.requestQuote({ resource: n }); if (!e.validateRequirement(a)) throw new Error("Invalid refund requirement received from server"); re