Goal
Scorer

Соревнование в прогнозах на футбол, которое человек заводит для своих — коллег, подписчиков, друзей. Мы ведём турнир, он зовёт людей.

10разделов, пять общих и пять по специальностям
9вопросов, на которые ещё нет ответа
42сквозные проверки уже проходят в браузере

Продукт в двух абзацах

Всем

Что это. Соревнование в прогнозах на футбол, устроенное как инструмент для сообществ. Человек с аудиторией — владелец телеграм-канала, HR в компании, администратор чата болельщиков — заводит турнир, зовёт своих по ссылке, и дальше каждую неделю его люди отвечают на вопросы о матчах и видят таблицу. Инструмент ведёт турнир целиком: собирает ответы, считает результат, показывает, кто впереди.

Чем это не является. Это не букмекерская контора и не ставки. Участники не вносят денег — ни нам, ни друг другу. Призы, если они есть, назначает и вручает организатор сам, а мы их не считаем, не держим и не выдаём. Деньги в продукте есть ровно в одном месте: организатор платит нам фиксированную подписку за то, что мы ведём его турнир.

Как устроена игра

Всем

Вопрос вместо ставки

Игрок отвечает на вопрос о матче: сколько будет голов, угловых или жёлтых карточек. На ответ тратится билет — внутренняя единица участия. Билеты выдаются бесплатно: при регистрации и затем еженедельно. За деньги билеты не продаются.

Неделя как единица

Соревнование идёт недельными турами. Тур равен календарной неделе и начинается в понедельник. Его не назначает человек — он вычисляется из календаря, открывается и закрывается сам.

ПнИтог прошлой недели, открывается новая
ВтПриём ответов
СрПриём ответов
ЧтПриём ответов
ПтПриём ответов
СбПриём ответов
ВсПо каждому матчу приём закрыт за час до начала

Сколько стоит угаданный ответ

Самое интересное место в механике. Ценность ответа зависит от того, сколько людей ответили так же: угадал то, что угадали все — мало очков; угадал то, чего не угадал почти никто — много.

Продукт поощряет не осторожность, а обоснованную смелость.

Формула живёт в коде и работает. Что именно она даёт в очках — один из открытых вопросов, он в разделе для маркетолога.

Заверенная запись

Особенность, которой нет у таблицы в чате. Каждый ответ фиксируется с точным временем и после закрытия приёма не может быть изменён никем — ни участником, ни организатором, ни нами. Участник может убедиться в этом сам, не веря нам на слово.

Это решает главную ссору всех ручных прогнозных лиг: «я писал 2:1 до матча!» — и доказать нечем.

Кто платит и за что

Всем

Игрок играет бесплатно и платить не должен никогда. Организатор платит фиксированную подписку, и он наш единственный клиент. Игрок — не покупатель; игрок — то, ради чего организатор платит.

За что именно берутся деньги

За ведение заверенного журнала турнира. Не за доступ к выигрышу, не за преимущество в игре, не за долю в призе.

Тариф привязан к ёмкости организатора: сколько турниров в месяц он может завести, какого размера, с какими возможностями. Например, четыре турнира в месяц — один тариф, шестнадцать — другой.

Тариф не зависит от числа участников и от размера приза.

Это не стилистика ценообразования, а условие, на котором держится вся модель: доход, растущий с масштабом игры, означал бы участие в игре.

Главный цикл роста

Организатор платит подписку зовёт 10–30 Игроки играют бесплатно неделями Часть из них хочет свой круг заводят свой турнир и приводят своих Конверсия — не «игрок начал платить», а «игрок собрал свой круг» Организатор платит подписку зовёт 10–30 Игроки играют бесплатно неделями Часть из них хочет свой круг заводят свой турнир и приводят своих
Замыкающая стрелка — единственный органический канал роста: платит не тот, кого привели, а тот, кто решил привести своих.

Границы, которые нельзя переходить

Всем. Этот раздел важнее остальных

Механика «участники скидываются, угадавшие делят» — это то, что право называет тотализатором, независимо от того, как это назвать на витрине. Лицензия на такую деятельность стоит сотни тысяч евро в год и требует отдела сопровождения. Мы её не получаем, поэтому строим продукт, которому она не нужна.

Выводы правового разбора приняты и обсуждению в рамках работы не подлежат.

Нельзя

ни в продукте, ни в макете, ни в базе данных

  • Принимать деньги участников в любом видеэто приём ставок
  • Собирать призовой фонд из взносовэто пул
  • Считать, хранить или выдавать призэто расчёт по игре
  • Подтверждать факт или размер чьего-либо платежаделает нас учётным слоем пула
  • Брать плату за участника или долю от призапривязывает наш доход к игре
  • Заводить поле «взнос», «фонд», «ставка»то же самое, но в коде

