import { NextRequest, NextResponse } from "next/server"; import Stripe from "stripe"; import { eq } from "drizzle-orm"; import { getCurrentUser } from "@/lib/auth/session"; import { requireCsrf } from "@/lib/auth/csrf"; import { db, isDatabaseAvailable, schema } from "@/lib/db"; const stripe = process.env.STRIPE_SECRET_KEY ? new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: "2025-01-27.acacia" as any, maxNetworkRetries: 2, }) : null; export const runtime = "nodejs"; export async function POST(req: NextRequest) { try { const csrfBlocked = requireCsrf(req); if (csrfBlocked) return csrfBlocked; const user = await getCurrentUser(req); if (!user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const hasDb = await isDatabaseAvailable().catch(() => false); // A recurring Stripe subscription is cancelled at period end, so Pro // access (and the Pro badge) continues for whatever the user already // paid for. A plan with no subscription behind it (lifetime, or a plan // granted without Stripe) has no period to run out, so it downgrades // immediately instead. if (user.stripeSubscriptionId && stripe) { try { await stripe.subscriptions.update(user.stripeSubscriptionId, { cancel_at_period_end: true, }); } catch (stripeErr: any) { console.warn("Could not schedule Stripe subscription cancellation (may already be cancelled):", stripeErr?.message); } if (hasDb) { try { // Optimistic UI flag — the authoritative sync happens via the // customer.subscription.updated webhook, which may lag behind. await db .update(schema.users) .set({ cancelAtPeriodEnd: true, updatedAt: new Date(), }) .where(eq(schema.users.id, user.id)); } catch (dbErr) { console.error("Database error while scheduling subscription cancellation:", dbErr); } } return NextResponse.json({ success: true, scheduled: true, expiresAt: user.expiresAt ? user.expiresAt.toISOString() : null, message: "Subscription will cancel at the end of the current billing period.", }); } // No Stripe subscription behind the plan — downgrade immediately. if (hasDb) { try { await db .update(schema.users) .set({ plan: "free", stripeSubscriptionId: null, expiresAt: null, cancelAtPeriodEnd: false, updatedAt: new Date(), }) .where(eq(schema.users.id, user.id)); } catch (dbErr) { console.error("Database error while cancelling subscription:", dbErr); } } return NextResponse.json({ success: true, scheduled: false, plan: "free", message: "Subscription cancelled and downgraded to Free successfully.", }); } catch (error: any) { console.error("Subscription cancel error:", error); return NextResponse.json( { error: error?.message || "Fehler beim Kündigen des Abonnements." }, { status: 500 } ); } }