import { NextResponse } from "next/server"; import { getSessionUser } from "@/lib/session"; import { clearSavedPaymentMethod, getOrgBillingConfig } from "@/lib/db"; import { detachPaymentMethod } from "@/lib/stripe"; import { safeError } from "@/lib/errors"; /** * DELETE /api/billing/saved-card * * Phase 9. Remove the saved card for the caller's org. Detaches * the PaymentMethod in Stripe (so it can't be charged again) and * clears the four display columns + the pm_id reference locally. * * Idempotent: calling on an org with no saved card returns 200 * (the desired end-state is already reached). * * Auth: any signed-in member of the org. Same reasoning as the * setup endpoint — card removal is a customer-visible action; it * doesn't leak anything, and a non-owner needing to remove a * stolen-card-on-file shouldn't be blocked by role gating. */ export async function DELETE() { const user = await getSessionUser(); if (!user) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } try { const cfg = await getOrgBillingConfig(user.orgId); if (!cfg || !cfg.stripeDefaultPaymentMethodId) { // Already empty — no-op, return success. return NextResponse.json({ removed: false }); } // Stripe detach first. If it fails for a real reason (network, // 500 from Stripe), we don't clear the DB — admin can retry. // 404 is treated as success by detachPaymentMethod (PM already // gone), so we proceed to clear the DB regardless. await detachPaymentMethod(cfg.stripeDefaultPaymentMethodId); await clearSavedPaymentMethod(user.orgId); return NextResponse.json({ removed: true }); } catch (e) { return NextResponse.json( { error: safeError(e, "Failed to remove card") }, { status: 500 } ); } }