nomadweb/frontend/src/components/ebook/BuyEbook.tsx
2026-09-04 10:30:11 -05:00

95 lines
3.3 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { trackEvent } from "@/lib/ebook/analytics";
import { getFeatures } from "@/lib/ebook/features";
import { useT } from "@/components/ebook/LocaleProvider";
import { useAuth } from "@/lib/auth";
import { api } from "@/lib/api";
/** Buy edition via FastAPI /pay/create (pay_type=ebook). */
export function BuyEbook({ compact = false }: { compact?: boolean }) {
const t = useT();
const { token } = useAuth();
const router = useRouter();
const [busy, setBusy] = useState(false);
const [error, setError] = useState("");
const [owned, setOwned] = useState(false);
const [hasFiles, setHasFiles] = useState(false);
const features = getFeatures();
const enabled = features.payment?.checkout?.enabled !== false;
const productName = features.payment?.checkout?.productName || "Download Edition";
const configuredButton = features.payment?.checkout?.buttonText;
useEffect(() => {
api.ebookProduct()
.then((p) => setHasFiles(Boolean(p.has_files)))
.catch(() => setHasFiles(false));
}, []);
useEffect(() => {
if (!token) return;
api.ebookStatus(token).then((s) => {
setOwned(s.owned);
if (typeof s.has_files === "boolean") setHasFiles(s.has_files);
}).catch(() => {});
}, [token]);
if (!enabled) return null;
const buyLabel = configuredButton
|| (hasFiles ? t("payment.buyDownload") : t("payment.buyUnlock"));
const buySub = hasFiles ? t("payment.buySub") : t("payment.buySubOnline");
const ownedLabel = hasFiles ? t("payment.ownedDownload") : t("payment.ownedOnline");
const buy = async () => {
setError("");
if (!token) {
router.push("/login?next=/book");
return;
}
if (owned) {
router.push("/book/thanks");
return;
}
setBusy(true);
trackEvent("buy_click", { from: compact ? "reader" : "cover" });
try {
const returnUrl = `${window.location.origin}/book/thanks`;
const res = await api.createPayment(token, returnUrl, "ebook");
if (res.order_id) {
localStorage.setItem("nomadro-ebook-order", res.order_id);
}
if (res.status === "paid" && res.order_id) {
await api.completePayment(token, res.order_id).catch(() => null);
router.push(`/book/thanks?order_id=${encodeURIComponent(res.order_id)}`);
return;
}
if (!res.redirect_url) {
throw new Error(t("payment.checkoutFailed"));
}
window.location.href = res.redirect_url;
} catch (err) {
setError(err instanceof Error ? err.message : t("payment.checkoutFailed"));
setBusy(false);
}
};
return (
<section className={`buy-ebook${compact ? " is-compact" : ""}`}>
<h2 className="buy-ebook-title">{t("payment.buyGet", { name: productName })}</h2>
<p className="buy-ebook-sub">{buySub}</p>
<button type="button" className="buy-ebook-btn" disabled={busy} onClick={buy}>
{busy ? t("payment.openingCheckout") : owned ? ownedLabel : buyLabel}
</button>
{error ? <p className="buy-ebook-error">{error}</p> : null}
<p className="buy-ebook-hint">
{t("payment.orReadOnline")}{" "}
<Link href="/book/read">{t("thanks.purchaseContinue")}</Link>
</p>
</section>
);
}