Антифрод-проверки при активации триала. Цель: не давать повторно активировать триал через обходы, но оплату НЕ блокировать — если пользователь подпадает под антифрод, триал не выдаём, но возможность оплатить оставляем.
Frontend: https://dev-app.chatplace.io/ (или deploy-preview с localStorage)
Backend: https://api-2020.review.chatplace.io/
MR: !2020 — DS-798 Реструктуризация TrialAntifraud
Платёжные системы: Stripe (тестовый режим), CloudPayments, Prodamus
DB таблицы: card_fingerprints, bot_ownership_history, user_fingerprints, subscription_logs
localStorage: localStorage.setItem('api_url', 'https://api-2020.review.chatplace.io/')
card_fingerprints id uuid PK user_id uuid FK → users(id) ON DELETE CASCADE provider varchar(32) — stripe / cloudpayments / prodamus card_hash varchar(255) — хеш/fingerprint карты user_fingerprint varchar(255) NULL — device ID (Amplitude) created_at timestamp Индексы: card_hash, user_fingerprint, user_id bot_ownership_history id uuid PK bot_identifier varchar — идентификатор бота platform_label varchar — платформа (instagram, telegram...) owner_id uuid — ID владельца created_at timestamp user_fingerprints id uuid PK user_id uuid fingerprint varchar — device fingerprint created_at timestamp
card_hash — если такой хеш уже есть в card_fingerprints для другого user_id → CARD_ALREADY_USEDcard_hash + user_fingerprint — блокируется только если совпадает И карта, И устройствоhasFingerprint (true/false)Новый чистый пользователь должен получить триал без проблем.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 1.1 | Зарегистрировать нового пользователя | Регистрация проходит, аккаунт создан | — | |
| 1.2 | Подключить Instagram/Telegram-бота | Бот привязан к аккаунту | — | |
| 1.3 | Оформить Pro-подписку (Stripe или CloudPayments) → получить триал | trial_ends_at в будущем | — | |
| 1.4 | Проверить subscription_logs — запись type = 1 | Есть запись с type = 1 (триал) | — | |
| 1.5 | Проверить card_fingerprints — запись с card_hash, provider, user_fingerprint | Запись сохранена с корректными данными | — | |
Пользователь купивший Unlimited НЕ должен блокироваться антифродом при переходе на Pro.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 2.1 | Создать нового пользователя | Аккаунт создан | — | |
| 2.2 | Купить Unlimited-план (НЕ триал) | trial_ends_at выставлен, но в subscription_logs НЕТ type = 1 | — | |
| 2.3 | Перейти на Pro-план | Переход успешен | — | |
| 2.4 | Проверить: триал доступен для Pro | Триал РАЗРЕШЁН — антифрод НЕ срабатывает | — | |
Если бот был привязан к аккаунту, который получил триал, затем передан другому — новый владелец НЕ получает триал.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 3.1 | Взять пользователя A из шага 1 (с триалом) | Пользователь с активным/истёкшим триалом | — | |
| 3.2 | Отвязать бота от A, привязать к новому пользователю B | Бот отвязан от A, привязан к B | — | |
| 3.3 | Проверить bot_ownership_history — записи для обоих владельцев | Обе записи присутствуют (owner_id = A и B) | — | |
| 3.4 | Оформить Pro для B → попытка активации триала | Триал ОТКЛОНЁН: BOT_HAD_TRIAL_OWNER | — | |
| 3.5 | Проверить: оплата всё равно прошла | trial_ends_at = now, next_payment_at через месяц/год. Платный план активен | — | |
Одна и та же карта в Stripe блокирует триал в другом аккаунте (проверка по card_hash).
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 4.1 | Использовать карту пользователя A (из шага 1) в новом аккаунте C | Карта принята | — | |
| 4.2 | Попытка активации триала для C | Триал ОТКЛОНЁН: CARD_ALREADY_USED | — | |
| 4.3 | Проверить: оплата прошла, сразу платный план | Подписка активна без триала, оплата списана | — | |
Для не-Stripe проверяется связка карта + устройство. Если устройство другое — триал разрешён.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 5.1 | Пользователь D активирует триал картой X с устройства D1 | Триал активирован, запись в card_fingerprints | — | |
| 5.2 | Пользователь E пытается активировать триал картой X с устройства D2 (другое) | Триал РАЗРЕШЁН — устройство другое | — | |
| 5.3 | Проверить card_fingerprints: 2 записи с одинаковым card_hash, разными user_fingerprint | Обе записи есть, user_fingerprint отличается | — | |
Та же карта + то же устройство → триал блокируется.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 6.1 | Пользователь F пытается активировать триал картой X с устройства D1 (как у D) | Триал ОТКЛОНЁН: CARD_ALREADY_USED | — | |
| 6.2 | Проверить: оплата доступна и работает | Платный план можно оформить без триала | — | |
Если у пользователя нет ботов — проверка по bot_ownership_history пропускается.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 7.1 | Создать пользователя БЕЗ подключённых ботов | Аккаунт без ботов | — | |
| 7.2 | Оформить Pro чистой картой | Триал РАЗРЕШЁН | — | |
| 7.3 | Проверить: bot_ownership_history — нет записей для этого бота | Проверка по ботам пропущена, карта чистая → триал выдан | — | |
API GET /user должен возвращать поле hasFingerprint.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 8.1 | GET /user для пользователя С fingerprint | hasFingerprint: true | — | |
| 8.2 | GET /user для пользователя БЕЗ fingerprint | hasFingerprint: false | — | |
Дополнительные проверки для полноты покрытия.
| ✓ | # | Шаг | Ожидание | Статус |
|---|---|---|---|---|
| 9.1 | Антифрод-отказ: проверить UX — пользователь видит предложение оплатить (не ошибку) | Понятный UI: «триал недоступен, оплатите подписку» | — | |
| 9.2 | Обычная оплата (без триала) — антифрод НЕ вмешивается | Оплата проходит штатно | — | |
| 9.3 | Пользователь с истёкшим триалом пытается активировать триал повторно (тот же аккаунт) | Триал отклонён — повторная активация заблокирована | — | |