Cobros con tarjeta
Host Factura no es sólo facturación: también cobra. El módulo de pagos genera links de cobro, cobra con tarjetas que el cliente ya tiene guardadas, maneja suscripciones recurrentes y —esto es lo propio de Host Factura— puede emitir el CFE del cobro cuando el cobro se confirma.
Cobrar y facturar son dos cosas separadas. Un cobro con tarjeta no emite ningún comprobante salvo que lo pidas explícitamente, y podés emitirlo después. Para cobrar no necesitás saber nada de facturación.
Lo primero: conocer la configuración
Sección titulada «Lo primero: conocer la configuración»Las preferencias de cobro de la empresa las administra la propia empresa desde el panel, no la integración. Lo que tu sistema necesita saber al arrancar —si la cuenta puede cobrar y si emite comprobante fiscal— viene en el diagnóstico de la credencial:
GET /api/v1/echo{ "data": { "facturacionEnabled": true, "habilitado": true, "politicaFiscal": "facturar_al_cobrar", "mediosHabilitados": ["cards", "banks", "paynet"], "notificarComprador": true }}| Campo | Qué significa |
|---|---|
facturacionEnabled | Si la cuenta emite CFE. Con false, la única política posible es solo_registrar |
habilitado | Interruptor de cobros de la empresa. false corta links y cobros nuevos |
politicaFiscal | Qué se emite cuando el cobro se confirma (ver más abajo) |
mediosHabilitados | cards, banks, paynet — lo que se ofrece en la página de pago |
notificarComprador | Si se le avisa por correo al comprador cuando su pago se registra |
Monedas: sólo UYU y USD
Sección titulada «Monedas: sólo UYU y USD»Flujo 1 — link de cobro
Sección titulada «Flujo 1 — link de cobro»El camino por defecto: el comprador paga en una página segura externa y vos no tocás datos de tarjeta.
POST /api/v1/pagos/linksAuthorization: Bearer <API_KEY>Idempotency-Key: orden-12345-link-1Content-Type: application/json
{ "monto": "1234.56", "moneda": "UYU", "returnUrl": "https://tienda.ejemplo.com/pago/retorno", "referenciaIntegrador": "orden-12345", "politicaFiscal": "facturar_al_cobrar", "borradorCfe": { "tipo": 101, "items": [{ "name": "Servicio mensual", "quantity": 1, "price": 1234.56, "billingIndex": 3 }] }}{ "data": { "solicitud": { "id": "8f14e45f-ceea-467a-9f5a-2b1c3d4e5f60", "referenciaIntegrador": "orden-12345", "estado": "pendiente", "monto": "1234.56", "moneda": "UYU", "tieneBorrador": true }, "redirectUrl": "https://pagos.ejemplo.com/checkout/abc123", "gatewayToken": "abc123" }, "meta": { "reusada": false }}Redirigís al comprador a redirectUrl (o se lo mandás por correo con
POST /v1/pagos/links/{id}/enviar-email).
El borrador del comprobante
Sección titulada «El borrador del comprobante»Si mandás borradorCfe, el comprobante se valida al crear el link (ítems, CAE vigente, certificado,
simulación de emisión) y se emite recién cuando el cobro se confirma. Es deliberado: un CFE quema su
número aunque después se anule.
Si la validación falla, la respuesta es 422 PAGOS_BORRADOR_INVALIDO con la lista de problemas en
error.details.detalles. Corregí y volvé a crear el link: es mucho más barato que descubrirlo cuando el
cliente ya pagó.
referenciaIntegrador: tu número de orden
Sección titulada «referenciaIntegrador: tu número de orden»El campo que hace operable todo lo demás.
- Es único por cuenta: un segundo link con la misma referencia y otra
Idempotency-Keyse rechaza con409 PAGOS_REFERENCIA_DUPLICADA. Es la defensa contra cobrar dos veces la misma orden. - Sirve como filtro exacto en
GET /v1/pagos/links?referenciaIntegrador=…yGET /v1/pagos?referenciaIntegrador=…. - En una cuenta sin facturación electrónica es el único identificador que el operador reconoce: no hay número de CFE con el que cruzar.
Flujo 2 — cobro con tarjeta registrada
Sección titulada «Flujo 2 — cobro con tarjeta registrada»Para cobros desatendidos: tu backend cobra sin que el comprador esté presente. Son cuatro llamadas:
POST /api/v1/clientes → clienteId (una vez por cliente)POST /api/v1/pagos/clientes/{clienteId}/tarjetas/alta (una vez por tarjeta)GET /api/v1/pagos/clientes/{clienteId}/tarjetas → tarjetaIdPOST /api/v1/pagos/cobrar { tarjetaId, monto, moneda } (cada cobro)Paso 1: el cliente
Sección titulada «Paso 1: el cliente»Toda tarjeta pertenece a un cliente. Lo creás (o lo recuperás) con tu propio identificador:
POST /api/v1/clientes
{ "nombre": "Ana Pérez", "referenciaIntegrador": "usuario-4821", "email": "ana@ejemplo.com"}Es un upsert: con la misma referenciaIntegrador devuelve siempre el mismo cliente, así que no
necesitás guardar nuestro id. El documento es opcional: para guardarle una tarjeta y cobrarle no
hace falta. Ver Clientes.
Paso 2: enrolar la tarjeta
Sección titulada «Paso 2: enrolar la tarjeta»POST /api/v1/pagos/clientes/{clienteId}/tarjetas/altaCuerpo opcional: cliente con el contacto del titular (firstName, lastName, email, phone,
documentNumber) para el antifraude de la red de pagos. Devuelve registrationUrl, metodo y campos.
La respuesta no es la confirmación: la tarjeta aparece en el listado cuando llega el aviso de la red de pagos,
y ahí se emite el webhook tarjeta.enrolada.
Paso 3: elegir la tarjeta
Sección titulada «Paso 3: elegir la tarjeta»GET /api/v1/pagos/clientes/{clienteId}/tarjetas?soloElegiblesRecurrente=trueNunca se devuelve PAN, CVV ni el token de la tarjeta: sólo mascara, brandCode, brand, dueDate y el
id con el que se cobra.
Paso 4: cobrar
Sección titulada «Paso 4: cobrar»POST /api/v1/pagos/cobrarIdempotency-Key: orden-12345-cobro-1
{ "tarjetaId": "3a2f1e0d-9c8b-4a7f-8e6d-5c4b3a2f1e0d", "monto": "1500.00", "moneda": "UYU", "descripcion": "Orden 12345"}{ "data": { "solicitudId": "8f14e45f-ceea-467a-9f5a-2b1c3d4e5f60", "pagoId": "3c9a7b1d-2e4f-5a6b-7c8d-9e0f1a2b3c4d", "yaEstabaCobrado": false, "facturado": false }, "meta": { "idempotencyKey": "orden-12345-cobro-1" }}El cliente sale de la tarjeta: no se manda. El cuerpo es una lista cerrada: cualquier otro campo es un 422.
Idempotency-Key es obligatoria acá. Sin clave, cada llamada es un cobro nuevo: un reintento por timeout
de red te cobraría dos veces al cliente. Sin cabecera, 400 PAGOS_IDEMPOTENCY_KEY_REQUERIDA; con un formato
inválido (8 a 128 caracteres de A-Za-z0-9._:~-), 400 PAGOS_IDEMPOTENCY_KEY_INVALIDA. Nunca se deriva ni
se inventa una. Usá una clave distinta para cada cobro distinto: la clave de un cobro no vence.
Cobrar y, además, facturar
Sección titulada «Cobrar y, además, facturar»Sin nada más, el cobro no emite comprobante (facturado: false). Si querés que el CFE salga junto con el
cobro, agregá el bloque factura:
{ "tarjetaId": "3a2f1e0d-9c8b-4a7f-8e6d-5c4b3a2f1e0d", "monto": "1500.00", "moneda": "UYU", "factura": { "tipo": "contado", "borrador": { "tipo": 101, "items": [{ "name": "Orden 12345", "quantity": 1, "price": 1229.51, "billingIndex": 3 }] } }}factura.tipo | Qué emite | Dato |
|---|---|---|
contado | e-Ticket o e-Factura por lo cobrado | borrador: la misma forma que el cuerpo de emitir un CFE |
recibo | e-Recibo que salda facturas ya emitidas | facturas: [{ "cfeEmitidoId", "monto" }], hasta 40; la suma tiene que dar lo cobrado |
El comprobante se valida antes de cobrar: si no se puede emitir, no se cobra y la respuesta es
422 PAGOS_BORRADOR_INVALIDO con los problemas en error.details.detalles. Pedir factura exige también el
alcance cfe:emitir, y en una empresa sin facturación electrónica da 409 PAGOS_POLITICA_REQUIERE_FACTURACION.
Cómo leer el resultado
Sección titulada «Cómo leer el resultado»| HTTP | Qué pasó | Qué hacer |
|---|---|---|
200 | Cobrado. yaEstabaCobrado: true = era el mismo cobro de un intento anterior | Éxito |
400 | Falta la clave de idempotencia o es inválida | Corregir el request |
403 | Pediste factura y la credencial no tiene cfe:emitir | Cobrar sin factura, o ampliar la credencial |
409 | Tarjeta no elegible, cobros apagados, clave reciclada, o factura en una empresa que no emite | Corregir |
422 | Rechazo definitivo (declinada, moneda no soportada), o factura no emitible (no se cobró) | No reintentar igual |
503 | Fallo transitorio, o el cobro quedó en_verificacion | Reintentar con la misma clave |
Flujo 3 — suscripciones
Sección titulada «Flujo 3 — suscripciones»POST /api/v1/pagos/planes → el monto y la periodicidad viven en el PLANPOST /api/v1/pagos/suscripciones/invitaciones → cliente + plan; devuelve la url a compartirLa invitación es la única forma de que nazca una suscripción: la empresa invita y el cliente contrata desde el link eligiendo su tarjeta. No se puede suscribir a alguien con una tarjeta que ya tenés guardada — quien paga tiene que consentir el débito recurrente, y ese consentimiento se da una sola vez, en el link.
intervalo: weekly, monthly, quarterly, semiannual, annual.
Pausar, reanudar y cancelar: el estado lo define la red de pagos. Se pide la transición y se
guarda lo que responde; no se da por pausada por optimismo. Una transición rechazada devuelve
409 PAGOS_SUSCRIPCION_TRANSICION_INVALIDA.
Cancelar también deshabilita la factura recurrente vinculada: dejarla activa seguiría facturando un servicio que ya nadie paga.
Cambiar el precio
Sección titulada «Cambiar el precio»El precio de una suscripción no se edita: se programa.
POST /api/v1/pagos/suscripciones/{id}/precio → { "monto": "1800.00", "motivo": "Ajuste anual" }DELETE /api/v1/pagos/suscripciones/{id}/precio → cancela el cambio que todavía no rigeNo se manda la fecha de vigencia: la calcula el servidor con el preaviso de la cuenta. Si quien
arma el request pudiera elegirla, el preaviso sería decorativo. La respuesta trae vigenteDesde y
rigeEnElProximoCobro: eso es lo que hay que comunicarle al cliente.
El puente fiscal: qué se emite y cuándo
Sección titulada «El puente fiscal: qué se emite y cuándo»| Política | Qué se emite al confirmarse el cobro |
|---|---|
facturar_al_cobrar | e-Ticket / e-Factura al contado, por el monto cobrado |
recibo_al_cobrar | CFE de cobranza (e-Recibo). Para cobrar una factura a crédito ya emitida |
solo_registrar | Nada. Es la única posible si la cuenta no emite CFE |
Quién decide la política:
- Cobro con tarjeta: el propio cobro, con el bloque
factura(contado=facturar_al_cobrar,recibo=recibo_al_cobrar). Sin bloque,solo_registrar. La configuración de la empresa no se aplica. - Link de cobro: la del link manda; si no tiene, la de la empresa.
Además:
- El comprobante sale cuando el cobro se confirma, no cuando se crea el link.
- Si el importe informado no cuadra con el pedido, la plata se registra igual pero no se dispara la
emisión: queda en
pendiente_facturacionpara que una persona lo resuelva conPOST /v1/pagos/{id}/facturar. reciboCfeIdno garantiza que DGI lo aceptó. Un comprobante puede quedar emitido y rechazado: el estado real se consulta conGET /v1/cfe/info/{id}.
Facturar después
Sección titulada «Facturar después»POST /api/v1/pagos/{id}/facturar → alcance cfe:emitir
{ "factura": { "tipo": "contado", "borrador": { "tipo": 101, "items": [ … ] } }}El mismo bloque factura que en el cobro, validado antes de emitir. Sirve para dos cosas: emitir el
comprobante de un cobro que se hizo sin él, y corregir y reintentar uno que quedó en
pendiente_facturacion (sin cuerpo, reintenta con lo que ya tenía). Emite un CFE real y consume numeración.
El comprobante de pago NO fiscal
Sección titulada «El comprobante de pago NO fiscal»GET /api/v1/pagos/{id}/comprobante.pdfNo es un CFE: no numera, no tiene CAE ni QR, no va a DGI y lleva la leyenda “Comprobante de pago. No es un comprobante fiscal (CFE)”. Es el único papel que una cuenta sin facturación electrónica le puede dar a su comprador. Si la cuenta factura, lo que corresponde entregar es la representación impresa del CFE.
en_verificacion: el estado que hay que tratar bien
Sección titulada «en_verificacion: el estado que hay que tratar bien»La red de pagos no contestó la captura. El cobro puede estar hecho. Se emite el webhook
pago.en_verificacion.
| Qué NO hacer | Por qué |
|---|---|
| Entregar el producto | Todavía no hay cobro confirmado |
| Declararlo impago | Puede haber plata adentro |
Reintentar con otra Idempotency-Key | Eso sí cobra dos veces |
| Mostrar un botón “reintentar cobro” | Misma razón |
Qué hacer: esperar el pago.confirmado o el pago.fallido que resuelve el job de conciliación, o reintentar
con la misma clave (viene en datos.idempotencyKey del evento).
Un cobro en en_verificacion lo resuelve la conciliación automática, que es la única que sabe
registrarlo con todas sus salvaguardas. Si uno queda trabado ahí más tiempo del razonable, es un caso
de soporte, no algo que la integración deba destrabar por su cuenta.
Estados
Sección titulada «Estados»Link (solicitud de pago)
Sección titulada «Link (solicitud de pago)»| Estado | Significa |
|---|---|
borrador | Creada, sin link de pago todavía |
pendiente | Link emitido, esperando que el comprador pague |
en_verificacion | Ambiguo: puede estar cobrado. Nunca dispara la emisión del CFE |
pagada | Llegó el aviso y el importe cerró. Es lo que dispara la emisión |
expirada | Venció expiraEn. Creá un link nuevo |
anulada | Anulada. Ya no se puede pagar |
fallida | El cobro falló de forma definitiva |
| Estado | Significa |
|---|---|
aprobado | La plata entró y no hay comprobante: el cobro no lo pidió. Se puede facturar después |
pendiente_facturacion | Plata adentro y la emisión falló. Trae intentosFacturacion y ultimoErrorFacturacion |
facturado | Hay reciboCfeId. No garantiza que DGI lo aceptó |
anulado | Revertido, total o parcialmente |
fallido | No prosperó |
El campo anulable del cobro es la fuente de verdad para saber si POST /v1/pagos/{id}/anular aplica.
Un cobro con comprobante fiscal emitido no es anulable: la reversa de un CFE es una nota de crédito,
porque el número ya se consumió.
Suscripción
Sección titulada «Suscripción»Copia del estado en la red de pagos: active, past_due, paused, cancelled. Ortogonal al estado,
requiereCobroManual significa que la tarjeta no sirve para cobro desatendido: no es un fallo puntual,
es estructural mientras no se cambie la tarjeta.
Conciliar
Sección titulada «Conciliar»GET /api/v1/pagos?estado=pendiente_facturacion → el comprobante se pidió y fallóGET /api/v1/pagos?estado=aprobado → cobros sin comprobante (no se pidió)GET /api/v1/pagos?referenciaIntegrador=orden-12345 → el cobro de TU ordenGET /api/v1/pagos?desde=2026-09-01&hasta=2026-09-30 → la ventana que quieras conciliarpendiente_facturacion es la consulta de alarma: plata cobrada cuyo comprobante se pidió y todavía no
salió, sin acotar a ninguna ventana — acotarla escondería justamente el caso grave. Los montos son el bruto
cobrado, nunca neteados por la comisión del adquirente.
Checklist
Sección titulada «Checklist»-
GET /v1/echoal arrancar: conocés si la cuenta puede cobrar y si emite comprobante fiscal. - Toda orden lleva
referenciaIntegradorúnica. - Todo
POSTque mueve plata mandaIdempotency-Keyderivada de la orden, no aleatoria. - Los reintentos usan la misma clave.
- Los clientes se crean con
POST /v1/clientesy tureferenciaIntegrador: nunca un id copiado del panel. - Si la empresa factura, cada cobro con tarjeta que sea una venta lleva
facturao se factura después. -
en_verificacionno entrega producto ni se reintenta con clave nueva. - Hay un receptor de webhooks con verificación de firma y deduplicación.
-
pago.pendiente_facturacionllega a una persona: es plata cobrada sin comprobante. - Se ramifica por
error.code, nunca porerror.message.
La referencia completa de cada endpoint, con todos los campos y ejemplos, está en la Referencia OpenAPI.