Devoluciones y vuelto
Estas operaciones están en producción, pero todavía no aparecen en la referencia interactiva; se agregarán en una próxima versión del contrato. Esta guía describe su comportamiento actual.
Una venta cobrada se devuelve por el mismo riel y al mismo pagador que la pagó: nunca a un tercero. El vuelto, en cambio, es parte del cobro: se entrega en el momento al cliente que está en caja.
| Operación | Ruta | Alcance de la clave |
|---|---|---|
| Solicitar una devolución | POST /api/v1/pagos/intenciones/{ref}/devoluciones | INTEGRACION o COMPLETO |
| Listar las devoluciones de una venta | GET /api/v1/pagos/intenciones/{ref}/devoluciones | INTEGRACION o COMPLETO |
| Consultar una devolución | GET /api/v1/pagos/devoluciones/{refundCode} | INTEGRACION o COMPLETO |
| Autorizar una devolución | POST /api/v1/pagos/devoluciones/{refundCode}/autorizar | Ver cuatro ojos |
| Cancelar una devolución | POST /api/v1/pagos/devoluciones/{refundCode}/cancelar | INTEGRACION o COMPLETO |
| Entregar el vuelto | POST /api/v1/pagos/intenciones/{ref}/vuelto | INTEGRACION, COMPLETO o EQUIPOS con permiso de vuelto |
| Listar los vueltos de una venta | GET /api/v1/pagos/intenciones/{ref}/vuelto | Igual que el anterior |
| Tasa de cambio vigente | GET /api/v1/pagos/tasa-cambio/vigente?moneda=USD | INTEGRACION, COMPLETO o EQUIPOS |
Devoluciones
EN_DUDA nunca se reintenta a ciegas: la conciliación consulta al banco y decide.
1. Solicitar
curl -s -X POST https://pos.finoracore.com/api/v1/pagos/intenciones/FNRLZ3K9Q1/devoluciones \
-H "X-API-Key: $FINORA_API_KEY" \
-H "Idempotency-Key: dev-ped-8841-01" \
-H "Content-Type: application/json" \
-d '{
"monto": 1500.00,
"moneda": "VES",
"motivo": "PRODUCTO_DEFECTUOSO",
"motivoTexto": "caja golpeada",
"intentoSecuencia": 1,
"solicitadoPor": "caja:ana",
"lineas": [ { "codigo": "SKU-1", "descripcion": "Licuadora", "cantidad": 1, "monto": 1500.00 } ]
}'
| Campo | Obligatorio | Notas |
|---|---|---|
monto | sí | Mayor que cero y no mayor que lo pagado menos lo ya devuelto. |
motivo | sí | CLIENTE_ARREPENTIDO, PRODUCTO_DEFECTUOSO, SERVICIO_NO_PRESTADO, ERROR_CAJERO, COBRO_DUPLICADO, SOSPECHA_FRAUDE u OTRO. |
solicitadoPor | sí | Quién opera en tu sistema (por ejemplo caja:ana). Se usa para el control de cuatro ojos. |
intentoSecuencia | no | Qué intento de cobro aprobado se devuelve, si la venta tuvo varios. |
rielForzado | no | Solo EFECTIVO o NOTA_CREDITO, cuando el pago no tiene salida bancaria al mismo pagador. |
lineas | no | Detalle de lo devuelto. |
Idempotency-Key (recomendada, hasta 64 caracteres): repetirla devuelve la devolución ya creada con 200. Respuesta 202:
{
"refundCode": "DEV-20261020-7F3A2C",
"estado": "SOLICITADA",
"tipo": "DEVOLUCION",
"riel": "P2P",
"monto": 1500.00,
"moneda": "VES",
"motivo": "PRODUCTO_DEFECTUOSO",
"motivoTexto": "caja golpeada",
"intencion": "FNRLZ3K9Q1",
"intentoSecuencia": 1,
"rielOrigen": "PAGO_MOVIL",
"beneficiario": { "tipoDocumento": "V", "documento": "V-****5678", "telefono": "0414***0000", "banco": "0999" },
"solicitadoPor": "caja:ana",
"autorizadoPor": null,
"creadoEn": "2026-10-20T15:02:11Z",
"autorizadoEn": null,
"aplicadoEn": null,
"salida": null,
"notaCredito": null,
"motivoRechazo": null,
"terminalId": "TERM0042",
"merchantCode": "COM00042"
}
El beneficiario se copia del pago original y viene enmascarado; el cuerpo de la petición no puede indicar otro.
Por qué riel sale
El Merchant elige el riel de salida (riel) a partir del pago original:
| Pago original | Riel de salida |
|---|---|
| Tarjeta en un terminal | Anulación si el lote del terminal sigue abierto; si no, devolución por tarjeta. |
| Pago móvil aprobado hoy con código de reverso | Reverso del pago móvil. |
| Pago móvil, transferencia o crédito/débito inmediato de persona natural, hasta el tope | Pago móvil al mismo teléfono y documento (P2P). |
| Ídem con pagador persona jurídica o por encima del tope | Crédito inmediato a la cuenta del pagador. |
| Efectivo | EFECTIVO: registro y nota de crédito; el dinero sale por caja. |
| Sin pagador identificado | 422 RIEL_SIN_SALIDA: pide rielForzado EFECTIVO o NOTA_CREDITO. |
Cuatro ojos
Una devolución la autoriza otra persona distinta de quien la solicitó. Al autorizar se ejecuta en la misma llamada y la respuesta trae el estado final (APLICADA, EN_DUDA o FALLIDA) con el bloque salida.
- Desde el portal del comercio (recomendado): un administrador autoriza hasta el tope de supervisor del comercio; por encima, el propietario.
- Por clave API:
POST /api/v1/pagos/devoluciones/{refundCode}/autorizarcon{ "autorizadoPor": "supervisor:luis" }. Está desactivada por defecto, porque una clave no distingue personas; si no está habilitada para tu comercio, responde403 CONTROL_DUAL. Aun habilitada, por clave solo se autoriza hasta el tope de supervisor (403 ROL_INSUFICIENTEpor encima).
El tope se compara contra el total acumulado devuelto de la venta, no contra cada solicitud: partir una devolución grande en varias pequeñas no evita la aprobación de mayor nivel.
{
"id": 9, "concepto": "DEVOLUCION", "riel": "P2P", "estado": "APLICADA",
"monto": 1500.00, "moneda": "VES", "montoOriginal": null, "monedaOriginal": null, "tasaCambio": null,
"idExterno": "DEV-20261020-7F3A2C", "referenciaBanco": "000123456789", "codigoAnulacion": null,
"codigoRespuesta": "201", "mensaje": null, "gateway": "SIMULADO",
"beneficiario": { "tipoDocumento": "V", "documento": "V-****5678", "telefono": "0414***0000", "banco": "0999" },
"ejecutadoEn": "2026-10-20T15:05:40Z", "conciliadoEn": null
}
Tras APLICADA, la devolución trae además notaCredito con su número, fecha y monto.
Cancelar
POST /api/v1/pagos/devoluciones/{refundCode}/cancelar con { "motivo": "…" }. Solo en SOLICITADA o AUTORIZADA; la devolución queda RECHAZADA.
Errores
| HTTP | codigo | Cuándo |
|---|---|---|
| 400 | PETICION_INVALIDA | Monto ≤ 0, motivo fuera del catálogo o solicitadoPor vacío. |
| 403 | CONTROL_DUAL | Autoriza la misma persona que solicitó, o la autorización por clave está desactivada. |
| 403 | ROL_INSUFICIENTE | El acumulado supera el tope de quien autoriza. |
| 404 | INTENCION_NO_ENCONTRADA, DEVOLUCION_NO_ENCONTRADA | Referencia inexistente o fuera del alcance de la clave. |
| 409 | INTENCION_NO_PAGADA | La venta no está PAGADA. |
| 409 | DEVOLUCION_DUPLICADA | Misma Idempotency-Key con otro cuerpo. |
| 409 | ESTADO_INVALIDO | La devolución no admite la operación en su estado. |
| 422 | MONTO_SUPERA_DISPONIBLE | El monto supera lo pagado menos lo devuelto. |
| 422 | RIEL_SIN_SALIDA | No hay salida al mismo pagador y no se forzó EFECTIVO o NOTA_CREDITO. |
Vuelto
El vuelto sale por pago móvil al cliente que está en caja. No lleva cuatro ojos, pero sí un tope por operación, y la venta debe estar PAGADA.
curl -s -X POST https://pos.finoracore.com/api/v1/pagos/intenciones/FNRLZ3K9Q1/vuelto \
-H "X-API-Key: $FINORA_API_KEY" -H "Content-Type: application/json" \
-d '{
"monto": 2.00, "moneda": "USD",
"telefono": "04140000000", "tipoDocumento": "V", "documento": "12345678", "banco": "0999",
"solicitadoPor": "caja:ana"
}'
monedaes opcional (por defecto, la de la venta). Si no esVES, se convierte con la tasa oficial vigente hoy, redondeada a 2 decimales, y la tasa queda congelada en la salida.- Sin tasa vigente para hoy:
422 TASA_CAMBIO_NO_DISPONIBLEy no se crea nada. Nunca se usa la tasa de ayer en silencio: ofrece el vuelto en efectivo. - El beneficiario debe ser persona natural (
V,EoP). - Un solo vuelto activo o aplicado por venta (
409 VUELTO_YA_EN_CURSO); tras unoFALLIDAse puede reintentar. - Por encima del tope del comercio, en bolívares:
422 VUELTO_SUPERA_TOPE.
Respuesta 201 (la salida, con la conversión):
{
"id": 12, "concepto": "VUELTO", "riel": "P2P", "estado": "APLICADA",
"monto": 73.00, "moneda": "VES",
"montoOriginal": 2.00, "monedaOriginal": "USD",
"tasaCambio": { "tasa": 36.50000000, "monedaOrigen": "USD", "monedaDestino": "VES",
"fechaValor": "2026-10-20", "fuente": "BCV", "id": 118 },
"idExterno": "VLT-FNRLZ3K9Q1-1", "referenciaBanco": "000123456790", "codigoAnulacion": null,
"codigoRespuesta": "201", "mensaje": null, "gateway": "SIMULADO",
"beneficiario": { "tipoDocumento": "V", "documento": "V-****5678", "telefono": "0414***0000", "banco": "0999" },
"ejecutadoEn": "2026-10-20T15:10:02Z", "conciliadoEn": null
}
El comprobante del cliente debe mostrar el monto en divisa, la tasa con su fecha valor y el monto en bolívares enviado.
Consultar la tasa antes de pedir el vuelto
curl -s "https://pos.finoracore.com/api/v1/pagos/tasa-cambio/vigente?moneda=USD" -H "X-API-Key: $FINORA_API_KEY"
{ "tasa": 36.50000000, "monedaOrigen": "USD", "monedaDestino": "VES",
"fechaValor": "2026-10-20", "fuente": "BCV", "id": 118, "vigenteHasta": "2026-10-20" }
Es la misma tasa que congelaría un vuelto en ese instante. Entre la consulta y el vuelto la tasa puede cambiar (por ejemplo, al pasar la medianoche): lo que vale para el comprobante es siempre el tasaCambio que devuelve el vuelto. moneda VES o mal formada responde 400 MONEDA_NO_SOPORTADA.
Avisos por webhook
Si tu venta vino de una sesión de checkout con webhookUrl, o tu comercio tiene una URL de webhook configurada, recibes devolucion.creada, devolucion.aprobada, devolucion.ejecutada, devolucion.en_duda, devolucion.fallida, devolucion.conciliada y vuelto.ejecutado, firmados como los demás webhooks del Merchant. El cuerpo es la devolución (o la salida del vuelto) más evento y ocurridoEn.