import { randomUUID } from 'crypto' import { getSession } from '@/lib/session' import { getGameCharacters, getAccountIdByCharacterName } from '@/lib/characters' import { checkSecurityToken } from '@/lib/security-token' import { createSumUpCheckout, sumupConfigured } from '@/lib/sumup' import { priceCart, isValidCharName, type CartLine } from '@/lib/gift' /** * Inicia el pago (SumUp) de un regalo. Condiciones: * - Sesión con cuenta de juego seleccionada. * - Personaje de ORIGEN: uno de tu cuenta. * - Personaje de DESTINO: un nombre válido que exista (el de un amigo). * - Token de seguridad válido. * - Carrito no vacío; el total se calcula con los precios REALES de la BD. * Al pagar, la reconciliación/return envía los ítems por correo (`.send items`). */ export async function POST(request: Request) { const session = await getSession() if (!session.accountId) return Response.json({ success: false, error: 'notAuthenticated' }, { status: 401 }) let body: { source?: string; destination?: string; cart?: CartLine[]; security_token?: string; provider?: string } = {} try { body = await request.json() } catch { return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 }) } const source = String(body.source ?? '') const destination = String(body.destination ?? '').trim() const token = String(body.security_token ?? '').trim() const cart = Array.isArray(body.cart) ? body.cart : [] if (!source || !destination || !token) { return Response.json({ success: false, message: 'Completa el personaje de origen, el de destino y el token de seguridad.' }) } if (!isValidCharName(destination)) { return Response.json({ success: false, message: 'El nombre del personaje de destino no es válido.' }) } // Origen: debe pertenecer a tu cuenta. const chars = await getGameCharacters(session.accountId) if (!chars.find((c) => c.name === source)) { return Response.json({ success: false, message: 'El personaje de origen no pertenece a tu cuenta.' }) } // Destino: debe existir. const destAccount = await getAccountIdByCharacterName(destination) if (!destAccount) return Response.json({ success: false, message: 'El personaje de destino no existe.' }) // Token de seguridad. if (!(await checkSecurityToken(session.accountId, token))) { return Response.json({ success: false, message: 'El token de seguridad no es válido.' }) } // Carrito -> precio real (BD). Nunca se confía en el precio del cliente. const priced = await priceCart(cart) if (!priced || priced.total <= 0) { return Response.json({ success: false, message: 'Tu carrito está vacío o contiene objetos no válidos.' }) } if (String(body.provider) !== 'sumup' || !sumupConfigured()) { return Response.json({ success: false, error: 'notConfigured', message: 'El método de pago no está disponible ahora mismo.' }) } const site = process.env.SITE_URL || '' const ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' const reference = randomUUID() const items = priced.lines.map((l) => ({ i: l.itemId, q: l.qty })) const description = `Regalo para ${destination} (${priced.lines.length} objeto${priced.lines.length === 1 ? '' : 's'})` const r = await createSumUpCheckout({ accountId: session.accountId, username: session.username || '', email: session.bnetEmail || '', acoreIp: ip, amount: priced.total, points: 0, reference, description, returnUrl: `${site}/service-success?service=send-gift&provider=sumup&ref=${reference}`, service: 'send-gift', characterName: destination, metadata: { sender: source, items: JSON.stringify(items), amount: String(priced.total) }, }) if (!r.success || !r.url) return Response.json({ success: false, error: r.error || 'sumupError', message: 'No se pudo iniciar el pago.' }) return Response.json({ success: true, url: r.url }) }