Предусловия
- Кассир Kaspi подключён (активная сессия). Нет сессии →
kaspi_session_not_configured. - Смена на кассе открыта. Открывается в приложении Kaspi Pos. Если закрыта — чек
вернётся с
error_code: shift_closed. - Товары есть в каталоге и фискальны — у позиции заполнен НТИН и штрихкод. Позиции без
НТИН в фискальный чек не идут (
item_not_fiscal). Как завести НТИН — см. Каталог, корзина и Нацкаталог.
Способ оплаты
payment_type |
Что это |
|---|---|
3 |
Наличные |
5 |
Через POS другого банка |
Для наличных можно передать received_amt (полученная сумма, может быть больше итога — для
сдачи). Для чужого POS received_amt всегда равен сумме чека.
Как выбить чек (по шагам)
Шаг 1 — превью (необязательно, но удобно)
curl -X POST https://api.apipay.kz/api/v1/receipts/preview \
-H "X-API-Key: <ваш ключ>" -H "Content-Type: application/json" \
-d '{ "payment_type": 3, "total_price": "10" }'
Ответ — готовый предпросмотр чека:
{ "data": [
{ "Title": "Сумма", "Subtitle": "10 ₸", "isBoldText": true },
{ "Title": "Способ оплаты", "Subtitle": "Наличные", "isBoldText": false }
] }
Шаг 2 — выбить чек
curl -X POST https://api.apipay.kz/api/v1/receipts \
-H "X-API-Key: <ваш ключ>" -H "Content-Type: application/json" \
-d '{
"payment_type": 3,
"client_operation_id": "pos-2026-07-12-0042",
"cart_items": [ { "catalog_item_id": 15041503, "quantity": 1, "price": 10 } ],
"received_amt": "500"
}'
Ответ 202 — чек принят в обработку:
{ "id": 4210, "status": "pending", "client_operation_id": "pos-2026-07-12-0042" }
client_operation_id — ваш ключ идемпотентности. Сгенерируйте его один раз на попытку
(например uuid). Повтор с тем же ключом вернёт 409 duplicate_client_operation_id и НЕ
выбьет второй чек — так двойной клик или ретрай сети не задвоят фискальный документ.
Шаг 3 — узнать результат
Поллинг:
curl https://api.apipay.kz/api/v1/receipts/4210 -H "X-API-Key: <ваш ключ>"
{
"id": 4210, "status": "issued", "total_price": "10.00",
"fpd": "000000000000", "operation_id": "KKM00000000", "shift_number": 106,
"link": "https://receipt.kaspi.kz/preview/cashier?extTranId=KKM00000000",
"error_code": null
}
Либо — вебхук на ваш URL (если включены push-уведомления):
receipt.issued (успех) / receipt.failed (ошибка, причина в receipt.error_code).
Статусы: pending → issued (готово, есть fpd/link) или failed (см. error_code).
Если что-то пошло не так
| error_code | Что значит и что делать |
|---|---|
shift_closed |
Смена закрыта — откройте смену в приложении Kaspi Pos и повторите |
item_not_fiscal |
Позиция без НТИН/штрихкода — заведите её в Нацкаталоге |
kaspi_session_not_configured |
Нет активного кассира — переавторизуйте кассу |
rfo_missing |
Не определена торговая точка кассы — переподключите кассира |
receipt_kaspi_error |
Kaspi отклонил чек — текст в error_message |
receipt_dispatch_error |
Технический сбой — повторите с новым client_operation_id |
duplicate_client_operation_id |
Чек с этим ключом уже создан (см. receipt_id в ответе) |
connection_ambiguous |
Несколько активных касс — передайте kaspi_connection_id |
Как протестировать в песочнице
Песочница зеркалит рабочий режим: Kaspi не вызывается и фискальный документ не пишется, но правила ровно те же. Обкатайте интеграцию здесь до боя — чеки в песочнице работают всегда, даже пока фича раскатывается на боевые организации.
Главное правило: позиция каталога фискальна, только если у неё есть и штрихкод, и НТИН.
Товар без НТИН даст failed / item_not_fiscal — это не особенность песочницы, а поведение боя.
- Создайте фискальную позицию —
POST /catalogсо штрихкодом (barcode) и НТИН (ntin). НТИН можно дозаполнить и позже:PATCH /catalog/{id}сntinделает позицию фискальной. Позиции без НТИН удобно найти фильтромGET /catalog?without_ntin=true. - Выбейте чек —
POST /receipts→GET /receipts/{id}дастissuedс реальной суммой,fpdиlink. - Проверьте отказ — добавьте в чек позицию без НТИН: получите
failed/item_not_fiscal, как и в бою. - Воспроизведите остальные ошибки полем
simulate(только в песочнице; на боевой организации вернётся403 not_sandbox, чек не создастся):
curl -X POST https://api.apipay.kz/api/v1/receipts \
-H "X-API-Key: <ключ песочницы>" -H "Content-Type: application/json" \
-d '{
"payment_type": 3,
"client_operation_id": "sb-001",
"cart_items": [ { "catalog_item_id": 12, "quantity": 2 } ],
"simulate": { "status": "failed", "error_code": "shift_closed" }
}'
simulate.status — issued или failed; simulate.error_code — shift_closed,
item_not_fiscal или receipt_kaspi_error (по умолчанию receipt_kaspi_error). Форсированная
ошибка выигрывает всегда, даже если позиции фискально корректны.
- Проверьте доставку вебхуков —
GET /webhook-logs?event=receipt.issuedи?event=receipt.failed. В песочнице они уходят независимо от настроек боевых уведомлений.
В кабинете то же самое доступно без кода: в тестовом режиме на экране «Выбить чек» есть галочка «Проверить, как себя ведёт чек при ошибке» — выбираете исход и код ошибки.
Частые вопросы
Можно ли отменить/переделать чек?
Нет — фискальный документ необратим. Ошиблись — это вопрос возврата по правилам ОФД, не удаление чека.
Почему ответ не сразу с чеком?
Выбивание асинхронное именно ради необратимости: сервер гарантирует ровно один чек на client_operation_id, даже если сеть моргнула. Итог берите поллингом GET /receipts/{id} или вебхуком.
Товары не из каталога (свободная услуга)?
Пока не поддерживаются — в фискальный чек идут только позиции каталога с НТИН.
Почему в песочнице чек выходит с ошибкой item_not_fiscal?
Значит, у товара нет НТИН. Это правильное поведение: в бою такой товар тоже не пройдёт. Заполните НТИН — и чек выбьется.