Как хранить деньги в базе данных
Владислав
17 нояб. 2025 г.
Лучшие практики работы с валютами
Предлагаемый подход: Целые числа в младших единицах
Мы используем подход Integer Minor Units (аналогично Stripe, Adyen, Klarna, MasterCard):
- Хранение: Суммы хранятся как целые числа в наименьшей единице валюты
- RUB: хранится в копейках (1 RUB = 100 копеек)
- USD: хранится в центах (1 USD = 100 центов)
- JPY: хранится как есть (нет младшей единицы, 0 знаков после запятой)
YooKassa, например, использует формат String Base Units (например, "499.00") в своём API.
А внутреннее хранение всегда использует младшие единицы.
Почему этот подход?
Преимущества
1. Точность: Нет ошибок с плавающей точкой
2. Производительность: Целочисленная арифметика быстрее
3. Отраслевой стандарт: Используется крупными платежными процессорами
4. Типобезопасность: Четкое различие между базовыми и младшими единицами
Компромиссы
- Требует утилитарные функции конвертации
- Менее интуитивно, чем хранение "499.00" напрямую
Утилиты для валют
Все конвертации валют используют централизованные утилиты в `src/shared/lib/currency.ts`:
// Конвертировать базовые единицы в младшие
toMinorUnits(499.00, 'RUB') // Возвращает 49900
// Конвертировать младшие единицы в базовые
fromMinorUnits(49900, 'RUB') // Возвращает 499.00
// Форматировать для отображения
formatAmount(49900, 'RUB') // Возвращает "499.00 ₽"Соответствие ISO 4217
Мы следуем стандарту ISO 4217 для кодов валют:
- База данных: Использует enum `Currency` для типобезопасности (RUB, USD, EUR, и т.д.)
- Типобезопасность: Только допустимые коды валют разрешены на уровне базы данных
- Метаданные младшей единицы: Хранятся в `CURRENCY_METADATA` в `src/shared/lib/currency.ts`
- Поддержка: доступны валюты с 0-18 знаками после запятой
- Текущие валюты: RUB, USD, EUR, KZT, BYN, UAH, CNY, JPY, GBP
Лучшие практики
1. Никогда не используйте арифметику с плавающей точкой для денег
2. Всегда храните код валюты вместе с суммой
3. Конвертируйте валюты как можно ближе к внешнему слою (в идеале через слой DTO)
4. Используйте централизованный модуль
5. Используйте типизирование везде, где это возможно