High-Level Design (HLD): Timbrador 2.0 — Balanceador y Enrutador de Timbrado (Multi-PAC)
1. Objetivo del Sistema
Diseñar e implementar un sistema intermediario (middleware) basado en eventos para el balanceo y procesamiento asíncrono de peticiones de timbrado de comprobantes fiscales. El sistema debe recibir altos volúmenes de peticiones, enrutarlas de manera inteligente entre múltiples Proveedores Autorizados de Certificación (PACs), y garantizar una alta tolerancia a fallos mediante mecanismos de reintento local y contingencia escalonada (fallback a N PACs). La arquitectura debe ser extensible para permitir la adición sencilla de nuevos conectores y colas en el futuro. Se debe asegurar que ninguna petición se pierda, alertar oportunamente sobre fallos definitivos, y evitar duplicidad de timbrados.
2. Requerimientos Funcionales Formales
2.1 Mensaje Canónico del Servicio Receptor
A partir del análisis de los conectores actuales (Click Factura y Pegaso), se ha extraído la información requerida (idXml, xmlData) para consolidar el mensaje canónico. Las credenciales (usuario, pass) y banderas de entorno (productivo) se manejarán internamente por cada conector PAC para mantener el receptor agnóstico al proveedor.
Payload Esperado (JSON):
{
"idDocumento": "string", // Identificador único del comprobante (previamente idXml)
"xmlData": "string", // Comprobante XML (Base64)
"webhookUrl": "string", // Opcional: URL de callback HTTP POST para ser notificado del resultado definitivo
"opcionesRuteo": { // Opcional
"pacPreferido": "string" // Ej: "PEGASO" o "CLICK_FACTURA"
}
}
- Recepción Canónica (API REST): Un servicio receptor que exponga un API REST. Debe recibir las peticiones de timbrado de forma síncrona usando el Mensaje Canónico, asignar un identificador de rastreo único (Tracking ID), guardar el estado inicial en la base de datos de control y retornar una respuesta rápida de aceptación (HTTP 202 Accepted) al cliente originador.
- Enrutamiento por Reglas de Negocio: El Receptor debe evaluar cada petición entrante y publicarla en la cola correspondiente (Queue 1, Queue 2... Queue N) basándose en una configuración centralizada (ej. porcentaje de balanceo de carga, costos, disponibilidad o reglas específicas por cliente).
- Procesamiento Asíncrono Desacoplado: Se requieren consumidores dedicados (Connectors) que lean los mensajes de sus respectivas colas y se comuniquen con los PACs asignados vía SOA/WSDL. Para agregar un PAC futuro, bastará con aprovisionar una nueva cola y desplegar su Connector asociado.
- Política de Reintentos Locales (Retry Pattern): Cada Connector debe estar configurado para realizar un número máximo de $X$ reintentos (preferentemente con backoff exponencial) ante fallas de comunicación, timeouts o errores temporales (ej. HTTP 500/503) con su PAC destino.
- Validación de Timbre Previo (Anti-Duplicidad): Antes de derivar una petición a un PAC de contingencia (debido a un timeout o error ambiguo en el PAC original), el sistema DEBE validar si la factura ya fue timbrada exitosamente en el PAC original. Esto evita que una factura procesada pero cuya respuesta falló por red sea enviada y timbrada de nuevo en un PAC distinto.
- Contingencia Escalonada (Fallback): Si un mensaje agota los $X$ reintentos en su PAC actual y se confirma que no fue timbrado, el sistema debe re-enrutar el mensaje a la cola del siguiente PAC disponible en la configuración.
- Prevención de Ciclos Infinitos: El sistema debe mantener un historial de los PACs intentados (
pacs_intentados). Si un mensaje agota las opciones de PACs disponibles, no debe volver a intentarse en los anteriores. - Manejo de Fallos Definitivos y Alertas Operativas: Los mensajes que agoten sus intentos en todos los PACs disponibles deben ser enviados a una cola de mensajes muertos (DLQ). Se debe emitir una alerta operativa (email, Slack/Teams) indicando el fallo definitivo para seguimiento técnico.
- Consumo de Webhook Opcional (Notificación de Resultado Definitivo): Si el mensaje canónico incluye el parámetro opcional
webhookUrl, el sistema DEBE consumir de forma asíncrona un webhook mediante HTTP POST al concluir la transacción (ya sea en caso de éxito o de fallo definitivo). La petición enviada al webhook aceptará un JSON con la siguiente estructura:{"idDocumento": "string","xmlData": "string","status": "TIMBRADO_EXITOSO | RECHAZADO_ERROR_NEGOCIO | ERROR_DEFINITIVO_TODOS_PACS"} - Trazabilidad de Estado End-to-End: Todos los componentes (Receptor y Connectors) deben tener acceso a la DB Control para actualizar el estado de la transacción en cada etapa del ciclo de vida (Ej.
RECIBIDO,PROCESANDO_PAC_N,REINTENTANDO_PAC_N,EN_CONTINGENCIA,ERROR_DEFINITIVO_TODOS_PACS,TIMBRADO_EXITOSO). - Endpoints de Consulta de Estado y Bitácora de Ejecución: El API Receptor debe exponer endpoints HTTP GET para permitir la auditoría y monitoreo en tiempo real del estado de cualquier comprobante y su bitácora histórica de eventos:
GET /api/v1/receptor/transacciones/{trackingId}: Retorna la información de control completa (estado actual, PAC actual, PAC final timbrado, arreglo depacs_intentados, reintentos ejecutados, último error,webhook_urly marcas de tiempo).GET /api/v1/receptor/transacciones/documento/{idDocumento}: Permite la búsqueda del estado por el folio/idDocumento asignado por el originador.GET /api/v1/receptor/transacciones/{trackingId}/bitacora: Retorna el historial cronológico completo de eventos almacenados en la tabla de auditoríahistorial_timbrado(id_historial,tracking_id,estado,pac_involucrado,detalle_evento,fecha_registro).
2.2 Visualización de Especificación OpenAPI / Swagger UI
A continuación se incluye la representación gráfica actualizada del Swagger UI del Timbrador 2.0 con todos los endpoints expuestos (recepción y consultas de bitácora):

