> Источник: https://apipay.kz/guides/vybit-fiskalnyy-chek-kaspi · Обновлено: 2026-07-12 · apipay.kz
> ApiPay — независимый сервис приёма платежей поверх вашего Kaspi Pay.

# Фискальный чек Kaspi для наличных и чужого POS: как выбить через API

**TL;DR.** Когда покупатель платит **наличными** или **через POS другого банка**, оплата идёт
мимо Kaspi QR — значит, Kaspi **не создаёт фискальный чек автоматически**. ApiPay позволяет
пробить такой чек в Kaspi OFD вручную: выбираете товары из каталога, указываете способ оплаты
и получаете чек с `fpd`, номером операции и ссылкой (`receipt.kaspi.kz`). Выбивание
**необратимо**, поэтому защищено ключом идемпотентности `client_operation_id` — повтор с тем же
ключом не создаёт второй чек. Работает и из кабинета, и по API (`X-API-Key`).

## Коротко

| Вопрос | Ответ |
|---|---|
| Когда нужно | Оплата наличными (`payment_type=3`) или через POS другого банка (`payment_type=5`) |
| Что нужно от товара | Позиция из каталога с НТИН (фискально зарегистрирована) |
| Как отдаётся | Асинхронно: чек `pending` → `issued`/`failed`, узнаёте поллингом или вебхуком |
| История чеков | `GET /api/v1/receipts` — фильтры по статусу, способу оплаты, счёту и периоду |

## Предусловия

1. **Активная подписка на ApiPay.** Без неё `POST /receipts` и `POST /receipts/preview` отдают
   `403 tariff_inactive` (в теле `expires_at` и `reason`). Чтение истории чеков работает всегда.
2. **Кассир Kaspi подключён** (активная сессия). Нет активного кассира →
   `409 kaspi_session_not_configured`.
3. **Смена на кассе открыта.** Открывается в приложении Kaspi Pos. Если закрыта — чек
   вернётся с `error_code: shift_closed`.
4. **Товары есть в каталоге и фискальны** — у позиции заполнен НТИН и штрихкод. Позиции без
   НТИН в фискальный чек не идут (`item_not_fiscal`). Как завести НТИН — см.
   [Каталог, корзина и Нацкаталог](/guides/katalog-korzina-nackatalog). Позиции без НТИН
   удобно найти фильтром `GET /catalog?without_ntin=true`.

## Способ оплаты

| `payment_type` | Что это |
|---|---|
| `3` | Наличные |
| `5` | Через POS другого банка |

Для наличных можно передать `received_amt` (полученная сумма, может быть больше итога — для
сдачи). Для чужого POS `received_amt` всегда равен сумме чека.

## Как выбить чек (по шагам)

### Шаг 1 — превью (необязательно, но удобно)

```bash
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" }'
```
Ответ — готовый предпросмотр чека:
```json
{ "data": [
  { "Title": "Сумма", "Subtitle": "10 ₸", "isBoldText": true },
  { "Title": "Способ оплаты", "Subtitle": "Наличные", "isBoldText": false }
] }
```

### Шаг 2 — выбить чек

```bash
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` — чек принят в обработку:
```json
{ "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 — узнать результат

Поллинг:
```bash
curl https://api.apipay.kz/api/v1/receipts/4210 -H "X-API-Key: <ваш ключ>"
```
```json
{
  "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`». Про сами режимы —
[Песочница и рабочий режим](/guides/pesochnitsa-i-rabochiy-rezhim).

Ошибки воспроизводятся полем `simulate` (только в песочнице; на боевой организации вернётся
`403 not_sandbox`, чек не создастся):

```bash
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}`.

То же самое без кода — [из кабинета](/guides/kak-vybit-chek-v-kabinete-apipay).

## Частые вопросы

**Можно ли отменить/переделать чек?** Нет — фискальный документ необратим. Ошиблись — это
вопрос возврата по правилам ОФД, не удаление чека.

**Товары не из каталога (свободная услуга)?** Пока не поддерживаются — в фискальный чек идут
только позиции каталога с НТИН.

---

ApiPay — независимый сервис и не аффилирован с АО «Kaspi Bank». Kaspi и Kaspi Pay — товарные знаки их правообладателя.
База знаний: https://apipay.kz/guides
