概述
场景介绍 Java微信扫码支付模式二Demo ,整合官网直接运行版本 的基础上,增加了websocket通知功能和查询订单 扫码支付模式二,用于web网站。用户点击支付后,根据商品生成的二维码,用户扫码完成支付,手机提示支付成功,微信支付系统把交易结果发送到回调接口中。
详细
功能演示
本地版本:
一、相关配置
微信公众号AppID(登录微信公众平台-->开发-->基本设置-->开发者ID(AppID))
微信公众号AppSecret(登录微信公众平台-->开发-->基本设置-->开发者密码(AppSecret))
微信支付商户号(登录微信商户平台-->账户中心-->商户信息-->基本账户信息-->微信支付商户号)
微信支付商户API密钥(登录微信商户平台-->账户中心-->API安全-->API密钥-->设置API密钥)
application.properties 文件 (Demo上都有官网的默认值,不需要修改直接使用,省心!)
支付扫码模式二,流程图
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_5
SDK与DEMO下载
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
二、目录结构
三、准备工作
(1)内网穿透工具
让外网能直接访问,你本地的服务,场景用于,接口调试,支付方面等等。
为了大家找了一个,免费配置简单的。绝对不是卖广告,之前用过花生壳,麻烦到要死坑哇~,现在的所有穿透都要身份证登记,很正常国家出了对应的法规。
下载地址:NATAPP-内网穿透 基于ngrok的国内高速内网映射工具
配置和使用说明:外网映射---内网穿透工具NATAPP---灵感源自QQ浏览器微信调试工具_natapp+xampp_kingrome2009的博客-优快云博客
四、功能演示
本地版本:请看最上面视频及流程图,访问链接 http://127.0.0.1:8080
穿透版本:请看最上面视频及流程图,访问链接 必须使用穿透工具,步骤请看视频~
支付流程:
五、前端代码
<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" />
<img id="payImg" width="300" height="300" >
</body>
</html>
六、后端代码
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);
}
}