☰ Meniu · Regulile localului și limite
Ghiduri

Regulile localului și limite

Cum controlează localul ce pot face casele conectate: niveluri de cashback, categorii de produse, rezervări, numerotarea bonurilor, bonuri trimise mai târziu, limite și verificarea scanării.

Tot ce este pe această pagină se setează de proprietarul localului în Dezvoltatori → Reguli pentru case. Regulile se aplică tuturor cheilor API ale localului; o singură cheie (o casă, un chioșc) poate avea valori proprii pentru oricare dintre ele. Integrarea ta citește regulile după care lucrează din GET /v1/account, în settings.

JSON
"settings": {
  "redemption": "hold",
  "hold_minutes": 30,
  "check_numbering": "per_register",
  "offline_max_hours": 72,
  "redeem_max_per_check": 50000,
  "redeem_max_per_day": null,
  "require_scan": true,
  "scan_window_minutes": 30,
  "allow_adjustments": false,
  "adjustment_max": 50000
}

Niveluri de cashback

Un program de cashback poate avea niveluri: de exemplu De bază 5%, Silver 7% de la a doua vizită, Gold 10% de la 1000,00 cheltuiți. Nivelurile se calculează după vizite, după suma cheltuită sau după ce se atinge primul. Proprietarul le setează în programul de fidelitate. Pe partea casei nu trebuie făcut nimic: fiecare bon acumulează la nivelul pe care oaspetele îl are după acel bon, iar fiecare obiect member îți arată unde se află oaspetele.

JSON
"cashback": {
  "balance": 10200,
  "held": 0,
  "available": 10200,
  "currency": "MDL",
  "earn_rate": 7,
  "level": "Silver",
  "level_basis": "any",
  "levels": [
    { "name": "Base", "earn_rate": 5, "min_visits": null, "min_spent": null },
    { "name": "Silver", "earn_rate": 7, "min_visits": 2, "min_spent": null },
    { "name": "Gold", "earn_rate": 10, "min_visits": null, "min_spent": 100000 }
  ],
  "next_level": { "name": "Gold", "earn_rate": 10, "visits_left": null, "spent_left": 80000 },
  ...
}

next_level.visits_left și next_level.spent_left pot fi afișate direct oaspetelui ("încă 2 vizite până la Silver"). display.receipt_lines conține deja acest rând în limba oaspetelui.

Categorii de produse

Încarcă o dată produsele cu categoria lor, apoi trimite doar modificările. Categoriile apar în panoul proprietarului, unde fiecare poate fi setată să nu acorde cashback (tutun, alcool), să nu poată fi plătită cu cashback (produse deja la promoție) sau să acorde un procent propriu în locul nivelului oaspetelui (cafea 10%). API-ul poate crea și redenumi produse și categorii, dar numai proprietarul schimbă aceste reguli.

curl
curl https://loyaltyfy.io/api/v1/catalog/products/bulk \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
      { "sku": "4840001", "name": "Marlboro Gold", "category": "TOB", "category_name": "Tobacco", "price": 6500 },
      { "sku": "1001", "name": "Latte", "category": "COF", "category_name": "Coffee", "price": 4500 },
      { "sku": "2040", "name": "Zeama", "category": "KIT", "category_name": "Kitchen", "price": 6500 }
    ]
  }'

# { "object": "catalog_sync", "upserted": 3 }

Apoi trimite line_items cu fiecare bon. O linie poate avea category sau doar sku: căutăm categoria în catalog. Totalurile liniilor sunt scalate la suma bonului, așa că reducerile făcute pe casă se distribuie corect. Liniile din categorii fără reguli acumulează la nivelul oaspetelui.

curl
curl https://loyaltyfy.io/api/v1/transactions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "CHK-2026-000130",
    "member": "2000111703779",
    "amount": 17500,
    "line_items": [
      { "sku": "4840001", "quantity": 1, "unit_price": 6500 },
      { "sku": "1001", "quantity": 1, "unit_price": 4500 },
      { "sku": "2040", "quantity": 1, "unit_price": 6500 }
    ]
  }'

