funcional
This commit is contained in:
+121
-74
@@ -1,26 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Servicio de WhatsApp - Envío de mensajes
|
||||
* Fecha: 13 de noviembre de 2025
|
||||
*/
|
||||
|
||||
class WhatsAppService {
|
||||
class WhatsAppService
|
||||
{
|
||||
private $token;
|
||||
private $phoneNumberId;
|
||||
private $apiUrl;
|
||||
private $db;
|
||||
|
||||
public function __construct() {
|
||||
$this->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 {
|
||||
];
|
||||
}
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user