Registrar pagos
Registra pagos parciales contra facturas y facturas de compra, y consulta el saldo en curso desde el ledger.
Las facturas y las facturas de compra mantienen un ledger de pagos:
una lista de pagos individuales, cada uno con su propio importe, fecha y
método. Registra los pagos de uno en uno a medida que entra el dinero — la
API recalcula los importes cobrado y pendiente después de cada
entrada y pasa el documento a paid cuando el saldo llega a cero.
No existe un estado «parcialmente pagada» aparte. El avance del cobro se
lee a partir de dos campos derivados, de solo presentación, en la factura:
paid_amount (suma del ledger) y pending_amount
(total − paid_amount). Un documento con pending_amount > 0 sigue
pending; aquel cuyo pending_amount llega a 0 pasa a paid.
Registrar un pago de venta
POST /v1/invoices/{id}/payments añade un pago a una factura de venta.
El body es pequeño:
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
amount | number | Sí | Mayor que 0. No puede superar pending_amount. |
paid_on | string (YYYY-MM-DD) | Sí | La fecha en que se recibió el dinero. |
payment_method | string (enum) | Sí | Uno de los valores del catálogo (ver abajo). |
reference | string | No | Tu propia referencia (p. ej. un número de transferencia). |
notes | string | No | Nota interna libre. |
payment_method es un enum cerrado de siete valores: bank_transfer,
direct_debit, cash, credit_card, check, paypal, other. Obtén el
catálogo con etiquetas desde GET /v1/payment-methods
en lugar de fijar los valores a mano.
La respuesta es 201 Created con el pago recién creado bajo data:
{
"data": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b",
"object": "payment",
"invoice_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"amount": 500.00,
"payment_date": "2026-05-20",
"payment_method": "bank_transfer",
"payment_method_text": "Transferencia bancaria",
"reference": "TRF-2026-0042",
"notes": null,
"created_at": "2026-05-20T10:30:00Z",
"updated_at": "2026-05-20T10:30:00Z"
}
}import os, requests
resp = requests.post(
'https://api.factuarea.com/v1/invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01/payments',
json={
'amount': 500.00,
'paid_on': '2026-05-20',
'payment_method': 'bank_transfer',
'reference': 'TRF-2026-0042',
},
headers={'Authorization': f"Bearer {os.environ['FACTUAREA_API_KEY']}"},
)
resp.raise_for_status()
payment = resp.json()['data']
print(payment['id'], payment['amount'])const res = await fetch(
'https://api.factuarea.com/v1/invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01/payments',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FACTUAREA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 500.0,
paid_on: '2026-05-20',
payment_method: 'bank_transfer',
reference: 'TRF-2026-0042',
}),
},
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data } = await res.json();
console.log(data.id, data.amount);curl -s -X POST \
https://api.factuarea.com/v1/invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01/payments \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 500.00,
"paid_on": "2026-05-20",
"payment_method": "bank_transfer",
"reference": "TRF-2026-0042"
}' | jq '.data'Pagos parciales y saldo
El saldo en curso no vive en el objeto del pago — vive en la
factura. Tras registrar uno o varios pagos, lee la factura
(GET /v1/invoices/{id}) para ver cómo está:
{
"data": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"object": "invoice",
"status": "pending",
"total": 1210.00,
"paid_amount": 500.00,
"pending_amount": 710.00,
"payments": {
"detail": [
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b",
"object": "payment",
"invoice_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"amount": 500.00,
"payment_date": "2026-05-20",
"payment_method": "bank_transfer",
"payment_method_text": "Transferencia bancaria",
"reference": "TRF-2026-0042",
"notes": null,
"created_at": "2026-05-20T10:30:00Z",
"updated_at": "2026-05-20T10:30:00Z"
}
],
"total": 500.00,
"pending": 710.00
}
}
}paid_amount/pending_amount— los totales cobrado y pendiente. Siempre presentes, calculados a partir del ledger.payments.total/payments.pending— las mismas dos cifras, reflejadas dentro del objetopayments. Siempre presentes.payments.detail— el array de pagos individuales. Se materializa solo en el endpoint de detalle (GET /v1/invoices/{id}); en los endpoints de listado llega como[](mientrastotalypendingsiguen poblados) para que los listados sean ligeros. Usa el sub-recurso para obtener el detalle por separado.
Cuando el último pago cierra el saldo (pending_amount llega a 0), la
factura pasa a paid.
Un pago cuyo amount supera pending_amount se rechaza con 422 y
subcode: "payment_exceeds_pending_amount" (param: "amount"). Un pago
exactamente igual al importe pendiente es válido y salda la factura.
Consulta Errores.
Listar pagos
GET /v1/invoices/{id}/payments devuelve el ledger completo de una
factura, del más reciente al más antiguo. Una factura sin pagos devuelve
{ "data": [] }, nunca un 404.
{
"data": [
{
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0b",
"object": "payment",
"invoice_id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a01",
"amount": 500.00,
"payment_date": "2026-05-20",
"payment_method": "bank_transfer",
"payment_method_text": "Transferencia bancaria",
"reference": "TRF-2026-0042",
"notes": null,
"created_at": "2026-05-20T10:30:00Z",
"updated_at": "2026-05-20T10:30:00Z"
}
]
}Pagos de factura de compra
Las facturas de compra mantienen su propio ledger (total_retention, la
retención IRPF agregada, vive en el recurso de la factura de compra). El
contrato es asimétrico respecto al de venta — léelo con atención antes
de reutilizar código:
POST /v1/purchase_invoices/{id}/paymentsdevuelve201con el pago creado bajodata(objetopurchase_invoice_payment), no la factura completa.GET /v1/purchase_invoices/{id}/paymentsdevuelve{ "data": [...] }, del más reciente al más antiguo.- El body añade un
bank_account_idopcional (entero), y aquípayment_methodes un string libre (máx. 30 caracteres), no el enum cerrado que se usa en el lado de venta.
| Campo | Tipo | Requerido | Notas |
|---|---|---|---|
amount | number | Sí | Mayor que 0. No puede superar el importe pendiente. |
paid_on | string (YYYY-MM-DD) | Sí | Entre la fecha de emisión y hoy. |
payment_method | string | Sí | Texto libre, máx. 30 caracteres. |
bank_account_id | integer | No | Cuenta bancaria desde la que se hizo el pago. |
reference | string | No | Tu propia referencia. |
notes | string | No | Nota interna libre. |
{
"data": {
"id": "01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a0c",
"object": "purchase_invoice_payment",
"amount": 423.50,
"paid_on": "2026-05-21",
"payment_method": "transferencia",
"bank_account_id": 12,
"reference": "TRF-2026-0099",
"notes": null,
"created_at": "2026-05-21T09:00:00Z"
}
}import os, requests
resp = requests.post(
'https://api.factuarea.com/v1/purchase_invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a05/payments',
json={
'amount': 423.50,
'paid_on': '2026-05-21',
'payment_method': 'transferencia',
'bank_account_id': 12,
},
headers={'Authorization': f"Bearer {os.environ['FACTUAREA_API_KEY']}"},
)
resp.raise_for_status()
print(resp.json()['data']['id'])const res = await fetch(
'https://api.factuarea.com/v1/purchase_invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a05/payments',
{
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.FACTUAREA_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 423.5,
paid_on: '2026-05-21',
payment_method: 'transferencia',
bank_account_id: 12,
}),
},
);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { data } = await res.json();
console.log(data.id);curl -s -X POST \
https://api.factuarea.com/v1/purchase_invoices/01931b3e-7c4a-7f2e-9a8b-3c5d6e7f8a05/payments \
-H "Authorization: Bearer $FACTUAREA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": 423.50,
"paid_on": "2026-05-21",
"payment_method": "transferencia",
"bank_account_id": 12
}' | jq '.data'Las reglas de pago de la factura de compra (BR-PUR-019) se aplican como
422: un importe por encima del saldo pendiente
(subcode: "payment_exceeds_pending_amount"), una fecha fuera de
fecha_emisión … hoy (subcode: "invalid_payment_date"), o un pago sobre
una factura cancelada (subcode: "purchase_invoice_not_payable").
Métodos de pago
GET /v1/payment-methods devuelve el catálogo cerrado que respalda el
campo payment_method de venta, cada uno con un value y una etiqueta
legible (en español). Es un catálogo de enum global — no específico de
empresa.
{
"data": [
{ "value": "bank_transfer", "label": "Transferencia bancaria" },
{ "value": "direct_debit", "label": "Domiciliación bancaria" },
{ "value": "cash", "label": "Efectivo" },
{ "value": "credit_card", "label": "Tarjeta de crédito" },
{ "value": "check", "label": "Cheque" },
{ "value": "paypal", "label": "PayPal" },
{ "value": "other", "label": "Otro" }
]
}Léelo una vez al arrancar y muestra las etiquetas en tu interfaz; devuelve
el value en payment_method.
Errores
422payment_exceeds_pending_amount— el importe es mayor que el saldo pendiente (param: "amount"). Es una violación de regla de negocio, así que es422, nunca409.409en unPOSTde pago se reserva para el envoltorio estándar de idempotencia / conflicto (unIdempotency-Keyreutilizado con un body distinto, o un conflicto de concurrencia) — no para los datos del pago en sí.
Consulta Errores para el envoltorio completo y el catálogo de códigos.
Importes y fechas
Cómo representa la API el dinero (EUR, dos decimales), las fechas (YYYY-MM-DD), los timestamps (ISO-8601) y la zona horaria Europe/Madrid usada para el reinicio de cuotas.
Versionado
Política de versionado plano /v1 con la cabecera Factuarea-Version. Compromisos de estabilidad y deprecación.