# Песочница: разработка и эксплуатация

Решение — ADR-0012, памятка для интеграторов — `docs/api/sandbox.md`.

## Как устроено в коде

- `config('app.sandbox')` (`LOYAL_SANDBOX`) читает `Modules\Kernel\Sandbox\Sandbox` — синглтон ядра.
- Контракт, который доходит до людей или денег, наследует `Modules\Kernel\Sandbox\RealWorldEffect`, а модуль
  регистрирует двойник: `Sandbox::double($this->app, Contract::class, fn () => new SandboxContract)`. Двойник
  реализует `SandboxDouble` и ничего не делает вовне; в песочнице он становится экземпляром контракта после загрузки
  приложения, так что настоящая реализация даже не создаётся. `tests/Sandbox/RealWorldEffectsTest.php` находит в
  исходниках модулей все контракты с отметкой `RealWorldEffect` и проверяет, во что они разрешаются; отметить новый
  контракт — обязанность того, кто его пишет.
- Ключи касс и мерчантов хранятся как хеш маркера окружения и секрета (в рабочем окружении маркер пустой), поэтому
  ключ одного окружения не подходит в другом даже при общей базе или кэше.
- Приём контактов участников: `Sandbox::assertTestPhone()` и `assertTestEmail()` там, где телефон или e-mail попадает в
  систему (подтверждение телефона, регистрация, смена телефона). E-mail сотрудников (учётные записи кабинетов) — не
  тестовые данные: это клиенты платформы, их настоящие адреса принимаются (ADR-0013). Код тестового телефона — `Sandbox::verificationCodeFor()`.
- Форматы учётных данных и маркер `test_` — `Modules\Access\Internal\CredentialFormat`.
- Заголовок `Loyal-Environment` ставит глобальный middleware `AnnounceEnvironment`.

## Тесты

Тесты режима песочницы лежат в `tests/Sandbox`: `Tests\SandboxTestCase` поднимает приложение с `LOYAL_SANDBOX=true`,
как настоящее развёртывание. Сценарии с участником берут тестовый телефон:
`CheckoutScenario::start(memberPhone: '+79000000001')`.

## Локальная песочница

```powershell
copy .env.sandbox.example .env.sandbox        # пароли ролей БД — те же, что в .env; APP_KEY и ключи ПДн — свои
php artisan key:generate --env=sandbox
php artisan pii:generate-keys --env=sandbox
php artisan db:bootstrap --database=loyal_sandbox
php artisan migrate --env=sandbox --database=pgsql_owner
php artisan serve --env=sandbox --port=8787
php artisan partners:create "Интегратор" --env=sandbox
php artisan oauth-clients:issue <partner-id> "Интеграция" --env=sandbox    # выдаст lc_test_… и lcs_test_…
```

- Локально роли PostgreSQL общие с основной базой разработки (роли действуют на весь кластер), отдельные —
  база `loyal_sandbox`, `APP_KEY` и ключи ПДн. В развёртывании песочницы и роли свои.
- `APP_ENV=sandbox` в `.env.sandbox` обязателен: `php artisan serve` запускает дочерний процесс сервера, и тот
  находит `.env.sandbox` только по `APP_ENV`.

## Развёртывание (этап 11)

- Тот же образ, что и рабочий, с `LOYAL_SANDBOX=true` и `APP_ENV=production`.
- Своя база и роли PostgreSQL, свои Redis и очереди, свои `APP_KEY`, `PII_ENCRYPTION_KEYS`, `PII_BLIND_INDEX_KEY`,
  свои секреты вебхуков; хост `sandbox.<хост API>`; никаких учётных данных провайдеров SMS, push, платежей.
- Лимиты — из `.env.sandbox.example`.
- Пересборка: не чаще раза в квартал, уведомление интеграторам за 14 дней; партнёры и OAuth-клиенты переносятся,
  остальное создаётся заново (`migrate:fresh` на базе песочницы).
- Этап 11 добавит отметку окружения в базе (`ALTER DATABASE … SET loyal.environment = 'sandbox'`) и проверку при
  старте, сетевую изоляцию и квоты.
