← На главную

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

27.06.2026 10:28 · hackernews

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

Самое важное — как представить деньги в коде. Float и double — почти всегда плохая идея: они теряют точность непредсказуемо. BigDecimal (или аналоги) даёт контроль над округлением, годится для сложных вычислений. Для обычных фиатных валют удобнее хранить сумму целым числом в минимальных единицах (€12.34 → 1234). У криптовалют нюансы: точность per-asset (у ERC-20 часто 18 знаков), числа выходят за пределы 64 бит, нужна произвольная точность. Рациональные числа — самый мощный вариант, но медленный и неконвертируемый без потерь. Выбор зависит от системы, но float отбрасываем сразу. И да: никогда не передавайте деньги как голое JSON-число — большинство парсеров превратит его в IEEE-754 double, и проблема вернётся. Шлите строкой ("12.34") или целым.

Округление — бизнес-решение. Его надо делать явно, как можно позже, и следить за остатками: если сумму разделили и округлили части, они могут не сойтись с исходной. Валюта всегда идёт в паре с суммой — запрещайте арифметику между разными валютами, проверяйте коды на входе. Для курсов запоминайте не просто число, а источник и момент времени — канонического курса не существует.

Деньги записываются двойной записью: каждая операция — проводка между счетами (дебет/кредит). Баланс никогда не хранится, он вычисляется из движений. Проводки неизменны — ошибки исправляются новой компенсирующей записью. У транзакции три времени: когда случилась (value), когда записали (booking) и когда деньги реально дошли (settlement). Не сливайте их в одно поле created_at.

Аудит требует полной истории, а не только текущего состояния. Event sourcing — самый последовательный подход: вы храните только события, а состояние выводите из них. Это дорого (нужны проекции, schema evolution), но даёт неизменяемую цепочку, которую нельзя подделать. С GDPR проблема решается легко: финансовые записи подпадают под исключение (обычно 5–10 лет хранения), а PII держите в отдельном хранилище, которое можно чистить.

Когда выполняете денежный поток, защищайте инварианты тремя способами: конструкцией (нельзя создать невалидный объект), runtime-проверками, и постфактум-анализом (reconciliation). Резервируйте средства до взаимодействия с внешним миром, иначе получите race condition и двойную трату. Баланс может уйти в минус (overdraft) — не прячьте это типом unsigned, а обрабатывайте явно: фиксируйте, выясняйте причины, делайте компенсирующие проводки.

Идемпотентность обязательна: ретраи неизбежны, нужно гарантировать, что повторный вызов не создаст деньги из воздуха. Используйте явные idempotency keys, храните прогресс в durable storage (не в памяти), и моделируйте flow как state machine, которую можно перезапускать. Вызовы внешних API ломаются всегда — валидируйте нужное на границе, храните все запросы/ответы, не верьте схеме и готовьтесь к тому, что 200 OK может прийти с телом ошибки.

Читать оригинал →