Wechat official account “Backend Advanced” focuses on back-end technology sharing: Java, Golang, WEB framework, distributed middleware, service governance and so on. The old driver give each other all the way, take you to advance, too late to explain quickly get on the car!

Before I was in a company’s first task is to develop a unified payment platform, due to the company’s business needs, need to access multiple third-party payment, before the company’s pay are scattered in various projects, and go against payment management, then aggregate tripartite pays, unified payment platform task falls on my hand, can be said to be completely start from 0 to design, After a practical summary, I found some thinking on architecture design, before they’ve always wanted to write his architectural design idea, but haven’t begin, asked in a few days ago in the technology of related issues, I think it is necessary to write it out, in order to help more and more need to develop a payment platform developers.

The component patterns

Since the company’s business covers many regions, it needs to provide a variety of payment channels to meet the business development. Therefore, the designed payment platform needs access to a variety of third-party payment channels, such as: Wechat Pay, Alipay Payment, PayPal, IPayLinks and so on, as we all know, each third-party payment, has its own set of external API, the official has a set of SDK to implement these apis, how should we organize these apis?

As the third-party payment channels will change with the development of business, these SDKS need to be flexibly plugged and unplugged without affecting the overall architecture of the payment platform. Here, I use the idea of components and divide the payment API into various components, such as payment component, refund component, order component and bill component. In this way, when introducing a third-party payment SDK, the required API can be flexibly added to the component. The architecture design is as follows:

Through Builder mode, the corresponding component object is constructed according to the request parameters, which separates the component from the outside and hides the implementation of component construction. Component mode + Builder mode makes the payment platform have high expansibility.

Multiple account system

The access to a variety of third-party payment platform, we had and an account of the problems, the reason was that the company was small programs and the APP is using a different WeChat account, so there will be a WeChat will pay corresponding to the problem of multiple accounts, design payment platform, and I was not considering this problem, the third-party payment only corresponds to one account, Moreover, different third-party payment accounts are independent and not unified.

So I introduced the account system, account system is one of the most important core concept in account for granularity, access to multiple third-party payment, the parameters of the unified account, the building of a unified payment account system, payment platform, do not need to care about different payment account differences and third-party payment whether how many accounts.

Now I add the account layer in the payment platform architecture diagram:

The front-end only needs to transfer accountId, the payment platform can query the corresponding payment account according to accountId, and then construct the corresponding component object of the payment account through Builder mode, completely shield the difference between different payments. In the multi-account system, unlimited multiple payment accounts can be supported. Fully meet the development needs of the company’s business.

Unify callback and asynchronous distribution processing

Students who have done payment development know that the current third-party payment has a feature, that is, after the payment/refund is successful, there will be a payment/refund callback function, so that the merchant platform can verify the legality of the order. For example: Prevent at the time of payment, the client parameters such as amount of malicious tampering, so at this point after the success of the payment, the order will be paid in the state, needs to wait for the callback of third-party payment, if the received callback, when check found that the amount of order and payment amount is wrong, then order to pay for failure, in order to prevent the capital losses. The idea of a callback is to ensure that the final consistency is guaranteed, so when we call the payout, we don’t need to check the correctness of the parameters at this point, we just need to check at the time of the callback.

Having said the purpose of callbacks, how do we design callbacks for payment platforms?

Due payment platform, access to a number of third-party payment, if the third party payment each set a callback address, so there will be multiple callback address, because the callback API must be exposed to accept third-party callback requests, so there will be a security issue, we must in the API outer set security filters, Otherwise it is easy to have some illegal violent access, so we need to unify the callback API, do security check, and then do a layer of distribution.

I suggest RocketMQ for distribution. One might ask, if RocketMQ is used for distribution, how do you return verification results to third-party payment in real time? This is a question THAT I was struggling with at the time. Here are some thoughts on callback design:

  1. The company’s system is based on SpringCloud micro-service architecture, and the micro-services communicate with each other through HTTP. At that time, many micro-services connected to my payment platform. If HTTP is used for distribution, real-time message return can be guaranteed, but there is also a problem. The request may fail or time out, and the stability of the interface cannot be guaranteed.
  2. If the third-party payment receives a false response, it will initiate the callback request again in the following period of time. The purpose of doing this is to ensure the success rate of the callback. For the third-party payment, there is nothing wrong with this, but for the merchant payment platform, it may be a bad design. Suppose you have an order to pay for the malicious tampering with the amount, the callback check fails, return false to third-party payment, the third-party payment will repeat send back, no matter how many times to send back, will check fails, it’s an extra unnecessary interactions, here, of course, you can also use idempotent for processing, the following is the application scenarios of WeChat pay callback:

Based on the above two thinking, I don’t think return false to the third-party payment is necessary, for the sake of the robustness of the system, I used the message queue for asynchronous distributed, payment platform directly after receiving callback request returns true, then you might have to ask you a question, if the check fails, the return true at this time, however, there is no problem? First of all, in the case of verification failure, the order must be in the state of payment failure, and the purpose of returning true is to reduce unnecessary remote interaction with the third-party payment.

Because RocketMQ messages are persisted to disk, the biggest benefits of using message queues for asynchronous distribution are the ability to review messages in the message queue for troubleshooting, and the ability to perform peak traffic clipping during peak traffic times.

The following is an architectural design for unified callback and distribution processing:

Aggregate pay

Payment platform polymerization a wide variety of third-party payment, so need to do a lot of adaptation work request layer, in order to meet the needs of a variety of payment, you might think, directly in the adapter there add a few lines if the else not have it, do so also no problem, can also meet the needs of a variety of payment, but have you ever thought of, assuming that add a third party payment at this time, What would you do? You can only add multiple else conditions to the existing method, which causes the request layer code to constantly change as the business evolves, which makes the code extremely unelegant and difficult to maintain, so we have to use the strategy mode to eliminate the if else code, and when we add a third party payment, All we need to do is create a new Strategy class and see how the Strategy pattern is used.

So I added a payment strategy layer to the Builder mode:

The request processing

Since the payment platform involves funds, all kinds of payment requests and returns, and abnormal records are extremely important in a payment platform, we need to record every payment request record for subsequent troubleshooting.

Based on this requirement, I designed a Handler layer before requesting third-party payment. All requests must be processed by the Handler layer. The core method of Handler is as follows:

public K handle(T t) {
  K k;
  try {
    before(t);
    k = execute(t);
    after(k);
  } catch (Exception e) {
    exception(t, e);
  }
  return k;
}
protected abstract void before(T t);
protected abstract void after(K k);
protected abstract void exception(T t, Exception exception);
Copy the code

In principle, I designed the Handler layer, using the template mode, not only can realize log recording, but also can realize a variety of processing methods, such as request monitoring, message push, etc., to achieve the high scalability of the Handler layer.

The following is an architectural design of the Handler layer:

Write in the last

So that’s my payment platform architecture design idea, in summary, payment platform requires scalability, stability, high availability, to design a payment platform, so I use a lot of design patterns and the introduction of the message queue processing callback distribution problems, make payment platform has the characteristics of, hope to be able to give you some inspiration and help, Finally, I posted the overall architecture design drawing of the payment platform: