baseUrl = config('services.repuve_federal.base_url'); $this->roboEndpoint = config('services.repuve_federal.robo_endpoint'); $this->inscripcionEndpoint = config('services.repuve_federal.inscripcion_endpoint'); $this->username = config('services.repuve_federal.username'); $this->password = config('services.repuve_federal.password'); } public function consultarPadron(string $niv) { $url = $this->baseUrl . $this->roboEndpoint; $arg2 = $niv . '|||||||'; $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_HTTPHEADER, [ 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "doConsPadron"', 'Content-Length: ' . strlen($soapBody), ]); // Ejecutar la solicitud $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); if ($error) { throw new Exception("Error en la petición SOAP: {$error}"); } if ($httpCode !== 200) { throw new Exception("Error al consultar REPUVE: Código HTTP {$httpCode}"); } return $this->parseVehicleResponse($response, $niv); } private function parseVehicleResponse(string $soapResponse, string $niv) { preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); if (!isset($matches[1])) { $errorFromDb = Error::where('code', '108')->first(); return [ 'has_error' => true, 'error_code' => '108', 'error_name' => $errorFromDb?->name, 'error_message' => $errorFromDb?->description ?? 'Error al parsear respuesta', 'timestamp' => now()->toDateTimeString(), 'niv' => $niv, 'repuve_response' => null, ]; } $contenido = trim($matches[1]); // Verificar si hay error de REPUVE Nacional (cualquier formato con ERR o ERROR) if (preg_match('/(ERR|ERROR|err|error):(-?\d+)/i', $contenido, $errorMatch)) { $errorCode = $errorMatch[2]; // Buscar el error completo en la base de datos $errorFromDb = Error::where('code', $errorCode)->first(); return [ 'has_error' => true, 'error_code' => $errorCode, 'error_name' => $errorFromDb?->name, 'error_message' => $errorFromDb?->description ?? "Error código {$errorCode} - no catalogado", 'timestamp' => now()->toDateTimeString(), 'niv' => $niv, 'repuve_response' => $contenido, ]; } // Si empieza con OK:, parsear los datos if (str_starts_with($contenido, 'OK:')) { $datos = str_replace('OK:', '', $contenido); $valores = explode('|', $datos); $campos = [ 'marca', 'submarca', 'tipo_vehiculo', 'fecha_expedicion', 'oficina', 'niv', 'placa', 'motor', 'modelo', 'color', 'version', 'entidad', 'marca_padron', 'submarca_padron', 'tipo_uso_padron', 'tipo_vehiculo_padron', 'estatus_registro', 'aduana', 'nombre_aduana', 'patente', 'pedimento', 'fecha_pedimento', 'clave_importador', 'observaciones' ]; $jsonResponse = []; foreach ($campos as $i => $campo) { $jsonResponse[$campo] = $valores[$i] ?? null; } return [ 'has_error' => false, 'error_code' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), 'niv' => $niv, 'repuve_response' => $jsonResponse, ]; } $errorFromDb = Error::where('code', '108')->first(); return [ 'has_error' => true, 'error_code' => '108', 'error_name' => $errorFromDb?->name, 'error_message' => $errorFromDb?->description ?? 'Error al parsear respuesta', 'timestamp' => now()->toDateTimeString(), 'niv' => $niv, 'repuve_response' => null, ]; } public function verificarRobo(?string $niv = null, ?string $placa = null): array { try { $url = $this->baseUrl . $this->roboEndpoint; $arg2 = ($niv ?? '') . '|' . ($placa ?? '') . 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_HTTPHEADER, [ 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "doConsRepRobo"', 'Content-Length: ' . strlen($soapBody), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); // Si hay error de conexión, retornar error if ($error) { logger()->error('REPUVE verificarRobo: Error de conexión', [ 'error' => $error, 'niv' => $niv, 'placa' => $placa, ]); return [ 'is_robado' => false, 'has_error' => true, 'error_message' => 'Error de conexión con el servicio REPUVE', ]; } // Si hay error HTTP, retornar error if ($httpCode !== 200) { logger()->error('REPUVE verificarRobo: HTTP error', [ 'http_code' => $httpCode, 'niv' => $niv, 'placa' => $placa, ]); return [ 'is_robado' => false, 'has_error' => true, 'error_message' => "Error HTTP {$httpCode} del servicio REPUVE", ]; } // Parsear respuesta $valorBuscado = $niv ?: $placa ?: 'N/A'; $resultado = $this->parseRoboResponse($response, $valorBuscado); // Si hubo error al parsear, loguear pero retornar el resultado completo if ($resultado['has_error'] ?? false) { logger()->warning('REPUVE verificarRobo: Error al parsear respuesta', [ 'niv' => $niv, 'placa' => $placa, 'error' => $resultado['error_message'] ?? 'Desconocido', ]); } // Retornar el array completo con toda la información return $resultado; } catch (Exception $e) { logger()->error('REPUVE verificarRobo: Excepción capturada', [ 'niv' => $niv, 'placa' => $placa, 'exception' => $e->getMessage(), ]); return [ 'is_robado' => false, 'has_error' => true, 'error_message' => 'Excepción capturada: ' . $e->getMessage(), ]; } } public function inscribirVehiculo(array $datos) { $url = $this->baseUrl . $this->inscripcionEndpoint; // DATOS HARDCODEADOS $arg2 = implode('|', [ $datos['ent_fed'] ?? '', // 1. Entidad federativa $datos['ofcexp'] ?? '', // 2. Oficina expedición $datos['fechaexp'] ?? '', // 3. Fecha expedición $datos['placa'] ?? '', // 4. Placa $datos['tarjetacir'] ?? '', // 5. Tarjeta circulación $datos['marca'] ?? '', // 6. Marca $datos['submarca'] ?? '', // 7. Submarca $datos['version'] ?? '', // 8. Versión $datos['clase_veh'] ?? '', // 9. Clase vehículo $datos['tipo_veh'] ?? '', // 10. Tipo vehículo $datos['tipo_uso'] ?? '', // 11. Tipo uso $datos['modelo'] ?? '', // 12. Modelo (año) $datos['color'] ?? '', // 13. Color $datos['motor'] ?? '', // 14. Número motor $datos['niv'] ?? '', // 15. NIV $datos['rfv'] ?? '', // 16. RFV $datos['numptas'] ?? '', // 17. Número puertas $datos['observac'] ?? '', // 18. Observaciones $datos['tipopers'] ?? '', // 19. Tipo persona $datos['curp'] ?? '', // 20. CURP $datos['rfc'] ?? '', // 21. RFC $datos['pasaporte'] ?? '', // 22. Pasaporte $datos['licencia'] ?? '', // 23. Licencia $datos['nombre'] ?? '', // 24. Nombre $datos['ap_paterno'] ?? '', // 25. Apellido paterno $datos['ap_materno'] ?? '', // 26. Apellido materno $datos['ent_fed'] ?? '', // 27. Entidad federativa propietario $datos['munic'] ?? '', // 28. Municipio $datos['callep'] ?? '', // 29. Calle principal $datos['num_ext'] ?? '', // 30. Número exterior $datos['num_int'] ?? '', // 31. Número interior $datos['colonia'] ?? '', // 32. Colonia $datos['cp'] ?? '', // 33. Código postal $datos['cve_vehi'] ?? '', // 34. Clave vehículo $datos['nrpv'] ?? '', // 35. NRPV $datos['fe_act'] ?? '', // 36. Fecha actualización $datos['tipo_mov'] ?? '', // 37. Tipo movimiento ]); // Construir el cuerpo SOAP $soapBody = << {$this->username} {$this->password} {$arg2} XML; // Configurar cURL $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_HTTPHEADER, [ 'Content-Type: text/xml; charset=utf-8', 'SOAPAction: "inscribe"', 'Content-Length: ' . strlen($soapBody), ]); // Ejecutar la petición $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $curlError = curl_error($ch); curl_close($ch); // Loguear para debug logger()->info('REPUVE Inscripción Request', [ 'url' => $url, 'soap_body' => $soapBody ]); logger()->info('REPUVE Inscripción Response', [ 'http_code' => $httpCode, 'curl_error' => $curlError, 'response' => $response ]); if ($curlError) { $errorFromDb = Error::where('code', '103')->first(); return [ 'has_error' => true, 'error_code' => '103', 'error_name' => $errorFromDb?->name, 'error_message' => $errorFromDb?->description ?? "Error de conexión: {$curlError}", 'timestamp' => now()->toDateTimeString(), 'http_code' => $httpCode, 'raw_response' => $response, ]; } if ($httpCode !== 200) { $errorFromDb = Error::where('code', '-1')->first(); return [ 'has_error' => true, 'error_code' => '-1', 'error_name' => $errorFromDb?->name, 'error_message' => $errorFromDb?->description ?? "Error interno HTTP {$httpCode}", 'timestamp' => now()->toDateTimeString(), 'http_code' => $httpCode, 'raw_response' => $response, ]; } // Parsear la respuesta return $this->parsearRespuestaInscripcion($response); } /** * Parsea la respuesta */ private function parsearRespuestaInscripcion(string $soapResponse) { preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); if (!isset($matches[1])) { $errorFromDb = Error::where('code', '108')->first(); return [ 'has_error' => true, 'error_code' => '108', 'error_name' => $errorFromDb?->name, 'error_message' => $errorFromDb?->description ?? 'Error al parsear respuesta', 'timestamp' => now()->toDateTimeString(), 'raw_response' => $soapResponse, 'repuve_response' => null, ]; } $contenido = trim($matches[1]); // Buscar patrones de error: ERR:, ERROR:, err:, error: if (preg_match('/(ERR|ERROR|err|error):(-?\d+)/i', $contenido, $errorMatch)) { $errorCode = $errorMatch[2]; // Buscar el error completo en la base de datos $errorFromDb = Error::where('code', $errorCode)->first(); if ($errorFromDb) { // Retornar nombre y descripción de la BD return [ 'has_error' => true, 'error_code' => $errorCode, 'error_name' => $errorFromDb->name, 'error_message' => $errorFromDb->description, 'timestamp' => now()->toDateTimeString(), 'raw_response' => $soapResponse, 'repuve_response' => $contenido, ]; } // Si no existe en BD, retornar el código sin descripción return [ 'has_error' => true, 'error_code' => $errorCode, 'error_name' => null, 'error_message' => "Error código {$errorCode} - no catalogado", 'timestamp' => now()->toDateTimeString(), 'raw_response' => $soapResponse, 'repuve_response' => $contenido, ]; } // Si empieza con OK: es éxito if (preg_match('/^OK:/i', $contenido)) { $datos = preg_replace('/^OK:/i', '', $contenido); return [ 'has_error' => false, 'error_code' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), 'raw_response' => $soapResponse, 'repuve_response' => [ 'status' => 'OK', 'data' => $datos, ], ]; } // Si no hay ERR/ERROR y no es OK, asumir que es respuesta exitosa return [ 'has_error' => false, 'error_code' => null, 'error_name' => null, 'error_message' => null, 'timestamp' => now()->toDateTimeString(), 'raw_response' => $soapResponse, 'repuve_response' => [ 'status' => 'OK', 'data' => $contenido, ], ]; } private function parseRoboResponse(string $soapResponse, string $valor): array { // Extraer contenido del tag preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); if (!isset($matches[1])) { logger()->error('REPUVE parseRoboResponse: No se encontró tag ', [ 'soap_response' => substr($soapResponse, 0, 500), 'valor' => $valor, ]); return [ 'has_error' => true, 'is_robado' => false, 'error_message' => 'Respuesta SOAP inválida', ]; } $contenido = trim($matches[1]); // Verificar si hay error de REPUVE Nacional (ERR: o ERROR:) if (preg_match('/(ERR|ERROR|err|error):(-?\d+)/i', $contenido, $errorMatch)) { $errorCode = $errorMatch[2]; logger()->warning('REPUVE parseRoboResponse: Servicio retornó error', [ 'error_code' => $errorCode, 'contenido' => $contenido, 'valor' => $valor, ]); return [ 'has_error' => true, 'is_robado' => false, 'error_code' => $errorCode, 'error_message' => "Error REPUVE código {$errorCode}", 'raw_response' => $contenido, ]; } // Si empieza con OK:, parsear los datos de robo if (str_starts_with($contenido, 'OK:')) { $datos = str_replace('OK:', '', $contenido); $valores = explode('|', $datos); // 1 = robado, 0 = no robado $indicador = $valores[0] ?? '0'; $isRobado = ($indicador === '1'); $roboData = [ 'has_error' => false, 'is_robado' => $isRobado, 'indicador' => $indicador, 'fecha_robo' => $valores[1] ?? null, 'placa' => $valores[2] ?? null, 'niv' => $valores[3] ?? null, 'autoridad' => $valores[4] ?? null, 'acta' => $valores[5] ?? null, 'denunciante' => $valores[6] ?? null, 'fecha_acta' => $valores[7] ?? null, 'raw_response' => $contenido, ]; // Log importante si está robado if ($isRobado) { logger()->warning('REPUVE: Vehículo reportado como ROBADO', [ 'valor_buscado' => $valor, 'niv' => $roboData['niv'], 'placa' => $roboData['placa'], 'autoridad' => $roboData['autoridad'], 'acta' => $roboData['acta'], 'denunciante' => $roboData['denunciante'], ]); } else { logger()->info('REPUVE: Vehículo NO reportado como robado', [ 'valor_buscado' => $valor, ]); } return $roboData; } // Si no tiene formato reconocido logger()->error('REPUVE parseRoboResponse: Formato desconocido', [ 'contenido' => $contenido, 'valor' => $valor, ]); return [ 'has_error' => true, 'is_robado' => false, 'error_message' => 'Formato de respuesta no reconocido', 'raw_response' => $contenido, ]; } }