概述
詳細
一、相關配置


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)設置熱部署
作為程序員都知道,每次修改后台代碼都要重啟項目,工作效率非常慢,所以我們要加快效率。
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
application.properties
#禁止thymeleaf緩存(建議:開發環境設置為false,生成環境設置為true) spring.thymeleaf.cache=false
開發工具是:idea
設置以下兩項(第一項如已設置直接設置第二項)
1) “File” -> “Settings” -> “Build,Execution,Deplyment” -> “Compiler”,選中打勾 “Build project automatically” 。
2) 組合鍵:“Shift+Ctrl+Alt+/” ,選擇 “Registry” ,選中打勾 “compiler.automake.allow.when.app.running”
相關鏈接:
https://www.cnblogs.com/jiangbei/p/8439394.html
https://blog.csdn.net/weixin_42884584/article/details/81561987
(2)內網穿透工具
讓外網能直接訪問,你本地的服務,場景用於,接口調試,支付方面等等。
為了大家找了一個,免費配置簡單的。絕對不是賣廣告,之前用過花生殼,麻煩到要死坑哇~,現在的所有穿透都要身份證登記,很正常國家出了對應的法規。
下載地址:https://natapp.cn/#download
配置和使用說明:https://blog.csdn.net/kingrome2017/article/details/77989442
四、功能講解
(1)訪問首頁
打開WxPayController.java
/**
* 二維碼首頁
*/
@RequestMapping(value = {"/"}, method = RequestMethod.GET)
public String wxPayList(Model model){
//商戶訂單號
model.addAttribute("outTradeNo",WxUtil.mchOrderNo());
return "/wxPayList";
}
我們和穿透工具連在一起使用
SpringBoot 默認端口號為:8080,穿透工具映射端口設置為:8080
雙擊,natapp.exe 執行

127.0.0:8080 <=> http://9xnrh8.natappfree.cc


修改,application.properties
(划重點,支付成功后回調本地服務,修改后記得重啟服務)
#統一下單-通知鏈接 wx.unifiedorder.notifyUrl=http://9xnrh8.natappfree.cc/wxPay/unifiedorderNotify
(2)生成二維碼
從頁面看到,有訂單流水號和支付金額,0.01 代表一分錢。支付金額可以修改的,點擊'生成二維碼'按鈕,然后把訂單流水號和支付金額傳到后台。
控制類:
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));
}
實現類:
@Override
public String wxPayUrl(Double totalFee,String outTradeNo,String signType) throws Exception {
HashMap<String, String> data = new HashMap<String, String>();
//公眾賬號ID
data.put("appid", WxConfig.appID);
//商戶號
data.put("mch_id", WxConfig.mchID);
//隨機字符串
data.put("nonce_str", WxUtil.getNonceStr());
//商品描述
data.put("body","測試支付");
//商戶訂單號
data.put("out_trade_no",outTradeNo);
//標價幣種
data.put("fee_type","CNY");
//標價金額
data.put("total_fee",String.valueOf(Math.round(totalFee * 100)));
//用戶的IP
data.put("spbill_create_ip","123.12.12.123");
//通知地址
data.put("notify_url",WxConfig.unifiedorderNotifyUrl);
//交易類型
data.put("trade_type","NATIVE");
//簽名類型
data.put("sign_type",signType);
//簽名
data.put("sign",WxUtil.getSignature(data, WxConfig.key,signType));
String requestXML = WxUtil.mapToXml(data);
String reponseString = HttpsClient.httpsRequestReturnString(WxConstants.PAY_UNIFIEDORDER,HttpsClient.METHOD_POST,requestXML);
Map<String,String> resultMap = WxUtil.processResponseXml(reponseString,signType);
if(resultMap.get(WxConstants.RETURN_CODE).equals("SUCCESS")){
return resultMap.get("code_url");
}
return null;
}
最終返回一個,二維碼鏈接 → 轉成二維碼圖片
/**
* 生成支付二維碼
* @param response 響應
* @param contents url鏈接
* @throws Exception
*/
public static void writerPayImage(HttpServletResponse response, String contents) throws Exception{
ServletOutputStream out = response.getOutputStream();
try {
Map<EncodeHintType,Object> hints = new HashMap<EncodeHintType,Object>();
hints.put(EncodeHintType.CHARACTER_SET,"UTF-8");
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
hints.put(EncodeHintType.MARGIN, 0);
BitMatrix bitMatrix = new MultiFormatWriter().encode(contents, BarcodeFormat.QR_CODE,300,300,hints);
MatrixToImageWriter.writeToStream(bitMatrix,"jpg",out);
}catch (Exception e){
throw new Exception("生成二維碼失敗!");
}finally {
if(out != null){
out.flush();
out.close();
}
}
}

