FinKIT
Написал с нуля.
Дальше дорабатывали другие.
Веб-приложение с заявками, балансом и выводом средств: витрина, кабинет, админка и фоновый обработчик. На этой странице только техника.
тест зелёный
- 263Этап 1
Витрина, вход по коду на почту, страница заявки, 6 API, схема базы на 9 таблиц.
- 504Этап 2
Кабинет, верификация, баланс с полным циклом. 108 файлов.
- 724Этап 3
Админка: заявки, выводы, доступы и свой TOTP по RFC 6238.
- 1026Этапы 4 и 5
Выплаты в сети TRON, взаимный TLS с банком, PostgreSQL, Docker. Архив на 434 файла прошёл все тесты на чистой распаковке.
- 118120 октября
Модульные зелёные, сквозные 8 из 8.
Статус меняет одна функция
Нажимайте статусы. Разрешённые подсвечены, остальные transition() отклонит. Таблица переходов настоящая, из кода, заявка учебная.
FK-DEMO: DRAFT, разрешено 4 перехода
- заявка создана, статус DRAFT
// единственный способ сменить статус
async function transition(id, to, actor) {
const from = await currentStatus(id)
if (!ALLOWED[from].includes(to))
throw new AppError('INVALID_TRANSITION')
// одна транзакция: статус, баланс, журнал
await updateWhere({ id, status: from }, { status: to })
await applyBalanceEffects(id, to)
await orderEvent.create({ id, from, to, actor })
} Условие status = from защищает от гонки двух операторов. Письма и Telegram уходят только после коммита.
В базе нет запятых
Рубли лежат в копейках, токен в миллионных долях. Деньги считает один модуль, округление одно: вниз. Тест читает его исходник и падает, если там появится число с плавающей точкой.
в базе 1000037 копеек
в базе 12345678 миллионных
Прибавьте 10 копеек
Плавающая точка
расхождения пока нетЦелые копейки
0 разВебхук банка приходит без подписи
Поэтому он только повод спросить банк. Состояние платежа всегда проверяется отдельным запросом. Выберите сценарий.
заявка FK-DEMOAWAITING_PAYMENT
- вебхук: повод проверить
- GET состояние платежа у банка, взаимный TLS
- статус меняется только по ответу банка
Заявка с уже созданным платежом закрывается с отсрочкой 2 минуты. Если оплата пришла позже, оператор получает тревогу.
Обновление без перезагрузки
Отпечаток данных и опрос раз в 3–4 секунды. WebSocket не брал: прокси пришлось бы настраивать ради той же задержки. Фоновое обновление дало три настоящие ошибки. Одна закрывала открытый диалог оператора.
Лимиты запросов считаются по пользователю, а не по IP. Иначе сосед за одним NAT отключал бы опрос.
опрос a41f09c2
Откройте диалог, начните писать и дождитесь опроса.
Одно приложение, один обработчик
- finkit/434 файла на момент передачи
- витрина/вход по коду на почту, страница заявки
- кабинет/верификация, баланс, вывод отдельным действием
- админка/заявки, выводы, доступы, TOTP с шифрованием секрета
- обработчик/статусы, подтверждения, фоновая сверка платежей
- тесты/Vitest и сквозные на Playwright
- инструкция.mdзапуск с чистой машины, проверена на распаковке


Два контура рядом
Тестовыйсвоя веткасвоя базасвой домен
Боевойсвоя веткасвоя базасвой домен
Боевой контур подготовлен рядом с тестовым, инструкция лежит в репозитории. Запуск ждёт боевые ключи заказчика.
Заказчику тоже нужна таблица статусов
Заказчик однажды написал, что не видит, что сделано и что осталось. Я в это время считал, что жду ответа от него. Теперь на командных проектах веду документ, который открывается без разработчика.
- Сделано
- приложение, тесты, стенд, боевой контур
- В работе
- правки главной
- Ждём от вас
- боевые ключи
- Что я делал
- Всё приложение с нуля: витрина, кабинет клиента, админка операторов, фоновый обработчик, тесты, стенд и боевой контур
- Кто ещё работал
- Другой разработчик команды подключился в конце августа. Интеграции с внешними поставщиками, сервер стенда, оплата по QR, дальше правки главной. Постановка и связь с заказчиком на менеджере
- Когда
- август – октябрь 2026
- Инструменты
- Next.js 16, React 19, TypeScript, Tailwind CSS 4, PostgreSQL 17, Prisma 7, Node.js 24, tronweb 6, Zod 4, Vitest, Playwright, Docker