Коротко
Курьер выставляет счёт прямо у двери: через Telegram-бота или из вашего курьерского приложения. Клиент достаёт телефон, сканирует QR или подтверждает счёт, пришедший в приложение Kaspi. Деньги идут напрямую на Kaspi-счёт компании, курьер не возит наличные. Номер заказа кладёте в externalOrderId, курьера и маршрут — в metadata, и отчётность собирается сама.
Сценарий
Заказ приняли на сайте или по телефону, оплата — при доставке. Как это было раньше: курьер берёт наличные, не хватает сдачи, в конце дня сдаёт деньги в кассу, часть теряется, отчёт появляется только в конце месяца.
Что нужно: курьер у двери нажимает одну кнопку, клиент платит с телефона, деньги сразу уходят на счёт компании, диспетчер видит оплату заказа в реальном времени.
Схема работы по шагам
- Курьер приехал. Передаёт заказ, подтверждает сумму (она может отличаться, если что-то заменили).
- Выставляется счёт. Курьер даёт команду
/invoiceв Telegram-боте или нажимает «Оплата» в своём приложении. За этим стоит вызовPOST /api/v1/invoices. - Клиент платит. Два варианта: курьер показывает QR на экране либо через
kind: "phone"счёт приходит push-уведомлением в приложение Kaspi. - Приходит подтверждение. Отправляется вебхук
invoice.paid, в приложении курьера заказ становится оплаченным. На практике это занимает несколько секунд. - Курьер отдаёт заказ и едет по следующему адресу.
- Диспетчер видит картину. В кабинете или в вашей системе видно, какие заказы на маршруте оплачены.
Какие методы API нужны
| Что делает | Метод |
|---|---|
| Счёт в момент доставки | POST /api/v1/invoices |
| Счета на маршрут заранее | POST /api/v1/invoices/bulk, 1-100 |
| Проверить статус | GET /api/v1/invoices/{id} |
| Клиент отказался | POST /api/v1/invoices/{id}/cancel |
| Товар вернули | POST /api/v1/invoices/{id}/refund |
| Узнать об оплате | Вебхук invoice.paid |
QR или счёт на телефон
| Ситуация | Что выбрать |
|---|---|
| Клиент у двери, у курьера есть экран | QR |
| Заказ принимает другой человек, а платит заказчик | Счёт на телефон |
| У двери неудобно, плохая погода | Счёт на телефон |
| У клиента нет приложения Kaspi | QR или ссылка payUrl |
| В сумме есть тиыны | QR (у счёта на телефон целые тенге) |
При отправке счёта на телефон номер должен быть в формате 7XXXXXXXXXX. Полная разница: QR-счёт или счёт по телефону.
Окно сканирования QR ограничено. Если клиент не отсканировал сразу, окно может закончиться — тогда закрываете старый счёт через cancel и создаёте новый. Точное время берите из поля expiresAt, не зашивайте константу в код.
Номер заказа и метаданные
Если правильно расставить пометки при создании счёта, отчётность соберётся сама:
externalOrderId— ваш номер заказа. Это поле возвращается в вебхуке и видно в списке и в выгрузке. Вопрос «к какому заказу относится платёж» решается одним поиском.metadata— произвольный JSON. Кладите туда идентификатор курьера, номер маршрута, смену, зону, время доставки.description— текст, который видит клиент. Пишите в формате «Заказ №1204, доставка». У QR-счёта 100 символов, у счёта на телефон — 60.
Если нужна отчётность по курьерам, хватит идентификатора курьера в metadata — при выгрузке CSV группируете по этому полю. Подробнее: Metadata и номер заказа.
Вариант без кода: Telegram-бот
Писать курьеру отдельное приложение необязательно — хватает Telegram-бота. Команды:
| Команда | Что делает |
|---|---|
/invoice | Создаёт счёт |
/last | Показывает последние счета |
/today | Итоги за день |
/status | Статус одного счёта |
/cancel | Отменяет счёт |
/support | Написать в поддержку |
Достаточно, чтобы у курьера на телефоне был Telegram. Бот привязывается из кабинета. Подробнее: Команды Telegram-бота.
При небольшом объёме доставок подойдёт и такой вариант: диспетчер создаёт счёт вручную в кабинете и отправляет ссылку курьеру.
Возвраты
Если клиент вскрыл заказ и вернул его, деньги возвращаются через POST /invoices/{id}/refund с телом { amount?, reason? }. Без amount возврат полный, с amount — частичный (например, за одну позицию).
Чтобы не вернуть дважды: если ответ на запрос неизвестен (оборвалась сеть, таймаут), не повторяйте сразу. Сначала прочитайте список возвратов через GET /invoices/{id}. Подробнее: API возвратов.
Право на возврат лучше оставить диспетчеру, а не курьеру: уберите scope refunds:write из ключа, которым пользуются курьеры.
Недоставленный заказ
Клиента нет дома, он не берёт трубку или отказался от заказа. Порядок:
- Счёт ещё не создан — делать ничего не нужно, помечаете заказ недоставленным в своей системе.
- Счёт создан, но не оплачен — закрываете его через
POST /invoices/{id}/cancel. Открытые счета не должны копиться. - Клиент оплатил, а потом отказался — делаете возврат.
- Пришла поздняя оплата — это случается. Если деньги придут по счёту
cancelledилиexpired, событиеinvoice.paidпридёт с признакомlate: true. Тогда либо доставляете заказ повторно, либо возвращаете деньги. Предусмотрите эту ветку в своей системе: Поздняя оплата.
Особые замечания
- Курьер не возит наличные — в этом основная выгода. Но часть клиентов всё равно захочет заплатить наличными, оставьте оба способа.
- Номер кассира виден клиенту в уведомлении о счёте. Это нормальная работа Kaspi. Виден номер кассира компании, а не личный номер курьера.
- Не давайте API-ключ курьеру. Курьерское приложение обращается к вашему серверу, а сервер — к нам. Ключ не должен попадать в мобильное приложение.
- Интернет. На части адресов связь плохая. Если создание счёта не удалось, в приложении курьера должна быть кнопка повтора — но защитите её
Idempotency-Key, чтобы не появились дубли. - Если сумма изменилась, закройте старый счёт и создайте новый. Менять сумму существующего счёта нельзя.
- Сначала песочница. На ключе
qp_test_…прогоните весь цикл: счёт, симуляция оплаты, возврат, отмена.
Вопросы и ответы
Нужно ли курьеру отдельное устройство? Нет. Telegram-бот работает на любом телефоне. Если у вас есть своё приложение, в него добавляется кнопка «Оплата».
Может ли курьер увидеть чужие счета? Если привязать API-ключ к конкретному кассиру, он видит только счета, прошедшие через этого кассира, на остальные вернётся 404.
Деньги приходят на счёт курьера? Нет. Деньги приходят напрямую на Kaspi-счёт компании.
Что делать, если клиент передумал у двери? Если счёт не оплачен, закрываете его через cancel. Если оплачен — делаете возврат.
Можно ли создать счета на весь маршрут заранее? Технически да, через bulk. Но окно сканирования QR ограничено, поэтому счёт лучше создавать в момент доставки.