96 lines
2.9 KiB
TypeScript
96 lines
2.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import {
|
|
activateProPlan,
|
|
adminResetUserCredits,
|
|
grantExtraCredits,
|
|
markFreeTopUpPurchased,
|
|
} from "@/lib/billing";
|
|
import { FREE_TOP_UP_CREDITS } from "@/lib/credits";
|
|
import { isBillingDevMock } from "@/lib/stripe";
|
|
import { getSessionUser } from "@/lib/session";
|
|
|
|
/**
|
|
* Local-only billing helpers (no Stripe CLI required).
|
|
* Enabled when NODE_ENV=development (unless BILLING_DEV_MOCK=false) or BILLING_DEV_MOCK=true.
|
|
*/
|
|
function assertDev() {
|
|
if (!isBillingDevMock()) {
|
|
return NextResponse.json({ error: "Not available outside development mock mode" }, { status: 404 });
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
const blocked = assertDev();
|
|
if (blocked) return blocked;
|
|
|
|
const user = await getSessionUser();
|
|
if (!user) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
let body: {
|
|
action?: string;
|
|
credits?: number;
|
|
plan?: "FREE" | "PREMIUM";
|
|
};
|
|
try {
|
|
body = await req.json();
|
|
} catch {
|
|
return NextResponse.json({ error: "Invalid JSON" }, { status: 400 });
|
|
}
|
|
|
|
const action = body.action ?? "grant-credits";
|
|
|
|
switch (action) {
|
|
case "grant-credits": {
|
|
const credits = Math.floor(Number(body.credits ?? FREE_TOP_UP_CREDITS));
|
|
if (!Number.isFinite(credits) || credits <= 0) {
|
|
return NextResponse.json({ error: "Invalid credits" }, { status: 400 });
|
|
}
|
|
await grantExtraCredits(user.id, credits);
|
|
return NextResponse.json({ ok: true, granted: credits });
|
|
}
|
|
case "free-top-up": {
|
|
await markFreeTopUpPurchased(user.id);
|
|
return NextResponse.json({ ok: true, granted: FREE_TOP_UP_CREDITS });
|
|
}
|
|
case "set-pro": {
|
|
await activateProPlan(user.id, {
|
|
stripeSubscriptionId: `dev_sub_${Date.now()}`,
|
|
cardLast4: "4242",
|
|
});
|
|
return NextResponse.json({ ok: true, plan: "PREMIUM" });
|
|
}
|
|
case "set-free": {
|
|
await adminResetUserCredits(user.id, { plan: "FREE", creditsUsed: 0 });
|
|
return NextResponse.json({ ok: true, plan: "FREE" });
|
|
}
|
|
case "reset-cycle": {
|
|
await adminResetUserCredits(user.id, {
|
|
plan: body.plan,
|
|
creditsUsed: 0,
|
|
});
|
|
return NextResponse.json({ ok: true, reset: true });
|
|
}
|
|
default:
|
|
return NextResponse.json({ error: `Unknown action: ${action}` }, { status: 400 });
|
|
}
|
|
}
|
|
|
|
/** Convenience GET for quick browser testing: /api/dev/grant-credits?action=set-pro */
|
|
export async function GET(req: NextRequest) {
|
|
const blocked = assertDev();
|
|
if (blocked) return blocked;
|
|
|
|
const action = req.nextUrl.searchParams.get("action") ?? "grant-credits";
|
|
const credits = Number(req.nextUrl.searchParams.get("credits") ?? FREE_TOP_UP_CREDITS);
|
|
|
|
const fake = new NextRequest(req.url, {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json", cookie: req.headers.get("cookie") ?? "" },
|
|
body: JSON.stringify({ action, credits }),
|
|
});
|
|
return POST(fake);
|
|
}
|