Возврат по QR: покупатель подтверждает возврат в Kaspi

Обновлено 27 июля 2026 · Справочник · Версия в Markdown
Содержание
  1. Как это выглядит для продавца
  2. Флоу в API
  3. Частые ошибки
  4. Как проверить без реального покупателя
  5. Частые вопросы

Как это выглядит для продавца

  1. Продавец начинает возврат и получает ссылку для покупателя.
  2. Отправляет её покупателю — в WhatsApp, Telegram или любым другим способом.
  3. Покупатель открывает ссылку и подтверждает возврат в Kaspi.
  4. Продавец видит список покупок этого покупателя, выбирает нужную и возвращает деньги — целиком или частично.

Схема:

Продавец              ApiPay                  Покупатель
   │  начать возврат    │                          │
   ├───────────────────►│  ссылка + QR             │
   │◄───────────────────┤                          │
   │  отправляет ссылку ─────────────────────────► │
   │                    │◄── подтверждает в Kaspi ─┤
   │  статус: покупатель опознан                   │
   │◄───────────────────┤                          │
   │  список покупок    │                          │
   │◄──────────────────►│                          │
   │  вернуть выбранную │                          │
   ├───────────────────►│ ─── деньги покупателю ──►│

Флоу в API

Все запросы — с вашим ключом в заголовке X-API-Key.

1. Начать возврат

curl -X POST https://api.apipay.kz/api/v1/qr-refunds \
  -H "X-API-Key: ВАШ_КЛЮЧ" \
  -H "Content-Type: application/json" \
  -d '{}'

В ответе — ссылка и картинка QR для покупателя, срок действия и рекомендованный интервал опроса:

{
  "id": 42,
  "status": "awaiting_scan",
  "qr_token_url": "https://qr.kaspi.kz/return/…",
  "qr_image_url": "https://…/storage/qr/uuid.png",
  "expires_at": "2026-07-27T17:27:09+05:00",
  "poll_interval_seconds": 3,
  "scan_wait_timeout_seconds": 90
}

Если у организации несколько активных касс, укажите kaspi_connection_id — иначе придёт 422 connection_ambiguous.

2. Дождаться подтверждения

curl https://api.apipay.kz/api/v1/qr-refunds/42 -H "X-API-Key: ВАШ_КЛЮЧ"

Опрашивайте с интервалом из poll_interval_seconds, пока статус не станет customer_identified (покупатель подтвердил) или expired (срок вышел, нужна новая сессия). Вместо опроса можно слушать вебхуки qr_refund.identified, qr_refund.completed, qr_refund.expired.

⚠️ Не прекращайте опрос после customer_identified. У сессии два срока: сам QR живёт минуты, и отдельно ограничено время на выбор покупки после подтверждения. Если продавец задумался, сессия истечёт — и запрос на возврат вернёт 409 qr_refund_expired уже после того, как человек выбрал покупку и ввёл сумму.

3. Посмотреть покупки

curl https://api.apipay.kz/api/v1/qr-refunds/42/operations -H "X-API-Key: ВАШ_КЛЮЧ"
{
  "client_name": "Иван И.",
  "operations": [
    { "ref": "…", "amount": 500, "date": "2026-07-25T16:12:52+05:00", "returnable": "full" },
    { "ref": "…", "amount": 10,  "date": "2026-07-25T16:16:54+05:00", "returnable": "partial" },
    { "ref": "…", "amount": 10,  "date": "2026-06-30T20:19:31+05:00", "returnable": "none" }
  ],
  "has_more": false,
  "next_cursor": null,
  "remaining_count": 0
}

returnable говорит, что Kaspi разрешает по этой покупке: full — только целиком, partial — можно частично, none — возврат недоступен. Если покупок больше, передавайте next_cursor в ?cursor=.

⚠️ ref непрозрачен и привязан к сессии. Не разбирайте его на части, не показывайте покупателю и не сохраняйте между сессиями — просто возвращайте обратно как есть.

4. Детали покупки

curl https://api.apipay.kz/api/v1/qr-refunds/42/operations/REF -H "X-API-Key: ВАШ_КЛЮЧ"

Приходят доступная к возврату сумма, уже возвращённое, ссылка на чек и позиции корзины — у каждой свой ref и свой остаток.

