SSM project -- integrating wechat payment

We only look at payment here. We don't look at other things that have nothing to do with payment

1, Prepare page

There are three main pages

1. The first page is the ticket purchase page

2. Second page, payment details page

The three purple ones are related to our payment

3. The third page, payment success jump page

2, Prepare QR code (test it)

1. First, import a JS and QR code generation plug-in qrious


2. Create a page, test it, and pay attention to this address

3, Prepare wechat parameters, environment, HttpClient tool class and snowflake algorithm tool class.

1. Create a weixinpay The properties configuration file is used to configure the parameters required for wechat payment. We don't use the callback address here.

2. Load our wechat payment profile in the main XML profile.


3. Create a wechat payment entity class and inject our data into it.


4. Import wechat payment SDK and development kit.


5. Import our HttpClient tool class, which will be used later to call wechat payment interface.


It may have the same name as other classes, so add Utils after it


6. Add the snowflake algorithm tool class, which is used to generate the order number that will never be repeated.

4, Start payment function code (order)

1. Create a WeixinPayService with two parameters.

2. Write the WeixinPayServiceImpl implementation class

package com.movie.service.impl;

import com.github.wxpay.sdk.WXPayUtil;
import com.movie.service.WeixinPayService;
import com.movie.utils.HttpClientUtils;
import com.movie.vo.WeiXin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import sun.net.www.http.HttpClient;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @BelongsProject: MovieSystem
 * @BelongsPackage: com.movie.service.impl
 * @Description: TODO
 */
@Service
public class WeixinPayServiceImpl implements WeixinPayService {

    @Resource
    WeiXin weiXin;

    /**
     * Call wechat interface to get the address of payment
     *
     * @param out_trade_no order number
     * @param total_fee    Amount (min)
     * @return
     */
    @Override
    public Map createNative(String out_trade_no, String total_fee) {
        // 1. Create parameters
        Map<String, String> param = new HashMap();// Create parameters
        //Parameters that must be transferred when calling the interface of wechat payment unified order
        param.put("appid", weiXin.getAppid());// official account
        param.put("mch_id", weiXin.getPartner());// Merchant number
        param.put("nonce_str", WXPayUtil.generateNonceStr());//Call the tool class (wechat SDK). Random string
        param.put("body", "Beihai cinema ticket");// Product description
        param.put("out_trade_no", out_trade_no);// Merchant order number
        param.put("total_fee", total_fee);// Total amount (in minutes)
        param.put("spbill_create_ip", "127.0.0.1");// IP
        param.put("notify_url", "http://test.easyshop.cn "); / / callback address (optional)
        param.put("trade_type", "NATIVE");// Transaction type. NATIVE type indicates code scanning support
        try {
            // 2. Generate the XML to be sent (convert the Map set to XML format), because wechat wants XML format
            String xmlParam = WXPayUtil.generateSignedXml(param, weiXin.getPartnerkey());
            //Using HttpClient to call the tool remotely
            //https://api.mch.weixin.qq.com/pay/unifiedorder This is the wechat payment interface
            HttpClientUtils client = new HttpClientUtils("https://api.mch.weixin.qq.com/pay/unifiedorder");
            client.setHttps(true);
            client.setXmlParam(xmlParam);
            client.post();
            // 3. Obtain results
            // Result is the result returned by the server in XML format
            String result = client.getContent();
            //Convert the results returned by the server from XML to Map
            Map<String, String> resultMap = WXPayUtil.xmlToMap(result);
            //Encapsulate return value
            Map<String, String> map = new HashMap<>();
            map.put("code_url", resultMap.get("code_url"));// Payment address
            map.put("total_fee", total_fee);// Total amount
            map.put("out_trade_no", out_trade_no);// order number
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return new HashMap<>();
        }
    }
 }

The notes are very clear, so we won't explain more. We need to pass two parameters to this method, one is the order number and the other is the total amount (that is, the amount of wechat code scanning payment). Finally, create a Map and return the payment address, total amount and order number.

3. Write BuyTicketController

package com.movie.web;

import com.movie.bean.Users;
import com.movie.service.BuyTicketService;
import com.movie.service.TicketService;
import com.movie.service.WeixinPayService;
import com.movie.utils.CommomResult;
import com.movie.utils.IdWorker;
import com.movie.vo.PlayVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;

@Controller
public class BuyTicketController {

    @Autowired
    BuyTicketService buyTicketService;

    @Autowired
    WeixinPayService weixinPayService;

    @Autowired
    TicketService ticketService;

    /**
     * Jump to the payment page and generate QR code
     *
     * @param play_id
     * @param seats
     * @return
     */
    @RequestMapping(value = "/buy_toPay", method = RequestMethod.POST)
    public String toPay(Integer play_id, String seats, Model model, HttpSession session) {

        //Seat number of the movie to buy
        session.setAttribute("seats",seats);

        //Find out the details of the movie according to the power id
        PlayVo vo = buyTicketService.findPlayVoById(play_id);

        //The price of a ticket
        Double money = vo.getMoney();

        //In order to split, look at how many pieces you bought
        StringBuffer stringBuffer = new StringBuffer();
        String[] arrays = seats.split(",");

        //Total amount to be paid
        Double sumMoney = money * arrays.length;

        //Loop traversal, plus row and seat
        for (String s : arrays) {
            s = s.replace("-", "Row") + "Seat";
            stringBuffer.append(s + " ");
        }
        model.addAttribute("seats", stringBuffer.toString());
        model.addAttribute("vo", vo);
        model.addAttribute("sumMoney", sumMoney);

        //2. Generate an order number that will never be repeated
        IdWorker worker = new IdWorker();
        long id = worker.nextId();
        String orderId = id+"";

        //Call wechat interface to obtain QR code payment address
        Map<String, String> map = weixinPayService.createNative(orderId, "1"); //The payment amount of wechat interface is in minutes by default
        //The address to be paid by wechat. Generate a QR code from this address
        String code_url = map.get("code_url");
        model.addAttribute("code_url", code_url);
        //Order number paid by user
        model.addAttribute("orderId", orderId);
        return "user_film_pay";
    }
}

