Чем песочница отличается от рабочего режима и как перейти без потерь

Обновлено 6 июля 2026 · Начало работы · Версия в Markdown
Содержание
  1. Что такое песочница и зачем она нужна?
  2. Клиент не получил счёт? Сначала проверьте режим
  3. Как включить рабочий режим
  4. Что удаляется при переходе в рабочий режим
  5. Технические отличия песочницы (для разработчиков)
  6. Полный цикл прогона в песочнице (для разработчиков)
  7. Возврат в песочнице: по-настоящему, а не симуляцией
  8. QR-счёт: создание и отсканирование в песочнице
  9. Проверка номера телефона: sandbox-моки
  10. Чек-лист выхода в рабочий режим
  11. Вопросы и ответы

Что такое песочница и зачем она нужна?

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

Оплату тестового счёта вы имитируете сами: в личном кабинете отметьте счёт оплаченным или отменённым, а из кода — методом simulate-status (подробный прогон — в разделе для разработчиков ниже). Так проверяются все ветки: успех, отмена, истечение, ошибка. Тот же цикл для автономного ИИ-агента — «Интеграция с ApiPay с помощью ИИ».

Клиент не получил счёт? Сначала проверьте режим

Счёт создаётся, а оплата в Kaspi не приходит — проверьте три признака тестового режима:

  1. Плашка в кабинете. Вверху горит «ТЕСТОВЫЙ РЕЖИМ … Счета не передаются в Kaspi» — это он.
  2. Ответ API. У счёта is_sandbox: true.
  3. Номер счёта. kaspi_invoice_id начинается с SANDBOX-.

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

Как включить рабочий режим

  1. Убедитесь, что кассир подключён: Настройки → «Авторизация Kaspi» (какой номер подойдёт — в статье «Требования к номеру кассира»). Без кассира счетам физически не через что уходить в Kaspi.
  2. В Настройках переключите режим на «Рабочий».
  3. Выставьте пробный счёт на свой личный номер и оплатите на минимальную сумму — убедитесь, что push пришёл и вебхук отработал.

Переключение обратимо, аккаунт, API-ключ и настройки вебхука не меняются: отдельного «sandbox-ключа» в ApiPay нет. Но каждый выход в рабочий режим стирает sandbox-данные организации — что именно, в разделе ниже; выгружайте нужное до переключения.

Ещё три правила переключателя:

  • менять режим можно не чаще одного раза в 5 минут — кабинет покажет, сколько осталось ждать; повтор того же значения ничего не меняет и sandbox-данные не удаляет
  • включить тестовый режим может только верифицированная организация
  • для выхода в рабочий режим нужна живая привязка кассира: если она истекла — переподключите кассира, если проверка не прошла — повторите позже; в обоих случаях режим не меняется и sandbox-данные остаются на месте

С момента активации рабочего доступа у вас есть 3 бесплатных дня триала, дальше — тариф. Подробности — «Тестовый период».

Что удаляется при переходе в рабочий режим

У обычного клиента (личный кабинет apipay.kz) режим — переключатель, API-ключ и настройки вебхука не меняются. Но данные пропадают: каждый переход в рабочий режим удаляет sandbox-данные организации — тестовые счета, возвраты по ним, тестовые подписки и тестовые позиции каталога. Всё, что нужно сохранить, выгружайте до переключения. Боевые счета, возвраты и подписки не затрагиваются.

У партнёров сверх этого есть свой сценарий. Тестовые организации — отдельные сущности со своими API-ключами. При переводе партнёрского аккаунта в production все тестовые организации удаляются полностью — вместе с их тестовыми счетами, возвратами, подписками — а их API-ключи деактивируются. Восстановлению они не подлежат: не храните в тестовых организациях ничего ценного, держите конфигурацию (URL вебхука, соответствия «ваш клиент → организация ApiPay») в своей системе и планируйте перевыпуск ключей для боевых организаций. Тестовая организация архитектурно не может стать боевой: её ключи с боевой никогда не заработают. Подробнее о партнёрском контуре — «Партнёрский API».

Если после перехода «ключ перестал работать» — проверьте два случая: ключ принадлежал удалённой тестовой организации либо был перевыпущен повторным вызовом (повторная выдача ключа организации перегенерирует его и перезаписывает Webhook URL с секретом — старый ключ умирает мгновенно).

