diff --git a/add_hello_world.php b/add_hello_world.php new file mode 100644 index 0000000..b3b7a56 --- /dev/null +++ b/add_hello_world.php @@ -0,0 +1,20 @@ +query( + "INSERT INTO message_templates (name, template_name, language_code, status) VALUES (?, ?, ?, ?)", + ['Hello World Template', 'hello_world', 'en_US', 'approved'] +); + +echo "✅ Plantilla agregada exitosamente\n"; + +// Verificar +$stmt = $db->query("SELECT template_name, language_code, status FROM message_templates"); +$templates = $stmt->fetchAll(); + +foreach ($templates as $template) { + echo "• {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n"; +} +?> \ No newline at end of file diff --git a/add_working_template.php b/add_working_template.php new file mode 100644 index 0000000..7f16605 --- /dev/null +++ b/add_working_template.php @@ -0,0 +1,42 @@ +query( + "INSERT INTO message_templates (template_name, language_code, status, body_text, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())", + ['hello_world', 'en_US', 'approved', 'Hello World! This is a test message from WhatsApp Business API.'] + ); + + echo "✅ Agregada plantilla 'hello_world' (en_US) - FUNCIONA CORRECTAMENTE\n\n"; + + // Mostrar plantillas finales + echo "📋 Plantillas en la base de datos:\n"; + $stmt = $db->query("SELECT id, template_name, language_code, status, body_text FROM message_templates ORDER BY template_name"); + $templates = $stmt->fetchAll(); + + foreach ($templates as $template) { + echo "• ID: {$template['id']}\n"; + echo " Nombre: {$template['template_name']}\n"; + echo " Idioma: {$template['language_code']}\n"; + echo " Estado: {$template['status']}\n"; + echo " Texto: {$template['body_text']}\n"; + echo " -----\n"; + } + + echo "\n🎉 Ahora puedes enviar mensajes usando la plantilla 'hello_world'\n"; + +} catch (Exception $e) { + echo "❌ Error: " . $e->getMessage() . "\n"; +} +?> \ No newline at end of file diff --git a/api/get_conversation_detail.php b/api/get_conversation_detail.php new file mode 100644 index 0000000..b2ce04f --- /dev/null +++ b/api/get_conversation_detail.php @@ -0,0 +1,96 @@ + 'user_id es requerido']); + exit; + } + + $db = Database::getInstance(); + + // Obtener información del usuario + $user = $db->fetch( + "SELECT id, phone_number, name FROM users WHERE id = ?", + [$user_id] + ); + + if (!$user) { + http_response_code(404); + echo json_encode(['error' => 'Usuario no encontrado']); + exit; + } + + // Obtener todos los mensajes de la conversación + $messages = $db->fetchAll( + "SELECT + id, + message_id, + direction, + message_type, + content, + status, + created_at + FROM conversations + WHERE user_id = ? + ORDER BY created_at ASC", + [$user_id] + ); + + // Formatear mensajes + $messages = array_map(function($msg) { + return [ + 'id' => intval($msg['id']), + 'message_id' => $msg['message_id'] ?? '', + 'direction' => $msg['direction'], + 'message_type' => $msg['message_type'], + 'content' => $msg['content'] ?? '', + 'status' => $msg['status'] ?? 'sent', + 'created_at' => $msg['created_at'], + 'time' => date('H:i', strtotime($msg['created_at'])), + 'date' => date('d/m/Y', strtotime($msg['created_at'])) + ]; + }, $messages); + + // Marcar mensajes como leídos + $db->query( + "UPDATE conversations SET status = 'read' WHERE user_id = ? AND direction = 'incoming' AND status != 'read'", + [$user_id] + ); + + echo json_encode([ + 'success' => true, + 'user' => [ + 'id' => intval($user['id']), + 'phone_number' => $user['phone_number'], + 'name' => $user['name'] ?? $user['phone_number'] + ], + 'messages' => $messages, + 'total_messages' => count($messages) + ]); + +} catch (Exception $e) { + error_log("Error in get_conversation_detail.php: " . $e->getMessage()); + http_response_code(500); + echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]); +} +?> \ No newline at end of file diff --git a/api/get_conversation_list.php b/api/get_conversation_list.php new file mode 100644 index 0000000..32c9201 --- /dev/null +++ b/api/get_conversation_list.php @@ -0,0 +1,95 @@ +fetchAll( + "SELECT + u.id as user_id, + u.phone_number, + COALESCE(u.name, u.phone_number) as name, + c.content as last_message, + c.direction as last_direction, + c.message_type as last_message_type, + c.created_at as last_message_time, + c.status as last_message_status, + COUNT(*) as total_messages, + SUM(CASE WHEN c.direction = 'incoming' AND c.status = 'received' THEN 1 ELSE 0 END) as unread_count + FROM users u + LEFT JOIN conversations c ON u.id = c.user_id + WHERE c.id IN ( + SELECT MAX(id) + FROM conversations + GROUP BY user_id + ) + GROUP BY u.id, u.phone_number, u.name, c.content, c.direction, c.message_type, c.created_at, c.status + ORDER BY c.created_at DESC + LIMIT 50" + ); + + // Si no hay datos, retornar array vacío + if (empty($conversations)) { + $conversations = []; + } + + // Formatear datos para el frontend + $conversations = array_map(function($conv) { + return [ + 'user_id' => intval($conv['user_id']), + 'phone_number' => $conv['phone_number'], + 'name' => $conv['name'], + 'last_message' => $conv['last_message'] ?? '', + 'last_direction' => $conv['last_direction'] ?? 'incoming', + 'last_message_type' => $conv['last_message_type'] ?? 'text', + 'last_message_time' => $conv['last_message_time'], + 'last_message_status' => $conv['last_message_status'] ?? 'sent', + 'total_messages' => intval($conv['total_messages']), + 'unread_count' => intval($conv['unread_count']), + 'time_ago' => timeAgo($conv['last_message_time']) + ]; + }, $conversations); + + echo json_encode([ + 'success' => true, + 'conversations' => $conversations, + 'total' => count($conversations) + ]); + +} catch (Exception $e) { + error_log("Error in get_conversation_list.php: " . $e->getMessage()); + http_response_code(500); + echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]); +} + +/** + * Calcular tiempo transcurrido + */ +function timeAgo($datetime) { + $time = time() - strtotime($datetime); + + if ($time < 60) return 'Ahora'; + if ($time < 3600) return floor($time/60) . 'm'; + if ($time < 86400) return floor($time/3600) . 'h'; + if ($time < 2592000) return floor($time/86400) . 'd'; + if ($time < 31536000) return floor($time/2592000) . ' mes'; + return floor($time/31536000) . ' año'; +} +?> \ No newline at end of file diff --git a/api/send_message.php b/api/send_message.php index 0239aed..ccf1385 100644 --- a/api/send_message.php +++ b/api/send_message.php @@ -18,21 +18,38 @@ if (!$debugMode) { header('Content-Type: application/json; charset=utf-8'); header('Access-Control-Allow-Origin: *'); -header('Access-Control-Allow-Methods: POST'); +header('Access-Control-Allow-Methods: POST, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type'); +// Manejar preflight OPTIONS request +if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { + http_response_code(200); + exit; +} + if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); - echo json_encode(['error' => 'Método no permitido']); + echo json_encode(['error' => 'Método no permitido. Use POST.', 'method_received' => $_SERVER['REQUEST_METHOD']]); exit; } try { - $input = json_decode(file_get_contents('php://input'), true); + $rawInput = file_get_contents('php://input'); + $input = json_decode($rawInput, true); + + // Debug logging + if ($debugMode) { + error_log("=== SEND MESSAGE DEBUG ==="); + error_log("Method: " . $_SERVER['REQUEST_METHOD']); + error_log("Content-Type: " . ($_SERVER['CONTENT_TYPE'] ?? 'not set')); + error_log("Raw input: " . $rawInput); + error_log("Parsed JSON: " . json_encode($input)); + error_log("========================"); + } if (!$input) { http_response_code(400); - echo json_encode(['error' => 'JSON inválido']); + echo json_encode(['error' => 'JSON inválido o vacío', 'raw_input' => $rawInput]); exit; } @@ -241,6 +258,49 @@ try { error_log("Response: " . json_encode($response)); error_log("======================================"); + // Guardar mensaje en la base de datos + try { + // Buscar o crear usuario por teléfono + $stmt = $db->query( + "SELECT id FROM users WHERE phone_number = ? LIMIT 1", + [$recipient] + ); + $user = $stmt->fetch(); + + $userId = null; + if ($user) { + $userId = $user['id']; + } else { + // Crear nuevo usuario + $stmt = $db->query( + "INSERT INTO users (phone_number, name, created_at) VALUES (?, ?, NOW())", + [$recipient, $recipient, date('Y-m-d H:i:s')] + ); + $userId = $db->lastInsertId(); + } + + if ($userId) { + // Preparar datos del mensaje + $messageContent = ''; + $messageType = $type; + + if ($type === 'text') { + $messageContent = $input['message']; + } elseif ($type === 'template') { + $templateName = $input['template'] ?? $input['template_name'] ?? ''; + $messageContent = "Plantilla: {$templateName}"; + } + + // Guardar en la tabla conversations + $stmt = $db->query( + "INSERT INTO conversations (user_id, content, direction, message_type, status, message_id, created_at) VALUES (?, ?, 'outgoing', ?, 'sent', ?, NOW())", + [$userId, $messageContent, $messageType, $response['messages'][0]['id'] ?? null] + ); + } + } catch (Exception $e) { + error_log("Error guardando mensaje en base de datos: " . $e->getMessage()); + } + echo json_encode([ 'success' => true, 'message' => 'Mensaje enviado correctamente', diff --git a/assets/css/styles.css b/assets/css/styles.css index 4f78c33..ee725a3 100644 --- a/assets/css/styles.css +++ b/assets/css/styles.css @@ -782,4 +782,304 @@ body { box-shadow: none; border: 1px solid #ddd; } +} + +/* ================================= + CHAT STYLES + ================================= */ + +.chat-container { + background: linear-gradient(135deg, #e5ddd5 0%, #d4c5b9 100%); +} + +.conversation-row:hover { + background-color: #f8f9fa !important; + cursor: pointer; +} + +.message-bubble { + position: relative; + word-wrap: break-word; + border: none; +} + +.message-bubble.outgoing { + margin-left: auto; +} + +.message-bubble.incoming { + margin-right: auto; +} + +.message-time { + text-align: right; + margin-top: 4px; + font-size: 0.7rem; +} + +.avatar-placeholder { + font-weight: bold; + font-size: 16px; + flex-shrink: 0; +} + +/* Modal de chat personalizado */ +#chatModal .modal-content { + border: none; + border-radius: 12px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +} + +#chatModal .modal-header { + border-radius: 12px 12px 0 0; + border-bottom: 1px solid rgba(255,255,255,0.2); +} + +#chatModal .modal-body { + padding: 0; +} + +.message-bubble.bg-success { + background: linear-gradient(135deg, #25d366, #20b358) !important; +} + +.message-bubble.bg-white { + background: #ffffff !important; + border: 1px solid #e9ecef; +} + +/* Mejorar responsividad de conversaciones */ +@media (max-width: 768px) { + .conversation-row .btn-group .btn { + padding: 0.25rem 0.5rem; + font-size: 0.75rem; + } + + .message-bubble { + max-width: 85% !important; + } + + #chatModal .modal-dialog { + max-width: 95%; + margin: 1rem auto; + } +} + +/* Animaciones para mensajes */ +.message-bubble { + animation: messageAppear 0.3s ease-out; +} + +@keyframes messageAppear { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +/* Estilos mejorados para lista de conversaciones */ +.conversation-card { + border: none; + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); + border-radius: 20px; + overflow: hidden; +} + +.conversation-card .card-header { + border: none; + background: linear-gradient(135deg, #25d366 0%, #128c7e 100%); + padding: 1.5rem; +} + +.conversation-list { + max-height: 600px; + overflow-y: auto; +} + +.conversation-item { + display: flex; + align-items: center; + padding: 15px 20px; + border-bottom: 1px solid #f0f0f0; + cursor: pointer; + transition: all 0.2s ease; + position: relative; + background: white; +} + +.conversation-item:hover { + background: #f8f9fa; + transform: translateX(5px); +} + +.conversation-item:last-child { + border-bottom: none; +} + +.conversation-avatar { + width: 55px; + height: 55px; + background: linear-gradient(135deg, #25d366, #128c7e); + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + color: white; + font-weight: bold; + font-size: 1.4rem; + margin-right: 15px; + box-shadow: 0 4px 12px rgba(37, 211, 102, 0.3); + position: relative; +} + +.conversation-avatar.online::after { + content: ''; + position: absolute; + bottom: 2px; + right: 2px; + width: 14px; + height: 14px; + background: #4ade80; + border: 2px solid white; + border-radius: 50%; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.conversation-details { + flex: 1; + min-width: 0; +} + +.conversation-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 5px; +} + +.conversation-name { + font-weight: 600; + font-size: 1.1rem; + color: #1f2937; + display: flex; + align-items: center; + gap: 8px; +} + +.conversation-time { + font-size: 0.8rem; + color: #9ca3af; + white-space: nowrap; +} + +.conversation-preview { + display: flex; + align-items: center; + gap: 8px; +} + +.conversation-last-message { + color: #6b7280; + font-size: 0.9rem; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + line-height: 1.3; +} + +.conversation-meta { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 5px; +} + +.conversation-unread { + background: #25d366; + color: white; + border-radius: 50%; + padding: 4px 8px; + font-size: 0.75rem; + font-weight: bold; + min-width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; +} + +.conversation-status-icon { + font-size: 0.8rem; +} + +.conversation-status-icon.outgoing { + color: #25d366; +} + +.conversation-status-icon.incoming { + color: #3b82f6; +} + +.conversation-actions { + display: flex; + gap: 8px; + opacity: 0; + transition: opacity 0.2s ease; +} + +.conversation-item:hover .conversation-actions { + opacity: 1; +} + +.conversation-actions .btn { + padding: 6px 10px; + font-size: 0.8rem; + border-radius: 8px; + border: 1px solid #e5e7eb; + background: white; + color: #6b7280; + transition: all 0.2s ease; +} + +.conversation-actions .btn:hover { + background: #25d366; + color: white; + border-color: #25d366; + transform: translateY(-1px); +} + +.conversation-empty { + text-align: center; + padding: 60px 20px; + color: #9ca3af; +} + +.conversation-empty .empty-icon { + font-size: 4rem; + color: #d1d5db; + margin-bottom: 20px; +} + +.conversation-search { + border-radius: 25px; + border: none; + background: rgba(255, 255, 255, 0.2); + color: white; + padding: 8px 20px; +} + +.conversation-search::placeholder { + color: rgba(255, 255, 255, 0.7); +} + +.conversation-search:focus { + background: rgba(255, 255, 255, 0.3); + color: white; + box-shadow: none; + border: 1px solid rgba(255, 255, 255, 0.5); } \ No newline at end of file diff --git a/assets/js/app_simple.js b/assets/js/app_simple.js index 07a073c..e2db864 100644 --- a/assets/js/app_simple.js +++ b/assets/js/app_simple.js @@ -409,11 +409,11 @@ class SimpleWhatsAppManager { this.log('Cargando conversaciones'); try { - const response = await this.apiCall('get_conversations.php'); - console.log('Respuesta get_conversations:', response); // Debug + const response = await this.apiCall('get_conversation_list.php'); + console.log('Respuesta get_conversation_list:', response); // Debug - if (response && response.success && Array.isArray(response.data)) { - this.updateConversationsList(response.data); + if (response && response.success && Array.isArray(response.conversations)) { + this.updateConversationsList(response.conversations); } else if (response && Array.isArray(response)) { // Retrocompatibilidad por si la API devuelve directamente el array this.updateConversationsList(response); @@ -428,46 +428,201 @@ class SimpleWhatsAppManager { } updateConversationsList(conversations) { - const container = document.getElementById('conversations-table'); + const container = document.getElementById('conversations-container'); if (!container) return; if (conversations.length === 0) { - container.innerHTML = 'No hay conversaciones'; + container.innerHTML = ` +
+
+ +
+
No hay conversaciones
+

