English

Платежная платформа для топливного процессинга

Введение

Планомерно осматривая рынок разработчиков платёжных решений, к нам обратился топливный оператор, который предоставляет компаниям, а также частным лицам, возможность заправки автомобиля через мобильное приложение. Очередным этапом развития компании, было предоставление возможности заправки не только через приложение, но также банковской картой через терминал, установленный прямо на заправочной колонке. Что несомненно является удобным дополнением к текущему сервису оператора.

Как и полагается, мы начали данный проект с обсуждения видения и требований к его реализации. Изначально, наш заказчик планировал реализовать собственный предпроцессинг, что в итоге оказалось довольно дорогой и времязатратной идеей. Обладая многолетним опытом в разработке индивидуальных платёжных решений, мы самостоятельно разработали схему взаимодействия между оборудованием и сервисами, которые участвуют в проекте.

Основой нашей части проекта, являлось создание платёжной платформы для топливного процессинга. Созданная нами платформа (сервис), состоит из нескольких модулей, самым важным из которых является модуль конвертации. Говоря простыми словами, этот модуль может на лету преобразовывать транзакции между двумя протоколами SmartVista и 3CardF. Сама концепция конвертации между двумя протоколами для нас не нова. Со своей работе мы уже не раз сталкивались с подобными задачами. Однако именно в проекте с топливными терминалами, наш сервис обрёл полноценный коммерческий вид, и поистине гибкую реализацию. Теперь наш сервис позволяет адаптировать и заменять отдельные модули под необходимые требования заказчика.

Давайте же немного погрузимся в подробности проекта и начнём с архитектуры.

Архитектура проекта

В требованиях к проекту не было ограничений на используемый стек технологий, поэтому в качестве основного языка программирования был выбран Python. На данном языке очень легко и удобно реализовывать сервисы любой сложности. Кроме того, наша команда обладает большим опытом разработки на Python, что несомненно было большим плюсом.

Мы начали продумывать архитектуру решения и остановились на следующей итерации:

Сервис состоит из нескольких модулей:

  • Хранилище транзакций.
  • Модуль интеграции.
  • Модуль автозакрытия смены.
  • Модуль конвертации.
  • Терминальный модуль (общение по TCP).
  • Модуль API (общение по https).

Логика работы такова – клиент отправляет нам транзакцию в формате SmartVista (здесь может быть любой другой протокол). Далее конвертируем данную транзакции в протокол 3CardF и отправляем её в банк через препроцессинг JoinPSP. Получив ответ от банка мы конвертируем 3CardF обратно в SmartVista и отправляем ответ клиенту, параллельно сохраняя информацию о транзакции в MongoDB .

Если же клиенту необходимо получить информацию по транзакции, то он обращается к сервису по API.

Изначально, в проекте не было надобности хранить данные по транзакциям довольно долгое время. Исходя из этого, в качестве временного хранилища мы использовали Redis. Однако, в ходе тестов, мы довольно быстро поняли, что наш Redis-кластер работает довольно нестабильно (для данного сервиса). Поэтому решили его заменить на MongoDB, спасибо простой архитектуре модуля.

Так как у нас уже есть интеграция с банком через наш препроцессинг, то мы просто поддержали его работу в сервисе. Да и проект задумывался как проксирующий, так что изначально предполагалось что он будет работать через него. Тут всё довольно просто – отправляешь транзакцию и получаешь ответ.

Модуль автозакрытия смены необходим только в том случае, если по какой-то причине устройство не может под конец дня произвести закрытие смены, или в архитектуре проекта это не было предусмотрено. Скрипт закрытия запускает по cron и производит закрытие смены для списка указанных терминалов.

Если уходить в детали, то в нашем API есть специальный метод используемый для закрытия смены по терминалу, и скрипт автозакрытия обращается к нему, когда это необходимо.

Завершая повествование об архитектурной части проекта, стоит рассказать про устройство логгирования в сервисе. Его функционал довольно прост, и реализуется на базе пакета logging. При этом все критические ошибки отправляются в наш специализированный телеграмм бот (в ближайшее время будет также реализована интеграция с Mattermost). Это очень удобно и позволяет быстро реагировать на проблемы в работе сервиса!

Далее перейдем к модулю конвертации, а также рассмотрим сам процесс.

Конвертация

Особого секрета здесь разумеется нет, и мы можем немного приоткрыть завесу тайны над модулем конвертации, в котором есть парочка интересных моментов на которые обязательно стоит обратить внимание.

Конвертер сделан в виде отдельного сервиса, чтобы было проще его обновлять, а также использовать в других проектах.

Обходя стороной APIшную обёртку, так как не она тут главная, перейдём сразу к сути!

Вся конвертация сообщения происходит через промежуточное представление. В качестве промежуточного представления у нас используется формат JSON .

То есть конвертации из SmartVista в 3CardF выглядит следующим образом:

SmartVista → JSON → 3CardF

Формат JSON был выбран не просто так, а в силу того, что почти все наши протокольные библиотеки умеют собирать из него транзакции. Это намного проще, чем делать обёртки над каждой используемой функцией в библиотеке.

Наша протокольная библиотека реализована на языке C++ стандарта 98, так как её довольно легко можно портировать на любое используемое нами устройство. Таким же образом у нас реализованы биндинги (интеграции) для нескольких используемых нами языков. Например для python, он реализован с использованием pybind11.

