当前位置:网站首页>Third party payment interface design

Third party payment interface design

2022-07-05 12:17:00 Xujunsheng

Preface

It is necessary to read this article 6 minute

​ Recently, the third-party payment has been connected in the project , For third party payments , The more complex function is payment 、 refund 、 Reconciliation .

In this article, we only introduce the interface design related to payment .

The nodes that a payment flow may involve include : payment 、 Payment result query 、 Payment result notice 、 cancel the order 、 Customs clearance 、 refund 、 Reconciliation .

Take Alipay for example , Alipay provides a very rich payment capability :app payment 、 Sweep code payment 、 Website payment, etc . There is little difference between different payment methods .

The process of docking with third-party payment is similar . According to the official documents, the docking can be completed quickly , So in this article, we will not discuss how to connect with third-party payment , We want to talk about things other than docking .

I am here How to design an interface ? It says , There are five points to consider when designing an interface : Security 、 stability 、 Efficiency 、 Maintainability 、 Readability .

Next, we will discuss how to design a payment interface around these features .

Security

The payment interface involves the flow of funds , Then its security is self-evident .

Alipay regulations , When accessing payment capacity , The data transmission interface is encrypted with public and private keys .

How can we ensure the security between our own interfaces ?

SHA256 perhaps RSA2.

The specific encryption algorithm is not described in detail here , I'm looking for a lot on the Internet .
This article simply talks about the encryption process , Here's the picture :

 Insert picture description here
To be specific :

  • stay App End first SHA256 perhaps RSA2 Encrypt the payment message , Then send it to the backstage service ;
  • Background parsing ciphertext , And verify that .
  • If the resolution passes, proceed to the next step of logic , Otherwise, it will prompt that the ciphertext parsing fails .

stability

Idempotency is extremely important for payment interfaces .

The database operations involved in the payment business include : Save payment flow 、 Synchronize order status 、 Update inventory quantity, etc .

Due to the natural non idempotent nature of new operations , So we need to ensure at the design level .

I'm in the project Use Redis The distributed lock realizes the idempotent of the payment interface .

  • About using Redis The principle of implementing distributed lock , I will share with you in the next article .

The way I implement it in the project :Redisson.

Redisson principle :

  • Thread to get lock , To be successful : perform lua Script , Save data to redis database .
  • Thread to get lock , Acquisition failure : All the way through while Loop trying to get lock , Execute after success lua Script , Save data to redis database .
  • Support the watchdog automatic delay mechanism .

Code instance :

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.6</version>
</dependency>
public void testReentrantLock(RedissonClient redisson){
    RLock lock = redisson.getLock("anyLock");
        try{
    
            // 1.  The most common use method 
            //lock.lock();// 2.  Support the function of overdue unlocking ,10 Automatically unlock after seconds ,  Don't need to call unlock Method to manually unlock 
            //lock.lock(10, TimeUnit.SECONDS);// 3.  Try to lock , Waiting for the most 3 second , After the lock 10 Seconds auto unlock 
            boolean res = lock.tryLock(3, 10, TimeUnit.SECONDS);
            if(res){
        // success 
                // do your business}
        } catch (InterruptedException e) {
    
            e.printStackTrace();
        } finally {
    
            lock.unlock();
        }}

In addition to idempotency , Another problem to be considered in the payment interface is Order closed over time . This question is left for you to think about first , We will introduce it in detail in a later article .

Transaction consistency

If the payment is successful, the order status should be updated 、 Inventory quantity .
In the context of microservices , The databases of each business are independent , In order to ensure the consistency of transactions, we need to use distributed transactions .

Common distributed transaction solutions :

  • XA Two-phase commit
  • TCC Pattern : Support TCC The open source frameworks for transactions are :ByteTCC、Himly、TCC-transaction.
  • Saga Business
  • Message based distributed transaction : Scheme based on transaction message 、 Local message based solutions
  • Distributed transaction middleware :Seata

Maintainability

At present, only Alipay and wechat are connected in our project , In the future, it may connect with UnionPay and so on . Payment methods will continue to increase with the growth of business .
But the process of each payment method is roughly the same : Decryption of payment information 、 payment 、 Order modification 、 Modify inventory .

In this way , Use if else Judgment will lead to high coupling between payment function and system business function .

if (payType.equals ("WeiXin")) {
    
//dosomething
}else if (payType.equals ("AliPay")) {
    
//dosomething
} else if(payType.equals ("UnionPay")) {
    
//dosomething
}

So I used in the project The strategy pattern , To define different implementations for different payment methods .

  • First, define an abstract class , Encapsulate common methods .

  • Then customize the annotation ServiceRoute, Marked in the specific payment implementation interface , When the project starts, it automatically marks ServiceRoute Annotated services are injected into the container ;

  • At the time of payment, according to the specific channel number , Call different payment implementation functions .

Custom annotation :

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface ServiceRoute {
    
    /** *  Payment channel number  * * @return */
    String value();
}

Service registration :

public class RegisterService implements ApplicationContextAware {
    
    private static final Logger logger = LoggerFactory.getLogger(RegisterService.class);private Map<String, Object> servicesMap = new ConcurrentHashMap<String, Object>();private static ApplicationContext applicationCtx = null;/** *  Register service interface  */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    
        applicationCtx = applicationContext;// Scan added ServiceRoute Annotated classes 
        Map<String, Object> allWebResBeans = applicationCtx.getBeansWithAnnotation(ServiceRoute.class);
        for (Object bean : allWebResBeans.values()) {
    
            String routeName = getServiceRoute(bean);
            if (routeName != null) {
    
                servicesMap.put(routeName, bean);
                logger.debug("register route,routeName={},bean={}", new Object[] {
    routeName,bean});
            }
        }
    }private String getServiceRoute(Object bean) {
    
        if (bean != null) {
    
            Annotation anno = AnnotationUtils.getAnnotation(bean.getClass(), ServiceRoute.class);
            if (anno != null) {
    
                return anno.getClass().getAnnotation(ServiceRoute.class).value();
            }
        }
        return null;
    }
    
    public Object getServiceByAnnoName(String name) {
    
        if (StringUtils.isNotEmpty(name)) {
    
            return servicesMap.get(name);
        }
        return null;
    }
}
原网站

版权声明
本文为[Xujunsheng]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140531120779.html