Terminal Sessions

Terminal Sessions te permite enviar cobros a una terminal POS de Recurrente directamente desde tu sistema (ERP, POS propio, o cualquier integración). Tu sistema envía un comando con el monto, y la terminal automáticamente muestra la pantalla de cobro para que el cliente pague con tarjeta.

Casos de uso

  • Integración ERP/POS: Tu sistema de facturación o punto de venta genera el cobro, y la terminal de Recurrente lo procesa.
  • Cobros remotos: Envía cobros a terminales en diferentes ubicaciones desde un sistema centralizado.
  • Automatización: Elimina la necesidad de ingresar montos manualmente en la terminal.

Requisitos previos

  • Una cuenta de Recurrente con llaves de API
  • Al menos una terminal POS vinculada a tu cuenta
  • El ID de tu terminal (lo encuentras en POS en tu dashboard — haz clic en una terminal para ver su ID)

Configurar la terminal en modo espera

Para que la terminal reciba comandos de tu sistema, debe estar en la pantalla de espera. Esta pantalla muestra “Listo para cobrar” y espera automáticamente por nuevos comandos.

  1. En tu dashboard, ve a POS
  2. Haz clic en la terminal que quieres configurar
  3. En la página de detalle, haz clic en Modo espera (en la esquina superior derecha)
  4. La terminal mostrará la pantalla de espera

Cuando tu sistema envíe un comando, la terminal automáticamente muestra la pantalla de cobro. Después de cada pago (exitoso o fallido), la terminal regresa al modo espera para recibir el siguiente comando.

En modo espera, el operador de la terminal no puede ingresar montos manualmente. Todos los cobros se controlan desde tu sistema vía API.

Flujo de integración

Tu Sistema Recurrente Terminal POS
│ │ │
│ POST /terminal_session_ │ │
│ commands │ │
│ {terminal_id, amount, │ │
│ currency, external_id} │ │
│─────────────────────────────>│ │
│ │ Crea checkout + comando │
│ │ (status: pending) │
│ {id, status, checkout_url} │ │
│<─────────────────────────────│ │
│ │ │
│ │ Terminal levanta comando │
│ │ (status: dispatched) │
│ │─────────────────────────────>│
│ │ │
│ │ Cliente paga │
│ │ con tarjeta
│ │ │
│ Webhook: │ │
│ payment_intent.succeeded │ │
│<─────────────────────────────│ │

1. Crear un comando de terminal

Envía un POST a /api/terminal_session_commands con el monto y la terminal destino.

$curl -X POST https://app.recurrente.com/api/terminal_session_commands \
> -H "X-PUBLIC-KEY: tu_llave_publica" \
> -H "X-SECRET-KEY: tu_llave_secreta" \
> -H "Content-Type: application/json" \
> -d '{
> "terminal_id": "trm_abc123",
> "amount_in_cents": 5000,
> "currency": "GTQ",
> "external_id": "order-1234"
> }'

Respuesta

  • 201 Created — se creó un nuevo comando
  • 200 OK — ya existe un comando activo con el mismo external_id (se retorna el existente)

Si recibes 200 y el status es dispatched, significa que el comando ya fue levantado por la terminal. Usa un nuevo external_id para crear un comando nuevo.

1{
2 "id": 42,
3 "external_id": "order-1234",
4 "status": "pending",
5 "terminal_id": "trm_abc123",
6 "amount_in_cents": 5000,
7 "currency": "GTQ",
8 "checkout_id": "ch_xyz789",
9 "checkout_url": "https://app.recurrente.com/checkout-session/ch_xyz789"
10}

Parámetros

ParámetroTipoRequeridoDescripción
terminal_idstringSiID de la terminal POS
amount_in_centsintegerSi*Monto en centavos (ej. 5000 = Q50.00)
amountnumberSi*Monto en unidades (ej. 50.00). Alternativa a amount_in_cents
currencystringSiMoneda: GTQ o USD
external_idstringSiID de tu sistema para este cobro (para idempotencia)

Envía amount_in_cents o amount, no ambos. Si envías amount, se convierte automáticamente a centavos.

2. Recibir el resultado del pago

Registra un webhook endpoint para recibir notificaciones cuando el pago se complete.

Escucha el evento payment_intent.succeeded:

1{
2 "event": "payment_intent.succeeded",
3 "data": {
4 "checkout": {
5 "id": "ch_xyz789",
6 "metadata": {
7 "source": "terminal_session_webhook",
8 "terminal_id": "trm_abc123",
9 "external_id": "order-1234"
10 }
11 },
12 "amount_in_cents": 5000,
13 "currency": "GTQ"
14 }
15}

Usa el campo metadata.external_id para relacionar el pago con la orden en tu sistema.

Estados del comando

EstadoDescripcion
pendingCreado, esperando que la terminal lo levante
dispatchedLa terminal recibio el comando y esta procesando el cobro
consumedEl cobro se completo exitosamente
supersededReemplazado por un comando mas reciente a la misma terminal
failedEl cobro fallo

Idempotencia

El external_id es unico por cuenta. Si envias el mismo external_id dos veces:

  • Si el comando anterior esta activo (pending o dispatched), se retorna el comando existente.
  • Si el comando anterior fue superseded o failed, se retorna el comando existente sin crear uno nuevo.

Esto te permite reintentar requests de forma segura sin crear cobros duplicados.

Superseding (reemplazo de comandos)

Si envias un nuevo comando a la misma terminal con un external_id diferente, todos los comandos pendientes anteriores para esa terminal se marcan como superseded. La terminal solo procesa el comando mas reciente.

Esto es util cuando:

  • El cliente cambia el monto de la orden
  • Necesitas cancelar un cobro y enviar uno nuevo
  • Tu sistema reintenta con un nuevo ID de orden

Ejemplo completo

1import requests
2
3API_URL = "https://app.recurrente.com/api"
4HEADERS = {
5 "X-PUBLIC-KEY": "tu_llave_publica",
6 "X-SECRET-KEY": "tu_llave_secreta",
7 "Content-Type": "application/json"
8}
9
10def send_to_terminal(terminal_id, amount, currency, order_id):
11 """Envía un cobro a una terminal POS."""
12 response = requests.post(
13 f"{API_URL}/terminal_session_commands",
14 headers=HEADERS,
15 json={
16 "terminal_id": terminal_id,
17 "amount": amount,
18 "currency": currency,
19 "external_id": order_id
20 }
21 )
22 return response.json()
23
24# Enviar cobro de Q150.00 a la terminal
25result = send_to_terminal("trm_abc123", 150.00, "GTQ", "order-5678")
26print(f"Comando creado: {result['status']}")