Коротко
Ежедневный или еженедельный сбор в таксопарке — это много однотипных счетов. Метод POST /api/v1/invoices/bulk создаёт от 1 до 100 счетов одним запросом: если передать номер водителя, счёт придёт push-уведомлением в его приложение Kaspi. Чтобы повторная отправка списка не наплодила дублей, используется Idempotency-Key. Ошибки приходят по каждому элементу отдельно — если один упал, остальные всё равно создадутся.
Сценарий
В парке 80 водителей. Каждый платит ежедневную аренду или комиссию. Как это выглядело раньше: водитель приезжает в парк, отдаёт наличные диспетчеру или переводит со своей карты, диспетчер отмечает в таблице, а в конце месяца никто точно не знает, кто сколько заплатил.
Что нужно: утром счета уходят сразу всем 80 водителям, каждому на телефон, в кабинете видно кто оплатил, отчёт собирается сам.
Схема работы по шагам
- Подготовка списка. Ваша система (своя база, таблица, диспетчерская программа) формирует список водителей, которые должны заплатить сегодня: телефон, сумма, внутренний номер.
- Один запрос.
POST /api/v1/invoices/bulk— в теле массив счетов, 1-100 элементов. Если больше, разбиваете на пакеты. - Идемпотентность. К запросу добавляете заголовок
Idempotency-Keyс устойчивым значением, привязанным к дате (например «парк-сбор-2026-09-14»). Даже если задача запустится повторно, счета не выставятся дважды. - Разбор ответа. По каждому элементу приходит результат. Успешные записываете в базу, проблемные выводите в журнал.
- Push водителю. Если выбран
kind: "phone", счёт приходит уведомлением в приложение Kaspi. Водитель открывает его и подтверждает. - Вебхук. При оплате приходит
invoice.paidсexternalOrderId— по нему находите водителя и закрываете его задолженность. - Отчёт. В конце дня фильтруете в кабинете или выгружаете CSV: кто заплатил, кто нет.
Какие методы API нужны
| Что делает | Метод |
|---|---|
| Счета по списку | POST /api/v1/invoices/bulk (1-100) |
| Счёт отдельному водителю | POST /api/v1/invoices |
| Проверить статус | GET /api/v1/invoices/{id} |
| Список за период | GET /api/v1/invoices |
| Водитель не вышел на смену | POST /api/v1/invoices/{id}/cancel |
| Вернуть лишнее | POST /api/v1/invoices/{id}/refund |
| Узнать об оплате | Вебхук invoice.paid, результат массовой отправки invoice.bulk |
Идемпотентность — то, что спасает от дублей
Ежедневный сбор — автоматическая задача, а автоматические задачи повторяются: cron запустился дважды, оборвалась сеть и запрос ушёл снова, диспетчер нажал кнопку два раза. Без идемпотентности каждому водителю уходит два счёта, телефон звонит дважды, а кто-то платит оба раза.
Решение — заголовок Idempotency-Key. Значение должно быть устойчивым: собирайте его из даты и номера пакета, а не из случайного числа. При повторе с тем же ключом новый счёт не создаётся, возвращается прежний (HTTP 200, idempotentReplay: true).
Обработка вебхука тоже должна быть идемпотентной: проверяйте пару (invoice.id, status), потому что при ответе не 2xx мы повторим доставку 11 раз. Подробнее: Идемпотентность.
Если дубли всё-таки пошли, есть порядок экстренной остановки: Счета дублируются.
Разбор ошибок по каждому элементу
В методе bulk каждый элемент проверяется сам по себе. Пакет не падает целиком: из 80 счетов создадутся 77, а 3 вернут ошибку.
Частые причины:
| Причина | Что делать |
|---|---|
Формат телефона не 7XXXXXXXXXX | Почистить номера в базе |
| Сумма ноль или отрицательная | Проверить логику расчёта |
| У счёта на телефон есть тиыны | Округлить до целых тенге |
| Слишком длинное описание | У счёта на телефон 60 символов, у QR — 100 |
| Исчерпан месячный лимит | Поднять тариф или разнести сбор |
| Сработала суточная защита | Разнести пакеты по времени |
Сопоставьте строки ответа с порядком своего списка, проблемные поставьте в отдельную очередь и отправьте после исправления. Весь пакет заново отправлять не нужно — даже с ключом идемпотентности это лишняя нагрузка.
Месячный лимит даёт ошибку tariff_limit_reached, суточная защита — tariff_daily_burst. Это разные вещи: суточное число не бизнес-лимит, а предохранитель от интеграции, ушедшей в цикл. Подробнее: Достигнут лимит — что делать.
Автоматизация ежедневного сбора
Устойчивый порядок работы:
- Утром в заданное время запускается задача.
- Формируется список водителей, которые платят сегодня (вышедшие на смену, с задолженностью).
- Список делится на пакеты не более 100 элементов.
- Каждый пакет отправляется через
bulk,Idempotency-Key— дата и номер пакета. - Между пакетами ставится пауза в несколько секунд — не отправляйте сотни запросов залпом.
- Результат пишется в базу, проблемные элементы попадают в очередь повторной отправки.
- Ближе к вечеру уходит вторая волна напоминаний тем, кто не заплатил.
Сначала прогоните весь цикл на ключе песочницы (qp_test_…): реальные деньги не двигаются, оплату вы ставите сами через simulate.
Отчётность и выгрузка
Расставляйте пометки в момент создания счёта, тогда собрать отчёт будет просто:
externalOrderId— внутренний номер водителя или строка вида «дата + водитель».metadata— номер машины, смена, диспетчер, подразделение парка.description— текст, который видит водитель: «Аренда, 14 сентября».
В разделе Счета кабинета фильтруете по периоду и статусу и выгружаете CSV. Этот же файл отдаёте в бухгалтерию. Подробнее: Экспорт CSV и отчётность.
Если подразделений несколько, заведите каждому свой API-ключ — отчётность разделится по источнику.
Особые замечания
- Номер водителя должен быть зарегистрирован в Kaspi. Человеку без приложения Kaspi счёт на телефон не придёт — дайте ему QR или ссылку
payUrl. - Номер кассира виден водителю в уведомлении о счёте. Это нормальная работа Kaspi.
- Если водитель не заплатил, счёт уйдёт в
expired. Предусмотрите в своей системе перенос задолженности на следующий день. - Поздние оплаты случаются. Если деньги придут по просроченному счёту, событие
invoice.paidпридёт с признакомlate: true— пересчитайте баланс или сделайте возврат. - Заранее посчитайте месячный лимит. 80 водителей × 30 дней = 2 400 счетов, плюс повторные отправки.
Вопросы и ответы
Сколько счетов можно отправить одним запросом? От 1 до 100. Больше — разбивайте на пакеты.
Деньги списываются с карты водителя автоматически? Нет. Мы только выставляем счёт, каждую оплату водитель подтверждает сам в приложении Kaspi.
Если задача запустится дважды, будут дубли? При корректном Idempotency-Key — нет. Значение ключа должно быть устойчивым и привязанным к дате.
Если один элемент пакета упал, остальные создадутся? Да. Каждый элемент проверяется отдельно, остальные обрабатываются как обычно.
Куда приходят деньги? Напрямую на Kaspi-счёт вашего парка. У нас они не задерживаются, процент с транзакций мы не берём — оплата сервиса это месячная подписка.