Saltar al contenido principal

Cobro empujado

Tu software crea una intención de cobro en el Merchant y la empuja al terminal que elijas. El terminal (AuroraPOS) la recoge por long-poll, la acepta, cobra con tarjeta y reporta el veredicto. La venta se concilia en la sucursal donde el terminal estaba asignado ese día.

tu ERP ──POST /api/v1/pagos/intenciones { despacho: { terminalId: "TERM0042" } }──▶ Merchant ── cola por terminal
▲ │
terminal AuroraPOS (deviceToken) ── GET /api/v1/pos/ordenes/pendiente ──────────────────┘ ▼
── POST …/{ref}/aceptar · cobra con tarjeta · POST …/{ref}/resultado

Dos credenciales: tu X-API-Key (lado administrativo, /api/v1/pagos) y el deviceToken del terminal (lo tiene la app; tú no lo tocas).

Estados del despacho​

PENDIENTE ──(sondeo)──▶ ENTREGADA ──(aceptar)──▶ ACEPTADA ──(resultado)──▶ COMPLETADA | EN_DUDA | CANCELADA
└── cancelar ────────┴──▶ CANCELADA └── ttl vencido ──▶ EXPIRADA (ACEPTADA no expira)
  • dispatchStatus es independiente del estado de la intención (CREADA, PAGADA…): uno dice si la orden llegó al equipo; el otro, si la venta se cobró.
  • Un cobro a la vez por terminal. Con una orden viva, el segundo despacho responde 409 TERMINAL_OCUPADO.
  • Vencer no es rechazar. EXPIRADA deja el terminal libre; si el veredicto llega tarde se registra igual.

Crear y empujar en un viaje​

cURL
curl -s -X POST https://pos.finoracore.com/api/v1/pagos/intenciones \
-H "X-API-Key: $FINORA_API_KEY" -H "Content-Type: application/json" \
-d '{
"idempotencyKey": "ped-8841-2026-09-13",
"merchantCode": "COM00042",
"amount": 2499.00, "currency": "VES",
"description": "Pedido delivery", "externalOrderId": "PED-8841",
"despacho": { "terminalId": "TERM0042", "ttlSegundos": 300,
"pagador": { "tipoDocumento": "V", "documento": "12345678" } }
}'
Node.js
const r = await fetch("https://pos.finoracore.com/api/v1/pagos/intenciones", {
method: "POST",
headers: { "Content-Type": "application/json", "X-API-Key": process.env.FINORA_API_KEY },
body: JSON.stringify({
idempotencyKey: `ped-8841-${hoy}`, merchantCode: "COM00042",
amount: 2499.0, currency: "VES", description: "Pedido delivery", externalOrderId: "PED-8841",
despacho: { terminalId: "TERM0042", ttlSegundos: 300, pagador: { tipoDocumento: "V", documento: "12345678" } },
}),
});
const intencion = await r.json(); // 200 siempre (crear y reintentar se ven igual)
if (!r.ok) console.error(intencion.codigo, intencion.referencia); // TERMINAL_OCUPADO deja la intención creada
Java
var cuerpo = """
{"idempotencyKey":"ped-8841-2026-09-13","merchantCode":"COM00042","amount":2499.00,"currency":"VES",
"description":"Pedido delivery","externalOrderId":"PED-8841",
"despacho":{"terminalId":"TERM0042","ttlSegundos":300,"pagador":{"tipoDocumento":"V","documento":"12345678"}}}
""";
var req = HttpRequest.newBuilder(URI.create("https://pos.finoracore.com/api/v1/pagos/intenciones"))
.header("Content-Type", "application/json").header("X-API-Key", System.getenv("FINORA_API_KEY"))
.POST(HttpRequest.BodyPublishers.ofString(cuerpo)).build();
var res = HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
C#
var res = await http.PostAsJsonAsync("https://pos.finoracore.com/api/v1/pagos/intenciones", new {
idempotencyKey = "ped-8841-2026-09-13", merchantCode = "COM00042",
amount = 2499.00m, currency = "VES", description = "Pedido delivery", externalOrderId = "PED-8841",
despacho = new { terminalId = "TERM0042", ttlSegundos = 300, pagador = new { tipoDocumento = "V", documento = "12345678" } },
});
Python
r = requests.post("https://pos.finoracore.com/api/v1/pagos/intenciones", headers={"X-API-Key": KEY}, json={
"idempotencyKey": "ped-8841-2026-09-13", "merchantCode": "COM00042",
"amount": 2499.00, "currency": "VES", "description": "Pedido delivery", "externalOrderId": "PED-8841",
"despacho": {"terminalId": "TERM0042", "ttlSegundos": 300, "pagador": {"tipoDocumento": "V", "documento": "12345678"}},
})
intencion = r.json()
PHP
$res = json_decode(finora_post("/api/v1/pagos/intenciones", [
"idempotencyKey" => "ped-8841-2026-09-13", "merchantCode" => "COM00042",
"amount" => 2499.00, "currency" => "VES", "description" => "Pedido delivery", "externalOrderId" => "PED-8841",
"despacho" => ["terminalId" => "TERM0042", "ttlSegundos" => 300, "pagador" => ["tipoDocumento" => "V", "documento" => "12345678"]],
]), true);
CampoObligatorioNotas
idempotencyKeysíRepetirla devuelve la misma intención: reintenta sin miedo tras un corte de red.
merchantCodesíTu comercio (COM00042) o una agencia dentro de tu alcance.
amount, currencysíUnidades con decimales (2499.00).
despacho.terminalIdsí (si hay bloque)TID destino; no tiene que ser móvil.
despacho.ttlSegundosno300 por defecto, máximo 900.
despacho.pagadornoSe copia a la intención solo si no traía documento.

