95 lines
3.3 KiB
TypeScript
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>
|
|
);
|
|
}
|