5. Вернуть деньги

curl -X POST https://api.apipay.kz/api/v1/qr-refunds/42/execute \
  -H "X-API-Key: ВАШ_КЛЮЧ" -H "Content-Type: application/json" \
  -d '{"operation_ref":"REF"}'

  -d '{"operation_ref":"REF","amount":7}'

  -d '{"operation_ref":"REF","items":[{"ref":"ITEM_REF","amount":5}]}'

amount и items взаимоисключимы — вместе их присылать нельзя. У позиции amount необязателен: без него вернётся вся её доступная сумма.

Запрос синхронный, итог приходит сразу:

{
  "id": 42,
  "status": "completed",
  "refunded_amount": "500.00",
  "receipt_url": "https://receipt.kaspi.kz/…",
  "client_name": "Иван И.",
  "completed_at": "2026-07-27T17:20:11+05:00"
}

Частые ошибки

Код Что означает Что делать
qr_refund_not_identified Покупатель ещё не подтвердил возврат Дождитесь customer_identified
qr_refund_expired Срок сессии истёк Начните новый возврат и отправьте свежую ссылку
qr_refund_completed Возврат по этой сессии уже выполнен Не повторяйте: запросите статус и покажите результат оттуда
operation_not_returnable Kaspi не разрешает возврат по этой покупке Выберите другую покупку
refund_amount_exceeds_available Сумма больше доступной к возврату Проверьте available_for_refund в деталях покупки
partial_refund_requires_return_items Эту покупку Kaspi возвращает только по товарам Пришлите items вместо amount
connection_ambiguous У организации несколько активных касс Укажите kaspi_connection_id при старте
При ошибках 422 сессия остаётся живой — можно повторить с другой покупкой или суммой, не начиная заново.

    Как проверить без реального покупателя

    В песочнице реальный QR никому не уходит, а шаги покупателя двигаются вручную:

    curl -X POST https://api.apipay.kz/api/v1/qr-refunds/42/simulate \
      -H "X-API-Key: ВАШ_КЛЮЧ" -H "Content-Type: application/json" \
      -d '{"event":"identified"}'
    
      -d '{"event":"expired"}'
    

    После identified отдаются три покупки: 500 ₸ (возврат целиком), 10 ₸ (только по товарам) и одна невозвратная. Неуспех возврата форсируется полем simulate в теле execute:

    { "operation_ref": "REF", "simulate": { "status": "failed", "error_code": "refund_amount_exceeds_available" } }
    

    Вне песочницы поле simulate даёт 403 not_sandbox.

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

    Обычный возврат перестанет работать?

    Нет. POST /invoices/{id}/refund не менялся и работает как раньше. Возврат по QR — запасной путь для случаев, когда обычный не проходит.

    Покупатель может подтвердить с чужого телефона?

    Открыть ссылку — да, но сканировать нужно тем Kaspi, которым он оплачивал. С другого аккаунта его покупка просто не найдётся, и список придёт пустым.

    Сколько живёт ссылка?

    Минуты — точное значение в expires_at. Поэтому не создавайте сессию заранее: сначала убедитесь, что покупатель у телефона.

    Что будет, если продавец нажмёт «вернуть» дважды?

    Второй запрос вернёт qr_refund_completed. Это не ошибка возврата: деньги уже вернулись, просто запросите статус сессии и покажите результат оттуда.

    Можно вернуть несколько покупок за одну сессию?

    Нет. Одна сессия — один возврат. Для второй покупки начните новый возврат.

    Приходят ли вебхуки?

    Да: qr_refund.identified при подтверждении покупателем, qr_refund.completed при успешном возврате и qr_refund.expired при истечении срока.

    Интеграция с ИИ

    Не настраивайте вручную — поручите ИИ

    Скопируйте готовый промпт и вставьте своему ИИ-ассистенту — он подключит приём платежей примерно за 15 минут. Настраивает ИИ, а не вы.

    Остались вопросы — напишите нам в WhatsApp: +7 708 516 74 89. Отвечаем быстро, без звонков.

    Написать в WhatsApp

    Для разработчика. Полная спецификация API — apipay.kz/docs. Эта статья в Markdown — /guides/vozvrat-po-qr-cherez-api.md. Справочник фактов для ИИ-агента — apipay.kz/llms.txt.