Вводные данные
В сфере онлайн-платежей, включая банки и платежные системы, возникла необходимость в получении курсов валют для конвертации суммы платежей, особенно в случаях, когда провайдер не поддерживает валюту платежа. Для этой задачи требуются следующие функциональные возможности от потенциального сервиса:
Быстрая обработка большого объема запросов для получения курсов валют и готовых результатов конвертации.
Возможность создания собственных стратегий конвертации, учитывая различные особенности, например, принятие оплаты в одной валюте и конвертацию в другую.
Пример конкретной задачи: Банк А принимает оплату только в PLN (польский злотый), но покупатель готов оплатить это только картой с валютой EUR. Нам необходимо определить сумму в PLN для отправки запроса к банку-эквайру. Какие есть варианты решения этой задачи?
Использование среднего курса валют с небольшим процентом наценки для нивелирования недополучения дохода нашему мерчанту.
Отказ от принятия оплаты в неподдерживаемой валюте (неоптимальный вариант для бизнеса, но идеальный для разработчика).
Попросить банк добавить поддержку нужной валюты (часто невозможно).
Изначально выбрал первый вариант, который казался успешным, пока не стали поступать жалобы о том, что суммы списания превышают заявленные. Дальнейший анализ показал, что средний курс валют сильно отличается от фактического и зависит от многих факторов, таких как провайдер, который выпустил карту, правила конвертации у банка, который принимает платеж.
Пример 1: У вас карта в евро. Вы совершаете покупки в отпуске в Италии тоже в евро. Сумма покупки — 50 евро, итого с вашего счёта спишется 50 евро.
Пример 2: У вас карта в евро. Вы совершаете покупку в Польше в польских злотых. В этом случае схема пересчёта следующая: польские злотые (PLN) → пересчитываются в USD по курсу Mastercard, далее USD → пересчитываются в CNY по курсу Mastercard, затем CNY → пересчитываются в евро по курсу банка.
(ref: https://telegra.ph/Izmenenie-valyuty-raschyotov-po-kartam-Mastercard-04-03)
Для точного расчета суммы после всех конвертаций нам нужна информация о курсах валют от следующих источников:
"Mastercard" PLN -> USD
"Mastercard" USD -> CNY
"Альфа-Банк Беларусь" CNY -> EUR
С такими вводными приступаем к анализу рынка...
Анализ рынка
Напомню, мы выделили основные критерии поиска:
1) Быстрая обработка большого объема запросов для получения курсов валют и готовых результатов конвертации.
2) Возможность создания собственных стратегий конвертации, учитывая различные особенности, например, принятие оплаты в одной валюте и конвертацию в другую.
Сервис |
Плюсы |
Минусы |
+ быстрый и удобный API |
- дорого |
|
+ быстрый и удобный API |
- нет возможности создания собственных стратегий для конвертации |
|
+ быстрый и удобный API |
- нет возможности создания собственных стратегий для конвертации |
|
+ быстрый и удобный API |
- нет возможности создания собственных стратегий для конвертации |
|
+ быстрый и удобный API |
- нет возможности создания собственных стратегий для конвертации |
Изначально выбрал https://openexchangerates.org и использовал именно его, но достаточно быстро появились новые бизнес-кейсы, такие как пример с "Альфа-Банк Беларусь" и покупкой в PLN, когда этого функционала перестало хватать. Об этом упоминалось выше.
Все сервисы имели почти одинаковый набор плюсов и минусов, но главное, что ни один из них не имел того, что требовалось мне. С этого момента произошло разочарование из-за отсутствия нужного инструмента, но с другой стороны появилось вдохновение от возможности создать то, что будет работать именно под мою конкретную задачу. Благодаря этому анализу у меня возникла некоторая насмотренность на то, как крупные игроки рынка делают свои API. Поэтому в моей голове уже начала складываться база того, как это должно выглядеть: source_currency, target_currency, amount, base_currency, и поехали, думал я. Но как я ошибался, что этим все ограничится.
Разработка
Ванильное представление о своем микросервисе быстро столкнулось с жесткой реальностью, так как оказалось, что все гораздо сложнее. Набор параметров на вход стал больше, также увеличилось количество логики в коде и интеграций с источниками курсов. В конечном итоге, получился минимальный набор для активно используемых эндпоинтов (не учитывая эндпоинты для получения информации о источниках, созданных конвертациях и т.д.).
/api/v1/rates - эндпоинт, который позволяет получать курсы валют, указывая необходимые параметры.
{
"rate_source_code": "alfa_bank_belarus", // Каждый источник курса имеет свой уникальный код.
"exchange_type_code": "card", // Определяет, какой курс использовать. В банках могут быть разные курсы для карточек, наличных операций и т.д.
"category_code": "bank", // Некоторые источники могут предоставлять курсы валют разных категорий, таких как банки и биржи.
"branch": "main", // Некоторые источники курса имеют разные курсы валют для различных отделений или филиалов.
"rounding": 2, // Округление результата до указанного числа цифр после запятой.
"test_mode": true
}
/api/v1/conversions - этот эндпоинт выполнит конвертацию в соответствии с указанной стратегией, учитывая все соответствующие правила и параметры конвертации.
{
"strategy_code": "alfa_bank_belarus_custom_1", // Код стратегии обмена, который создается отдельно (следующий эндпоинт, который ниже).
"source_currency": "PLN",
"target_currency": "EUR",
"amount": 40.99,
"source_currency_unit": "amount", // Формат суммы конвертации, который может быть как в привычном формате (1.00), так и в копеечном (100). Этот параметр регулирует формат.
"target_currency_unit": "amount", // Как и выше, только это формат результата.
"rounding": 2,
"branch": "main",
"test_mode": true
}
/api/v1/conversion_strategies - для создания стратегии конвертации валют.
{
"code": "alfa_bank_belarus_custom_1",
"name": "Custom strategy for Alfa-Bank Belarus",
"data": [
{
"priority": 1, // Приоритет внутренней конвертации.
"action_type": "accurate", // Можно задавать точный курс валют у определенного источника, либо брать средний ("average").
"rate_source_code": "mastercard",
"category_code": "payment_system",
"exchange_type_code": "card",
"target_currency": "USD",
"base_currency": "BYN" // Валюта для конвертации, используемая в случае отсутствия у источника курса прямой конвертации запрошенных валют.
},
{
"priority": 2,
"action_type": "accurate",
"rate_source_code": "mastercard",
"category_code": "payment_system",
"exchange_type_code": "card",
"target_currency": "CNY",
"base_currency": "BYN"
},
{
"priority": 3,
"action_type": "accurate",
"rate_source_code": "alfa_bank_belarus",
"category_code": "bank",
"exchange_type_code": "card",
"target_currency": "REQUESTED",
"base_currency": "BYN"
}
]
}
Как только все было запущено в продакшн и протестировано, я почувствовал облегчение. Однако, спустя некоторое время осознал, что получился микросервис, который по своей функциональности объединяет все доступные на рынке API-сервисы для конвертации валют и получения курсов. Кроме того, он добавляет такие возможности, как:
Получение курсов из любых источников: банков, платежных систем, бирж и т.д. (ограничивается лишь вашим набором интеграций).
Создание собственных стратегий для конвертации, которые позволяют решать самые сложные кейсы.
Использование точной конвертации осуществляется по определенным параметрам, таким как источник курса, его тип обмена и категория. Альтернативно, можно использовать средний курс из всех источников, доступных в базе данных, с учетом фильтрации по категории источника валют.
Обсуждение в комментариях может привести к ценным идеям и обмену опытом. Если у вас возникали аналогичные сценарии или есть желание использовать более широкий функционал сервисов курсов валют, я бы с интересом прочитал ваши мысли и опыт.
Какие функции или возможности вам кажутся наиболее важными или полезными для улучшения этого сервиса? Я открыт к обсуждению и готов рассмотреть любые предложения по его улучшению.
P.S. Сам микросервис я трансформировал в отдельное API, к которому теперь есть доступ не только у меня. Если кто-то захочет использовать его или просто "потыкать палкой", то велкоме: https://irates.io.
Комментарии (5)
Breathe_the_pressure
11.05.2024 10:27+1Пример 1: У вас карта в евро. Вы совершаете покупки в отпуске в Италии тоже в евро.
Ах как хорошо мы плохо жили...
gudvinr
11.05.2024 10:27+1Все популярные API скупил APILayer, теперь fixer.io & co - платные
Раз уж вы делаете новый эндпоинт, то имеет смысл хотя бы на уровне структуры данных быть похожим на какой-нибудь существующий, иначе чтобы поддержать ещё один - надо будет каждые полгода по новому провайдеру со своей уникальной схемой добавлять
Ситуация настолько плохая, что мне пришлось библиотеку сделать, чтобы не переписывать код под каждый новый сервис. Можете посмотреть и еще пару источников себе в бэкенд утащить:
nanshakov
А что внутри, редис ?какая производитель ность ?
gogochikago Автор
Rails, PG, Sidekiq, Redis. Есть планы на эксперименты с Elixir, чтобы сравнить затрату ресурсов сервера с производительностью приложения.
Если говорить про публичную версию, которая сейчас крутиться на https://irates.io, то при текущей нагрузке, хватает 1 app сервера (4GB RAM, 2 AMD vCPUs, 80 GB SSD). На такой конфигурации app сервера, я делал load test на 80rps, ничего не улетало в потолок, "average response time" запросов был 391ms