Resumen del Contrato REST (Swagger)
| Método | Endpoint | Descripción | Parámetros / Schema | Respuesta Éxito |
|---|---|---|---|---|
POST | /api/v1/receptor/timbrar | Recepción de comprobante fiscal | Body: MensajeCanonicoReceptor (idDocumento, xmlData, webhookUrl, opcionesRuteo) | 202 Accepted (trackingId, estado: "RECIBIDO") |
GET | /api/v1/receptor/transacciones/{trackingId} | Consulta de estado por Tracking ID | Path: trackingId (UUID) | 200 OK (RespuestaEstadoTransaccion: estado, pacActual, pacsIntentados, uuidCfdi, etc.) |
GET | /api/v1/receptor/transacciones/documento/{idDocumento} | Consulta de estado por ID Documento | Path: idDocumento (string) | 200 OK (RespuestaEstadoTransaccion) |
GET | /api/v1/receptor/transacciones/{trackingId}/bitacora | Consulta la bitácora histórica de eventos | Path: trackingId (UUID) | 200 OK (RespuestaBitacoraTransaccion: lista cronológica de historial_timbrado) |
2.3 Servicios REST de Consulta de Estado y Bitácora de Auditoría (Telemetría de Operaciones)
Para garantizar la observabilidad total del ciclo de vida de los comprobantes y permitir la resolución transparente de incidencias operativas, el API Receptor expone servicios especializados de consulta síncrona. Estos servicios consumen directamente la Base de Datos de Control (transaccion_timbrado e historial_timbrado), proporcionando visibilidad detallada tanto a los sistemas cliente (ERP/Core) como al equipo de soporte técnico.
2.3.1 Consulta de Estado Transaccional (GET /transacciones/{trackingId} y GET /transacciones/documento/{idDocumento})
Permite conocer el estado instantáneo de la transacción, identificando con precisión si el documento ya fue timbrado, en qué PAC se encuentra actualmente, qué PACs fallaron previamente durante la contingencia y cuál fue el resultado definitivo.
Campos Clave de Respuesta y Significado Operativo:
trackingId(UUID): Identificador único global de la transacción en el middleware.idDocumento(String): Folio o UUID interno provisto por el cliente emisor.estado(String): Estado actual dentro del autómata (RECIBIDO,PROCESANDO_PAC,EN_CONTINGENCIA,TIMBRADO_EXITOSO,RECHAZADO_ERROR_NEGOCIO,ERROR_DEFINITIVO_TODOS_PACS).uuidCfdi(String): Folio Fiscal oficial otorgado por el SAT (solo presente siestado == TIMBRADO_EXITOSO).pacActual(String): PAC que procesó o está procesando la petición (ej."PEGASO").pacsIntentados(Array): Lista ordenada de los PACs que fallaron técnicamente y provocaron un fallback (ej.["CLICK_FACTURA"]). Permite auditar exactamente qué proveedores sufrieron caídas.intentosActuales(Integer): Reintentos locales ejecutados sobre el PAC activo.ultimoError(String): Extracto descriptivo de la última excepción técnica o código de rechazo del PAC.webhookUrl(String): URL registrada para la notificación asíncrona.
Ejemplo de Respuesta de Estado (200 OK - Tras Fallback Exitoso):
{
"trackingId": "trk-550e8400-e29b-41d4-a716-446655440000",
"idDocumento": "DOC-2026-987654321",
"estado": "TIMBRADO_EXITOSO",
"uuidCfdi": "4F9E82B1-3C4D-4E5F-8A9B-0C1D2E3F4A5B",
"pacActual": "PEGASO",
"pacsIntentados": [
"CLICK_FACTURA"
],
"intentosActuales": 0,
"ultimoError": "HTTP 504 Gateway Timeout en Click Factura. Reintentos locales agotados.",
"webhookUrl": "https://api.cliente.com/webhooks/facturacion",
"fechaCreacion": "2026-08-04T10:00:00Z",
"fechaActualizacion": "2026-08-04T10:00:04Z"
}
2.3.2 Consulta de Bitácora Histórica de Eventos (GET /transacciones/{trackingId}/bitacora)
Expone la traza completa de auditoría almacenada en historial_timbrado. Este servicio funciona bajo el principio de Event Sourcing, registrando cada evento relevante en el ciclo de vida de la factura con su marca de tiempo exacta (timestamp).
Eventos Registrados en la Bitácora:
RECIBIDO: Registro del comprobante en la puerta de entrada REST y encolamiento inicial.PROCESANDO_PAC: Inicio de atención por el Connector del PAC $N$.REINTENTO_LOCAL: Detalle del error de red/timeout y contador de reintento local.EN_CONTINGENCIA: Agotamiento de reintentos en PAC $N$, validación anti-duplicidad exitosa (no timbrado previamente) y publicación en la cola del PAC $N+1$.TIMBRADO_EXITOSO: Confirmación del timbre por parte del PAC y obtención del UUID CFDI.NOTIFICACION_WEBHOOK: Resultado de la ejecución HTTP POST hacia lawebhookUrldel cliente.ERROR_DEFINITIVO_TODOS_PACS: Derivación a la DLQ tras agotar la totalidad de PACs configurados.
Ejemplo de Respuesta de Bitácora (200 OK):
{
"trackingId": "trk-550e8400-e29b-41d4-a716-446655440000",
"idDocumento": "DOC-2026-987654321",
"totalEventos": 4,
"bitacora": [
{
"idHistorial": 1001,
"trackingId": "trk-550e8400-e29b-41d4-a716-446655440000",
"estado": "RECIBIDO",
"pacInvolucrado": null,
"detalleEvento": "Comprobante recibido y registrado en DB Control. Encolado en Queue 1 (CLICK_FACTURA).",
"fechaRegistro": "2026-08-04T10:00:00Z"
},
{
"idHistorial": 1002,
"trackingId": "trk-550e8400-e29b-41d4-a716-446655440000",
"estado": "REINTENTANDO_PAC",
"pacInvolucrado": "CLICK_FACTURA",
"detalleEvento": "Intento 1/3 fallido: Connection Timeout (5000ms) al consumir WSDL de Click Factura.",
"fechaRegistro": "2026-08-04T10:00:02Z"
},
{
"idHistorial": 1003,
"trackingId": "trk-550e8400-e29b-41d4-a716-446655440000",
"estado": "EN_CONTINGENCIA",
"pacInvolucrado": "CLICK_FACTURA",
"detalleEvento": "Agotados 3 reintentos en Click Factura. Verificación anti-duplicidad confirma NO timbrado. Redirigiendo a Queue 2 (PEGASO).",
"fechaRegistro": "2026-08-04T10:00:03Z"
},
{
"idHistorial": 1004,
"trackingId": "trk-550e8400-e29b-41d4-a716-446655440000",
"estado": "TIMBRADO_EXITOSO",
"pacInvolucrado": "PEGASO",
"detalleEvento": "Timbrado exitoso en Pegaso. UUID asignado: 4F9E82B1-3C4D-4E5F-8A9B-0C1D2E3F4A5B.",
"fechaRegistro": "2026-08-04T10:00:04Z"
}
]
}
3. Arquitectura del Sistema
La arquitectura implementa el patrón de Mensajería Asíncrona (Asynchronous Messaging) y se compone de los siguientes elementos:
- Receptor (API / Microservicio): Punto de entrada. Valida el payload básico, registra en DB Control y publica el evento en el Message Broker.
- Message Broker (Gestor de Colas):
- Queue 1...N: Alojan mensajes destinados a cada PAC respectivo. Agregar un nuevo PAC implica crear una
Queue N. - DLQ (Dead Letter Queue): Almacena mensajes que fallaron en la contingencia de todos los PACs o que tienen errores irrecuperables de estructura.
- Queue 1...N: Alojan mensajes destinados a cada PAC respectivo. Agregar un nuevo PAC implica crear una
- Connectors (Workers / Consumers): Microservicios independientes (escalables horizontalmente) para cada PAC. Extraen mensajes de sus colas, validan timbres previos, ejecutan la lógica del cliente WSDL hacia el PAC, manejan reintentos y actualizan la DB Control.
- DB Control (Base de Datos): Repositorio relacional (PostgreSQL) para el rastreo del estado. Sirve como fuente de verdad para auditoría y consulta del estado final del comprobante.
- Servicio de Notificación (Alert Manager): Proceso que monitorea la DLQ y dispara las alertas operativas al equipo de soporte.
3.1 Diagrama de Arquitectura General
3.2 Diagrama de Despliegue de Componentes
4. Flujo de Procesamiento y Tolerancia a Fallos
Para lograr la lógica de enrutamiento dinámico a N PACs sin ciclos, el Payload del Mensaje (o los Headers en el broker) debe inyectarse con metadatos de control desde el inicio.
Estructura sugerida de Metadatos del Mensaje:
{
"target_actual": "PAC1",
"pacs_intentados": [], // Agrega el PAC cuando falla definitivamente
"retry_count": 0 // Incrementa en cada fallo local hasta X
}
4.1. Escenario de Éxito (Happy Path)
4.2. Escenario de Fallo y Contingencia (Fallback)
4.3. Escenario de Error de Negocio
4.4. Escenario de Fallo Total y Prevención de Ciclos
5. Consideraciones Técnicas y Patrones Recomendados
- Patrón Circuit Breaker (Cortocircuito Custom/Manual en Connectors): Implementar una lógica custom/manual de cortocircuito en los conectores. El circuito opera mediante una máquina de estados simplificada (
CLOSED,OPEN,HALF-OPEN) gestionada manualmente:- Bloqueo Temporal y Redirección de Tráfico (Estado
OPEN): Si un PAC experimenta degradación o caídas continuas, la lógica custom conmuta a estadoOPEN. Durante este estado, el conector bloquea temporalmente el envío de peticiones a ese PAC y redirige automáticamente todo el tráfico entrante a la cola del siguiente PAC disponible en la secuencia de contingencia, incluso si el PAC bloqueado fue configurado explícitamente comopacPreferidopor el originador. - Detección de Recuperación y Re-habilitación (Estado
HALF-OPENaCLOSED):- Tiempo de Espera en Estado Abierto (
waitDurationInOpenState): Al abrirse el circuito, la implementación manual registra un timestamp de bloqueo y mantiene un temporizador de enfriamiento configurable (ej. 30 a 60 segundos). - Peticiones de Prueba (
HALF-OPEN): Cumplido el tiempo de espera, la lógica custom conmuta al estadoHALF-OPENpermitiendo evaluar un número reducido de peticiones de prueba. - Re-habilitación Definitiva (
CLOSED): Si la secuencia de pruebas resulta exitosa, la lógica restablece los contadores de error y vuelve a estadoCLOSED.
- Tiempo de Espera en Estado Abierto (
- Bloqueo Temporal y Redirección de Tráfico (Estado
- Separación de Errores (Business vs Technical): La lógica de reintentos y contingencia solo debe aplicar para errores técnicos temporales (Timeouts, HTTP 50x, Connection Refused). Si un PAC responde con un error de negocio, el comprobante se marca inmediatamente como
RECHAZADO_ERROR_NEGOCIOy no se reintenta. - Idempotencia y Validación Obligatoria: La validación del estatus de la factura es un paso imperativo antes de encolar en el siguiente PAC.
6. Modelo de Datos y Diccionario
El diagrama ER de la Base de Datos de Control del Timbrador 2.0:
6.1 Tabla Central: transaccion_timbrado
tracking_id(UUID - PK): Identificador único autogenerado por el receptor en el momento de la recepción.id_documento(VARCHAR): Identificador o folio del comprobante provisto por el sistema core (proveniente deidXml).estado(VARCHAR): Estado actual (RECIBIDO,PROCESANDO_PAC,EN_CONTINGENCIA,TIMBRADO_EXITOSO,RECHAZADO_ERROR_NEGOCIO,ERROR_DEFINITIVO_TODOS_PACS).xml_recibido(TEXT/CLOB): Payload original (XML en Base64).xml_timbrado(TEXT/CLOB): Comprobante final con el timbre fiscal aplicado por el PAC.uuid_cfdi(VARCHAR): Folio fiscal oficial (UUID del SAT) obtenido tras un timbrado exitoso.pac_actual(VARCHAR): Identificador del proveedor de certificación que tiene actualmente encolada la petición (ej. "CLICK_FACTURA").pacs_intentados(JSONB/VARCHAR): Arreglo de los PACs que ya se intentaron y fallaron (ej.["CLICK_FACTURA"]).intentos_actuales(INT): Contador de reintentos locales ejecutados sobre elpac_actual.ultimo_error(TEXT): Extracto descriptivo de la última excepción o respuesta de error.webhook_url(VARCHAR): URL opcional provista por el originador para notificar por HTTP POST.fecha_creacion(TIMESTAMP): Momento exacto de recepción.fecha_actualizacion(TIMESTAMP): Momento de última actualización.
6.2 Tabla de Auditoría: historial_timbrado
id_historial(BIGINT - PK): Llave primaria secuencial autoincremental.tracking_id(UUID - FK): Llave foránea haciatransaccion_timbrado.estado(VARCHAR): Estado al que transitó la petición en este evento.pac_involucrado(VARCHAR): Proveedor con el cual ocurrió el evento.detalle_evento(TEXT): Información contextual / traza detallada.fecha_registro(TIMESTAMP): Instante exacto en que ocurrió la transición.