POS API · касса әзірлеушілеріне

Касса жүйелеріне арналған ашық API

Мекеменің кассасы Unemi клиентін өзі таниды, оның бонустары мен сыйлықтарын чекке қолданады және төлемнен кейін соманы Unemi-ге жібереді — мөртаңба, бонус немесе абонемент Unemi Staff кассасындағыдай өтеді. Касса жүйелерін әзірлеушілерге және өз кассасы бар мекемелерге арналған.

Қалай қосуға болады

  1. Pro тарифі

    Ашық API Pro тарифінде жұмыс істейді. Басқа тарифтерде кілт жасалмайды, ал бұрын жасалған кілтпен келген сұрауларға бас тартылады.

  2. Кабинеттегі кілт

    Иесі кабинетте «API и интеграции» (API және интеграциялар) бөлімін ашып, кілт жасайды: атауы — мысалы, касса не нүкте бойынша — және құқықтары.

  3. Құқықтар: нүкте немесе бүкіл мекеме

    Нүкте кілті тек сол нүктеде жұмыс істейді. Бүкіл мекемеге арналған кілт клиентті танығанда және чекті жойғанда нүктені көрсетеді — нүктелердің id-і сол бөлімде.

  4. Кілт бір рет көрсетіледі

    Оны сервердегі касса баптауларына көшіріңіз. Unemi тек хэшін сақтайды: жоғалған кілт қалпына келмейді — жаңасын жасап, ескісінің күшін жойыңыз.

Касса Unemi-мен қалай жұмыс істейді

Әр чекке төрт қадам. Клиенттің келісімі — әр чекке: оның телефонындағы QR не код бір чекке арналған сессияны ашады.

  1. Клиент QR көрсетеді

    Немесе оның астындағы алты санды айтады. Касса клиентті танып, оның баланстарын, сыйлықтарын, купондарын және жүріп жатқан акцияларды алады.

    posIdentify
  2. Кассир жеңілдікті қолданады

    Бонустарды есептен шығаруды, сыйлықты не купонды кассир өз кассасында кәдімгі жеңілдік сияқты өткізеді.

  3. Чек жабылды — сома Unemi-ге

    Касса чек нөмірін, бағдарламаны, соманы, есептен шығаруды және өтелген сыйлықтарды, қажет болса — кассир мен чектегі тауарларды да жібереді. Клиентке Unemi Staff кассасынан кейінгідей пуш келеді.

    posApply
  4. Болдырмау мен қайтару — 48 сағат ішінде

    Чек жойылса не қайтарылса, касса оны Unemi-де де чек нөмірі бойынша жояды: бәрі бұрынғы күйіне оралады. Ішінара қайтаруды касса сомасымен жібереді — Unemi оны мекеме иесіне көрсетеді.

    posUndo

Лимиттер

  • Бір кілтке минутына 120 сұрау
  • Тану сессиясы бір чекті өткізеді және 2 сағат жарамды, касса кілтінде — 12 сағат
  • Чекті 48 сағат ішінде жоюға не қайтаруға болады
  • Бір чекте — бір бағдарлама және 5-ке дейін сыйлық пен купон
  • 15 минуттағы тану қателері: 5-тен кейін — 1 минут үзіліс, 10-нан кейін — 5 минут, 15-тен кейін — 30 минут
  • Бір мекемеге 10-ға дейін белсенді кілт

Әдістер анықтамалығы

Анықтамалық орыс тілінде: Қазақстандағы касса әзірлеушілері оны осы тілде оқиды. Әдістердің, өрістердің және қате кодтарының атаулары кез келген тілде бірдей. Анықтамалық ағылшын тілінде

Общие правила

