This commit is contained in:
lizandrogd
2026-01-13 00:31:22 -05:00
parent 1abe5bba2d
commit 91b74db234
2 changed files with 586 additions and 18 deletions
+366
View File
@@ -0,0 +1,366 @@
<?php
/**
* Test de Webhook - Diagnóstico completo
* Fecha: 13 de enero de 2026
*/
require_once 'config/config.php';
// Verificar autenticación
if (!isUserLoggedIn()) {
header('Location: login.php');
exit;
}
header('Content-Type: text/html; charset=utf-8');
?>
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Diagnóstico de Webhook - WhatsApp Bot</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body { background: #f8f9fa; padding: 20px; }
.test-card { background: white; border-radius: 10px; padding: 20px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.ok { color: #28a745; font-weight: bold; }
.error { color: #dc3545; font-weight: bold; }
.warning { color: #ffc107; font-weight: bold; }
.info { color: #17a2b8; font-weight: bold; }
.code-box { background: #f8f9fa; border: 1px solid #dee2e6; border-radius: 5px; padding: 15px; margin: 10px 0; font-family: 'Courier New', monospace; font-size: 12px; }
.badge-status { font-size: 14px; padding: 8px 15px; }
</style>
</head>
<body>
<div class="container">
<div class="test-card">
<h1 class="mb-4">
<i class="fas fa-plug"></i> Diagnóstico de Webhook WhatsApp
</h1>
<p class="text-muted">Verificación completa del estado del webhook y configuración</p>
</div>
<?php
$db = Database::getInstance();
$allOk = true;
// Test 1: Verificar configuración
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-cog'></i> 1. Configuración del Sistema</h3>";
$config = [
'WHATSAPP_TOKEN' => defined('WHATSAPP_TOKEN') ? WHATSAPP_TOKEN : null,
'WHATSAPP_PHONE_NUMBER_ID' => defined('WHATSAPP_PHONE_NUMBER_ID') ? WHATSAPP_PHONE_NUMBER_ID : null,
'WEBHOOK_VERIFY_TOKEN' => defined('WEBHOOK_VERIFY_TOKEN') ? WEBHOOK_VERIFY_TOKEN : null,
'WHATSAPP_API_URL' => defined('WHATSAPP_API_URL') ? WHATSAPP_API_URL : null,
];
echo "<table class='table table-sm'>";
foreach ($config as $key => $value) {
$status = !empty($value) ? 'ok' : 'error';
$icon = !empty($value) ? '✅' : '❌';
$displayValue = !empty($value) ? (strlen($value) > 30 ? substr($value, 0, 30) . '...' : $value) : 'NO CONFIGURADO';
if ($status === 'error') $allOk = false;
echo "<tr>";
echo "<td class='$status'>$icon $key</td>";
echo "<td><code>$displayValue</code></td>";
echo "</tr>";
}
echo "</table>";
echo "</div>";
// Test 2: URL del Webhook
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-link'></i> 2. URL del Webhook</h3>";
$protocol = isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$webhookUrl = $protocol . '://' . $host . '/api/webhook.php';
echo "<div class='alert alert-info'>";
echo "<strong>URL para configurar en Meta/Facebook:</strong><br>";
echo "<code style='font-size: 14px;'>$webhookUrl</code>";
echo "<button class='btn btn-sm btn-outline-primary ms-2' onclick='navigator.clipboard.writeText(\"$webhookUrl\")'>
<i class='fas fa-copy'></i> Copiar
</button>";
echo "</div>";
// Verificar si el archivo existe
$webhookFile = __DIR__ . '/api/webhook.php';
if (file_exists($webhookFile)) {
echo "<p class='ok'>✅ Archivo webhook.php existe</p>";
} else {
echo "<p class='error'>❌ Archivo webhook.php NO encontrado</p>";
$allOk = false;
}
echo "</div>";
// Test 3: Verificación GET
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-check-circle'></i> 3. Test de Verificación (GET)</h3>";
$verifyUrl = $webhookUrl . '?hub.mode=subscribe&hub.verify_token=' . urlencode(WEBHOOK_VERIFY_TOKEN) . '&hub.challenge=test_challenge_12345';
echo "<p><strong>URL de prueba:</strong></p>";
echo "<div class='code-box'>" . htmlspecialchars($verifyUrl) . "</div>";
echo "<div class='alert alert-info'>";
echo "<strong>💡 Nota:</strong> Este test intenta verificar el webhook desde el servidor mismo. ";
echo "Si estás en localhost, es normal que falle. Lo importante es que el archivo exista y esté configurado correctamente.";
echo "</div>";
// Intentar hacer la verificación solo si no es localhost
$isLocalhost = (strpos($host, 'localhost') !== false || strpos($host, '127.0.0.1') !== false);
if (!$isLocalhost) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $verifyUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($httpCode == 200 && $response === 'test_challenge_12345') {
echo "<p class='ok'>✅ Verificación GET exitosa - El webhook responde correctamente</p>";
echo "<p>Respuesta: <code>$response</code></p>";
} else {
echo "<p class='error'>❌ Error en verificación GET</p>";
echo "<p>Código HTTP: <code>$httpCode</code></p>";
echo "<p>Respuesta: <code>" . htmlspecialchars($response) . "</code></p>";
if ($error) echo "<p>Error cURL: <code>$error</code></p>";
$allOk = false;
}
} else {
// Test manual para localhost
echo "<div class='alert alert-warning'>";
echo "<strong>⚠️ Entorno Local Detectado</strong><br>";
echo "No se puede hacer la verificación automática en localhost. ";
echo "En su lugar, puedes probar manualmente:";
echo "</div>";
echo "<ol>";
echo "<li>Haz clic en el botón de abajo para probar el webhook</li>";
echo "<li>Deberías ver el texto: <code>test_challenge_12345</code></li>";
echo "</ol>";
echo "<a href='$verifyUrl' target='_blank' class='btn btn-primary mb-3'>";
echo "<i class='fas fa-external-link-alt'></i> Probar Webhook Manualmente";
echo "</a>";
// Verificar el archivo directamente
$webhookFilePath = __DIR__ . '/api/webhook.php';
if (file_exists($webhookFilePath)) {
echo "<p class='ok'>✅ El archivo webhook.php existe y es accesible</p>";
// Verificar permisos de lectura
if (is_readable($webhookFilePath)) {
echo "<p class='ok'>✅ El archivo tiene permisos de lectura</p>";
// Verificar que contenga el código necesario
$webhookContent = file_get_contents($webhookFilePath);
if (strpos($webhookContent, 'hub_verify_token') !== false &&
strpos($webhookContent, 'hub_challenge') !== false) {
echo "<p class='ok'>✅ El archivo contiene el código de verificación correcto</p>";
} else {
echo "<p class='warning'>⚠️ El archivo puede no contener el código de verificación</p>";
}
} else {
echo "<p class='error'>❌ El archivo no tiene permisos de lectura</p>";
$allOk = false;
}
}
}
echo "</div>";
// Test 4: Estructura de tablas
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-database'></i> 4. Estructura de Base de Datos</h3>";
$requiredTables = ['users', 'conversations', 'menus', 'menu_options', 'autoresponses', 'webhook_logs'];
echo "<table class='table table-sm'>";
foreach ($requiredTables as $table) {
$exists = $db->fetchAll("SHOW TABLES LIKE '$table'");
$status = !empty($exists) ? 'ok' : 'error';
$icon = !empty($exists) ? '✅' : '❌';
if ($status === 'error') $allOk = false;
echo "<tr>";
echo "<td class='$status'>$icon Tabla: <strong>$table</strong></td>";
if (!empty($exists)) {
// Contar registros
$count = $db->fetch("SELECT COUNT(*) as total FROM $table");
echo "<td><span class='badge bg-info'>{$count['total']} registros</span></td>";
} else {
echo "<td><span class='badge bg-danger'>NO EXISTE</span></td>";
}
echo "</tr>";
}
echo "</table>";
echo "</div>";
// Test 5: Últimos mensajes recibidos
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-inbox'></i> 5. Mensajes Recibidos Recientemente</h3>";
$recentMessages = $db->fetchAll(
"SELECT c.*, u.phone_number, u.name
FROM conversations c
LEFT JOIN users u ON c.user_id = u.id
WHERE c.direction = 'incoming'
ORDER BY c.created_at DESC
LIMIT 5"
);
if (!empty($recentMessages)) {
echo "<p class='ok'>✅ Se encontraron mensajes entrantes</p>";
echo "<table class='table table-sm table-striped'>";
echo "<thead><tr><th>Fecha</th><th>Usuario</th><th>Mensaje</th><th>Estado</th></tr></thead>";
echo "<tbody>";
foreach ($recentMessages as $msg) {
$date = date('d/m/Y H:i', strtotime($msg['created_at']));
$phone = $msg['phone_number'] ?? 'Desconocido';
$content = htmlspecialchars(substr($msg['content'], 0, 50));
$status = $msg['status'];
echo "<tr>";
echo "<td>$date</td>";
echo "<td>$phone</td>";
echo "<td>$content</td>";
echo "<td><span class='badge bg-success'>$status</span></td>";
echo "</tr>";
}
echo "</tbody></table>";
} else {
echo "<p class='warning'>⚠️ No se encontraron mensajes entrantes. El webhook puede no estar recibiendo datos.</p>";
echo "<div class='alert alert-warning'>";
echo "<strong>Posibles causas:</strong><ul>";
echo "<li>El webhook no está configurado en Meta/Facebook</li>";
echo "<li>La URL del webhook es incorrecta</li>";
echo "<li>El servidor no es accesible desde Internet</li>";
echo "<li>Aún no se han enviado mensajes de prueba</li>";
echo "</ul></div>";
}
echo "</div>";
// Test 6: Webhook Logs
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-file-alt'></i> 6. Logs del Webhook</h3>";
$webhookLogs = $db->fetchAll(
"SELECT * FROM webhook_logs
ORDER BY created_at DESC
LIMIT 5"
);
if (!empty($webhookLogs)) {
echo "<p class='ok'>✅ Se encontraron logs del webhook</p>";
echo "<table class='table table-sm'>";
echo "<thead><tr><th>Fecha</th><th>Status</th><th>IP</th><th>Detalles</th></tr></thead>";
echo "<tbody>";
foreach ($webhookLogs as $log) {
$date = date('d/m/Y H:i:s', strtotime($log['created_at']));
$status = $log['status_code'];
$ip = $log['ip_address'];
$statusClass = $status == 200 ? 'success' : 'danger';
echo "<tr>";
echo "<td>$date</td>";
echo "<td><span class='badge bg-$statusClass'>$status</span></td>";
echo "<td>$ip</td>";
echo "<td><button class='btn btn-sm btn-outline-info' onclick='alert(\"Request: " . addslashes(substr($log['request_body'], 0, 200)) . "\")'>Ver</button></td>";
echo "</tr>";
}
echo "</tbody></table>";
} else {
echo "<p class='info'>️ No hay logs del webhook registrados</p>";
}
echo "</div>";
// Test 7: Configuración de Menús y Respuestas
echo "<div class='test-card'>";
echo "<h3><i class='fas fa-robot'></i> 7. Configuración del Bot</h3>";
$menuCount = $db->fetch("SELECT COUNT(*) as total FROM menus WHERE is_active = 1");
$optionCount = $db->fetch("SELECT COUNT(*) as total FROM menu_options WHERE is_active = 1");
$autoResponseCount = $db->fetch("SELECT COUNT(*) as total FROM autoresponses WHERE is_active = 1");
echo "<div class='row'>";
echo "<div class='col-md-4'>";
echo "<div class='alert alert-info text-center'>";
echo "<h4>{$menuCount['total']}</h4>";
echo "<p>Menús Activos</p>";
echo "</div>";
echo "</div>";
echo "<div class='col-md-4'>";
echo "<div class='alert alert-info text-center'>";
echo "<h4>{$optionCount['total']}</h4>";
echo "<p>Opciones de Menú</p>";
echo "</div>";
echo "</div>";
echo "<div class='col-md-4'>";
echo "<div class='alert alert-info text-center'>";
echo "<h4>{$autoResponseCount['total']}</h4>";
echo "<p>Respuestas Automáticas</p>";
echo "</div>";
echo "</div>";
echo "</div>";
echo "</div>";
// Resumen final
echo "<div class='test-card'>";
if ($allOk) {
echo "<div class='alert alert-success'>";
echo "<h4><i class='fas fa-check-circle'></i> ¡Sistema Configurado Correctamente!</h4>";
echo "<p>El webhook está listo para recibir mensajes de WhatsApp.</p>";
echo "</div>";
} else {
echo "<div class='alert alert-danger'>";
echo "<h4><i class='fas fa-exclamation-triangle'></i> Se encontraron problemas</h4>";
echo "<p>Revisa los errores anteriores y corrígelos antes de continuar.</p>";
echo "</div>";
}
echo "<div class='mt-3'>";
echo "<h5>Próximos pasos:</h5>";
echo "<ol>";
echo "<li>Copia la URL del webhook y configúrala en Meta Developer Console</li>";
echo "<li>Usa el token de verificación: <code>" . WEBHOOK_VERIFY_TOKEN . "</code></li>";
echo "<li>Envía un mensaje de prueba desde WhatsApp</li>";
echo "<li>Verifica que aparezca en la sección de mensajes recibidos</li>";
echo "</ol>";
echo "</div>";
echo "</div>";
?>
<div class="text-center mt-4">
<a href="index.php" class="btn btn-primary">
<i class="fas fa-home"></i> Volver al Dashboard
</a>
<button onclick="location.reload()" class="btn btn-secondary">
<i class="fas fa-sync"></i> Recargar Test
</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>