Java WeChat code scanning payment mode 2 Demo, websocket direct running version

Overview

Scenario introduction Java WeChat scan code payment mode two Demo, based on the integration of the official website direct running version, adds websocket notification function and query order scan code payment mode two, used for web websites. After the user clicks to pay, the user scans the QR code generated by the product to complete the payment. The mobile phone prompts that the payment is successful, and the WeChat payment system sends the transaction result to the callback interface.

detailed

Demo

Local version:

Penetration.png

local.png

 

1. Related configurations

WeChat public account AppID (Log in to WeChat public platform-->Development-->Basic settings-->Developer ID (AppID))

WeChat public account AppSecret (Log in to WeChat public platform-->Development-->Basic settings-->Developer password (AppSecret))

WeChat Pay Merchant ID (Log in to WeChat Merchant Platform-->Account Center-->Merchant Information-->Basic Account Information-->WeChat Pay Merchant ID)

WeChat Pay Merchant API Key (Log in to WeChat Merchant Platform-->Account Center-->API Security-->API Key-->Set API Key)

application.properties file ( the Demo has the default values ​​​​from the official website, no need to modify them and use them directly, worry-free! )

QQ picture 20210108093355.png

Payment scanning mode 2, flow chart

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5

SDK and DEMO download

https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1

2. Directory structure

QQ picture 20210118154932.png

3. Preparation work

(1) Intranet penetration tool

Allow the external network to directly access your local services, scene applications, interface debugging, payment, etc.

I found one for everyone, it is free and easy to configure. It’s definitely not an advertisement. I’ve used peanut shells before, and it was so troublesome. Now, all penetrations require ID card registration. It’s normal for countries to have corresponding regulations.

Download address: NATAPP-Intranet penetration domestic high-speed intranet mapping tool based on ngrok

Configuration and usage instructions: External network mapping---Intranet penetration tool NATAPP---Inspired by QQ browser WeChat debugging tool_natapp+xampp_kingrome2009's blog-CSDN blog

4. Function demonstration

Local version: Please see the top video and flow chart, visit the link http://127.0.0.1:8080

Penetration version: Please see the top video and flow chart. You must use the penetration tool to access the link. Please see the video for the steps~

Payment process:

QQ picture 20210402151909.jpg

Picture 111111.jpg

5. Front-end code