ПараметрЗначение
Адрес https://api.unemi.kz/<метод>
Методы posIdentify, posApply, posUndo — только POST
Заголовки Authorization: Bearer unemi_<id>_<секрет>, Content-Type: application/json
Тело JSON-объект; ответ — JSON
Время ISO 8601 в UTC: "2026-10-07T09:30:00.000Z"
Деньги и бонусы Целые тенге; 1 бонус = 1 ₸
Id запроса Заголовок ответа X-Unemi-Request-Id (и requestId в ошибке) — его просят у поддержки
Точка Ключ одной точки работает только на ней; ключ на всё заведение передаёт venueId в posIdentify и posUndo, а posApply берёт точку из сессии
Ключ кассы Ключ одной точки с кассовой системой и правилами точки: сессия 12 часов и программа по умолчанию — раздел «Ключ кассы»
Лимит 120 запросов в минуту на ключ
CORS Нет: ключ живёт на сервере кассы, не в браузере
Тексты Названия программ, наград, купонов и акций — как их записало заведение в кабинете; названия уровней и message в ошибках — по-русски

Сессия и согласие клиента

  • Согласие клиента — на каждый чек: QR или шесть цифр с экрана клиента открывают сессию, она проводит один чек и живёт 2 часа, у ключа кассы — 12 часов. Следующий чек — новый QR.
  • QR и код одноразовые: posIdentify гасит их, как скан кассира. Повтор того же запроса тем же ключом (ответ не дошёл) отдаёт ту же сессию — пока по ней не проведён чек и она не истекла; кассе Unemi и другому ключу — code_used.
  • У ключа кассы тот же код или номер телефона в пределах 12 часов отдаёт ту же открытую сессию и после того, как сам код устарел (живёт около минуты): касса спрашивает карту клиента несколько раз за заказ. Свежий код другого клиента с теми же цифрами важнее — он гасится и открывает новую сессию.
  • Чек отменили (posUndo) — согласие на него не израсходовано: та же сессия, пока жива, проводит его заново.
  • По номеру телефона находятся только клиенты этого заведения (у них уже есть карта, бонусный счёт или абонемент), а сессия — только для начислений: accrualOnly: true, штампы и бонусы без списания.
  • Номер ничем не подтверждён: списать бонусы, оформить или выдать абонемент, погасить награду можно только по QR или коду с телефона клиента — иначе consent_required. Нового клиента по номеру API не заводит.

Повторы и идемпотентность

  • Ответ не дошёл — повторите тот же запрос: второй раз операция не проводится.
  • posIdentify с тем же QR или кодом от того же ключа отдаёт ту же сессию, пока по ней не проведён чек и она не истекла.
  • posApply с тем же receiptId и теми же данными отдаёт сохранённый ответ с replayed: true — без второй операции и второго пуша клиенту. Тот же receiptId с другими данными или по другой сессии — receipt_conflict. Кассир и позиции (cashier, items) — справка: в сравнение не входят.
  • receiptId уникален в пределах точки: если на точке несколько касс, их номера чеков не должны совпадать — например, с префиксом кассы. После отмены (posUndo) тот же receiptId можно провести заново.
  • Повтор posUndo отдаёт тот же ответ с replayed: true.
  • rate_limited и too_many_attempts — подождите details.seconds секунд; internal — повторите позже.

Ответ с ошибкой

Ошибка — HTTP-статус и тело:

{
  "error": {
    "code": "cooldown",
    "message": "Слишком рано: следующий штамп можно через 12 мин",
    "details": { "minutes": 12 },
    "requestId": "Qk2v9XyZaB7cD1eF3gH5"
  }
}

Касса смотрит на error.code — коды стабильны (таблицы ниже). message — по-русски, как в кассе Unemi; details — числа к коду (минуты, лимит, баланс).

posIdentify — узнать клиента

POST https://api.unemi.kz/posIdentify

QR из приложения Unemi, шесть цифр под ним или номер телефона → сессия на один чек, клиент, балансы, награды и купоны, идущие акции.

Запрос

ПолеОписание
qr Строка QR-кода с экрана клиента: unemi:1:st:… — или шесть цифр цифрового QR (см. «Ключ кассы»)
code Шесть цифр под QR — когда сканера нет
phone Казахстанский номер клиента — только начисления (см. «Сессия и согласие клиента»)
venueId Id точки — для ключа на всё заведение; у ключа точки необязателен

Ровно одно из qr, code, phone.

