Что болело до нас
Семь лет личных финансов и инвестиций жили в Google-таблицах. Формулы съезжали. Мобильный Sheets был мучением. Сверху висел риск, что доступ к Google в России закроют и семь лет данных исчезнут за один день.
Рыночная боль шире. Excel умирает молча: вносишь «когда-нибудь», последняя запись за август. Десять банков и три брокера дают десять приложений и ноль общей картины, поэтому никто не может объяснить, почему за прошлый месяц ушло заметно больше обычного. Серые трекеры не тянут обратно, привычка не складывается. В инвестиционной части своя беда: реальная доходность портфеля считается руками, брокерское приложение показывает позиции и молчит про структуру и концентрацию риска.
Была и продуктовая правда, которую вскрыл внутренний аудит проекта. Ниша личных финтрекеров сыпется на 90-м дне, ручной ввод чеков убивает удержание. Поэтому первой задачей стала петля возврата, доказанная метрикой. Функции строили уже после неё.
Костёр недели: пять цифр по памяти
Ключевая развилка проекта: сервис считает состояние, а ввод чеков отсутствует как класс. Раз в неделю человек за минуту вносит пять чисел по памяти. Дальше всё считается из последовательности снимков: дельты, серии недель роста, вехи подушки в 1, 3, 6 и 12 месяцев, метрики удержания по когортам.
Сверху ритуал, который держит возврат. Telegram-бот зовёт в выбранный день. Костёр горит ярко, тлеет или остаётся углями по свежести данных, границы 7 и 21 день. Сундук наполняется подушкой рубль в рубль.
Разбор недели выдаёт детерминированный движок: он смотрит всю историю снимков и формулирует одну честную фразу без выдуманных цифр. ИИ-инсайт здесь сейчас выключен намеренно, ради бюджета на токены, и это помечено прямо в коде, чтобы при смоук-тесте выключение не читалось как поломка.
Пороги удержания задали до старта разработки петли возврата: W2 от 25%, W4 от 20%, прохождение онбординга от 60%. Пока W2 ниже порога, продукт упрощают на входе и усиливают разбор недели. Новые механики ждут сигнала. По этому же правилу целый пласт геймификации лежит замороженным, хотя он придуман и описан.
Что внутри кабинета
Кабинет app.pixelmoney.ru собран на Next.js 16: 13 страниц и 31 API-роут, три инструмента в одном окне.
- Бюджет: доходы и расходы по категориям и месяцам, фонды накоплений, графики денежного потока, история без лимита по годам.
- Импорт из Google-таблиц и Excel через CSV: листы и годы сопоставляются автоматически по совпадению сумм с порогом расхождения 5%. Вся история выгружается обратно в CSV.
- Инвест-портфель Т-Инвестиций по read-only токену: все брокерские счета сводятся в один портфель, одинаковые позиции склеиваются по FIGI со взвешенной средней, есть разбивка по классам активов и результат по каждой позиции.
- ИИ-аналитик «Лумос» разбирает структуру портфеля, распределение и то, что упускается из виду. Прямые советы «купи/продай» запрещены на уровне системного промпта.
- Управленческий учёт по проектам: выручка, переменные и постоянные расходы, налоги, дивиденды собственнику, план и факт по месяцам, точка безубыточности, 7 отраслевых шаблонов строк.
- Подписка через Т-Кассу: 160 ₽ в месяц с явным чекбоксом привязки карты, 1490 ₽ за год разовым платежом, чек по 54-ФЗ, идемпотентный вебхук, снятие Pro при возврате.
- Две темы кабинета: пиксельная «Сокровищница» по умолчанию и строгая светлая для тех, кому нужен банковский вид. Плюс PWA с service worker, который никогда не кэширует API и POST.
Как это устроено
Два раздельных контура на одном домене. Лендинг это чистая статика без сборки и фреймворков: один самодостаточный HTML-файл и три canvas-сцены. Первый экран рисуется процедурно в разрешении 384x216, при включённом prefers-reduced-motion рисуется один кадр. Отдаётся nginx напрямую.
Кабинет упакован в Docker-образ и работает за nginx. Секреты приходят в рантайме, в образ не пекутся. Авторизация двухслойная: middleware стоит перед всем, сверяет подпись сессии и прокидывает идентификатор пользователя в API. Публичные пути перечислены явно, у каждого вебхука своя защита. Пароли хешируются PBKDF2-SHA256 в 100 000 итераций, сравнение хешей в постоянном времени. Брокерский токен лежит зашифрованным AES-256-GCM.
Хранилище это PostgreSQL. Любая правка ячейки бюджета идёт транзакцией с блокировкой строки, поэтому параллельные запросы больше не затирают друг друга. RLS включён на всех таблицах, грантов для анонимной роли нет вовсе. Деплой: один git push в main, сборка образа в GitHub Actions, сервер забирает обновление по таймеру примерно за две минуты. Заходить на сервер за этим никому не нужно.
Грабли, которые собрали по дороге
Самая дорогая касалась денег. Пришла жалоба, что донат отдаёт ошибку, хотя касса подключена. Касса была ни при чём: домен эквайринга отдавал сертификат УЦ Минцифры, а этого корня нет ни в Node, ни в alpine-образе. Соединение рвалось, в логах лежало бесполезное «fetch failed», настоящая причина пряталась в поле cause. Диагностировали по кодам ответа и цепочке сертификатов, без доступа к серверу. Лечение вышло двойным: эквайринг переехал на домен с глобальным сертификатом, а для API брокера пришлось вшить корень в код. Решение здесь сознательно узкое: отдельный https-агент в одном модуле. Глобальная настройка заставила бы весь процесс доверять этому УЦ во всех исходящих запросах. Побочный урок оказался неприятнее самой поломки: тем же вызовом жило ночное автосписание, поэтому подписки неделями молча не продлевались, и вскрылось это через донат.
- Деплой шёл в мёртвый путь: ключи честно заносили в env и перезапускали процесс, а домен обслуживал совсем другой контейнер. Правило: сначала смотрим, чем реально отдаётся домен, потом настраиваем.
- Пейвол существовал только в интерфейсе. Замок висел в навигации, API отдавал платные данные любому с сессией. Ввели серверную проверку Pro на всех платных роутах, включая вложенные.
- Middleware тихо убил бота и крон: новые бессессионные эндпоинты забыли внести в список публичных путей, и запросы резались до роута. Внешне всё выглядело рабочим, просто ничего не приходило.
- Одна строка в конфиге остановила весь деплой. Убрали режим standalone, а Dockerfile копирует именно его папку, сборка легла на COPY. Вернули и оставили в конфиге комментарий «не убирать».
- Сообщение «Не удалось создать платёж» на терминале с онлайн-кассой означало отсутствие блока чека в запросе. Причём чек уходит только при наличии email, для доната пришлось подставлять служебный адрес.
- Пиксельные шрифты не умеют по-русски: у одного кириллицы нет вовсе, у другого она кривая. Прошли обе ступени, прежде чем сели на рабочий вариант. Для продукта, где пиксель-арт это позиционирование, потеря была неочевидной.
- Пасхалка умерла при редизайне первого экрана, счётчик 7 из 7 стал недостижим, и обещанная награда не выдавалась никому. Понадобился отдельный аудит всех семи. Интерактив, размазанный по вёрстке, проверяем целиком после каждой переделки.
Частые вопросы
Как перенести учёт из Google-таблиц в свой сервис и не потерять историю?
В PixelMoney есть импорт из Google-таблиц и Excel через CSV. Листы и годы сопоставляются автоматически по совпадению сумм, порог расхождения 5%. История хранится без лимита по годам и в любой момент выгружается обратно в CSV.
Сколько заняло превратить таблицу в работающий сервис с оплатой?
От локального дашборда для одного человека до платящего сервиса прошло около трёх с половиной месяцев. Основной спринт занял 7 дней и дал примерно две трети функциональности. Кабинет собран на Next.js 16: 13 страниц и 31 API-роут.
Люди бросают финансовые трекеры через пару месяцев. Что с этим делали?
Ниша личных финтрекеров сыпется на 90-м дне, ручной ввод чеков убивает удержание. Поэтому ввода чеков здесь нет: раз в неделю человек вносит пять чисел по памяти, дальше всё считается из снимков. Пороги записали до кода: W2 от 25%, W4 от 20%, прохождение онбординга от 60%. Пока W2 ниже порога, новые механики ждут сигнала.
Можно ли подключить подписку и брокерский счёт?
Подписка идёт через Т-Кассу: 160 ₽ в месяц с явным чекбоксом привязки карты и 1490 ₽ за год разовым платежом, с чеком по 54-ФЗ и идемпотентным вебхуком. Портфель тянется из Т-Инвестиций по read-only токену: все брокерские счета сводятся в один, одинаковые позиции склеиваются по FIGI со взвешенной средней. Сам токен лежит зашифрованным AES-256-GCM.
Насколько безопасно держать личные финансы в таком сервисе?
Авторизация двухслойная, middleware стоит перед всем и сверяет подпись сессии. Пароли хешируются PBKDF2-SHA256 в 100 000 итераций, сравнение хешей идёт в постоянном времени. RLS включён на всех таблицах, грантов для анонимной роли нет вовсе. Деньги, вход и движок удержания закрыты 44 unit-тестами.
