Ana içeriğe geç
v26.3

Disko API'si (Nakitsiz Kulüp)

Disko modülü, tamamen nakitsiz yürütülen bir kulüp/disko işletmesini temsil eder: Her misafir girişte, tüm süre boyunca bir hesap görevi gören bir kart (çip/RFID/NFC) alır. Her şey — giriş, içecekler, vestiyer, masa siparişleri — karta kaydedilir ve ancak ayrılırken (veya ön ödemeli yüklemeyle) hesaplanır. API, bir kartın tüm yaşam döngüsünü kapsar: girişe al → kaydet → yükle → hesapla → çıkışa izin ver.

Tüm uç noktalar ortak api/v1/disco ön ekinin altındadır (bu ön eki üç denetleyici paylaşır: Misafirler/Kartlar, İstatistikler, Yapılandırma — artı api/v1/disco/turnstile altındaki turnike donanımı API'si). Tüm alanlarda olduğu gibi: JWT Bearer, yanıt zarfı { "success": true, "data": { ... } }, camelCase alanlar, opak kimlikler.

Disko modülünü gerektirir

Disko modülünü gerektirir; aktif bir modül lisansı olmadan uç noktalar HTTP 403 döner (errorCode: "MODULE_NOT_LICENSED").

Kart kimliği ile dahili kimlik

Her kayıtta dahili bir id (veritabanı anahtarı) ve bir guestId bulunur — bu, çipin fiziksel olarak üzerine yazıldığı kart numarasıdır. Tüm {cardId} rotaları dahili id'yi değil, guestId'yi bekler.

Misafirler ve Kartlar

DiscoController — Temel api/v1/disco:

Yöntem Uç Nokta Amaç
POST /enter Misafiri turnikede içeri alma (giriş kart taraması)
POST /leave Misafiri ödeme ile çıkışa alma (çıkış taraması)
POST /leave-combined Birden fazla kartı („çift") ortak bir belgede hesaplama ve kapatma
POST /book Karta ürün kaydetme (bar/tezgâh)
POST /book/batch Karta birden fazla ürünü aynı anda kaydetme
POST /book/{guestId}/{bonIndex}/void Kaydedilen kalemi iptal etme
POST /table/book-on-card Masa siparişini karta kaydetme (masa ödemesinin yerini alır)
POST /wardrobe/return/{guestId}/{bonIndex} Vestiyer iadesi (numarayı kaldırır, tutar kalır)
GET /guest/{cardId} Misafir/kart, güncel ciro dahil (yalnızca açık kartlar)
GET /guest/{cardId}/status Kart durumu, kilitli/iptal edilmiş/hesaplanmış dahil
GET /guest/{cardId}/checkout Hesaplama görünümü: açık tutar, ön ödeme karşılığı, çıkabilir mi
GET /guests Şu anda mevcut olan (giriş yapmış) tüm misafirler
POST /guest/{cardId}/topup Ön ödemeli bakiye yükleme
POST /guest/{cardId}/transfer Kartı/bakiyeyi başka bir karta aktarma (kart değişimi)
POST /guest/{cardId}/lock Kartı kilitleme (artık kayıt yapılamaz)
POST /guest/{cardId}/unlock Kartın kilidini açma
POST /guest/{cardId}/storno Kartın tamamını iptal etme + ödeme yapmayan olarak işaretleme
POST /guest/{cardId}/collect-storno İptal edilmiş kartı kasada sonradan tahsil etme
POST /guest/{cardId}/image Kart fotoğrafı ekleme
POST /terminal/pay Bilgi terminalinde self servis kart ödemesi başlatma
POST /personal/{cardId}/close Bir personel kartını hesaplama (öz tüketim)
POST /personal/close-all Tüm açık personel kartlarını öz tüketim olarak hesaplama
POST /close-all Tüm açık kartları kapatma (işletme kapanışı/gün sonu raporu)

Karta kaydetme

POST /api/v1/disco/book, açık bir karta bir ürün kaydeder (ödemesiz bar/tezgâh satışı — ödeme ancak leave/çıkışta gerçekleşir).

POST /api/v1/disco/book
Authorization: Bearer eyJ...
Content-Type: application/json

{
  "guestId": "crd_10042",
  "articleId": "art_cola",
  "articleName": "Cola 0,3l",
  "count": 2,
  "price": 3.50,
  "workplaceId": "wp_bar1",
  "workplaceName": "Bar 1"
}

Yanıt (200 OK, DiscoGuestResponse):

{
  "success": true,
  "data": {
    "id": "dgu_9f8e7d",
    "guestId": "crd_10042",
    "gender": 1,
    "enterDate": "2026-07-12T22:05:00Z",
    "amount": 7.00,
    "payedAmount": 0,
    "uploadAccount": 20.00,
    "iou": 0,
    "isOpen": true,
    "isLocked": false,
    "openBons": [
      { "articleId": "art_cola", "articleName": "Cola 0,3l", "price": 3.50, "count": 2, "total": 7.00,
        "workplaceId": "wp_bar1", "workplaceName": "Bar 1", "bookDate": "2026-07-12T23:10:00Z", "isVoided": false }
    ]
  }
}

Kart durumu

GET /api/v1/disco/guest/{cardId}/status, kartın hâlâ açık olup olmadığından bağımsız olarak durumu döner — yalnızca açık kartları bulan GET /guest/{cardId}'den farklı olarak. Böylece bar/vestiyer/kasa, kilitli veya iptal edilmiş kartları da tanır.

GET /api/v1/disco/guest/crd_10042/status
Authorization: Bearer eyJ...
{
  "success": true,
  "data": {
    "found": true,
    "isOpen": true,
    "isLocked": false,
    "stornoed": false,
    "guest": { "id": "dgu_9f8e7d", "guestId": "crd_10042", "amount": 7.00, "uploadAccount": 20.00 }
  }
}

Güncel ciro

GET /api/v1/disco/guest/{cardId}, yukarıdakiyle aynı DiscoGuestResponse'u, devam eden açık kayıtlar (openBons) ve ön ödemeli bakiye (uploadAccount) dahil olarak döner. Kart (artık) açık değilse yanıt 404 GUEST_NOT_FOUND olur.

Hesaplama/Çıkış

GET /api/v1/disco/guest/{cardId}/checkout hiçbir şeyi değiştirmez — kartın hâlâ ne borçlu olduğunun, ön ödemeli bakiyenin neyi karşıladığının ve otomatik olarak çıkıp çıkamayacağının (self-checkout, mobil çıkış kasası, turnike) salt bir önizlemesidir.

{
  "success": true,
  "data": {
    "cardId": "crd_10042",
    "found": true,
    "isOpen": true,
    "isLocked": false,
    "consumption": 7.00,
    "netOwed": 7.00,
    "prepaid": 20.00,
    "amountToPay": 0,
    "refund": 13.00,
    "canAutoSettle": true,
    "status": "ok",
    "message": "Karte kann raus — Restguthaben 13.00 €"
  }
}

status, ok | pay | locked | closed | notfound değerlerinden biridir.

Karta masa

POST /api/v1/disco/table/book-on-card, bir masanın açık kalemlerini bir Disko kartına aktarır (Disko masa modu) — masadaki nakit/kart ödemesinin yerini alır, kart limitleri geçerliliğini korur, masa boşalır.

{ "tableId": "tbl_12", "cardId": "crd_10042", "openBonIds": null }

openBonIds: null, tüm açık kalemleri aktarır; bir liste yalnızca bir „Sofort" (anlık) turunu aktarır.

Yükleme

POST /api/v1/disco/guest/{cardId}/topup, kartın ön ödemeli bakiyesini (uploadAccount) yükler — tutar serbestçe seçilebilir, kalan bakiye çıkışta iade edilir.

{ "amount": 20.00, "paymentMethod": "Bar" }

Yanıt yine güncellenmiş DiscoGuestResponse'dur (uploadAccount artırılmış).

Karıştırma riski: Disko kart bakiyesi ile müşteri hesabı bakiyesi

Burada söz konusu olan, tek bir Disko ziyaretinin kart ön ödemesidir (ayrılışta biter/iade edilir). Kalıcı bir müşteri hesabı bakiyesi ise ayrı bir kavramdır — bkz. Müşteri Hesabı: Bakiye ve Puanlar.

İstatistikler

DiscoStatsController — Temel api/v1/disco:

Yöntem Uç Nokta Amaç
GET /stats Canlı göstergeler (ciro, mevcut misafirler, gruba/çalışma yerine/personele/saate göre doluluk)
GET /search Kart/misafir arama (kart numarası, ad, dönem, cinsiyet, iptal filtresi, sayfalama)
GET /personal/{ownerId}/activity Bir personel kartının etkinliği (tüketim, iptal, kayıp/fire)
GET /daylog/{date} Bir tarih için gün protokolü

Örnek: GET /api/v1/disco/stats

{
  "success": true,
  "data": {
    "totalGuests": 214,
    "currentGuests": 87,
    "totalMale": 96,
    "totalFemale": 118,
    "totalRevenue": 4211.50,
    "averageSpend": 19.68,
    "byGroup": [ { "groupId": "eg_std", "groupName": "Standard", "count": 180, "revenue": 3500.00 } ],
    "byWorkplace": [ { "workplaceId": "wp_bar1", "workplaceName": "Bar 1", "count": 400, "revenue": 2200.00 } ],
    "byStaff": [ { "personalId": "per_1", "personalName": "Mo", "count": 120, "revenue": 900.00 } ],
    "byHour": [ { "hour": 23, "count": 60, "revenue": 800.00 } ]
  }
}

GET /search, diğerlerinin yanı sıra cardId, name, dateFrom/dateTo, gender, timeFrom/timeTo, workplaceId, enterGroupId, wardrobe, stornoed, offset/limit, excludeImages parametrelerini destekler.

Yapılandırma

DiscoConfigController — Temel api/v1/disco, giriş/fiyat grupları için klasik CRUD („Entergroups" — ör. giriş ücreti, kart limiti, ücretsiz içecekler, minimum tüketim belirler):

Yöntem Uç Nokta Amaç
GET /entergroups Tüm giriş grupları
GET /entergroups/{id} Tek bir giriş grubu
POST /entergroups Giriş grubu oluşturma
PUT /entergroups/{id} Giriş grubunu güncelleme
DELETE /entergroups/{id} Giriş grubunu silme

Turnike Donanımı

DiscoTurnstileControllerapi/v1/disco/turnstile/{key}/{cardId} (GET/POST).

JWT yok — kimlik doğrulama URL içindeki API anahtarı ({key}) ile yapılır, böylece basit bir turnike kumandası rotayı bir giriş akışı olmadan çağırabilir. 200 = kart çıkabilir, diğer tüm kodlar bir neden taşır (kilitli/açık/zaten hesaplanmış/bilinmiyor/yanlış anahtar).

→ Ayrıntılar, kurulum ve durum kodu tablosu: Disko Turnikesi.

Uçtan Uca Akış

Tipik bir misafir akışı, tüm adımlar crd_10042 üzerinden:

TOKEN="eyJ..."
API="https://ihre-instanz.dikas.de/api/v1"

# 1. Einlass am Drehkreuz (Kartenscan Eingang)
curl -X POST "$API/disco/enter" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "guestId": "crd_10042", "gender": 1, "enterGroupId": "eg_std" }'

# 2. Status prüfen (z. B. Wiedereinlass nach kurzem Verlassen)
curl "$API/disco/guest/crd_10042/status" -H "Authorization: Bearer $TOKEN"

# 3. Prepaid aufladen
curl -X POST "$API/disco/guest/crd_10042/topup" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "amount": 20.00, "paymentMethod": "Bar" }'

# 4. An der Bar buchen (einzeln oder als Batch)
curl -X POST "$API/disco/book" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "guestId": "crd_10042", "articleId": "art_cola", "articleName": "Cola 0,3l", "count": 2, "price": 3.50 }'

# 5. Laufenden Umsatz abfragen
curl "$API/disco/guest/crd_10042" -H "Authorization: Bearer $TOKEN"

# 6. Tischbestellung auf die Karte buchen
curl -X POST "$API/disco/table/book-on-card" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "tableId": "tbl_12", "cardId": "crd_10042" }'

# 7. Vor dem Ausgang: Checkout-Vorschau
curl "$API/disco/guest/crd_10042/checkout" -H "Authorization: Bearer $TOKEN"

# 8. Verlassen mit Zahlung (falls noch etwas offen ist)
curl -X POST "$API/disco/leave" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "cardId": "crd_10042", "payments": [ { "method": "Cash", "amount": 0 } ] }'

Arıza durumu — kart çıkışta ödeme yapamıyor (ödeme yapmayan misafir):

# Karte stornieren: als Nichtzahler markieren, Verzehr als Schwund verbuchen
curl -X POST "$API/disco/guest/crd_10042/storno" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "reason": "Karte verloren" }'

# Später an der Kasse nachkassiert
curl -X POST "$API/disco/guest/crd_10042/collect-storno" -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "payments": [ { "method": "Cash", "amount": 7.00 } ] }'