По QR
{ "qr": "unemi:1:st:Xk2pQ9LmNb7Tz1Rw4Ycd", "venueId": "v8Kp2LmQx1" }
По шести цифрам
{ "code": "482913" }
По номеру телефона
{ "phone": "+7 701 123 45 67" }

Ответ

Ответ
{
  "session": "Gm3kQ8rT2vXw9LpZa1Yc",
  "expiresAt": "2026-10-07T11:30:00.000Z",
  "identifiedBy": "qr",
  "accrualOnly": false,
  "customer": {
    "id": "cus_4f1c9a0b2d7e8f3a6b5c1d2e",
    "name": "Айгерим"
  },
  "level": {
    "level": 3,
    "name": "Друг",
    "metric": "visits",
    "value": 18
  },
  "selectedProgramId": "p8Fh2kLm",
  "programs": [
    {
      "id": "p8Fh2kLm",
      "type": "stamps",
      "name": "Штамп-карта",
      "locked": false,
      "minLevel": 1,
      "stamps": {
        "count": 3,
        "required": 6,
        "rewardTitle": "Подарок",
        "rewardPercent": 100,
        "steps": [
          { "stamp": 4, "kind": "discount", "percent": 20, "title": "" }
        ]
      }
    },
    {
      "id": "q2Nw7xRt",
      "type": "bonus",
      "name": "Бонусная карта",
      "locked": false,
      "minLevel": 1,
      "bonus": {
        "balance": 1250,
        "percent": 5,
        "percentBp": 500,
        "levelPercent": 7,
        "levelBp": 700,
        "welcomeBonusPending": false
      }
    },
    {
      "id": "s5Kd1vBn",
      "type": "subscription",
      "name": "Абонемент",
      "locked": false,
      "minLevel": 1,
      "subscription": {
        "item": "Позиция дня",
        "price": 15000,
        "durationDays": 30,
        "lifetime": false,
        "perDay": 1,
        "pass": {
          "expiresAt": "2026-10-20T07:00:00.000Z",
          "expired": false,
          "leftToday": 1,
          "lifetime": false
        }
      }
    }
  ],
  "rewards": [
    {
      "id": "rW7yT2pQ",
      "kind": "reward",
      "title": "Подарок",
      "programId": "p8Fh2kLm",
      "percent": 100,
      "item": null,
      "expiresAt": "2026-10-30T09:00:00.000Z"
    },
    {
      "id": "cP3mN8sD",
      "kind": "coupon",
      "title": "−20% на заказ",
      "programId": null,
      "percent": 20,
      "item": null,
      "expiresAt": "2026-10-08T18:00:00.000Z"
    }
  ],
  "promos": [
    {
      "title": "Акции по расписанию",
      "description": "Бонусы ×2",
      "until": "2026-10-07T11:00:00.000Z",
      "effect": { "kind": "bonus_multiplier", "value": 2 }
    }
  ]
}
ПолеОписание
session, expiresAt Сессия для posApply и до какого времени она действует — 2 часа, у ключа кассы — 12 часов.
identifiedBy, accrualOnly Как узнали клиента: qr (и цифровой QR), code или phone. accrualOnly: true — по номеру: только начисления.
customer id — постоянный id клиента для этого заведения (в другом заведении у того же человека другой); name — имя, как его видит кассир Unemi Staff. Телефона, почты и внутреннего id Unemi касса не получает.
level Уровень клиента, если у заведения включены уровни; иначе null. value — сколько набрано за всё время: дни с визитами (metric: "visits") или сумма покупок в тенге ("spend"); заработанный уровень не понижается.
stamps.steps Награды на промежуточных штампах карты по возрастанию номера: stamp — номер штампа (от 1 до required; на последнем штампе гость получает и награду шага, и главную награду rewardTitle), kind — discount (скидка percent%) или item (товар в подарок, percent — 100), title — название, пусто — назовите по скидке. Нет наград — пустой список.
locked, minLevel locked: true — программа «с уровня», а уровень клиента ниже minLevel: операция по ней ответит level_required (кроме выдачи по уже оплаченному абонементу).
bonus.balance Сколько бонусов можно списать. welcomeBonusPending: true — первый визит: приветственные бонусы начислятся этой же операцией, и их уже можно списать.
bonus.percent, bonus.levelPercent percent — процент программы. levelPercent — процент этого клиента по его уровню, если заведение задало проценты по уровням и он выше percent: по нему posApply и начислит; иначе null.
bonus.percentBp, bonus.levelBp Те же проценты точно, в базисных пунктах: 250 — 2,5%, 10 000 — 100%. Процент бывает дробным (до двух знаков после запятой, от 0 до 100); percent и levelPercent — целые, округлены половиной вверх (2,5% → 3), для касс, подключённых раньше. «Начислится» — по bp: floor((amount − spend) × bp / 10000).
subscription.pass Абонемент клиента; null — не оформлен.
subscription.lifetime Бессрочная программа: durationDays у неё 36 500. pass.lifetime — бессрочный абонемент клиента: дата окончания через сто лет, продлевать его не нужно (pass_lifetime_active).
selectedProgramId Программу клиент выбрал в приложении перед показом QR; null — выбирает кассир.
rewards Непогашенные награды штамп-карт и купоны рассылок в сроке: percent — скидка (100 — бесплатно), item — товар купона.
promos Акции по расписанию на точке сейчас. Эффект (bonus_multiplier, bonus_extra_percent, double_stamps) сервер применит сам при posApply; без эффекта (effect: null) — напоминание кассиру провести скидку.
defaultProgram Только у ключа кассы: по какой программе пройдёт чек без programId и action — { "programId": "q2Nw7xRt", "action": "bonus" }: выбранная клиентом в приложении штамп-карта или бонусная карта, иначе программа по умолчанию ключа; null — ни того, ни другого. У обычного ключа поля нет.

