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