SSM framework integrates Alipay sandbox (June 2021)

Posted by Snart on Sun, 23 Jan 2022 06:16:59 +0100

SSM framework integrates Alipay sandbox

First, Sandbox service into the management center of Alipay open platform.

website: https://open.alipay.com/

2, Set RSA2(SHA256) key

1, Download Alipay Development Assistant

website: https://opendocs.alipay.com/open/291/introduce

2. Generate key

3, copy the public key to set up the RSA2(SHA256) key in the Alipay sandbox.

4, save the generated Alipay public key.

3, In POM Introducing dependency into XML

<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-sdk-java</artifactId>
    <version>4.13.50.ALL</version>
</dependency>
<dependency>
    <groupId>com.alipay.sdk</groupId>
    <artifactId>alipay-easysdk</artifactId>
    <version>2.1.0</version>
</dependency>

Four, under the resources resource folder, the new Alipay sandbox configuration file: "apply.properties"

## Alipay configuration (no need to use the public key, the application of public key is just to generate Alipay public key), the following configuration can be found in Alipay sandbox settings.
# Application ID
alipay.appId = 
# Apply private key
alipay.privateKey = 
# Note that the public key of Alipay is not generated by the application public key.
alipay.publicKey = 
#Payment gateway configuration, this item is written dead, and the formal environment is OpenAPI alipay. com
alipay.gateway = 
# Alipay front desk jump address (write your own logic and page)
alipay.returnUrl = 
# Alipay backstage notification address (write logic and page)
alipay.notifyUrl = 
# Alipay mobile phone web payment to cancel the jump address (write logic and page)
alipay.errorUrl = 

Five, write Alipay sandbox configuration initialization class: AlipayConfig

The purpose of writing this class is to load Alipay's configuration into Spring container and complete the initialization of Alipay SDK.

The configuration class needs to implement the ApplicationListener interface. Its generic type is ContextRefreshedEvent, which is used to trigger the ContextRefreshedEvent event when all bean s are initialized and loaded successfully. The ContextRefreshedEvent provides an onApplicationEvent method, that is, the onApplicationEvent method will be executed after initialization, Write the initialization logic in this method.

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

@PropertySource("classpath:/apply.properties")  //Introducing Alipay configuration files
@Component
public class AlipayConfig implements ApplicationListener<ContextRefreshedEvent> {
    // Application id
    @Value("${alipay.appId}")
    private String appId;

    // Private key
    @Value("${alipay.privateKey}")
    private String privateKey;

    // Public key
    @Value("${alipay.publicKey}")
    private String publicKey;

    // Alipay gateway
    @Value("${alipay.gateway}")
    private String gateway;

    // The callback address of the interface after successful payment is not a friendly page for callback. Don't confuse it
    @Value("${alipay.notifyUrl}")
    private String notifyUrl;

    private Config getOptions() {
        //Some unnecessary configurations are omitted here. Please refer to the description of the document

        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = this.gateway;
        config.signType = "RSA2";

        config.appId = this.appId;

        // In order to avoid the disclosure of the private key with the source code, it is recommended to read the private key string from the file instead of writing it to the source code
        config.merchantPrivateKey = this.privateKey;

        // Note: if the non certificate mode is adopted, there is no need to assign the three certificate paths above, instead of assigning the following Alipay public key string.
        config.alipayPublicKey = this.publicKey;

        // You can set the asynchronous notification receiving service address (optional)
        config.notifyUrl = notifyUrl;

        return config;
    }


    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        //Initializing Alipay SDK
        Factory.setOptions(getOptions());
        System.out.println("**********Alipay SDK Initialization complete**********");
    }
}

Six, the new "AlipayService" class, the preparation of Alipay service layer logic code

Note: only computer payment and mobile payment are simulated here. For other methods, please refer to the official documents and test by yourself!

package cn.bjwlxy.service;

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.payment.facetoface.models.AlipayTradePayResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.alipay.easysdk.payment.wap.models.AlipayTradeWapPayResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;

@PropertySource("classpath:/apply.properties")
@Service
public class AlipayService {
    // Page to jump to after successful payment
    @Value("${alipay.returnUrl}")
    private String returnUrl;

    // Alipay front office mobile phone web payment cancelled halfway address
    @Value("${alipay.errorUrl}")
    private String errorUrl;

