Pagos QR Banco Ganadero

GoSoft · QR Empresarial

Pagos QR Banco Ganadero

Integración bidireccional del QR Empresarial. GoSoft pide al banco la orden de cobro y muestra el QR en el documento; el banco avisa a GoSoft cuando el cliente paga, y ese aviso genera automáticamente el comprobante de ingreso.

Spec Integración QR v1.17.1 Formato JSON · UTF-8 Auth entrante JWT HS256 Monedas BOB · USD

Convenciones

La integración tiene dos direcciones y cada una se autentica de forma distinta. Conviene tenerlas claras antes de leer los endpoints.

DirecciónQuién llamaAutenticaciónServicios
Entrante El banco llama a GoSoft JWT HS256 que emite GoSoft /bg-qr/login · /bg-qr/payments
Saliente GoSoft llama al banco X-Api-Key + Bearer del banco access, collections, cancellations, status, transactions

URL base de los servicios entrantes

El host identifica a la empresa (multi-tenant). Los paths son exactos por exigencia de la spec.

Producción
https://{empresa}.gosoft.app/bg-qr/login
https://{empresa}.gosoft.app/bg-qr/payments

Envoltura de respuesta

Los servicios entrantes responden en el formato de la spec. A diferencia de otras APIs de GoSoft, acá el status HTTP sí es real y acompaña al result.

Estructura
{
  "result":  "COD000",                       // codigo de la spec
  "message": "Autenticacion satisfactoria"   // texto legible
}
resultSignificadoHTTP
COD000Operación satisfactoria.200
COD001Parámetros insuficientes, credenciales inválidas o qrId inexistente.400 / 401 / 404
COD002Reservado por la spec.
COD003Error al procesar el pago del lado de GoSoft.422 / 500

Cabeceras

CabeceraValorNota
Content-Typeapplication/jsonObligatoria. También se acepta form-urlencoded como respaldo.
AuthorizationBearer <token>Sólo en /bg-qr/payments. Se aceptan los prefijos Bearer, JWT Bearer o el token pelado.
Bitácora

Cada llamada del banco —exitosa o fallida— queda registrada en bg_qr_webhook_log con IP, cuerpo, cabeceras, status y resultado. Si hay una discrepancia, esa tabla es la fuente de verdad para el reclamo.

Flujo del cobro

El registro del pago depende del webhook, no de la pantalla: aunque nadie esté mirando el estado, el cobro se contabiliza igual.

PASO 1

Generar la orden

GoSoft pide al banco una orden de cobro por el saldo del documento y recibe el QR.

PASO 2

Mostrar el QR

El QR se imprime en el PDF de la venta o cobranza, junto al link de estado.

PASO 3

El cliente paga

Escanea desde su banca móvil. El banco procesa y acredita la cuenta.

PASO 4

El banco avisa

Llama a /bg-qr/login y luego a /bg-qr/payments con el qrId.

PASO 5

Se contabiliza

GoSoft genera el comprobante de ingreso y baja el saldo de las ventas cubiertas.


ENDPOINT 01 Entrante · lo llama el banco

Login del banco

POST /bg-qr/login

Sección 7 de la spec. El banco se autentica contra GoSoft y recibe el token que usará para confirmar pagos.

Cuerpo (JSON)

CampoTipoDescripción
userNamestringreq Usuario del webhook, entregado por GoSoft a la empresa y de ahí al banco.
passwordstringreq Contraseña del webhook. Comparación en tiempo constante.

Ejemplo de llamada

cURL
curl -X POST "https://demo.gosoft.app/bg-qr/login" \
  -H "Content-Type: application/json" \
  -d '{
        "userName": "bg_webhook_demo",
        "password": "**********"
      }'
Autenticación correcta200
{
  "result": "COD000",
  "message": "Autenticacion satisfactoria",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiZ193ZWJob29rX2RlbW8ifQ.k7Qw..."
}
Faltan credenciales400
{
  "result": "COD001",
  "message": "Parametros de entrada insuficientes (userName/password)"
}
Credenciales inválidas401
{
  "result": "COD001",
  "message": "Credenciales invalidas para userName 'bg_webhook_demo'"
}
Sobre el token

Es un JWT HS256 emitido y firmado por GoSoft, con vigencia de 3600 segundos. Lleva el usuario en sub y la empresa en company_id. Al vencer, el banco vuelve a llamar a /bg-qr/login.

Las credenciales del webhook son por empresa: viven en bg_qr_credentials, no en un archivo de configuración compartido.

ENDPOINT 02 Entrante · lo llama el banco

Registro de pago

POST /bg-qr/payments

