Fuel Processing Payment Platform

Introduction

Systematically surveying the market of payment solution developers, a fuel operator approached us. They offer both companies and individual users the convenience of refueling vehicles through a mobile application. As a crucial step in the company's evolution, they decided to introduce an additional feature – the ability to refuel not only through the app but also with a bank card via a terminal directly installed on the fueling column. Undoubtedly, this serves as a valuable enhancement to the operator's existing services.

As is customary, we kicked off this project by delving into its vision and implementation requirements. Initially, our client intended to implement their own pre-processing, which eventually revealed itself as a costly and time-consuming idea. Leveraging our extensive experience in crafting customized payment solutions, we meticulously designed the interaction scheme between the equipment and services integral to the project.

At the core of our project was the development of a payment platform for fuel processing. The platform, or service, we crafted comprises various modules, with the conversion module standing out as pivotal. Simply put, this module dynamically converts transactions between two protocols, SmartVista and 3CardF. While the concept of protocol conversion is not new to us, it's in our project involving fuel terminals that our service has evolved into a fully-fledged commercial solution with exceptional flexibility. Our service now empowers the seamless adaptation and replacement of individual modules tailored to the customer's specific requirements.

Let's delve into the project details, starting with the architecture.

Project Architecture

The project requirements did not impose constraints on the technology stack, hence Python was chosen as the primary programming language This language offers great ease and convenience in implementing services of any complexity. In addition, our team has a lot of Python development experience, which was definitely a big plus.

We began contemplating the solution's architecture and settled on the following iteration:

The service consists of several modules:

  • Transaction Storage
  • Integration Module
  • Shift Auto-Closing Module
  • Conversion Module
  • Terminal module (TCP communication)
  • API module (https communication).

The logic is as follows - the client sends us a transaction in SmartVista format (any other protocol can be used here). Next, we convert the transaction to the 3CardF protocol and send it to the bank through the JoinPSP pre-processing Upon receiving the bank's response, we convert it back to SmartVista and send the response to the client, simultaneously saving transaction information in MongoDB.

If the client needs transaction details, they can access the service through the API.

If the client needs transaction details, they can access the service through the API. Initially, there was no need to store transaction data for an extended period in the project. Based on this, we used Redis as a temporary storage. However, during testing, we quickly realized that our Redis cluster was somewhat unstable (for this particular service). Therefore, we decided to replace it with MongoDB, thanks to the module's simple architecture .

Since we already have integration with the bank through our pre-processing, we seamlessly incorporated it into the service. Moreover, the project was conceived as a proxy, so it was initially anticipated to operate through it. The process is quite straightforward – you send a transaction and receive a response.

The shift auto-closing module is only necessary if, for some reason, the device cannot close the shift at the end of the day or if it wasn't designed in the project architecture. The script is scheduled to run via cron, closing the shift for the specified terminals.

Delving a bit deeper, within our API, there's a specialized method designed for closing shifts by terminals, and the script seamlessly leverages it as needed.

As we wrap up the architectural aspect of the project, we'd like to shed light on our logging system. It operates on a simple yet effective setup using the logging package. In the event of critical errors, we've implemented a system that promptly notifies our dedicated Telegram bot (with plans for Mattermost integration in the pipeline). This approach significantly streamlines our troubleshooting process, ensuring a swift response to any service-related challenges!

Next, let's move on to the conversion module and look at the process itself.

Conversion

Of course, there is no special secret here, and we can slightly open the veil of secrecy over the conversion module, which has a couple of interesting points that are definitely worth paying attention to.

The converter is designed as a standalone service for easier updates and potential use in other projects.

Skipping over the API wrapper, as it's not the main focus here, let's dive straight into the essence!

All message conversion takes place through an intermediary representation. We utilize JSON as the intermediary representation.

So, the conversion from SmartVista to 3CardF looks like this:

SmartVista → JSON → 3CardF

The choice of the JSON format was deliberate, primarily because almost all our protocol libraries can easily collect transactions from it. It's much simpler than creating wrappers for each function used in the library.

Our protocol library is implemented in C++ standard 98, making it easy to port to any device we use. Similarly, we have bindings (integrations) for several languages we use. For example, in Python, it's implemented using pybind11.

However, let's not delve into the details of our protocol libraries. The main problem of conversion between protocols is converting the format of fields from one protocol to another.

In general, conversion between protocols based on ISO 8583 is not that difficult. For example, the conversion between 3CardF and TPTP (TranzWare POS Terminal Protocol) is much more difficult to implement!

In the case of MTI, the transformation is basically to compose a simple match. Like an echo:

0800 (SmartVista) ←→ 1800 (3CardF)

But, for example, field 22 (Point of Service Data Code in 3CardF ) has to be converted into a set of fields P062.1 - P062.3 .

There are almost no problems with the rest of the fields, as many are converted without any changes, and for some you only need to manually set them to one of the standard values.

Terminal module

Currently, the terminal module supports three types of requests:

  • Payment;
  • Technical Cancellation;
  • Echo.

This list can be easily expanded as needed, but at the current stage, the data requests were sufficient for the operation.

Moving on to the stages of the module's operation, they turn out to be quite typical:

  1. Receiving a transaction (in SmartVista format).
  2. Conversion (to 3CardF).
  3. Sending to the bank (via JoinPSP).
  4. Response Conversion (in SmartVista).
  5. Response to client.
  6. Updating the data in the database.

API module

Let's start with the same as in the description of the last module - supported requests:

  • Transaction Status;
  • Cancellation (full and partial);
  • Shift Closing;
  • Healthcheck.

If the first three points are clear, why the healthcheck?

Since we use HAProxy for load balancing, there's a need for a separate route in such small services. It makes it much easier to check whether the service is alive or not. Also, we prefer not to clutter the logs with incorrect requests to any of the main routes.

As for the first three types of requests, the situation is similar to the previous section — we receive, process, and respond. The key is not to forget about error handling and data validation.

It's worth mentioning that the service is built on the Falcon framework. As of today, you could choose any other from the multitude of available frameworks (flask, fastapi, ...).

There are often debates about the functionality of frameworks, but in our specific case, Falcon fully meets all the requirements of this project

Gitlab CI/CD

When discussing the service, it's essential not to overlook a crucial stage that has become a standard in development — CI/CD.

Our project's CI/CD consists of just four stages.

The first stage, 'prepare,' biulds the basic docker containers. It is divided into a base image ('base') and a container for lint and test stages ('dev').

It's worth noting that all containers are stored in the Container Registry, simplifying their deployment and reuse. Docker (in some cases, Podman) has become one of our most important development tools, significantly streamlining the deployment of the final solution.

The lint and test stages involve running utilities such as black, ruff, pylint, and pytest, safeguarding the project against poor style and numerous errors. While isort and flake8 could be used here, currently, ruff is starting to replace many of them, at least partially.

In the final 'build' stage, a working container is assembled, ready for use in the test and production environments.

Afterword

Consequently, the client received a ready-made interaction scheme for all services and a flexible payment platform for fuel processing, enabling the project's launch and scaling within record timeframes.

As of now, our client is completing the project's expansion, reaching around 2500 refueling stations, each fitted with 6-8 terminals.

On our part, we're ready to extend a similar service to any project seeking a protocol upgrade on terminal devices or a seamless transition to API, without the need for implementing complex banking protocols.

Empowering our clients with top-notch payment solutions is our unwavering commitment. Elevate your payment experience with us!

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

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

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

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