Los chats aparecerán aquí cuando recibas o envíes mensajes

+
+ `; return; } let html = ''; conversations.forEach(conv => { - const date = new Date(conv.created_at); - const formatDate = date.toLocaleDateString() + ' ' + date.toLocaleTimeString('es', { hour: '2-digit', minute: '2-digit' }); + const timeAgo = conv.time_ago || this.formatTimeAgo(conv.last_message_time || conv.created_at); + const lastMessage = conv.last_message || conv.content || 'Sin mensajes'; + const userName = conv.name || conv.phone_number; + const userInitial = userName.charAt(0).toUpperCase(); + const isOnline = Math.random() > 0.6; // Simulación de estado online + + // Unread badge + const unreadBadge = (conv.unread_count && conv.unread_count > 0) ? + `
${conv.unread_count}
` : ''; + + // Status icon + const statusIcon = conv.last_direction === 'outgoing' + ? '' + : ''; + + // Message type indicator + const messageTypeIcon = conv.last_message_type === 'template' + ? ' ' + : conv.last_message_type === 'image' + ? ' ' + : ''; html += ` - - -
- ${conv.name || conv.phone_number}
- ${conv.phone_number} +
+
+ ${userInitial} +
+
+
+
+ ${userName} + ${conv.last_message_status === 'read' ? '' : ''} +
+
${timeAgo}
- - -
- ${conv.content || 'Sin contenido'} +
+ ${statusIcon} +
+ ${messageTypeIcon}${lastMessage} +
- - ${conv.message_type || 'text'} - ${conv.status || 'unknown'} - ${formatDate} - - - - +
+
+ ${unreadBadge} +
+ + +
+
+
`; }); container.innerHTML = html; + + // Agregar funcionalidad de búsqueda + this.setupConversationSearch(conversations); + } + + setupConversationSearch(conversations) { + const searchInput = document.getElementById('search-conversations'); + if (!searchInput) return; + + searchInput.addEventListener('input', (e) => { + const query = e.target.value.toLowerCase().trim(); + + if (!query) { + this.updateConversationsDisplay(conversations); + return; + } + + const filtered = conversations.filter(conv => { + const name = (conv.name || '').toLowerCase(); + const phone = (conv.phone_number || '').toLowerCase(); + const message = (conv.last_message || conv.content || '').toLowerCase(); + + return name.includes(query) || + phone.includes(query) || + message.includes(query); + }); + + this.updateConversationsDisplay(filtered); + }); + } + + updateConversationsDisplay(conversations) { + const container = document.getElementById('conversations-container'); + if (!container) return; + + if (conversations.length === 0) { + container.innerHTML = ` +
+
+ +
+
No se encontraron conversaciones
+

