import { NextResponse } from "next/server"; import { z } from "zod"; import { auth } from "@/auth"; import { isAdminRole } from "@/lib/admin"; import { prisma } from "@/lib/prisma"; const bodySchema = z.object({ slug: z.string().min(1), }); export async function POST(req: Request) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } try { const json = await req.json(); const { slug } = bodySchema.parse(json); const sku = await prisma.prizeSku.findUnique({ where: { slug } }); if (!sku) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } const admin = isAdminRole(session.user!.role); const result = await prisma.$transaction(async (tx) => { if (!admin) { const wallet = await tx.wallet.findUnique({ where: { userId: session.user!.id } }); if (!wallet || wallet.balanceCredits < sku.costCredits) { throw new Error("INSUFFICIENT_CREDITS"); } } const redemption = await tx.redemption.create({ data: { userId: session.user!.id, prizeSkuId: sku.id, creditsSpent: admin ? 0 : sku.costCredits, }, }); if (!admin) { await tx.ledgerEntry.create({ data: { userId: session.user!.id, delta: -sku.costCredits, type: "DEBIT_SPEND", redemptionId: redemption.id, memo: `Redeem: ${sku.title}`, }, }); await tx.wallet.update({ where: { userId: session.user!.id }, data: { balanceCredits: { decrement: sku.costCredits } }, }); } return redemption.id; }); return NextResponse.json({ ok: true, redemptionId: result, adminBypass: admin, }); } catch (e) { if (e instanceof z.ZodError) { return NextResponse.json({ error: "Invalid input", issues: e.issues }, { status: 400 }); } if (e instanceof Error && e.message === "INSUFFICIENT_CREDITS") { return NextResponse.json({ error: "Insufficient BLW (Blue Wave)" }, { status: 402 }); } console.error(e); return NextResponse.json({ error: "Redeem failed" }, { status: 500 }); } }