<html>
<head >
    <title>微信支付二维码生成</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script type="text/javascript" src="/js/jquery/jquery-3.3.1.min.js"></script>
    <script type="text/javascript" src="/js/websocket/sockjs.min.js"></script>
    <script type="text/javascript" src="/js/websocket/stomp.min.js"></script>
    <script type='text/javascript'>
 
        $(function () {
            wxQRCode();
        });
 
        //当前查询次数
        var nowQueryCount = 0;
        //生成二维码
        function wxQRCode(){
            getOutTradeNo();
            var outTradeNo = $("#outTradeNo").val();
            $("#payImg").attr("src",'/wxPay/payUrl'+"?totalFee="+ $("#totalFee").val()+"&outTradeNo=" + outTradeNo + "&random=" + new Date().getTime());
            //重置查询次数
            nowQueryCount = 0;
            queryOrder();
        }
 
        //获取流水号
        function getOutTradeNo(){
            $.ajax({
                type : "POST",
                url : '/wxPay/outTradeNo',
                async : false,
                success : function(jsonData) {
                    if(jsonData.code == 200){
                        var outTradeNo = jsonData.data;
                        $("h2").html(outTradeNo);
                        $("#outTradeNo").val(outTradeNo);
                        //启动webSocket监听器
                        webSocketListener(outTradeNo);
                    }else{
                        alert(jsonData.message);
                    }
                    return false;
                },
                error:function(XMLHttpRequest,textStatus,errorThrown){
                    alert("服务器错误!状态码:"+XMLHttpRequest.status);
                    // 状态
                    console.log(XMLHttpRequest.readyState);
                    // 错误信息
                    console.log(textStatus);
                    return false;
                }
            });
        }
 
        var stompClient = null;
        //webSocketListener 监听器 是否已经支付
        function webSocketListener(outTradeNo){
            //多次生成二维码,关闭上一次webSocket,避免浏览器负担
            closeWebSocket();
            if ("WebSocket" in window){
                    var socket = new SockJS('/websocket');
                    stompClient = Stomp.over(socket);
                    stompClient.connect({}, function (frame) {
                        console.log('Connected: ' + frame);
                        stompClient.subscribe('/member/'+outTradeNo+'/send/message', function(jsonData) {
                            jsonData = JSON.parse(jsonData.body);
                            if(jsonData.code == 200){
                                var data = jsonData.data;
                                var outTradeNo = data.outTradeNo;
                                var transactionId = data.transactionId;
                                var payDateStr = data.payDateStr;
                                console.log("WebSocket:"+"\n"+"商户订单号:"+outTradeNo +"\n"+"微信支付订单号:"+transactionId+"\n"+"支付时间:"+payDateStr);
                                alert("WebSocket:"+"\n"+"商户订单号:"+outTradeNo +"\n"+"微信支付订单号:"+transactionId+"\n"+"支付时间:"+payDateStr);
                                closeWebSocket();
                                return false;
                            }
                        });
                    });
            }else{
                alert('我不兼容WebSocket');
            }
        }
 
        //关闭webSocket
        function closeWebSocket(){
            if (stompClient != null) {
                stompClient.disconnect();
            }
        }
 
        //查询订单总次数
        var autoQueryCount = 100;
        //定时器
        var queryOrderSetTimeout = null;
        //查询订单状态
        function queryOrder(){
            nowQueryCount ++;
            var outTradeNo = $("#outTradeNo").val();
            $.ajax({
                type : "POST",
                url : '/wxPay/payStatus?outTradeNo='+ outTradeNo +"&random=" + new Date().getTime(),
                async : false,
                success : function(jsonData) {
                    if(jsonData.code == 200){
                        var data = jsonData.data;
                        var outTradeNo = data.outTradeNo;
                        var transactionId = data.transactionId;
                        var payDateStr = data.payDateStr;
                        console.log("查询订单:"+"\n"+"商户订单号:"+outTradeNo +"\n"+"微信支付订单号:"+transactionId+"\n"+"支付时间:"+payDateStr);
                        alert("查询订单:"+"\n"+"商户订单号:"+outTradeNo +"\n"+"微信支付订单号:"+transactionId+"\n"+"支付时间:"+payDateStr);
                        //已支付,清除定时器
                        clearTimeout(queryOrderSetTimeout);
                        return false;
                    }else{
                        if(nowQueryCount < autoQueryCount){
                            //定时查询订单状态,5秒查询一次
                            console.log(currentTime()+" queryOrder:"+jsonData.message+" 查询次数:"+nowQueryCount);
                            queryOrderSetTimeout = setTimeout(queryOrder,5*1000);
                        }else{
                            console.log(currentTime()+" queryOrder:"+jsonData.message+" 查询结束");
                            //超过查询次数,清除定时器
                            clearTimeout(queryOrderSetTimeout);
                            return false;
                        }
                    }
                },
                error:function(XMLHttpRequest,textStatus,errorThrown){
                    alert("服务器错误!状态码:"+XMLHttpRequest.status);
                    // 状态
                    console.log(XMLHttpRequest.readyState);
                    // 错误信息
                    console.log(textStatus);
                    return false;
                }
            });
        }
 
 
        //当时时间
        function currentTime(){
            var date = new Date();
            this.year = date.getFullYear();
            this.month = date.getMonth() + 1;
            this.date = date.getDate();
            this.hour = date.getHours() < 10 ? "0" + date.getHours() : date.getHours();
            this.minute = date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes();
            this.second = date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds();
            var currentTime = this.year+'-'+this.month + '-' + this.date + ' ' + this.hour + ':' + this.minute + ':' + this.second;
            return currentTime;
        }
 
    </script>
</head>
<body>
<p>订单流水号:<h2></h2></p>
支付金额:<input id="totalFee"    type="text"   value="0.01"/>
<button type="button" onclick="wxQRCode();" style="cursor: pointer">生成二维码</button>
<input id="outTradeNo"  type="hidden" />
&nbsp;&nbsp;<img  id="payImg" width="300" height="300"  >
</body>
</html>

6. Back-end code

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 
   <groupId>com.juno</groupId>
   <artifactId>weixin-websoket</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>
 
   <name>weixin-websoket</name>
   <description>Demo project for Spring Boot</description>
 
   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>2.4.0</version>
      <relativePath/> <!-- lookup parent from repository -->
   </parent>
 
   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
      <commons-lang3.version>3.7</commons-lang3.version>
      <commons-collections.version>3.2.2</commons-collections.version>
      <com.google.zxing.version>3.3.3</com.google.zxing.version>
      <fastjson.version>1.2.46</fastjson.version>
   </properties>
 
   <dependencies>
      <!-- mvc支持-->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
 
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-thymeleaf</artifactId>
      </dependency>
 
      <!-- 热部署模块 -->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-devtools</artifactId>
         <optional>true</optional>
      </dependency>
 
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
 
      <!-- Commons utils begin -->
      <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
         <version>${commons-lang3.version}</version>
      </dependency>
      <dependency>
         <groupId>commons-collections</groupId>
         <artifactId>commons-collections</artifactId>
         <version>${commons-collections.version}</version>
      </dependency>
      <!-- Commons utils end -->
 
      <!-- google 生成二维码 begin-->
      <dependency>
         <groupId>com.google.zxing</groupId>
         <artifactId>javase</artifactId>
         <version>${com.google.zxing.version}</version>
      </dependency>
      <!-- google 生成二维码 end-->
 
      <!-- JSONObject JSONArray begin -->
      <dependency>
         <groupId>com.alibaba</groupId>
         <artifactId>fastjson</artifactId>
         <version>${fastjson.version}</version>
      </dependency>
      <!-- JSONObject JSONArray end -->
 
      <!-- websocket begin -->
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-websocket</artifactId>
      </dependency>
      <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <scope>test</scope>
      </dependency>
      <!-- websocket end -->
 
   </dependencies>
 
   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
               <fork>true</fork>
            </configuration>
         </plugin>
      </plugins>
   </build>
 
 