Sección 8 de la spec. El banco confirma que una orden de cobro fue pagada. Esta llamada es la que genera el asiento contable.

Cuerpo (JSON)

CampoTipoDescripción
qrIdstringreq Identificador de la orden de cobro devuelto por el banco al generarla.
transactionIdstringopc Número de transacción del banco. Queda como número de documento del comprobante. Si viene vacío se usa QR{qrId}.
payDatestringopc Fecha de pago en formato ddmmyyyy. Si no es válida se usa la fecha del servidor.

Ejemplo de llamada

cURL
curl -X POST "https://demo.gosoft.app/bg-qr/payments" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -d '{
        "qrId": "3286941",
        "transactionId": "BG-0000884213",
        "payDate": "20082026"
      }'
Pago registrado200
{
  "result": "COD000",
  "message": "Pago registrado correctamente"
}
Reintento del banco200
{
  "result": "COD000",
  "message": "Pago ya registrado"
}
Idempotencia

Si el QR ya estaba pagado, GoSoft responde COD000 con «Pago ya registrado» y no genera un segundo comprobante. El banco puede reintentar sin riesgo de duplicar el ingreso.

Todo el registro corre dentro de una transacción: un comprobante por el total, la acreditación a cuentas por cobrar de cada venta cubierta y un débito al fondo del Banco Ganadero.

Respuestas de error

Token inválido o vencido401
{
  "result": "COD001",
  "message": "Token JWT invalido o expirado (Authorization)"
}
Falta el qrId400
{
  "result": "COD001",
  "message": "Falta qrId en el body"
}
QR desconocido404
{
  "result": "COD001",
  "message": "qrId no encontrado en bg_qr_orders"
}
No se pudo contabilizar422
{
  "result": "COD003",
  "message": "Falta configuracion contable (fondo/CxC)"
}
Qué hacer ante un COD003

El pago existe en el banco pero GoSoft no pudo contabilizarlo: falta configuración contable o el QR no tiene ventas asociadas. El banco debe reintentar: una vez corregida la configuración, la misma llamada registra el pago. Nada queda a medias, porque la escritura es transaccional.

ENDPOINT 03 Público · sin sesión

Estado del QR

GET /qr-pago-estado/{public_token}

Espejo del estado del cobro, para la pantalla del cajero o para el cliente. Es sólo lectura: el pago se registra por el webhook, no por esta vista.

Parámetro de ruta

CampoTipoDescripción
public_tokenstringreq Token aleatorio de la orden. Nunca se expone el qrId en esta ruta.

Ejemplo de llamada

cURL
curl "https://demo.gosoft.app/qr-pago-estado/9f3c1ab84d27e6b5c081"
Pagado200
{
  "found": true,
  "estado": "paid",
  "amount": 1250.50,
  "currency": "BOB",
  "pay_date": "2026-08-20"
}
Token desconocido404
{
  "found": false
}

Valores de estado

ValorSignificado
pendingQR emitido, aún sin confirmación de pago del banco.
paidEl banco confirmó el pago y GoSoft lo contabilizó.
cancelledLa orden fue anulada, o el documento se cobró por otro medio.
Vista HTML

La misma orden tiene una página para humanos en GET /qr-pago/{public_token}, que hace polling contra este endpoint y se actualiza sola cuando el pago entra. Es el link que se imprime junto al QR.


Servicios del banco que consume GoSoft

Lado saliente de la integración. Se documentan para trazabilidad: la especificación oficial de estos servicios es la del banco.

Bases por ambiente

Endpoints del banco
QA     https://api.bg.com.bo/bgqa/ws-servicio-codigo-qr-empresas/service/v1/qrcode/
PROD   https://api.bg.com.bo/bgprod/empresas/ws-servicio-codigo-qr-empresas/service/v1/qrcode/
ServicioSpecPara qué lo usa GoSoft
POST access§2Obtener el token del banco. Requiere el header X-Api-Key.
POST collections§3Generar la orden de cobro y recibir la imagen del QR.
POST cancellations§4Anular un QR vigente antes de emitir uno nuevo por el saldo actualizado.
POST transactions§5Listar órdenes de cobro y pago por rango de fechas.
POST status§6Consultar el estado de una orden puntual.

Ejemplo: generación de la orden de cobro

Solicitud
POST .../qrcode/collections
Authorization: Bearer <token del banco>

{
  "accountReference": "1000123456",
  "amount": "1250.50",
  "currency": "BOB",
  "gloss": "Venta #40821",
  "expirationDate": "27082026",
  "singleUse": 1,
  "userName": "empresa_demo",
  "apiKey": "**********"
}
Respuesta200
{
  "result": "COD000",
  "message": "Orden generada",
  "qrId": "3286941",
  "qrImage": "iVBORw0KGgoAAAANSUhEUgAA..."
}
Formato de fechas

