☰ Меню · POS і каси
Посібники

POS і каси

Усе, що потрібно касі: знайти гостя, провести чек, списати кешбек, обробити повернення та випустити картки з власними номерами каси.

Звичайний сценарій на касі: касир сканує картку гостя в гаманці (або вводить телефон), каса показує баланс, гість вирішує, чи оплатити частину чека кешбеком, чек закривається, і каса надсилає його нам. Картка в телефоні гостя оновлюється за кілька секунд.

1. Знайдіть гостя

GET /v1/members/{member} приймає те, що є в каси: номер картки, вміст штрихкоду, телефон у міжнародному форматі або наш id учасника. Телефон кодуйте для URL (+ перетворюється на %2B).

curl
# by the number on the card (or the barcode the scanner read)
curl https://loyaltyfy.io/api/v1/members/2000111703779 \
  -H "Authorization: Bearer sk_test_..."

# by phone, with fresh add-to-wallet links
curl "https://loyaltyfy.io/api/v1/members/%2B37366791102?expand=wallet" \
  -H "Authorization: Bearer sk_test_..."

Покажіть касиру з об’єкта учасника те, що важливо для програми:

program.mechanicЩо показати
cashbackcashback.balance і максимальну суму списання: менше з двох значень, балансу та redeem_max_percent від суми чека.
stampsstamps.current з stamps.required, а також stamps.rewards_available, якщо на гостя чекає безкоштовна позиція.
visit_discount, vipdiscount.percent і discount.tier: застосуйте цю знижку до чека на своєму боці.

2. Що містить штрихкод картки

Заклад вибирає формат штрихкоду в розділі Розробникам → Штрихкод картки, щоб він відповідав тому, що читає сканер каси:

ФорматЩо містить штрихкодКоли використовувати
QR з номером карткиcard_number, наприклад 2000111703779Сканер читає QR, а каса шукає за номером картки. Рекомендовано.
Штрихкод (Code 128) з номером карткиТой самий номер у вигляді лінійного штрихкоду, надрукований під смужкамиСтаріші лазерні сканери, які не читають QR.
QR для каси LoyaltyfyНаш внутрішній серійний номер (UUID)За замовчуванням. Його читає лише касовий застосунок Loyaltyfy. POS усе одно може знайти гостя за телефоном.

Після зміни формату картки, які вже є в гаманцях гостей, оновлюються самі, нічого перевстановлювати не потрібно. Незалежно від формату, передавайте відскановане значення як є в GET /v1/members/{member} або в POST /v1/scans. Останній також прибирає префікси сканера та переноси рядків.

3. Випускайте картки з номерами каси

Якщо в каси вже є своя система карток, залиште її номери. Реєструйте гостя з card_number, що дорівнює номеру каси. Картка в гаманці показує цей номер і кодує його в штрихкоді, тож сканер знайде гостя і у власній базі каси. Номери унікальні в межах закладу: дублікат отримає 409 card_number_taken. Щоб пізніше призначити номер каси наявному учаснику, викличте PATCH /v1/members/{member} з card_number.

curl
curl https://loyaltyfy.io/api/v1/members \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 7d1b2c4e-enrol-000123" \
  -d '{
    "phone": "+37366791102",
    "first_name": "Ion",
    "last_name": "Popescu",
    "card_number": "2000111703779",
    "language": "ro",
    "consent": true
  }'

У відповіді є wallet.apple_url і wallet.google_url. Надрукуйте їх як QR на чеку, надішліть SMS або покажіть на дисплеї покупця: гість відкриває посилання, і картка додається в телефон.

4. Проведіть чек

Надсилайте кожен закритий чек один раз, після оплати. amount означає повну суму чека до списання кешбеку, redeem_amount показує, скільки з неї гість оплатив кешбеком. Ми застосовуємо правила програми (мінімальний чек, ліміт списання, рівні) так само, як екран касира в закладі.

curl
curl https://loyaltyfy.io/api/v1/transactions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "CHK-2026-000123",
    "member": "2000111703779",
    "amount": 25000,
    "currency": "MDL",
    "register_id": "POS-1",
    "closed_at": "2026-10-06T18:17:00+03:00",
    "line_items": [
      { "sku": "1001", "name": "Espresso", "quantity": 2, "unit_price": 4000, "category": "Coffee" },
      { "sku": "2005", "name": "Cheesecake", "quantity": 1, "unit_price": 17000, "category": "Desserts" }
    ]
  }'