posApply — провести закрытый чек

POST https://api.unemi.kz/posApply

Чек закрыт и оплачен → операция по программе и погашение наград. Операция, погашения наград, визит для уровней, подарки «Приведи друга» и подарки за новый уровень — одной транзакцией: либо всё, либо ничего.

Запрос

ПолеОписание
session Из ответа posIdentify.
receiptId Id чека в кассе: 1–64 печатных знака ASCII без пробелов или целое неотрицательное число. Ключ идемпотентности — в пределах точки.
programId, action Операция по программе — парой (действия ниже). Без пары — только погашение наград, а у ключа кассы чек с amount — по программе по умолчанию.
amount Сумма чека, ₸. Для bonus обязательна (1 … 10 000 000); у остальных необязательна (целое 0 … 10 000 000) и хранится для разборов.
spend Сколько бонусов списано в этом чеке; только для bonus (у ключа кассы без программы — тоже, тогда программа по умолчанию должна быть бонусной картой), не больше amount и баланса.
rewardIds До 5 наград и купонов из posIdentify, применённых к чеку.
cashier Необязательно: кто провёл чек — { "id": "1234", "name": "Вдовин Б.А." }; id — код сотрудника в кассе (до 64 знаков без пробелов), имя — до 100 знаков. Хранится в чеке для разборов и антифрода.
items Необязательно: до 100 позиций — { "code": "7644", "name": "Позиция дня", "qty": 2, "amount": 2400 }; код товара кассы — до 40 знаков, количество больше нуля (весовой товар — дробное), сумма после скидок — целые тенге. Нужны правилу ключа кассы «штамп только за товары».

В одном чеке — одна программа и до 5 наград и купонов.

Чек по бонусной карте: списание и купон
{
  "session": "Gm3kQ8rT2vXw9LpZa1Yc",
  "receiptId": "A-1024",
  "programId": "q2Nw7xRt",
  "action": "bonus",
  "amount": 4500,
  "spend": 500,
  "rewardIds": ["cP3mN8sD"]
}

Действия по программам

actionПрограммаЧто делает
stamp Штамп-карта Штамп на карту клиента
bonus Бонусная карта Бонусы с оплаченного деньгами и списание spend
pass_purchase Абонемент Оформить или продлить абонемент
pass_issue Абонемент Выдать по абонементу

