Comunicación POS ↔ Backend¶
Cómo se hablan la terminal y el backend hoy, qué cambió respecto del pasado, y qué patrones de resiliencia hacen que el checkout no se rompa cuando la red tiembla.
El canal: REST sobre /autocompras/v1¶
La terminal habla con el backend por HTTP REST. Todas las mutaciones del ticket son POST bajo el context-path /autocompras/v1. No hay WebSocket ni STOMP en el flujo actual.
sequenceDiagram
participant POS as Terminal (BackendClientBloc)
participant API as Backend (tickets)
Note over POS,API: Mutaciones de ticket (REST)
POS->>API: POST /openTicket
API-->>POS: TicketDto
POS->>API: POST /agregarArticulo
API-->>POS: TicketDto recalculado
POS->>API: POST /payTicket
API-->>POS: TicketDto (PAGADO / VOUCHERPENDING)
POS->>API: POST /closeTicket
API-->>POS: TicketDto (CLOSE)
Note over POS,API: Salud de servicios (polling)
loop cada 60 s (sano) / 15 s (degradado)
POS->>API: GET /status
API-->>POS: { servicesOnline, offlineServices }
end
Quién orquesta esto en la terminal¶
El BackendClientBloc (en lib/viewmodels/backend_client/) es el centro de la comunicación:
- Mutaciones: el evento
SendMessage(destination, request, …)despacha cada operación de ticket alTicketService, que hace elPOSTcorrespondiente. - Salud: un
Timeradaptativo disparaCheckStatusServices, que consulta/statusvíaStatusRepository. Si los servicios están sanos, el timer se reprograma a 60 s; si están degradados, baja a 15 s para detectar la recuperación más rápido.
Identidad de la terminal en cada request¶
El HttpCliente (en lib/services/http/http_cliente.dart) inyecta automáticamente tres headers en cada llamada:
| Header | Para qué |
|---|---|
X-Cod-Terminal |
Identifica la terminal. |
X-Terminal-Uuid |
UUID del device; el backend lo valida contra la tabla pos. |
X-Device-Health |
Última salud de los dispositivos de pago (pinpad/Point), insumo del Cockpit. |
Resiliencia: por qué no se rompe el checkout¶
La terminal opera en un comercio real, con red que se cae. Los mecanismos que la sostienen:
- Polling adaptativo de salud. La terminal siempre sabe si el backend está sano o degradado, y reacciona (60 s ↔ 15 s).
ConnectionRecoveryMixin(en las pantallas de pago QR y Point Smart): maneja timeout + reintento automático de la consulta de transacción, y navega al resultado correcto sin dejar la pantalla colgada.- Idempotencia de pago (
paymentAttemptId). Se genera una vez por intención de pago y se reusa en los reintentos del mismo intento. Así, si la red corta entre "cobré" y "recibí la confirmación", reintentar no genera un doble cobro: el backend deduplica. - Recuperación de pagos in-flight. Al arrancar, la terminal revisa la entidad
IsarPendingPayment(persistida en Isar) para recuperar pagos que quedaron sin resolver en la sesión anterior. Un corte de luz en medio de un cobro no deja la plata en el limbo. - Manejo de errores de transporte. El
HttpClientetraduceSocketException(sin conexión),TimeoutExceptionyHandshakeException(mismatch http/https) a fallas de dominio claras, en vez de propagar excepciones crudas.
El detalle más delicado está en los pagos
La idempotencia y la máquina de estados de Point Smart (created → at_terminal → processed/failed/action_required/expired/...) son lo más fácil de romper. Antes de tocar cualquier flujo de cobro, leé Referencia → Pagos entero.
Lo que cambió: de STOMP a REST¶
Históricamente la terminal usaba STOMP/WebSocket (un broker, suscripciones a tópicos como /topic/status, mutaciones por mensajes). Ese modelo se reemplazó por REST por la fase del cutover descrita en MIGRACION_BLOC_REST.md:
- Las mutaciones pasaron de mensajes STOMP a
POSTREST (uno por operación). - El estado de servicios pasó de una suscripción push a un polling adaptativo.
graph LR
subgraph Antes["Antes (STOMP)"]
A1["mensajes a /app/*"] --> A2["broker"]
A2 --> A3["suscripción /topic/status"]
end
subgraph Ahora["Ahora (REST)"]
B1["POST /autocompras/v1/*"] --> B2["respuesta directa"]
B3["GET /status (polling)"]
end
Vestigios que vas a encontrar
- Backend:
TerminalSessionServiceexiste pero está muerto; no hay@MessageMappingni config de broker. - Terminal: quedan enums y comentarios que nombran STOMP, y la dependencia
stomp_dart_clienttodavía está enpubspec.yaml. La Fase 3 de la migración es eliminarlos.
Ninguno de esos vestigios participa del flujo real. La verdad operativa de hoy es REST.