import { randomUUID } from 'crypto' import { getSession } from '@/lib/session' import { getGameCharacters } from '@/lib/characters' import { createCheckoutSession } from '@/lib/stripe' import { createSumUpCheckout, sumupConfigured } from '@/lib/sumup' import { getPaidService, fulfillOk } from '@/lib/paid-services' import { payServiceWithDPoints } from '@/lib/pay-with-dpoints' /** Locale seguro (es/en) para construir la URL de retorno. */ function safeLocale(v: unknown): string { return v === 'en' ? 'en' : 'es' } export async function POST(request: Request, { params }: { params: Promise<{ service: string }> }) { const { service } = await params const cfg = getPaidService(service) if (!cfg) return Response.json({ success: false, error: 'invalidService' }, { status: 404 }) const session = await getSession() if (!session.accountId) return Response.json({ success: false, error: 'notAuthenticated' }, { status: 401 }) let body: Record = {} try { body = await request.json() } catch { return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 }) } const character = String(body.character ?? '') if (!character) return Response.json({ success: false, error: 'invalidCharacter' }) const chars = await getGameCharacters(session.accountId) if (!chars.find((c) => c.name === character)) { return Response.json({ success: false, error: 'invalidCharacter' }) } // Comprobación previa al pago (condiciones del servicio, p.ej. cambio de facción / transferencia). if (cfg.precheck) { const pc = await cfg.precheck({ accountId: session.accountId, email: session.bnetEmail || '', character, body }) if (!pc.ok) return Response.json({ success: false, error: 'ineligible', message: pc.message }) } // Campos extra del servicio (gold_amount, destination_account...) -> metadata. // `service` va en metadata para que el webhook sepa qué entregar sin la URL. const metadata: Record = { service } for (const f of cfg.extraFields) { const v = String(body[f] ?? '').trim() if (!v) return Response.json({ success: false, error: 'missingFields' }) metadata[f] = v } const price = await cfg.price(metadata) if (!price || price <= 0) return Response.json({ success: false, error: 'invalidRequest' }) const site = process.env.SITE_URL || '' const ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || '0.0.0.0' const productName = cfg.productName(character, metadata) // Pago con saldo PD: se descuenta el saldo y se ejecuta la acción al momento // (sin pasarela). Devuelve la URL de éxito para que el form redirija igual que // con Stripe/SumUp; la entrega ya se ha realizado aquí. if (String(body.provider) === 'pd') { const r = await payServiceWithDPoints(session.accountId, price, () => cfg.fulfill(character, metadata).then(fulfillOk)) if (!r.success) return Response.json({ success: false, error: r.error }) const locale = safeLocale(body.locale) return Response.json({ success: true, url: `${site}/${locale}/service-success?service=${service}&provider=pd&name=${encodeURIComponent(character)}`, }) } // Pago por SumUp (euros): al pagar, la reconciliación/return ejecuta la acción // del servicio sobre el personaje (no acredita PD). if (String(body.provider) === 'sumup') { if (!sumupConfigured()) return Response.json({ success: false, error: 'notConfigured' }) const reference = randomUUID() const r = await createSumUpCheckout({ accountId: session.accountId, username: session.username || '', email: session.bnetEmail || '', acoreIp: ip, amount: price, points: 0, reference, description: productName, returnUrl: `${site}/service-success?service=${service}&provider=sumup&ref=${reference}`, service, 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: price, characterName: character, productName, successUrl: `${site}/service-success?service=${service}`, cancelUrl: `${site}/${service}`, metadata, }) if (!r.success || !r.url) return Response.json({ success: false, error: 'stripeError' }) return Response.json({ success: true, url: r.url }) }