FINORA|Documentación
MerchantAprende FINORA
Merchant · Integración

FINORA Pago para Windows: integra tu sistema de caja

Tu ERP, tu caja en VB6 o tu web en Chrome no integran bancos ni terminales. Le piden a FINORA Pago «cobra 227,00 por el pedido 8841» y reciben «APROBADA, referencia PM000100001». Con ventana para el cliente o en silencio; por HTTP local, por una URL finora-pago:// o ejecutando un .exe.

Estado En redacciónProyecto merchant/finora-pago-windowsInstalador FINORA-Pago-1.0.0-win-x64.exeAPI local 127.0.0.1:9810Actualizado 2026-09-13

1API local o API de nube

Tu situaciónUsa
El sistema de caja corre en el mismo equipo de Windows del mostrador y quieres que el cliente vea una ventana de pago, o que la caja mande el medio ya elegido (tarjeta al terminal, pago móvil con los datos tecleados en tu pantalla).FINORA Pago (esta guía): API local http://127.0.0.1:9810, URI finora-pago:// o CLI finora-pago.exe.
El sistema es un servidor (ERP central, tienda en línea, backend propio) que cobra desde la nube y quiere webhooks.API de Checkout con tu clave de API y, si quieres, el widget @finora/checkout en tu web (ver Merchant → API).
Tienes un terminal de pago en la LAN y quieres hablarle directamente (semi-integrado).Cobro empujado o el agente de hardware finora-merchant-agent (puerto 9800).

FINORA Pago no guarda tarjetas, no habla ISO 8583 y no decide rieles: crea una sesión de Checkout con la clave del comercio, abre (o no) una ventana con el widget oficial y te devuelve el veredicto. Lo que se corrija en el Checkout queda corregido aquí.

Tu caja pide el cobro a FINORA Pago en la misma máquina; FINORA Pago crea la sesión en el Merchant y devuelve el veredicto TU MÁQUINA DE MOSTRADORNUBE FINORA Tu sistema de cajaSAP · Odoo · ERP propio · VB6 · webPOST 127.0.0.1:9810/v1/cobrosfinora-pago://cobrar?…finora-pago.exe cobrar … FINORA Pagobandeja · API local · ventana de pagocrea la sesión con X-API-Keymonta @finora/checkout (o no)sondea el estado cada 2 s Merchant FINORApos.finoracore.com · sesiones de Checkoutbanco simulado (sandbox)bancos · terminal de tarjeta petición veredicto · callback HTTPS estado / recibo

2Instalación

  1. Ejecuta el instalador

    FINORA-Pago-1.0.0-win-x64.exe (NSIS, por máquina, pide permisos de administrador). Instala en C:\Program Files\FINORA Pago\, crea el acceso del menú Inicio y registra el esquema finora-pago://.

    certutil -hashfile FINORA-Pago-1.0.0-win-x64.exe SHA256

    Compara el resultado con el .sha256 publicado junto al instalador antes de instalar.

  2. Configura RIF y clave

    Al terminar arranca sola y abre Configuración (PIN de fábrica 0000). Escribe el RIF, pega la clave de API y pulsa «Probar conexión».

  3. Déjala en la bandeja

    Vive junto al reloj. Clic: Estado. Menú: Configuración, Salir (PIN). Datos en %APPDATA%\finora-pago\ (configuracion.json, historial.json, bitacora.log).

SmartScreenEl instalador no lleva firma de código: Windows avisa la primera vez («Más información → Ejecutar de todas formas»).

3Configuración

