Как правильно интегрировать платежные сервисы с разными бизнес-правилами на id-транзакций?
Всем привет!
Три года назад, я и моя команда, создали интернет-банкинг для одного банка и с тех пор сопровождаем его. За это время у нас появилось множество всяких интеграций с внешними системами:
* ИБСО (текущие счета, депозиты, конвертация валют);
* Карточный процессинг нашего банка
* Сторонний карточный процессинг
* Несколько поставщиков услуг
* Партнерские системы автокредитования
* Несколько микрофинансовых организаций
Пользователь может:
1 делать переводы
* С-Карты-На-Карту,
* С-Карты-На-Счет/Депозит,
* Со-Счета/Депозита-На-Карту,
* Cо-Счета/Депозита-На-Счет/Депозит;
2 Делать платежи за любою услугу (телефон, комуналка, steam) с любого вышеупомянутого источника.
3 Погашать кредиты;
Архитектура у нас микросервисная, под каждую интеграцию есть шлюз - отдельное ПО (сервис), который имеет свою БД и свой учет всех проходящих сквозь него транзакций. Также есть один сервис-координатор, который вообще решает какие гейтвеи учавствуют в проведении платежа и в каком порядке, именно он генерирует UUID - общий для всех участников идентификатор, которым пользуются все остальные.
Процесс, на уровне НАШИХ сервисов, примерно такой:
1 в сервис-координатор приходит платежное поручение;
2 сервис-координатор идентифицирует тип платежноного поручения. Из типа поручения вытекает список шлюзов-участников;
3 сервис-координатор выясняет у всех участников готовность принять платеж, сюда же относится проверка на право проведения платежных поручений пользователем-инициатором;
4 сервис-координатор дает шлюзам-участникам указание на исполнение их части сделки;
Процесс, на уровне взаимодействия ШЛЮЗОВ С ВНЕШНИМ API, вписывается в два варианта:
1 Мы отсылаем наш ID (UUID) на создание транзакции
1.1 Получаем ответ, который не является ошибочным
1.2 Опрашиваем по расписанию статус транзакции ПО НАШЕМУ ID
2 Мы отсылаем наш ID (UUID) на создание транзакции
2.1 Получаем ответ, содержащий EXTERNAL_ID;
2.2 Опрашиваем по расписанию статус транзакции ПО ВНЕШНЕМУ EXTERNAL_ID.
Каким-то образом ВСЕ произведенные нами интеграции за последнии три года, позволяли использовать в сделаках наш UUID. Был правда один случай, когда внешняя система ожидала только тип INTEGER для идентификатора, мы решили это создав таблицу с целочисленным ключом заполняющимся AUTOINCREMENT. В этот шлюз приходил наш UUID, который сохранялся AUTOINCREMENT-таблицу, а дальше отправляли уже целочисленный ID, который мапился на UUID.
Теперь к проблеме!
Прилетела задача (точнее 5 задач) на интеграцию сразу с 5-ю платежными системами и ни одна из них не принимает UUID в качестве идентификаторов с нашей стороны! По разным причинам, с разными бизнес-правилами.
1 У одних идентификатор должен содержать ТОЛЬКО ЦИФРЫ, может быть длиной в МАКСИМУМ в 10 символов и МИНИМУМ в ЧЕТЫРЕ!
2 у других строковый идентификатор 30 символов (UUID не влезет);
3 у третих идентификатор строковый, МАКСИМУМ 15 символов, но ДОЛЖЕН содержать хотябы одну цифру, поэтому тут выгодней только цифры слать;
4 у оставшихся двоих все просто: целочисленный 10 символов
Хочу узнать как вообще принято правильно разруливать такую интеграцю? Есть ли best practices? Мне почему-то не хочется создавать отдельную таблицу с AUTOINCREMENT просто для маппинга UUID->INT, но других вариантов не вижу.
Отдельно прошу подсказать как бы вы реализовали генерацию идентификаторов для правил описанных в п.1
вам не нужно генерировать свой идентификатор для каждого, достаточно одного цифрового идентификатора, который вы делаете по автоинкременту, вот и скармливайте его им, там где есть лимит на минимум добавляйте слева или справа 9999...
Когда работаешь с чужими сервисами и не может повлиять на них, ничего кроме самостоятельно поддерживать свою связку части их базы со своей, и слава богу что у вас только один идентификатор! а не так, что может понадобится заводить и следить за состоянием части базы из стороннего сервиса.
Если я правильно понял вопрос, то да - независимые и их сильно больше чем я описал.
В общем есть сервис transactions с которого и начинается процесс. Этот сервис генерит UUID (назовем его внутренним id-транзакции), а также по типу транзакции определяет какие "ведомые" сервисы-шлюзы будут учавстниками транзанкции. Тут старались воплотить оркестрируемый паттерн SAGA. Этот единый UUID рассылается в "оркестирируемые" сервисы, а дальше уже варианты. В те API, что принимают UUID мы отсылаем тот же самый UUID. Там где UUID не принимают мы думали использовать AUTOINCREMENT INT, но я решил поискать best practices и не найдя их задал вопрос тут.
Я просто задумался как быть когда таблица распухнет, шардировать будет сложнее.
Независимые это значит совпадение идентификаторов одной и той же транзакции в разных сервисах не помешает их работе.
Или к примеру сторонний сервис хочет сам создавать идентификаторы, у него например шардинг так настроен, в остатке от деления идентификатора на N скрыт номер узла, в котором идет обработка (где N - количество нод), а тут вы со своим идентификатором так не специально направите нагрузку не туда (например там 2 узла, четные идентификаторы на A нечетные на B, ваш сервис к примеру на определенный паттерн поведения создает парами транзакции, первую направляет в один сервис вторые надругой, вот наш сервис и получает только четные и плачет)
p.s. последнее что вас должно волновать - размер базы, так как стоимость места на порядок дешевле стоимости процессорного времени... лучше больше хранить но быстрее ехать
p.p.s. почему именно UUID? лично мне они не очень нравятся, дорогие по обработке, и единственный бонус - гарантированно уникальные без необходимости создавать единый сервис генерации этих идентификаторов, что актуально когда у вас шардинг на 100500 нод.
Независимые это значит совпадение идентификаторов одной и той же транзакции в разных сервисах не помешает их работе.
Независимые!
...почему именно UUID?... ...единственный бонус - гарантированно уникальные...
Гарантированно уникальные - это не маленький такой бонус:
1 Проще расследовать инциденты, искать в логах какое-либо число такое себе удовольствие!
2 Проще формировать отчетность. Отчетностью занимаются другие люди, которые при заполнении DWH и витрин в скриптах постоянно путали между собой целочисленные идентификаторы, которые то и дело пресекаются случайным образом в разных сущностях; Т.е. ребята скриптом собирали все данные из почти 40 БД в одну БД (DWH), чтобы потом расчитать и заполнить витрины (DATAMARTS). Пока использовали INT, постоянно саппортили их, а с UUID, они к нам даже не обращались, если связь не построилась - значит не стой колонкой сопоставляешь!
3 есть требование от безапасников скрыть количество пользователей/транзакций, а также сделать максимально трудной предугадываение идентификаторов для новых сущностей. Вы не представляете сколько фрод-атак происходит! Также этот аргумент был первым от команды, у которой мы заказывали Пентест.
4 Идентификатор сущности может быть получен до фактической ее записи в БД.
ksimmi, Изначально были INT, они так бы и остались, потому что переделывать дорого, опасно и вообще ЛЕНЬ. Потом прилетело требование от безопасников по результатам пентеста - передалали. Остальные, описанные плюсы выявили в процессе эксплуатации.