下面的請求是定時器,檢查是否已支付,本來我想用socket。
(3)支付


(4)支付完畢

(5)回調接口
收到微信支付結果通知后,請嚴格按照示例返回參數給微信支付
/**
* 統一下單-通知鏈接
*/
@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());
System.out.println("requstXml : " + requstXml);
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)){
//商戶訂單號
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");
System.out.println("支付時間:" + systemFormat.format(payDate));
//臨時緩存
WxConfig.setPayMap(outTradeNo,"SUCCESS");
xmlContent = "<xml>" +
"<return_code><![CDATA[SUCCESS]]></return_code>" +
"<return_msg><![CDATA[OK]]></return_msg>" +
"</xml>";
}
}catch (Exception e){
e.printStackTrace();
}
WxUtil.responsePrint(response,xmlContent);
}

更多的信息,請看官網
官網相關鏈接:https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=9_7&index=8
(6)微信支付,關於XML解析存在的安全問題指引
代碼已修改

更多的信息,請看官網
官網相關鏈接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=23_5
(7)前端頁面
<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/jquery/jquery.timers-1.2.js"></script>
<script type='text/javascript'>
$(function () {
getOutTradeNo();
});
//生成二維碼
function save(){
var outTradeNo = $("#outTradeNo").val();
$("#payImg").attr("src",'/wxPay/payUrl'+"?totalFee="+ $("#totalFee").val()+"&outTradeNo=" + outTradeNo);
$('body').everyTime('2s','payStatusTimer',function(){
$.ajax({
type : "POST",
url : '/wxPay/payStatus?outTradeNo='+ outTradeNo +"&random=" + new Date().getTime(),
contentType:"application/json",
dataType : "json",
async : "false",
success : function(json) {
if(json != null && json.status == 0){
alert("支付成功!");
$('body').stopTime ('payStatusTimer');
return false;
}
},
error:function(XMLHttpRequest,textStatus,errorThrown){
alert("服務器錯誤!狀態碼:"+json.status);
// 狀態
console.log(json.readyState);
// 錯誤信息
console.log(json.statusText);
return false;
}
})
});
}
//獲取流水號
function getOutTradeNo(){
$.ajax({
type : "POST",
url : '/wxPay/outTradeNo',
success : function(json) {
if(json != null){
$("h3").html(json);
$("#outTradeNo").val(json);
}else{
alert("獲取流水號失敗!");
}
return false;
},
error:function(XMLHttpRequest,textStatus,errorThrown){
alert("服務器錯誤!狀態碼:"+XMLHttpRequest.status);
// 狀態
console.log(XMLHttpRequest.readyState);
// 錯誤信息
console.log(textStatus);
return false;
}
});
}
</script>
</head>
<body>
<p>訂單流水號:<h3></h3></p>
支付金額:<input id="totalFee" type="text" value="0.01"/>
<button type="button" onclick="save();">生成二維碼</button>
<input id="outTradeNo" type="hidden" value="${outTradeNo}"/>
<img id="payImg" width="300" height="300" >
</body>
</html>
啟動一個定時器,每個2秒去掃描是否已經支付~
因為相關配置,我是從官網下載的,所以支付后還有退款功能(退款要等很多天,請耐心等待),是不是很神奇~

還有微信登錄,自定義微信公眾號菜單創建-修改-刪除功能(前台-數據庫保存),微信公眾號接受、回復信息Demo
但是,沒有官網例子直接運行的,很多地方不能截圖,后面我想想怎么做吧~
謝謝大家觀看~