Відповідь · 201
{
  "object": "transaction",
  "id": "f8acbb16-0b3b-4afc-acbb-e5eabd4dd072",
  "external_id": "CHK-2026-000123",
  "status": "completed",
  "amount": 25000,
  "redeem_amount": 0,
  "cashback_earned": 1250,
  "stamps_earned": 0,
  "reward_unlocked": false,
  "net_amount": 25000,
  "currency": "MDL",
  "register_id": "POS-1",
  "closed_at": "2026-10-06T15:17:00+00:00",
  "livemode": false,
  "created_at": "2026-10-06T18:17:17.921+00:00",
  "voided_at": null,
  "member": {
    "object": "member",
    "id": "6dacfe0a-2500-44c6-a37c-05c660b093b4",
    "card_number": "2000111703779",
    "cashback": { "balance": 1250, "currency": "MDL", "earn_rate": 5, ... },
    ...
  }
}
external_id має бути унікальним серед усіх кас закладу. Якщо номери чеків починаються заново на кожній касі чи в кожній зміні, поєднуйте їх, наприклад POS-3-000123 (каса 3, чек 123).

Кешбек нараховується на суму, яку гість фактично сплатив: amount − redeem_amount. У програмах зі штампами та візитами чек зараховується як візит. Поля stamps_earned і reward_unlocked показують, що сталося, тож каса може написати «ще одна кава, і наступна безкоштовно».

5. Оплата кешбеком

Спершу запитайте баланс, дайте гостю вибрати, а потім надішліть чек із redeem_amount. Якщо баланс тим часом змінився (гість щойно розрахувався деінде), ви отримаєте 402 insufficient_balance з поточною сумою в available. Покажіть її, щоб касир зміг повторити.

curl
curl https://loyaltyfy.io/api/v1/transactions \
  -H "Authorization: Bearer sk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "CHK-2026-000124",
    "member": "2000111703779",
    "amount": 12000,
    "redeem_amount": 1250
  }'
Відповідь · 402
{
  "error": {
    "type": "invalid_request_error",
    "code": "insufficient_balance",
    "message": "redeem_amount is more than the available balance.",
    "param": "redeem_amount",
    "available": 1250
  },
  "request_id": "req_6f4c30b8ca7d4423a9d4"
}
Надавайте знижку кешбеком лише після нашої відповіді 201 або 200. Надсилайте чек, коли сума вже остаточна, але до прийому оплати. Якщо відповіді немає, повторіть запит з тим самим external_id або закрийте чек без кешбеку. Інакше гість отримає і знижку, і збереже баланс.

6. Видача нагороди за штампи

Коли stamps.rewards_available більше нуля, гостю належить безкоштовна позиція. Видайте її на касі та зафіксуйте:

curl
curl -X POST https://loyaltyfy.io/api/v1/members/4285324286429/rewards/redeem \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: reward-4285324286429-0001"

7. Повернення та помилки

Скасуйте весь чек за його external_id. Нараховані кешбек або штампи списуються, а використаний кешбек повертається гостю. Часткове повернення: скасуйте чек і надішліть новий із виправленою сумою та новим external_id.

curl
curl -X POST https://loyaltyfy.io/api/v1/transactions/CHK-2026-000123/void \
  -H "Authorization: Bearer sk_test_..."

8. Коли каса офлайн

Зберігайте чеки в локальній черзі разом з external_id і closed_at та надсилайте їх, коли з’явиться зв’язок. Дублікати ігноруються, тому всю чергу можна спокійно надіслати повторно. Для пошуку гостя потрібна мережа. Якщо її немає, дозвольте касиру провести чек без лояльності, а не блокуйте продаж.

Чеки із сервера каси

Деякі POS-системи не можуть звертатися до API безпосередньо з каси, але вміють надсилати закриті чеки зі свого сервера. Це теж підходить: надсилайте той самий POST /v1/transactions із сервера одразу після закриття чека, з номером картки, який відсканував касир. Якщо каса хоче показати результат пізніше, підпишіться на transaction.completed.

Питання щодо інтеграції: api@loyaltyfy.io. Відповідаємо протягом одного робочого дня.