← Главная

🛡️ DS-798 — Антифрод для триала

📋 DS-798 👤 Marat Komarov 📌 MR ожидает тестирования 🏃 Sprint 16 🔗 CPD-192 (продуктовая) 📅 25.03.2026

📝 Суть задачи

Антифрод-проверки при активации триала. Цель: не давать повторно активировать триал через обходы, но оплату НЕ блокировать — если пользователь подпадает под антифрод, триал не выдаём, но возможность оплатить оставляем.

Проверки при активации триала:

⚠️ Ключевое правило: антифрод блокирует ТОЛЬКО триал, оплата всегда доступна!

🔧 Окружение для тестирования

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

✅ Логика антифрод-проверок (по коду)

0 / 30 проверок
1. Новый пользователь — триал успешно 5 проверок

Новый чистый пользователь должен получить триал без проблем.

#ШагОжиданиеСтатус
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Запись сохранена с корректными данными
2. Unlimited/Club — нет ложного срабатывания (ключевой фикс) 4 проверки

Пользователь купивший Unlimited НЕ должен блокироваться антифродом при переходе на Pro.

⚠️ Ключевой фикс: Unlimited/Club выставляют trial_ends_at, но в subscription_logs НЕТ записи type = 1. Антифрод должен проверять именно subscription_logs, а не trial_ends_at.
#ШагОжиданиеСтатус
2.1Создать нового пользователяАккаунт создан
2.2Купить Unlimited-план (НЕ триал)trial_ends_at выставлен, но в subscription_logs НЕТ type = 1
2.3Перейти на Pro-планПереход успешен
2.4Проверить: триал доступен для ProТриал РАЗРЕШЁН — антифрод НЕ срабатывает
3. Передача бота — блокировка по истории владения 5 проверок

Если бот был привязан к аккаунту, который получил триал, затем передан другому — новый владелец НЕ получает триал.

#ШагОжиданиеСтатус
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 через месяц/год. Платный план активен
4. Повторная карта — Stripe 3 проверки

Одна и та же карта в Stripe блокирует триал в другом аккаунте (проверка по card_hash).

#ШагОжиданиеСтатус
4.1Использовать карту пользователя A (из шага 1) в новом аккаунте CКарта принята
4.2Попытка активации триала для CТриал ОТКЛОНЁН: CARD_ALREADY_USED
4.3Проверить: оплата прошла, сразу платный планПодписка активна без триала, оплата списана
5. Та же карта, другое устройство — CloudPayments/Prodamus 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. Та же карта + то же устройство — CloudPayments/Prodamus 2 проверки

Та же карта + то же устройство → триал блокируется.

#ШагОжиданиеСтатус
6.1Пользователь F пытается активировать триал картой X с устройства D1 (как у D)Триал ОТКЛОНЁН: CARD_ALREADY_USED
6.2Проверить: оплата доступна и работаетПлатный план можно оформить без триала
7. Пользователь без ботов 3 проверки

Если у пользователя нет ботов — проверка по bot_ownership_history пропускается.

#ШагОжиданиеСтатус
7.1Создать пользователя БЕЗ подключённых ботовАккаунт без ботов
7.2Оформить Pro чистой картойТриал РАЗРЕШЁН
7.3Проверить: bot_ownership_history — нет записей для этого ботаПроверка по ботам пропущена, карта чистая → триал выдан
8. API — поле hasFingerprint 2 проверки

API GET /user должен возвращать поле hasFingerprint.

#ШагОжиданиеСтатус
8.1GET /user для пользователя С fingerprinthasFingerprint: true
8.2GET /user для пользователя БЕЗ fingerprinthasFingerprint: false
9. Граничные случаи и регрессия 3 проверки

Дополнительные проверки для полноты покрытия.

#ШагОжиданиеСтатус
9.1Антифрод-отказ: проверить UX — пользователь видит предложение оплатить (не ошибку)Понятный UI: «триал недоступен, оплатите подписку»
9.2Обычная оплата (без триала) — антифрод НЕ вмешиваетсяОплата проходит штатно
9.3Пользователь с истёкшим триалом пытается активировать триал повторно (тот же аккаунт)Триал отклонён — повторная активация заблокирована