Files
whatsapp/WEBHOOK_ENDPOINTS.md
2026-06-09 16:44:39 -05:00

164 lines
3.6 KiB
Markdown

# Webhook WhatsApp — Endpoints y Características
## 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;
}
```