POST /cfdi/complemento-pago timbra un CFDI tipo P (Recibo Electrónico de Pago, o REP)
contra una o varias facturas PPD que ya timbraste con Ipsofactura. Cada elemento de
pagos[].documentos_relacionados apunta a la factura que ese pago liquida.
id_documento y CFDI de otro PAC
Un documento relacionado se referencia de una de dos formas, y sólo una es necesaria:
Un pago que liquida una PPD por folio fiscal
400 VALIDATION_ERROR
(“Cada documento relacionado requiere factura_id o id_documento”). Si id_documento no es
un UUID, también 400 VALIDATION_ERROR.
Sólo facturas timbradas con Ipsofactura
id_documento no es un folio fiscal cualquiera: se busca entre los CFDIs de tu cuenta.
Un folio fiscal que no timbramos nosotros no existe para este endpoint y la respuesta es
400 VALIDATION_ERROR (“CFDI relacionado no encontrado: …”).
Además de existir en tu cuenta, la factura relacionada debe cumplir (si no, 400 VALIDATION_ERROR con el detalle en message):
- ser de
tipo_comprobanteI o E; - tener
metodo_pagoPPD — una PUE no lleva complemento; - estar en estatus timbrado;
- tener el mismo RFC emisor que el complemento.
Esto se diferencia de
cfdi_relacionados al timbrar (nodo CfdiRelacionados), donde sí
puedes declarar folios fiscales que no pasaron por Ipsofactura: ahí el UUID viaja tal cual
al XML y el SAT lo valida. En un complemento de pago no basta con arrastrar el UUID: hay que
calcular saldos.Migración desde otro PAC
Si llegas a Ipsofactura con facturas PPD abiertas timbradas con tu proveedor anterior, esos saldos no se migran. Las opciones son dos:- Liquidarlas en el proveedor anterior: timbra ahí los complementos de pago que falten, y usa Ipsofactura para las facturas nuevas.
- Refacturar: cancela la PPD abierta y vuelve a emitirla con Ipsofactura. La
cancelación sí la puedes hacer desde aquí, por
folio_fiscal— ver Cancelar un CFDI timbrado con otro proveedor y Refacturación.
Parcialidad y saldos: derivados o explícitos
Por omisión no mandasnum_parcialidad, imp_saldo_ant ni imp_saldo_insoluto: los
derivamos del histórico de pagos que ya timbramos contra esa factura. El primer pago arranca
del total de la factura y cada siguiente arrastra el imp_saldo_insoluto del anterior. Si tu
sistema no lleva contabilidad de saldos, no tienes que hacer nada — este es el camino de
siempre y no cambió.
Si tu sistema sí lleva su propia contabilidad, puedes declararlos por documento
relacionado y entonces gana lo que mandas:
Un pago que declara su propia parcialidad y saldos
Qué validamos
Puedes mandar los tres, algunos o ninguno; el que omitas se deriva. Lo que no puedes mandar es una cadena incoherente, y son tres reglas — todas400 VALIDATION_ERROR nombrando el campo,
sin consumir timbre:
- La identidad, con tolerancia cero:
imp_saldo_ant − imp_pagado = imp_saldo_insoluto. Se verifica sobre los valores a dos decimales que realmente vamos a timbrar, así que una request que aceptamos no puede después ser rechazada por el SAT (CRP20244) por un redondeo nuestro. num_parcialidadmonótona: tiene que ser mayor a la última que timbramos para esa factura. Un hueco sí se permite — tu contabilidad puede contar parcialidades que nosotros nunca timbramos, por ejemplo pagos registrados fuera de Ipsofactura. Repetir o ir hacia atrás no, porque dejaría dos REPs reclamando la misma parcialidad del mismo documento.serie_dr/folio_drno pueden contradecir: sólo se aceptan si la factura resuelta no trae serie o folio propios, o si coinciden. Un valor distinto timbraría un REP apuntando a una factura que no existe bajo esa serie y folio.
Estos campos son de
POST /cfdi/complemento-pago. En
timbrado por XML la parcialidad y el saldo
se siguen recalculando a partir de nuestro histórico, no de lo que el XML declare.Egreso aplicado antes del REP
Si aplicaste una nota de crédito (CFDI tipo E) contra la PPD antes de timbrar el complemento, el saldo que tú traes ya viene disminuido y el nuestro no: nosotros derivamos del histórico de pagos, y un egreso no es un pago. Ahí es dondeimp_saldo_ant explícito importa
— mándalo con el saldo real después del egreso y el REP se timbra con la cadena correcta. Sin
él, derivaríamos el saldo previo al egreso y la suma que verías en el XML no cuadraría con tu
contabilidad.
Límite del periodo de prueba
Un complemento de pago consume un timbre igual que una factura, así que el límite de tu periodo de prueba también se aplica aquí: agotado el cupo,POST /cfdi/complemento-pago
responde 402 TRIAL_LIMIT_EXCEEDED antes de resolver nada del request y sin llamar al PAC. Es
el mismo código y el mismo mensaje que en POST /cfdi/timbrar.
Si el timbrado del REP no responde
POST /cfdi/complemento-pago tiene la misma red de seguridad que el timbrado de facturas.
Cuando el PAC no confirma a tiempo, la respuesta es 503 STAMPING_OUTCOME_UNKNOWN con
retry_after, y el REP no se pierde:
- Ipsofactura vuelve a preguntar por ese timbrado en segundo plano.
- Cuando el resultado se confirma, el complemento queda registrado completo —su árbol de
pagos y documentos relacionados incluidos— y se emite el webhook
pago.timbradotardío, idéntico al de un REP timbrado a tiempo. - Un reintento con la misma
idempotency_keyresponde409 DUPLICATE_IN_PROGRESSmientras la reconciliación sigue pendiente, y el201original del complemento —con susdocumentos_relacionados— en cuanto se resuelve.
rfc_receptor: sólo para el sandbox
El receptor de un CFDI tipo P lo determina la factura que liquida —el SAT exige que coincidan—,
así que el REP nunca lleva un receptor propio. Por eso el campo opcional rfc_receptor de
POST /cfdi/complemento-pago no se timbra: existe para que en el
sandbox puedas forzar un escenario con los mismos RFC reservados que
usas en POST /cfdi/timbrar. En producción se ignora.
Forzar un escenario del sandbox en el REP