1API local o API de nube
| Tu situación | Usa |
|---|---|
| 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í.
2Instalación
Ejecuta el instalador
FINORA-Pago-1.0.0-win-x64.exe(NSIS, por máquina, pide permisos de administrador). Instala enC:\Program Files\FINORA Pago\, crea el acceso del menú Inicio y registra el esquemafinora-pago://.certutil -hashfile FINORA-Pago-1.0.0-win-x64.exe SHA256Compara el resultado con el
.sha256publicado junto al instalador antes de instalar.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».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).
3Configuración
| Campo | Qué es | Por defecto |
|---|---|---|
| URL del Merchant | Base del Merchant FINORA | https://pos.finoracore.com |
| RIF del comercio | El RIF con el que el Merchant te dio de alta | vacío |
| Clave de API | La 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 tarjeta | TID del terminal físico al que se empuja la tarjeta (MOTO0001, un TID de AuroraPOS). Vacío: la caja lo manda en cada cobro | vacío |
| Puerto | De la API local. El agente Java de hardware usa 9800; no los mezcles | 9810 |
| Token de la API local | Si lo generas, toda petición debe traerlo en X-Pago-Token; con él se firma el callback | vacío (sin token) |
| Mostrar la ventana por defecto | Cuando la petición no dice mostrar | sí |
| Modo de la ventana de pago | Inmersivo: 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 modo | Inmersivo |
| Monitor | Dónde va la ventana inmersiva; con dos pantallas, la que mira al cliente | principal |
| Ventana «Estado del cobro» para el cajero | Con 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 |
| Sonidos | Feedback sonoro del widget en modo inmersivo | no |
| 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 veredicto | Segundos que APROBADA / RECHAZADA queda en pantalla; 0 espera a que pulsen OK (o Enter / Escape) | 8 |
| Arrancar con Windows | Se abre en la bandeja al iniciar sesión | no |
| Sandbox por defecto | Los cobros que no digan sandbox van al banco simulado | no |
| PIN de administrador | 4 a 8 dígitos; protege Configuración y Salir | 0000 (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).
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>(oAuthorization: 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): sinriel, el cliente elige el medio en la ventana (widget oficial). Conriel+campos, se fija el medio y la ventana muestra la espera y el veredicto.mododecide la presentación de ese cobro (FORMALcompacto para el cajero,INMERSIVOa pantalla completa para el cliente). - Sin ventana (
mostrar:false):rieles obligatorio; loscamposson 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_CURSOcon elcobroIddel 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.
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}/cancelar | Cancela 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/ocultar | Traen 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
2xxrápido. Si tu caja estaba caída, el veredicto sigue enGET /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
| HTTP | codigo | Cuándo |
|---|---|---|
| 400 | PETICION_INVALIDA | monto, referencia, riel, campos, modo, callbackUrl… (mensaje dice cuál) |
| 400 | DATOS_INCOMPLETOS, MEDIO_NO_DISPONIBLE, TERMINAL_NO_ENCONTRADO… | el Merchant rechazó el medio fijado; se cancela la sesión y te llega su motivo |
| 401 | TOKEN_LOCAL_INVALIDO | falta o no coincide X-Pago-Token |
| 404 | COBRO_NO_ENCONTRADO, SIN_VENTANA, NO_ENCONTRADO | |
| 409 | COBRO_EN_CURSO | ya hay una ventana de cobro abierta (trae cobroId), o el cobro no se puede cancelar aún |
| 503 | MERCHANT_INALCANZABLE | no se llegó al Merchant o no respondió en 15 s; el cobro queda RECHAZADA con ese código |
| 503 | SIN_CONFIGURAR | falta 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"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énPAGO_PUERTOyPAGO_TOKEN_LOCALpor entorno.
.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.
| Riel | Campos | Notas |
|---|---|---|
TARJETA | cedula | Necesita 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_MOVIL | cedula, 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. |
TRANSFERENCIA | cedula, banco, cuenta (20 dígitos) | |
QR | cedula, telefono | |
CRIPTO | cedula, 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éntimos | Desenlace |
|---|---|
.51 | RECHAZADA 51 Fondos insuficientes |
.05 | RECHAZADA 05 No honrar |
.61 | RECHAZADA 61 Excede el límite |
.96 | EN_DUDA; se resuelve APROBADA minutos después |
.91 | EN_DUDA; se resuelve RECHAZADA |
| cualquier otro | APROBADA 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 errorJava
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 errorVB6 / 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 IfO 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/saludresponde aunque falte la configuración:comercio: nullymerchantAlcanzable: 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).