# ADR-0012. Песочница для интеграторов

- Статус: принято, реализовано на этапе 5 (5.6)
- Дата: 2026-10-01

## Контекст

Интеграторам (кассы, CRM, собственные продукты компании) нужна среда, где можно пройти весь сценарий — от
создания мерчанта до чека со списанием баллов по коду — не касаясь реальных людей, денег и ПДн и не рискуя
спутать её с рабочей. Варианты: тестовые мерчанты внутри рабочей базы (как test mode у Stripe) или отдельное
развёртывание.

## Решение

- **Отдельное развёртывание той же сборки** с `LOYAL_SANDBOX=true`: своя база PostgreSQL и роли, свои Redis и
  очереди, свои `APP_KEY`, ключи ПДн (`PII_ENCRYPTION_KEYS`, `PII_BLIND_INDEX_KEY`) и хост `sandbox.<хост API>`;
  `APP_ENV=production`. Код, API и спецификации те же: никаких маршрутов только для песочницы.
- **Отвергнуто: тестовые мерчанты в рабочей базе.** Им понадобились бы удаление из неизменяемых таблиц (леджер,
  журнал аудита — ADR-0003, ADR-0010), фильтр «не тест» во всех будущих отчётах, биллинге, KPI и рассылках, доля
  рабочих лимитов и SLO и тестовые ПДн в рабочей базе.
- **Маркер в учётных данных.** В песочнице у всех учётных данных после типа стоит `test_`: `lk_test_…`, `lm_test_…`,
  `lc_test_…`, `lcs_test_…`, `lat_test_…` (`Modules\Access\Internal\CredentialFormat`). Учётные данные другого
  окружения распознаются по формату до обращения к кэшу и базе: 401 `environment_mismatch`, на точке токенов —
  `invalid_client` с пояснением. Маркер входит и в то, что хранится: идентификатор клиента и хеши секрета клиента и
  токена содержат его, ключи касс и мерчантов хранятся как хеш маркера и секрета, ключи их кэша — с маркером. Секреты
  вебхуков (`whsec_`) без маркера, но у каждого окружения свои.
- **Ничего не уходит людям и деньгам.** Контракты, которые доходят до людей или денег, наследуют
  `Modules\Kernel\Sandbox\RealWorldEffect`; в песочнице `Sandbox::double()` делает их экземпляром двойник
  (`SandboxDouble`) сразу после загрузки приложения, так что настоящая реализация не создаётся. Сейчас это `CodeSender`
  (SMS и звонки — `SandboxCodeSender` ничего не отправляет и не пишет в лог); каналы этапа 7 (SMS, push, Wallet) и
  платежи и ЭДО этапа 10 обязаны сделать так же — тест находит в исходниках модулей все контракты с отметкой
  `RealWorldEffect` и проверяет, что в песочнице они двойники.
- **Только тестовые данные.** Телефоны +7 900 000-00-00 … +7 900 000-99-99 (проходят проверки телефонов на кассах),
  e-mail доменов example.com, example.net, example.org и зон .test, .example, .invalid, .localhost (RFC 2606,
  RFC 6761). Проверяются там, где контакт попадает в систему: начало подтверждения телефона, регистрация участника,
  смена телефона. Иначе — 422 `sandbox_test_data_required` с полем `field`.
- **Код подтверждения тестового телефона — всегда `000000`** (и только в песочнице и только для тестового телефона);
  хранится, как обычно, хешем; лимиты, попытки и время жизни те же.
- **Заголовок `Loyal-Environment: live|sandbox`** в каждом ответе обоих окружений — для плашки «ТЕСТОВЫЙ РЕЖИМ» на
  кассе. SDK показывают его, но не полагаются на него.
- **Лимиты** — те же переменные окружения с меньшими значениями (`.env.sandbox.example`). **Данные хранятся**; полная
  пересборка песочницы — не чаще раза в квартал с уведомлением за 14 дней, партнёры и OAuth-клиенты сохраняются.
  «Сброс» для интегратора — новый мерчант (сценарий быстрого старта SDK с новым `external_id`).

## Последствия

- Песочница не может навредить рабочему окружению и не может быть с ним спутана: ошибочный `LOYAL_SANDBOX=true` в
  рабочем окружении отвергает все рабочие учётные данные (сбой заметен сразу, а код `000000` недостижим без
  учётных данных); песочница, по ошибке направленная на рабочую базу или её кэш, не примет ни одного рабочего ключа:
  маркер входит в хеши и ключи кэша.
- SMS pumping через песочницу невозможен: реального отправителя там нет.
- Имена, даты рождения и номера карт — свободный текст, и в песочнице могут оказаться настоящими: это принятый
  остаточный риск, данные шифруются ключами песочницы, политика данных — в `docs/api/sandbox.md`.
- Этап 11 добавляет сетевую изоляцию, отметку окружения в самой базе (`ALTER DATABASE … SET loyal.environment`) с
  проверкой при старте, квоты при злоупотреблениях и регламент пересборки.

## Дополнение (этап 6a)

- Сотрудники интеграторов — клиенты платформы, поэтому учётные записи сотрудников в песочнице принимают настоящие
  e-mail, и письма им (приглашения, сброс пароля) уходят по-настоящему (ADR-0013, решение владельца продукта).
  Правило «только тестовые телефоны и e-mail» остаётся для участников программ.
