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, sendGiftByMail, type CartLine } from '@/lib/gift' import { payServiceWithDPoints, payServiceWithVPoints } from '@/lib/pay-with-dpoints' /** * 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; locale?: 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.' }) } const site = process.env.SITE_URL || '' const items = priced.lines.map((l) => ({ i: l.itemId, q: l.qty })) // Pago con saldo PD o PV: se descuenta el saldo y se envía el regalo al momento. // PV es más caro que PD (son puntos gratis por votar). if (String(body.provider) === 'pd' || String(body.provider) === 'vp') { const useVp = String(body.provider) === 'vp' const r = useVp ? await payServiceWithVPoints(session.accountId, priced.total, () => sendGiftByMail(destination, source, items)) : await payServiceWithDPoints(session.accountId, priced.total, () => sendGiftByMail(destination, source, items)) if (!r.success) return Response.json({ success: false, error: r.error, message: 'No se pudo completar el pago.' }) const locale = String(body.locale) === 'en' ? 'en' : 'es' return Response.json({ success: true, url: `${site}/${locale}/service-success?service=send-gift&provider=${useVp ? 'vp' : 'pd'}&name=${encodeURIComponent(destination)}`, }) } 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 ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' const reference = randomUUID() 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 }) }