# 🛡️ Шашки Рояль — Chips Economy: Инструкция по применению SQL

Это инструкция для **ручного применения** SQL-миграций в Supabase Dashboard,
которая активирует систему фишек (Chips) + ставок + защиту от двойной выплаты + аудит.

> **Важно**: эти миграции **обратно-совместимы**. Существующие данные не теряются.
> Все CREATE TABLE / CREATE INDEX / ALTER используют `IF NOT EXISTS`, RPC через `CREATE OR REPLACE`.

---

## 📋 Порядок применения

Применять **строго по очереди**, не пропуская:

### Шаг 1 — `01_stakes_core.sql` (Phase A)
Содержит:
- Таблицы `wallets`, `wallet_transactions`, `game_stakes`
- RPC: `create_stake_game`, `join_stake_game`, `process_stake_game_result`, `cancel_stake_game`
- RLS-политики (read-only с клиента)
- Realtime подписки

### Шаг 2 — `02_audit_and_crypto_prep.sql` (Phase B)
Содержит:
- Расширение `wallets` (total_won/total_lost/total_deposited/total_withdrawn)
- Расширение `wallet_transactions` (idempotency_key UNIQUE + balance_before/after + metadata)
- Новые типы транзакций (daily_bonus, quest_reward, provider_*)
- Таблица `audit_logs`
- Скелет-таблицы под будущую крипту: `payment_intents`, `withdrawal_requests` (НЕ используются сейчас)
- RPC: `log_audit_event`, `grant_daily_bonus`, `create_or_get_profile`
- Trigger: автоматическое обновление total_won/total_lost при появлении транзакций

---

## 🚀 Как применить

1. Открой Supabase Dashboard → твой проект → **SQL Editor**
2. Нажми **New query**
3. Открой файл `/app/frontend/supabase/migrations/01_stakes_core.sql`, скопируй ВЕСЬ текст
4. Вставь в SQL Editor и нажми **Run** (или Ctrl+Enter)
5. Проверь, что внизу написано "Success. No rows returned" или "Success"
6. Создай **New query** ещё раз
7. Открой `/app/frontend/supabase/migrations/02_audit_and_crypto_prep.sql`, скопируй и вставь
8. Нажми **Run**

---

## ✅ Проверка успешности (опционально)

После применения обоих файлов в SQL Editor выполни:

```sql
-- 1. Проверить, что таблицы созданы
SELECT table_name FROM information_schema.tables
WHERE table_schema = 'public'
  AND table_name IN ('wallets','wallet_transactions','game_stakes','audit_logs','payment_intents','withdrawal_requests')
ORDER BY table_name;
-- Должен вернуть 6 строк

-- 2. Проверить, что RPC созданы
SELECT routine_name FROM information_schema.routines
WHERE routine_schema = 'public'
  AND routine_name IN (
    'create_stake_game','join_stake_game','process_stake_game_result','cancel_stake_game',
    'grant_daily_bonus','create_or_get_profile','log_audit_event'
  )
ORDER BY routine_name;
-- Должен вернуть 7 строк

-- 3. Проверить колонки wallets
SELECT column_name FROM information_schema.columns
WHERE table_schema='public' AND table_name='wallets'
ORDER BY ordinal_position;
-- Должен содержать: profile_id, crypto_balance, locked_balance,
-- total_won, total_lost, total_deposited, total_withdrawn, updated_at

-- 4. Проверить, что idempotency_key UNIQUE индекс есть
SELECT indexname FROM pg_indexes
WHERE schemaname='public' AND tablename='wallet_transactions'
  AND indexname='wallet_transactions_idem_uniq';
-- Должен вернуть 1 строку

-- 5. Дать одному игроку стартовый бонус для теста (опционально)
-- Замени 'your-player-id' на реальный player_id из localStorage
SELECT * FROM create_or_get_profile('test-player-' || gen_random_uuid()::text, 'TestUser', 0);
```

---

## 🔥 Что заработает после применения