The notes are very clear, so I won't explain more. Store the obtained payment address and order number in the model and return it.


4. Page display QR code

5. Test:


OK, no problem

5, Start payment function code (query order status)

We can scan the code to pay. Then the problem comes. After we scan the code, how can the page know whether it has paid or not?

This is about to query the payment status of the order. Query the payment status of the order according to the order number to see whether it is successful or failed.

1. Add a method to query the order status under service

2. As above, implement it

/**
     * Query the payment status of the order according to the order number
     * @param out_trade_no
     * @return
     */
    @Override
    public Map queryPayStatus(String out_trade_no) {
        Map param = new HashMap();
        param.put("appid", weiXin.getAppid());// Public account ID
        param.put("mch_id", weiXin.getPartner());// Merchant number
        param.put("out_trade_no", out_trade_no);// ----->Order No
        param.put("nonce_str", WXPayUtil.generateNonceStr());// Random string
        //Interface for querying order payment status
        String url = "https://api.mch.weixin.qq.com/pay/orderquery";
        try {
            String xmlParam = WXPayUtil.generateSignedXml(param, weiXin.getPartnerkey());
            HttpClientUtils client = new HttpClientUtils(url);
            client.setHttps(true);
            client.setXmlParam(xmlParam);

            //Send POS request
            client.post();

            //Result returned by calling interface
            String result = client.getContent();
            //Convert the returned XML file format to Map
            Map<String, String> map = WXPayUtil.xmlToMap(result);

            System.out.println("---------------Payment result returned by server interface:-----------------------");
            if(map!=null){
                Set<Map.Entry<String, String>> entries = map.entrySet();
                for (Map.Entry<String, String> entry : entries) {
                    System.out.println(entry.getKey()+"--->"+entry.getValue());
                }
            }

            return map;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

Pass an order number and return the Map result


3. Write the Controller, which is the same class as the previous Controller

The process is as follows:
When we pass an order number, we will call service to query the status of the order and return a Map. We judge the Map. If the Map is not empty and the trade in it_ If the value of state is SUCCESS, the payment is successful.
If the Map is empty, the payment is abnormal and failed.
Then there is another problem. If the user opens the page, but he doesn't scan the code to pay? Our request is only sent once. Isn't it inappropriate? We can give it a thread and let it execute once every 3 seconds. A cycle takes 5 minutes. If there is no code scanning within 5 minutes, the payment will timeout.

/**
     * Query order payment status according to order number -- polling order payment status interface
     *
     * @param out_trade_no
     * @return
     */
    @ResponseBody
    @RequestMapping("/pay_queryPayStatus")
    public CommomResult queryPayStatus(String out_trade_no,Integer play_id,HttpSession session) {
        CommomResult commonResult = null;
        try {
            //The order number of payment query is 1320615983324332000
            System.out.println("The order number of payment query is:"+out_trade_no);
            System.out.println("Query payment order play_id:"+play_id);
            String seats=(String)session.getAttribute("seats");// 3-4,5-6,7-8
            Users loginUser=(Users) session.getAttribute("loginUser");
            commonResult = null;
            int x = 0;
            while (true) {
                //Query order payment status
                Map map = weixinPayService.queryPayStatus(out_trade_no);
                if (map != null) {
                    if (map.get("trade_state").equals("SUCCESS")) {// If successful
                        //If the payment is successful, the successful payment data will be stored in the data, and the order can be viewed later
                        String[] seatNumers = seats.split(",");
                        for (String seatName : seatNumers) {
                            int seat_id = ticketService.getSeatIdBySeatName(seatName);
                            //Insert into order table ticket table
                            ticketService.addTicket(out_trade_no,play_id,loginUser.getUser_id(),seat_id);
                        }
                        commonResult = new CommomResult(200, "Payment successful");
                        break;
                    }
                }
                if (map == null) {
                    commonResult = new CommomResult(500, "Abnormal payment! fail");
                    break;
                }
                try {
                    Thread.sleep(3000);//Sleep for 3 seconds and execute every 3 seconds
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //In order not to let the loop run endlessly, we define a loop variable. If the variable exceeds this value, we will exit the loop and set the time to 5 minutes
                x++;
                if (x >= 60) {
                    commonResult = new CommomResult(502, "Payment timeout!");
                    break;
                }
            }
        } catch (Exception e) {
            commonResult = new CommomResult(500, "Payment exception!");
            e.printStackTrace();
        } finally {
        }
        return commonResult;
    }


4. Write page

When do we start calling the Controller to query the order status? Of course, as soon as the page opens, the QR code comes out and starts calling


5. Start the test:

Wait until 5 minutes without payment, timeout

Click OK to refresh the page and scan the code for payment

If the payment is successful, skip to the success page

----

It's not easy to create. If you feel good, you can easily praise, pay attention to and collect (* ~). Thank you~~

Keywords: SSM

Added by jimwp on Mon, 21 Feb 2022 22:19:30 +0200