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

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

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

## Коротко

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

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

Песочница — это режим, в котором ApiPay работает «понарошку»: вы создаёте счета, получаете вебхуки, смотрите статусы — но в Kaspi ничего не уходит, и настоящих денег нет. Это безопасная среда, где можно отладить интеграцию (сайт, Telegram-бот, CRM), не рискуя выставить реальный счёт живому человеку.

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

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

Гоняете цикл автономным ИИ-агентом? Для этого есть отдельное руководство «[Интеграция с ApiPay с помощью ИИ](/for-ai)» — эта статья про ручной прогон человеком.

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

Самый частый сценарий: «всё подключили, счёт создаётся, а оплата в Kaspi не приходит». В 9 из 10 случаев включён тестовый режим. Проверьте три признака:

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 нет.

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

## Тестовые организации и их ключи удаляются — как не потерять настройки

Это самое важное место статьи для тех, кто работает через партнёрский API (платформы, CRM, white-label).

**У обычного клиента** (личный кабинет apipay.kz) всё просто: режим — переключатель, ключ один и тот же, ничего не пропадает.

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

Чтобы переход прошёл без нервов:

- **Не храните ничего ценного в тестовых организациях.** Всё, что там лежит, — расходный материал.
- **Вынесите конфигурацию наружу.** URL вебхука, названия организаций, соответствия «ваш клиент → организация ApiPay» держите в своей системе, а не «в памяти» тестовой среды.
- **Планируйте перевыпуск ключей.** После перехода создайте боевые организации и выпустите для них новые ключи и вебхук-секреты. Полный API-ключ показывается **один раз — при создании**; дальше видна только маска (key_hint). Сразу кладите его в менеджер секретов.
- **Помните про лимиты песочницы:** до 20 тестовых организаций на партнёра и до 500 тестовых счетов на организацию.

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

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

- **Вебхуки-ретраи короче:** в песочнице 3 попытки доставки (через 5 и 15 секунд), в рабочем режиме — 11 попыток с нарастающими интервалами до 1 часа. Если ваш endpoint «просыпается» медленно, в тесте вы можете не дождаться повтора — это отличие среды, а не баг.
- **Симуляция статусов** — метод `simulate-status` (для счёта по номеру) и параметр `simulate` при создании QR-счёта; полный прогон — в разделе «Полный цикл прогона» ниже.
- **Подписки в песочнице** доступны только при верифицированной организации — иначе API вернёт 400.
- **Имена тестовых организаций** генерируются автоматически (вида «ТОО Синие птицы») — так их не спутаешь с боевыми.
- Настройка вебхуков, 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` (полный или частичный `amount`, опц. `reason`, опц. позиционный `return_items[]`). Отдельного `simulate` для возврата **нет**. Возврат возможен только у счёта в `paid` — поэтому порядок такой: `simulate-status paid` → затем `refund`.

В песочнице бэкенд сам доставляет вебхуки возврата: `invoice.refunded` (со `status: completed` либо `failed` + `refund.error_code` при неудаче) и — на **первый частичный** возврат — дополнительно `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 нет), `description` ≤100 символов.
- **Sandbox-шорткат:** в теле `POST /invoices/qr` можно передать `simulate: paid|cancelled|expired` — QR-счёт создастся сразу в терминальном статусе с мгновенной отправкой вебхука (когда сам скан проверять не нужно).
- **Отсканирование:** чтобы проверить событие `invoice.qr_scanned`, вызовите `simulate-status` со `status: qr_scanned` (только для QR; статус остаётся `pending`, один раз — повтор даст `400 already_scanned`). После скана транзиентно возможны и `paid`, и `cancelled` — симулируйте нужную ветку следом.
- Возврат и отмена QR-счёта не поддерживаются — это ограничение самого QR-флоу.

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

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

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

Это единственные телефонные значения, которые стоит использовать в тестах. Формат имени Kaspi — имя и первая буква фамилии с точкой; полная фамилия не отдаётся.

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

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

- [ ] `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`.
- [ ] Приёмник вебхуков проверяет HMAC-подпись по raw body и отвечает `2xx` быстрее 5 секунд (детали — «[Настройка вебхуков ApiPay](/guides/nastroyka-webhookov-apipay)»).
- [ ] Обработчик дедуплицирует по `(invoice.id, invoice.status)` и идемпотентен.
- [ ] Ветки ошибок разобраны по `error_code` (включая виденный `sandbox_simulated_error`).
- [ ] `POST /clients/check` используется точечно, а не перебором.
- [ ] Переключение — в кабинете; после него `is_sandbox` станет `false`, а `simulate-status` перестанет быть доступен — это ожидаемо.

## Частые ошибки

- **Ждать реальный push Kaspi в песочнице.** Не придёт by design — имитируйте оплату сами.
- **Включать рабочий режим до подключения кассира.** Сначала кассир (Настройки → «Авторизация Kaspi»), потом режим.
- **«Оплатил тариф, а счета всё ещё SANDBOX-».** Оплата тарифа не переключает режим сама — проверьте переключатель в Настройках. Если рабочий режим включён, а счета всё равно тестовые — напишите в поддержку, разберём.
- **Партнёру: перейти в production, не сохранив конфигурацию.** Тестовые организации и ключи удалятся штатно — подготовьтесь по чек-листу выше.
- **Потерять API-ключ.** Он показывается один раз при создании. Не записали — перевыпустите ключ (старый перестанет работать).
- **Тестировать «на проде» на счетах клиентов.** Для экспериментов есть песочница — она для этого и существует.

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

**Песочница платная? Сколько можно в ней сидеть?**
Бесплатна всегда, по времени не ограничена. Лимит — 500 тестовых счетов на организацию.

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

**Можно ли вернуться из рабочего режима в тестовый?**
Да, переключение обратимо в обе стороны. Счета, созданные в песочнице, останутся тестовыми навсегда.

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

**Что произойдёт с тестовыми счетами при переходе в рабочий режим?**
У клиента ЛК они останутся в истории с пометкой тестовых. У партнёра при переходе в production тестовые организации удаляются целиком вместе со счетами и ключами.

**Как понять из кода, что счёт тестовый?**
По полю `is_sandbox: true` в ответе API и вебхуке, и по префиксу `SANDBOX-` в `kaspi_invoice_id`.

**Почему сразу после создания счёт нельзя симулировать?**
`POST /invoices` возвращает счёт в статусе `processing`, а `simulate-status` переводит счёт **из `pending`**. Опросите `GET /invoices/{id}` до `pending`, потом симулируйте — иначе получите `400 invalid_status_transition` с `allowed_from: ["pending"]`.

**Как проверить номер телефона в песочнице, не имея реального клиента?**
Используйте детерминированные моки `POST /clients/check`: `87770000001` → `has_kaspi: true` (имя «Иван И.»), `87770000002` → `has_kaspi: false`. Любой другой валидный номер вернёт `has_kaspi: false`. В Kaspi запрос при этом не уходит.

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

---

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