ADD: Implementar servicios para consulta de REPUVE

This commit is contained in:
Juan Felipe Zapata Moreno 2025-11-21 12:33:50 -06:00
parent 1060435f52
commit 3640fc8a13
5 changed files with 388 additions and 236 deletions

View File

@ -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=

View File

@ -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);
}
}

View File

@ -0,0 +1,184 @@
<?php
namespace App\Services;
use Exception;
class PadronEstatalService
{
private string $soapUrl;
public function __construct()
{
$this->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 = <<<XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://mx/tgc/ConsultaPadronVehicular.wsdl">
<soapenv:Header/>
<soapenv:Body>
<wsdl:getVehiculosRepuve>
<Data>{$data}</Data>
</wsdl:getVehiculosRepuve>
</soapenv:Body>
</soapenv:Envelope>
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>(.*?)<\/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,
];
}
}

View File

@ -0,0 +1,161 @@
<?php
namespace App\Services;
use Exception;
class RepuveService
{
private string $apiUrl;
private string $username;
private string $password;
public function __construct()
{
$this->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 = <<<XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://consultaRpv.org/wsdl">
<soapenv:Header/>
<soapenv:Body>
<wsdl:doConsPadron>
<arg0>{$this->username}</arg0>
<arg1>{$this->password}</arg1>
<arg2>{$arg2}</arg2>
</wsdl:doConsPadron>
</soapenv:Body>
</soapenv:Envelope>
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>(.*?)<\/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;
}
}
}

View File

@ -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'),
],
];