Skip to content

Commit 48b1e1d

Browse files
committed
api dni
1 parent 0488a8b commit 48b1e1d

File tree

5 files changed

+203
-4
lines changed

5 files changed

+203
-4
lines changed

.env

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ AUTH_TOKEN=zutdqfxwquLzf4wckzAVr4Jl4BVuUnGDjAadQVrDsan1GoOVVl
33
CACHE_TTL_DAYS=7
44

55
DB_CONNECTION=mysql
6-
DB_HOST=127.0.0.1
7-
DB_PORT=3306
6+
DB_HOST=85.239.244.90
7+
DB_PORT=6033
88
DB_DATABASE=api-fenbot-gob
99
DB_USERNAME=root
10-
DB_PASSWORD=
10+
DB_PASSWORD=VjvC0pRo5
1111

1212
EXTERNAL_API_URL=https://api.decolecta.com
1313

app/routes.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
use App\Application\Actions\User\ListUsersAction;
66
use App\Application\Actions\User\ViewUserAction;
7+
use App\Application\Api\ReniecController;
78
use Psr\Http\Message\ResponseInterface as Response;
89
use Psr\Http\Message\ServerRequestInterface as Request;
910
use Slim\App;
1011
use Slim\Interfaces\RouteCollectorProxyInterface as Group;
1112
use App\Application\Api\SunatController;
13+
1214
return function (App $app) {
1315
$app->options('/{routes:.*}', function (Request $request, Response $response) {
1416
// CORS Pre-Flight OPTIONS Request Handler
@@ -26,4 +28,5 @@
2628
});
2729

2830
$app->get('/api/ruc/{ruc}', [SunatController::class, 'lookup']);
31+
$app->get('/api/dni/{dni}', [ReniecController::class, 'lookup']);
2932
};
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?php
2+
3+
namespace App\Application\Api;
4+
5+
use Psr\Http\Message\ResponseInterface as Response;
6+
use Psr\Http\Message\ServerRequestInterface as Request;
7+
use App\Models\ApiToken;
8+
use App\Models\DniCache;
9+
use GuzzleHttp\Client;
10+
use GuzzleHttp\Exception\RequestException;
11+
12+
class ReniecController
13+
{
14+
15+
public function lookup(Request $request, Response $response, array $args): Response
16+
{
17+
$dni = $args['dni'];
18+
19+
// 1. Validar token del header
20+
$authHeader = $request->getHeaderLine('Authorization');
21+
$token = trim(str_replace('Bearer', '', $authHeader));
22+
23+
if (empty($token) || $token !== $_ENV['AUTH_TOKEN']) {
24+
return $this->json($response, [
25+
'success' => false,
26+
'message' => 'No se encuentra autenticado'
27+
], 401);
28+
}
29+
30+
// 2. Revisar cache
31+
$record = DniCache::where('document_number', $dni)->first();
32+
$ttl = intval($_ENV['CACHE_TTL_DAYS'] ?? 7) * 86400;
33+
34+
if ($record && $record->fecha_registro && (time() - strtotime($record->fecha_registro)) < $ttl) {
35+
// Construir respuesta desde cache
36+
return $this->json($response, $this->buildSuccessResponse($record->toArray()));
37+
}
38+
39+
// 3. Obtener token disponible
40+
$apiTokenRecord = ApiToken::getAvailableToken();
41+
42+
if (!$apiTokenRecord) {
43+
return $this->json($response, [
44+
'success' => false,
45+
'message' => 'No hay tokens disponibles este mes'
46+
], 503);
47+
}
48+
49+
// 4. Consultar API intermedia
50+
$remoteResponse = $this->remoteQuery($dni, $apiTokenRecord->token);
51+
52+
// 5. Verificar si hay error en la respuesta del API
53+
if (isset($remoteResponse['error'])) {
54+
return $this->json($response, [
55+
'success' => false,
56+
'message' => $remoteResponse['message'] ?? $remoteResponse['error'] ?? 'Error desconocido'
57+
]);
58+
}
59+
60+
// 6. Incrementar contador del token usado
61+
$apiTokenRecord->incrementCounter();
62+
63+
// 7. Guardar/actualizar cache
64+
if ($record) {
65+
// Actualizar registro existente
66+
$record->update([
67+
'first_name' => $remoteResponse['first_name'],
68+
'first_last_name' => $remoteResponse['first_last_name'],
69+
'second_last_name' => $remoteResponse['second_last_name'],
70+
'full_name' => $remoteResponse['full_name'],
71+
'document_number' => $remoteResponse['document_number'],
72+
'fecha_registro' => date("Y-m-d H:i:s")
73+
]);
74+
} else {
75+
// Crear nuevo registro
76+
DniCache::create([
77+
'first_name' => $remoteResponse['first_name'],
78+
'first_last_name' => $remoteResponse['first_last_name'],
79+
'second_last_name' => $remoteResponse['second_last_name'],
80+
'full_name' => $remoteResponse['full_name'],
81+
'document_number' => $remoteResponse['document_number'],
82+
'fecha_registro' => date("Y-m-d H:i:s")
83+
]);
84+
}
85+
86+
// 8. Devolver respuesta formateada
87+
return $this->json($response, $this->buildSuccessResponse($remoteResponse));
88+
}
89+
90+
private function buildSuccessResponse(array $data): array
91+
{
92+
93+
$ubigeoArray = [null, null, null];
94+
95+
return [
96+
'success' => true,
97+
'data' => [
98+
"numero" => $data['document_number'],
99+
"nombre_completo" => $data['full_name'],
100+
"nombres" => $data['first_name'],
101+
"apellido_paterno" => $data['first_last_name'],
102+
"apellido_materno" => $data['second_last_name'],
103+
"direccion" => '',
104+
"direccion_completa" => '',
105+
"departamento" => '',
106+
"provincia" => '',
107+
"distrito" => '',
108+
"codigo_verificacion" => '',
109+
"ubigeo_inei" => '',
110+
"location_id" => $ubigeoArray,
111+
"ubigeo" => $ubigeoArray
112+
]
113+
];
114+
}
115+
116+
private function json(Response $response, $data, int $status = 200): Response
117+
{
118+
$payload = json_encode($data);
119+
$response->getBody()->write($payload);
120+
return $response->withHeader('Content-Type', 'application/json')->withStatus($status);
121+
}
122+
123+
private function remoteQuery(string $dni, string $apiToken): array
124+
{
125+
126+
$url = rtrim($_ENV['EXTERNAL_API_URL'], '?') . '/v1/reniec/dni?numero=' . urlencode($dni);
127+
128+
$client = new Client([
129+
'timeout' => 10,
130+
'headers' => [
131+
'Authorization' => 'Bearer ' . $apiToken,
132+
'Accept' => 'application/json',
133+
]
134+
]);
135+
136+
try {
137+
$response = $client->get($url);
138+
$statusCode = $response->getStatusCode();
139+
140+
$body = (string) $response->getBody();
141+
$data = json_decode($body, true);
142+
143+
// Si el API devuelve 422, significa DNI no válido
144+
if ($statusCode === 422) {
145+
return [
146+
'error' => 'DNI no valido',
147+
'message' => $data['message'] ?? 'dni no valido'
148+
];
149+
}
150+
151+
// Verificar que la respuesta tenga los datos esperados
152+
if (!isset($data['document_number']) || empty($data['document_number'])) {
153+
return [
154+
'error' => 'Respuesta inválida del API externo'
155+
];
156+
}
157+
158+
return $data;
159+
} catch (RequestException $e) {
160+
// Verificar si es un error 422 específicamente
161+
if ($e->hasResponse() && $e->getResponse()->getStatusCode() === 422) {
162+
return [
163+
'error' => 'DNI no valido',
164+
'message' => 'dni no valido'
165+
];
166+
}
167+
168+
return [
169+
'error' => 'Error al consultar la API externa: ' . $e->getMessage()
170+
];
171+
} catch (\Throwable $e) {
172+
return [
173+
'error' => 'Error inesperado: ' . $e->getMessage()
174+
];
175+
}
176+
}
177+
}

src/Application/Api/SunatController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function lookup(Request $request, Response $response, array $args): Respo
3232

3333
// 2. Revisar cache
3434
$record = RucCache::where('numero_documento', $ruc)->first();
35-
$ttl = intval($_ENV['CACHE_TTL_SECONDS'] ?? 86400); // 1 día por defecto
35+
$ttl = intval($_ENV['CACHE_TTL_DAYS'] ?? 7) * 86400;
3636

3737
if ($record && $record->fecha_registro && (time() - strtotime($record->fecha_registro)) < $ttl) {
3838
// Construir respuesta desde cache

src/Models/DniCache.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace App\Models;
4+
5+
use Illuminate\Database\Eloquent\Model;
6+
7+
class DniCache extends Model
8+
{
9+
public $timestamps = false;
10+
protected $table = 'dni_cache';
11+
protected $fillable = [
12+
'first_name',
13+
'first_last_name',
14+
'second_last_name',
15+
'full_name',
16+
'document_number',
17+
'fecha_registro'
18+
];
19+
}

0 commit comments

Comments
 (0)