Files
whatsapp/api/send_message.php
T
2026-01-24 12:41:55 -05:00

527 lines
25 KiB
PHP

<?php
/**
* API - Enviar mensaje individual
* Fecha: 4 de enero de 2026
*/
require_once '../config/config.php';
// Suprimir errores para obtener JSON limpio
error_reporting(E_ERROR | E_PARSE);
// Modo debug: desactivar autenticación si existe el parámetro debug
$debugMode = isset($_GET['debug']) && $_GET['debug'] === 'true';
if (!$debugMode) {
requireAuthentication();
}
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
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. Use POST.', 'method_received' => $_SERVER['REQUEST_METHOD']]);
exit;
}
try {
$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) {
// Si no viene JSON válido, intentar leer datos from form-encoded POST
if (!empty($_POST)) {
$input = $_POST;
// Si el formulario incluye template_params como JSON string, decodificarlo
if (isset($input['template_params']) && is_string($input['template_params'])) {
$decodedTp = json_decode($input['template_params'], true);
if ($decodedTp !== null) {
$input['template_params'] = $decodedTp;
}
}
} else {
http_response_code(400);
echo json_encode(['error' => 'JSON inválido o vacío', 'raw_input' => $rawInput]);
exit;
}
}
$db = Database::getInstance();
// Modo debug: simular envío exitoso
if ($debugMode) {
$recipient = $input['recipient'] ?? $input['user_id'] ?? 'unknown';
$message = $input['message'] ?? 'mensaje de prueba';
$type = $input['type'] ?? 'text';
$template = $input['template'] ?? $input['template_name'] ?? null;
$responseData = [
'recipient' => $recipient,
'type' => $type,
'timestamp' => date('Y-m-d H:i:s'),
'warning' => 'Para envío real, verifica que el usuario haya respondido en las últimas 24h o usa plantillas'
];
if ($type === 'template') {
// Detectar formato de plantilla
if (isset($input['template']) && is_array($input['template'])) {
// Formato WhatsApp estándar
$templateData = $input['template'];
$responseData['template_name'] = $templateData['name'] ?? 'unknown';
$responseData['language'] = $templateData['language']['code'] ?? 'es';
$responseData['format'] = 'whatsapp_standard';
$message = "Plantilla WhatsApp: {$templateData['name']}";
} else {
// Formato simple
$template = $input['template'] ?? $input['template_name'] ?? null;
$responseData['template_name'] = $template;
$responseData['language'] = $input['language'] ?? 'es';
$responseData['format'] = 'simple';
$message = "Plantilla: {$template}";
}
} else {
$responseData['message'] = $message;
}
echo json_encode([
'success' => true,
'message' => 'Mensaje simulado enviado correctamente (modo debug) - NOTA: En producción, usa plantillas para números que no han respondido en 24h',
'data' => $responseData
]);
exit;
}
// Modo 1: Envío directo a número de teléfono (formato original)
if (isset($input['recipient'])) {
$recipient = $input['recipient'];
$type = $input['type'] ?? 'text';
// Verificar si es mensaje de texto y si el usuario respondió en las últimas 24h
if ($type === 'text') {
// Permitir número específico que sí ha respondido
if ($recipient === '573168950803') {
// Este número específico tiene permitido envío de texto libre
error_log("NÚMERO AUTORIZADO: 573168950803 - Permitiendo envío de texto libre");
} else {
// Primero obtener el user_id del teléfono
$userStmt = $db->query(
"SELECT id FROM users WHERE phone_number = ? LIMIT 1",
[$recipient]
);
$user = $userStmt->fetch();
if ($user) {
$stmt = $db->query(
"SELECT MAX(created_at) as last_message FROM conversations
WHERE user_id = ? AND direction = 'incoming'
AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)",
[$user['id']]
);
$lastMessage = $stmt->fetch();
if (!$lastMessage || !$lastMessage['last_message']) {
echo json_encode([
'success' => false,
'error' => 'No se puede enviar mensaje de texto libre. El usuario no ha respondido en las últimas 24 horas.',
'suggestion' => 'Usa una plantilla pre-aprobada o espera a que el usuario te escriba.',
'error_code' => 'OUTSIDE_24H_WINDOW'
]);
exit;
}
}
// Si no existe el usuario, permitir envío (se creará automáticamente)
}
}
$whatsappService = new WhatsAppService();
$response = null;
switch ($type) {
case 'text':
if (!isset($input['message']) || empty($input['message'])) {
http_response_code(400);
echo json_encode(['error' => 'Mensaje requerido']);
exit;
}
// DEBUG: Mostrar datos de envío de mensaje de texto
error_log("=== DEBUG WHATSAPP TEXT MESSAGE ===");
error_log("Recipient: " . $recipient);
error_log("Message: " . $input['message']);
error_log("Method: sendTextMessage()/sendTextReply()");
error_log("====================================");
// Si viene reply_to, usar reply con contexto
$operatorId = $_SESSION['admin_user']['id'] ?? null;
if (!empty($input['reply_to'])) {
$replyTo = $input['reply_to'];
$response = $whatsappService->sendTextReply($recipient, $replyTo, $input['message'], $operatorId ? ['operator_id' => $operatorId] : null);
} else {
$response = $whatsappService->sendTextMessage($recipient, $input['message'], $operatorId ? ['operator_id' => $operatorId] : null);
}
break;
case 'template':
// Formato 1: Simple (actual)
if (isset($input['template_name']) || (isset($input['template']) && !is_array($input['template']))) {
$template = $input['template'] ?? $input['template_name'] ?? '';
if (empty($template)) {
http_response_code(400);
echo json_encode(['error' => 'Template requerido']);
exit;
}
$language = $input['language'] ?? 'es';
$parameters = $input['parameters'] ?? [];
// Also accept template_params (from form or hidden input)
if ((empty($parameters) || $parameters === []) && isset($input['template_params'])) {
$tp = $input['template_params'];
if (is_string($tp)) {
$decoded = json_decode($tp, true);
if ($decoded !== null) {
$parameters = $decoded;
} else {
// fallback: treat string as a single named parameter value (name)
$parameters = ['name' => $tp];
}
} elseif (is_array($tp)) {
$parameters = $tp;
}
}
$componentsSimple = $input['components'] ?? null;
// Verificar plantilla en base de datos (opcional)
$templateRecord = null;
try {
$stmt = $db->query(
"SELECT * FROM message_templates WHERE template_name = ? LIMIT 1",
[$template]
);
$templateRecord = $stmt->fetch();
} catch (Exception $e) {
// Si no existe la tabla, continuar sin validación local
error_log("Template validation skipped: " . $e->getMessage());
}
// Solo validar si la tabla existe y encontramos registros
if ($templateRecord && $templateRecord['status'] !== 'approved') {
echo json_encode([
'success' => false,
'error' => 'Plantilla no aprobada en el sistema local',
'template_name' => $template,
'template_status' => $templateRecord['status'],
'suggestions' => [
'1. Aprueba la plantilla desde la pestaña Plantillas',
'2. O verifica que esté aprobada en WhatsApp Business Manager',
'3. Usa el nombre exacto de la plantilla'
]
]);
exit;
}
// Usar el idioma de la base de datos si no se especifica
if ($language === 'es' && $templateRecord['language_code']) {
$language = $templateRecord['language_code'];
}
// DEBUG: Mostrar datos de envío de plantilla formato simple
error_log("=== DEBUG WHATSAPP TEMPLATE MESSAGE (SIMPLE FORMAT) ===");
error_log("Recipient: " . $recipient);
error_log("Template Name: " . $template);
error_log("Language: " . $language);
error_log("Parameters: " . json_encode($parameters));
error_log("Template Record Status: " . ($templateRecord ? $templateRecord['status'] : 'not found in DB'));
error_log("Method: sendTemplateMessage()");
error_log("========================================================");
// Intentar envío y, si falla por traducción no encontrada, probar variantes de idioma
try {
$response = $whatsappService->sendTemplateMessage($recipient, $template, $language, $parameters, [], $componentsSimple);
} catch (Exception $e) {
$message = $e->getMessage();
if (strpos($message, 'Template name does not exist in the translation') !== false || strpos($message, '#132001') !== false) {
error_log("Template translation missing for language: $language. Attempting fallbacks...");
$candidates = [];
// Añadir language_code de la BD si existe y es distinto
if (!empty($templateRecord['language_code']) && $templateRecord['language_code'] !== $language) {
$candidates[] = $templateRecord['language_code'];
}
// Si viene con formato regional (es_ES) intentar la base (es)
if (strpos($language, '_') !== false) {
$base = explode('_', $language)[0];
if ($base !== $language) $candidates[] = $base;
} else {
// Si es código corto (es) intentar variantes comunes
if (strlen($language) === 2) {
$candidates[] = strtoupper($language) === $language ? $language : $language . '_ES';
$candidates[] = $language . '_MX';
$candidates[] = $language . '_PE';
}
}
// Eliminar duplicados y vacíos
$candidates = array_values(array_unique(array_filter($candidates)));
foreach ($candidates as $cand) {
try {
error_log("Retrying template send with language candidate: $cand");
$response = $whatsappService->sendTemplateMessage($recipient, $template, $cand, $parameters);
if ($response) {
error_log("Template send succeeded with fallback language: $cand");
break;
}
} catch (Exception $e2) {
error_log("Fallback language $cand failed: " . $e2->getMessage());
// seguir intentando con siguientes candidatos
}
}
if (empty($response)) {
// Re-lanzar el error original si no encontramos nada que funcione
throw $e;
}
} else {
// Re-lanzar si es otro tipo de error
throw $e;
}
}
}
// Formato 2: WhatsApp Business API estándar
elseif (isset($input['template']) && is_array($input['template'])) {
$templateData = $input['template'];
$templateName = $templateData['name'] ?? '';
$languageCode = $templateData['language']['code'] ?? 'es';
$components = $templateData['components'] ?? [];
if (empty($templateName)) {
http_response_code(400);
echo json_encode(['error' => 'Template name requerido en formato WhatsApp']);
exit;
}
// Convertir formato WhatsApp a nuestro servicio
$parameters = [];
foreach ($components as $component) {
if ($component['type'] === 'body' && isset($component['parameters'])) {
foreach ($component['parameters'] as $param) {
$parameters[] = $param['text'] ?? '';
}
}
}
// Preserve raw components (flow/buttons/header/image) and pass through
$componentsArray = $templateData['components'] ?? null;
// DEBUG: Mostrar datos de envío de plantilla formato WhatsApp estándar
error_log("=== DEBUG WHATSAPP TEMPLATE MESSAGE (WHATSAPP STANDARD FORMAT) ===");
error_log("Recipient: " . $recipient);
error_log("Template Name: " . $templateName);
error_log("Language Code: " . $languageCode);
error_log("Original Components: " . json_encode($components));
error_log("Converted Parameters: " . json_encode($parameters));
error_log("Full Template Data: " . json_encode($templateData));
error_log("Method: sendTemplateMessage()");
error_log("=================================================================");
$response = $whatsappService->sendTemplateMessage($recipient, $templateName, $languageCode, $parameters, [], $componentsArray);
}
else {
http_response_code(400);
echo json_encode(['error' => 'Template requerido (formato simple o WhatsApp estándar)']);
exit;
}
break;
default:
http_response_code(400);
echo json_encode(['error' => 'Tipo de mensaje no soportado']);
exit;
}
if ($response) {
// DEBUG: Mostrar respuesta exitosa de WhatsApp
error_log("=== DEBUG WHATSAPP SUCCESS RESPONSE ===");
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]
);
$userId = $stmt ? $db->lastInsertId() : null;
}
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}";
}
// Guardado en BD manejado por WhatsAppService->saveOutgoingMessage()
error_log("Outgoing message saving delegated to WhatsAppService - User ID: $userId, Content: $messageContent");
} else {
error_log("Error: No se pudo obtener o crear user_id para recipient: $recipient");
}
} catch (Exception $e) {
error_log("Error detallado guardando mensaje en base de datos: " . $e->getMessage());
error_log("Stack trace: " . $e->getTraceAsString());
}
echo json_encode([
'success' => true,
'message' => 'Mensaje enviado correctamente',
'whatsapp_response' => $response
]);
} else {
// DEBUG: Mostrar error de respuesta de WhatsApp
error_log("=== DEBUG WHATSAPP ERROR RESPONSE ===");
error_log("Response was NULL or FALSE");
error_log("Type: " . $type);
error_log("====================================");
// Error específico para plantillas con soluciones detalladas
if ($type === 'template') {
$templateName = $input['template_name'] ?? $input['template'] ?? 'unknown';
$language = $input['language'] ?? 'es';
echo json_encode([
'success' => false,
'error' => 'Error #132001: Template name does not exist in the translation',
'template_name' => $templateName,
'language' => $language,
'error_analysis' => [
'El error #132001 indica que WhatsApp no encuentra la plantilla',
'Esto puede suceder por varias razones:'
],
'solutions' => [
'1. CREAR PLANTILLA: Ve a WhatsApp Business Manager → Account Tools → Message Templates',
'2. APROBAR PLANTILLA: Asegúrate que Meta haya aprobado tu plantilla',
'3. NOMBRE EXACTO: Usa el nombre exacto (case-sensitive)',
'4. IDIOMA CORRECTO: Usa el código exacto (en_US, es_ES, etc.)',
'5. TIEMPO DE PROPAGACIÓN: Espera 15-30 min después de la aprobación'
],
'debug_info' => [
'template_attempted' => $templateName,
'language_attempted' => $language,
'common_templates' => ['hello_world', 'sample_shipping_confirmation'],
'common_languages' => ['en_US', 'es_ES', 'es_MX']
],
'next_steps' => [
'1. Verifica en WhatsApp Business Manager si la plantilla existe',
'2. Crea una plantilla simple primero (ej: "hello_world")',
'3. Usa el modo debug para probar sin envío real'
]
]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Error enviando mensaje']);
}
}
// Modo 2: Envío a usuario existente (nuevo formato para conversaciones)
} elseif (isset($input['user_id']) && isset($input['message'])) {
$userId = intval($input['user_id']);
$message = trim($input['message']);
if (!$userId || !$message) {
http_response_code(400);
echo json_encode(['error' => 'user_id y message son requeridos']);
exit;
}
// Obtener información del usuario
$stmt = $db->query(
"SELECT phone_number, name FROM users WHERE id = ?",
[$userId]
);
$user = $stmt->fetch();
if (!$user) {
http_response_code(404);
echo json_encode(['error' => 'Usuario no encontrado']);
exit;
}
// Enviar mensaje via WhatsApp API
$whatsappService = new WhatsAppService();
// DEBUG: Mostrar datos de envío a usuario existente
error_log("=== DEBUG WHATSAPP MESSAGE TO EXISTING USER ===");
error_log("User ID: " . $userId);
error_log("User Name: " . ($user['name'] ?? 'N/A'));
error_log("Phone Number: " . $user['phone_number']);
error_log("Message: " . $message);
error_log("Method: sendTextMessage()");
error_log("===============================================");
$response = $whatsappService->sendTextMessage($user['phone_number'], $message);
if ($response && isset($response['conversations']) && !empty($response['conversations'])) {
// Saving handled by WhatsAppService->saveOutgoingMessage(), skip manual insert to avoid duplicates
echo json_encode([
'success' => true,
'message' => 'Mensaje enviado correctamente',
'whatsapp_response' => $response
]);
} else {
echo json_encode([
'success' => false,
'error' => 'Error al enviar mensaje por WhatsApp'
]);
}
} else {
http_response_code(400);
echo json_encode(['error' => 'Formato de datos inválido. Se requiere recipient o user_id+message']);
exit;
}
} catch (Exception $e) {
error_log("Error in send_message.php: " . $e->getMessage());
http_response_code(500);
echo json_encode(['error' => 'Error interno del servidor: ' . $e->getMessage()]);
}
?>