    /**
     * Alipay computer web payment
     * @param total  amount of money
     * @param orderId  Order number
     * @return  
     */
    public String page(String total, String orderId) {
        try {

            AlipayTradePagePayResponse response = Factory.Payment
                    // Select computer website
                    .Page()
                    // Call the payment method (order name, merchant, order number, amount, success page)
                    .pay("Payment test", orderId, total, returnUrl);

            return response.body;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;

    }

    /**
     * Alipay mobile phone web payment
     * @param total     amount of money
     * @param orderId  Order No
     * @return
     */
    public String wap(String total, String orderId) {

        try {
            AlipayTradeWapPayResponse response = Factory.Payment
                    //Select mobile website
                    .Wap()
                    // Call the payment method (order name, merchant, order number, amount, midway exit page, success page)
                    .pay("Payment test", orderId, total, errorUrl, returnUrl);

            return response.body;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }


}

Six, the new "AlipayController" class, the preparation of Alipay control layer logic code

package cn.bjwlxy.controller;

import cn.bjwlxy.service.AlipayService;
import com.alipay.easysdk.factory.Factory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/api/alipay")
public class AlipayController {
    @Autowired
    private AlipayService alipayService;


    /**
     * @description: Alipay computer web payment
     * @param total: amount of money
     * @return java.lang.String
     */
    //Here, products = {"text / HTML; charset = UTF-8"} sets the encoding set of the returned data to prevent errors in the transmission process
    @PostMapping(value = "/page",produces = {"text/html;charset=UTF-8"})
    public String page(String total,String orderId)  
        return alipayService.page(total,orderId);
    }

    /**
     * @description: Alipay mobile phone web payment
     * @param orderId: Order No
     * @param total: amount of money
     * @return java.lang.String
     */
	//Here, products = {"text / HTML; charset = UTF-8"} sets the encoding set of the returned data to prevent errors in the transmission process
    @PostMapping(value = "/wap",produces = {"text/html;charset=UTF-8"})
    public String wap(String total,String orderId) {
        return alipayService.wap(total, orderId);
    }

	
    /**
     * @description: Alipay asynchronous callback
     * @param request: request
     * @return java.lang.String
     */
    @PostMapping("/notify_url")
    public String notify_url(HttpServletRequest request) throws Exception {

        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========Alipay asynchronous callback========");

            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
            }

            // Alipay check
            if (Factory.Payment.Common().verifyNotify(params)) {
                // The signature is passed
                System.out.println("Transaction name: " + params.get("subject"));
                System.out.println("Transaction status: " + params.get("trade_status"));
                System.out.println("Alipay transaction No.: " + params.get("trade_no"));
                System.out.println("Merchant order number: " + params.get("out_trade_no"));
                System.out.println("Transaction amount: " + params.get("total_amount"));
                System.out.println("Buyer in Alipay only id: " + params.get("buyer_id"));
                System.out.println("Buyer payment time: " + params.get("gmt_payment"));
                System.out.println("Buyer payment amount: " + params.get("buyer_pay_amount"));
            }
        }

        return "success";
    }


}

7, Write method to generate order number

Create a new OrderUtil and write the method to generate the order number:

package cn.bjwlxy.common;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

public class OrderUtil {
    /**
     *  Generate order number based on Timestamp
     * */
    public static String getOrderNo () {
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
        LocalDateTime localDateTime = Instant.ofEpochMilli(System.currentTimeMillis()).atZone(ZoneOffset.ofHours(8)).toLocalDateTime();
        return df.format(localDateTime);
    }

}

8, Write front-end test page

After the back-end code is written here, let's start writing the front-end test page. There are three front-end test pages: purchase page (buy.jsp), payment success page (return_url.html) and payment failure page (error_url.html). This is just for testing and does not do any effect.

1. Purchase page (buy.jsp)

<%@ page import="cn.bjwlxy.common.OrderUtil" %>
<%@ taglib prefix="security" uri="http://www.springframework.org/security/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<h1>Product purchase page</h1>
Total goods ${param.total_price}element
<form action="/api/alipay/page" method="post">
    Order No.:<input type="text" value="<%=OrderUtil.getOrderNo()%>" name="orderId" readonly/>
    Total order amount:<input type="text" value="${param.total_price}" name="total" readonly/>
    <button type="submit">Computer confirmed payment</button>
</form>
<form action="/api/alipay/wap" method="post">
    Order No.:<input type="text" value="<%=OrderUtil.getOrderNo()%>" name="orderId" readonly/>
    Total order amount:<input type="text" value="${param.total_price}" name="total" readonly/>
    <button type="submit">Mobile phone to confirm payment</button>
</form>

<script>
</script>
</html>

2. Payment success page (return_url.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>Payment successful</h2>
</body>
</html>

3. Payment failure page (error_url.html)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>Payment failed</h2>
</body>
</html>

Nine, Alipay sandbox purchase test

1. Purchase page

2. Computer payment test

There are two test methods:

The first is to download Alipay sandbox version APP, scan code payment.

The second is to get virtual accounts and passwords on Alipay open platform, manual input.

In fact, Alipay sandbox simulation is a transaction between buyers and sellers, because it is simulated in sandbox environment, so there will be no real transactions.

Alipay official provides a buyer's account and business account. When we pay, we use the buyer's account. When the payment is successful, the amount of the buyer's account will be deducted accordingly, and the amount of the merchant account will be increased accordingly, thus simulating a transaction in the real environment.

The gateway address we are using now is https://openapi.alipaydev.com/gateway.do This is a gateway in Alipay sandbox. When using this gateway configuration, it will automatically jump to the sandbox environment when payment is made. If all tests pass, you only need to change the gateway in the configuration to https://openapi.alipay.com It's OK. It's very convenient!

3. Mobile payment test

The process of mobile phone test is roughly the same, except that the payment device is replaced by a mobile phone. The screenshot is not shown here.

Topics: Java MySQL Spring