Ir al contenido

Cierre de turno

Identificador del evento: shift.closed

IDPOS le avisa de que una caja cerró su turno. El aviso trae el resumen de lo cobrado en ese turno: la cantidad de ventas y el total por medio de pago y moneda.

Cuando se cierra el turno de una caja. Llega una vez por cada cierre.

  • Registrar el cierre de caja en su sistema, con su número de turno y sus totales.
  • Cuadrar lo cobrado por medio de pago y moneda contra lo que registró de cada venta.
  • Detectar ventas que no le llegaron: si los totales no coinciden, falta al menos un Pago completo de esa caja.
  • Identificar el cierre con el mismo número que figura en el ticket de cierre de la caja (correlative).
  1. Verifique la firma.

    Calcule el HMAC con el cuerpo crudo y compárelo con X-Webhook-Signature. Si no coincide, no procese el evento. Los pasos están en Autenticación y firma.

  2. Responda 2xx de inmediato.

    Guarde el evento en una cola y responda sin esperar a procesarlo. Si tarda más que el tiempo de espera, IDPOS lo trata como fallido y lo reintenta.

  3. Descarte los duplicados.

    Si ya procesó ese event_id del cuerpo, ya verificado, no lo procese otra vez. Como segunda clave use event_type junto con data.id (por ejemplo, shift.closed y el id del turno).

  4. Procese el evento en segundo plano.

    Registre el cierre con data.id y correlative. Compare summary con las ventas que registró de la misma caja (cashier.id) entre opens_at y closes_at.

IDPOS calcula el resumen con la misma consulta que usa el cierre de caja en el punto de venta, por lo que los totales coinciden con el reporte de cierre que se imprime.

En Cierre de turnoCon qué compararlo en Pago completo
cashier.iddata.cashier.id
Turno entre opens_at y closes_atpayments[].paid_at dentro de ese rango, con ambos extremos incluidos
summary.payments[], por method_id y currencySuma de payments[].amount por method_id
summary.payments[].tipSuma de payments[].tip por method_id
summary.sales_countCantidad de órdenes distintas

Una diferencia no siempre es una venta perdida:

  • Los pagos de adelantos no generan Pago completo y no se incluyen en el resumen.
  • Una orden pagada en partes en distintos turnos suma en cada turno lo que se cobró en él, pero envía Pago completo una sola vez, cuando se termina de pagar.
  • summary.payments[].amount es el monto neto del vuelto entregado, en la moneda en que se recibió el pago. Un cobro con vuelto o en una moneda distinta a la de la orden puede no coincidir con payments[].amount de Pago completo.
  • data.cashier de Pago completo es la caja donde se registró la orden. Si se cobró en otra caja, el cobro suma en el turno de la caja que cobró.
shift.closed.json
{
"event_id": "01J9ZM8T2W5K7Q3N6R1D4F0G8H",
"event_type": "shift.closed",
"timestamp": "2026-10-12T23:05:00Z",
"api_version": "v1",
"data": {
"id": "7b9d1f3a-5c7e-4a2b-8d4f-6e8a0c2b4d71",
"correlative": 128,
"store": {
"id": "6a8c0e2f-5b7d-4d9b-9e3a-4f6b8d0c2e53",
"name": "Tienda Centro",
"external_id": "T01"
},
"cashier": {
"id": "9d2f4b6c-8e0a-4c3b-9d5f-7a1b3c5e7f82",
"name": "Caja 1"
},
"opens_at": "2026-10-12T13:00:00Z",
"closes_at": "2026-10-12T23:04:58Z",
"closed_by": {
"id": "8c1e3a5b-7d9f-4b2a-8c4e-6f0a2b4d6e71",
"name": "Ana Torres"
},
"summary": {
"sales_count": 42,
"payments": [
{
"method_id": "2a4c6e8b-0d2f-4b6a-9c1e-3f5a7b9d1c04",
"method_name": "Efectivo",
"currency": "PEN",
"amount": "1250.40",
"tip": "15.00"
},
{
"method_id": "1b3d5f7a-9c2e-4d6b-8f1a-3c5e7a9b2d93",
"method_name": "Visa",
"currency": "PEN",
"amount": "2380.00",
"tip": "95.50"
},
{
"method_id": "2a4c6e8b-0d2f-4b6a-9c1e-3f5a7b9d1c04",
"method_name": "Efectivo",
"currency": "USD",
"amount": "60.00",
"tip": "0.00"
}
]
}
}
}

Cómo leer las tablas: el tipo aparece debajo del nombre del campo y «nulo» indica que puede venir como null. Los demás términos están en la guía de lectura de campos.

CampoDescripción
id
string (UUID)
Identificador del turno
correlative
entero · nulo
Número del turno. Es único por caja y es el que se imprime en el ticket de cierre
opens_at
string (fecha)
Fecha y hora de apertura del turno
closes_at
string (fecha)
Fecha y hora de cierre del turno
CampoDescripción
store
objeto
Tienda
store.id
string (UUID) · nulo
Identificador de la tienda
store.name
string · nulo
Nombre de la tienda
store.external_id
string · nulo
Código externo de la tienda
CampoDescripción
cashier
objeto
Caja que cerró el turno
cashier.id
string (UUID) · nulo
Identificador de la caja. Usa el mismo identificador que cashier.id de Pago completo
cashier.name
string · nulo
Nombre de la caja
CampoDescripción
closed_by
objeto · nulo
Usuario que cerró el turno. Es null si no se registró quién lo cerró o si ese usuario fue eliminado
closed_by.id
string (UUID)
Identificador del usuario
closed_by.name
string
Nombre del usuario
CampoDescripción
summary.sales_count
entero
Cantidad de órdenes distintas con al menos un pago aprobado de la caja entre opens_at y closes_at, ambos incluidos. No cuenta adelantos
summary.payments
lista
Total cobrado, con una fila por medio de pago y moneda. Vacía si no hubo cobros. Las filas vienen ordenadas por currency, luego por method_name y luego por method_id
summary.payments[].method_id
string (UUID)
Identificador del medio de pago. Es el mismo method_id que llega en Pago completo
summary.payments[].method_name
string
Nombre del medio de pago
summary.payments[].currency
string
Moneda en que se recibió el pago, por ejemplo PEN o USD
summary.payments[].amount
string (2 dec)
Monto cobrado neto del vuelto entregado: monto recibido menos vuelto, igual que en el reporte de cierre de caja. Puede ser negativo. No incluye propinas ni adelantos
summary.payments[].tip
string (2 dec)
Propinas de ese medio de pago y moneda