Files
Lizandro GuarnizoandClaude Opus 5 32c209710c Documentación completa del proyecto en el módulo Soporte
21 documentos en cuatro secciones, escritos sobre el comportamiento real del
sistema —incluidos los casos que costaron diagnosticar esta semana.

Manual de usuario (visible para todos): primeros pasos, recepción, toma de
muestras, portal del enfermero y administración. Orientado a tareas concretas,
no a describir pantallas.

Documentación técnica: índice de módulos, turnero, formularios y firma digital,
WhatsApp y bot, domicilios, webhook (migrado de WEBHOOK_ENDPOINTS.md) e
inventario de endpoints.

Arquitectura: visión general, enrutamiento y registro de módulos, roles y
permisos, modelo de datos, integración con WhatsApp, y decisiones tomadas con
su deuda técnica asociada.

Operación: runbook de incidentes ordenado por síntoma, configuraciones críticas
—incluido qué vive en Meta y no en la base— y despliegue.

Se documentan explícitamente las trampas conocidas: role/role_id que hay que
mantener sincronizados, las columnas can_* que el control de acceso no lee, las
URL de plantilla que no se cambian desde el código, y las columnas históricas
que quedaron en NULL sin forma de recuperarlas.

README_DOCS.md apunta al módulo y explica cómo agregar páginas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 10:14:32 -05:00

167 lines
3.7 KiB
Markdown

# 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;
}
```