Возвраты
Возврат отправляет деньги покупателю обратно через Kaspi — со счёта того кассира, через которого прошла оплата. Можно вернуть всю сумму или её часть, в том числе несколькими возвратами.
Создание возврата
Вернуть можно платёж в статусе succeeded или partially_refunded. Без параметра amount возвращается весь остаток.
curl "https://pay.sezgir.com/api/v1/payments/pay_8Rt2XvL5nQ1mZ7cK4wB9pJ3s/refunds" \
-H "Authorization: Bearer $SEZGIR_API_KEY" \
-H "Idempotency-Key: refund-pos-77-1029-1" \
-H "Content-Type: application/json" \
-d '{ "amount": 1000, "reason": "Нет круассанов" }'{
"id": "re_5KdW2pQ9sXbN7mT1vLc8Rf4Z",
"object": "refund",
"payment_id": "pay_8Rt2XvL5nQ1mZ7cK4wB9pJ3s",
"amount": 1000,
"status": "succeeded",
"requires_action": false,
"reason": "Нет круассанов",
"failure_reason": null,
"created_at": "2026-10-25T12:40:19.007Z"
}refund-<id заказа>-<номер>.Частичные возвраты
Можно сделать несколько возвратов по одному платежу, пока их сумма не достигнет суммы платежа. После каждого успешного возврата растёт amount_refunded платежа, а статус становится partially_refunded или, когда возвращено всё, refunded.
| Действие | amount_refunded | Статус платежа |
|---|---|---|
| Оплата 15 000 ₸ | 0 | succeeded |
Возврат { "amount": 5000 } | 5 000 | partially_refunded |
Возврат {} — остаток | 15 000 | refunded |
| Ещё один возврат | — | 409 already_refunded |
Возвраты, ожидающие подтверждения (pending), резервируют свою сумму: пока результат неизвестен, вернуть её второй раз нельзя.
Ответы и статусы
Запрос на возврат синхронно обращается в Kaspi, поэтому результат обычно известен сразу:
| HTTP | status возврата | Что произошло | Что делать |
|---|---|---|---|
| 201 | succeeded | Kaspi выполнил возврат | Готово |
| 202 | pending | Kaspi не ответил или результат не удалось подтвердить; requires_action: true | Ждать вебхук, не повторять с новым ключом |
| 502 | failed | Kaspi явно отказал; в ответе code вида kaspi_<число> | Посмотреть сообщение, решить вручную |
| 503 | failed | Запрос не дошёл до Kaspi (provider_unavailable) | Повторить позже — деньги не двигались |
При отказе Kaspi и при 503 возврат сохраняется со статусом failed и причиной в failure_reason, а зарезервированная сумма освобождается. Любой неоднозначный исход (таймаут, оборванный ответ, неожиданная ошибка) — это всегда 202 с requires_action: true, никогда не ошибка. В тестовом режиме возвраты выполняются сразу и всегда успешно.
Ответ 202 и requires_action
Если Kaspi не ответил на запрос возврата (обрыв связи, таймаут), Sezgir Pay не знает, выполнен ли он. Пометить такой возврат неуспешным — верный способ вернуть деньги дважды, поэтому API отвечает 202 Accepted:
{
"id": "re_5KdW2pQ9sXbN7mT1vLc8Rf4Z",
"object": "refund",
"payment_id": "pay_8Rt2XvL5nQ1mZ7cK4wB9pJ3s",
"amount": 1000,
"status": "pending",
"requires_action": true,
"reason": "Нет круассанов",
"failure_reason": "Kaspi не ответил — проверьте возврат в Kaspi Pay и подтвердите результат",
"created_at": "2026-10-25T12:40:19.007Z"
}- Сумма остаётся зарезервированной, и повторный возврат той же суммы невозможен.
- Администратор проверяет операцию в Kaspi Pay и подтверждает результат в кабинете.
- Вы получаете
refund.succeeded(иpayment.refunded) либоrefund.failed. До этого момента считайте возврат незавершённым.
Idempotency-Key безопасен — он вернёт тот же ответ 202. Новый ключ создаст второй возврат, и если первый на самом деле прошёл, покупатель получит деньги дважды.События
| Событие | Когда | data.object |
|---|---|---|
refund.succeeded | Возврат выполнен — сразу или после подтверждения | Refund |
payment.refunded | Сразу после refund.succeeded — для полного и для частичного возврата | Payment с новым amount_refunded |
refund.failed | Kaspi отказал, запрос не дошёл до Kaspi или администратор отметил возврат неуспешным | Refund |
Чтобы отличить полный возврат от частичного, смотрите на status платежа в payment.refunded: refunded или partially_refunded.
Ошибки
| HTTP | code | Причина |
|---|---|---|
| 400 | validation_failed | amount не целое число или меньше 1; reason длиннее 500 символов |
| 400 | — | Сумма больше доступного остатка (param: "amount"). В сообщении указан доступный остаток. |
| 404 | — | Платёж не найден (или принадлежит другому сервису либо режиму) |
| 409 | payment_not_refundable | Платёж не оплачен: pending, failed, expired или canceled |
| 409 | already_refunded | Сумма уже возвращена полностью или остаток зарезервирован возвратом в обработке |
| 502 | kaspi_* | Kaspi отказал в возврате; код Kaspi передаётся как kaspi_<число>. Возврат — failed |
| 503 | provider_unavailable | Kaspi недоступен или кассир требует повторного входа |
Общий формат ошибок — в разделе Ошибки.
Просмотр возвратов
Все возвраты платежа
curl "https://pay.sezgir.com/api/v1/payments/pay_8Rt2XvL5nQ1mZ7cK4wB9pJ3s/refunds" \
-H "Authorization: Bearer $SEZGIR_API_KEY"Возвращает все возвраты платежа, от новых к старым, одним списком (has_more всегда false).
Один возврат
curl "https://pay.sezgir.com/api/v1/refunds/re_5KdW2pQ9sXbN7mT1vLc8Rf4Z" \
-H "Authorization: Bearer $SEZGIR_API_KEY"Поля объекта описаны в справочнике.