Коротко
В большинстве случаев причина одна: истекло окно сканирования QR. Каждый QR выпускается под конкретный счёт и живёт недолго — около трёх минут. Решение: создать новый счёт и показать новый QR. Обновить, продлить или оживить старый QR нельзя — окно задаёт Kaspi, и повлиять на него мы не можем.
Почему так
QR здесь — не постоянный «QR магазина», а временный код под конкретный счёт. В ответе на создание счёта приходят qrUrl, qrImageUrl, payUrl, deepLink и expiresAt. Последнее поле и говорит, до какого момента этот QR действителен.
После истечения окна покупатель при сканировании не попадает на экран оплаты: вместо него появляется сообщение вида «попробуйте позже». Это не поломка приложения Kaspi у покупателя и не ошибка ваших настроек.
Не зашивайте срок в код константой. Kaspi может изменить окно. Читайте expiresAt и стройте таймер по нему — тогда код подстроится сам.
По симптому
| Что видите | Вероятная причина | Что делать |
|---|---|---|
| Покупатель отсканировал, появилось «попробуйте позже» | Окно истекло | Создайте новый счёт |
| QR долго висел на экране, потом его отсканировали | Та же причина | Показывайте QR, когда покупатель готов платить |
| Напечатанный и повешенный на стену QR перестал работать | QR счёта не предназначен для печати | Используйте ссылку на оплату |
Счёт всё ещё pending, но QR не работает | Окно кончилось, счёт ещё не закрыт | Создайте новый счёт, старый закройте |
| QR вообще не открывается, картинка пустая | Счёт не создан, вернулась ошибка | Посмотрите код ошибки в ответе API |
| В песочнице QR не принимает реальную оплату | Тестовый режим | Оплата не приходит покупателю |
Что делать
- Закройте старый счёт через
POST /api/v1/invoices/{id}/cancel— тогда покупатель позже не оплатит его случайно - Создайте новый счёт на ту же сумму
- Покажите покупателю новый
qrImageUrlилиpayUrl
Если покупатель стоит у кассы, это работа на несколько секунд. Для удобства выведите на экран кассы кнопку «Обновить QR», которая создаёт новый счёт и показывает новый код.
Печатный QR и QR на экране
Путать их — частая ошибка.
| QR счёта | Ссылка на оплату | |
|---|---|---|
| Для чего | Один конкретный платёж на конкретную сумму | Постоянный адрес для многоразового использования |
| Срок жизни | Короткое окно, expiresAt | Работает, пока вы её не остановите |
| Можно печатать | Нет | Да |
| Сумма | Задаётся при создании счёта | Фиксированная или открытая |
Если вам нужен постоянный QR — повесить на стену, напечатать в меню, наклеить на стикер — используйте ссылку на оплату: qut.kz/p/<slug>. QR такой ссылки не устаревает, потому что ведёт не на счёт, а на саму ссылку: счёт создаётся в момент, когда покупатель её открыл. Есть и ссылки с открытой суммой — её вводит сам покупатель.
Динамический QR на экране хорошо работает на офлайн-точке: кассир вводит сумму, на экране появляется QR, покупатель сразу его сканирует.
Как не упираться в окно
- Создавайте QR в последний момент. Не при открытии формы заказа, а когда покупатель нажал «Оплатить».
- Показывайте таймер. Посчитайте оставшееся время из
expiresAtи покажите его покупателю. - По истечении окна выводите кнопку. «QR устарел, создать новый» — покупатель нажмёт сам.
- Если покупателя нет рядом, берите счёт вида
phone. Счёт на телефон живёт дольше, уведомление можно открыть и оплатить позже. - Не отправляйте картинку QR почтой или в мессенджер. Пока письмо откроют, окно закончится — отправляйте ссылку на оплату.
Вопросы и ответы
Можно ли продлить окно QR? Нет. Окно задаёт Kaspi, изменить его мы не можем.
Сколько именно минут? Около трёх, но точное значение берите из expiresAt. Константу в коде не пишите.
Если окно кончилось, счёт сразу станет expired? Не обязательно: окно сканирования QR и общий статус счёта — разные вещи. Счёт может оставаться pending, а QR уже не работать. Подробнее: Счёт завис в статусе pending.
А если покупатель всё же оплатит по старому QR? После окна оплата не проходит. Если оплата всё-таки случится, вам придёт событие invoice.paid с пометкой late: true — тогда либо оказывайте услугу, либо возвращайте деньги.
Можно ли показать QR одного счёта дважды? Да, qrImageUrl остаётся прежним. Но после окончания окна он тоже перестаёт работать.