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

# Как сделать возврат Kaspi через API и почему он не проходит?

**TL;DR.** Возврат делается из кабинета (раздел «Счета» → кнопка возврата) или запросом `POST /api/v1/invoices/{id}/refund`. Возвращать можно только оплаченные счета, окно возврата у Kaspi — около 14 дней. Возврат асинхронный: заявка принимается сразу, итог приносит вебхук `invoice.refunded`. Отказ Kaspi по существу **терминален с первой попытки**: повторную попытку система делает только при временном сбое связи или сессии кассира. Частая причина отказа — **на Kaspi-счёте не хватает денег**: Kaspi удерживает комиссию сразу при оплате, поэтому вернуть полную сумму без пополнения счёта нельзя. Статуса `refunded` у счёта не существует: полностью возвращённый счёт остаётся `paid` с флагом `is_fully_refunded: true`.

## Коротко

| Вопрос | Ответ |
|---|---|
| Какие счета можно вернуть | Только оплаченные (`paid` / `partially_refunded`) и не возвращённые полностью |
| Кассир отключён | Возврат через ApiPay невозможен — только вручную в приложении Kaspi Pay под аккаунтом владельца |
| Частичный возврат | Да: по сумме (`amount`) или по позициям (`return_items[]`: штуки `count` либо сумма `amount`). По корзинному счёту — только через `return_items[]` |
| Как узнать итог | Вебхук `invoice.refunded` (приходит и на `completed`, и на `failed`) |

## Как сделать возврат: кабинет и API

**Из кабинета (без кода):** apipay.kz → раздел «Счета» → найдите оплаченный счёт → кнопка возврата справа → укажите сумму (по умолчанию — вся доступная) → подтвердите. Дальше всё автоматически.

**Через API:**

```bash
# Полный возврат (вся доступная сумма)
curl -X POST "https://api.apipay.kz/api/v1/invoices/{id}/refund" \
  -H "X-API-Key: ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json"

# Частичный возврат по сумме
curl -X POST "https://api.apipay.kz/api/v1/invoices/{id}/refund" \
  -H "X-API-Key: ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{"amount": 1500}'
```

Ответ — `201` со статусом возврата `pending`. Это **не** «деньги вернулись», это «заявка принята»: дальше система асинхронно проводит возврат в Kaspi и присылает вебхук `invoice.refunded` с итоговым статусом `completed` или `failed`. Жизненный цикл возврата: `pending → processing → completed | failed`.

Синхронные отказы при создании возврата:

| Код | Когда | Что делать |
|---|---|---|
| `400 Invoice is not refundable` | Счёт не оплачен или уже возвращён полностью | Неоплаченный счёт отменяют через `POST /api/v1/invoices/{id}/cancel`, а не возвращают |
| `400 Refund amount exceeds available amount` | Сумма больше `available_for_refund` | Прочитать остаток из `GET /invoices/{id}` и повторить с ним |
| `422 partial_refund_requires_return_items` | Частичный возврат корзинного счёта отправлен плоским `amount` | Передать `return_items[]` |
| `403 tariff_inactive` | Не оплачена подписка ApiPay | Оплатить тариф — без него возвраты закрыты |

Список возвратов: `GET /api/v1/invoices/{id}/refunds` и общий `GET /api/v1/refunds`.

## Частичный возврат: по сумме или по штукам?

Три способа, выбирайте один:

- **Просто сумма** — `{"amount": 1500}`: вернуть часть денег без привязки к товарам. Подходит счетам без корзины.
- **По позициям штуками** — `return_items: [{"catalog_item_id": 11, "count": 2}]`: вернуть 2 штуки конкретного товара из корзины.
- **По позиции суммой** — `return_items: [{"catalog_item_id": 15, "amount": 900}]`: частичный возврат неделимой позиции (например, вернуть 900 ₸ из услуги за 5 000 ₸).

Правило API: в каждой позиции `return_items[]` указывается **ровно одно** из `count` (целые штуки) или `amount` (сумма), иначе `422`.

⚠️ **Частичную сумму по корзинному счёту передавайте только через `return_items[]`.** Тот же `error_code` приходит в вебхуке `invoice.refunded` со `status: failed`, если синхронная проверка не сработала.

В вебхуке частичного возврата по сумме `refund.items[].count` может быть `0` — возврат был по сумме, а не по штукам.

