Skip to main content

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:

  1. Recibir el payload JSON de MDX-Cloud con los datos estructurados de la venta.
  2. Generar el XML válido bajo el estándar CFDI 4.0 del SAT.
  3. Sellar el documento y obtener el Timbre Fiscal Digital (TFD).
  4. Generar la representación impresa (PDF) de la factura.
  5. 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 JSONDescripción en Factura.comFuente en MDX-Cloud
Receptor.UIDID interno del cliente en el PACventas.clientes.uid_facturacom
Receptor.RegimenFiscalRClave SAT del régimenventas.clientes.regimen_fiscal
TipoDocumento"factura" (ingreso), "nota_credito"Tipo de operación
UsoCFDIClave SAT del uso (ej. "G03")Seleccionado por cliente
SerieID de la serie configurada en el PACconfiguracion.facturacion.serie_id
FormaPagoClave SAT (ej. "01" = Efectivo)Seleccionado en POS
MetodoPago"PUE" o "PPD"Según condiciones de crédito
Moneda"MXN"Constante
Conceptos[].ClaveProdServClave del producto del SATcatalogos.productos.clave_sat
Conceptos[].ClaveUnidadClave de unidad de medidacatalogos.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 PACTipoAcción
408 Timeout / 503 Service UnavailableTransitorioReintento automático
500 Internal Server Error (PAC caído)TransitorioReintento automático
400 Bad Request (XML inválido)PermanenteFalla a Failed, alerta soporte
401 Unauthorized (credenciales)PermanenteFalla a Failed, alerta crítica
RFC receptor no encontrado en SATPermanenteFalla a Failed, notifica al usuario
CFDI duplicado (UUID ya timbrado)IdempotenteRecuperar 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)

ClaveMotivoRequiere CFDI Sustituto
01Comprobante emitido con errores con relación
02Comprobante emitido con errores sin relaciónNo
03No se llevó a cabo la operaciónNo
04Operación nominativa relacionada en la factura globalNo

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.