# Оплата подтверждается медленно — почему и что делать

> Покупатель заплатил, а счёт не сразу становится paid. Как частота проверки зависит от возраста счёта, сколько это занимает на практике и что делать в сценариях, чувствительных к задержке.

## Коротко

Мы опрашиваем Kaspi о статусе счетов, и этот опрос идёт по очереди, а не мгновенно. Поэтому между моментом, когда покупатель подтвердил оплату, и моментом, когда ваша система узнала об этом, всегда есть небольшая задержка. **На практике webhook обычно приходит в течение пяти секунд.** Если ваш сценарий чувствителен к задержке (турникет, шлагбаум, вендинг, прямой эфир), ведите два канала параллельно: ждите webhook и одновременно опрашивайте `GET /api/v1/invoices/{id}` — что придёт первым, то и принимайте.

## Как устроена проверка

Poller — внутренний процесс, который спрашивает у Kaspi статусы счетов. Он делает круг **каждые 3 секунды**, но за один круг опрашивает не все открытые счета: частота зависит от возраста счёта.

| Возраст счёта | Как часто проверяется |
|---|---|
| Младше 3 минут | Каждый круг, то есть максимально часто |
| От 3 до 30 минут | Примерно раз в 20 секунд |
| Старше 30 минут | Примерно раз в 90 секунд |

Очередь начинается с самого свежего счёта. Логика простая: только что выставленный счёт с большой вероятностью оплачивают прямо сейчас, а счёт часовой давности чаще всего уже не оплатят. Поэтому новые счета подтверждаются быстрее всего.

Практический вывод: **если покупатель идёт платить сразу после выставления счёта, подтверждение будет самым быстрым.** Если счёт выставили заранее, а оплатили через полчаса, подтверждение может задержаться до 90 секунд.

## Сколько это занимает на практике

В обычной ситуации после того, как покупатель отсканировал QR и подтвердил оплату в Kaspi, событие `invoice.paid` приходит на ваш webhook-адрес **примерно за пять секунд**. Это наблюдаемое среднее, а не гарантия: нагрузка на стороне Kaspi и состояние сети могут его увеличить.

**Важная деталь.** Kaspi не передаёт в payload точное время оплаты. То есть измерить промежуток «от подтверждения покупателем до получения нами» по данным Kaspi **невозможно** — такого поля просто нет. Любой расчёт «реальной задержки» будет оценкой. На своей стороне вы можете измерить только интервал между созданием счёта и приходом webhook, а в него входит ещё и время, пока покупатель раздумывал.

## Сценарии, чувствительные к задержке

Если сразу после подтверждения оплаты должно произойти физическое действие — открыться шлагбаум, автомат выдать товар, в эфире подтвердиться заказ — ведите два канала параллельно:

1. **Webhook** — основной канал. Вы узнаёте о смене статуса, ничего не спрашивая.
2. **`GET /api/v1/invoices/{id}`** — резервный канал. Первые 1-2 минуты после создания счёта опрашивайте статус раз в несколько секунд.

Что первым скажет «paid», то и принимайте, и выполняйте действие. Чтобы не выполнить его дважды, когда придёт второй канал, обработка должна быть идемпотентной: записывайте в журнал пару `(id счёта, статус)` и, если такая пара уже обработана, ничего не делайте.

Не опрашивайте вечно. Разумный режим: первые 2 минуты — раз в 2-3 секунды, дальше реже, а после истечения `expiresAt` остановиться. При слишком частых запросах вы получите `rate_limited` или `request_rate_limited`.

## Что задержкой не является

Не всякая задержка — вина poller. Сначала исключите вот это:

| Признак | Что происходит на самом деле |
|---|---|
| Счёт вообще не становится paid | Покупатель ещё не заплатил или вы в тестовом режиме |
| Webhook не приходит, но в кабинете счёт paid | Проблема на стороне webhook: [Вебхук не приходит](/kb/ru/webhook-not-arriving) |
| В кабинете статус сразу, а в вашей системе поздно | Забита ваша собственная очередь, проверьте свою обработку |
| Счёт закрыт, а деньги пришли | Это поздняя оплата, приходит с признаком `late: true` |
| Денег не видно на счёте Kaspi | Это другая тема: [Когда и куда приходят деньги](/kb/ru/money-arrival) |

Отдельно про позднюю оплату: деньги могут прийти по счёту в статусе `cancelled` или `expired`, и тогда событие `invoice.paid` придёт позже с признаком `late: true`. Не игнорируйте такой счёт — либо окажите услугу, либо верните деньги.

## В песочнице задержки нет

В режиме песочницы Kaspi не вызывается, статус вы ставите сами через `POST /api/v1/invoices/{id}/simulate`. Поэтому там подтверждение мгновенное. Это особенность тестовой среды — не оценивайте по песочнице скорость боевого режима.

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

**Можно ли изменить частоту работы poller?** Нет, она общая для всех и подстраивается под возраст счёта автоматически.

**Почему один счёт подтвердился за 3 секунды, а другой за 40?** У них был разный возраст. Свежий счёт проверяется каждый круг, а получасовой — раз в 90 секунд.

**Если я буду чаще опрашивать статус, подтверждение ускорится?** Немного да: ваш запрос `GET /invoices/{id}` не зависит от очереди poller. Но не перебарщивайте, иначе получите 429. Лучший вариант — два канала параллельно.

**Можете назвать точную задержку в секундах?** Поскольку Kaspi не передаёт время оплаты, промежуток «от подтверждения покупателем до нас» не может измерить никто. Измеримо только время от создания счёта до прихода webhook.

**Что показывать покупателю, пока идёт ожидание?** После создания счёта покажите экран ожидания и опрашивайте статус в фоне. Когда придёт подтверждение — смените экран. Технические тексты ошибок покупателю показывать не надо.
