> Источник: https://apipay.kz/guides/pesochnitsa-i-rabochiy-rezhim · Обновлено: 2026-07-06 · apipay.kz
> ApiPay — независимый сервис приёма платежей поверх вашего Kaspi Pay.

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

**TL;DR.** Песочница (тестовый режим) — тренировочный зал ApiPay: счета **не передаются в Kaspi**, покупателю ничего не приходит, оплату вы имитируете сами. Она **бесплатна всегда** и не тратит дни триала; лимит — **1000 тестовых счетов** на организацию. Рабочий режим включается в Настройках одним переключателем, и для обычного клиента он обратим: тот же аккаунт, тот же API-ключ. Если вернулись в тестовый режим — выключайте его сразу после проверки: пока он включён, боевые счета покупателям в Kaspi не уходят и деньги не поступают. Каждый переход в рабочий режим **стирает sandbox-данные организации** — тестовые счета, возвраты по ним, тестовые подписки и позиции каталога; выгружайте нужное заранее. У партнёров сверх этого при переходе в production удаляются **все тестовые организации вместе с их API-ключами**. Как подготовиться — ниже.

## Коротко

| Вопрос | Песочница | Рабочий режим |
|---|---|---|
| Счета уходят в Kaspi | Нет — покупателю ничего не приходит | Да — push в Kaspi покупателю |
| Оплата | Имитируете сами (в кабинете или `simulate`) | Реальные деньги на ваш Kaspi-счёт |
| Цена | Бесплатно всегда | 3 дня триала, затем тариф |
| Признаки счёта | `is_sandbox: true`, `kaspi_invoice_id` начинается с `SANDBOX-` | Обычные номера счетов Kaspi |
| Лимит | 1000 тестовых счетов на организацию | По тарифу |
| Ретраи вебхуков | 3 попытки (через 5 и 15 секунд) | 11 попыток, интервалы до 1 часа |
| API-ключ | Тот же, что и в рабочем (у клиента ЛК) | Тот же ключ |
| Судьба данных при выходе в бой | Тестовые счета, возвраты, подписки и позиции каталога стираются | Боевые данные не затрагиваются |

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

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

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

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

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

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

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

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

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

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

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

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

С момента активации рабочего доступа у вас есть **3 бесплатных дня** триала, дальше — тариф. Подробности — «[Тестовый период](/guides/testovyy-period)».

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

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

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

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

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

- **Отмена QR-счёта:** в песочнице проходит (`200`, статус `cancelled`), в рабочем режиме — `409 qr_cancel_unsupported`. Логику отмены калибруйте не по песочнице.
- **Вебхуки-ретраи короче:** 3 попытки вместо 11 — медленно «просыпающийся» endpoint в тесте может не дождаться повтора.
- **Подписки в песочнице** — не больше **10** на организацию, превышение → `400 sandbox_subscription_limit`. Создание подписки в любом режиме требует верифицированной организации, иначе `403`.
- **Имена тестовых организаций** генерируются автоматически (вида «ТОО Синие птицы») — так их не спутаешь с боевыми.
- Настройка вебхуков, HMAC-подпись и локальное тестирование — «[Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay)» и страница [/local-testing](/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"]`).

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

# 1) создать sandbox-счёт (номер — sandbox-константа, не реальный человек)
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')

# 2) дождаться pending ПЕРЕД симуляцией (сразу после создания счёт в processing)
until [ "$(curl -s "$BASE/invoices/$ID" -H "X-API-Key: $KEY" | jq -r '.status')" = "pending" ]; do sleep 1; done

# 3) симулировать оплату (только песочница)
curl -s -X POST "$BASE/invoices/$ID/simulate-status" -H "X-API-Key: $KEY" \
  -H "Content-Type: application/json" -d '{"status":"paid"}'

# 4) проверить, что вебхук ушёл
curl -s "$BASE/webhook-logs?invoice_id=$ID&event=invoice.status_changed" \
  -H "X-API-Key: $KEY" | jq '.data[] | {event, status, response_status}'

# 5) вернуть по-настоящему (возврат симуляции не требует; счёт должен быть paid)
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` | → терминальный `paid` (с `paid_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](/guides/vozvraty-kaspi-cherez-api)»).

У возврата по QR ветка другая (см. «[Возврат по QR](/guides/vozvrat-po-qr-cherez-api)»), и симуляция там есть: сессию `POST /qr-refunds` двигают ручкой `POST /qr-refunds/{id}/simulate` — `event: 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 paid` → `refund` → пришёл `invoice.refunded` (`completed`), а первый частичный дал `partially_refunded`.
- [ ] Приёмник вебхуков проверяет подпись и отвечает `2xx` быстрее 5 секунд (детали — «[Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay)»).
- [ ] Обработчик дедуплицирует по `(invoice.id, invoice.status)` и идемпотентен.
- [ ] Ветки ошибок разобраны по `error_code` (включая виденный `sandbox_simulated_error`).
- [ ] Всё, что нужно из песочницы, выгружено — переход его удалит.

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

**Оплатил тариф, а счета всё ещё `SANDBOX-`.**
Оплата тарифа режим не переключает — переключите его сами в Настройках.

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

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

Смотрите также: [Требования к номеру кассира](/guides/trebovaniya-k-nomeru-kassira) · [Привязка кассира разорвалась](/guides/pochemu-sletala-sessiya-kassira) · [Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay) · [Безопасность и как работает ApiPay](/guides/bezopasnost-i-kak-rabotaet-apipay) · пиллар «[Как принимать оплату Kaspi через API](/kaspi-api)» · [Локальное тестирование вебхуков](/local-testing).

---

ApiPay — независимый сервис и не аффилирован с АО «Kaspi Bank». Kaspi и Kaspi Pay — товарные знаки их правообладателя.
База знаний: https://apipay.kz/guides
