Cobrar desde una caja registradora
El modo de escucha del servidor ECR del terminal está en revisión de seguridad: las próximas versiones del terminal podrían aceptar órdenes solo desde el propio equipo y exigir que el administrador habilite la red local de forma explícita. Confirma con FINORA la versión instalada en tus terminales antes de integrar. Si la caja y el terminal no comparten red, usa el cobro empujado.
Tu caja registradora (ERP, punto de venta) envía el monto y la referencia de la venta al terminal FINORA. El terminal muestra el monto, el cliente presenta la tarjeta y escribe el PIN en el terminal, y la caja recibe el veredicto en la misma respuesta. La caja nunca ve el número de la tarjeta: queda fuera del alcance de la certificación de tarjetas.
Antes de empezar
| Necesitas | Dónde |
|---|---|
| La dirección del terminal en la red de la tienda | Configuración del terminal. El servidor ECR escucha en el puerto 8080. |
| El token del terminal | El administrador lo ve en la configuración global del terminal y lo copia en la caja. |
| Una clave de idempotencia por venta | La genera tu caja (por ejemplo, un UUID) y la guarda junto a la venta. |
Los ejemplos usan terminal.tienda.example como dirección del terminal.
1. Comprobar que el terminal está libre
GET /estado no requiere token: no mueve dinero y sirve para descubrir el terminal.
curl -s http://terminal.tienda.example:8080/estado
{
"terminal": "AuroraPOS",
"disponible": true,
"operacionEnCurso": null,
"tokenRequerido": true
}
Si disponible es false, hay un cobro en curso: muestra el aviso al cajero y no envíes otro.
2. Cobrar
CLAVE=$(uuidgen)
curl -s -X POST http://terminal.tienda.example:8080/pagos \
-H "Content-Type: application/json" \
-H "X-Terminal-Token: $FINORA_TERMINAL_TOKEN" \
--max-time 200 \
-d '{
"montoCentavos": 125000,
"referencia": "F-00123",
"cajaId": "CAJA-03",
"cajeroId": "cajero.demo",
"idempotencyKey": "'"$CLAVE"'",
"documentoPagador": "V12345678",
"tipoDocumentoPagador": "V"
}'
| Campo | Obligatorio | Notas |
|---|---|---|
montoCentavos | sí | Entero en la menor unidad de la moneda: Bs 1.250,00 es 125000. Nunca decimal. |
referencia | sí | Número de factura o de venta en tu caja. |
idempotencyKey | recomendado | Permite reintentar sin riesgo de doble cobro. |
cajaId, cajeroId | no | Para cuadrar por caja y por turno. |
documentoPagador, tipoDocumentoPagador | no | Documento del cliente si la caja ya lo capturó (V, E, J, P…). Vuelven como eco en la respuesta; el terminal no los guarda. |
El token también se acepta como Authorization: Bearer <token>. Sin token, o con uno que no coincide, el terminal responde 401 con estado: "NO_AUTORIZADO".
La respuesta es síncrona: llega cuando el cliente termina de pagar. Una venta con PIN puede tardar; el terminal espera hasta 180 segundos, así que configura en tu cliente un tiempo de espera mayor.
{
"estado": "APROBADA",
"aprobada": true,
"codigoRespuesta": "00",
"codigoAutorizacion": "A1B2C3",
"rrn": "622916125320",
"panEnmascarado": "990500******0015",
"marcaTarjeta": "VISA",
"montoCentavos": 125000,
"numeroLote": 52,
"stan": 125320,
"referencia": "F-00123",
"idempotencyKey": "550e8400-e29b-41d4-a716-446655440000",
"documentoPagador": "V12345678",
"tipoDocumentoPagador": "V"
}
3. Actuar según el estado
estado | HTTP | Qué significa | Qué hace la caja |
|---|---|---|---|
APROBADA | 200 | Cobro aprobado. | Imprimir el comprobante y cerrar la venta. |
RECHAZADA | 200 | El banco rechazó; codigoRespuesta dice por qué. | Ofrecer otro medio de pago. |
CANCELADA | 200 | El cliente o el operador cancelaron. | Volver al cobro. |
EN_DUDA | 200 | No se sabe si cobró. | No entregar ni cobrar de nuevo. Consultar la clave (paso 4). |
TERMINAL_OCUPADO | 409 | Ya hay un cobro en curso. | Esperar; no reintentar de inmediato. |
MONTO_INVALIDO | 400 | Monto menor o igual a cero. | Corregir. |
PETICION_INVALIDA | 400 | JSON mal formado. | Corregir. |
NO_AUTORIZADO | 401 | Falta el token o no coincide. | Revisar el token configurado en la caja. |
Si la conexión se corta o vence tu tiempo de espera, trata la venta como EN_DUDA. Dar por rechazada una venta que el banco aprobó deja al cliente sin mercancía y con el cargo hecho.
4. Salir de la duda: consultar la clave
GET /pagos/{idempotencyKey} no dispara ningún cobro: dice lo que el terminal recuerda de esa clave.
curl -s http://terminal.tienda.example:8080/pagos/$CLAVE \
-H "X-Terminal-Token: $FINORA_TERMINAL_TOKEN"
| Caso | HTTP | Cuerpo |
|---|---|---|
| Resuelta | 200 | La misma respuesta que devolvió (o debió devolver) el POST. |
| En curso | 200 | { "estado": "EN_CURSO", "aprobada": false, "idempotencyKey": "…" }: sigue consultando. |
| Desconocida | 404 | { "estado": "DESCONOCIDA", "aprobada": false } |
Reenviar el mismo POST /pagos con la misma clave también es seguro: si la clave está en curso, el terminal se engancha a la operación viva; si ya se resolvió, devuelve el mismo veredicto sin cobrar otra vez. Lo que nunca es seguro es reintentar con una clave nueva.
El terminal recuerda en memoria los últimos 100 desenlaces. Si la app del terminal se reinicia, una clave puede pasar a DESCONOCIDA: eso no significa que no se cobró. Concilia contra las ventas del comercio en el portal o el cierre de lote antes de volver a cobrar.
Un cobro a la vez
Un terminal atiende un solo cobro. Una segunda orden recibe 409 TERMINAL_OCUPADO en lugar de encolarse: encolar cobros en un punto de venta produce cargos que el cajero no espera.
Complemento para cajas Windows
FINORA prepara un complemento para cajas Windows (biblioteca .NET y ejecutable con código de retorno para Delphi, VB6 o FoxPro) compatible con el token del terminal. Mientras tanto, integra el contrato HTTP de esta guía o usa FINORA Pago, que cobra desde el propio equipo de la caja.
Lo que sigue
- Referencia de la API ECR.
- Sandbox del Merchant: finales del número de tarjeta que provocan cada rechazo en el terminal de pruebas.