Можно и нужно

на этом стоит весь продукт

  • Заверять, что человек сказал и когдаэто наш продукт
  • Показывать полноту и порядок записиэто его ценность
  • Брать фиксированную плату за ведение записиэто наш доход
  • Показывать репутацию организатора по отзывам участниковэто не проверка, а отзывы

Заверяем, что человек сказал, а не что человек заплатил.

Правило помогает проверить любую новую идею за секунду.

Условие, о котором легко забыть

Защита «мы общий инструмент ведения записи» держится, пока продукт действительно общий. Турнир без всяких призов должен быть полноценным, массовым сценарием, а не отговоркой в оферте. Это не только правовое требование — лига коллег без призов и есть наш основной случай применения.

Что уже сделано

Всем

Проект не на старте. Работающего кода много, и участник приходит не на пустое место.

Недельные туры
открываются и закрываются планировщиком сами
Матчи, расписание
импорт от внешних провайдеров, ручное ведение, перенос, отмена
Билеты и ответы
приём с окном закрытия, защита от двойной отправки
Расчёт
в фоновой очереди, с повторами, с разбором сбоев
Статистика
личная, публичная, рейтинг
Права и роли
каталог прав, роли, разграничение доступа
Админка оператора
девять разделов: обзор, туры, матчи, очередь, настройки, пользователи и другие
Интерфейс игрока
расписание и ответ, билеты, кошелёк, профиль, уведомления
Витрина без входа
расписание, статистика, «как это работает», обязательные страницы
Настройки игры
меняются в админке, не в коде
Сквозные тесты
42 проверки в браузере, прогон 11 минут
Симулятор
прогон недель на исторических данных, 8900 реальных матчей

Технологии

Backend
TypeScript, NestJS, PostgreSQL, TypeORM, Redis, BullMQ, Zod
Frontend
TypeScript, React, RTK Query, архитектура FSD, клиент API из OpenAPI
Инфраструктура
Docker, Caddy, GitHub Actions, GitHub Packages
Проверки
юнит-тесты, проверка правил слоёв, Playwright

Чего ещё нет

Продукт и маркетинг

Маркетологу, продакту

Продукт существует, а понимания, как приводить людей, почти нет. Нужен человек, который превратит догадки в проверяемые гипотезы.

Поиск организаторов

Это не воронка, а разговоры. Наш клиент — человек, который уже ведёт прогнозную лигу вручную и мучается: собирает ответы по чату, считает по выходным, спорит с участниками о том, кто что написал до матча.

Где они: телеграм-каналы о футболе с прогнозами в комментариях, Discord-серверы, сообщества фэнтези-лиг, форумы болельщиков клубов, корпоративные чаты.

Первые десять находятся вручную, по одному, и им всё настраивается за них. Это не провал воронки, а её форма: мы привлекаем не игроков, а тех, кто приводит игроков.

Что держит игрока

Таблица, недельный ритм, уведомление о том, что тебя обошли, и возможность показать результат другим. Последнее — единственный органический канал: после тура игрок должен получать что-то, что не стыдно кинуть в чат. И это «что-то» у нас может быть подтверждённым, чего нет ни у кого.

Что мерить

Игроков на организатора
сколько стоит один разговор
Доля игроков, заведших свой турнир
работает ли главный цикл — единственная метрика, за которой стоит следить в первые месяцы
Возврат во второй турнир
держит ли продукт
Платящих после бесплатного
есть ли бизнес

Чего делать нельзя

Платная реклама на игроков — это покупка не-клиентов. Привлечение обещанием призов возвращает правовую рамку и ломает мотив. Партнёрские ссылки на букмекеров: продукт, зарабатывающий отправкой людей в контору, и есть контора. Массовая холодная рассылка сожжёт единственный канал, который у нас есть.

Что входит в тариф и что происходит при исчерпании?

Ось выбрана: тарифицируется ёмкость организатора — например, 4 турнира в месяц против 16. Границ нет. Что ещё входит в тариф: размер турнира, длительность, число рынков, свои правила подсчёта, брендинг, история, экспорт? Что происходит, когда ёмкость исчерпана — турнир не заводится, заводится в режиме только-чтение, или переносится на следующий месяц? Бесплатный первый турнир — это приманка или его нет вовсе?

Полный рейтинг — привилегия или приманка?

По исходным требованиям полный рейтинг открыт только обладателю платного аккаунта. Для сервиса, продающего статус, это логично. Для соревнования между людьми рейтинг — главная причина прийти, и закрывать его значит прятать ровно то, ради чего человек регистрировался. Оставляем как есть или открываем всем?