CampoQué esPor defecto
URL del MerchantBase del Merchant FINORAhttps://pos.finoracore.com
RIF del comercioEl RIF con el que el Merchant te dio de altavacío
Clave de APILa misma de /api/v1/pagos y del Checkout. Se guarda cifrada con DPAPI, ligada al usuario de Windows. «Probar conexión» consulta GET /api/v1/pos/comercios/{rif}vacío
Terminal para tarjetaTID del terminal físico al que se empuja la tarjeta (MOTO0001, un TID de AuroraPOS). Vacío: la caja lo manda en cada cobrovacío
PuertoDe la API local. El agente Java de hardware usa 9800; no los mezcles9810
Token de la API localSi lo generas, toda petición debe traerlo en X-Pago-Token; con él se firma el callbackvacío (sin token)
Mostrar la ventana por defectoCuando la petición no dice mostrar
Modo de la ventana de pagoInmersivo: pantalla del cliente, monitor entero sin marco, guía animada e iconos grandes. Formal: ventana compacta con marco en la pantalla del cajero. La petición puede forzarlo con modoInmersivo
MonitorDónde va la ventana inmersiva; con dos pantallas, la que mira al clienteprincipal
Ventana «Estado del cobro» para el cajeroCon dos monitores y modo inmersivo: ventana pequeña en la pantalla del cajero con el estado en vivo y los botones Cancelar cobro / OK (OK cierra las dos)no
SonidosFeedback sonoro del widget en modo inmersivono
Color del comercio#rrggbb para el botón principal y los acentos del widget (token --fc-color-primary del SDK)vacío (navy FINORA)
Cierre automático del veredictoSegundos que APROBADA / RECHAZADA queda en pantalla; 0 espera a que pulsen OK (o Enter / Escape)8
Arrancar con WindowsSe abre en la bandeja al iniciar sesiónno
Sandbox por defectoLos cobros que no digan sandbox van al banco simuladono
PIN de administrador4 a 8 dígitos; protege Configuración y Salir0000 (cámbialo)

Todo se puede fijar también por variables de entorno, que mandan sobre la pantalla (imágenes de instalación, MDM): PAGO_MERCHANT_URL, PAGO_RIF, PAGO_API_KEY, PAGO_TERMINAL_ID, PAGO_PUERTO, PAGO_TOKEN_LOCAL, PAGO_MOSTRAR, PAGO_MODO_VENTANA, PAGO_MONITOR, PAGO_VENTANA_CAJERO, PAGO_SONIDOS, PAGO_COLOR_COMERCIO, PAGO_CIERRE_SEGUNDOS, PAGO_ARRANCAR_CON_WINDOWS, PAGO_SANDBOX, PAGO_CORS_ORIGENES.

Los dos modos de la ventana

Inmersivo: pantalla del cliente

Monitor entero sin marco y siempre encima, guía animada, pasos e iconos grandes, total en grande, teclado táctil, veredicto a toda pantalla con OK y cierre automático. Con dos monitores, la ventana «Estado del cobro» queda en la pantalla del cajero con Cancelar / OK.

Formal: pantalla del cajero

Ventana compacta con marco, centrada en la pantalla principal; el mismo widget en su versión densa y el veredicto compacto. Para un mostrador con un solo monitor, o para cobros que atiende el cajero (por teléfono, con los datos ya dictados).

Regla prácticaUn solo monitor mirando al cajero → Formal. Una segunda pantalla mirando al cliente → Inmersivo en ese monitor y «Estado del cobro» en el del cajero. Un cobro concreto puede pedir el otro modo (modo en la API, la URI o la CLI) sin tocar la configuración.

4API local http://127.0.0.1:9810

  • Solo loopback: nadie de la red la ve. Una caja en otra máquina usa la API de nube.
  • Cabecera X-Pago-Token: <token> (o Authorization: Bearer) si configuraste el token. Sin él: 401 TOKEN_LOCAL_INVALIDO.
  • JSON UTF-8. Los errores son siempre {"codigo": "...", "mensaje": "..."}.
  • Sin CORS por defecto. Una caja en Chrome en la misma máquina lo habilita con PAGO_CORS_ORIGENES=http://localhost:5173 (nunca *: cualquier página web podría pedir cobros).

GET /v1/salud

curl -s http://127.0.0.1:9810/v1/salud
{"version":"1.0.0","comercio":{"rif":"J123456789","nombre":"Comercio Demo FINORA C.A.","merchantCode":"900000000001"},
 "terminal":null,"merchantAlcanzable":true,"ventanaAbierta":false,"cobroEnCurso":null,"puerto":9810,"sandboxPorDefecto":true}

merchantAlcanzable consulta al Merchant en el momento (hasta 15 s sin red): úsalo al arrancar tu caja, no antes de cada cobro.

POST /v1/cobros

