From 3640fc8a1353cbb4ca459ae3d08f2da22150ad5b Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Fri, 21 Nov 2025 12:33:50 -0600 Subject: [PATCH] ADD: Implementar servicios para consulta de REPUVE --- .env.example | 7 + .../Repuve/InscriptionController.php | 262 ++---------------- app/Services/PadronEstatalService.php | 184 ++++++++++++ app/Services/RepuveService.php | 161 +++++++++++ config/services.php | 10 + 5 files changed, 388 insertions(+), 236 deletions(-) create mode 100644 app/Services/PadronEstatalService.php create mode 100644 app/Services/RepuveService.php diff --git a/.env.example b/.env.example index 2131894..0906027 100644 --- a/.env.example +++ b/.env.example @@ -74,6 +74,13 @@ AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false +# REPUVE FEDERAL +REPUVE_FED_URL= +REPUVE_FED_USERNAME= +REPUVE_FED_PASSWORD= + +REPUVE_EST_URL= + REVERB_APP_ID= REVERB_APP_KEY= REVERB_APP_SECRET= diff --git a/app/Http/Controllers/Repuve/InscriptionController.php b/app/Http/Controllers/Repuve/InscriptionController.php index 0772e68..35ca69d 100644 --- a/app/Http/Controllers/Repuve/InscriptionController.php +++ b/app/Http/Controllers/Repuve/InscriptionController.php @@ -15,13 +15,24 @@ use App\Models\Error; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Auth; +use App\Services\RepuveService; +use App\Services\PadronEstatalService; class InscriptionController extends Controller { + private RepuveService $repuveService; + private PadronEstatalService $padronEstatalService; - /* =========================================================== + public function __construct( + RepuveService $repuveService, + PadronEstatalService $padronEstatalService + ) { + $this->repuveService = $repuveService; + $this->padronEstatalService = $padronEstatalService; + } + + /* * Inscripción de vehículo al REPUVE - * =========================================================== */ public function vehicleInscription(VehicleStoreRequest $request) { @@ -50,7 +61,7 @@ public function vehicleInscription(VehicleStoreRequest $request) } // Verificar robo (API Repuve Nacional) - $isStolen = $this->checkIfStolen($folio); + $isStolen = $this->checkIfStolen($niv); if ($isStolen) { return ApiResponse::FORBIDDEN->response([ @@ -65,8 +76,8 @@ public function vehicleInscription(VehicleStoreRequest $request) DB::beginTransaction(); // Obtener 37 datos de API Estatal - $vehicleData = $this->getVehicle(); - $ownerData = $this->getOwner(); + $vehicleData = $this->getVehicle($niv); + $ownerData = $this->getOwner($niv); // Crear propietario $owner = Owner::updateOrCreate( @@ -283,11 +294,9 @@ public function vehicleInscription(VehicleStoreRequest $request) } } - private function checkIfStolen(string $folio): bool + private function checkIfStolen(string $niv): bool { - // Aquí api servicio de REPUVE Nacional - // simulamos con random - return (bool) rand(0, 1); + return $this->repuveService->verificarRobo($niv); } public function stolen(Request $request) @@ -347,87 +356,9 @@ public function stolen(Request $request) ]); } - private function sendToRepuveNacional(string $vin): array + private function sendToRepuveNacional(string $niv): array { - // Enviar datos a API Repuve Nacional - // Aquí se haría la llamada real a la API de Repuve Nacional - // Por ahora simulamos respuestas aleatorias usando la tabla errors - - /* $hasError = (bool) rand(0, 1); - - if ($hasError) { - // Obtener un error aleatorio de la tabla errors - $error = Error::inRandomOrder()->first(); - - if (!$error) { - // Si no hay errores en la tabla, retornar error genérico - return [ - 'has_error' => true, - 'error_code' => 'ERR_UNKNOWN', - 'error_message' => 'No hay errores registrados en el catálogo', - 'timestamp' => now()->toDateTimeString(), - 'vin' => $vin, - 'response_data' => null, - ]; - } - - return [ - 'has_error' => true, - 'error_code' => $error->code, - 'error_message' => $error->description, - 'timestamp' => now()->toDateTimeString(), - 'vin' => $vin, - 'response_data' => null, - ]; - } */ - - // Respuesta exitosa mockup REPUVE - $mockResponse = "OK:SIN INFORMACION|OTROS|SIN INFORMACION|10/07/2023|CENTRO|$vin|WSA548B|HR16777934V|2023|BLANCO|ADVANCE|TABASCO|NISSAN|MARCH|PARTICULAR|AUTOMOVIL|ACTIVO|null||null|0|null|0|10337954|E2003412012BB0C130FAD04D|Sin observaciones "; - - // Parsear la cadena a JSON - // Parsear la cadena a JSON usando los campos oficiales - $fields = [ - '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' - ]; - $values = explode('|', str_replace('OK:', '', $mockResponse)); - $jsonResponse = []; - foreach ($fields as $i => $field) { - $jsonResponse[$field] = $values[$i] ?? null; - } - - return [ - 'has_error' => false, - 'error_code' => null, - 'error_message' => null, - 'timestamp' => now()->toDateTimeString(), - 'vin' => $vin, - 'repuve_response' => $jsonResponse, - ]; + return $this->repuveService->consultarPadron($niv); } public function searchRecord(Request $request) @@ -475,156 +406,15 @@ public function searchRecord(Request $request) ]); } - private function getVehicle(): array + private function getVehicle(string $niv): array { - - $apiResponse = [ - "error" => 0, - "datos" => [[ - "entidadfederativa" => null, - "ofcexp" => "INTERNET", - "fechaexp" => "04/01/2022", - "placa" => "WNU700B", - "tarjetacir" => "2020-WNU700B", - "marca" => "CHEVROLET G.M.C.", - "submarca" => "AVEO", - "version" => "PAQ. \"A\" LS", - "clase_veh" => "AUTOMOVIL", - "tipo_veh" => "SEDAN", - "tipo_uso" => "PARTICULAR", - "modelo" => "2022", - "color" => "BLANCO", - "motor" => "H. EN WUHANLL,SGM", - "niv" => "LSGHD52H0ND032457", - "rfv" => null, - "numptas" => "4", - "observac" => null, - "tipopers" => 1, - "curp" => null, - "rfc" => "GME111116GJA", - "pasaporte" => null, - "licencia" => null, - "nombre" => "GOLSYSTEMS DE MEXICO S DE RL DE CV", - "ap_paterno" => null, - "ap_materno" => null, - "ent_fed" => "27", - "munic" => "04", - "callep" => "C BUGAMBILIAS ", - "num_ext" => "118", - "num_int" => null, - "colonia" => "FRACC. BLANCAS MARIPOSAS", - "cp" => "86179", - "cve_vehi" => "0037804", - "nrpv" => "5BK9MDO0", - "fe_act" => "10/01/2025", - "tipo_mov" => "1" - ]] - ]; - - $data = $apiResponse['datos'][0]; - - // Retornar datos mapeados para el vehículo - return [ - 'placa' => $data['placa'], - 'niv' => $data['niv'], - 'marca' => $data['marca'], - 'linea' => $data['submarca'], - 'sublinea' => $data['version'], - 'modelo' => $data['modelo'], - 'color' => $data['color'], - 'numero_motor' => $data['motor'], - 'clase_veh' => $data['clase_veh'], - 'tipo_servicio' => $data['tipo_uso'], - 'rfv' => $data['rfv'], - 'rfc' => $data['rfc'], - 'ofcexpedicion' => $data['ofcexp'], - 'fechaexpedicion' => $data['fechaexp'], - 'tipo_veh' => $data['tipo_veh'], - 'numptas' => $data['numptas'], - 'observac' => $data['observac'], - 'cve_vehi' => $data['cve_vehi'], - 'tipo_mov' => $data['tipo_mov'], - ]; + $datos = $this->padronEstatalService->getVehiculoByNiv($niv); + return $this->padronEstatalService->extraerDatosVehiculo($datos); } - private function getOwner(): array + private function getOwner(string $niv): array { - $apiResponse = [ - "error" => 0, - "datos" => [[ - "entidadfederativa" => null, - "ofcexp" => "INTERNET", - "fechaexp" => "04/01/2022", - "placa" => "WNU700B", - "tarjetacir" => "2020-WNU700B", - "marca" => "CHEVROLET G.M.C.", - "submarca" => "AVEO", - "version" => "PAQ. \"A\" LS", - "clase_veh" => "AUTOMOVIL", - "tipo_veh" => "SEDAN", - "tipo_uso" => "PARTICULAR", - "modelo" => "2022", - "color" => "BLANCO", - "motor" => "H. EN WUHANLL,SGM", - "niv" => "LSGHD52H0ND032457", - "rfv" => null, - "numptas" => "4", - "observac" => null, - "tipopers" => 1, - "curp" => null, - "rfc" => "GME111116GJA", - "pasaporte" => null, - "licencia" => null, - "nombre" => "GOLSYSTEMS DE MEXICO S DE RL DE CV", - "ap_paterno" => null, - "ap_materno" => null, - "ent_fed" => "27", - "munic" => "04", - "callep" => "C BUGAMBILIAS ", - "num_ext" => "118", - "num_int" => null, - "colonia" => "FRACC. BLANCAS MARIPOSAS", - "cp" => "86179", - "cve_vehi" => "0037804", - "nrpv" => "5BK9MDO0", - "fe_act" => "10/01/2025", - "tipo_mov" => "1" - ]] - ]; - - $data = $apiResponse['datos'][0]; - - // Construir dirección completa - $addressParts = array_filter([ - $data['callep'], - $data['num_ext'] ? "Num {$data['num_ext']}" : null, - $data['num_int'] ? "Int {$data['num_int']}" : null, - $data['colonia'], - $data['cp'] ? "CP {$data['cp']}" : null, - $data['munic'] ? "Mun {$data['munic']}" : null, - $data['ent_fed'] ? "Edo {$data['ent_fed']}" : null, - ]); - - $address = implode(', ', $addressParts); - - // Retornar datos mapeados para el propietario - return [ - 'name' => $data['nombre'], - 'paternal' => $data['ap_paterno'], - 'maternal' => $data['ap_materno'], - 'rfc' => $data['rfc'], - 'curp' => $data['curp'], - 'address' => $address, - 'tipopers' => $data['tipopers'], - 'pasaporte' => $data['pasaporte'], - 'licencia' => $data['licencia'], - 'ent_fed' => $data['ent_fed'], - 'munic' => $data['munic'], - 'callep' => $data['callep'], - 'num_ext' => $data['num_ext'], - 'num_int' => $data['num_int'], - 'colonia' => $data['colonia'], - 'cp' => $data['cp'], - ]; + $datos = $this->padronEstatalService->getVehiculoByNiv($niv); + return $this->padronEstatalService->extraerDatosPropietario($datos); } } diff --git a/app/Services/PadronEstatalService.php b/app/Services/PadronEstatalService.php new file mode 100644 index 0000000..3d45d78 --- /dev/null +++ b/app/Services/PadronEstatalService.php @@ -0,0 +1,184 @@ +soapUrl = config('services.padron_estatal.url'); + } + + public function getVehiculoByNiv(string $niv): array + { + return $this->consultarPadron('niv', $niv); + } + + public function getVehiculoByFolio(string $folio): array + { + return $this->consultarPadron('folio', $folio); + } + + /** + * Consulta el padrón vehicular estatal + */ + private function consultarPadron(string $tipo, string $valor): array + { + // Construir el Data en formato JSON + $data = json_encode([ + 'tipo' => $tipo, + 'valor' => $valor + ]); + + // Construir el cuerpo SOAP + $soapBody = << + + + + {$data} + + + + XML; + + // Configurar cURL + $ch = curl_init($this->soapUrl); + 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: ""', + 'Content-Length: ' . strlen($soapBody) + ]); + + // Ejecutar la petición + $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 al padrón estatal: {$error}"); + } + + if ($httpCode !== 200) { + throw new Exception("Error HTTP {$httpCode} al consultar padrón estatal"); + } + + // Parsear la respuesta + return $this->parsearRespuesta($response); + } + + /** + * Parsea la respuesta del padrón estatal + */ + private function parsearRespuesta(string $soapResponse): array + { + // Extraer el contenido del tag + preg_match('/(.*?)<\/result>/s', $soapResponse, $matches); + + if (!isset($matches[1])) { + throw new Exception("No se pudo extraer el resultado del SOAP del padrón estatal"); + } + + $jsonContent = trim($matches[1]); + + // Decodificar el JSON + $result = json_decode($jsonContent, true); + + if (json_last_error() !== JSON_ERROR_NONE) { + throw new Exception("Error al decodificar JSON del padrón estatal: " . json_last_error_msg()); + } + + // La respuesta es un array con un objeto que tiene error y datos + if (!isset($result[0])) { + throw new Exception("Formato de respuesta inesperado del padrón estatal"); + } + + $data = $result[0]; + + // Verificar si hay error + if ($data['error'] !== 0) { + throw new Exception("Error en consulta al padrón estatal: código {$data['error']}"); + } + + // Verificar si hay datos + if (!isset($data['datos'][0])) { + throw new Exception("No se encontraron datos del vehículo en el padrón estatal"); + } + + return $data['datos'][0]; + } + + /** + * Extrae los datos del vehículo del resultado + */ + public function extraerDatosVehiculo(array $datos): array + { + return [ + 'placa' => $datos['placa'] ?? null, + 'niv' => $datos['niv'] ?? null, + 'marca' => $datos['marca'] ?? null, + 'linea' => $datos['submarca'] ?? null, + 'sublinea' => $datos['version'] ?? null, + 'modelo' => $datos['modelo'] ?? null, + 'color' => $datos['color'] ?? null, + 'numero_motor' => $datos['motor'] ?? null, + 'clase_veh' => $datos['clase_veh'] ?? null, + 'tipo_servicio' => $datos['tipo_uso'] ?? null, + 'rfv' => $datos['rfv'] ?? null, + 'rfc' => $datos['rfc'] ?? null, + 'ofcexpedicion' => $datos['ofcexp'] ?? null, + 'fechaexpedicion' => $datos['fechaexp'] ?? null, + 'tipo_veh' => $datos['tipo_veh'] ?? null, + 'numptas' => $datos['numptas'] ?? null, + 'observac' => $datos['observac'] ?? null, + 'cve_vehi' => $datos['cve_vehi'] ?? null, + 'tipo_mov' => $datos['tipo_mov'] ?? null, + ]; + } + + /** + * Extrae los datos del propietario del resultado + */ + public function extraerDatosPropietario(array $datos): array + { + // Construir dirección completa + $addressParts = array_filter([ + $datos['callep'] ?? null, + isset($datos['num_ext']) && $datos['num_ext'] ? "Num {$datos['num_ext']}" : null, + isset($datos['num_int']) && $datos['num_int'] ? "Int {$datos['num_int']}" : null, + $datos['colonia'] ?? null, + isset($datos['cp']) && $datos['cp'] ? "CP {$datos['cp']}" : null, + isset($datos['munic']) && $datos['munic'] ? "Mun {$datos['munic']}" : null, + isset($datos['ent_fed']) && $datos['ent_fed'] ? "Edo {$datos['ent_fed']}" : null, + ]); + + $address = implode(', ', $addressParts); + + return [ + 'name' => $datos['nombre'] ?? null, + 'paternal' => $datos['ap_paterno'] ?? null, + 'maternal' => $datos['ap_materno'] ?? null, + 'rfc' => $datos['rfc'] ?? null, + 'curp' => $datos['curp'] ?? null, + 'address' => $address, + 'tipopers' => $datos['tipopers'] ?? null, + 'pasaporte' => $datos['pasaporte'] ?? null, + 'licencia' => $datos['licencia'] ?? null, + 'ent_fed' => $datos['ent_fed'] ?? null, + 'munic' => $datos['munic'] ?? null, + 'callep' => $datos['callep'] ?? null, + 'num_ext' => $datos['num_ext'] ?? null, + 'num_int' => $datos['num_int'] ?? null, + 'colonia' => $datos['colonia'] ?? null, + 'cp' => $datos['cp'] ?? null, + ]; + } +} diff --git a/app/Services/RepuveService.php b/app/Services/RepuveService.php new file mode 100644 index 0000000..b0af8c5 --- /dev/null +++ b/app/Services/RepuveService.php @@ -0,0 +1,161 @@ +apiUrl = config('services.repuve_federal.soap_url'); + $this->username = config('services.repuve_federal.username'); + $this->password = config('services.repuve_federal.password'); + } + + public function consultarPadron(string $niv) + { + $arg2 = $niv . '|||||||'; + + $soapBody = << + + + + {$this->username} + {$this->password} + {$arg2} + + + + XML; + + $ch = curl_init($this->apiUrl); + 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): array + { + preg_match('/(.*?)<\/return>/s', $soapResponse, $matches); + + if (!isset($matches[1])) { + throw new Exception("No se pudo extraer la respuesta del SOAP"); + } + + $contenido = trim($matches[1]); + + // Verificar si hay error + if (str_starts_with($contenido, 'ERROR:')) { + $errorMessage = str_replace('ERROR:', '', $contenido); + return [ + 'has_error' => true, + 'error_code' => 'REPUVE_ERROR', + 'error_message' => $errorMessage, + 'timestamp' => now()->toDateTimeString(), + 'niv' => $niv, + 'repuve_response' => null, + ]; + } + + // 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, + ]; + } + + throw new Exception("Formato de respuesta desconocido: {$contenido}"); + } + + /** + * Verifica si un vehículo está reportado como robado + */ + public function verificarRobo(string $niv): bool + { + 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; + } + } +} diff --git a/config/services.php b/config/services.php index 27a3617..d8cce6e 100644 --- a/config/services.php +++ b/config/services.php @@ -35,4 +35,14 @@ ], ], + 'repuve_federal' => [ + 'soap_url' => env('REPUVE_FED_URL'), + 'username' => env('REPUVE_FED_USERNAME'), + 'password' => env('REPUVE_FED_PASSWORD'), + ], + + 'padron_estatal' => [ + 'url' => env('REPUVE_EST_URL'), + ], + ];