Как это работает у вас
Покупатель у автомата (наличных и карты нет — есть телефон с Kaspi)
│
Экран автомата: «Введите номер телефона»
│
ПО автомата → POST /invoices { phone_number, amount, cart_items/описание }
│
Покупателю приходит push в Kaspi: счёт с суммой и составом покупки
│
«Оплатить» в приложении Kaspi
│
Вебхук invoice.status_changed: paid → контроллер автомата выдаёт товар
ApiPay работает по механике счёта на номер покупателя: автомату не нужен экран под QR, покупатель подтверждает оплату в своём телефоне.
Что понадобится
Автомату нужен выход в интернет и доступ к Kaspi API:
| Компонент | Зачем | Где подробнее |
|---|---|---|
| ПО автомата с интернетом | Ввод номера, вызов API, приём вебхука, команда выдачи | ваше/вендора |
| Номер кассира на каждую торговую точку | Кассиры одного юрлица живут в одной организации | Требования к номеру |
| API-ключ + вебхук на организацию | Ключи и вебхуки общие внутри организации и настраиваются в её кабинете | Ключ и секрет |
| Каталог/cart_items (при Kaspi ОФД) | Чтобы в чеке был состав покупки | cart_items и 422 |
| Тариф на организацию | Лимит — счетов в день; на лето можно даунгрейдиться | тарифы — на apipay.kz |
Пошаговый сетап
- Подключите первую организацию: регистрация на apipay.kz, номер кассира, API-ключ и вебхук-секрет.
- Прогоните цикл в песочнице: создание счёта → simulate-оплата → вебхук → команда «выдать». Отладьте выдачу до боевых денег.
- Экран ввода номера: формат
8XXXXXXXXXX; после ввода автомат вызывает:
curl -X POST https://api.apipay.kz/api/v1/invoices \
-H "X-API-Key: КЛЮЧ_ТОЧКИ" \
-H "Content-Type: application/json" \
-d '{
"phone_number": "8701XXXXXXX",
"amount": 500,
"description": "Автомат №3: кофе американо",
"external_order_id_idempotency": "vend-3-slot-12-1720180000"
}'
- Выдача строго по вебхуку
paid(подписьX-Webhook-Signatureпо raw body — настройка). Покажите на экране «Ожидаем оплату…» и таймер: у счёта окно 24 часа, но для автомата разумно закрывать сессию через 2–3 минуты и предлагать попробовать снова. - Идемпотентность обязательна: ключ вида
автомат-слот-времязащитит от двойного счёта при сетевых ретраях контроллера; повтор вернёт409с данными уже созданного счёта. - Масштабирование на точки: внутри одного юрлица добавляйте кассиров в ту же организацию и передавайте
kaspi_connection_idв каждомPOST /invoices— ключ, вебхук и тариф остаются общими. Отдельное юрлицо (второй ИП) — отдельная организация со своим кассиром, ключом, вебхуком и тарифом (несколько организаций).
Грабли этого бизнеса
- Частота выставления счетов. Потолок по объёму — дневной лимит тарифа организации, счета из кабинета в него не входят (см. Лимит счетов по тарифу). При систематическом превышении
POST /invoicesначинает отдавать429 tariff_limit_reached— обработайте этот код в ПО автомата и покажите покупателю «оплата временно недоступна». - Покупатель ушёл, не оплатив. Счёт живёт 24 часа — он может «догнать» покупателя позже, когда товар уже никто не ждёт. Решение: короткий таймер сессии и выдача только по вебхуку, привязанному к конкретному счёту.
- Выдача по слову «оплатил». У автомата некому проверить — только вебхук
paid, ничего другого. - Каталог и скидки. Если нужен состав покупки в чеке (Kaspi ОФД) —
cart_itemsобязателен; при своих ценах/скидках надёжнее передаватьpriceпозиции явно и считать скидки на своей стороне. - Одна организация = одно юрлицо (ИП/ТОО), кассиров в ней может быть несколько. Если активных кассиров больше одного и primary не задан, запрос без
kaspi_connection_idвернёт422 connection_ambiguous. SIM считайте по числу торговых точек, тарифы — по числу организаций. - Оплата прошла, товар не выдан. Автомат заклинило или слот пуст — деньги уже на вашем Kaspi-счёте, автоматического отката нет: возврат делаете вы,
POST /invoices/{id}/refund(как устроены возвраты).
Частые вопросы
Почему не QR на экране автомата?
QR-счёт ApiPay даёт окно на скан меньше трёх минут (окно и лимиты QR), плюс автомату нужен экран под QR. Сценарий «QR на сумму как терминал» — это официальная «Виртуальная касса» Kaspi, отдельный продукт самого банка. Механика ApiPay для автомата — счёт на номер покупателя: проще экран, подтверждение в телефоне покупателя.
Что если покупатель ввёл чужой/несуществующий номер?
POST /invoices всё равно ответит 201 со status: processing, а через несколько секунд счёт уйдёт в терминальный error с error_code: client_not_found — код придёт вебхуком invoice.status_changed, по нему показывайте на экране «номера нет в Kaspi». Чужой существующий номер просто не оплатит счёт. Чтобы не плодить мёртвые счета, номер можно проверить заранее — POST /api/v1/clients/check.
У меня 5 автоматов на 2 ИП — сколько подключений нужно?
По числу юрлиц: 2 ИП = 2 организации, 2 ключа, 2 вебхука, 2 тарифа. Автоматы одного ИП делят одну организацию, а различайте их через external_order_id/описание.
Можно ли принимать и наличные, и Kaspi?
Да, ApiPay не трогает остальную обвязку автомата; деньги от оплат идут напрямую на Kaspi-счёт вашего ИП.