{
  "monto": 227.00,                       // obligatorio, > 0, hasta 2 decimales ("227,50" también vale)
  "moneda": "VES",                       // opcional, ISO 4217; por defecto la del comercio
  "referencia": "8841",                  // obligatorio, hasta 64: tu pedido o factura
  "descripcion": "Pedido 8841",          // opcional, se muestra en la ventana
  "items": [{"nombre":"Cafe","cantidad":2,"precio":3.5}],   // opcional, JSON libre, solo se muestra
  "pagador": {"tipoDocumento":"V","documento":"12345678","nombre":"Ana","telefono":"04141234567"},
  "riel": "PAGO_MOVIL",                  // opcional: TARJETA · PAGO_MOVIL · TRANSFERENCIA · QR · CRIPTO
  "campos": {"telefono":"04141234567","banco":"0102","clave":"1234"},   // valores de los campos del riel
  "terminalId": "MOTO0001",              // para TARJETA si no hay terminal configurado
  "mostrar": true,                       // abre la ventana (por defecto: lo configurado)
  "modo": "INMERSIVO",                   // opcional: FORMAL | INMERSIVO; por defecto lo configurado
  "esperar": false,                      // true: la respuesta es el veredicto final (bloquea hasta el TTL)
  "ttlSegundos": 900,                    // 60..3600
  "callbackUrl": "http://127.0.0.1:5000/pago",   // POST firmado al terminar
  "sandbox": true                        // banco simulado, sin dinero real
}
  • Con ventana (mostrar:true): sin riel, el cliente elige el medio en la ventana (widget oficial). Con riel + campos, se fija el medio y la ventana muestra la espera y el veredicto. modo decide la presentación de ese cobro (FORMAL compacto para el cajero, INMERSIVO a pantalla completa para el cliente).
  • Sin ventana (mostrar:false): riel es obligatorio; los campos son los del riel (§7). Nadie ve nada; la aplicación orquesta y te responde.
  • Una ventana a la vez: un segundo cobro con ventana mientras hay otro en curso recibe 409 COBRO_EN_CURSO con el cobroId del actual. Los silenciosos sí conviven; el Merchant protege el terminal (TERMINAL_OCUPADO).

Respuestas: 201 sin esperar ({"cobroId":"CB…","sesionId":"CS…","estado":"EN_CURSO","urlPago":"…"}; urlPago es la página alojada del Checkout, por si quieres enseñar un QR) o 200 con esperar:true: el cobro completo con veredicto. Salida literal de una prueba real contra pos.finoracore.com en sandbox:

{"cobroId":"CBf0Eu90y93C78","sesionId":"CS2XXRLh8KtYTm41g4EwZBjQ","estado":"APROBADA","monto":227,"moneda":null,
 "referencia":"PW-8841","descripcion":"Prueba FINORA Pago Windows","riel":"PAGO_MOVIL","codigo":"00","mensaje":"Aprobada",
 "autorizacion":{"estado":"APROBADO","codigo":"00","mensaje":null,"referenciaBanco":"PM000100001","autorizador":"BANCO_SIM","resueltoEn":"2026-09-13T15:32:59.685+00:00"},
 "recibo":{"referencia":"FNRMTZZ3THP","referenciaComercio":"PW-8841","monto":227,"moneda":"VES","comercio":"DEMO FINORA","riel":"PAGO_MOVIL",
           "referenciaBanco":"PM000100001","referenciaPagador":"04141234567","fecha":"2026-09-13T15:32:59.685+00:00"},
 "urlPago":null,"sandbox":true,"mostrar":false,"origen":"api","creadoEn":"2026-09-13T15:32:55.127Z","actualizadoEn":"2026-09-13T15:32:56.764Z","cerradoEn":"2026-09-13T15:32:56.764Z"}

Estados: EN_CURSO · APROBADA · RECHAZADA · EN_DUDA · CANCELADA · EXPIRADA. Los cinco últimos son finales.

EN_DUDA no es un rechazoEl banco no confirmó. No vuelvas a cobrar ni entregues; el Merchant la resuelve en minutos. Consulta GET /v1/cobros/{id} o espera el segundo webhook de nube.

codigo es el DE 39 del banco («00», «51», «05»…) o un motivo técnico: MERCHANT_INALCANZABLE, DATOS_INCOMPLETOS, MEDIO_NO_DISPONIBLE, SESION_NO_ENCONTRADA, SIN_VEREDICTO, CANCELADA_POR_COMERCIO, CANCELADA_POR_CLIENTE, TERMINAL_SIN_RESPUESTA.

Consulta, cancelación, historial, ventana

