verificar.php: - Pública, sin autenticación ni menú lateral - Diseño moderno: card glassmorphism sobre gradiente brand - Flujo en 3 pasos con indicador animado - Paso 1: ingresa cédula → busca paciente - Paso 2: inputs OTP de 6 dígitos con navegación automática y pegado desde portapapeles; cuenta regresiva + reenvío a los 60s - Paso 3: pantalla de éxito con animación api/public/send_otp.php: - Busca paciente por cédula, genera OTP aleatorio de 6 dígitos - Guarda en lab_otp_tokens con expiración 5 min - Envía SMS via WEBSms usando config de lab_config - Retorna teléfono enmascarado (3**1234**67) api/public/verify_otp.php: - Valida cedula+codigo contra lab_otp_tokens activos - Marca OTP como usado y paciente como verificado (telefono_verificado=1, verificado_at=NOW()) DB: - lab_pacientes: +telefono_verificado, +verificado_at - lab_otp_tokens: tabla nueva para almacenar OTPs temporales Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
57 lines
2.0 KiB
PHP
57 lines
2.0 KiB
PHP
<?php
|
|
/**
|
|
* POST /api/public/verify_otp.php
|
|
* Público — no requiere autenticación.
|
|
* Body JSON: { cedula: "12345678", codigo: "123456" }
|
|
*/
|
|
ob_start();
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
error_reporting(0);
|
|
|
|
require_once __DIR__ . '/../../config/config.php';
|
|
|
|
function jpubOk(array $data = []): void { ob_clean(); echo json_encode(['ok' => true] + $data); exit; }
|
|
function jpubErr(string $msg, int $code = 400): void {
|
|
ob_clean(); http_response_code($code);
|
|
echo json_encode(['ok' => false, 'error' => $msg]); exit;
|
|
}
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') jpubErr('Método no permitido', 405);
|
|
|
|
$datos = json_decode(file_get_contents('php://input'), true);
|
|
$cedula = trim($datos['cedula'] ?? '');
|
|
$codigo = trim($datos['codigo'] ?? '');
|
|
|
|
if (!$cedula) jpubErr('Documento requerido.');
|
|
if (!preg_match('/^\d{6}$/', $codigo)) jpubErr('Código inválido.');
|
|
|
|
try {
|
|
$db = Database::getInstance();
|
|
$pac = $db->fetch("SELECT id, nombre_completo FROM lab_pacientes WHERE numero_documento = ? AND is_active = 1", [$cedula]);
|
|
} catch (\Throwable $_) { jpubErr('Error de base de datos.', 500); }
|
|
|
|
if (!$pac) jpubErr('Paciente no encontrado.');
|
|
|
|
// Buscar OTP válido
|
|
try {
|
|
$otp = $db->fetch(
|
|
"SELECT id FROM lab_otp_tokens
|
|
WHERE paciente_id = ? AND codigo = ? AND usado = 0 AND expires_at > NOW()
|
|
ORDER BY id DESC LIMIT 1",
|
|
[$pac['id'], $codigo]
|
|
);
|
|
} catch (\Throwable $_) { jpubErr('Error de base de datos.', 500); }
|
|
|
|
if (!$otp) jpubErr('Código incorrecto o expirado. Solicita uno nuevo.');
|
|
|
|
// Marcar OTP como usado y paciente como verificado
|
|
try {
|
|
$db->query("UPDATE lab_otp_tokens SET usado = 1 WHERE id = ?", [$otp['id']]);
|
|
$db->query(
|
|
"UPDATE lab_pacientes SET telefono_verificado = 1, verificado_at = NOW() WHERE id = ?",
|
|
[$pac['id']]
|
|
);
|
|
} catch (\Throwable $_) { jpubErr('Error al guardar verificación.', 500); }
|
|
|
|
jpubOk(['nombre' => explode(' ', $pac['nombre_completo'])[0]]);
|