import type { RowDataPacket } from 'mysql2' import { db, DB } from './db' import { authenticate } from './auth' import { checkSecurityToken } from './security-token' import { is2faEnabled } from './two-factor' /** * Condiciones para transferir un personaje a otra cuenta (/transfer-character): * personaje desconectado, contraseña correcta, token de seguridad, 2FA activo, * cuenta destino válida, y —para Caballeros de la Muerte— la cuenta destino debe * tener un personaje de nivel 55+ y no tener ya un DK. Bloqueo de 5 s tras una * transferencia reciente. Se comprueba ANTES del pago (SumUp). */ export async function checkTransferEligibility( accountId: number, email: string, character: string, body: Record, ): Promise<{ ok: boolean; message?: string }> { const destination = String(body.destination_account ?? '').trim() const password = String(body.password ?? '') const token = String(body.security_token ?? '').trim() if (!destination || !password || !token) { return { ok: false, message: 'Completa la cuenta destino, la contraseña y el token de seguridad.' } } // Personaje: de la cuenta, desconectado, clase. const [chRows] = await db(DB.characters).query( 'SELECT class, online FROM characters WHERE name = ? AND account = ? LIMIT 1', [character, accountId], ) const ch = chRows[0] if (!ch) return { ok: false, message: 'El personaje no pertenece a tu cuenta.' } if (ch.online === 1) return { ok: false, message: 'El personaje debe estar desconectado.' } const isDK = Number(ch.class) === 6 // Contraseña + token de seguridad + 2FA activo. if (!(await authenticate(email, password))) return { ok: false, message: 'La contraseña de tu cuenta es incorrecta.' } if (!(await checkSecurityToken(accountId, token))) return { ok: false, message: 'El token de seguridad no es válido.' } if (!(await is2faEnabled(accountId))) return { ok: false, message: 'Debes tener el 2FA activado en tu cuenta.' } // Cuenta destino (por nombre de cuenta). let destId = 0 try { const [acc] = await db(DB.auth).query('SELECT id FROM account WHERE username = ? LIMIT 1', [destination]) destId = acc[0] ? Number(acc[0].id) : 0 } catch { return { ok: false, message: 'No se pudo verificar la cuenta de destino.' } } if (!destId) return { ok: false, message: 'La cuenta de destino no existe.' } if (destId === accountId) return { ok: false, message: 'La cuenta de destino no puede ser la tuya.' } // Condiciones de Caballero de la Muerte. if (isDK) { const [lvl] = await db(DB.characters).query( 'SELECT 1 FROM characters WHERE account = ? AND level >= 55 LIMIT 1', [destId], ) if (!lvl[0]) { return { ok: false, message: 'La cuenta destino debe tener un personaje de nivel 55 o superior para recibir un Caballero de la Muerte.' } } const [dk] = await db(DB.characters).query( 'SELECT 1 FROM characters WHERE account = ? AND class = 6 LIMIT 1', [destId], ) if (dk[0]) return { ok: false, message: 'La cuenta destino ya tiene un Caballero de la Muerte.' } } // Bloqueo de 5 s tras una transferencia reciente de esta cuenta. try { const [recent] = await db(DB.default).query( "SELECT id FROM stripelog WHERE account_id = ? AND service = 'transfer' AND fulfilled = 1 AND timestamp > (NOW() - INTERVAL 5 SECOND) LIMIT 1", [accountId], ) if (recent[0]) return { ok: false, message: 'Espera unos segundos antes de transferir otro personaje.' } } catch { /* ignore */ } return { ok: true } }