80 lines
2.2 KiB
TypeScript
80 lines
2.2 KiB
TypeScript
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 });
|
|
}
|
|
}
|