164 lines
3.6 KiB
Markdown
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;
|
|
}
|
|
```
|