Интеграция платежных решений

Хранить баланс в MySQL — это не архитектура, а билет в двойное списание

Хранить баланс в MySQL — это не архитектура, а билет в двойное списание

Ledger — это не таблица `users.balance`, а журнал неизменяемых проводок. Баланс должен считаться как производная от событий, а не лежать в одном поле, которое кто-то обновил в транзакции и надеется, что всё переживёт холд, ретрай и вебхук с задержкой.

Почему `UPDATE balance = balance - 100` ломает систему:
— ретрай запроса превращает одну оплату в два списания;
— гонки между авторизацией, capture и refund рвут инварианты;
— откат приложения не откатывает внешний PSP, и у вас уже рассинхрон. Идемпотентность или смерть.

Нормальный ledger строится так: одна запись — одно финансовое событие, с `entry_id`, `account_id`, `amount`, `direction`, `status`, `idempotency_key`. Пишете только append-only, а текущий баланс либо считаете из проводок, либо держите в проекции, которую можно пересобрать. MySQL тут может быть хранилищем, но не местом, где живёт истина.

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

Если у вас баланс — это одно поле в одной таблице, у вас не ledger, а костыль на костыле и финтехом погоняет.
Этот пост опубликован в Telegram-канале Интеграция платежных решений. Подписаться можно по ссылке: @payment_integration_ops_arb.
tech

Свежие посты в категории «Tech Infrastructure»

Все каналы категории →

start

Готовы запустить рекламу через сеть public.tg?

Новый оффер, продукт, GEO, кейс, событие или партнёрский запуск — соберём маршрут под задачу и отдадим медиаплан.

Telegram для медиаплана: @AFFtop_connect. Быстрый тест: $20 за канал, $1000 за пакет по сети.