Nuevo módulo `soporte` con la documentación del proyecto en cuatro secciones:
manual de usuario (visible para todos), y documentación técnica, arquitectura
y operación (solo administradores).
- Markdown.php: renderizador propio del subconjunto que usa la documentación
(encabezados, listas anidadas, tablas, código, citas). Escapa todo el texto
antes de aplicar formato, así que los .md no pueden inyectar HTML. Se
prefirió un archivo auditable a incorporar una dependencia externa.
- DocIndex.php: descubre los .md, arma el árbol, resuelve acceso por sección
y construye el índice del buscador.
- Generadores.php: expande marcadores {{modulos}}, {{endpoints}}, {{tablas}},
{{roles}} y {{servicios}} leyendo el código y la base en cada carga, para
que los inventarios no puedan quedar desactualizados.
Se registra en SYSTEM_MODULES y se concede a los 12 roles con permission=read.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
86 lines
3.9 KiB
Markdown
86 lines
3.9 KiB
Markdown
# Integración con WhatsApp
|
|
|
|
Es la parte del sistema con más piezas fuera de nuestro control. Buena parte de la configuración vive **en Meta**, no en la base de datos, y eso explica varios comportamientos que de otro modo parecen inexplicables.
|
|
|
|
## Dos números, una misma cuenta
|
|
|
|
El laboratorio opera con dos líneas sobre la misma cuenta de WhatsApp Business (WABA):
|
|
|
|
| Canal | Configuración | Para qué |
|
|
|---|---|---|
|
|
| Principal | `whatsapp_phone_number_id` | Bot de atención general |
|
|
| Turnero | `whatsapp_phone_number_id_turnero` | Consentimientos, encuestas y avisos de turno |
|
|
|
|
Se elige al construir el servicio:
|
|
|
|
```php
|
|
$wa = new WhatsAppService(); // línea principal
|
|
$wa = new WhatsAppService('turnero'); // línea del turnero
|
|
```
|
|
|
|
> Si un mensaje sale por el número equivocado, casi siempre es porque se instanció sin el canal. Es el mismo WABA y el mismo token: **lo único que cambia es el `phone_number_id`**.
|
|
|
|
## Configuración
|
|
|
|
Todo en `system_config`:
|
|
|
|
| Clave | Qué es |
|
|
|---|---|
|
|
| `whatsapp_token` | Token de acceso a la API |
|
|
| `whatsapp_api_url` | URL base de la Cloud API |
|
|
| `whatsapp_business_account_id` | Identificador del WABA |
|
|
| `whatsapp_phone_number_id` | Número principal |
|
|
| `whatsapp_phone_number_id_turnero` | Número del turnero |
|
|
| `webhook_verify_token` | Verificación del webhook |
|
|
|
|
## Plantillas: la parte que no controlamos
|
|
|
|
Para escribir primero a alguien (fuera de la ventana de 24 horas) hay que usar una **plantilla aprobada por Meta**. `message_templates` guarda una copia local, pero **la copia no manda**: la versión real está en Meta.
|
|
|
|
Esto tiene una consecuencia importante y poco intuitiva:
|
|
|
|
> **Las URL de los botones viven en la plantilla, no en nuestro código.**
|
|
|
|
La plantilla `consentimiento_turno_v2` tiene un botón así:
|
|
|
|
```
|
|
https://erp.laboratorioximenacaicedo.com/form_cliente.php?t={{1}}
|
|
```
|
|
|
|
Nuestro código solo envía el token como `{{1}}`. Cambiar el código **no cambia** el enlace que recibe el paciente: hay que editar la plantilla en el WhatsApp Manager de Meta y esperar la reaprobación.
|
|
|
|
El único lugar donde sí armamos la URL completa es el **respaldo en texto plano**, que se usa cuando falla el envío por plantilla (`modules/turnero/api/send_consentimiento.php`).
|
|
|
|
## Por qué el enlace pasa por dos páginas
|
|
|
|
El botón apunta a `form_cliente.php?t=<UUID>`, pero el consentimiento del turnero lo muestra `ver_formulario_enviado.php?token=<UUID>`.
|
|
|
|
`form_cliente.php` detecta que el token tiene formato UUID —o sea, que viene del turnero— y redirige. Convive así porque la plantilla ya estaba aprobada apuntando a la página de envíos, y cambiarla obliga a otra ronda de aprobación en Meta.
|
|
|
|
## Términos y condiciones
|
|
|
|
Antes de conversar, el bot exige aceptar los términos. El usuario responde **ACEPTO** o **NO ACEPTO**.
|
|
|
|
La URL del documento está escrita en **dos lugares** y hay que cambiarlos juntos:
|
|
|
|
| Dónde | Rol |
|
|
|---|---|
|
|
| `terms_versions.documento_url` | El bot la adjunta al final del mensaje |
|
|
| `system_config.terms_message` | Va escrita dentro del texto de bienvenida |
|
|
|
|
Se vuelve a pedir la aceptación si: nunca aceptó, pasaron más de 6 meses, o hay una versión nueva con `forzar_reenvio`.
|
|
|
|
## El webhook
|
|
|
|
Meta envía los mensajes entrantes al webhook, que los registra y se los pasa a `BotService`. Ahí se decide si responde el bot automático o queda para un operador humano, según el estado de la conversación y el horario de atención (`BusinessHoursService`).
|
|
|
|
## Qué revisar cuando algo falla
|
|
|
|
| Síntoma | Dónde mirar primero |
|
|
|---|---|
|
|
| El mensaje sale por el número equivocado | Que se haya pasado `'turnero'` al constructor |
|
|
| Un enlace llega roto o apunta mal | La plantilla en Meta, no el código |
|
|
| No llega ninguna plantilla | Estado de aprobación en el WhatsApp Manager |
|
|
| Falla el envío pero llega un texto plano | Es el respaldo actuando: la plantilla falló |
|
|
| El bot no responde | `webhook_logs`, y el horario de atención |
|