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

3.7 KiB

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)

{
  "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)

"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)

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:
$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;
}