Бонусы начисляются с оплаченного деньгами (amount − spend) по проценту программы — вниз до целого бонуса, floor((amount − spend) × bp / 10000), — а клиенту, чей уровень даёт больше, — по проценту его уровня до этого чека (bonus.levelBp из posIdentify); с эффектом акций по расписанию, если он идёт. Прибавки уровня и акции складываются, а не умножаются, и вместе не больше оплаченного.

Ключ кассы без программы. Чек с amount, но без пары programId и action проходит по программе по умолчанию: штамп-карта или бонусная карта, которую клиент выбрал в приложении перед показом QR, иначе программа и действие из правил ключа.

Пороги правил: оплачено деньгами (amount − spend) меньше порога — без начисления; у штампа программы по умолчанию с кодами товаров нужна хотя бы одна позиция с таким кодом (без items — штампа нет). Списание бонусов порог не останавливает, а без бонусной программы оно невозможно — action_mismatch.

Не начислено — ответ всё равно успешный: operation: null и причина в skipped; чек записан, сессия закрыта. У обычного ключа поля skipped нет, а чек без пары и без наград — invalid_request.

Ответ

Ответ
{
  "receiptId": "A-1024",
  "operationId": "bonus:Zx81LmQa0pTt7uVv3wYb",
  "operation": {
    "type": "bonus",
    "programId": "q2Nw7xRt",
    "programName": "Бонусная карта",
    "amount": 4500,
    "spent": 500,
    "earned": 480,
    "welcome": 0,
    "balance": 1230,
    "promo": {
      "title": "Акции по расписанию",
      "kind": "bonus_multiplier",
      "value": 2,
      "extra": 200
    },
    "levelPercent": 7,
    "levelBp": 700,
    "levelExtra": 80
  },
  "redeemed": [
    { "id": "cP3mN8sD", "kind": "coupon", "title": "−20% на заказ" }
  ],
  "referral": null,
  "levelUp": null,
  "replayed": false
}
operation штампа
{
  "type": "stamp",
  "programId": "p8Fh2kLm",
  "programName": "Штамп-карта",
  "stamps": 4,
  "required": 6,
  "added": 1,
  "rewardIssued": false,
  "rewardTitle": null,
  "stepRewards": [{ "stamp": 4, "title": "Скидка 20%", "percent": 20 }],
  "promo": null
}
operation оформления абонемента
{
  "type": "pass_purchase",
  "programId": "s5Kd1vBn",
  "programName": "Абонемент",
  "item": "Позиция дня",
  "price": 15000,
  "expiresAt": "2026-11-19T07:00:00.000Z",
  "renewed": true,
  "lifetime": false
}
operation выдачи по абонементу
{
  "type": "pass_issue",
  "programId": "s5Kd1vBn",
  "programName": "Абонемент",
  "item": "Позиция дня",
  "leftToday": 0,
  "perDay": 1,
  "expiresAt": "2026-10-20T07:00:00.000Z",
  "lifetime": false
}
levelUp с подарками: крупная покупка подняла клиента сразу через уровень (уровни по сумме покупок)
{
  "from": 1,
  "to": 3,
  "name": "Друг",
  "gifts": [
    {
      "level": 2,
      "kind": "coupon",
      "rewardId": "cL4vR9kTq2",
      "title": "−15% на заказ",
      "percent": 15,
      "item": null,
      "expiresAt": "2026-11-05T18:59:59.000Z"
    },
    {
      "level": 3,
      "kind": "bonus",
      "programId": "q2Nw7xRt",
      "programName": "Бонусная карта",
      "amount": 500
    }
  ]
}
ПолеОписание
operationId, operation Проведённая операция; null — в чеке только погашение наград или, у ключа кассы, начислять было нечего (см. skipped).
promo Акции по расписанию, сработавшие в операции: extra — сколько бонусов или штампов добавила акция.
levelPercent, levelBp, levelExtra У бонусной операции: процент уровня клиента, если начислено по нему (levelBp — точно, в базисных пунктах; levelPercent — целым), и сколько бонусов он добавил сверх процента программы (earned — уже с ними); иначе null, null и 0. В примере: Друг — 7% вместо 5% и «бонусы ×2» с 4 000 ₸: 280 по уровню + 200 от акции = 480, а не 560.
rewardIssued true — штамп закрыл карту, у клиента новая награда (rewardTitle); погасить её можно следующим чеком.
stepRewards Награды промежуточных штампов, которые выдал этот чек: номер штампа stamp, название title (без названия — «Скидка 20%» или «Подарок»), скидка percent (100 — товар в подарок). Обычно пусто или одна. Это обычные награды: придут в rewards при узнавании, погасить можно следующим чеком.
redeemed Погашенные награды и купоны из rewardIds.
referral Первый визит приглашённого друга: { "gift": { "kind": "bonus", "amount": 300, "programName": "Бонусная карта" } }; иначе null.
levelUp Клиент поднялся на уровень: from, to, name и gifts — подарки за новый уровень, выданные этим чеком (пусто — подарков нет; через уровень — за каждую ступень); иначе null. Купон: rewardId — id награды (придёт и в rewards при узнавании, погасить можно следующим чеком), title, скидка percent или товар item, срок expiresAt. Бонусы: programId, programName, amount — уже на счёте клиента (если на карте этого чека — и в operation.balance).
skipped Только у ключа кассы, в чеке без программы: почему ничего не начислено — no_program (у ключа нет программы по умолчанию, клиент её не выбрал), below_minimum (оплачено меньше порога), no_qualifying_items (в чеке нет товаров из правила); null — начислено.
replayed true — повтор чека: ответ сохранённый, второй операции не было.

