Предусловия
- Активная подписка на ApiPay. Без неё
POST /receiptsиPOST /receipts/previewотдают403 tariff_inactive(в телеexpires_atиreason). Чтение истории чеков работает всегда. - Кассир Kaspi подключён (активная сессия). Нет активного кассира →
409 kaspi_session_not_configured. - Смена на кассе открыта. Открывается в приложении Kaspi Pos. Если закрыта — чек
вернётся с
error_code: shift_closed. - Товары есть в каталоге и фискальны — у позиции заполнен НТИН и штрихкод. Позиции без
НТИН в фискальный чек не идут (
item_not_fiscal). Как завести НТИН — см. Каталог, корзина и Нацкаталог. Позиции без НТИН удобно найти фильтромGET /catalog?without_ntin=true.
Способ оплаты
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). Пока попытка в pending или завершилась
issued, повтор с тем же ключом вернёт 409 duplicate_client_operation_id (в теле —
receipt_id и status) и второго фискального документа не создаст. После failed документа
нет, и ключ снова свободен — тот же запрос можно переслать без изменений. При освобождении ключ
снимается со старой неудачной записи, найти её по нему уже нельзя — храните id чека из
ответа 202.
Шаг 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/web/fiscal?i=000000000000&f=000000000000&s=10&t=2026-07-12%2015%3A25%3A43",
"error_code": null
}
Либо — вебхук: при терминальном статусе чека уходит receipt.issued (успех) или
receipt.failed (причина в receipt.error_code). Событие получают все активные ключи
организации, у которых сохранён webhook_url (дедуп по URL). Дедуплицируйте у себя по
(event, receipt.id).
Статусы: pending → issued (готово, есть fpd/link) или failed (см. error_code).
link — публичная страница чека
receipt.kaspi.kz/web/fiscal?i={ФПД}&f={РНМ кассы}&s={сумма}&t={время операции}: открывается
в любом браузере, можно отправить покупателю, распечатать или показать по QR. Все четыре
параметра сверяются на стороне Kaspi — используйте ссылку целиком, как она пришла.
В песочнице и в редком ответе Kaspi без РНМ кассы в link приходит внутренняя ссылка вида
/preview/cashier: она рисует чек только внутри приложения Kaspi, поле не обнуляется.
Шаг 4 — история чеков
GET /api/v1/receipts — чеки организации, свежие сверху, конверт {current_page, data, total};
элемент — та же форма, что у GET /receipts/{id}. Фильтры: status (pending|issued|failed),
payment_type (3|5), invoice_id, from/to по created_at, page, per_page (1..100,
default 20). Окно from/to — в зоне Asia/Almaty: голая дата в to включает весь день,
to раньше from → 422. Боевая организация видит только боевые чеки, тестовая — только
тестовые. Если исход прошлой попытки неизвестен, сначала найдите её здесь или через
GET /receipts/{id} — и только потом решайте, повторять ли.
Если что-то пошло не так
| error_code | Что значит и что делать |
|---|---|
shift_closed |
Смена закрыта — откройте смену в приложении Kaspi Pos и повторите |
item_not_fiscal |
Позиция без НТИН/штрихкода — заведите её в Нацкаталоге |
kaspi_session_not_configured |
409 — нет активного кассира, переавторизуйте кассу |
kaspi_session_invalid |
Сессия кассира Kaspi недействительна — переподключите кассира и повторите чек |
rfo_missing |
Не определена торговая точка кассы — переподключите кассира |
receipt_kaspi_error |
Kaspi отклонил чек — текст в error_message |
receipt_dispatch_error |
Технический сбой, фискальный документ не создан — перешлите тот же запрос (после failed прежний client_operation_id свободен) |
duplicate_client_operation_id |
409 — по этому ключу чек уже есть (receipt_id и status в теле). После failed ключ освобождается и повтор разрешён |
connection_ambiguous |
422 — несколько активных касс, передайте kaspi_connection_id |
fiscal_receipts_disabled |
403 — выбивание чеков через этого кассира сейчас недоступно; на другие кассы организации ограничение не распространяется. Напишите в поддержку |
tariff_inactive |
403 — подписка на ApiPay не активна. POST /receipts и /receipts/preview заблокированы, история чеков (GET) доступна |
Как протестировать в песочнице
В песочнице Kaspi не вызывается и фискальный документ не пишется, остальное поведение
совпадает с боем — включая правило «нет НТИН → 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). Форсированная
ошибка выигрывает всегда, даже если позиции фискально корректны.
Вебхуки receipt.issued / receipt.failed в песочнице уходят так же, как в бою. Строк
доставки по receipt.* в вебхук-логе нет — если вебхук не дошёл, итог берите поллингом
GET /receipts/{id}.
То же самое без кода — из кабинета.
Частые вопросы
Можно ли отменить/переделать чек?
Нет — фискальный документ необратим. Ошиблись — это вопрос возврата по правилам ОФД, не удаление чека.
Товары не из каталога (свободная услуга)?
Пока не поддерживаются — в фискальный чек идут только позиции каталога с НТИН.