"use client"; import { useEffect, useMemo, useState } from "react"; import { useSearchParams } from "next/navigation"; import { useQuery, useMutation } from "@tanstack/react-query"; import { useTranslations } from "next-intl"; import { useApiError } from "@/lib/use-api-error"; import { useRouter } from "@/i18n/routing"; import { ArrowRight, ArrowLeft, ShieldCheck } from "lucide-react"; import { apiGet, apiPost } from "@/lib/api/client"; import { isCafeOwner } from "@/lib/auth-permissions"; import { useAuthStore } from "@/lib/stores/auth.store"; import { formatCurrency, formatNumber } from "@/lib/format"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { PageHeader } from "@/components/layout/page-header"; import { PRICES, type PlanId } from "@/components/settings/plan-comparison"; type SubscribeResponse = { paymentId: string; paymentUrl: string; }; type PaymentMethod = { id: string; displayNameFa: string; isDefault: boolean; }; const BILLABLE_PLANS: PlanId[] = ["Pro", "Business"]; const MONTH_OPTIONS = [1, 3, 6, 12]; export function CheckoutScreen() { const t = useTranslations("subscription"); const tc = useTranslations("subscription.checkout"); const tPlans = useTranslations("settings.plans"); const apiError = useApiError(); const searchParams = useSearchParams(); const router = useRouter(); const user = useAuthStore((s) => s.user); const cafeId = user?.cafeId; const role = user?.role; const planParam = searchParams.get("plan") as PlanId | null; const isBillable = !!planParam && BILLABLE_PLANS.includes(planParam); const plan = (isBillable ? planParam : "Pro") as PlanId; const [months, setMonths] = useState(1); const [paymentMethod, setPaymentMethod] = useState(""); const [payError, setPayError] = useState(null); const numberLocale = typeof document !== "undefined" && document.documentElement.lang === "en" ? "en-US" : "fa-IR"; const isRtl = numberLocale !== "en-US"; const cafeName = useMemo(() => { if (!user) return ""; const membership = user.memberships?.find((m) => m.cafeId === user.cafeId); return membership?.cafeName ?? ""; }, [user]); const { data: paymentMethods = [] } = useQuery({ queryKey: ["billing-payment-methods", cafeId], queryFn: () => apiGet("/api/billing/payment-methods"), enabled: !!cafeId && isCafeOwner(role), }); // If the owner is still covered (active plan and/or queued plans), this purchase will be // queued to start when the current coverage ends rather than activating immediately. const { data: billingStatus } = useQuery({ queryKey: ["billing-status", cafeId], queryFn: () => apiGet<{ planTier: string; planExpiresAt: string | null; isPlanExpired: boolean; queuedPlans: { effectiveTo: string }[]; }>("/api/billing/status"), enabled: !!cafeId && isCafeOwner(role), }); const coverageEnd = useMemo(() => { if (!billingStatus) return null; const now = Date.now(); let end = 0; if ( billingStatus.planTier !== "Free" && billingStatus.planExpiresAt && !billingStatus.isPlanExpired ) { end = Math.max(end, new Date(billingStatus.planExpiresAt).getTime()); } for (const q of billingStatus.queuedPlans ?? []) { end = Math.max(end, new Date(q.effectiveTo).getTime()); } return end > now ? new Date(end) : null; }, [billingStatus]); useEffect(() => { if (!paymentMethod && paymentMethods.length > 0) { const def = paymentMethods.find((m) => m.isDefault) ?? paymentMethods[0]; setPaymentMethod(def.id); } }, [paymentMethods, paymentMethod]); const subscribe = useMutation({ mutationFn: (body: { planTier: string; months: number; paymentMethod: string }) => apiPost("/api/billing/subscribe", body), onSuccess: (data) => { setPayError(null); window.location.href = data.paymentUrl; }, onError: (err: unknown) => { setPayError(apiError(err, tc("paymentFailed"))); }, }); if (!cafeId) return null; if (!isCafeOwner(role)) { return (

{t("ownerOnly")}

); } if (!isBillable) { return (

{tc("invalidPlan")}

); } const unitPrice = PRICES[plan] ?? 0; const subtotal = unitPrice * months; const total = subtotal; const planName = tPlans(`names.${plan}`); const BackIcon = isRtl ? ArrowRight : ArrowLeft; const issuedAt = new Date().toLocaleDateString(numberLocale); const invoiceNo = `MZ-${Date.now().toString().slice(-8)}`; return (
router.push("/subscription")} > {tc("backToPlans")} } /> {coverageEnd ? (

{tc("queuedNotice", { date: coverageEnd.toLocaleDateString(numberLocale) })}

) : null} {/* Factor / invoice */} {/* Invoice header */}

{tc("invoiceLabel")}

{cafeName || tc("invoiceLabel")}

{tc("invoiceNo")}:
{invoiceNo}
{tc("issuedAt")}:
{issuedAt}
{/* Billing period selector */}

{tc("billingPeriod")}

{MONTH_OPTIONS.map((m) => ( ))}
{/* Line items */}
{tc("description")} {tc("qty")} {tc("unitPrice")} {tc("amount")}
{tc("planLine", { plan: planName })} {tc("monthsCount", { count: formatNumber(months, numberLocale) })} {formatCurrency(unitPrice, numberLocale)} {formatCurrency(subtotal, numberLocale)}
{/* Totals */}
{tc("subtotal")} {formatCurrency(subtotal, numberLocale)}
{tc("total")} {formatCurrency(total, numberLocale)}
{/* Payment method */} {paymentMethods.length > 0 ? (

{t("paymentMethod")}

{paymentMethods.map((m) => ( ))}
) : null}
{/* Pay action */}

{tc("secureNote")}

{payError && (

{payError}

)}
); }