Respuesta 200: la IntencionResponse con reference, state, amount, pendingAmount, rails[], attempts[] y el bloque despacho:

{ "reference": "FNRLZ3K9Q1", "state": "CREADA", "amount": 2499.00, "capturedAmount": 0, "pendingAmount": 2499.00, "currency": "VES",
"despacho": { "estado": "PENDIENTE", "terminalId": "TERM0042", "outletStoreCode": "SUC001",
"despachadoEn": "…", "entregadoEn": null, "aceptadoEn": null, "expiraEn": "…", "codigoResultado": null, "mensajeResultado": null } }

Si el despacho falla, la intención queda creada y el error trae su referencia para reintentar:

{ "type": "urn:finora:pos:despacho:terminal-ocupado", "title": "El terminal ya tiene una orden en curso", "status": 409,
"codigo": "TERMINAL_OCUPADO", "referencia": "FNRLZ3K9Q1" }

Seguir, reintentar y cancelar​

MétodoRutaQué hace
GET/api/v1/pagos/intenciones/{ref}La intención con su bloque despacho (o null si no fue empujada).
GET/api/v1/pagos/intenciones/{ref}/despachoSolo el bloque.
POST/api/v1/pagos/intenciones/{ref}/despacho {terminalId, ttlSegundos?}Empuja una intención ya creada (reintento tras TERMINAL_OCUPADO, o a otro motorizado tras EXPIRADA). Idempotente.
POST/api/v1/pagos/intenciones/{ref}/despacho/cancelarSolo con PENDIENTE o ENTREGADA; tras ACEPTADA el motorizado puede estar cobrando (409 TRANSICION_INVALIDA).
curl -s https://pos.finoracore.com/api/v1/pagos/intenciones/FNRLZ3K9Q1/despacho -H "X-API-Key: $FINORA_API_KEY"
curl -s -X POST https://pos.finoracore.com/api/v1/pagos/intenciones/FNRLZ3K9Q1/despacho/cancelar -H "X-API-Key: $FINORA_API_KEY"
curl -s -X POST https://pos.finoracore.com/api/v1/pagos/intenciones/FNRLZ3K9Q1/despacho -H "X-API-Key: $FINORA_API_KEY" \
-H "Content-Type: application/json" -d '{"terminalId":"MOTO0009"}'

Cuando el terminal reporta APROBADA, la intención pasa a PAGADA y el despacho a COMPLETADA con codigoResultado: "00". Sondea GET …/despacho cada pocos segundos o, si prefieres un aviso, crea la venta como sesión de checkout con terminalId y webhookUrl: es el mismo despacho con webhook firmado.

Asignación diaria del terminal (motorizados)​

outlet(venta) = asignación(terminal, fecha local) ?? terminal.outlet. Un terminal fijo no necesita asignaciones; solo se registra lo que se mueve.

# Hoy el motorizado sale de la sucursal SUC001
curl -s -X POST https://pos.finoracore.com/api/v1/pagos/asignaciones -H "X-API-Key: $FINORA_API_KEY" -H "Content-Type: application/json" \
-d '{"terminalId":"TERM0042","storeCode":"SUC001","fecha":"2026-09-13","asignadoPor":"supervisor.perez"}'

curl -s "https://pos.finoracore.com/api/v1/pagos/asignaciones?terminalId=TERM0042" -H "X-API-Key: $FINORA_API_KEY" # historial

Upsert por (terminal, fecha); terminal y tienda deben ser del mismo comercio.

Alcance de la clave (regla D9)​

Tu clave identifica a tu comercio y de ahí sale sobre qué comercios puede actuar: un principal, sobre sí mismo y sus agencias; una agencia, solo sobre sí misma. Empujar a un terminal fuera del alcance → 403 DESPACHO_NO_PERMITIDO. Una clave filtrada compromete un comercio, no la plataforma: revócala en Claves API y crea otra.

Errores​

HTTPcodigoQué hacer
401CLAVE_INVALIDARevisar la clave.
403CLAVE_SIN_ACCESO_AL_COMERCIO, DESPACHO_NO_PERMITIDONo reintentar; el comercio o el terminal están fuera de tu alcance.
404TERMINAL_NO_ENCONTRADO, INTENCION_NO_ENCONTRADACorregir TID o referencia.
409TERMINAL_OCUPADOReintentar más tarde o cancelar la orden anterior.
409ESTADO_INVALIDOIntención pagada, anulada, vencida, ya despachada a otro terminal o con cobro en duda.
409TRANSICION_INVALIDACancelar tras aceptar, aceptar sin entregar. Consultar el estado.

Para gobernar los terminales (estado, parámetros, suspender, mensajes) mira TMS.