Внешняя система лояльности
Страница для разработчиков сервисов лояльности (UDS, Premium Bonus, свои системы баллов), которые хотят работать в заведениях на Mison. Заведение продаёт в Mison, а баллы гостей ведёт ваш сервис: касса показывает баланс из вашей системы, списание баллов резервирует у вас, продажи отправляет вам на начисление.
В отличие от остальных разделов API здесь Mison вызывает вас: вы реализуете у себя пять методов по этому контракту, заведение указывает в кабинете их адрес.
Подключение
Заведение в кабинете: Лояльность → Внешняя система лояльности. Указывает:
- название — его увидит кассир («UDS»);
- адрес API — базовый URL ваших методов, например
https://api.example.com/mison/v1. Mison добавит к нему имя метода:…/mison/v1/customer; - секрет подписи — Mison генерирует его сам, заведение передаёт его вам.
Там же — кнопка проверки (метод ping), поиск гостя по телефону и журнал всех операций с ответами вашего сервиса.
Пока подключение включено, своя лояльность Mison на этом бренде не начисляет и не списывает баллы: нет начисления за чек, бонуса за регистрацию, кэшбэка по QR-коду на чеке.
Запросы
Все методы — POST с JSON-телом на {адрес API}/{метод}:
POST https://api.example.com/mison/v1/hold
Content-Type: application/json
X-Mison-Request-Id: 0b7c6f1e-2d4a-4b8e-9f3a-5c6d7e8f9a0b
X-Mison-Signature: sha256=5d41402abc4b2a76b9719d911017c592...
X-Mison-Signature— HMAC-SHA256 сырого тела запроса секретом подписи, hex, с префиксомsha256=. Проверяйте её на каждом запросе — так же, как подпись вебхуков.X-Mison-Request-Id— uuid операции, он жеoperation_idв теле. Повтор с тем же id не должен списывать или начислять второй раз — отдайте результат первого раза. Mison повторяет запросы при обрыве связи.
Отвечайте JSON. Код ответа решает, что будет дальше:
| Ответ | Для Mison |
|---|---|
2xx | Операция выполнена |
4xx | Отказ по существу: баллов не хватает, гость не найден, операция невозможна. Не повторяется. Текст из message увидит кассир |
5xx, таймаут | Сбой. Синхронные методы — касса покажет «сервис не ответил», очередь — повторит |
{ "message": "Недостаточно баллов: доступно 1 200" }
Таймаут — 5 секунд. customer и hold ждёт кассир у кассы, отвечайте быстро.
Как идёт продажа
- Кассир вводит телефон гостя или код из вашего приложения →
customer. Касса показывает баланс и даёт оплатить баллами не больше него. - Кассир закрывает чек с оплатой баллами →
hold: зарезервируйте сумму. Отказали — чек этой оплатой не закроется, кассир выберет другую. - Чек закрыт и дошёл до облака →
commit: спишите резерв и начислите баллы за покупку. Отправляется очередью: не ждёт кассира и повторяется, пока вы не ответите. - Чек удалили →
cancel. Вернули часть товара →refund.
Оплата баллами в Mison — это оплата чека (как наличные или карта), а не скидка: в отчётах заведения баллы видны отдельным способом оплаты.
ping
Проверка из кабинета: адрес доступен, подпись проверяется.
{ "operation_id": "…", "message": "Проверка подключения Mison" }
Ответ — любой 2xx.
customer
Найти гостя. Приходит телефон (цифры, с кодом страны) или код из вашего приложения — кассир вводит его в то же поле, что и номер карты.
{
"point_id": "c91fdd54-a539-44ab-8a8f-103838d31388",
"phone": "998901234567"
}
{ "point_id": "c91fdd54-…", "code": "482913" }
Ответ:
{
"customer_id": "u-58123",
"name": "Азиз",
"phone": "998901234567",
"balance": 12500,
"level": "Золото",
"can_redeem": true
}
| Поле | Описание |
|---|---|
customer_id | Обязательно. Ваш id гостя — его Mison передаст в hold, commit, refund |
phone | Обязательно при поиске по коду: по телефону Mison находит или заводит своего клиента |
balance | Баланс в баллах; 1 балл = 1 сум оплаты |
can_redeem | false — касса покажет баланс, но оплатить баллами не даст |
level | Уровень гостя — показывается кассиру |
Гость не найден — 404. Отвечайте 200 с новым customer_id, если ваш сервис заводит гостя при первом визите.
hold
Зарезервировать баллы при закрытии чека.
{
"operation_id": "6f1c2d5e-8a7b-4c3d-9e0f-1a2b3c4d5e6f",
"customer_id": "u-58123",
"amount": 5000,
"point_id": "c91fdd54-…",
"cheque": {
"uuid": "a3f9c1e2-…",
"total": 84000,
"lines": [
{ "product_id": 1201, "name": "Плов", "quantity": 2, "price": 35000, "amount": 70000 }
]
}
}
Ответ — id резерва:
{ "hold_id": "h-99812" }
Если правила сервиса не дают списать столько (например, не больше 50 % чека) — 400 с message: кассир увидит его и уменьшит сумму. Кассир может закрыть тот же чек повторно с другой суммой — тогда Mison отменит прежний резерв методом cancel.
commit
Чек закрыт. Спишите резерв и начислите баллы за покупку.
{
"operation_id": "…",
"customer": { "customer_id": "u-58123", "phone": "998901234567" },
"hold_id": "h-99812",
"redeemed": 5000,
"cheque": {
"uuid": "a3f9c1e2-…",
"number": 1452,
"point_id": "c91fdd54-…",
"opened_at": "2026-10-25 19:04:11",
"closed_at": "2026-10-25 20:31:40",
"total": 84000,
"goods_total": 70000,
"discount": 0,
"lines": [
{ "line_id": 501233, "product_id": 1201, "name": "Плов", "quantity": 2, "price": 35000, "amount": 70000 }
],
"payments": [
{ "type": "points", "amount": 5000 },
{ "type": "card", "amount": 79000 }
]
}
}
| Поле | Описание |
|---|---|
redeemed | Сколько баллов списать. Обычно равно резерву; hold_id может быть null, если касса старой версии закрыла чек без резерва — тогда спишите напрямую или откажите 4xx |
cheque.total | Итог к оплате с обслуживанием и доставкой |
cheque.goods_total | Сумма товаров |
cheque.lines | Товары. Обслуживание и доставка строками не передаются — они в total |
cheque.payments | cash, card, points, other. Начисляйте с той части, которую считаете базой — обычно без points |
customer.customer_id | Может быть null, если гостя привязали к чеку после продажи (QR-код на чеке) — ищите по phone |
Ответ — сколько начислено (Mison покажет это в чеке и отчётах):
{ "accrued": 3950, "balance": 11450 }
commit приходит и без оплаты баллами — когда гость просто указан в чеке: тогда redeemed = 0, hold_id = null.
cancel
Отмена ранее выполненной операции: чек удалён после закрытия (отмена commit — верните списанное, снимите начисленное) или резерв больше не нужен (отмена hold).
{
"operation_id": "…",
"ref_operation_id": "6f1c2d5e-…",
"ref_operation": "commit",
"hold_id": "h-99812"
}
ref_operation_id — operation_id отменяемой операции. Ответ — любой 2xx; если её у вас нет, тоже 2xx.
refund
Возврат части или всего чека. Деньги за возвращённые товары заведение отдаёт гостю целиком — включая долю, оплаченную баллами. Поэтому снимите начисление за возвращённое, а списанные баллы не возвращайте.
{
"operation_id": "…",
"ref_operation_id": "…",
"customer": { "customer_id": "u-58123", "phone": "998901234567" },
"amount": 35000,
"lines": [
{ "product_id": 1201, "quantity": 1, "amount": 35000 }
]
}
ref_operation_id — operation_id исходного commit. Ответ — 2xx, по желанию { "revoked": 1975, "balance": 9475 }.
Повторы
commit, cancel и refund Mison повторяет через 1, 5, 30 минут, 2, 6 и 12 часов, пока не получит 2xx или 4xx. После этого операция помечается недоставленной, и заведение может отправить её снова из журнала в кабинете. Дедуплицируйте по operation_id.