☰ Меню · POS и кассы
POS и кассы
Всё, что нужно кассе: найти гостя, провести чек, списать кэшбэк, обработать возврат и выпустить карты с номерами самой кассы.
Обычный сценарий на кассе: кассир сканирует карту гостя в Wallet (или вводит телефон), касса показывает баланс, гость решает, оплатить ли часть чека кэшбэком, чек закрывается, и касса отправляет его нам. Карта на телефоне гостя обновляется за несколько секунд.
1. Найдите гостя
GET /v1/members/{member} принимает то, что есть у кассы: номер карты, содержимое штрихкода, телефон в международном формате или наш id участника. Телефон кодируйте для URL (+ превращается в %2B).
# 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 | Что показать |
|---|---|
| cashback | cashback.balance и максимум к списанию: меньшее из баланса и redeem_max_percent от суммы чека. |
| stamps | stamps.current из stamps.required и stamps.rewards_available, если гостя ждёт бесплатная позиция. |
| visit_discount, vip | discount.percent и discount.tier: эту скидку к чеку касса применяет сама. |
2. Что зашито в штрихкод карты
Формат штрихкода заведение выбирает в разделе Разработчикам → Штрихкод карты, чтобы он подходил сканеру кассы:
| Формат | Содержимое штрихкода | Когда использовать |
|---|---|---|
| QR с номером карты | card_number, например 2000111703779 | Сканер читает QR, а касса ищет по номеру карты. Рекомендуем. |
| Штрихкод (Code 128) с номером карты | Тот же номер в виде линейного штрихкода, напечатан под полосками | Старые лазерные сканеры, которые не читают QR. |
| QR для кассы Loyaltyfy | Наш внутренний серийный номер (UUID) | По умолчанию. Его читает только кассовое приложение Loyaltyfy, но POS всё равно может найти гостя по телефону. |
При смене формата карты, которые уже лежат в Wallet у гостей, обновляются сами, ничего переустанавливать не нужно. В любом формате передавайте отсканированное значение как есть в GET /v1/members/{member} или в POST /v1/scans: этот эндпоинт ещё и убирает префиксы сканера и переводы строк.
3. Выпускайте карты с номерами кассы
Если у кассы уже есть своя система карт, сохраните её номера. Регистрируйте гостя, передавая в card_number номер кассы: карта в Wallet покажет этот номер, а её штрихкод будет его содержать, так что сканер найдёт гостя и в собственной базе кассы. Номера уникальны в пределах заведения, на дубликат придёт 409 card_number_taken. Чтобы позже привязать номер кассы к существующему участнику, вызовите PATCH /v1/members/{member} с card_number.
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 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" }
]
}'{
"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 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
}'{
"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 -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 -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.