El banco usa ddmmyyyy sin separadores, tanto en expirationDate como en los rangos de transactions y en el payDate que envía al confirmar el pago.

El amount viaja como cadena con dos decimales y punto decimal: "1250.50", nunca 1250.5 ni con separador de miles.

Credenciales por empresa

apiKey, userName, password y accountReference son distintos para cada empresa y viven en bg_qr_credentials, nunca en el código ni en variables de entorno. Una empresa puede tener además un juego de credenciales separado para ventas sin factura.

Estados y códigos

Cada orden guarda dos estados independientes: en qué punto del cobro está, y si la fila sigue vigente.

Estado del cobro order_state

ValorEstadoDescripción
1PendienteQR emitido y vigente, esperando que el cliente pague.
2PagadoEl banco confirmó el pago y existe comprobante de ingreso.
3AnuladoLa orden se dio de baja sin cobro.

Vigencia de la orden status

ValorEstadoDescripción
1ActivoÚnico estado que se muestra y se puede cobrar.
0AnuladoBaja confirmada también en el banco.
3Baja pendienteAnulado en GoSoft pero aún vivo en el banco: la anulación remota falló y se reintenta automáticamente hasta cerrarla.
Por qué existe el estado 3

Si el banco falla al anular y la orden se marcara igual como dada de baja, un cliente podría escanear un QR viejo y pagar un monto que ya no corresponde, sin rastro. Con la baja pendiente el caso queda visible y se reintenta. Ninguna consulta de QR cobrables incluye el estado 3.

Tabla de errores

Respuestas de los servicios entrantes, con su status HTTP real.

HTTPresultmessageCausa y qué hacer
400COD001Parametros de entrada insuficientes (userName/password)Falta usuario o contraseña en el login.
401COD001Credenciales invalidas para userName '...'Usuario o contraseña del webhook incorrectos, o credencial inactiva.
401COD001Token JWT invalido o expirado (Authorization)Token vencido (dura 1 hora) o firmado con otro secreto. Repetir el login.
400COD001Falta qrId en el bodyLa confirmación de pago llegó sin identificar la orden.
404COD001qrId no encontrado en bg_qr_ordersLa orden no fue emitida por esta empresa, o se apuntó al host equivocado.
422COD003QR sin ventas asociadasLa orden quedó huérfana. Reportar a GoSoft; el banco debe reintentar luego.
422COD003Falta configuracion contable (fondo/CxC)La empresa no configuró el fondo del banco o la cuenta por cobrar.
500COD003Excepcion: ...Fallo inesperado. La transacción se revierte; el reintento es seguro.

Reglas de negocio

Límites de la spec y comportamientos propios de GoSoft.

Límites de la orden de cobro

ConceptoValor
Monto máximo por QR69.000,00 Bs
Caducidad máxima365 días
UsosingleUse = 1 (un solo pago por QR)
MonedasBOB · USD
Timeout de conexión / lectura15 s / 30 s

QR de un solo uso, siempre al día

Cada vez que se reimprime un documento, GoSoft anula el QR anterior y emite uno nuevo por el saldo actual. La razón: el documento pudo haberse cobrado parcialmente en efectivo o por transferencia, y un QR viejo seguiría cobrando un monto que ya no corresponde.

Cobranza conjunta

Un QR puede cubrir varias ventas a la vez (cobranza de cuentas por cobrar). Al confirmarse el pago se genera un solo comprobante por el total y se baja el saldo de cada venta cubierta. Si el cliente tenía además el QR de una venta individual incluida en esa cobranza, ese QR hermano se anula al momento de pagar, no antes: hasta entonces el cliente puede elegir cuál pagar.

Degradación ante fallas del banco

Si el banco no responde al generar la orden, el documento se imprime igual, sin QR. Nunca se bloquea una venta por una falla del servicio de QR.

Recomendaciones de integración

  • Reintentar /bg-qr/payments ante cualquier respuesta que no sea COD000: el endpoint es idempotente.
  • Renovar el token con /bg-qr/login cuando llegue un 401; no cachearlo más de una hora.
  • Enviar siempre transactionId: es lo que permite conciliar el extracto del banco con el comprobante de GoSoft.
  • Apuntar al host de la empresa correcta; el qrId no existe fuera de su propio tenant.
  • Ante una discrepancia, pedir el detalle de bg_qr_webhook_log: guarda cada llamada recibida con su cuerpo original.