posUndo — отменить чек

POST https://api.unemi.kz/posUndo

Отмена возвращает всё как было до чека: штамп, бонусы, абонемент, погашенные награды и купоны, визит уровня, подарки «Приведи друга» и подарки за новый уровень (купон-подарок, который клиент уже погасил, остаётся у него; потраченные бонусы подарка снимаются, сколько осталось на счёте). Можно в пределах 48 часов после чека: сторно и возврат бывают и в конце смены, и на следующий день.

Запрос

ПолеОписание
receiptId Id чека, проведённого posApply на этой точке.
venueId Только для ключа на всё заведение.
amount Необязательно: сумма возврата, ₸ (целое 0 … 10 000 000). Меньше суммы чека — частичный возврат; без неё или с полной суммой — отмена чека целиком.
Отмена чека
{ "receiptId": "A-1024", "venueId": "v8Kp2LmQx1" }

Повтор отмены отдаёт тот же ответ с replayed: true. Сессия чека снова открыта, пока жива: тот же чек можно провести заново той же сессией.

Отменить нельзя, если после чека карта клиента уже изменилась (undo_not_last), начисленные бонусы потрачены (undo_bonus_spent), награду за этот штамп уже выдали (undo_reward_redeemed) или по оформленному абонементу уже выдавали (undo_pass_used).

Частичный возврат (amount меньше суммы чека): операция остаётся, ответ успешный — partial: true, ничего не отменено.

Частичный возврат и отказы undo_too_late, undo_not_last, undo_bonus_spent, undo_reward_redeemed, undo_pass_used, operation_not_found записываются владельцу в журнал возвратов кабинета, а если он включил «Подозрительные операции» — ещё и сигналом в утренней сводке. Возврат на кассе по ответу posUndo не блокируйте: код отказа значит, что Unemi не отменил начисление, а решает владелец. Удавшаяся отмена снимает запись из журнала.

Ответ

Ответ
{
  "receiptId": "A-1024",
  "undone": ["bonus:Zx81LmQa0pTt7uVv3wYb", "redeem:cP3mN8sD"],
  "undoneAt": "2026-10-07T09:41:12.000Z",
  "replayed": false
}
Частичный возврат
{
  "receiptId": "A-1024",
  "undone": [],
  "undoneAt": null,
  "partial": true,
  "replayed": false
}
ПолеОписание
undone Что отменено: id операции и погашений (redeem:<id награды>); пусто — частичный возврат.
undoneAt Время отмены; null — частичный возврат.
partial true — частичный возврат: ничего не отменено, запись владельцу. При отмене целиком поля нет.
replayed true — повтор отмены: ответ сохранённый.

