# Webhook de WhatsApp _Migrado de `WEBHOOK_ENDPOINTS.md` (raíz del repositorio), donde vivía suelto._ ## Endpoint principal ``` URL: /api/webhook.php ``` --- ## GET — Verificación de webhook ``` GET /api/webhook.php?hub.mode=subscribe&hub.verify_token=TOKEN&hub.challenge=CHALLENGE ``` ### Parámetros que envía Meta | Parámetro | Valor esperado | |---|---| | `hub.mode` | `subscribe` | | `hub.verify_token` | El token configurado en `system_config.webhook_verify_token` | | `hub.challenge` | Número aleatorio que debe devolverse tal cual | ### ⚠️ Bug conocido El código lee `$_GET['hub_verify_token']` (con guión bajo), pero PHP convierte los puntos a guiones bajos automáticamente al parsear `$_GET`, por lo que **funciona correctamente**. ### Respuesta exitosa ``` HTTP 200 Body: {challenge} ``` ### Respuesta fallida ``` HTTP 403 Body: {"error":"Token de verificación inválido"} ``` --- ## POST — Recepción de eventos ``` POST /api/webhook.php Content-Type: application/json ``` ### Estructura del payload esperado (Meta Cloud API) ```json { "object": "whatsapp_business_account", "entry": [{ "id": "WABA_ID", "changes": [{ "field": "messages", "value": { "messaging_product": "whatsapp", "metadata": { "phone_number_id": "PHONE_NUMBER_ID" }, "contacts": [{ "wa_id": "573001234567", "profile": { "name": "Nombre Contacto" } }], "messages": [{ "from": "573001234567", "id": "wamid.XXX", "timestamp": "1234567890", "type": "text", "text": { "body": "Hola" } }] } }] }] } ``` ### Tipos de mensaje soportados | `type` | Descripción | |---|---| | `text` | Texto plano | | `image` | Imagen (con caption opcional) | | `audio` | Audio / nota de voz | | `video` | Video | | `document` | Documento / PDF | | `sticker` | Sticker | | `reaction` | Reacción emoji a otro mensaje | | `interactive` | Respuesta de lista o botón | ### El campo `field` del change puede ser - `messages` → mensajes entrantes y estados - `conversations` → alias aceptado también ### Eventos de estado (statuses) ```json "statuses": [{ "id": "wamid.XXX", "status": "sent|delivered|read|failed", "recipient_id": "573001234567" }] ``` ### Respuesta exitosa ``` HTTP 200 Body: {"status":"success"} ``` --- ## Configuración necesaria en `system_config` (BD) | config_key | Descripción | |---|---| | `whatsapp_token` | Access Token de Meta | | `whatsapp_phone_number_id` | Phone Number ID de la línea | | `webhook_verify_token` | Token de verificación del webhook | | `whatsapp_api_url` | `https://graph.facebook.com/v22.0/` | --- ## Variables de entorno equivalentes (`.env`) ```env WHATSAPP_TOKEN= WHATSAPP_PHONE_NUMBER_ID= WEBHOOK_VERIFY_TOKEN= WHATSAPP_API_URL=https://graph.facebook.com/v22.0/ DB_HOST= DB_PORT=3306 DB_NAME= DB_USER= DB_PASS= ``` --- ## Tablas BD que usa el webhook | Tabla | Uso | |---|---| | `users` | Crea o busca usuario por `phone_number` | | `conversations` | Guarda cada mensaje (deduplicado por `message_id`) | | `webhook_logs` | Registra el payload crudo de cada POST | | `notifications` | Crea aviso de nuevo mensaje entrante | | `media_queue` | Encola media que no pudo descargarse en el momento | | `system_config` | Lee tokens y configuración | --- ## Seguridad — pendiente de implementar - No valida la firma `X-Hub-Signature-256` en el POST. - Se recomienda agregar antes de procesar: ```php $signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? ''; $expected = 'sha256=' . hash_hmac('sha256', $input, APP_SECRET); if (!hash_equals($expected, $signature)) { http_response_code(401); exit; } ```