import { randomUUID } from 'crypto' import { getSession } from '@/lib/session' import { getGameCharacters } from '@/lib/characters' import { getRealmName, realmSlug } from '@/lib/realm' import { priceStoreCart, purchaseStoreCart, storeEuroTotal, createStoreOrder, storeConcept, type StoreCartLine } from '@/lib/store' import { CARD_MIN_EUR } from '@/lib/store-pricing' import { createCheckoutSession } from '@/lib/stripe' import { createSumUpCheckout, sumupConfigured } from '@/lib/sumup' function safeLocale(v: unknown): string { return v === 'en' ? 'en' : 'es' } /** * Compra de la tienda. `provider`: * - ausente/'balance' → cobra del saldo PD/PV y envía los ítems al momento. * - 'stripe'/'sumup' → pago con tarjeta (euros); el pedido se guarda y se * entrega tras confirmarse el pago (webhook/reconciliación). * El precio se recalcula siempre en el servidor; nunca se confía en el cliente. */ export async function POST(request: Request) { const session = await getSession() if (!session.accountId || !session.username) { return Response.json({ success: false, error: 'notAuthenticated' }, { status: 401 }) } // `items` viene del cliente y NO se cree nada de él salvo los ids y las // copias: precios, moneda y lote los relee priceStoreCart de la BD. let body: { character?: string; items?: StoreCartLine[]; provider?: string; locale?: string } = {} try { body = await request.json() } catch { return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 }) } const character = String(body.character ?? '') const chars = await getGameCharacters(session.accountId) if (!character || !chars.find((c) => c.name === character)) { return Response.json({ success: false, error: 'invalidCharacter' }) } const cart = await priceStoreCart(Array.isArray(body.items) ? body.items : []) if (!cart) return Response.json({ success: false, error: 'emptyCart' }) const provider = String(body.provider ?? 'balance') // Pago con tarjeta: guarda el pedido y crea el checkout de la pasarela. if (provider === 'stripe' || provider === 'sumup') { const amount = storeEuroTotal(cart.pdTotal, cart.vpTotal) const minEur = CARD_MIN_EUR[provider] if (amount < minEur) return Response.json({ success: false, error: 'amountTooLow', min: minEur }) const ref = randomUUID() if (!(await createStoreOrder(ref, session.accountId, character, cart))) { return Response.json({ success: false, error: 'genericError' }) } const site = process.env.SITE_URL || '' const ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' // Concepto con quién compra y qué: es lo único que verá la pasarela en su // panel para identificar el pago (ver `storeConcept`). const productName = storeConcept(cart, { bnetEmail: session.bnetEmail, account: session.username, character, ip, }) const metadata = { service: 'store', order_ref: ref } if (provider === 'sumup') { if (!sumupConfigured()) return Response.json({ success: false, error: 'notConfigured' }) const r = await createSumUpCheckout({ accountId: session.accountId, username: session.username || '', email: session.bnetEmail || '', acoreIp: ip, amount, points: 0, reference: ref, description: productName, returnUrl: `${site}/service-success?service=store&provider=sumup&ref=${ref}`, service: 'store', characterName: character, metadata, }) if (!r.success || !r.url) return Response.json({ success: false, error: r.error || 'sumupError' }) return Response.json({ success: true, url: r.url }) } const r = await createCheckoutSession({ accountId: session.accountId, username: session.username || '', email: session.bnetEmail || '', acoreIp: ip, amount, characterName: character, productName, successUrl: `${site}/service-success?service=store`, cancelUrl: `${site}/${safeLocale(body.locale)}/store-${realmSlug(await getRealmName())}`, metadata, }) if (!r.success || !r.url) return Response.json({ success: false, error: 'stripeError' }) return Response.json({ success: true, url: r.url }) } // Pago con saldo PD/PV: cobra y envía al momento. const r = await purchaseStoreCart(session.accountId, character, cart) if (!r.success) return Response.json({ success: false, error: r.error }) return Response.json({ success: true }) }