Но не будем вдаваться в подробности наших протокольных библиотек. Основной проблемой конвертации между протоколами является преобразование формата полей из одного протокола в другой.

Если же смотреть в целом, то конвертация между протоколами основанные на ISO 8583 не такое уж и сложное занятие. Так например, конвертация между протоколами 3CardF и TPTP (TranzWare POS Terminal Protocol), намного сложнее в реализации!

В случае с MTI преобразование в основном заключается в составлении простого соответствия. Например эхо:

0800 (SmartVista) ←→ 1800 (3CardF)

А вот, например, поле 22 ( Point of Service Data Code в 3CardF ) уже нужно преобразовывать в набор полей P062.1 - P062.3 .

С остальным же полями почти нет никаких проблем, так как многие преобразуются происходят без изменений, а для некоторых необходимо лишь вручную установить в одно из стандартных значений.

Терминальный модуль

На данный момент терминальный модуль поддерживает три типа запросов:

  • Оплата;
  • Техническая отмена;
  • Эхо.

Этот список можно легко расширить по надобности, но на текущем этапе данных запросов было вполне достаточно для работы.

Если переходить на рассмотрение этапов работы модуля, то они окажутся довольно типичными:

  1. Получение транзакции (в формате SmartVista).
  2. Конвертация (в 3CardF).
  3. Отправка в банк (через JoinPSP).
  4. Конвертация ответа (в SmartVista).
  5. Ответ клиенту.
  6. Обновление данных в БД.

API модуль

Начнём с того же, что и в описании прошлого модуля — поддерживаемые запросы:

  • Статус транзакции;
  • Отмена (полная и частичная);
  • Закрытие смены;
  • Healthcheck.

Если по первым трём пунктам понятно, то зачем healtcheck?

Так как мы используем HAProxy, для балансировки нагрузки, то в таких небольших сервисах появляется необходимость в отдельном роуте. С его помощью намного легче производить проверку живой ли сервис или нет. Да и забивать логи некорректным запросом к любому из боевых роутов, разумеется, не хотелось.

Что же до первых трёх типов запросов, то ситуация схожа с предыдущей главой — получили, обработали и ответили. Главное не забыть про обработку ошибок и валидацию данных.

Стоит также упомянуть тот факт, что сервис построен на фреймворке Falcon. На сегодняшний день можно выбрать любой другой из множества доступных фреймворков, благо их очень много (flask, fastapi, …).

Обычно, можно услышать довольно много споров по поводу функционала фреймворков, однако в нашем конкретном случае Falcon полностью удовлетворяет всем запросам данного проекта.

Gitlab CI/CD

Рассказывая про сервис нельзя обойти стороной важный этап, который стал стандартным этапом в разработке — CI/CD.

CI/CD нашего проекта состоит всего из четырёх этапов.

Первый этап prepare собирает базовые докер контейнеры. Разделён он на базовый образ (base) и контейнер для этапов lint и test (dev).

Стоит сделать одну ремарку, что все контейнеры складываются в Container Registry, что очень упрощает их раскатку и переиспользование. Для нас docker (в некоторых случаях podman) стал одним из важнейших инструментов в разработке – он сильно упростил развёртывание готового решения.

На этапах lint и test запускаются такие утилиты как black, ruff, pylint и pytest, что защищает проект от плохого стиля и многих ошибок. Здесь также можно использовать isort, flake8, но на данный момент, хоть и частично, но ruff начинает заменять многие из них.

И на завершающем этапе build, производится сборка рабочего контейнера, который можно использовать на тестовой и продакшн среде.

Послесловие

Таким образом, заказчик получил готовую схему взаимодействия всех сервисов, а также гибкую платёжную платформу для топливного процессинга, что позволило произвести запуск и масштабирование проекта в рекордно сжатые сроки.

На данный момент наш заказчик завершает масштабирование проекта, а число заправочных станций с установленными терминалами оплаты приближается к 2500. При этом каждая заправочная станция оснащается 6-8 терминалами.

Со своей стороны, уже сегодня мы готовы предложить подобный сервис для любого проекта, где есть необходимость заменить протокол на терминальном устройстве, либо же полностью перейти на API, не реализовывая банковские протоколы.

Мы всегда предоставить нашим заказчикам качественные платёжные решения!

Отзыв клиента

В июле 2019 года компания ООО «Аэро-Трейд» искала подрядчика для выполнения работ по внедрению системы безналичной оплаты товаров самолетах авиакомпании «AZUR air». После тщательного изучения рынка, мы решили обратиться для реализации данного проекта в компанию ООО «МСТ Компани». Основной задачей было обеспечить каждый борт авиакомпании «AZUR air» терминалом, который сможет принимать платежи не только на земле, но и во время полёта.

В течении всего времени нашего сотрудничества, специалисты ООО «МСТ Компани» продемонстрировали отличные профессиональные навыки при подготовке проекта, и разработке документации. В результате мы получили гибкое и надёжное решение, которое удовлетворяет нашим требованиям.

По итогам работы с компанией ООО «МСТ Компани» хочется отметить соблюдение принципов делового партнерства, а также четкое соблюдение сроков работ и выполнение взятых на себя обязательств. ООО «Аэро-Трейд» выражает благодарность специалистам компании за проделанную работу в рамках внедрения системы безналичной оплаты на самолетах авиакомпании «AZUR air». И рекомендует компанию ООО «МСТ Компани» как надёжного партнёра в области платёжных решений.