У більшості програм баг — це інцидент. У фінтеху баг — це зобов'язання, яке, можливо, вже коштувало грошей, і цього ще ніхто не помітив.
В архітектурі фінтеху є невелика кількість рішень, що справді не підлягають обговоренню. Помиліться в них — і жодна подальша інженерія цього повністю не надолужить: ви успадкуєте систему, де гроші можуть бути беззвучно неправильними, а це єдиний режим відмови, якого фінансовий продукт не витримує.
1. Баланси виводяться, а не зберігаються як істина
Найпоширеніша і найруйнівніша помилка — змінна колонка балансу, яку код оновлює напряму. Це зручно, швидко і невідновно: коли вона поїде, немає способу визначити, якою вона мала бути.
Правильна модель — журнал проводок лише на дозапис. Нічого ніколи не оновлюється і не видаляється; виправлення — це нові компенсаційні проводки. Баланс є сумою проводок: кешується для швидкодії, але завжди відтворюється з журналу.
| Змінний баланс | Журнал append-only |
|---|---|
| UPDATE accounts SET balance = balance - 100 | INSERT проводка (дебет 100), INSERT проводка (кредит 100) |
| Історія втрачається при кожному записі | Повна історія за самою конструкцією |
| Розбіжність невиявна і невиправна | Будь-який стан відтворюється на будь-який момент |
| Помилки конкурентності псують стан назавжди | Інваріант подвійного запису ловить помилки |
| Аудит означає читати логи застосунку | Аудит означає читати журнал |
2. Кожен грошовий шлях ідемпотентний
Мережі повторюють запити. Користувачі клікають двічі. Платіжні провайдери доставляють вебхуки більше одного разу — це задокументована поведінка, а не крайній випадок. Будь-яка операція, що рухає гроші, має бути безпечною при повторному виконанні.
- Кожна грошова операція несе ключ ідемпотентності від викликача, унікальний для логічного наміру
- Ключ зберігається з обмеженням унікальності до побічних ефектів, а не після
- Повторений ключ повертає початковий результат замість виконувати роботу знову
- Обробники вебхуків спершу зберігають сирий payload за ідентифікатором події провайдера, а вже потім обробляють — так повторна доставка розпізнається навіть посеред обробки
3. Гроші — це цілі числа в мінорних одиницях
Число з плаваючою комою не може точно представити десяткові дроби, тому арифметика над грошима у float накопичує похибку. Зберігайте мінорні одиниці цілими числами (або десятковим типом фіксованої точності) і робіть валюту явною біля кожної суми — сума без валюти це баг, що чекає на першого міжнародного клієнта.
4. Звірка — це функція, а не запізніла думка
Припускайте розбіжність між вашим журналом і платіжним провайдером — вона станеться через таймаути, часткові відмови і виправлення на боці провайдера. Питання в тому, чи ви її виявите.
- Регулярна задача, що порівнює внутрішній стан із даними сетлменту провайдера
- Сповіщення при розбіжності, з визначеним відповідальним і runbook
- Визначений шлях розв'язання — компенсаційні проводки, ніколи тиха корекція
- Прочісування завислих станів: транзакції в pending довше, ніж мали б
5. Ізоляція в обох сенсах
- Ізоляція орендарів — забезпечена на рівні даних, а не сподіванням, що кожен запит містить правильний фільтр
- Ізоляція відмов — повільний провайдер не має вичерпувати пул запитів і класти непов'язану функціональність
- Ізоляція середовищ — жодних тестових транзакцій у продакшн-журналах
6. Будуйте під аудит, який зрештою настане
Ні. Він надає технічні висновки в погоджених межах. Правова оцінка й формальна сертифікація потребують відповідних кваліфікованих спеціалістів.
- Незмінний аудиторський слід, хто що і коли робив — включно з внутрішніми адміністративними діями
- Дані карток ніколи не торкаються ваших серверів, якщо ви справді не збираєтесь бути PCI-сумісними (використовуйте hosted fields або hosted-сторінку)
- Зберігання і видалення даних, які реально можна виконати на запит
- Контроль доступу, який можна переглянути — хто може рухати гроші, хто це схвалив
У звичайному ПЗ ви оптимізуєте швидкість змін. У фінтеху ви оптимізуєте здатність довести згодом, що саме сталося і чому.
П'ять питань до власної системи
- Якщо цей вебхук прийде двічі, чи зміниться щось на другий раз?
- Чи можу я відтворити баланс будь-якого клієнта на будь-який момент минулого лише з журналу?
- Чи виявили б ми розбіжність із провайдером, чи нам сказав би про неї клієнт?
- Чи може підроблений запит прочитати або перемістити гроші іншого орендаря?
- Якби регулятор запитав, хто схвалив ручне коригування торік у березні, чи відповіли б ми за кілька хвилин?
Розділяйте прийняття транзакції та розрахунок
Платіжні стани мають пояснювати запізнілі й повторні події. Перевіряйте фінансові записи разом із процедурою корекцій.
- Використовуйте точні десяткові або цілі мінімальні одиниці за правилами валюти.
- Зіставляйте події й записи без подвійного зарахування повторів.
- Зберігайте оригінали та слід погоджених корекцій.
Часті запитання
Чи потрібен подвійний запис для простого фінтех-продукту?
Якщо ви тримаєте баланси або рухаєте гроші від імені користувачів — так. Журнал лише-на-дозапис не бухгалтерська формальність: саме він робить стан відтворюваним, а помилки — виявними. Додавати його після того, як баланси поїхали, значно важче, ніж почати з нього.
Який найпоширеніший серйозний недолік у ранніх фінтех-системах?
Змінні баланси без журналу, а одразу за ними — відсутність ідемпотентності на платіжних шляхах і вебхуках. Обидва дозволяють грошам стати беззвучно неправильними, і це найважчий для виявлення та найважчий для подальшого усунення режим відмови.
Чи варто зберігати дані карток у себе?
Майже напевно ні. Використовуйте hosted fields або hosted-сторінку оплати провайдера, щоб дані карток ніколи не потрапляли на ваші сервери. Робота з ними самостійно втягує всю вашу інфраструктуру в область дії PCI DSS, а це значний постійний тягар відповідності та аудиту.
Коли фінтех-стартапу робити аудит архітектури?
До першого суттєвого зростання обсягів і перед будь-яким раундом, де очікується технічний due diligence. Наведені структурні рішення дешево перевіряються рано і дорого виправляються після того, як через систему пройшли справжні гроші.
Чи всі валюти мають два десяткові знаки?
Ні. Визначте точність і округлення за валютою та операцією і перевірте вимоги провайдера. Уникайте binary float там, де потрібні точні десяткові обчислення.
Хочете перевірити це на своїй системі?
Ми аудитуємо фінтех-архітектуру саме за цим переліком — цілісність журналу, ідемпотентність, звірка, ізоляція.
Читати далі за темою
Технічний due diligence для інвесторів: повний посібник
Технічний due diligence — це не code review. Це відповідь на одне питання: скільки коштуватиме привести цю технологію туди, де її потребує інвестиційна теза?
Аудит архітектури системи: коли він потрібен і що знаходить
Аудит архітектури — це не думка про ваш стек. Це карта того, де система ламається під планом, який ви справді маєте.