GET /v1/cobros/{cobroId}El cobro como esté. ?esperar=1&plazo=60 bloquea hasta el veredicto o hasta plazo segundos: así te recuperas si perdiste la respuesta del POST.
POST /v1/cobros/{cobroId}/cancelarCancela en el Merchant y responde el cobro CANCELADA (CANCELADA_POR_COMERCIO). 409 COBRO_EN_CURSO si el banco está respondiendo o el terminal ya aceptó la orden.
GET /v1/cobros?desde=&hasta=&max=Historial local (últimos 5.000), más recientes primero. Fechas ISO 8601.
POST /v1/ventana/mostrar {"cobroId"} · POST /v1/ventana/ocultarTraen al frente la ventana del cobro en curso, o la esconden, sin cancelar nada.

Callback firmado a tu caja

Si mandaste callbackUrl, al terminar el cobro FINORA Pago hace:

POST /pago HTTP/1.1
Content-Type: application/json
User-Agent: FINORA-Pago-Callback/1.0
X-Pago-Evento: pago.cobro
X-Pago-Cobro: CBf0Eu90y93C78
X-Pago-Firma: sha256=f44ebc9873f965313dadb3804003d9cbccec91f515342aee3fa20bf4ad33b396

{"evento":"pago.cobro","cobroId":"CBf0Eu90y93C78","estado":"APROBADA", …el mismo JSON del cobro… }
  • Firma: sha256=hex(HMAC-SHA256(tokenLocal, cuerpo crudo)). Verifica sobre los bytes que recibiste, no sobre el JSON reparseado. Sin token configurado, la clave del HMAC es la cadena vacía.
  • Reintentos: 3 (0 s, 2 s, 8 s). Responde 2xx rápido. Si tu caja estaba caída, el veredicto sigue en GET /v1/cobros/{id}.
  • Vector de prueba: token secreto, cuerpo {"a":1}. Si tu HMAC no coincide, estás firmando otra cosa (espacios, reparseo).

Códigos de error

HTTPcodigoCuándo
400PETICION_INVALIDAmonto, referencia, riel, campos, modo, callbackUrl… (mensaje dice cuál)
400DATOS_INCOMPLETOS, MEDIO_NO_DISPONIBLE, TERMINAL_NO_ENCONTRADOel Merchant rechazó el medio fijado; se cancela la sesión y te llega su motivo
401TOKEN_LOCAL_INVALIDOfalta o no coincide X-Pago-Token
404COBRO_NO_ENCONTRADO, SIN_VENTANA, NO_ENCONTRADO
409COBRO_EN_CURSOya hay una ventana de cobro abierta (trae cobroId), o el cobro no se puede cancelar aún
503MERCHANT_INALCANZABLEno se llegó al Merchant o no respondió en 15 s; el cobro queda RECHAZADA con ese código
503SIN_CONFIGURARfalta RIF o clave de API

5URI finora-pago://

Para cajas que solo saben abrir una URL (ShellExecute, start, un enlace en una intranet). Mismos parámetros que POST /v1/cobros; los campos del riel como campo.<code>= y el pagador como pagador.<campo>=. callback = callbackUrl, ttl = ttlSegundos.

start "" "finora-pago://cobrar?monto=227.00&referencia=8841&callback=http%3A%2F%2F127.0.0.1%3A5000%2Fpago"
start "" "finora-pago://cobrar?monto=227.00&referencia=8841&mostrar=false&riel=PAGO_MOVIL&campo.telefono=04141234567&campo.banco=0102&campo.clave=1234&pagador.documento=12345678&pagador.tipoDocumento=V&sandbox=true"
start "" "finora-pago://cobrar?monto=64.90&referencia=8842&modo=formal&callback=http%3A%2F%2F127.0.0.1%3A5000%2Fpago"
No hay respuesta por la URIEl veredicto llega por el callback o se consulta por GET /v1/cobros. Si la aplicación no está abierta, Windows la abre y el cobro arranca en cuanto está lista.

6CLI finora-pago.exe

