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 consultarPorVin(?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); } Log::info('ReporteRoboService: Consultando REPUVE', [ 'vin' => $vin, 'placa' => $placa ]); $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 SOAP 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' ]; } }