Коды ошибок

Доступ и запрос

КодHTTPКогдаdetails
unauthorized 401 Нет ключа, ключ не похож на ключ Unemi или не подходит
key_revoked 401 Ключ отозван в кабинете
plan_feature 403 Заведение не на Pro (или оплата просрочена больше 7 дней) plan, feature
method_not_allowed 405 Не POST
invalid_request 400 Тело не JSON-объект или поле не того вида field
rate_limited 429 Больше 120 запросов в минуту на ключ или 30 неудачных входов с одного адреса за минуту seconds, limit
venue_required 400 Ключ на всё заведение, а venueId нет field
venue_forbidden 403 Ключ одной точки, а запрос про другую
venue_not_found 404 Точки нет, она чужая или выключена
internal 500 Сбой на стороне Unemi — повторите позже

Узнавание (posIdentify)

КодHTTPКогдаdetails
unknown_qr 400 Не QR Unemi и не шесть цифр
reward_qr 400 Показан QR награды — нужен QR карты; награды приходят в rewards
invalid_phone 400 Номер не казахстанский
code_not_found 404 Кода нет (опечатка или чужой QR)
code_used 409 Код уже погашен
code_expired 409 Код устарел (живёт около минуты) — клиент обновит экран
customer_not_found 404 По номеру не нашёлся клиент заведения
customer_unknown 400 В коде нет клиента
too_many_attempts 429 Пауза после промахов узнавания seconds

Чек (posApply)

КодHTTPКогдаdetails
session_not_found 404 Сессии нет, она чужая или с точки, недоступной ключу
session_expired 409 Сессии больше 2 часов (у ключа кассы — 12)
session_used 409 По сессии уже проведён другой чек
receipt_conflict 409 Этот чек уже проведён с другими данными operationId
program_not_found 404 Программы нет или она чужая
program_paused 409 Программа выключена
action_mismatch 409 Действие не подходит типу программы; у ключа кассы — списание в чеке без бонусной программы по умолчанию type
level_required 409 Программа с уровня, уровень клиента ниже minLevel, level
cooldown 409 Штамп этой карте на точке был недавно minutes
daily_limit 409 Дневной лимит штампов limit
invalid_amount 400 Сумма или списание не целое в границах max
bonus_insufficient 409 Списание больше баланса balance
pass_not_found 409 Выдача без абонемента
pass_expired 409 Абонемент истёк expiredAtMillis
pass_day_limit 409 Сегодня по абонементу уже выдано limit
pass_lifetime_active 409 У клиента действует бессрочный абонемент — оформлять его снова не нужно
reward_not_found 404 Награды нет или она другого клиента
reward_other_business 403 Награда другого заведения
reward_already_redeemed 409 Награда уже погашена
reward_expired 409 Срок награды вышел
coupon_exhausted 409 Купон уже использовали первые N limit

Отмена (posUndo)

КодHTTPКогдаdetails
receipt_not_found 404 Чек через API на этой точке не проводился
invalid_amount 400 Сумма возврата не целое в границах max
undo_too_late 409 Прошло больше 48 часов (minutes — длина окна отмены, 2880) minutes
undo_already 409 Операцию уже отменили на кассе или в кабинете
undo_not_last 409 После чека карта клиента изменилась
undo_reward_redeemed 409 Награду за этот штамп уже выдали
undo_bonus_spent 409 Начисленные бонусы уже потрачены balance
undo_pass_used 409 По абонементу уже выдавали
operation_not_found 404 Операции чека больше нет (награду удалили)

Безопасность и данные

  • Ключ — unemi_<id ключа, 20 знаков>_<секрет, 48 hex>, 192 случайных бита секрета. Unemi хранит только sha256 секрета и последние четыре знака для узнавания в списке; неверный, кривой и несуществующий ключ — один ответ unauthorized.
  • Чужое заведение недоступно: ключ привязан к заведению, сессия и чек ищутся только в нём; ключ точки видит только свою точку. Чужие сессия, программа, награда и чек отвечают «не найдено».
  • Персональные данные: касса получает имя клиента (как кассир Unemi Staff), балансы, награды и уровень. Телефона, почты и внутреннего id Unemi в ответах нет; id клиента — отдельный псевдоним для каждого заведения.
  • Перебор кодов и номеров: промахи узнавания (code_not_found, customer_not_found) копятся на ключ 15 минут; после 5 — пауза 1 минута, после 10 — 5 минут, после 15 — 30 минут (too_many_attempts). Проводить и отменять чеки пауза не мешает.
  • Журнал запросов в кабинете — без данных клиента, хранится 30 дней.
  • Песочницы нет: интеграцию проверяют на тестовом заведении на тарифе Pro.

