79
src/app/api/rewards/redeem/route.ts
Normal file
79
src/app/api/rewards/redeem/route.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
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 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user