Qut Pay Сайт Кабинет База знаний Инструкции Документация API ҚАЗРУС
ГлавнаяБаза знаний → Справочник

Симуляция оплаты в песочнице

Обновлено: 2026-09-14 · Версия в Markdown

Коротко

В песочнице настоящего Kaspi нет, поэтому оплату покупателя вы имитируете сами:

POST /api/v1/invoices/{id}/simulate
{ "status": "paid" }

Дальше счёт ведёт себя как при реальной оплате: меняется статус, присваивается номер чека, пишутся события и вебхук уходит точно так же, как в бою. Это позволяет прогнать интеграцию целиком, не рискуя ни одним тенге.

Работает только в песочнице. В боевом режиме вернётся ошибка not_sandbox (HTTP 403).

В какие статусы можно перевести

statusЧто произойдёт
paidСчёт помечается оплаченным, выдаётся номер чека, уходит invoice.paid
failedСчёт закрывается как неоплаченный, уходит invoice.failed
expiredСчёт закрывается по сроку, уходит invoice.expired

Любое другое значение даст invalid_status (HTTP 422).

Перевод применяется к открытому счёту (new, pending). Повторная симуляция по уже закрытому счёту вернёт invoice_not_open (HTTP 409).

Есть одно исключение: счёт в статусе cancelled или expired можно перевести в paid. Это намеренное воспроизведение поздней оплаты — ситуации, когда счёт уже закрыт, а деньги пришли позже. Событие тогда приходит с признаком late: true, ровно как в продакшене. Так и проверяют, что обработчик встречает этот случай правильно: Поздняя оплата.

Что нужно

Кассир Kaspi для песочницы не требуется: Чем песочница отличается от боевого режима.

Kaspi при этом не вызывается вообще. Никаких SMS, настоящих QR и движения денег. Счета песочницы не входят в месячный лимит тарифа и не запускают пробный период.

Как сделать вручную

Есть три способа, результат одинаковый.

Через API:

curl -X POST https://api.qut.kz/api/v1/invoices/inv_…/simulate \
  -H 'X-API-Key: qp_test_…' \
  -H 'Content-Type: application/json' \
  -d '{"status":"paid"}'

В ответ приходит сам обновлённый счёт: status, paidAt, receiptNumber, receiptUrl и остальные поля.

Со страницы оплаты: откройте payUrl счёта песочницы — там есть кнопка, имитирующая оплату. Самый быстрый путь при ручной проверке.

Из кабинета: в разделе Счета откройте карточку счёта — у счёта песочницы доступно действие симуляции.

Цикл проверки

Короткий порядок проверки интеграции:

  1. Создайте счёт. POST /api/v1/invoices ключом qp_test_…. Сохраните id и payUrl из ответа.
  2. Убедитесь, что адрес вебхука зарегистрирован. Кабинет → Интеграции. В песочнице требования к адресу мягче, но он должен быть доступен.
  3. Сделайте симуляцию. POST /invoices/{id}/simulate с {"status":"paid"}.
  4. Проверьте, что вебхук дошёл. В своём журнале: пришло ли invoice.paid, прошла ли проверка подписи, ответили ли вы 2xx.
  5. Перечитайте счёт. GET /invoices/{id} — должен быть status: "paid" и заполненный receiptNumber.
  6. Повторите для остальных веток: новый счёт → failed, ещё один → expired, ещё один отмените и затем переведите в paid (поздняя оплата).

Если вебхук не приходит, причина обычно не в симуляции: Вебхук не приходит.

Автотесты

Симуляция и сделана ради автотестов. Каркас одного теста:

const API = 'https://api.qut.kz/api/v1';
const H = { 'X-API-Key': process.env.QUTPAY_TEST_KEY, 'Content-Type': 'application/json' };

// 1. счёт
const inv = await (await fetch(`${API}/invoices`, {
  method: 'POST', headers: { ...H, 'Idempotency-Key': `test-${Date.now()}` },
  body: JSON.stringify({ amount: 100, description: 'Тест', externalOrderId: 'T-1' }),
})).json();

// 2. имитируем оплату
await fetch(`${API}/invoices/${inv.id}/simulate`, {
  method: 'POST', headers: H, body: JSON.stringify({ status: 'paid' }),
});

// 3. проверяем результат
const after = await (await fetch(`${API}/invoices/${inv.id}`, { headers: H })).json();
if (after.status !== 'paid') throw new Error(`ожидался paid, пришёл ${after.status}`);

Если хотите проверять и вебхук, есть два подхода:

Чтобы тест был повторяемым, соблюдайте два условия: на каждом прогоне создавайте новый счёт (один и тот же нельзя оплатить дважды) и меняйте значение Idempotency-Key.

Ошибки

ОшибкаHTTPПричина
not_sandbox403Организация в боевом режиме или счёт боевой
invalid_status422В status допустимы только paid, failed, expired
invoice_not_open409Счёт уже закрыт (и это не случай поздней оплаты в paid)
invoice_not_found404Неверный идентификатор или счёт чужой организации
insufficient_scope403У ключа нет права invoices:write

Полный список: Каталог ошибок.

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

Можно ли симулировать боевой счёт? Нет, никогда. В боевом режиме эндпоинт возвращает not_sandbox, и это сделано намеренно.

Вебхук после симуляции такой же, как настоящий? Да, полностью: те же имена событий, та же схема подписи, тот же порядок повторов при ответе не-2xx.

Получает ли счёт песочницы чек? Да, номер чека присваивается и страница чека создаётся, но на ней стоит пометка SANDBOX. Фискальный чек при этом не выбивается.

Можно ли симулировать возврат? Отдельная симуляция не нужна: по счёту, доведённому в песочнице до paid, вызывайте обычный возврат.

Подойдёт ли этот цикл для ИИ-агента? Да, именно он удобен агенту для самопроверки: Поручить интеграцию ИИ-агенту.

Связанные статьи

Чем песочница отличается от боевого режимаВ песочнице Kaspi не вызывается вообще, реальных денег нет и кассир не нужен — оплату вы симулируете сами. Полное сравнение двух режимов и что проверить при переходе в боевой.Вебхук не приходит — как найти причинуСчёт оплачен, а на ваш сервер уведомление не пришло. С чего начать диагностику, какая причина встречается чаще всего и как проверить её одним запросом.Поздняя оплата — счёт закрыт, а деньги пришлиНа отменённый или просроченный счёт деньги могут прийти с опозданием. В этом случае событие invoice.paid приходит с меткой late: true. Что делать и как заранее подготовить к этому код.Поручить интеграцию ИИ-агентуClaude, Cursor или другой ИИ-агент может написать интеграцию с Qut Pay сам. Что ему дать, как устроен автономный цикл в песочнице и почему боевой ключ агенту давать нельзя.Каталог ошибок — что возвращает API и что делатьВсе основные коды ошибок Qut Pay API по группам: авторизация, привязка Kaspi, счета, возвраты, лимиты тарифа, вебхуки, подписки. Причина и решение для каждой.

Остались вопросы? WhatsApp +77788813333 · kazprose@gmail.com
Написать можно и из кабинета: Поддержка.

Qut Pay — независимый сервис, не аффилирован с АО «Kaspi Bank». Kaspi и Kaspi Pay — товарные знаки их правообладателя.