15. Integración con PAC y Timbrado CFDI 4.0
(Última modificación: 9 de Junio de 2026)
Esta sección documenta la arquitectura de integración entre MDX-Cloud y el Proveedor Autorizado de Certificación (PAC) para la emisión, cancelación y gestión de Comprobantes Fiscales Digitales por Internet bajo el estándar CFDI 4.0 del SAT.
💡 Principio Arquitectónico: Toda generación de facturas a partir de remisiones o pedidos ocurre exclusivamente dentro de MDX-Cloud. No se utilizan portales externos de autofacturación. El PAC es únicamente el servicio de sellado criptográfico final.
15.1 Rol del PAC en la Arquitectura
El PAC actúa como intermediario certificado entre MDX-Cloud y el SAT. Su única responsabilidad técnica es:
- Recibir el payload JSON de MDX-Cloud con los datos estructurados de la venta.
- Generar el XML válido bajo el estándar CFDI 4.0 del SAT.
- Sellar el documento y obtener el Timbre Fiscal Digital (TFD).
- Generar la representación impresa (PDF) de la factura.
- Retornar el identificador interno (UID), el UUID del SAT, y permitir la descarga de los archivos.
15.2 Construcción del Payload JSON para Factura.com
El módulo MdxCloud.Modules.CFDI no construye el XML manualmente. En su lugar, transforma los datos del modelo relacional (Kardex, Clientes, Productos) en la estructura JSON requerida por el API v4 de Factura.com.
Mapeo de Campos Críticos
| Campo JSON | Descripción en Factura.com | Fuente en MDX-Cloud |
|---|---|---|
Receptor.UID | ID interno del cliente en el PAC | ventas.clientes.uid_facturacom |
Receptor.RegimenFiscalR | Clave SAT del régimen | ventas.clientes.regimen_fiscal |
TipoDocumento | "factura" (ingreso), "nota_credito" | Tipo de operación |
UsoCFDI | Clave SAT del uso (ej. "G03") | Seleccionado por cliente |
Serie | ID de la serie configurada en el PAC | configuracion.facturacion.serie_id |
FormaPago | Clave SAT (ej. "01" = Efectivo) | Seleccionado en POS |
MetodoPago | "PUE" o "PPD" | Según condiciones de crédito |
Moneda | "MXN" | Constante |
Conceptos[].ClaveProdServ | Clave del producto del SAT | catalogos.productos.clave_sat |
Conceptos[].ClaveUnidad | Clave de unidad de medida | catalogos.presentaciones.unidad_sat |
Estructura del JSON Generado (Ejemplo de Envío)
{
"Receptor": {
"UID": "55c0fdc67593d",
"RegimenFiscalR": "601"
},
"TipoDocumento": "factura",
"UsoCFDI": "G03",
"Serie": 1247,
"FormaPago": "03",
"MetodoPago": "PUE",
"Moneda": "MXN",
"CondicionesDePago": "Pago de contado",
"Conceptos": [
{
"ClaveProdServ": "51101500",
"Cantidad": "10.000000",
"ClaveUnidad": "H87",
"Unidad": "PIEZA",
"Descripcion": "PARACETAMOL 500MG CAJA 10 TAB",
"ValorUnitario": "150.000000",
"Importe": "1500.000000",
"Descuento": "0.00",
"ObjetoImp": "02",
"Impuestos": {
"Traslados": [
{
"Base": "1500.000000",
"Impuesto": "002",
"TipoFactor": "Tasa",
"TasaOCuota": "0.160000",
"Importe": "240.000000"
}
],
"Retenidos": [],
"Locales": []
}
}
]
}
15.3 Integración con la API del PAC
Configuración de Credenciales
Las credenciales del API de Factura.com se almacenan como secretos de entorno (API Key y Secret Key) y se inyectan en los Headers:
# Variables de entorno en el VPS (.env)
PAC_API_URL=https://api.factura.com/v4
PAC_API_KEY=JDJ5JDEwJGRuT1Y3cUM3W...
PAC_SECRET_KEY=JDJ5JDEwJDZaTjRhWD...
Contrato de la Solicitud (REST POST)
POST https://api.factura.com/v4/cfdi/create
Content-Type: application/json
F-PLUGIN: 9d4095c8f7ed5785cb14c0e3b033eeb8252416ed
F-Api-Key: {PAC_API_KEY}
F-Secret-Key: {PAC_SECRET_KEY}
{ Payload JSON ... }
Contrato de la Respuesta (Éxito)
{
"status": "success",
"message": "CFDI creado exitosamente",
"data": {
"UID": "63389a6a27f88",
"UUID": "1a7cf8f9-3406-4024-9028-84266cab1f13",
"Folio": "F 693",
"Status": "enviada"
}
}
15.4 Manejo de Errores y Política de Reintentos
El timbrado puede fallar por causas transitorias (timeout del PAC, mantenimiento del SAT) o permanentes (XML inválido, RFC inexistente). Hangfire gestiona ambos escenarios:
Clasificación de Errores
| Código HTTP / Error PAC | Tipo | Acción |
|---|---|---|
408 Timeout / 503 Service Unavailable | Transitorio | Reintento automático |
500 Internal Server Error (PAC caído) | Transitorio | Reintento automático |
400 Bad Request (XML inválido) | Permanente | Falla a Failed, alerta soporte |
401 Unauthorized (credenciales) | Permanente | Falla a Failed, alerta crítica |
| RFC receptor no encontrado en SAT | Permanente | Falla a Failed, notifica al usuario |
| CFDI duplicado (UUID ya timbrado) | Idempotente | Recuperar UUID existente, marcar Facturado |
Política de Reintentos Exponenciales
// Configuración en Program.cs (Hangfire)
GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute
{
Attempts = 3,
DelaysInSeconds = new[] { 120, 240, 480 } // 2min → 4min → 8min
});
Flujo de Error Permanente
15.5 Cancelación de CFDI ante el SAT
La cancelación de un CFDI es un proceso reversible sujeto a las reglas del SAT. MDX-Cloud la ejecuta en un flujo atómico de tres pasos:
Motivos de Cancelación SAT (CFDI 4.0)
| Clave | Motivo | Requiere CFDI Sustituto |
|---|---|---|
01 | Comprobante emitido con errores con relación | Sí |
02 | Comprobante emitido con errores sin relación | No |
03 | No se llevó a cabo la operación | No |
04 | Operación nominativa relacionada en la factura global | No |
Flujo de Cancelación
15.6 Almacenamiento Permanente de CFDIs
Conforme a la legislación fiscal mexicana, los CFDIs deben conservarse por al menos 5 años. MDX-Cloud los almacena en Cloudflare R2 (bóveda permanente) usando el patrón de Pre-Signed URLs (ver § 06.4):
Convención de Nombres en R2
cfdi/{RFC_EMISOR}/{AÑO}/{MES}/{UUID}.xml
cfdi/{RFC_EMISOR}/{AÑO}/{MES}/{UUID}.pdf
Ejemplo:
cfdi/MDX800101ABC/2026/06/6128a3f1-4c9e-4b2d-8e3a-1234567890ab.xml
cfdi/MDX800101ABC/2026/06/6128a3f1-4c9e-4b2d-8e3a-1234567890ab.pdf
Regla en Base de Datos
La tabla cfdi.comprobantes nunca almacena el binario del XML o PDF. Solo registra la ruta (clave) del objeto en R2:
-- Columnas relevantes en cfdi.comprobantes
uuid_cfdi VARCHAR(36) NOT NULL UNIQUE, -- UUID del SAT
url_xml_r2 TEXT NOT NULL, -- Clave en R2, no URL completa
url_pdf_r2 TEXT, -- Clave en R2
estado VARCHAR(30) NOT NULL, -- Facturado / Cancelado_Fiscal
fecha_timbrado TIMESTAMPTZ NOT NULL
15.7 Verificación y Salud del Servicio PAC
Para detectar indisponibilidad del PAC antes de que afecte operaciones en producción, el backend implementa un health check periódico:
// Registrado en Program.cs
builder.Services.AddHealthChecks()
.AddUrlGroup(
new Uri($"{pacConfig.ApiUrl}/health"),
name: "pac-timbrado",
failureStatus: HealthStatus.Degraded,
tags: new[] { "fiscal", "externo" }
);
El endpoint /health de MDX-Cloud incluye el estado del PAC, visible en el dashboard de Grafana (§ 06.6). Si el PAC reporta Degraded, Grafana dispara una alerta preventiva al equipo de soporte antes de que ocurra un error de timbrado.