> Источник: 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 вручную: выбираете товары из синхронизированного
каталога, указываете способ оплаты (наличные или чужой POS), и получаете фискальный чек с
`fpd`, номером операции и ссылкой на чек (`receipt.kaspi.kz`). Выбивание **необратимо**,
поэтому защищено ключом идемпотентности `client_operation_id` — повтор с тем же ключом не
создаёт второй чек. Работает и из кабинета, и по API (`X-API-Key`).

## Коротко

| Вопрос | Ответ |
|---|---|
| Когда нужно | Оплата наличными (`payment_type=3`) или через POS другого банка (`payment_type=5`) |
| Почему вручную | Эти оплаты идут мимо Kaspi QR — авто-чека нет |
| Что нужно от товара | Позиция из каталога с НТИН (фискально зарегистрирована) |
| Как отдаётся | Асинхронно: чек `pending` → `issued`/`failed`, узнаёте поллингом или вебхуком |
| Идемпотентность | `client_operation_id` (уникален на организацию) — двойного чека не будет |
| Результат | `fpd`, `operation_id`, `link` (ссылка на чек на receipt.kaspi.kz) |

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

1. **Кассир Kaspi подключён** (активная сессия). Нет сессии → `kaspi_session_not_configured`.
2. **Смена на кассе открыта.** Открывается в приложении Kaspi Pos. Если закрыта — чек
   вернётся с `error_code: shift_closed`.
3. **Товары есть в каталоге и фискальны** — у позиции заполнен НТИН и штрихкод. Позиции без
   НТИН в фискальный чек не идут (`item_not_fiscal`). Как завести НТИН — см.
   [Каталог, корзина и Нацкаталог](/guides/katalog-korzina-nackatalog).

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

| `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). Повтор с тем же ключом вернёт `409 duplicate_client_operation_id` и НЕ
выбьет второй чек — так двойной клик или ретрай сети не задвоят фискальный документ.

### Шаг 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/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` — это не особенность песочницы, а поведение боя.

1. **Создайте фискальную позицию** — `POST /catalog` со штрихкодом (`barcode`) и НТИН (`ntin`).
   НТИН можно дозаполнить и позже: `PATCH /catalog/{id}` с `ntin` делает позицию фискальной.
   Позиции без НТИН удобно найти фильтром `GET /catalog?without_ntin=true`.
2. **Выбейте чек** — `POST /receipts` → `GET /receipts/{id}` даст `issued` с реальной суммой,
   `fpd` и `link`.
3. **Проверьте отказ** — добавьте в чек позицию **без НТИН**: получите `failed` /
   `item_not_fiscal`, как и в бою.
4. **Воспроизведите остальные ошибки** полем `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`). Форсированная
ошибка выигрывает всегда, даже если позиции фискально корректны.

5. **Проверьте доставку вебхуков** — `GET /webhook-logs?event=receipt.issued` и
   `?event=receipt.failed`. В песочнице они уходят независимо от настроек боевых уведомлений.

В кабинете то же самое доступно без кода: в тестовом режиме на экране «Выбить чек» есть
галочка «Проверить, как себя ведёт чек при ошибке» — выбираете исход и код ошибки.

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

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

**Почему ответ не сразу с чеком?** Выбивание асинхронное именно ради необратимости: сервер
гарантирует ровно один чек на `client_operation_id`, даже если сеть моргнула. Итог берите
поллингом `GET /receipts/{id}` или вебхуком.

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

**Почему в песочнице чек выходит с ошибкой `item_not_fiscal`?** Значит, у товара нет НТИН.
Это правильное поведение: в бою такой товар тоже не пройдёт. Заполните НТИН — и чек выбьется.

---

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