@cedros/pay-react
Version:
React frontend library for Cedros Pay - unified Stripe and Solana x402 payments
1,638 lines • 58.6 kB
JavaScript
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