finora-pago.exe cobrar --monto 227.00 --referencia 8841 --json
finora-pago.exe cobrar --monto 64.90 --referencia 8842 --modo formal --json
finora-pago.exe cobrar --monto 227.00 --referencia 8841 --mostrar false --riel PAGO_MOVIL --campo-telefono 04141234567 --campo-banco 0102 --campo-clave 1234 --pagador-documento 12345678 --pagador-tipoDocumento V --sandbox true --json
finora-pago.exe cobrar --monto 15.00 --referencia 8843 --mostrar false --riel TARJETA --terminalId MOTO0001 --json
finora-pago.exe consultar CBf0Eu90y93C78 --esperar --json
finora-pago.exe cancelar CBf0Eu90y93C78 --json
finora-pago.exe salud --json
  • Siempre espera el veredicto e imprime el JSON del cobro (o {"error":{codigo,mensaje}}).
  • Códigos de salida: 0 aprobada · 1 rechazada, cancelada o expirada · 2 en duda · 3 error.
  • Si FINORA Pago no está abierta, la CLI la lanza en segundo plano y espera hasta 20 s a que responda.
  • Lee puerto y token de %APPDATA%\finora-pago\configuracion.json; también PAGO_PUERTO y PAGO_TOKEN_LOCAL por entorno.
stdout en WindowsUn .exe de Electron es gráfico: su salida llega intacta cuando lo lanzas con tuberías (Process.Start + RedirectStandardOutput, execFile, subprocess.run), que es como lo usa una caja. Tecleado a mano en cmd.exe puede no verse: redirige a un archivo (finora-pago.exe salud --json > salida.json).

Salida literal de una prueba real (sandbox):

> finora-pago cobrar --monto 5.05 --referencia CLI-02 --mostrar false --riel PAGO_MOVIL --campo-telefono 04141234567 --campo-clave 1234 --campo-banco 0102 --pagador-documento 12345678
RECHAZADA CBCgGq0WBJw1Tf CLI-02 5.05 [05] ref FNRMTZZ6XMX-1
exit=1

7Rieles y campos

Los campos los define el Merchant por riel (back-office); estos son los del comercio de pruebas hoy. La cédula puede ir en pagador.documento + pagador.tipoDocumento o en campos.cedula.

RielCamposNotas
TARJETAcedulaNecesita terminalId (o el configurado). La orden se empuja al terminal; queda EN_CURSO hasta que el terminal cobra o vence. El PAN nunca pasa por tu caja ni por FINORA Pago.
PAGO_MOVILcedula, telefono (0(412|414|416|424|426) + 7), banco (4 dígitos), clave (C2P, 4 a 8 dígitos), referencia (P2C ya pagado, opcional)La clave viaja al banco y no se guarda.
TRANSFERENCIAcedula, banco, cuenta (20 dígitos)
QRcedula, telefono
CRIPTOcedula, wallet (20 a 64 alfanuméricos)

8Sandbox

"sandbox": true (o «Sandbox por defecto») manda el cobro al banco simulado de FINORA: sin dinero real, con reglas públicas por céntimos del monto.

CéntimosDesenlace
.51RECHAZADA 51 Fondos insuficientes
.05RECHAZADA 05 No honrar
.61RECHAZADA 61 Excede el límite
.96EN_DUDA; se resuelve APROBADA minutos después
.91EN_DUDA; se resuelve RECHAZADA
cualquier otroAPROBADA 00

Pago móvil: teléfono fuera del patrón → 14; termina en 0000 → 57. Tarjeta (en el terminal): PAN terminado en 5100 / 1732 / 0018 / 5156 → 51 / 43 / 54 / 41; en 9999 → sin respuesta (EN_DUDA). Tabla completa en Banco simulado.

9Ejemplos

Todos hacen lo mismo: POST /v1/cobros con esperar:true y deciden por estado. Los ejemplos completos (caja Express con callback, consola .NET 8) están en merchant/finora-pago-windows/examples/.

C# / .NET

using System.Net.Http.Json;

var http = new HttpClient { BaseAddress = new Uri("http://127.0.0.1:9810") };
http.DefaultRequestHeaders.Add("X-Pago-Token", Environment.GetEnvironmentVariable("PAGO_TOKEN") ?? "");

var respuesta = await http.PostAsJsonAsync("/v1/cobros", new {
    monto = 227.00m, referencia = "8841", descripcion = "Pedido 8841",
    mostrar = true, esperar = true, sandbox = true
});
var cobro = await respuesta.Content.ReadFromJsonAsync<Cobro>();
if (!respuesta.IsSuccessStatusCode) { Console.WriteLine($"{cobro?.codigo}: {cobro?.mensaje}"); return; }
switch (cobro!.estado) {
    case "APROBADA": MarcarPagado(cobro.referencia, cobro.autorizacion?.referenciaBanco); break;
    case "EN_DUDA":  BloquearEntrega(cobro.cobroId); break;   // no vuelvas a cobrar
    default:         Console.WriteLine($"No pagado: {cobro.estado} {cobro.codigo}"); break;
}