Технические отличия песочницы (для разработчиков)

  • Отмена QR-счёта: в песочнице проходит (200, статус cancelled), в рабочем режиме — 409 qr_cancel_unsupported. Логику отмены калибруйте не по песочнице.
  • Вебхуки-ретраи короче: 3 попытки вместо 11 — медленно «просыпающийся» endpoint в тесте может не дождаться повтора.
  • Подписки в песочнице — не больше 10 на организацию, превышение → 400 sandbox_subscription_limit. Создание подписки в любом режиме требует верифицированной организации, иначе 403.
  • Имена тестовых организаций генерируются автоматически (вида «ТОО Синие птицы») — так их не спутаешь с боевыми.
  • Настройка вебхуков, HMAC-подпись и локальное тестирование — «Настройка вебхуков ApiPay» и страница /local-testing.

Полный цикл прогона в песочнице (для разработчиков)

Прогоните перед продом весь путь реальными запросами: создать счёт → дождаться pending → симулировать статус → проверить вебхук → сделать возврат. Каждый шаг — обычный запрос к публичному API (https://api.apipay.kz/api/v1, заголовок X-API-Key), без единого телефонного номера живого человека.

Главный нюанс порядка — processing → pending перед симуляцией. POST /invoices (счёт по номеру) обрабатывается асинхронно: 201 приходит со status: "processing". Метод simulate-status переводит счёт из pending, поэтому сразу после создания симулировать нельзя — сначала опросите GET /invoices/{id} (лимит 1000/мин, читает из кэша) до status: "pending". Симуляция не-pending счёта вернёт 400 invalid_status_transition (в ответе — current_status и allowed_from: ["pending"]).

BASE="https://api.apipay.kz/api/v1"
KEY="YOUR_API_KEY"

ID=$(curl -s -X POST "$BASE/invoices" -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" \
  -d '{"amount":10000,"phone_number":"87770000001","description":"Sandbox test"}' | jq -r '.id')

until [ "$(curl -s "$BASE/invoices/$ID" -H "X-API-Key: $KEY" | jq -r '.status')" = "pending" ]; do sleep 1; done

curl -s -X POST "$BASE/invoices/$ID/simulate-status" -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" -d '{"status":"paid"}'

curl -s "$BASE/webhook-logs?invoice_id=$ID&event=invoice.status_changed" \
  -H "X-API-Key: $KEY" | jq '.data[] | {event, status, response_status}'

curl -s -X POST "$BASE/invoices/$ID/refund" -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" -d '{"amount":5000,"reason":"Sandbox refund"}'

Метод POST /invoices/{id}/simulate-status — только для тестовых счетов (is_sandbox: true; боевой счёт → 403 not_sandbox), свой лимит 60 запросов/мин на ключ (общий бюджет 200/мин не тратит). Тело — { "status": <enum> }; опционально kaspi_source_type (GOLD|RED|LOAN|BUSINESSACCOUNT|BANKINTEGRATIONACCOUNT) и kaspi_sale_type (Remote|QR|Static|Restaurant); при paid без них они выбираются случайно.

status Что происходит со счётом Какой вебхук уходит Условия / ошибки
paid → терминальный paidpaid_at) invoice.status_changed (paid) из pending
cancelled → терминальный cancelled invoice.status_changed (cancelled) из pending
expired → терминальный expired invoice.status_changed (expired) из pending
error → терминальный error, error_code: sandbox_simulated_error, error_message (свой текст параметром error_message ≤255; дефолт «Симулированная ошибка (sandbox).») invoice.status_changed (error) — как при реальной ошибке из pending
qr_scanned остаётся pending (транзиентное суб-событие) invoice.qr_scanned (qr_substate: "scanned") только QR-счёт: не-QR → 400 not_qr_invoice; повтор → 400 already_scanned

Критерий успеха каждого шага — в GET /webhook-logs по нужному event появляется запись status: success.

Возврат в песочнице: по-настоящему, а не симуляцией

Возврат по счёту на номер делают в песочнице реальным запросом POST /invoices/{id}/refund — своей симуляции у него нет. Возврат возможен только у счёта в paid, поэтому порядок такой: simulate-status paid → затем refund (параметры запроса — «Возвраты Kaspi через API»).

У возврата по QR ветка другая (см. «Возврат по QR»), и симуляция там есть: сессию POST /qr-refunds двигают ручкой POST /qr-refunds/{id}/simulateevent: identified (покупатель отсканировал возвратный QR, client_name = «Иван И.») или event: expired (QR просрочен). «Ещё не отсканировал» — это начальное состояние awaiting_scan, его достаточно прочитать через GET /qr-refunds/{id}. На боевой сессии ручка отдаёт 403 not_sandbox.

В песочнице бэкенд сам доставляет вебхуки возврата: invoice.refunded (со status: completed либо failed) и — на первый частичный возврат — дополнительно invoice.status_changed со status: partially_refunded. Проверить возвраты по счёту — GET /invoices/{id}/refunds. Нюанс ретраев: refund-вебхуки в песочнице ретраятся по полной боевой сетке (11 попыток), в отличие от invoice-вебхука (3 попытки в песочнице).

QR-счёт: создание и отсканирование в песочнице

  • Создать QR-счётPOST /invoices/qr: запрос синхронный, 201 приходит сразу со status: "pending" и QR-полями (отдельного pending-вебхука у QR нет).
  • Sandbox-шорткат: в теле POST /invoices/qr можно передать simulate: paid|cancelled|expired — QR-счёт создастся сразу в терминальном статусе с мгновенной отправкой вебхука (когда сам скан проверять не нужно).
  • Отсканирование: чтобы проверить событие invoice.qr_scanned, вызовите simulate-status со status: qr_scanned (только для QR; статус остаётся pending, один раз — повтор даст 400 already_scanned). После скана транзиентно возможны и paid, и cancelled — симулируйте нужную ветку следом.

Проверка номера телефона: sandbox-моки

POST /clients/check в песочнице не ходит в Kaspi и ничего не пишет в кэш/счётчики — отдаёт детерминированный ответ ровно по двум номерам:

  • 87770000001{ "has_kaspi": true, "client_name": "Иван И." }
  • 87770000002{ "has_kaspi": false, "client_name": null }
  • любой другой валидный номер → { "has_kaspi": false, "client_name": null }

Это единственные телефонные значения, которые стоит использовать в тестах.

Чек-лист выхода в рабочий режим

Перед переключением убедитесь, что в песочнице прошло:

  • [ ] POST /invoices → счёт дошёл до pending (поллинг GET /invoices/{id} работает).
  • [ ] Симулированы все терминалы — paid, cancelled, expired, error — и по каждому в GET /webhook-logs есть status: success с нужным event.
  • [ ] Для QR (если используете): qr_scanned → затем paid/cancelled.
  • [ ] Возврат: simulate-status paidrefund → пришёл invoice.refunded (completed), а первый частичный дал partially_refunded.
  • [ ] Приёмник вебхуков проверяет подпись и отвечает 2xx быстрее 5 секунд (детали — «Настройка вебхуков ApiPay»).
  • [ ] Обработчик дедуплицирует по (invoice.id, invoice.status) и идемпотентен.
  • [ ] Ветки ошибок разобраны по error_code (включая виденный sandbox_simulated_error).
  • [ ] Всё, что нужно из песочницы, выгружено — переход его удалит.

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

Оплатил тариф, а счета всё ещё SANDBOX-.

Оплата тарифа режим не переключает — переключите его сами в Настройках.

Идёт ли триал, пока я в песочнице?

Нет. 3 бесплатных дня относятся к рабочему доступу; песочница бесплатна независимо от них.

Нужен ли отдельный API-ключ для песочницы?

Нет. У клиента личного кабинета ключ один и тот же для обоих режимов — переключается только режим. Отдельные тестовые ключи есть только у тестовых организаций партнёров, и они удаляются при переходе в production.

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

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

Скопируйте готовый промпт и вставьте своему ИИ-ассистенту — он настроит интеграцию за вас: получит доступы, создаст тестовый счёт в песочнице и подключит вебхуки. Техническую часть делает ИИ, а не вы.

Остались вопросы — напишите нам в WhatsApp: +7 700 307 65 12. Отвечаем быстро, без звонков.

Написать в WhatsApp

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