Zymplo · Canal propio · Reunión de equipo
El mismo cerebro que responde en WhatsApp, ahora también dentro de la app — con la misma conversación, sin pagarle a Meta por cada mensaje. Todo está construido y probado, detrás de un interruptor apagado. Este informe explica qué tenemos, qué hace, qué falta y qué hace falta para encenderlo.
No es otra app con menús. Es el mismo chat de siempre: el MEI ya sabe usar WhatsApp, así que la app le da exactamente esa experiencia — más lo que WhatsApp no puede dar (historial visual, avisos instantáneos gratis, todo en un lugar).
Empezás una conversación en WhatsApp y la seguís en la app (y al revés) sin perder nada:
el hilo es el mismo (user_{id}_tenant_{empresa}), así que la memoria es compartida gratis.
Tabla de dispositivos + cliente Expo + AppPushClient. El aviso se escribe en el
historial y se manda como push. Reemplaza la plantilla de WhatsApp que cuesta plata.
/app/token cambia la sesión de Oracle por un pase corto atado a (teléfono, empresa).
Reemplaza el token fijo que venía metido en el binario. Convive con el viejo para no romper la app publicada.
/app/chat/history guarda todo y espeja los turnos de WhatsApp. El MEI ve la charla
completa, no importa por dónde escribió.
Los mismos payloads que el bot genera para WhatsApp, mostrados nativos en la app: botones como chips tocables, listas y tarjetas de confirmación.
/app/chat/media: el audio pasa por la misma transcripción y la foto por la misma
visión/OCR que WhatsApp. La prueba de fuego: “terminei o serviço do João, cobra 350” por voz → registra el cobro.
channel_router intenta el push gratis y cae a WhatsApp si hace falta. Además un
medidor de costo real de Meta por usuario (wa_send_ledger) — la base para
recalibrar el precio el 1-sep.
Motor de precio por canal (el mismo plan más barato en la app, financiado por el ahorro de Meta) y la línea del ahorro en el paywall. De paso arreglamos un bug real: el paywall decía R$39 y la tabla cobraba R$19,90 — el MEI veía un precio y pagaba otro.
Antes del PR corrimos una revisión adversarial del código completo. Salieron 9 problemas reales (los verifiqué uno por uno contra el código antes de tocar nada). Todos arreglados, con tests que los fijan.
El endpoint que emite el pase de la app aceptaba cualquier número de teléfono que le mandaran. Como los MEIs publican su WhatsApp, alguien logueado podía pedir un pase con el número de otro y leerle la conversación entera o robarle los avisos. Arreglado: ahora el servidor verifica, con su propia llave, que ese teléfono pertenece de verdad a la empresa de quien pide el pase.
| Sev. | Qué pasaba | Arreglo |
|---|---|---|
| CRÍTICO | Pase (JWT) emitido para cualquier teléfono → robo de identidad entre empresas | El servidor ata teléfono→empresa con su service-key y exige que coincida. Falla cerrado. |
| ALTO | Token viejo (en binarios publicados) leía historial ajeno en los endpoints nuevos | Los endpoints nuevos exigen pase por usuario; el token viejo queda solo para el chat ya publicado. |
| ALTO | Recordatorio dado por “enviado” con solo el push aceptado → se perdía para siempre si el device tenía notifs apagadas | Push solo best-effort fuera de ventana; dentro de ventana WhatsApp sigue garantizado y reintenta. |
| MEDIO | El ruteador daba por entregado un aviso sin fila de historial | Ante error cae a WhatsApp, sin importar la prioridad. |
| MEDIO | Chat con pase sin empresa caía al empresa del body | Falla cerrado (400) — nunca confía en el body. |
| MEDIO | Registro de device sin teléfono en la huella → re-emparejar a otro número no re-registraba | El teléfono entra en la huella. |
| BAJO | El device se ataba al empresa del body | Se ata a la empresa del pase verificado. |
028_app_channel.sql
(dispositivos + mensajes) y 029_wa_send_ledger.sql (medidor de costo). encenderAPP_JWT_SECRET (secreto fuerte,
sin él /app/token responde 503) y APP_CHANNEL_ENABLED=true. encenderAPP_LEGACY_TOKEN_OK=false. despuészymplo-api (channel_pricing).
follow-up/auth/me para cerrar el
caso Equipe (varios usuarios por empresa). follow-upCrea las tablas de dispositivos, mensajes y el medidor de costo. (Victor)
APP_JWT_SECRET
Un secreto fuerte de 32+ bytes. Sin él, el endpoint del pase responde 503 a propósito. (Victor)
APP_CHANNEL_ENABLED=true
Recién acá los endpoints /app/* dejan de responder 404. (Victor)
El paso que confirma que el canal funciona de verdad. (Luz)
WhatsApp ↔ app mismo hilo, voz, foto y push con la app cerrada. (Luz)
APP_PRICE_DISCOUNT_PCT=40–50 (preço de lançamento · decisión de Carlos).
Tras el update forzado del binario: APP_LEGACY_TOKEN_OK=false. (Victor)
Meta publica las tarifas por país. Ahí recalibramos el descuento de la app con los datos reales del
medidor que dejamos construido (wa_ledger.monthly_cost). El motivo de todo esto: desde el
1-oct Meta cobra los mensajes de servicio que hoy son gratis.