record Autorizacion(string? codigo, string? referenciaBanco);
record Cobro(string cobroId, string estado, string referencia, string? codigo, string? mensaje, Autorizacion? autorizacion);

Alternativa sin HTTP, lanzando el ejecutable y leyendo JSON de stdout:

var psi = new System.Diagnostics.ProcessStartInfo(@"C:\Program Files\FINORA Pago\finora-pago.exe",
    "cobrar --monto 227.00 --referencia 8841 --json") { RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true };
using var p = System.Diagnostics.Process.Start(psi)!;
string json = await p.StandardOutput.ReadToEndAsync();
await p.WaitForExitAsync();
// p.ExitCode: 0 aprobada · 1 rechazada/cancelada/expirada · 2 en duda · 3 error

Java

var http = java.net.http.HttpClient.newHttpClient();
var cuerpo = """
  {"monto":227.00,"referencia":"8841","mostrar":true,"esperar":true,"sandbox":true}""";
var req = java.net.http.HttpRequest.newBuilder(java.net.URI.create("http://127.0.0.1:9810/v1/cobros"))
    .header("Content-Type", "application/json").header("X-Pago-Token", System.getenv().getOrDefault("PAGO_TOKEN", ""))
    .timeout(java.time.Duration.ofSeconds(930))
    .POST(java.net.http.HttpRequest.BodyPublishers.ofString(cuerpo)).build();
var res = http.send(req, java.net.http.HttpResponse.BodyHandlers.ofString());
var json = new com.fasterxml.jackson.databind.ObjectMapper().readTree(res.body());
if (res.statusCode() == 200 && "APROBADA".equals(json.get("estado").asText())) {
    marcarPagado("8841", json.path("autorizacion").path("referenciaBanco").asText());
}

Node.js

const r = await fetch("http://127.0.0.1:9810/v1/cobros", {
  method: "POST",
  headers: { "Content-Type": "application/json", "X-Pago-Token": process.env.PAGO_TOKEN ?? "" },
  body: JSON.stringify({ monto: 227.0, referencia: "8841", mostrar: true, esperar: true, sandbox: true }),
});
const cobro = await r.json();
if (r.ok && cobro.estado === "APROBADA") marcarPagado(cobro.referencia, cobro.autorizacion?.referenciaBanco);

Verificar el callback (Express):

import { createHmac, timingSafeEqual } from "node:crypto";
app.post("/pago", express.raw({ type: "application/json" }), (req, res) => {
  const esperada = Buffer.from("sha256=" + createHmac("sha256", process.env.PAGO_TOKEN ?? "").update(req.body).digest("hex"));
  const recibida = Buffer.from(req.get("X-Pago-Firma") ?? "");
  if (esperada.length !== recibida.length || !timingSafeEqual(esperada, recibida)) return res.sendStatus(401);
  const cobro = JSON.parse(req.body.toString("utf8"));
  if (cobro.estado === "APROBADA") marcarPagado(cobro.referencia);
  res.sendStatus(200);
});

Python

import os, requests
r = requests.post("http://127.0.0.1:9810/v1/cobros",
    json={"monto": 227.00, "referencia": "8841", "mostrar": True, "esperar": True, "sandbox": True},
    headers={"X-Pago-Token": os.environ.get("PAGO_TOKEN", "")}, timeout=930)
cobro = r.json()
if r.ok and cobro["estado"] == "APROBADA":
    marcar_pagado(cobro["referencia"], cobro["autorizacion"]["referenciaBanco"])
elif cobro.get("estado") == "EN_DUDA":
    bloquear_entrega(cobro["cobroId"])

PowerShell

$cuerpo = @{ monto = 227.00; referencia = "8841"; mostrar = $true; esperar = $true; sandbox = $true } | ConvertTo-Json
$cobro = Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:9810/v1/cobros" -ContentType "application/json" -Body $cuerpo -Headers @{ "X-Pago-Token" = $env:PAGO_TOKEN } -TimeoutSec 930
if ($cobro.estado -eq "APROBADA") { "Pagado: $($cobro.autorizacion.referenciaBanco)" } else { "No pagado: $($cobro.estado) $($cobro.codigo)" }

