Saltar a contenido

RF-004 — Estado de pago en tiempo real

Estado En uso por Bancard QR y Bancard vPOS; pendiente de revisión
Tipo Capa propia sobre el servidor HTTP de Medusa (Server-Sent Events)
Ubicación src/lib/sse-manager.ts · src/api/payment-events/[session_id]/
Depende de RF-003 Bancard QR · RF-011 Bancard vPOS · Modelo de intentos de pago

Requisito

Informar al storefront el cambio de estado de una sesión de pago asincrónica —confirmado, rechazado, expirado o cancelado— sin que el cliente recargue la página ni consulte de forma periódica, y liberar la conexión cuando el navegador se desconecta. Lo usan los dos medios cuyo pago confirma Bancard: el QR y la tarjeta por vPOS.

Solución adoptada

Exponer un canal Server-Sent Events sobre el propio servidor HTTP de Medusa, con un registro en memoria de conexiones indexado por sesión de pago. La decisión de usar SSE en lugar de Socket.IO está documentada en el código: evita puerto adicional y dependencias externas.

sequenceDiagram
    participant N as Navegador
    participant API as Backend
    participant BAN as Bancard
    N->>API: GET /payment-events/:session_id?publishable_api_key=pk_...
    API-->>N: event: connected
    Note over N,API: Conexión abierta, sin timeout de socket
    BAN->>API: Webhook de confirmación
    API-->>N: event: payment_status {status: captured, order_id}
    N->>N: Redirigir a la confirmación del pedido
    N--xAPI: close
    API->>API: Liberar la conexión del registro

Contrato del canal

Aspecto Definición
Ruta GET /payment-events/:session_id — fuera de /store porque EventSource no admite cabeceras propias
Autenticación publishable_api_key por query string, validada por prefijo pk_
Evento de apertura connected con el session_id
Evento de estado payment_status con status en captured, failed, expired o canceled
Carga útil adicional order_id, nombre del pagador, marcas de tiempo y código de respuesta según el caso
CORS Origen reflejado del solicitante, con credenciales habilitadas y preflight OPTIONS

Reglas de negocio

  • Emitir a todas las conexiones abiertas de una misma sesión de pago y descartar las que ya cerraron.
  • Desactivar el timeout del socket y el buffering intermedio (X-Accel-Buffering: no) para que el evento llegue de inmediato.
  • Tratar la emisión como accesoria: una falla en el canal no interrumpe la captura del pago ni el cierre del intento.
  • Cada medio ofrece su propia consulta puntual de estado como respaldo del canal: GET /store/bancard-qr/sessions/:id/status y GET /store/bancard-vpos/sessions/:id/status, ambas con el carrito dueño. La ruta común /store/payment-status/:session_id del modelo anterior fue eliminada al migrar el QR (#263).

Criterios de aceptación

# Criterio
1 Establecer la conexión y recibir el evento connected con clave publicable válida
2 Rechazar con 401 la conexión sin publishable_api_key o con formato inválido
3 Recibir payment_status con estado captured dentro del mismo segundo del webhook
4 Recibir payment_status con estado expired al vencer sin pago (QR vencido o intento de tarjeta conciliado)
5 Liberar el registro de la sesión al cerrarse la última conexión

Limitaciones conocidas

  • Mantener el registro de conexiones en memoria del proceso: con más de una instancia, el evento solo alcanza a los clientes conectados a la instancia que procesó el webhook.
  • Validar la clave publicable solo por prefijo, sin verificarla contra el módulo API Key.
  • No reemitir eventos perdidos: un cliente que se conecta después de la confirmación depende de la consulta puntual de estado.