The payment system design will be divided into two parts: concept and practice. - Payment evolution: Module to Service (Concept) - Payment services should have capabilities (Concept) - Payment database design (Practice) - Payment system Architecture and code organization (Practice)Copy the code
In this article, we talk about how payment has evolved into an independent system step by step, starting with modules. If have inadequacy place, welcome platoon brick, study together.
From modules to services
I remember when I first started working, all the functionality: add cart/order/pay logic was in one project. If a new project requires a feature, copy that part of the package to the new project. The database is also copied intact, slightly modified according to demand.
This is the era of monolithic apps, and as companies start to diversify their product lines, each of them will need payment services. If the payment module adjusts the code, it will be changed and tested everywhere. On the other hand, the transaction data of the company is fragmented in different systems, which cannot be effectively summarized, unified, analyzed and managed.
At this point it was time for the system to evolve, and we separated the payment modules from each product line into a unified service. Provide unified USE of apis within the company, and further package these apis into corresponding SDK for rapid access of internal business lines. Whether the service uses HTTP or RPC is up to the company. However, considering the future use of the third party, IT is recommended to use HTTP protocol.
Evolution of the system:
To sum up, the benefits of separating payment into services are as follows:
-
Avoid repeated development, the phenomenon of data isolation;
-
The evolution of peripheral functions of the payment system is easier, and the whole system is more complete and full. Such as: reconciliation system, real-time transaction data display;
-
It can develop and export Paas capacity at any time to become a project with income;
-
Dedicated team for maintenance, the system has more opportunities to evolve into a top system;
-
Company important account information is saved in one place, less risk.
System capacity
If we take over this requirement, we need to build a payment system for the company from scratch. Where should we start? What kind of capabilities would such a system need?
First, the payment system can be thought of as an adapter. It needs to integrate and encapsulate many third-party interfaces to provide unified interfaces to reduce the cost of internal access. As a basic payment system. The following interfaces need to be provided internally:
-
Initiate payment, we call it: /gopay
-
Initiate a refund with the name: /refund
-
Interface asynchronous notification, we named: /notify/ payment channel/merchant transaction number
-
Interface synchronous notification, we named: /return/ payment channel/merchant transaction number
-
The trade query is named /query/trade
-
For a refund query, we named: /query/refund
-
Bill fetch, we call it: /query/bill
-
The settlement details are named /query/settle
A basic payment system, the above 8 interfaces are definitely required (ignore some of the transfer and card binding interfaces in payment). Now let’s look at some of the systems that use these interfaces.
The following describes how to use these interfaces and the internal logic in terms of the system dimension.
The application system
Generally, payment gateways provide two ways for application systems to access:
-
Gateway mode, that is, the application system needs to develop a cashier desk; (Suitable for third parties)
-
In cashier mode, the application system directly opens the unified cashier of the payment gateway. (Internal business)
In order to clarify the design idea, we will explain it according to the gateway mode.
For the application it needs to be able to request payment, that is, call the Gopay interface. This interface will process the merchant’s data, and call the third-party gateway interface after completion, and return the result to the application party after unified processing.
It should be noted that according to my experience, the third-party payment interface generally has the following situations:
-
When making payment, there is no need to call a third party and generate data according to the rules.
-
When payment is made, multiple third-party interfaces need to be invoked to complete the logic (this can be slow, limiting/downsizing needs to be considered for large activities);
-
The data returned is a URL that can be directly jumped to a third party to complete the payment (WAP/PC station);
-
The data returned is an XML/JSON structure that needs to be assembled or passed as a parameter to her SDK (app).
Since the return structure of the third party is not uniform, we need to process it in a unified format and return it to the merchant end. I recommend using json format.
{ "errno":0, "msg":"ok", "data":{ }}Copy the code
We encapsulate all changes in a data structure. For example, if a URL is returned. All you need is for the application to initiate a GET request. We can return like this:
{ "errno":0, "msg":"ok", "data":{ "url":"xxxxx", "method":"GET" }}Copy the code
If it is a returned structure, the application is required to issue a POST request directly. We can return like this:
{ "errno":1, "msg":"ok", "data":{ "from":"<form action="xxx" method="POST">xxxxx</form>", "method":"POST" }}Copy the code
The form field here generates a form form that the application can display and then automatically submit when it gets it. Of course, encapsulating the FROM form can also be done on the merchant side.
The data format above is only a guide. You can adjust it according to your own needs.
In addition to the interface for initiating payment, the general application system may also need to call the interface for querying payment results. Of course, in most cases no calls are required, and the application system should only rely on its own system state for the status of the transaction.
Reconciliation system
For reconciliation, there are generally two types: transaction reconciliation and settlement reconciliation
Transaction reconciliation
The core of transaction reconciliation is to check that each transaction is correct. Its main purpose is to see if every transaction in our system is consistent with every transaction by a third party.
The logic of the check is simple: compare the two bill data. It uses the /query/bill interface to retrieve transaction data from a third party. And then compare it with our transaction success data. Check for errors.
The logic is pretty simple, but there are a few things to note:
-
Our data shall be the sum of normal payment data + repeated payment data;
-
The unsuccessful account checking mainly includes: the amount is incorrect, the third party has not found the corresponding transaction data, and we do not have the corresponding transaction data.
In view of these cases need to have corresponding means to deal with. In my experience, I have encountered all of these situations.
Incorrect amount: it is mainly due to a third-party problem, such as system upgrade failure or incorrect amount of bill interface.
No transaction data of the third party: it may be the time dimension problem of the bill (such as time difference), and such time zone problem needs to confirm with the third party to find the corresponding time difference. It can also be an attack, and someone impersonates a third-party asynchronous notification (indicating that the system verification mechanism is faulty or the key is leaked).
No transaction data in your own system: This may be due to a third party notification not being sent or handled properly.
Query /trade query/refund can be used to handle most of these problems automatically.
Settlement and reconciliation
So with the above transaction reconciliation why do you need to settle reconciliation? What is this system for? So let’s think about what settlement means.
Settlement means that the third-party gateway remit T+ X or other agreed amount to the company account at a fixed point in time.
Let’s assume that the settlement period is: T+1. The main interface used for settlement reconciliation is /query/settle. The main content obtained by this interface is which transactions are composed of each settlement amount (transaction success and refund data). And the amount of procedures deducted from this settlement.
The logic is actually quite simple. We first calculate how much the other party should remit to us according to the settlement period of T+1 from our own system. Then compare with the amount of data just obtained by the interface:
Bank collection amount + handling fee = amount calculated in our system
After this step is passed, there is no problem with the amount. Next, we need to check whether each order under this settlement is consistent.
The settlement system is strongly dependent on the reconciliation system. If the reconciliation is abnormal, then the settlement amount will certainly be abnormal. In addition, some issues need to be noted in settlement are:
-
The bank may refund the user by itself, since the user can apply for the refund directly from the card issuing bank;
-
Settlement also has the time zone difference problem;
-
The detailed transaction status in the settlement interface is not exactly consistent with ours. For example, it is found that a certain refund has been completed in the bank settlement, but our system treats the comparison according to the logic that the refund has not been completed.
In view of the above problems, we need to do some solutions to automate processing according to their own business needs.
The financial system
Financial system has many internal business, I only talk about the payment system related. (of course, the above reconciliation system can also be regarded as a financial category).
One of the main points in the financial system’s relationship to payments is the verification of transactions and refunds. The check transaction can be done using the query/trade query/refund interfaces. This logical process need not be said. The following focuses on refund.
I saw that a lot of system refunds are directly placed in the application. When users apply for refunds, they directly call the refund interface for refunds. The stakes are very high. The interface of the payment system on the flow of funds must be careful, so as not to expose too much directly to the outside and bring risks.
The function of refund should be put into the financial system to do. This can go through an internal approval process (whether it needs to be based on business) and more checks can be done in the financial system to decide whether a refund should be made immediately, or to enter a wait, reject, etc.
Third-party gateway
The main use of the third party is actually asynchronous notification and synchronous notification two interfaces. The logic of this part is actually quite simple. A change in the status of a transaction is effected by notification from a third party. And notify the corresponding application system.
This part is more complicated, the third party notification data structure is not unified, the type of notification is not unified. For example, some refunds return results synchronously and some asynchronously. How to design here will give the answer in the later system design.
That’s the end of part I. If you have any questions, please leave a comment on our GitHub page. You can click on [Read article] to enter
GitHub: https://github.com/skr-shop