Saltar a contenido
v26.3

Cuenta de cliente: saldo y puntos de fidelidad

Cada cliente (api/v1/customers) mantiene dos cuentas separadas y permanentes:

  • Saldo prepago (accountBalance) — saldo en dinero real que el cliente ingresa por adelantado y luego utiliza como medio de pago en sus compras. Se puede pagar (retirar).
  • Puntos de fidelidad / bono (loyaltyPoints / bonusBalance) — puntos/bono que se abonan y se canjean en las compras. No se pueden pagar (el bono caduca al realizar una retirada de saldo).

Esta página corrige una indicación incorrecta anterior en el Resumen de la API REST: no existe POST /customers/{id}/credit ni GET /customers/{id}/transactions — estas rutas no existen en el código. Las rutas reales se documentan a continuación.

Endpoints

CustomersController — base api/v1/customers:

Método Endpoint Descripción
GET /{id}/account-transactions Movimientos de saldo/cuenta (libro prepago)
GET /{id}/point-transactions Movimientos de puntos de fidelidad
POST /{id}/payout Pagar el saldo restante (solo accountBalance, no el bono)
POST /{id}/settle Compensar un saldo pendiente (p. ej. un pagaré del Disco) mediante un ingreso
GET /card/{cardId} Resolver el cliente a partir de la tarjeta de cliente
GET /{id}/invoice-summary Resumen de facturas/suscripciones del cliente

Ambos endpoints de transacciones aceptan opcionalmente dateFrom/dateTo como parámetros de consulta para acotar el periodo.

Movimientos de cuenta (account-transactions)

GET /api/v1/customers/cus_a1b2c3/account-transactions?dateFrom=2026-07-01&dateTo=2026-07-31
Authorization: Bearer eyJ...
{
  "success": true,
  "data": [
    {
      "id": "cat_001",
      "customerId": "cus_a1b2c3",
      "customerName": "Max Mustermann",
      "receiptId": "rec_9f8e7d",
      "type": 0,
      "amount": 50.00,
      "balanceAfter": 50.00,
      "bonusBalanceAfter": 2.50,
      "description": "Guthaben-Aufladung: Guthaben 50€ x 1",
      "personalId": "usr_kasse1",
      "transactionDate": "2026-07-05T18:22:00Z"
    }
  ]
}

type es un int sin un esquema de enum público propio; valores observados en la práctica: 0 recarga/ingreso, 1 uso (pago con saldo), 2 corrección (rectificación manual de anulación), 3 abono de bono, 4 uso/caducidad de bono, 5 pago (retirada).

Movimientos de puntos (point-transactions)

GET /api/v1/customers/cus_a1b2c3/point-transactions
Authorization: Bearer eyJ...
{
  "success": true,
  "data": [
    {
      "id": "cpt_001",
      "customerId": "cus_a1b2c3",
      "customerName": "Max Mustermann",
      "receiptId": "rec_9f8e7d",
      "articleId": "art_cola",
      "articleName": "Cola 0,3l",
      "type": 0,
      "points": 3.5,
      "pointsAfter": 42.5,
      "description": "Punkte für Verkauf",
      "transactionDate": "2026-07-05T18:22:00Z"
    }
  ]
}

type: 0 = abonado (Earned), 1 = canjeado (Redeemed), 2 = corrección (Correction).

Pagar (payout)

Paga saldo del accountBalance (no del bono — este caduca en el proceso).

POST /api/v1/customers/cus_a1b2c3/payout
Authorization: Bearer eyJ...
Content-Type: application/json

{ "amount": 20.00 }
{ "success": true, "data": { "success": true, "accountBalance": 30.00, "bonusBalance": 0 } }

Caso de error (saldo insuficiente) — 400 con errorCode: "INSUFFICIENT_BALANCE".

Compensar saldo (settle)

Para un saldo pendiente (negativo) — p. ej. un pagaré del Disco (importe restante no cubierto al salir, véase API de Disco) — el cliente realiza un ingreso y el saldo se acerca a cero:

POST /api/v1/customers/cus_a1b2c3/settle
Authorization: Bearer eyJ...
Content-Type: application/json

{ "amount": 15.00, "method": "EC" }

method es libre (por defecto "Bar") y solo se refleja en la descripción del movimiento — sin vinculación a un terminal de pago.

Recargar saldo — la vía real

No existe un endpoint /credit propio. En su lugar, un saldo prepago se recarga exactamente igual que cualquier otra venta: a través de los endpoints de pago normales (POST /api/v1/payments/direct o POST /api/v1/payments/table), con dos condiciones:

  1. El artículo vendido tiene extraOption: 1 (KundenAufladung) — un «artículo de recarga» (p. ej. «Saldo 50 €»), creado mediante la API de artículos normal (POST /api/v1/articles).
  2. El pago está vinculado al cliente mediante customerId.

El precio del artículo (× cantidad) se abona entonces automáticamente al accountBalance del cliente — además de la propia gestión del pago (el invitado paga el importe de recarga con total normalidad mediante payments[], p. ej. en efectivo o con tarjeta). Si en el artículo hay configurado un bono de recarga (cardUploadPercent/cardUploadAmount), se abona automáticamente también un bono adicional.

POST /api/v1/payments/direct
Authorization: Bearer eyJ...
Idempotency-Key: 6f1c2a9e-3b7d-4e21-9b0a-1f2e3d4c5b6a
Content-Type: application/json

{
  "items": [ { "articleId": "art_aufladung50", "quantity": 1 } ],
  "payments": [ { "method": "Cash", "amount": 50.00 } ],
  "customerId": "cus_a1b2c3"
}

La recarga aparece después como una entrada type: 0 en account-transactions (véase arriba), vinculada mediante receiptId con el recibo de caja generado.

Flujo

  1. Crear clientePOST /api/v1/customers (mínimo: name/customerNumber).
  2. Recargar saldo — venta de un artículo KundenAufladung con customerId (POST /api/v1/payments/direct, véase arriba).
  3. Cargar en la cuenta — las compras posteriores del cliente con payments[].method = "CustomerAccount" se deducen del accountBalance (se debita, no se recarga).
  4. ControlGET /{id}/account-transactions permite comprobar el historial.
  5. Puntos — se abonan/canjean en el proceso de venta normal, consultables mediante GET /{id}/point-transactions.
  6. Pagar/CompensarPOST /{id}/payout (devolver el saldo restante) o POST /{id}/settle (liquidar un saldo pendiente/pagaré).

Diferencia: saldo de cuenta de cliente frente a saldo de tarjeta Disco

Dos conceptos distintos que se confunden fácilmente:

Saldo de cuenta de cliente Saldo de tarjeta Disco
Endpoint payments/* + customers/* POST /api/v1/disco/guest/{cardId}/topup
Vinculación al registro del cliente, permanente a la tarjeta física, para una estancia
Pago POST /{id}/payout saldo restante en el checkout (disco/guest/{cardId}/checkout)

→ Detalles sobre la variante de tarjeta: API de Disco (club sin efectivo).