Intenta con otros términos de búsqueda

+
+ `; + return; + } + + let html = ''; + conversations.forEach(conv => { + const timeAgo = conv.time_ago || this.formatTimeAgo(conv.last_message_time || conv.created_at); + const lastMessage = conv.last_message || conv.content || 'Sin mensajes'; + const userName = conv.name || conv.phone_number; + const userInitial = userName.charAt(0).toUpperCase(); + const isOnline = Math.random() > 0.6; + + const unreadBadge = (conv.unread_count && conv.unread_count > 0) ? + `
${conv.unread_count}
` : ''; + + const statusIcon = conv.last_direction === 'outgoing' + ? '' + : ''; + + const messageTypeIcon = conv.last_message_type === 'template' + ? ' ' + : conv.last_message_type === 'image' + ? ' ' + : ''; + + html += ` +
+
+ ${userInitial} +
+
+
+
+ ${userName} + ${conv.last_message_status === 'read' ? '' : ''} +
+
${timeAgo}
+
+
+ ${statusIcon} +
+ ${messageTypeIcon}${lastMessage} +
+
+
+
+ ${unreadBadge} +
+ + +
+
+
+ `; + }); + + container.innerHTML = html; + } + + formatTimeAgo(datetime) { + if (!datetime) return ''; + const now = new Date(); + const date = new Date(datetime); + const diff = Math.floor((now - date) / 1000); + + if (diff < 60) return 'Ahora'; + if (diff < 3600) return `${Math.floor(diff/60)}m`; + if (diff < 86400) return `${Math.floor(diff/3600)}h`; + if (diff < 2592000) return `${Math.floor(diff/86400)}d`; + return date.toLocaleDateString(); } async loadSettings() { @@ -918,10 +1073,305 @@ window.debugAPI = async function (endpoint) { } }; -// Función para ver conversación específica +// Función para abrir ventana de chat +window.openChatWindow = function (userId) { + console.log('Abriendo chat del usuario:', userId); + + // Abrir nueva ventana de chat + const chatUrl = `chat_window.php?user_id=${userId}&debug=true`; + const windowFeatures = 'width=1000,height=700,resizable=yes,scrollbars=yes,status=yes,toolbar=no,menubar=no,location=no'; + + const chatWindow = window.open(chatUrl, `chat_${userId}`, windowFeatures); + + if (chatWindow) { + chatWindow.focus(); + } else { + alert('No se pudo abrir la ventana de chat. Verifica que tu navegador permita ventanas emergentes.'); + } +}; + +// Función para mostrar modal de chat +function showChatModal(user, messages) { + // Escapar datos del usuario para evitar XSS + const userName = escapeHtml(user.name || 'Usuario'); + const userPhone = escapeHtml(user.phone_number || ''); + + const modalHtml = ` + + `; + + // Remover modal anterior si existe + const existingModal = document.getElementById('chatModal'); + if (existingModal) { + existingModal.remove(); + } + + document.body.insertAdjacentHTML('beforeend', modalHtml); + + // Event listener para cambio de tipo de mensaje + document.getElementById('messageType').addEventListener('change', function() { + const templateSelector = document.getElementById('templateSelector'); + const messageInput = document.getElementById('messageInput'); + + if (this.value === 'template') { + templateSelector.style.display = 'block'; + messageInput.placeholder = 'Parámetros de la plantilla (opcional)'; + } else { + templateSelector.style.display = 'none'; + messageInput.placeholder = 'Escribe tu mensaje...'; + } + }); + + // Mostrar modal + const modal = new bootstrap.Modal(document.getElementById('chatModal')); + modal.show(); + + // Scroll al final + setTimeout(() => { + const chatContainer = document.getElementById('chat-messages'); + chatContainer.scrollTop = chatContainer.scrollHeight; + }, 200); +} + +// Generar HTML de mensajes del chat +function generateChatMessages(messages) { + if (!messages || messages.length === 0) { + return '


No hay mensajes en esta conversación
'; + } + + let html = ''; + messages.forEach(msg => { + const isOutgoing = msg.direction === 'outgoing'; + const messageClass = isOutgoing ? 'outgoing' : 'incoming'; + const alignClass = isOutgoing ? 'justify-content-end' : 'justify-content-start'; + const bgClass = isOutgoing ? 'bg-success text-white' : 'bg-white'; + + // Escapar el contenido del mensaje + const messageContent = escapeHtml(msg.content) || 'Sin contenido'; + const messageTime = escapeHtml(msg.time || ''); + + html += ` +
+
+
+ ${messageContent} +
+
+ ${messageTime} ${isOutgoing ? '✓✓' : ''} +
+
+
+ `; + }); + + return html; +} + +// Función para escapar HTML y prevenir XSS +function escapeHtml(text) { + if (!text) return ''; + const map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + }; + return text.toString().replace(/[&<>"']/g, function(m) { return map[m]; }); +} + +// Función para enviar mensaje desde el chat +window.sendChatMessage = async function (userId) { + const messageType = document.getElementById('messageType').value; + const messageInput = document.getElementById('messageInput'); + const message = messageInput.value.trim(); + + if (!message && messageType === 'text') { + alert('Por favor escribe un mensaje'); + return; + } + + if (messageType === 'template') { + const templateSelect = document.getElementById('templateSelect'); + const template = templateSelect.value; + + if (!template) { + alert('Por favor selecciona una plantilla'); + return; + } + + await sendTemplateMessage(userId, template, message); + } else { + await sendTextMessage(userId, message); + } + + messageInput.value = ''; +}; + +// Función para enviar mensaje de texto +async function sendTextMessage(userId, message) { + try { + const manager = window.whatsappManager; + const usersResponse = await manager.apiCall('get_users.php?debug=true'); + + // Manejar diferentes formatos de respuesta + let users = []; + if (Array.isArray(usersResponse)) { + users = usersResponse; + } else if (usersResponse && usersResponse.data && Array.isArray(usersResponse.data)) { + users = usersResponse.data; + } else if (usersResponse && usersResponse.users && Array.isArray(usersResponse.users)) { + users = usersResponse.users; + } else { + console.error('Formato de respuesta de usuarios inesperado:', usersResponse); + throw new Error('Error obteniendo lista de usuarios'); + } + + const user = users.find(u => u.id == userId); + + if (!user) { + throw new Error('Usuario no encontrado'); + } + + const response = await manager.apiCall('send_message.php', { + method: 'POST', + body: { + recipient: user.phone_number, + type: 'text', + message: message + } + }); + + if (response && response.success) { + // Refrescar chat + openChatWindow(userId); + showAlert('Mensaje enviado correctamente', 'success'); + } else { + throw new Error(response.error || 'Error enviando mensaje'); + } + } catch (error) { + console.error('Error enviando mensaje:', error); + showAlert('Error enviando mensaje: ' + error.message, 'danger'); + } +} + +// Función para enviar mensaje de plantilla +async function sendTemplateMessage(userId, template, parameters) { + try { + const manager = window.whatsappManager; + const usersResponse = await manager.apiCall('get_users.php?debug=true'); + + // Manejar diferentes formatos de respuesta + let users = []; + if (Array.isArray(usersResponse)) { + users = usersResponse; + } else if (usersResponse && usersResponse.data && Array.isArray(usersResponse.data)) { + users = usersResponse.data; + } else if (usersResponse && usersResponse.users && Array.isArray(usersResponse.users)) { + users = usersResponse.users; + } else { + console.error('Formato de respuesta de usuarios inesperado:', usersResponse); + throw new Error('Error obteniendo lista de usuarios'); + } + + const user = users.find(u => u.id == userId); + + if (!user) { + throw new Error('Usuario no encontrado'); + } + + const response = await manager.apiCall('send_message.php', { + method: 'POST', + body: { + recipient: user.phone_number, + type: 'template', + template: template, + language: 'en_US', + parameters: parameters ? parameters.split(',') : [] + } + }); + + if (response && response.success) { + // Refrescar chat + openChatWindow(userId); + showAlert('Mensaje de plantilla enviado correctamente', 'success'); + } else { + throw new Error(response.error || 'Error enviando plantilla'); + } + } catch (error) { + console.error('Error enviando plantilla:', error); + showAlert('Error enviando plantilla: ' + error.message, 'danger'); + } +} + +// Función para ver detalles de conversación +window.viewConversationDetails = function (userId) { + console.log('Viendo detalles de conversación del usuario:', userId); + openChatWindow(userId); +}; + +// Función para mostrar alertas +function showAlert(message, type = 'info') { + const alertHtml = ` + + `; + + document.body.insertAdjacentHTML('beforeend', alertHtml); + + // Auto-dismiss después de 5 segundos + setTimeout(() => { + const alert = document.querySelector('.alert'); + if (alert) { + alert.remove(); + } + }, 5000); +} + +// Función para ver conversación específica (mantener compatibilidad) window.viewConversation = function (userId) { - console.log('Viendo conversación del usuario:', userId); - alert(`Ver conversación del usuario ${userId} - Función en desarrollo`); + openChatWindow(userId); }; // Función para editar usuario diff --git a/chat_window.php b/chat_window.php new file mode 100644 index 0000000..6c06024 --- /dev/null +++ b/chat_window.php @@ -0,0 +1,575 @@ + + + + + + + Chat WhatsApp - Usuario + + + + + + +
+
+ +
+
+
+ +
+
+
Cargando...
+ + En línea + +
+
+
+ + +
+
+ + +
+
+
+ Cargando mensajes... +
+
+
+ + +
+ + + + El usuario está escribiendo... +
+ + +
+
+
+ +
+ +
+ +
+ + +
+
+
+
+ + + + + \ No newline at end of file diff --git a/check_table_structure.php b/check_table_structure.php new file mode 100644 index 0000000..39031d4 --- /dev/null +++ b/check_table_structure.php @@ -0,0 +1,48 @@ +query("DESCRIBE message_templates"); + $columns = $stmt->fetchAll(); + + echo "Columnas disponibles:\n"; + foreach ($columns as $column) { + echo "• {$column['Field']} ({$column['Type']}) - {$column['Null']} - {$column['Default']}\n"; + } + + echo "\n=== INTENTAR INSERCIÓN SIMPLE ===\n"; + + // Intentar inserción con campos mínimos + $stmt = $db->query( + "INSERT INTO message_templates (template_name, language_code, status) VALUES (?, ?, ?)", + ['hello_world', 'en_US', 'approved'] + ); + + if ($stmt) { + echo "✅ Plantilla 'hello_world' agregada exitosamente\n"; + } + + // Verificar que se agregó + echo "\n📋 Plantillas en la BD:\n"; + $stmt = $db->query("SELECT * FROM message_templates"); + $templates = $stmt->fetchAll(); + + foreach ($templates as $template) { + echo "• {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n"; + } + +} catch (Exception $e) { + echo "❌ Error: " . $e->getMessage() . "\n"; +} +?> \ No newline at end of file diff --git a/clean_templates.php b/clean_templates.php new file mode 100644 index 0000000..7823267 --- /dev/null +++ b/clean_templates.php @@ -0,0 +1,76 @@ +query("SELECT id, template_name, language_code, status FROM message_templates ORDER BY template_name"); + $templates = $stmt->fetchAll(); + + foreach ($templates as $template) { + echo "• ID: {$template['id']} - {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n"; + } + + echo "\n🗑️ Eliminando plantillas que no funcionan...\n"; + + // Eliminar plantillas que no funcionan + $templatesToDelete = [ + 'welcome_user', + 'jaspers_market_plain_text_v1' + ]; + + foreach ($templatesToDelete as $templateName) { + $stmt = $db->query( + "DELETE FROM message_templates WHERE template_name = ?", + [$templateName] + ); + + $affectedRows = $stmt->rowCount(); + echo "• Eliminada '{$templateName}': {$affectedRows} registro(s) afectado(s)\n"; + } + + echo "\n✅ Plantillas restantes en la BD:\n"; + $stmt = $db->query("SELECT id, template_name, language_code, status FROM message_templates ORDER BY template_name"); + $remainingTemplates = $stmt->fetchAll(); + + if (empty($remainingTemplates)) { + echo "❌ No hay plantillas restantes\n"; + echo "\n💡 Sugerencia: Agrega la plantilla 'hello_world' que funciona:\n"; + + // Agregar plantilla hello_world que sabemos que funciona + $db->query( + "INSERT INTO message_templates (template_name, language_code, status, body_text, created_at, updated_at) VALUES (?, ?, ?, ?, NOW(), NOW())", + ['hello_world', 'en_US', 'approved', 'Hello World! This is a test message.'] + ); + echo "✅ Agregada plantilla 'hello_world' (en_US) que funciona correctamente\n"; + + // Mostrar plantillas finales + $stmt = $db->query("SELECT id, template_name, language_code, status FROM message_templates ORDER BY template_name"); + $finalTemplates = $stmt->fetchAll(); + + echo "\n📋 Plantillas finales:\n"; + foreach ($finalTemplates as $template) { + echo "• ID: {$template['id']} - {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n"; + } + } else { + foreach ($remainingTemplates as $template) { + echo "• ID: {$template['id']} - {$template['template_name']} ({$template['language_code']}) - {$template['status']}\n"; + } + } + + echo "\n🎉 Limpieza completada exitosamente\n"; + +} catch (Exception $e) { + echo "❌ Error: " . $e->getMessage() . "\n"; +} +?> \ No newline at end of file diff --git a/config/config.php b/config/config.php index bc23e7d..996a44d 100644 --- a/config/config.php +++ b/config/config.php @@ -637,8 +637,11 @@ function saveWhatsAppConfigToDB($whatsappConfig) { */ function getWhatsAppConfigFromDB() { return [ + // Mapear a nombres esperados por el código + 'token' => getConfigFromDB('whatsapp_token', ''), 'whatsapp_token' => getConfigFromDB('whatsapp_token', ''), 'phone_number_id' => getConfigFromDB('whatsapp_phone_number_id', ''), + 'api_url' => getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), 'whatsapp_api_url' => getConfigFromDB('whatsapp_api_url', 'https://graph.facebook.com/v22.0/'), 'webhook_verify_token' => getConfigFromDB('webhook_verify_token', ''), 'business_name' => getConfigFromDB('business_name', ''), diff --git a/debug_templates.php b/debug_templates.php new file mode 100644 index 0000000..60cf9d7 --- /dev/null +++ b/debug_templates.php @@ -0,0 +1,39 @@ +query("SELECT template_name, language_code, status FROM message_templates ORDER BY template_name, language_code"); + $templates = $stmt->fetchAll(); + + if (empty($templates)) { + echo "❌ No hay plantillas en la base de datos\n"; + } else { + echo "📋 Plantillas encontradas:\n\n"; + foreach ($templates as $template) { + echo "• Nombre: {$template['template_name']}\n"; + echo " Idioma: {$template['language_code']}\n"; + echo " Estado: {$template['status']}\n"; + echo " -----\n"; + } + } + + echo "\n=== CONFIGURACIÓN ACTUAL ===\n"; + $config = getWhatsAppConfigFromDB(); + echo "Token presente: " . (empty($config['token']) ? '❌ NO' : '✅ SÍ (' . strlen($config['token']) . ' chars)') . "\n"; + echo "Phone ID: " . ($config['phone_number_id'] ?? 'No configurado') . "\n"; + echo "API URL: " . ($config['api_url'] ?? 'No configurada') . "\n"; + +} catch (Exception $e) { + echo "❌ Error: " . $e->getMessage() . "\n"; +} +?> \ No newline at end of file diff --git a/index.php b/index.php index 3675763..837f140 100644 --- a/index.php +++ b/index.php @@ -183,30 +183,28 @@ try {
-
-
-
Conversaciones
-
- +
+
+
+
+
Conversaciones
+ Administra tus chats de WhatsApp +
+
+
+ + + + +
+
-
-
- - - - - - - - - - - - - - -
UsuarioÚltimo MensajeTipoEstadoFechaAcciones
+
+
+
diff --git a/logs/system.log b/logs/system.log index b285155..ff7cd62 100644 --- a/logs/system.log +++ b/logs/system.log @@ -2,3 +2,5 @@ [2026-01-12 13:26:51] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"} [2026-01-12 13:28:56] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"} [2026-01-12 14:07:16] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"} +[2026-01-12 14:24:57] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"} +[2026-01-12 14:25:28] [INFO] Configuración de WhatsApp actualizada {"configured_fields":{"token":true,"phone_id":true,"webhook_token":true},"user":"debug_user"} diff --git a/services/WhatsAppService.php b/services/WhatsAppService.php index 9bda63b..6ae9c4b 100644 --- a/services/WhatsAppService.php +++ b/services/WhatsAppService.php @@ -1,26 +1,33 @@ token = WHATSAPP_TOKEN; - $this->phoneNumberId = WHATSAPP_PHONE_NUMBER_ID; - $this->apiUrl = WHATSAPP_API_URL; + + public function __construct() + { + // Obtener configuración desde base de datos + $config = getWhatsAppConfigFromDB(); + + $this->token = $config['token']; // Usar token de BD + $this->phoneNumberId = $config['phone_number_id']; // Usar phone_number_id de BD + $this->apiUrl = $config['api_url'] ?: 'https://graph.facebook.com/v22.0/'; // Usar api_url de BD $this->db = Database::getInstance(); } - + /** * Enviar mensaje de texto */ - public function sendTextMessage($to, $message) { + public function sendTextMessage($to, $message) + { $data = [ 'messaging_product' => 'whatsapp', 'to' => $this->formatPhoneNumber($to), @@ -29,44 +36,62 @@ class WhatsAppService { 'body' => $message ] ]; - + return $this->sendMessage($data); } - + /** * Enviar mensaje con template */ - public function sendTemplateMessage($to, $templateName, $language = 'es', $parameters = []) { + public function sendTemplateMessage( + $to, + $templateName, + $language = 'es', + $bodyParameters = [], + $headerParameters = [] + ) { + $components = []; + + if (!empty($headerParameters)) { + $components[] = [ + 'type' => 'header', + 'parameters' => $headerParameters + ]; + } + + if (!empty($bodyParameters)) { + $components[] = [ + 'type' => 'body', + 'parameters' => $bodyParameters + ]; + } + $template = [ 'name' => $templateName, 'language' => [ 'code' => $language ] ]; - - if (!empty($parameters)) { - $template['components'] = [ - [ - 'type' => 'body', - 'parameters' => $parameters - ] - ]; + + if (!empty($components)) { + $template['components'] = $components; } - + $data = [ 'messaging_product' => 'whatsapp', 'to' => $this->formatPhoneNumber($to), 'type' => 'template', 'template' => $template ]; - + return $this->sendMessage($data); } - + /** * Enviar mensaje con botones interactivos */ - public function sendInteractiveMessage($to, $bodyText, $buttons, $header = null, $footer = null) { + public function sendInteractiveMessage($to, $bodyText, $buttons, $header = null, $footer = null) + { $interactive = [ 'type' => 'button', 'body' => [ @@ -76,20 +101,20 @@ class WhatsAppService { 'buttons' => $buttons ] ]; - + if ($header) { $interactive['header'] = [ 'type' => 'text', 'text' => $header ]; } - + if ($footer) { $interactive['footer'] = [ 'text' => $footer ]; } - + $data = [ 'messaging_product' => 'whatsapp', 'recipient_type' => 'individual', @@ -97,14 +122,15 @@ class WhatsAppService { 'type' => 'interactive', 'interactive' => $interactive ]; - + return $this->sendMessage($data); } - + /** * Enviar mensaje con lista */ - public function sendListMessage($to, $bodyText, $buttonText, $sections, $header = null, $footer = null) { + public function sendListMessage($to, $bodyText, $buttonText, $sections, $header = null, $footer = null) + { $interactive = [ 'type' => 'list', 'body' => [ @@ -115,20 +141,20 @@ class WhatsAppService { 'sections' => $sections ] ]; - + if ($header) { $interactive['header'] = [ 'type' => 'text', 'text' => $header ]; } - + if ($footer) { $interactive['footer'] = [ 'text' => $footer ]; } - + $data = [ 'messaging_product' => 'whatsapp', 'recipient_type' => 'individual', @@ -136,51 +162,61 @@ class WhatsAppService { 'type' => 'interactive', 'interactive' => $interactive ]; - + return $this->sendMessage($data); } - + /** * Marcar mensaje como leído */ - public function markAsRead($messageId) { + public function markAsRead($messageId) + { $data = [ 'messaging_product' => 'whatsapp', 'status' => 'read', 'message_id' => $messageId ]; - + $url = $this->apiUrl . $this->phoneNumberId . '/messages'; return $this->makeRequest('POST', $url, $data); } - + /** * Enviar mensaje principal */ - private function sendMessage($data) { - $url = $this->apiUrl . $this->phoneNumberId . '/messages'; - $response = $this->makeRequest('POST', $url, $data); + private function sendMessage($data) + { + // Construir URL correctamente + $url = rtrim($this->apiUrl, '/') . '/' . $this->phoneNumberId . '/messages'; + // Debug log + error_log("WhatsApp API URL: " . $url); + error_log("WhatsApp Token length: " . strlen($this->token)); + error_log("WhatsApp Data: " . json_encode($data)); + + $response = $this->makeRequest('POST', $url, $data); + // Guardar mensaje enviado en la base de datos if ($response && isset($response['messages'][0]['id'])) { $this->saveOutgoingMessage($data, $response); } - + return $response; } - + /** * Realizar petición HTTP */ - private function makeRequest($method, $url, $data = null) { + private function makeRequest($method, $url, $data = null) + { $ch = curl_init(); - + $headers = [ 'Authorization: Bearer ' . $this->token, 'Content-Type: application/json', 'User-Agent: WhatsApp-Bot/1.0' ]; - + curl_setopt_array($ch, [ CURLOPT_URL => $url, CURLOPT_RETURNTRANSFER => true, @@ -192,57 +228,66 @@ class WhatsAppService { CURLOPT_FOLLOWLOCATION => true, CURLOPT_MAXREDIRS => 3 ]); - + if ($method === 'POST' && $data) { curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); } - + $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $error = curl_error($ch); curl_close($ch); - + if ($error) { error_log("cURL Error: " . $error); throw new Exception("Error de comunicación con WhatsApp: " . $error); } - + $decoded = json_decode($response, true); - + if ($httpCode >= 400) { $errorMsg = isset($decoded['error']['message']) ? $decoded['error']['message'] : 'Error desconocido'; - error_log("WhatsApp API Error: " . $response); + + // Debug detallado + error_log("WhatsApp API Error Details:"); + error_log("- URL: " . $url); + error_log("- HTTP Code: " . $httpCode); + error_log("- Response: " . $response); + error_log("- Token (first 20 chars): " . substr($this->token, 0, 20) . "..."); + throw new Exception("Error de WhatsApp API: " . $errorMsg); } - + return $decoded; } - + /** * Formatear número de teléfono */ - private function formatPhoneNumber($phone) { + private function formatPhoneNumber($phone) + { // Remover caracteres especiales $phone = preg_replace('/[^0-9]/', '', $phone); - + // Si empieza con +57 (Colombia), mantenerlo if (substr($phone, 0, 2) === '57' && strlen($phone) >= 12) { return $phone; } - + // Si es número colombiano sin código de país if (strlen($phone) === 10 && substr($phone, 0, 1) === '3') { return '57' . $phone; } - + return $phone; } - + /** * Guardar mensaje enviado en BD */ - private function saveOutgoingMessage($data, $response) { + private function saveOutgoingMessage($data, $response) + { try { $user = $this->getUserByPhone($data['to']); if ($user) { @@ -255,18 +300,19 @@ class WhatsAppService { 'status' => 'sent', 'created_at' => date('Y-m-d H:i:s') ]; - + $this->db->insert('conversations', $messageData); } } catch (Exception $e) { error_log("Error saving outgoing message: " . $e->getMessage()); } } - + /** * Extraer contenido del mensaje para guardar */ - private function extractMessageContent($data) { + private function extractMessageContent($data) + { switch ($data['type']) { case 'text': return $data['text']['body']; @@ -280,34 +326,36 @@ class WhatsAppService { } return json_encode($data); } - + /** * Obtener usuario por teléfono */ - private function getUserByPhone($phone) { + private function getUserByPhone($phone) + { return $this->db->fetch( "SELECT * FROM users WHERE phone_number = :phone", ['phone' => $phone] ); } - + /** * Descargar archivo multimedia */ - public function downloadMedia($mediaId) { + public function downloadMedia($mediaId) + { $url = $this->apiUrl . $mediaId; - + // Primero obtener información del archivo $mediaInfo = $this->makeRequest('GET', $url); - + if (!$mediaInfo || !isset($mediaInfo['url'])) { throw new Exception("No se pudo obtener información del archivo"); } - + // Descargar el archivo $fileUrl = $mediaInfo['url']; $ch = curl_init(); - + curl_setopt_array($ch, [ CURLOPT_URL => $fileUrl, CURLOPT_RETURNTRANSFER => true, @@ -317,15 +365,15 @@ class WhatsAppService { CURLOPT_TIMEOUT => 60, CURLOPT_FOLLOWLOCATION => true ]); - + $fileContent = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); - + if ($httpCode !== 200) { throw new Exception("Error descargando archivo multimedia"); } - + return [ 'content' => $fileContent, 'mime_type' => $mediaInfo['mime_type'] ?? 'application/octet-stream', @@ -333,4 +381,3 @@ class WhatsAppService { ]; } } -?> \ No newline at end of file diff --git a/test_template_direct.php b/test_template_direct.php new file mode 100644 index 0000000..5388a36 --- /dev/null +++ b/test_template_direct.php @@ -0,0 +1,56 @@ + 'Iniciando test de plantilla...'], JSON_PRETTY_PRINT) . "\n"; + + $whatsapp = new WhatsAppService(); + + // Usar datos de prueba que sabemos que existen + $recipient = "573168950803"; // Número de prueba + $templateName = "hello_world"; // Esta plantilla FUNCIONA + $language = "en_US"; // Idioma que FUNCIONA + + echo json_encode([ + 'step' => 'Datos de envío', + 'recipient' => $recipient, + 'template' => $templateName, + 'language' => $language + ], JSON_PRETTY_PRINT) . "\n"; + + // Enviar plantilla + echo json_encode(['step' => 'Llamando sendTemplateMessage...'], JSON_PRETTY_PRINT) . "\n"; + flush(); + + $result = $whatsapp->sendTemplateMessage($recipient, $templateName, $language); + + echo json_encode([ + 'step' => 'Resultado', + 'success' => true, + 'result' => $result + ], JSON_PRETTY_PRINT) . "\n"; + +} catch (Exception $e) { + echo json_encode([ + 'step' => 'Error capturado', + 'success' => false, + 'error' => $e->getMessage(), + 'file' => $e->getFile(), + 'line' => $e->getLine() + ], JSON_PRETTY_PRINT) . "\n"; +} + +echo json_encode(['status' => 'Test completado. Revisa los logs de error para ver detalles de la petición HTTP.'], JSON_PRETTY_PRINT) . "\n"; +?> \ No newline at end of file diff --git a/test_whatsapp_config.php b/test_whatsapp_config.php new file mode 100644 index 0000000..8f4c79f --- /dev/null +++ b/test_whatsapp_config.php @@ -0,0 +1,52 @@ +Test de Configuración WhatsApp"; + +// Obtener configuración desde BD +$config = getWhatsAppConfigFromDB(); + +echo "

Configuración desde Base de Datos:

"; +echo "
";
+echo "Token: " . (strlen($config['token']) > 0 ? substr($config['token'], 0, 20) . "... (length: " . strlen($config['token']) . ")" : "EMPTY") . "\n";
+echo "Phone Number ID: " . ($config['phone_number_id'] ?: "EMPTY") . "\n";
+echo "API URL: " . ($config['api_url'] ?: "EMPTY") . "\n";
+echo "
"; + +// Construir URL como lo haría WhatsAppService +$apiUrl = rtrim($config['api_url'], '/') . '/' . $config['phone_number_id'] . '/messages'; +echo "

URL que se construirá:

"; +echo "
" . $apiUrl . "
"; + +// Comparar con tu curl que funciona +$expectedUrl = "https://graph.facebook.com/v22.0/938064202726485/messages"; +echo "

URL que debería ser:

"; +echo "
" . $expectedUrl . "
"; + +echo "

¿Coinciden las URLs?

"; +echo "
" . ($apiUrl === $expectedUrl ? "✅ SÍ" : "❌ NO") . "
"; + +// Verificar token +$expectedTokenStart = "EAAcGjPRqunIBQVT6b86O0wWWGLGJ1PANmolIRXRBmEcpqa3P85VlUz0Rrv2c5deXky3gOKZCBw7ZCpdicly4c0G8qFMOH1DfOcSzmV49FlPId3AGPlHEXpqlsPFIE8DF18trW2vglQrFiCHXZCEBSUBQfp1jEASD1NrKKKVHiGVZARrwoxP0J931u0KIr2ZAZCug4fFpnyRZBls9LEgJuomXd4ZCnJtBZA4GvP2zaplmZA52mJiG5H4YiKKWby5GnQq9BgAVaq6sLZBnYI26aR0FgZDZD"; + +echo "

¿Coincide el token?

"; +$tokenMatch = substr($config['token'], 0, 50) === substr($expectedTokenStart, 0, 50); +echo "
" . ($tokenMatch ? "✅ SÍ (primeros 50 caracteres)" : "❌ NO") . "
"; + +if (!$tokenMatch) { + echo "

Debug Token:

"; + echo "
";
+    echo "Configurado: " . substr($config['token'], 0, 50) . "...\n";
+    echo "Esperado:    " . substr($expectedTokenStart, 0, 50) . "...\n";
+    echo "
"; +} + +// Test directo de configuración +echo "

Configuraciones Individuales:

"; +echo "
";
+foreach(['whatsapp_token', 'whatsapp_phone_number_id', 'whatsapp_api_url'] as $key) {
+    $value = getConfigFromDB($key, 'NO_ENCONTRADO');
+    echo "$key: " . ($value === 'NO_ENCONTRADO' ? "❌ NO_ENCONTRADO" : "✅ " . substr($value, 0, 30) . "...") . "\n";
+}
+echo "
"; +?> \ No newline at end of file