baseUrl = config('services.repuve_federal.base_url'); $this->roboEndpoint = config('services.repuve_federal.robo_endpoint'); $this->username = config('services.repuve_federal.username'); $this->password = config('services.repuve_federal.password'); } public function consultarRobado(?string $vin = null, ?string $placa = null) { try { if (empty($vin) && empty($placa)) { Log::warning('ReporteRoboService: No se proporcionó VIN ni PLACA'); return [ 'tiene_reporte' => false, 'datos' => [], 'error' => 'Debe proporcionar al menos VIN o PLACA' ]; } $url = $this->baseUrl . $this->roboEndpoint; // Construir arg2 según los parámetros if (!empty($vin) && !empty($placa)) { $arg2 = $vin . '|' . $placa . str_repeat('|', 5); } elseif (!empty($vin)) { $arg2 = $vin . str_repeat('|', 7); } else { $arg2 = '||' . $placa . str_repeat('|', 5); } $soapBody = << {$this->username} {$this->password} {$arg2} XML; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $soapBody); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "doConsRepRobo"', 'Content-Length: ' . strlen($soapBody), ]); try { $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); if ($error) { Log::error('ReporteRoboService: Error de conexión', ['error' => $error]); return [ 'tiene_reporte' => false, 'datos' => [], 'error' => 'Error de conexión con REPUVE' ]; } if ($httpCode !== 200) { Log::error('ReporteRoboService: HTTP error', ['http_code' => $httpCode]); return [ 'tiene_reporte' => false, 'datos' => [], 'error' => "Error HTTP {$httpCode}" ]; } return $this->parseRoboResponse($response, $vin ?? $placa); } finally { unset($ch); } } catch (Exception $e) { Log::error('ReporteRoboService: Excepción', ['message' => $e->getMessage()]); return [ 'tiene_reporte' => false, 'datos' => [], 'error' => $e->getMessage() ]; } } /** * Parsea la respuesta del servicio de robo */ private function parseRoboResponse(string $soapResponse, string $valor) { // Extraer contenido preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); if (!isset($matches[1])) { Log::error('ReporteRoboService:'); return [ 'tiene_reporte' => false, 'datos' => [], 'error' => 'Respuesta inválida' ]; } $contenido = trim($matches[1]); // Verificar si hay error (ERR: o ERROR:) if (preg_match('/(ERR|ERROR|err|error):(-?\d+)/i', $contenido, $errorMatch)) { $errorCode = $errorMatch[2]; Log::warning('ReporteRoboService: Error del servicio', ['error_code' => $errorCode]); return [ 'tiene_reporte' => false, 'datos' => ['error_code' => $errorCode], 'error' => "Error REPUVE código {$errorCode}" ]; } // Si empieza con OK:, parsear los datos if (str_starts_with($contenido, 'OK:')) { $datos = str_replace('OK:', '', $contenido); $valores = explode('|', $datos); // 1 = robado, 0 = no robado $indicador = $valores[0] ?? '0'; $tieneReporte = ($indicador === '1'); $datosRobo = [ 'indicador' => $indicador, 'fecha_robo' => $valores[1] ?? null, 'placa' => $valores[2] ?? null, 'vin' => $valores[3] ?? null, 'autoridad' => $valores[4] ?? null, 'acta' => $valores[5] ?? null, 'denunciante' => $valores[6] ?? null, 'fecha_acta' => $valores[7] ?? null, ]; if ($tieneReporte) { Log::warning('ReporteRoboService: ¡VEHÍCULO ROBADO DETECTADO!', [ 'vin' => $datosRobo['vin'], 'placa' => $datosRobo['placa'], 'autoridad' => $datosRobo['autoridad'] ]); } return [ 'tiene_reporte' => $tieneReporte, 'datos' => $datosRobo ]; } Log::error('ReporteRoboService:', ['contenido' => $contenido]); return [ 'tiene_reporte' => false, 'datos' => [], 'error' => 'Formato de respuesta no reconocido' ]; } /** * Consultar datos del vehículo (sin reporte de robo) */ public function consultarVehiculo(?string $niv = null, ?string $placa = null): array { try { if (empty($niv) && empty($placa)) { Log::warning('ReporteRoboService: consultarVehiculo sin NIV ni PLACA'); return [ 'success' => false, 'has_error' => true, 'error_message' => 'Debe proporcionar al menos NIV o PLACA', 'datos' => [] ]; } $url = $this->baseUrl . '/jaxws-consultarpv/ConsultaRpv'; // Construir arg2 if ($placa) { $arg2 = ($niv ?? '') . '|' . $placa . str_repeat('|', 5); } else { $arg2 = ($niv ?? '') . str_repeat('|', 7); } $soapBody = << {$this->username} {$this->password} {$arg2} XML; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $soapBody); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "doConsRPV"', 'Content-Length: ' . strlen($soapBody), ]); try { $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); if ($error) { Log::error('ReporteRoboService: consultarVehiculo - Error de conexión', [ 'error' => $error, 'niv' => $niv, 'placa' => $placa ]); return [ 'success' => false, 'has_error' => true, 'error_message' => 'Error de conexión', 'datos' => [] ]; } if ($httpCode !== 200) { Log::error('ReporteRoboService: consultarVehiculo - HTTP error', [ 'http_code' => $httpCode, 'niv' => $niv, 'placa' => $placa ]); return [ 'success' => false, 'has_error' => true, 'error_message' => "Error HTTP {$httpCode}", 'datos' => [] ]; } return $this->parseConsultaVehiculoResponse($response); } finally { unset($ch); } } catch (\Exception $e) { Log::error('ReporteRoboService: consultarVehiculo - Excepción', [ 'niv' => $niv, 'placa' => $placa, 'exception' => $e->getMessage() ]); return [ 'success' => false, 'has_error' => true, 'error_message' => $e->getMessage(), 'datos' => [] ]; } } /** * Parsear respuesta de consulta de vehículo * Extrae solo: VIN, placa, marca, modelo y color */ private function parseConsultaVehiculoResponse(string $soapResponse): array { // Extraer contenido del tag preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); if (!isset($matches[1])) { Log::error('ReporteRoboService: parseConsultaVehiculoResponse - Respuesta inválida'); return [ 'success' => false, 'has_error' => true, 'error_message' => 'Respuesta inválida del servicio', 'datos' => [] ]; } $contenido = trim($matches[1]); // Verificar si hay error if (preg_match('/(ERR|ERROR):(-?\d+)/i', $contenido, $errorMatch)) { $errorCode = $errorMatch[2]; Log::warning('ReporteRoboService: parseConsultaVehiculoResponse - Error del servicio', [ 'error_code' => $errorCode ]); return [ 'success' => false, 'has_error' => true, 'error_message' => "Error REPUVE código {$errorCode}", 'datos' => [] ]; } // Si empieza con OK:, parsear los datos if (str_starts_with($contenido, 'OK:')) { $datos = str_replace('OK:', '', $contenido); $valores = explode('|', $datos); // Estructura esperada del REPUVE Nacional // Posiciones aproximadas basadas en el servicio $datosVehiculo = [ 'vin' => $valores[0] ?? null, 'placa' => $valores[1] ?? null, 'marca' => $valores[2] ?? null, 'modelo' => $valores[3] ?? null, 'color' => $valores[4] ?? null, ]; // Filtrar valores vacíos $datosVehiculo = array_filter($datosVehiculo, fn($v) => !empty($v)); if (empty($datosVehiculo)) { return [ 'success' => false, 'has_error' => false, 'error_message' => 'No se encontró información del vehículo', 'datos' => [] ]; } Log::info('ReporteRoboService: Vehículo encontrado en REPUVE Nacional', [ 'vin' => $datosVehiculo['vin'] ?? null, 'placa' => $datosVehiculo['placa'] ?? null ]); return [ 'success' => true, 'has_error' => false, 'datos' => $datosVehiculo ]; } Log::error('ReporteRoboService: parseConsultaVehiculoResponse - Formato no reconocido', [ 'contenido' => $contenido ]); return [ 'success' => false, 'has_error' => true, 'error_message' => 'Formato de respuesta no reconocido', 'datos' => [] ]; } }