Почему всего 5 минут — это не баг?
QR-счёт по механике — тот же QR, который кассир показывает на терминале: Kaspi генерирует короткоживущий платёжный токен под конкретную оплату «здесь и сейчас». ApiPay не может продлить его — таково устройство Kaspi. Пять минут — это qr_expires_at в ответе API; фактический терминальный статус (expired) выставляется, когда сам Kaspi признаёт токен истёкшим. Закладывайтесь на около 5 минут и показывайте покупателю таймер.
Если ваш сценарий — «отправлю ссылку, клиент оплатит, когда увидит», QR не подходит по определению: к моменту, когда клиент откроет сообщение, ссылка будет мертва. Для этого сценария существует счёт по номеру телефона: он живёт 24 часа, а покупатель получает push прямо в приложении Kaspi — см. «Как создать счёт по номеру».
Что на самом деле значит «Попробуйте позже»?
Когда покупатель сканирует истёкший QR, приложение Kaspi показывает «Попробуйте позже» — формулировка сбивает с толку: кажется, что «сервис лежит, надо подождать». Реальность: ждать бесполезно, нужен новый QR. Типовая хроника: QR создали, отправили клиенту в мессенджер, клиент открыл через 20 минут — «Попробуйте позже». Решение — создавать QR только в момент, когда покупатель готов платить, либо использовать счёт по номеру.
Редкие другие причины того же экрана: временный сбой/троттлинг на стороне Kaspi. Отличить просто: свежесозданный (моложе 5 минут) QR тоже не открывается — тогда подождите 1–2 минуты и создайте новый.
Как создать QR-счёт
curl -X POST https://api.apipay.kz/api/v1/invoices/qr \
-H "X-API-Key: ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"description": "Заказ №123"
}'
Ответ — сразу 201 со статусом pending (QR-создание синхронное, в отличие от счёта по номеру):
{
"id": 1,
"amount": "5000.00",
"status": "pending",
"qr_token_url": "https://qr.kaspi.kz/...",
"qr_image_url": "https://.../storage/qr/abc.png",
"qr_expires_at": "2026-07-02T12:05:00+05:00"
}
qr_image_url— готовая PNG-картинка QR на хранилище ApiPay: показывайте её как есть, рендерить QR самим не нужно.qr_token_url— платёжная ссылка Kaspi: её можно открыть на телефоне напрямую (без сканирования).- Телефон покупателя не нужен — в этом смысл QR-формата.
- В песочнице доступно поле
simulate: paid | cancelled | expired— протестировать все исходы без реальной оплаты.
Ограничения QR-счёта, о которых надо знать заранее
- Описание — до 100 символов. Больше — ошибка «Описание QR-счёта не должно превышать 100 символов». У счёта по номеру лимит 500.
- Частота. При слишком частом создании QR на организацию —
429 qr_rate_limit, подождите и повторите. - Ошибки создания: временная ошибка авторизации Kaspi (переподключите в Настройках → «Авторизация Kaspi»),
502 kaspi_error(ошибка на стороне Kaspi),500 qr_render_failed(не удалось сформировать картинку — повторите запрос). - Pending-вебхука нет: статус
pendingвы уже получили синхронно в201; следующий вебхук будетpaid/cancelled/expired.
Можно ли держать несколько активных QR одновременно?
Да. QR-счета сосуществуют: создание нового QR не отменяет предыдущие, каждый созданный QR оплачиваем до своего истечения. Два параллельных запроса на создание оба получат 201. Если в старых материалах вы встречали «один активный QR на кассира», «409 superseded» или «новый QR отменяет старый» — это описание давно изменённого поведения, не используйте его: cancelled по QR-счёту теперь означает только реальную отмену покупателем (закрыл оплату или свернул приложение, не подтвердив), а не вытеснение новым QR.
Практическое следствие: для нескольких покупателей в очереди можно спокойно создавать по QR каждому.
Как узнать, что покупатель отсканировал QR?
Событие invoice.qr_scanned приходит вебхуком один раз на QR: статус счёта остаётся pending, добавляется маркер qr_substate: "scanned". Это удобно для UI кассы («Клиент сканирует…»), но помните: скан — не оплата. После скана возможны и paid, и cancelled (покупатель закрыл или свернул приложение) — интерфейс должен уметь вернуться из «Ожидается подтверждение» в исходное состояние. Настройка вебхуков — «Как настроить вебхуки ApiPay».
QR или счёт по номеру: как выбрать?
| Критерий | QR-счёт | Счёт по номеру |
|---|---|---|
| Время жизни | ~5 минут | 24 часа |
| Покупатель | Рядом, платит сейчас (касса, витрина, самовывоз, оплата на сайте «здесь и сейчас») | Удалённо, оплатит когда увидит push |
| Нужен ли номер телефона | Нет | Да (формат 8XXXXXXXXXX, номер должен быть в Kaspi) |
| Описание | ≤100 символов | ≤500 символов |
| Создание | Синхронное: 201 pending + QR сразу |
Асинхронное: 201 processing, затем вебхук |
| Канал доставки | Экран/картинка/ссылка | Push в приложении Kaspi |
Простое правило: покупатель перед вами (или перед экраном) — QR; покупатель «где-то там» — счёт по номеру.
Частые ошибки
- Отправлять QR-ссылку в мессенджер «на потом». Через 5 минут она мертва. Для «потом» — счёт по номеру (24 часа).
- Показывать QR без таймера. Покупатель должен видеть, сколько осталось; по истечении — кнопка «Создать новый QR».
- Трактовать «Попробуйте позже» как сбой сервиса. В 9 случаях из 10 это истёкший QR.
- Считать скан оплатой.
qr_scanned— промежуточное событие; ждитеpaid. - Писать описание длиннее 100 символов. Валидация отклонит запрос.
- Пытаться «продлить» QR. Механизма продления нет; создавайте новый — это дёшево и мгновенно.
Вопросы и ответы
Можно ли продлить время жизни QR-счёта?
Нет. ~5 минут — устройство платёжного QR Kaspi (как на кассе). Нужно дольше — используйте счёт по номеру телефона (24 часа).
Почему покупатель видит «Попробуйте позже»?
Почти всегда QR истёк — создайте новый и попросите оплатить сразу. Если не открывается даже свежий QR — у Kaspi временный сбой, подождите 1–2 минуты.
Новый QR отменяет предыдущий?
Нет. QR-счета сосуществуют, все оплачиваемы до своего истечения. Старые описания про «409 superseded» устарели.
Что значит cancelled у QR-счёта?
Покупатель реально отменил оплату: явно закрыл её или свернул приложение, не подтвердив. Это не «вытеснение» новым QR.
Есть ли у QR-счёта вебхук о создании?
Нет — pending вы получаете синхронно в ответе 201. Дальше приходят qr_scanned (если сканировали) и терминальный paid/cancelled/expired.
Как протестировать QR без реальных денег?
В песочнице: поле simulate со значением paid, cancelled или expired в запросе создания — придут те же вебхуки, что и в бою (с is_sandbox: true).