import Stripe from 'stripe' import type { RowDataPacket, ResultSetHeader } from 'mysql2' import { db, DB } from './db' const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string) /** * Reembolsa un pago de Stripe, entero o en parte. * * `amountEur` omitido = todo. Stripe cobra en la unidad mínima, así que el * importe va en céntimos. El reembolso se pide sobre el PaymentIntent de la * sesión de checkout, que es lo que guardamos en `stripelog`. */ export async function refundStripeSession(sessionId: string, amountEur?: number): Promise { if (!sessionId) return false try { const s = await stripe.checkout.sessions.retrieve(sessionId) const pi = typeof s.payment_intent === 'string' ? s.payment_intent : s.payment_intent?.id if (!pi) return false const cents = amountEur === undefined ? undefined : Math.round(amountEur * 100) if (cents !== undefined && cents <= 0) return false await stripe.refunds.create({ payment_intent: pi, ...(cents === undefined ? {} : { amount: cents }) }) return true } catch (e) { console.error(`[stripe] no se pudo reembolsar ${sessionId}:`, e) return false } } export async function isSessionPaid(sessionId: string): Promise { if (!sessionId) return false try { const s = await stripe.checkout.sessions.retrieve(sessionId) return s.payment_status === 'paid' } catch { return false } } export interface CheckoutParams { accountId: number username: string email: string acoreIp: string amount: number characterName: string productName: string successUrl: string cancelUrl: string currency?: string metadata?: Record } /** Crea una sesión de pago Stripe y registra el StripeLog. Devuelve la URL de checkout. */ export async function createCheckoutSession( p: CheckoutParams, ): Promise<{ success: boolean; url?: string; error?: string }> { try { const sep = p.successUrl.includes('?') ? '&' : '?' const successUrl = `${p.successUrl}${sep}session_id={CHECKOUT_SESSION_ID}` const session = await stripe.checkout.sessions.create({ payment_method_types: ['card'], line_items: [ { price_data: { currency: p.currency || 'eur', product_data: { name: p.productName }, unit_amount: Math.round(p.amount * 100), }, quantity: 1, }, ], mode: 'payment', success_url: successUrl, cancel_url: p.cancelUrl, metadata: p.metadata, }) const mode = (process.env.STRIPE_SECRET_KEY || '').startsWith('sk_live') ? 'live' : 'test' await db(DB.default).query( 'INSERT INTO stripelog (account_id, username, email, acore_ip, stripe_ip, product_name, amount, session_id, mode, character_name, timestamp, fulfilled) ' + 'VALUES (?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, NOW(), 0)', [p.accountId, p.username, p.email, p.acoreIp, p.productName, p.amount, session.id, mode, p.characterName], ) return { success: true, url: session.url ?? undefined } } catch (e) { return { success: false, error: String(e) } } } /** * Verifica que la sesión está pagada y aún no entregada; la marca como entregada * (evita re-entregas). Devuelve el character_name del log o null. (Port de * require_paid_stripe.) */ export async function claimPaidCheckout( sessionId: string, ): Promise<{ characterName: string; metadata: Record } | null> { if (!sessionId) return null let metadata: Record = {} try { const s = await stripe.checkout.sessions.retrieve(sessionId) if (s.payment_status !== 'paid') return null metadata = (s.metadata as Record) ?? {} } catch { return null } const [rows] = await db(DB.default).query( 'SELECT id, character_name FROM stripelog WHERE session_id = ?', [sessionId], ) const log = rows[0] if (!log) return null // Reclamo ATÓMICO: solo gana quien pasa fulfilled 0->1. Evita doble entrega si // el webhook y la página de éxito corren a la vez. const [res] = await db(DB.default).query( 'UPDATE stripelog SET fulfilled = 1 WHERE id = ? AND fulfilled = 0', [log.id], ) if (res.affectedRows === 0) return null return { characterName: log.character_name, metadata } } /** Registra la IP de Stripe y actualiza el timestamp del log (como el webhook de Django). */ export async function recordStripeIp(sessionId: string, ip: string): Promise { if (!sessionId) return await db(DB.default).query('UPDATE stripelog SET stripe_ip = ?, timestamp = NOW() WHERE session_id = ?', [ ip.slice(0, 45), sessionId, ]) }