Сколько ещё можно вернуть по счёту, видно в `GET /invoices/{id}`: поля `total_refunded`, `is_fully_refunded` и `available_for_refund` (число).

## «Возврат создан, но не проходит» — что происходит?

Симптом: возврат создан, итог `failed`, Kaspi отвечает «Недостаточно денег на счёте. Пополните счёт или сделайте возврат наличными». Возврат уходит на **полную** сумму покупки, а на счёт организации она пришла за вычетом комиссии Kaspi — с пустого счёта полной суммы не хватит.

Такой ответ Kaspi терминален: возврат сразу получает `failed`, уходит вебхук `invoice.refunded` с `refund.error_code`. Автоповторов на него нет, ждать бесполезно.

**Что делать вам:**

1. Пополните Kaspi-счёт организации (или дождитесь следующих оплат — комиссия «размажется» по обороту).
2. Создайте возврат заново — той же кнопкой или тем же запросом. После `failed` сумма освобождается.

⚠️ Пока предыдущий возврат висит в `pending`/`processing`, его сумма **занята**: `available_for_refund` считается как сумма счёта минус завершённые минус незавершённые возвраты, поэтому дубль на ту же сумму отобьётся `400 Refund amount exceeds available amount`.

**Отключаете кассира?** После удаления кассира возвраты по ранее оплаченным счетам через ApiPay недоступны — их придётся делать вручную в приложении Kaspi Pay. Окно возврата у Kaspi около 14 дней: если по счетам последних двух недель возможны возвраты, отключайте кассира после закрытия этого окна ([Смена и отключение кассира](/guides/smena-kassira-nomera-vladeltsa)).

## Почему статус счёта не меняется на «refunded»?

Такого статуса нет: счёт и возврат — разные объекты. **Счёт** фиксирует факт продажи и остаётся `paid`; при частичном возврате переходит в `partially_refunded`, при полном получает флаг **`is_fully_refunded: true`**. **Возврат** — отдельная операция со своими статусами (`pending → processing → completed | failed`).

Для интеграции это значит: не ждите вебхук `invoice.status_changed` со статусом «refunded» — его не будет никогда. Итог возврата приносит событие **`invoice.refunded`** (и на успех, и на отказ). Дедуплицируйте его на своей стороне по паре `(refund.id, refund.status)`; `refund.error_message` в вебхуке отсутствует намеренно — детали смотрите в кабинете или через `GET /invoices/{id}/refunds`. Настройка вебхуков — «[Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay)».

## Сроки: окно возврата и зачисление покупателю

- **Окно возврата — около 14 дней** с момента оплаты. Точный срок контролирует Kaspi; после окна возврат откажет с кодом `refund_window_expired`. Если окно истекло, деньги покупателю возвращают вне ApiPay — переводом или наличными по вашим правилам торговли.
- **Зачисление покупателю** после успешного возврата происходит на стороне Kaspi.

Возвраты, сделанные кассиром вручную в приложении Kaspi Pay, ApiPay подтягивает автоматически: они появятся в кабинете и придут тем же вебхуком `invoice.refunded`. Двойного учёта не будет.

## Если возврат так и не проходит — возврат по QR

Вторая механика возврата — с подтверждением покупателя: он сканирует возвратный QR тем Kaspi, которым платил, и только после этого вы видите список его покупок и возвращаете нужную. В API это `POST /api/v1/qr-refunds`, в кабинете — кнопка **«Возврат с подтверждением»** на странице возвратов. Применяйте её, когда возврат по счёту упирается в отказ Kaspi или когда оплата шла мимо ваших счетов.

Флоу, коды ошибок и песочница — в статье [Возврат по QR: покупатель подтверждает возврат в Kaspi](/guides/vozvrat-po-qr-cherez-api).

## Вопросы и ответы

**Можно ли запретить возвраты совсем?**
Серверного «запрета возвратов» не существует: возврат — штатная функция Kaspi. Контролируйте это на своей стороне — просто не вызывайте возврат из своей интеграции; в кабинете операция доступна владельцу.

Смотрите также: [Как создать счёт Kaspi по номеру](/guides/kak-sozdat-schet-kaspi-po-nomeru) · [Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay) · Тарифы и комиссия ApiPay · пиллар «[Как принимать оплату Kaspi через API](/kaspi-api)».

---

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