# Tobacco earns nothing, Coffee has its own 10%, Kitchen earns at the guest's level (5%):
# 0 + 450 + 325 = "cashback_earned": 775

Plata cu cashback prin rezervare

Cu În bon (implicit), casa trimite redeem_amount împreună cu bonul închis. Cu Prin rezervare, casa pune suma deoparte când oaspetele cere să plătească cu cashback, iar bonul închis o confirmă. Dacă bonul nu mai vine (a căzut rețeaua, vânzarea a fost anulată), rezervarea se eliberează după timpul stabilit de proprietar, iar banii rămân la oaspete. Nicio reducere nu se acordă fără să fie scăzută din sold.

  1. 1
    Rezervă

    POST /v1/holds cu membrul, suma și totalul bonului. Se aplică aceleași plafoane ca la un bon. Răspunsul conține id-ul rezervării.

  2. 2
    Confirmă

    POST /v1/transactions cu hold_id. Omite redeem_amount ca să folosești toată rezervarea sau trimite o sumă mai mică.

  3. 3
    Sau eliberează

    POST /v1/holds/{id}/release când vânzarea este anulată. O eliberare repetată nu are niciun efect.

curl
curl https://loyaltyfy.io/api/v1/holds \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: hold-R3-000131" \
  -d '{ "member": "2000111703779", "amount": 3000, "check_amount": 20000, "register_id": "R3" }'

# at payment: the check captures it
curl https://loyaltyfy.io/api/v1/transactions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "external_id": "000131", "register_id": "R3", "member": "2000111703779",
        "amount": 20000, "hold_id": "5b0d6c1e-8f3a-4d7e-9a61-2c4b8e0f7d19" }'
Rezervările contează peste tot: în cashback.available al membrului, în limite și în aplicația de casă a localului, așa că aceiași bani nu pot fi cheltuiți de două ori.

Numerele bonurilor pe fiecare casă

Dacă fiecare casă numără bonurile de la 1, alege Fiecare casă are numerotarea ei. Atunci register_id devine obligatoriu, iar bonul este salvat ca <register_id>:<external_id>, de exemplu R3:15. Folosește acest id combinat ca să citești, să anulezi sau să returnezi bonul.

Bonuri trimise mai târziu

După o întrerupere, casa își trimite coada. Cu o limită de, să zicem, 72 de ore, bonurile al căror closed_at este mai vechi sunt refuzate cu check_too_old, așa că o casă restaurată dintr-o copie de rezervă veche nu poate umple localul cu bonuri expirate. Un closed_at cu mai mult de 10 minute în viitor este refuzat întotdeauna.

Limite de folosire

Pe lângă partea din bon stabilită de program, proprietarul poate seta suma maximă de cashback pe bon și pe oaspete pe zi (toate casele împreună, după fusul orar al localului). Un bon care cere mai mult folosește cât este permis, iar răspunsul arată redeem_amount real; o rezervare care cere mai mult este refuzată cu limited_by. Folosește calculul ca să-i arăți mai întâi oaspetelui suma exactă.

Cardul trebuie scanat

Cu această regulă, un bon, o vizită, o rezervare sau o recompensă pentru un card sunt acceptate doar dacă aceeași cheie a trimis cardul la POST /v1/scans cu puțin înainte. Un număr de card tastat de mână pe casă este refuzat cu scan_required. Trimite la /v1/scans ce a produs scanerul, fără modificări, apoi continuă ca de obicei.

Corecții manuale

Dezactivate implicit. Când proprietarul le permite, o cheie cu permisiunea members:adjust poate adăuga sau scădea cashback cu un motiv, până la limita stabilită de proprietar pentru o corecție. Fiecare corecție apare în istoricul membrului și trimite balance.adjusted.

curl
curl https://loyaltyfy.io/api/v1/members/2000111703779/adjustments \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{ "external_id": "ADJ-2026-0009", "amount": 2000, "reason": "Goodwill after a complaint" }'

# a negative amount takes cashback away
Întrebări despre integrare: api@loyaltyfy.io. Răspundem în cel mult o zi lucrătoare.