/** * Feature gating for PAYG billing. * * If user has an active subscription → all features unlocked. * If no subscription → free features only. * * Usage in server actions: * await requireSubscription("payroll"); * * Usage in client components: * const hasAccess = await checkSubscription(); */ import { createAdminClient } from "@/lib/supabase/server"; import { auth } from "@clerk/nextjs/server"; interface SubscriptionCheck { hasSubscription: boolean; status: string; currentPeriodEnd: string | null; cancelAtPeriodEnd: boolean; } /** * Check if the organisation has an active subscription. */ export async function checkSubscription(): Promise { const { userId } = await auth(); if (!userId) return { hasSubscription: false, status: "none", currentPeriodEnd: null, cancelAtPeriodEnd: false }; const supabase = await createAdminClient(); const { data: member } = await supabase .from("organisation_members") .select("organisation_id") .eq("user_id", userId) .single(); if (!member) return { hasSubscription: false, status: "none", currentPeriodEnd: null, cancelAtPeriodEnd: false }; const { data: sub } = await supabase .from("subscriptions") .select("status, current_period_end, cancel_at_period_end") .eq("organisation_id", member.organisation_id) .single(); const isActive = sub?.status === "active" || sub?.status === "trialing"; return { hasSubscription: isActive, status: sub?.status || "none", currentPeriodEnd: sub?.current_period_end || null, cancelAtPeriodEnd: sub?.cancel_at_period_end || false, }; } /** * Require an active subscription for a specific feature. * Throws an error if no active subscription. */ export async function requireSubscription(feature: string): Promise { const check = await checkSubscription(); if (!check.hasSubscription) { throw new Error(`"${feature}" requires an active subscription. Go to Settings → Billing to subscribe.`); } }