import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/auth"; import { prisma } from "@/lib/prisma"; import { generateServerSeed, deriveInt } from "@/lib/provably-fair"; import { creditWalletCredits, debitWalletCredits, INSUFFICIENT_CREDITS } from "@/lib/wallet-safety"; export const dynamic = "force-dynamic"; const SYMBOLS = ["🍒", "🍋", "🍊", "🍇", "💎", "7️⃣"]; const SYMBOL_WEIGHTS = [30, 25, 20, 15, 7, 3]; // out of 100 // Paytable: 3 of a kind multipliers const PAYTABLE: Record = { "🍒": 2, "🍋": 3, "🍊": 5, "🍇": 10, "💎": 25, "7️⃣": 50, }; function weightedSymbol(roll: number): string { let cumulative = 0; for (let i = 0; i < SYMBOLS.length; i++) { cumulative += SYMBOL_WEIGHTS[i]; if (roll < cumulative) return SYMBOLS[i]; } return SYMBOLS[SYMBOLS.length - 1]; } export async function POST(req: NextRequest) { const session = await auth(); if (!session?.user?.id) return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); const body = await req.json(); const { wageBLW, clientSeed } = body as { wageBLW: number; clientSeed?: string }; if (!Number.isInteger(wageBLW) || wageBLW < 1) return NextResponse.json({ error: "Invalid wager" }, { status: 400 }); const serverSeed = generateServerSeed(); const cs = clientSeed ?? "default"; const reels = [ weightedSymbol(deriveInt(serverSeed, cs, 0, 0, 99)), weightedSymbol(deriveInt(serverSeed, cs, 1, 0, 99)), weightedSymbol(deriveInt(serverSeed, cs, 2, 0, 99)), ]; let multiplier = 0; let outcome = "loss"; if (reels[0] === reels[1] && reels[1] === reels[2]) { multiplier = PAYTABLE[reels[0]] ?? 2; outcome = "win"; } else if (reels[0] === reels[1] || reels[1] === reels[2] || reels[0] === reels[2]) { multiplier = 1.5; outcome = "win"; } const payout = outcome === "win" ? Math.floor(wageBLW * multiplier) : 0; try { await prisma.$transaction(async (tx) => { await debitWalletCredits(tx, session.user.id, wageBLW); await tx.ledgerEntry.create({ data: { userId: session.user.id, delta: -wageBLW, type: "DEBIT_GAME_BET", memo: "SLOTS bet" }, }); if (payout > 0) { await creditWalletCredits(tx, session.user.id, payout); await tx.ledgerEntry.create({ data: { userId: session.user.id, delta: payout, type: "CREDIT_GAME_WIN", memo: "SLOTS payout" }, }); } await tx.gameSession.create({ data: { userId: session.user.id, gameType: "SLOTS", wageredBLW: wageBLW, payoutBLW: payout, multiplier, outcome, serverSeed, clientSeed: cs, resultData: { reels, multiplier }, }, }); }); } catch (e) { if (e instanceof Error && e.message === INSUFFICIENT_CREDITS) { return NextResponse.json({ error: "Insufficient balance" }, { status: 402 }); } console.error(e); return NextResponse.json({ error: "Could not settle slots game" }, { status: 500 }); } return NextResponse.json({ reels, multiplier, payout, outcome, serverSeed }); }