# O con el ejecutable:
& "C:\Program Files\FINORA Pago\finora-pago.exe" cobrar --monto 227.00 --referencia 8841 --json | ConvertFrom-Json
$LASTEXITCODE   # 0 aprobada · 1 rechazada · 2 en duda · 3 error

VB6 / COM (WinHttp)

Dim http As Object
Set http = CreateObject("WinHttp.WinHttpRequest.5.1")
http.SetTimeouts 5000, 5000, 5000, 930000
http.Open "POST", "http://127.0.0.1:9810/v1/cobros", False
http.SetRequestHeader "Content-Type", "application/json"
http.SetRequestHeader "X-Pago-Token", TOKEN_LOCAL
http.Send "{""monto"":227.00,""referencia"":""8841"",""mostrar"":true,""esperar"":true}"
If http.Status = 200 Then
    If InStr(http.ResponseText, """estado"":""APROBADA""") > 0 Then
        MarcarPagado "8841"
    End If
End If

O sin HTTP, ejecutando finora-pago.exe cobrar … --json con la salida redirigida a un archivo y leyéndolo:

Shell "cmd /c ""C:\Program Files\FINORA Pago\finora-pago.exe"" cobrar --monto 227.00 --referencia 8841 --json > C:\caja\ultimo-cobro.json", vbHide
' espera a que el archivo exista y léelo; el JSON trae "estado" y "codigo"

10Diagnóstico

  • Bandeja → Estado: salud de la API local y del Merchant, últimos cobros con su código, y la bitácora con códigos cortos (M-3F2A) que se dictan por teléfono.
  • %APPDATA%\finora-pago\bitacora.log: una línea JSON por evento; rota a 5 MB.
  • GET /v1/salud responde aunque falte la configuración: comercio: null y merchantAlcanzable: false.
  • 401 TOKEN_LOCAL_INVALIDO: la caja no manda el token que está en Configuración. Genera uno nuevo, cópialo a la caja y guarda.
  • 409 COBRO_EN_CURSO: hay una ventana abierta con otro cobro. Ciérrala con OK, espera el cierre automático o cancela por API.
  • 503 MERCHANT_INALCANZABLE: sin internet o Merchant caído; revisa «Probar conexión».
  • «El puerto 9810 ya está en uso»: hay otra instancia (mira la bandeja) o cambia el puerto.
  • La ventana no se ve en pantalla completa: apaga «Pantalla completa» para probar en un escritorio; en el mostrador vuelve a encenderla.

11Preguntas frecuentes

¿Puede la caja mandar el número de tarjeta?

No. La tarjeta se cobra en el terminal físico (riel: TARJETA + terminalId); tu caja y FINORA Pago nunca ven un PAN. Es lo que te deja fuera del alcance PCI DSS.

¿Qué pasa si mi caja se cae con un cobro en curso?

El cobro sigue en el Merchant y en el historial local. Al volver, GET /v1/cobros/{id}?esperar=1 te da el veredicto; el callback, si lo pediste, se intentó tres veces.

¿Y si FINORA Pago se cierra con cobros en curso?

Al arrancar consulta al Merchant los cobros que quedaron EN_CURSO y los cierra con su veredicto real.

¿Puedo cobrar desde varias cajas con una sola instalación?

La API local es solo de esta máquina. Cada equipo de mostrador lleva su FINORA Pago; el RIF y la clave pueden ser los mismos.

¿Cómo pruebo sin dinero?

sandbox:true (o Sandbox por defecto) con la clave del comercio de pruebas. Los montos con céntimos .51, .05, .96 fuerzan rechazos y dudas.

¿Se actualiza sola?

El instalador publica latest.yml para un actualizador futuro; hoy se instala la versión nueva encima de la anterior (la configuración se conserva).

Otras guías

FINORA MerchantGuía operativa: encendido, guion de demostración, cierre y ciclo, cobro empujado, paneles y runbooks. FINORA CajaCaja de escritorio (Electron): flujo guiado de cobro, terminal semi‑integrado y modo desatendido. Banco simuladoEl banco visto desde afuera: pago móvil, transferencias, QR, cripto y host ISO 8583 con reglas públicas. Aprende FINORA con ejemplosSiete lecciones de cinco minutos: compra con tarjeta, rieles, lote, compensación, liquidación, conciliación y cobro empujado.