Чем становится коэффициент, когда делить нечего?

Формула «проигравшие делятся между угадавшими» измеряет, насколько неочевиден был ответ. Становится ли она прямо множителем очков, или очки считаются иначе? Риск: если очки равны коэффициенту напрямую, выгодно охотиться за редкими исходами вместо прогноза. И второе: на тонкой линии коэффициент прыгал от 2,9 до 7,0 — неделя с малым числом матчей перевесит неделю с большим.

Интерфейс и дизайн

Дизайнеру

Четырнадцать новых страниц и один новый сложный элемент — таблица турнира. Плюс пересборка главной: сейчас она говорит «играй», а должна говорить «собери своих».

Три поверхности, и у них разные задачи

Витрина
смотрит случайный человек, ждёт ответа «что это и стоит ли». Тёплая, светлая
Игра
смотрит вошедший игрок, ждёт понятности. Та же тёплая палитра
Админка
смотрит оператор сервиса, ждёт точности. Холодная, плотная

Организатор — не четвёртая поверхность, а раздел внутри игры: у него та же палитра и навигация, и он почти всегда и сам играет.

Что предстоит нарисовать

Витрина
страница турнира по ссылке-приглашению, архив результатов, тарифы, вопрос-ответ, ответственная игра, переработка главной
Игрок
мои турниры, страница турнира с таблицей, страница матча с раскладом ответов, публичный профиль
Организатор
создание турнира, настройки, участники и приглашения, подписка с остатком ёмкости, отметка о выдаче приза
Оператор
список всех турниров, подписки

Правила, которые уже действуют

Это не пожелания, а работающие договорённости, подкреплённые кодом.

Показывать ли расклад ответов до закрытия приёма?

На странице матча предполагается расклад: сколько участников ответили так и сколько иначе. Это честная информация. Но показанный заранее, он сдвигает поведение: человек видит, где ответов мало, и идёт туда, а не туда, где считает исход вероятным. Варианты: показывать всегда; только после закрытия приёма; показывать доли без абсолютных чисел.

Что видно в публичном профиле игрока и спрашивается ли согласие?

Соревнование требует, чтобы человек был виден: точность, серии, разрез по видам вопросов, последние туры. Сейчас всё это считается только для себя. Какие поля публичны, виден ли профиль без входа, спрашивается ли согласие, и что происходит с отказом — выпадает из рейтинга или показывается под псевдонимом?

Приз — описанием или структурой?

Платформа приз не считает, не держит и не выдаёт. Но что-то показать надо: участник должен понимать, за что играет. Приз одним текстовым полем, структурой без денежных полей, или текстом плюс отметка «выдано»? Структура с полем «сумма» выглядит как призовой фонд, даже если мы к деньгам не прикасаемся, — а как это выглядит, имеет значение.

Разработка

Программисту

Три новых модуля на backend, четырнадцать страниц на frontend, и до этого — вывод существующего в интернет и получение результатов матчей из внешних источников.

Работа делится на две части, и порядок зафиксирован

Часть первая: вывести то, что есть, в интернет и прожить на нём неделю. Публичный стенд, настоящие данные, боты, играющие в реальном времени, неделя без вмешательства руками. Это проверка того, что построенное живёт само: планировщик срабатывает по расписанию, провайдер отвечает когда хочет, очередь переживает перезапуск, неделя кончается сама.

Часть вторая: турниры, подписка, заверение записи.

Вторая часть меняет надстройку, а не фундамент. Если фундамент не живёт неделю сам, переделка надстройки это не исправит, а скроет.

Часть первая: конкретные задачи

Получение результатов матчей — самая большая и блокирующая. Сейчас внешний интерфейс интеграции объявляет единственный метод: получить расписание на дату. Результатов нет в самом контракте, не только в реализациях. Отдельная фоновая задача переводит матч в статус «идёт» по времени, но счёта не забирает. Голы, угловые и карточки вносит оператор руками.

Работа: расширить интерфейс, реализовать в двух адаптерах, завести фоновую задачу, писать результат через доменный метод, а не записью в базу, вести журнал как у импорта расписания, показать оператору расхождение, когда провайдер говорит одно, а в базе другое.

Включить непрерывную сборку. Сейчас она выключена намеренно: стоит задание-заглушка, которое падает сразу, и весь граф висит на нём. Выкладка идёт руками.

Эмулятор игроков. Существующий симулятор вызывает сервисы напрямую — он проверяет экономику и годится для этого. Эмулятор стенда должен ходить по HTTP под токенами игроков: иначе прогон не проверит ни авторизацию, ни права, ни защиту от двойной отправки, ни поведение под параллельными запросами — то есть ровно то, ради чего он затевается.

