Архитектура FinTech-леджера — это подход, при котором каждое движение денег или актива фиксируется неизменяемыми journal entry, система соблюдает правило двойной записи, а балансы выводятся из этих записей. Ордеры, переводы и платежи по-прежнему нужны, но они описывают workflow, а не являются единственным источником финансовой истины.
Статус «payment succeeded» сам по себе не отвечает на вопросы: сколько фактически списано у клиента, какую комиссию получила платформа, что остаётся в расчётах с провайдером и как отразить последующий reversal. Леджер отвечает на них через сбалансированные проводки с сохранённой историей.
POLADO проектирует торговые и финансовые продукты так, чтобы пользовательские сценарии, интеграции и операционные процессы не противоречили друг другу. Подробнее о подходе — в услуге FinTech и trading-системы.
Почему изменяемых записей ордеров и платежей недостаточно
Объекты ордера и платежа обычно проходят статусы created → pending → succeeded → reversed. Это удобно для интерфейса и оркестрации. Но при изменении одной записи предыдущая картина состояния перезаписывается. По её финальным полям бывает невозможно восстановить, какая сумма была зарезервирована, какая комиссия списана отдельно и почему позже появилась корректировка.
В ledger-модели бизнес-событие создаёт journal transaction из двух или более entry. Сумма дебетов и кредитов должна быть равна нулю. У каждого движения есть вторая сторона: деньги клиента, доход платформы от комиссии, расчётный счёт процессинга, обязательство перед merchant, дебиторская позиция брокера или другой явно определённый счёт.
Это не формальность из бухгалтерии, добавленная после разработки. Это инженерный инвариант: система должна отвечать на вопрос «откуда пришла эта стоимость и куда она ушла?» через проводки, а не через последовательность перезаписанных статусов. Надёжная ledger transaction объединяет entry так, чтобы они были проведены все вместе или не были проведены вовсе.
Базовая модель: счета, journal transaction и entry
Разделяйте счета по финансовому и операционному назначению
Ledger account — это не только общий «баланс пользователя». Продукту могут понадобиться отдельные счета для:
- доступных денежных средств или доступного актива пользователя;
- средств в резерве под ордер, вывод или платёжную авторизацию;
- дохода от комиссии и начисленной комиссии платформы;
- расчётов с банком, процессором, брокером или кастодианом;
- обязательств перед merchant, seller или партнёром;
- операционных корректировок и suspense-позиций.
Счета могут быть разделены по tenant, пользователю, валюте, инструменту, провайдеру или операционной сущности. Таксономию счетов и направления debit/credit нужно определить заранее: от них зависят балансные проекции, сверка и возможность объяснить конкретную позицию службе поддержки.
Одна journal transaction должна отражать одно финансовое событие
Capture платежа, исполнение сделки, внутренний перевод между wallet или списание комиссии могут создавать отдельную journal transaction. Для неё полезны устойчивый идентификатор, тип события, effective time, время создания, исходная система, correlation ID и внешние reference. Каждая entry должна содержать счёт, сумму, валюту или актив и направление debit/credit.
Номер ордера, reference платёжного провайдера и описание можно хранить как metadata. Но после posting финансовый эффект — счета, суммы и направления — не следует менять на месте.
Неизменяемость не означает невозможность исправления
Immutable ledger не делает ошибку вечной по экономическому результату. Он запрещает тихо переписывать историю. Исправляйте проведённую ошибку новой сбалансированной transaction типа reversal или adjustment.
Например, если вместо комиссии 2 USD платформа провела 20 USD, не редактируйте исходную entry. Проведите reversal, нейтрализующий эффект 20 USD, затем отдельную transaction с правильной комиссией 2 USD. В пользовательском интерфейсе можно показать итог, а оператору останется видна цепочка: исходная операция, причина корректировки и её результат.
То же необходимо для trading-событий. Если брокер позже скорректировал fill, представьте изменение отдельной связанной корректировкой, а не изменением старого fill до неузнаваемости. В финансовых аналитических продуктах, подобных ESFX, критична прослеживаемость между событиями-источниками, расчётами и итоговыми балансами.
Явно моделируйте holds, releases и available balance
В wallet- и торговых продуктах available balance часто не равен общему балансу. Пользователь может разместить заявку на покупку до внешнего settlement, однако эти средства уже нельзя использовать для другой операции.
Понятная модель разделяет available и held позиции. При создании hold стоимость переносится со счёта available на счёт held. При отмене заявки release возвращает её обратно. При частичном исполнении только использованная часть переходит в settlement и fee entry, а остаток освобождается.
Такой подход надёжнее, чем хрупкая формула «available = total − active holds». Каждый hold, частичное использование и release становятся отдельным событием. Поддержка может объяснить, почему операция отклонена, а инженерная команда — воспроизвести историю счёта при расследовании.
Idempotent posting и конкуренция: не допускайте двойных движений
Тайм-ауты сети, повторные webhook и доставка сообщений «как минимум один раз» делают дублирующиеся запросы штатной ситуацией. Endpoint для posting в леджер должен принимать idempotency key и сохранять его вместе с fingerprint запроса и результатом. При повторе того же key с тем же payload система должна вернуть исходный результат, а не создать новую journal transaction. Тот же key с другим payload должен быть явной ошибкой.
Одной идемпотентности недостаточно, если два параллельных запроса пытаются потратить один available balance. Posting должен быть атомарным: проверка правил, вставка всех entry и обновление нужной projection выполняются в одной транзакции базы данных либо в эквивалентной надёжной границе. Также нужен управляемый retry при конфликтах. Например, serializable isolation способна обнаружить результат параллельной работы, который нельзя получить при последовательном выполнении, но приложение должно уметь повторять transaction после serialization failure.
Проекции баланса — это read model, а не сам леджер
Не нужно суммировать миллионы entry при каждом открытии пользовательского кабинета. Для быстрых запросов полезна balance projection, materialized view или специализированная read model. Но это слой производительности, а не source of truth: его необходимо уметь полностью пересобрать из проведённого леджера.
Практический вариант:
- Хранить posted journal transaction и entry как append-only основу.
- Обновлять account-balance projection в том же commit либо через надёжный outbox/event-механизм.
- Сохранять версию projection и номер последней обработанной ledger sequence.
- Периодически пересчитывать выбранные счета по entry и направлять расхождения в exception queue.
Запоздавший cache или поисковый индекс — это проблема пользовательского опыта. Необъяснимое расхождение между леджером и projection — операционный инцидент, у которого должен быть владелец.
Reconciliation и exception queue: сопоставляйте внутреннее ожидание с внешним подтверждением
Внутренний леджер фиксирует то, что, по мнению продукта, должно было произойти. Банк, платёжный процессор, брокер или кастодиан дают внешнее подтверждение того, что они зафиксировали. Поэтому trading platform balance reconciliation и сверка платежей должны сопоставлять ожидаемые settlement, fill, комиссии и переводы с webhook, выписками или batch-файлами провайдера.
Разделяйте результаты на понятные состояния: matched, missing external, missing internal, amount mismatch, duplicate, late event и needs review. Не проводите необъяснимую разницу на баланс пользователя автоматически. Создавайте exception record с reference, суммами, временем, данными провайдера или строкой выписки и назначенным процессом проверки.
Такой подход создаёт основу для масштабируемых операций в FinTech-продуктах, подобных FT10: engineering-команда отвечает за безопасный posting и воспроизводимость, а operations-команда получает объяснимые кейсы вместо непонятных итоговых цифр.
Чек-лист архитектуры FinTech-леджера перед релизом
- Равны ли суммы debit и credit в каждой posted transaction?
- Неизменяемы ли проведённые финансовые entry, а corrections оформлены новыми transaction?
- Есть ли для каждого внешнего события устойчивый source reference и стратегия идемпотентности?
- Протестированы ли отдельно holds, частичное использование, releases, fees и reversals?
- Одинаково ли определены available, held, pending и settled в UI, API и ledger?
- Можно ли пересобрать balance projection из entry и обнаружить расхождения?
- Попадают ли несоответствия reconciliation в проверяемую exception queue?
- Может ли support или back-office объяснить состав баланса до уровня отдельных entry?
Сначала леджер, затем сложная балансная логика
Статусы ордеров, платежей и переводов остаются важными понятиями продукта. Но они не являются неизменяемым доказательством финансового состояния. Грамотно спроектированная архитектура FinTech-леджера балансирует движения стоимости, контролирует повторный posting, сохраняет историю корректировок, делает быстрые balance view проверяемыми и создаёт надёжную основу для reconciliation.
Если вы создаёте wallet, платёжный workflow или trading-продукт либо хотите заменить изменяемую балансную логику, которой уже трудно доверять, POLADO поможет оценить data model, posting flow, reconciliation и back-office инструменты в рамках услуги FinTech и trading-системы.