</project>

WxPayController.java

package com.juno.weixin.modules.controller.wx;
 
import com.juno.weixin.modules.common.util.JSONResponse;
import com.juno.weixin.modules.common.wx.WxConfig;
import com.juno.weixin.modules.common.wx.WxConstants;
import com.juno.weixin.modules.common.wx.WxUtil;
import com.juno.weixin.modules.service.WxMenuService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
 
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 微信支付控制类
 * @author lujunjie
 * @date   2020/12/31
 */
@Controller
public class WxPayController {
 
    @Autowired
    private WxMenuService wxMenuService;
 
    /**
     * 二维码首页
     */
    @RequestMapping(value = {"/"}, method = RequestMethod.GET)
    public String wxPayList(Model model){
        return "/wxPayList";
    }
 
    /**
     * 获取流水号
     */
    @RequestMapping(value = {"/wxPay/outTradeNo"})
    @ResponseBody
    public JSONResponse getOutTradeNo(){
        //商户订单号
        return JSONResponse.success(WxUtil.mchOrderNo());
    }
 
    final private String signType = WxConstants.SING_MD5;
    /**
     * 统一下单-生成二维码
     */
    @RequestMapping(value = {"/wxPay/payUrl"})
    public void payUrl(HttpServletRequest request, HttpServletResponse response,
                       @RequestParam(value = "totalFee")Double totalFee,
                       @RequestParam(value = "outTradeNo")String outTradeNo) throws Exception{
        WxUtil.writerPayImage(response,wxMenuService.wxPayUrl(totalFee,outTradeNo,signType));
    }
    /**
     * 查询订单状态
     */
    @RequestMapping(value = {"/wxPay/payStatus"})
    @ResponseBody
    public JSONResponse payStatus(HttpServletRequest request, HttpServletResponse response,
                       @RequestParam(value = "outTradeNo")String outTradeNo) throws Exception{
        return this.wxMenuService.queryOrder(outTradeNo,signType);
    }
 
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate;
 
    /**
     * 统一下单-通知链接
     */
    @RequestMapping(value = {"/wxPay/unifiedorderNotify"})
    public void unifiedorderNotify(HttpServletRequest request, HttpServletResponse response) throws Exception{
 
        //商户订单号
        String outTradeNo = null;
        String xmlContent = "<xml>" +
                "<return_code><![CDATA[FAIL]]></return_code>" +
                "<return_msg><![CDATA[签名失败]]></return_msg>" +
                "</xml>";
 
        try{
            String requstXml = WxUtil.getStreamString(request.getInputStream());
            Map<String,String> map = WxUtil.xmlToMap(requstXml);
            String returnCode= map.get(WxConstants.RETURN_CODE);
            //校验一下 ,判断是否已经支付成功
            if(StringUtils.isNotBlank(returnCode) && StringUtils.equals(returnCode,"SUCCESS")  &&  WxUtil.isSignatureValid(map, WxConfig.key,signType)){
                System.out.println("------------"+"统一下单-通知链接:支付成功"+"------------");
                System.out.println("统一下单-通知链接:"+"requstXml : " + requstXml);
                //商户订单号
                outTradeNo = map.get("out_trade_no");
                System.out.println("统一下单-通知链接 "+"outTradeNo : "+ outTradeNo);
                //微信支付订单号
                String transactionId = map.get("transaction_id");
                System.out.println("统一下单-通知链接 "+"transactionId : "+ transactionId);
                //支付完成时间
                SimpleDateFormat payFormat= new SimpleDateFormat("yyyyMMddHHmmss");
                Date payDate = payFormat.parse(map.get("time_end"));
                SimpleDateFormat systemFormat= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String payDateStr = systemFormat.format(payDate);
                System.out.println("统一下单-通知链接 "+"支付时间:" + payDateStr);
 
                HashMap<String,Object> returnMap = new HashMap<>();
                returnMap.put("outTradeNo",outTradeNo);
                returnMap.put("transactionId",transactionId);
                returnMap.put("payDateStr",payDateStr);
                //通知前台,我完成支付了
                simpMessagingTemplate.convertAndSendToUser(outTradeNo,"/send/message",JSONResponse.success(returnMap));
                xmlContent = "<xml>" +
                        "<return_code><![CDATA[SUCCESS]]></return_code>" +
                        "<return_msg><![CDATA[OK]]></return_msg>" +
                        "</xml>";
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        //返回信息给微信
        WxUtil.responsePrint(response,xmlContent);
    }
 
}

Guess you like

Origin blog.csdn.net/hanjiepo/article/details/133028501