| Фича | Где | Описание |
|---|---|---|
| Стартовый бонус | Авто | Новый игрок получает 1000 🪙 в `wallets.crypto_balance` |
| Ставочные столы | `/stake-lobby` | Создание/вход в столы по 10/50/100/500 🪙 |
| Escrow lock | RPC `create_stake_game` | Ставка списывается из `crypto_balance` в `locked_balance` |
| Escrow join | RPC `join_stake_game` | 2-й игрок блокирует ставку, статус → 'locked' |
| Payout победителю | RPC `process_stake_game_result` | (pot − 5% комиссия) → победителю; loser теряет ставку |
| Idempotent payout | `escrow_status IN ('paid','refunded')` | Защита от двойной выплаты |
| Refund при ничьей | RPC | Ставки возвращаются обоим |
| Cancel до старта | RPC `cancel_stake_game` | Создатель возвращает свою ставку |
| Wallet UI | `/wallet` | Баланс + locked + total_won/total_lost + история |
| Daily bonus | `/wallet` → кнопка | +100 🪙 раз в сутки (idempotent по дате) |
| Audit log | Таблица `audit_logs` | Запись событий (на сейчас — daily_bonus + profile.created) |
| Crypto-prep | `payment_intents`, `withdrawal_requests` | Пустые таблицы — будут подключены позже |

---

## ⚠️ Чего пока НЕТ (намеренно)

- ❌ Реальные платежи (Stripe/Crypto)
- ❌ Вывод фишек на крипто-кошелёк
- ❌ Reconnect timer при disconnect (TODO в OnlineGame.tsx)
- ❌ Лидерборд по выигрышам (отдельная таблица не нужна — данные уже есть в `wallets`)

---

## 🧪 Тест-план (после применения SQL)

### Test 1 — Стартовый бонус
1. Открой `/` в инкогнито
2. Должен показаться баланс `1000 🪙` на главной (рядом с кнопкой «Играть на Chips»)

### Test 2 — Ежедневный бонус
1. Зайди в `/wallet`
2. Нажми «Забрать»
3. Должно: `+100 🪙`, баланс стал `1100`
4. Нажми ещё раз — toast: «Бонус уже получен сегодня»

### Test 3 — Создать стол на 10 фишек
1. `/stake-lobby` → «Создать стол» → выбери 10 → «♛ Создать»
2. Баланс должен упасть до `1090` (или `990` после Test 2: `1100 - 10 = 1090`)
3. Кошелёк: locked_balance = 10
4. Запрос в SQL Editor: `SELECT crypto_balance, locked_balance FROM wallets WHERE profile_id = '<твой profile_id>';`

### Test 4 — Войти в чужой стол на 10 фишек (нужно 2-е устройство или 2-й браузер)
1. С 2-го устройства открой `/stake-lobby`
2. Увидь стол создателя, нажми «Войти»
3. С обоих устройств игра должна начаться
4. Сыграй любым способом до конца
5. **Проверь idempotency**: refresh страницу в момент завершения — payout не должен повториться

### Test 5 — Cancel
1. Создай стол, не дожидаясь соперника, нажми «Назад» / закрой страницу
2. Стол должен исчезнуть из лобби (или будет очищен через polling)
3. (Опц.) Через SQL: `SELECT escrow_status FROM game_stakes WHERE game_id = '<id>';` → должно быть 'refunded'

---

## 🐛 Если что-то сломалось

1. **Ошибка "function create_stake_game does not exist"**:
   - 01_stakes_core.sql не применился. Применить заново.
2. **Ошибка "column total_won does not exist"**:
   - 02_audit_and_crypto_prep.sql не применился. Применить.
3. **RLS политика блокирует чтение**:
   - В SQL Editor: `SELECT current_setting('app.current_player_id', true);` — должен вернуть твой player_id (на сервере, не на клиенте).
   - Клиент сам устанавливает контекст через `setPlayerContext()`.
4. **Прежние данные в wallets**:
   - Не страшно. `total_won` и `total_lost` начнут считаться от 0 ТОЛЬКО для новых транзакций (trigger AFTER INSERT). Старые игры до миграции в счётчики не попадут — это ожидаемо.

---

## 📞 Что дальше

После того, как обе миграции применены и тесты прошли:

1. Сообщи мне — соберу финальный APK с обновлённым UI
2. Когда захочешь подключить реальную крипту: я добавлю `payment_provider_webhook` RPC, который будет писать в `payment_intents` и `withdrawal_requests` (таблицы уже готовы)
3. Reconnect timer + heartbeat можно добавить отдельной миграцией без переписывания