Ключ кассы

  • Ключ кассы — ключ API одной точки, отмеченный кассовой системой: r_keeper, iiko, Syrve или своя касса сети. Создаёт владелец в кабинете: «API и интеграции» → «Создать ключ» → «Касса r_keeper». Ключа кассы на всё заведение нет: касса стоит в конкретной точке.
  • У ключа — правила точки: программа по умолчанию и действие (штамп — для штамп-карты, бонусы — для бонусной карты), порог суммы (0 … 10 000 000 ₸) и коды товаров кассы (до 100, каждый до 40 знаков без пробелов), — и язык текстов на экране кассы, один из шести языков Unemi. Правила и язык меняются без нового ключа; для другой кассы создают новый ключ.
  • Что иначе в методах: сессия живёт 12 часов, и тот же код или номер в заказе отдаёт ту же сессию; posIdentify отдаёт defaultProgram; чек без программы проходит по программе по умолчанию, а если ничего не начислено — причина в skipped.
  • Цифровой QR: пока у заведения есть действующий ключ кассы r_keeper, в QR карты клиента — те же шесть цифр, что под ним: сканер r_keeper читает их как номер карты. В posIdentify их передают в qr (identifiedBy: "qr"). Общий QR из меню приложения (без карты) остаётся обычным: клиент называет шесть цифр под ним.
Что владелец задаёт в кабинете: название, точка, касса, язык и правила
{
  "name": "r_keeper · Абая",
  "venueId": "v8Kp2LmQx1",
  "pos": {
    "system": "r_keeper",
    "language": "ru",
    "rules": {
      "programId": "q2Nw7xRt",
      "action": "bonus",
      "minAmount": 0,
      "productCodes": []
    }
  }
}

Мост r_keeper для дилеров Скоро

Модуль HttpFarCard (ExtDllHTTP.dll) кассового сервера r_keeper сам шлёт POST с XML — запрос карты (getcardinfoex) и проводки закрытого чека (transactionsex), — а мост posRkeeper проводит их тем же ядром, что и методы выше: узнаёт клиента, проводит чек, отменяет его при откате и возврате.

ПараметрЗначение
Адрес https://api.unemi.kz/posRkeeper — строка Address в ExtDllHttp.ini
Обработчики GetCardInfoEx=getcardinfoex, TransactionsEx=transactionsex
Вход Базовая авторизация: логин unemi_<id ключа>, пароль — секрет ключа; только https, иначе https_required
Ключ Ключ кассы r_keeper на одну точку: адрес, логин и пароль владелец получает в кабинете при создании ключа и передаёт дилеру. Обычный ключ API или ключ другой кассы — pos_key_required
Привязка Первый запрос запоминает объект r_keeper (его коды в запросах кассы); запрос с другого объекта — venue_mismatch
Ответы XML протокола HttpFarCard. Отказ проводок — Result="1" с причиной в TRRESPONSE; карта не принята — Locked="1" с причиной в WhyLock

Проводки отклоняются, только когда нельзя принять оплату бонусами (например, не хватает бонусов, клиент назван по телефону или нет бонусной программы): чек без оплаты бонусами касса закрывает всегда, а если начисление не прошло, причину владелец видит в журнале запросов.

Откат оплаты, возврат и откат возврата кассу не останавливают: возврат отменяет чек в пределах 48 часов, а что Unemi не отменил, владелец видит в журнале возвратов. Подарки и купоны через r_keeper не гасятся — их выдают в кассе Unemi. Тексты на экране кассы — на языке ключа.