Kundenkonto: Guthaben & Treuepunkte¶
Jeder Kunde (api/v1/customers) führt zwei getrennte, dauerhafte Konten:
- Prepaid-Guthaben (
accountBalance) — Echtgeld-Guthaben, das der Kunde vorab einzahlt und dann bei Käufen als Zahlmittel nutzt. Auszahlbar. - Treuepunkte / Bonus (
loyaltyPoints/bonusBalance) — Punkte/Bonus, die bei Käufen gutgeschrieben und eingelöst werden. Nicht auszahlbar (Bonus verfällt bei einer Guthaben-Auszahlung).
Diese Seite korrigiert eine ältere Falschangabe in der REST-API-Übersicht: Es gibt kein
POST /customers/{id}/credit und keine GET /customers/{id}/transactions — diese Routen
existieren im Code nicht. Die echten Routen sind unten dokumentiert.
Endpoints¶
CustomersController — Basis api/v1/customers:
| Methode | Endpoint | Zweck |
|---|---|---|
GET |
/{id}/account-transactions |
Guthaben-/Kontobewegungen (Prepaid-Ledger) |
GET |
/{id}/point-transactions |
Treuepunkte-Bewegungen |
POST |
/{id}/payout |
Restguthaben auszahlen (nur accountBalance, nicht Bonus) |
POST |
/{id}/settle |
Offenen Saldo (z. B. Disco-Schuldschein) durch Einzahlung ausgleichen |
GET |
/card/{cardId} |
Kunde per Kundenkarte auflösen |
GET |
/{id}/invoice-summary |
Rechnungs-/Abo-Übersicht des Kunden |
Beide Transaktions-Endpoints akzeptieren optional dateFrom/dateTo als Query-Parameter zur
Eingrenzung des Zeitraums.
Kontobewegungen (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 ist ein int ohne eigenes öffentliches Enum-Schema; in der Praxis beobachtete Werte:
0 Aufladung/Einzahlung, 1 Verwendung (Bezahlung mit Guthaben), 2 Korrektur (manuelle
Stornoberichtigung), 3 Bonusgutschrift, 4 Bonusverwendung/-verfall, 5 Auszahlung.
Punktebewegungen (point-transactions)¶
{
"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 = gutgeschrieben (Earned), 1 = eingelöst (Redeemed), 2 = Korrektur (Correction).
Auszahlen (payout)¶
Zahlt Guthaben aus dem accountBalance aus (nicht aus dem Bonus — der verfällt dabei).
POST /api/v1/customers/cus_a1b2c3/payout
Authorization: Bearer eyJ...
Content-Type: application/json
{ "amount": 20.00 }
Fehlerfall (zu wenig Guthaben) — 400 mit errorCode: "INSUFFICIENT_BALANCE".
Saldo ausgleichen (settle)¶
Für einen offenen (negativen) Saldo — z. B. ein Disco-Schuldschein (nicht gedeckter Restbetrag beim Verlassen, siehe Disco-API) — zahlt der Kunde ein, der Saldo bewegt sich Richtung Null:
POST /api/v1/customers/cus_a1b2c3/settle
Authorization: Bearer eyJ...
Content-Type: application/json
{ "amount": 15.00, "method": "EC" }
method ist frei (Standard "Bar") und landet nur in der Beschreibung der Buchung — keine
Verknüpfung zu einem Zahlungsterminal.
Guthaben aufladen — der echte Weg¶
Es gibt keinen eigenen /credit-Endpoint. Ein Prepaid-Guthaben wird stattdessen genauso
aufgeladen wie jeder andere Verkauf: über die normalen Zahlungs-Endpoints
(POST /api/v1/payments/direct oder POST /api/v1/payments/table), mit zwei Bedingungen:
- Der verkaufte Artikel hat
extraOption: 1(KundenAufladung) — ein „Aufladeartikel" (z. B. „Guthaben 50 €"), angelegt über die normale Artikel-API (POST /api/v1/articles). - Die Zahlung ist mit
customerIdan den Kunden verknüpft.
Der Artikelpreis (× Menge) wird dann automatisch dem accountBalance des Kunden gutgeschrieben —
zusätzlich zur eigentlichen Zahlungsabwicklung (der Gast bezahlt den Aufladebetrag ganz normal per
payments[], z. B. bar oder Karte). Ist am Artikel ein Aufladebonus konfiguriert
(cardUploadPercent/cardUploadAmount), wird automatisch zusätzlich Bonus gutgeschrieben.
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"
}
Die Aufladung erscheint danach als type: 0-Eintrag in account-transactions (siehe oben),
verknüpft über receiptId mit dem entstandenen Kassenbeleg.
Flow¶
- Kunde anlegen —
POST /api/v1/customers(Minimum:name/customerNumber). - Guthaben aufladen — Verkauf eines
KundenAufladung-Artikels mitcustomerId(POST /api/v1/payments/direct, s. o.). - Auf Konto buchen — spätere Käufe des Kunden mit
payments[].method = "CustomerAccount"ziehen vomaccountBalanceab (belastet, nicht aufgeladen). - Kontrolle —
GET /{id}/account-transactionsprüft den Verlauf. - Punkte — werden im normalen Verkaufsprozess gutgeschrieben/eingelöst, nachlesbar über
GET /{id}/point-transactions. - Auszahlen/Ausgleichen —
POST /{id}/payout(Restguthaben zurück) oderPOST /{id}/settle(offenen Saldo/Schuldschein begleichen).
Abgrenzung: Kundenkonto-Guthaben ↔ Disco-Karten-Guthaben¶
Zwei unterschiedliche Konzepte, die leicht verwechselt werden:
| Kundenkonto-Guthaben | Disco-Karten-Guthaben | |
|---|---|---|
| Endpoint | payments/* + customers/* |
POST /api/v1/disco/guest/{cardId}/topup |
| Bindung | an den Kunden-Datensatz, dauerhaft | an die physische Karte, für einen Aufenthalt |
| Auszahlung | POST /{id}/payout |
Restguthaben beim Checkout (disco/guest/{cardId}/checkout) |
→ Details zur Karten-Variante: Disco-API (Cashless-Club).