Generación de CFDI 4.0

Generar un CFDI sellado

La generación se separa en cuatro endpoints, uno por tipo de comprobante, cada uno con su propia estructura JSON anidada (emisor, receptor, documento y movimientos o pagos):

POST /api/v1/cfdi/generar/ingreso   (tipo I) POST /api/v1/cfdi/generar/egreso    (tipo E) POST /api/v1/cfdi/generar/traslado  (tipo T) POST /api/v1/cfdi/generar/pago      (tipo P, REP con complemento Pagos 2.0) Authorization: Bearer <token> Content-Type: application/json

Genera un CFDI 4.0 sellado con el CSD de la empresa registrada en el panel de empresas: el emisor solo lleva su rfc (el nombre, régimen y sellos salen del registro). El comprobante se valida con el mismo pipeline de validación antes de sellarse; si no pasa, responde 422 con los errores y no se sella. La decodificación es estricta: cualquier campo no admitido por el tipo (incluidos los que el generador deriva) responde 400.

Ingreso y egreso comparten estructura (el tipo lo fija el endpoint): el documento lleva los datos del comprobante incluyendo sub_total, descuento, total e impuestos globales (los envía el cliente y deben cuadrar con los movimientos: si no, 422), y cada movimiento es un concepto con sus impuestos. En egreso, una nota de crédito se liga con cfdi_relacionados tipo 01.

{
  "emisor": {"rfc": "EKU9003173C9"},
  "receptor": {
    "rfc": "XAXX010101000",
    "nombre": "PUBLICO EN GENERAL",
    "domicilio_fiscal_receptor": "01000",
    "regimen_fiscal_receptor": "616",
    "uso_cfdi": "S01"
  },
  "documento": {
    "serie": "A", "folio": "1",
    "moneda": "MXN",
    "forma_pago": "01", "metodo_pago": "PUE",
    "exportacion": "01", "lugar_expedicion": "01000",
    "sub_total": 1000.0,
    "total": 1160.0,
    "impuestos": {
      "total_impuestos_trasladados": 160.0,
      "traslados": [
        {"base": 1000.0, "impuesto": "002", "tipo_factor": "Tasa", "tasa_o_cuota": 0.16, "importe": 160.0}
      ]
    }
  },
  "movimientos": [
    {
      "clave_prod_serv": "01010101",
      "cantidad": 1, "clave_unidad": "H87",
      "descripcion": "Producto",
      "valor_unitario": 1000.0, "importe": 1000.0,
      "objeto_imp": "02",
      "impuestos": {
        "traslados": [
          {"base": 1000.0, "impuesto": "002", "tipo_factor": "Tasa", "tasa_o_cuota": 0.16, "importe": 160.0}
        ]
      }
    }
  ]
}

Traslado deriva la forma canónica (no se pide en el input): Moneda="XXX", SubTotal/ Total 0, ObjetoImp="01" en cada movimiento y sin forma/método de pago, descuento ni impuestos; el documento solo admite serie, folio, fecha, exportacion y lugar_expedicion, y cada movimiento solo clave_prod_serv, cantidad, clave_unidad, unidad, descripcion, valor_unitario e importe.

Pago genera un recibo de pago (REP) con el complemento Pagos 2.0: el generador deriva la forma canónica del comprobante (Moneda="XXX", SubTotal/Total 0, concepto único 84111506/ACT sin impuestos y sin forma/método de pago) y el documento admite los mismos campos que traslado. El body incluye pagos con uno o más pagos, cada uno con su forma_de_pago_p, moneda_p, monto y uno o más doctos_relacionados (UUID del documento, parcialidad y saldos). El generador calcula Totales.MontoTotalPagos (suma de los pagos convertidos con su tipo_cambio_p). Los impuestos del complemento son opcionales y se declaran solo por documento relacionado (campo impuestos con traslados y retenciones: base, impuesto, tipo_factor y tasa_o_cuota); el generador deriva el ObjetoImpDR (02 con impuestos, 01 sin ellos), calcula cada ImporteDR (base × tasa) y agrega por su cuenta los ImpuestosP de cada pago (convertidos a la MonedaP) y los atributos Total* del nodo Totales (en pesos).

{
  "emisor": {"rfc": "EKU9003173C9"},
  "receptor": {
    "rfc": "XAXX010101000",
    "nombre": "PUBLICO EN GENERAL",
    "domicilio_fiscal_receptor": "01000",
    "regimen_fiscal_receptor": "616",
    "uso_cfdi": "CP01"
  },
  "documento": {"exportacion": "01", "lugar_expedicion": "01000"},
  "pagos": [
    {
      "forma_de_pago_p": "03",
      "moneda_p": "USD",
      "tipo_cambio_p": 17.5,
      "monto": 60.0,
      "doctos_relacionados": [
        {
          "id_documento": "F1A2B3C4-1111-4222-8333-944455556666",
          "moneda_dr": "MXN",
          "equivalencia_dr": 17.5,
          "num_parcialidad": 1,
          "imp_saldo_ant": 1000.0,
          "imp_pagado": 875.0,
          "imp_saldo_insoluto": 125.0
        },
        {
          "id_documento": "E5F6A7B8-9999-4888-B777-C66655544433",
          "moneda_dr": "EUR",
          "equivalencia_dr": 0.92,
          "num_parcialidad": 1,
          "imp_saldo_ant": 9.2,
          "imp_pagado": 9.2,
          "imp_saldo_insoluto": 0.0
        }
      ]
    }
  ]
}
// Un docto_relacionado con impuestos (el generador deriva el resto):
{
  "id_documento": "F1A2B3C4-1111-4222-8333-944455556666",
  "moneda_dr": "MXN",
  "num_parcialidad": 1,
  "imp_saldo_ant": 1160.0,
  "imp_pagado": 1160.0,
  "imp_saldo_insoluto": 0.0,
  "impuestos": {
    "traslados": [
      {"base": 1000.0, "impuesto": "002", "tipo_factor": "Tasa", "tasa_o_cuota": 0.16}
    ],
    "retenciones": [
      {"base": 1000.0, "impuesto": "001", "tipo_factor": "Tasa", "tasa_o_cuota": 0.10}
    ]
  }
}

Timbrar el CFDI generado

Los cuatro endpoints de generación aceptan el parámetro opt-in ?timbrar=true: tras sellar el CFDI, la app lo envía al proveedor de timbrado configurado en el servidor (primero: CONTPAQi Factura Electrónica) y responde con el XML ya timbrado. Detalle completo en Timbrado de CFDI.