true] + $data); exit; } function jpubErr(string $msg, int $code = 400): void { ob_clean(); http_response_code($code); echo json_encode(['ok' => false, 'error' => $msg]); exit; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') jpubErr('Método no permitido', 405); $datos = json_decode(file_get_contents('php://input'), true); $cedula = trim($datos['cedula'] ?? ''); $codigo = trim($datos['codigo'] ?? ''); if (!$cedula) jpubErr('Documento requerido.'); if (!preg_match('/^\d{6}$/', $codigo)) jpubErr('Código inválido.'); try { $db = Database::getInstance(); $pac = $db->fetch("SELECT id, nombre_completo FROM lab_pacientes WHERE numero_documento = ? AND is_active = 1", [$cedula]); } catch (\Throwable $_) { jpubErr('Error de base de datos.', 500); } if (!$pac) jpubErr('Paciente no encontrado.'); // Buscar OTP válido try { $otp = $db->fetch( "SELECT id FROM lab_otp_tokens WHERE paciente_id = ? AND codigo = ? AND usado = 0 AND expires_at > NOW() ORDER BY id DESC LIMIT 1", [$pac['id'], $codigo] ); } catch (\Throwable $_) { jpubErr('Error de base de datos.', 500); } if (!$otp) jpubErr('Código incorrecto o expirado. Solicita uno nuevo.'); // Marcar OTP como usado y paciente como verificado try { $db->query("UPDATE lab_otp_tokens SET usado = 1 WHERE id = ?", [$otp['id']]); $db->query( "UPDATE lab_pacientes SET telefono_verificado = 1, verificado_at = NOW() WHERE id = ?", [$pac['id']] ); } catch (\Throwable $_) { jpubErr('Error al guardar verificación.', 500); } jpubOk(['nombre' => explode(' ', $pac['nombre_completo'])[0]]);