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', 'folio_ci', 'identificador_ci', '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, ]; } /** * Verifica si un vehículo está reportado como robado */ public function verificarRobo(string $niv) { try { $resultado = $this->consultarPadron($niv); if ($resultado['has_error']) { return false; } $estatus = $resultado['repuve_response']['estatus_registro'] ?? null; return in_array(strtoupper($estatus), ['ROBADO', 'ROBO']); } catch (Exception $e) { logger()->error("Error al verificar robo en REPUVE: " . $e->getMessage()); return false; } } 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, ], ]; } }