Еженедельная выдача билетов. Решение принято, код не написан. Это и есть бесплатный режим, на котором держится вся модель.

Известные долги. Выбор текущего тура смотрит на время создания, а не на календарь, — открытый тур следующей недели перехватит расписание. Проверка правил слоёв на frontend падает: 26 нарушений. Публичность стенда поднимает вес вопроса о защите от накруток.

Часть вторая: что переиспользуется, что меняет смысл, что заново

Расчёт
сохраняется целиком, меняется назначение результата: не деньги, а очки
Туры
сохраняется
Матчи и расписание
сохраняется, матчи общие для всех турниров
Билеты
сохраняется, билет — единица участия
Статистика
расширяется: таблица турнира, публичный профиль, архив
Настройки
расширяется
Кошелёк, вывод
замораживаются
Покупка билетов
заменяется подпиской
Турниры
новый модуль
Подписка
новый модуль
Заверение записи
новый модуль

Правила работы, которые придётся соблюдать

В системе не должно появиться поля, описывающего взнос участника, призовой фонд числом, кошелька организатора или выплаты через систему.

Это не пожелание, а граница модели; к этому нужен тест, который падает, если такое поле завелось.

Турнир — представление над общим недельным туром или своя сущность со своим календарём?

Главная архитектурная развилка. В первом варианте матчи те же, расчёт тот же, турнир лишь отбирает подмножество матчей и считает свою таблицу по своим участникам. Во втором — у турнира свой календарь, своё начало и конец, свой расчёт. Разница в объёме работы — между неделей и двумя месяцами. Во втором варианте у билета появляется второй владелец, и становится неоднозначным, по какому расчёту его разбирать.

Что именно подписывается криптографически и где это проверяется?

Что заверяем: ответ, результат матча, итоговую таблицу? Чем: ключом платформы, организатора, игрока? Где участник проверяет — внутри продукта или независимо от него? Подпись ключом платформы доказывает только отсутствие правок задним числом и ничего не доказывает про саму платформу. Если проверить можно лишь внутри продукта, функция почти декоративна.

Что делать с кошельком, покупкой и выводом?

Три модуля построены под модель, от которой продукт отказывается, и к ним есть страницы, покрытые тестами. Удалить, заморозить (код остаётся, маршруты закрыты, страницы спрятаны) или переосмыслить — баланс как счётчик билетов участия, без покупки и без вывода? Удалённое вернуть дорого, если правовая оценка изменится.

Управление и процесс

Менеджеру

Нужен человек, который держит горизонт, разводит развилки по владельцам и не даёт работе расползтись. Открытых вопросов сейчас больше тридцати, и часть из них блокирует код.

Как устроена работа

Что сейчас мешает двигаться

Результаты матчей не приезжают сами
неделя живого прогона встанет на третий день
Непрерывная сборка выключена
выкладка руками
Шесть развилок второй части
пока открыты, код писать нельзя
Правовая проверка модели
специалист ещё не привлечён

Правовая рамка запуска

Модель выбрана и описана, но юристом по игорному законодательству не проверена. Проверить надо: держится ли конструкция в выбранных юрисдикциях; работает ли бесплатный способ участия рядом с платным и при каких условиях; квалификация подписки, дающей возможности, но не шансы, — нет ли риска, что её сочтут завуалированным взносом; налоговый агент по призам. Нужен именно профильный юрист: общий корпоративный ошибается в обе стороны.

Право и документы

Юристу, аналитику

Нужны тексты, которые можно предъявить, и проверка модели специалистом. Обязательные страницы в продукте есть и покрыты тестами, но тексты в них — заглушки.

Как мы описываем себя

Формулировка, которой стоит придерживаться во всех документах:

Сервис ведёт заверенный журнал прогнозов в соревнованиях, которые организуют пользователи. Сервис фиксирует, какой прогноз дал участник и когда, и делает запись неизменяемой и проверяемой независимо.

Сервис не принимает и не переводит средства, не формирует призовой фонд, не определяет и не выплачивает призы и не удерживает долю. Плата — фиксированная подписка организатора за объём ведения журнала, не зависящая от числа участников и от того, есть ли в соревновании приз.

Признак того, что описание собрано правильно: денег в нём нет вовсе.

Что нужно написать

Положение о соревновании, правила подсчёта (не маркетинг, а описание механики: как считаются очки, что с переносами и отменами матчей), пользовательское соглашение, политика конфиденциальности, правила для организаторов — включая прямой запрет собирать взносы с участников — и раздел об ответственной игре.

Последний — не формальность. Платёж у нас добровольный и игра бесплатная, и именно поэтому самоограничение стоит описать своими словами, а не скопировать у других.