一:前期微信支付掃盲知識
前提條件是已經有申請了微信支付功能的公眾號,然后我們需要得到公眾號APPID和微信商戶號,這個分別在微信公眾號和微信支付商家平台上面可以發現。其實在你申請成功支付功能之后,微信會通過郵件把Mail轉給你的,有了這些信息之后,我們就可以去微信支付服務支持頁面:https://pay.weixin.qq.com/service_provider/index.shtml
打開這個頁面,點擊右上方的鏈接【開發文檔】會進入到API文檔說明頁面,看起來如下
選擇紅色圓圈的掃碼支付就是我們要做接入方式,鼠標移動到上面會提示你去查看開發文檔,如果這個都不知道怎么查看,可以洗洗睡了,你真的不合適做程序員,地址如下:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1在瀏覽器中打開之后會看到
一:前期微信支付掃盲知識
前提條件是已經有申請了微信支付功能的公眾號,然后我們需要得到公眾號APPID和微信商戶號,這個分別在微信公眾號和微信支付商家平台上面可以發現。其實在你申請成功支付功能之后,微信會通過郵件把Mail轉給你的,有了這些信息之后,我們就可以去微信支付服務支持頁面:https://pay.weixin.qq.com/service_provider/index.shtml
打開這個頁面,點擊右上方的鏈接【開發文檔】會進入到API文檔說明頁面,看起來如下
選擇紅色圓圈的掃碼支付就是我們要做接入方式,鼠標移動到上面會提示你去查看開發文檔,如果這個都不知道怎么查看,可以洗洗睡了,你真的不合適做程序員,地址如下:
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=6_1在瀏覽器中打開之后會看到
我們重點要關注和閱讀的內容我已經用紅色橢圓標注好了,首先閱讀【接口規則】里面的協議規范,開玩笑這個都不讀你就想做微信支付,這個就好比你要去泡妞,得先收集點基本背景信息,了解對方特點,不然下面還怎么溝通。事實證明只有會泡妞得程序員才是好銷售。跑題了我們接下來要看一下【場景介紹】中的案例與規范,只看一下記得一定要微信支付的LOGO下載下來,是為了最后放到我們自己的掃碼支付網頁上,這樣看上去比較專業一點。之后重點關注【模式二】
我們這里就是要采用模式二的方式實現PC端頁面掃碼支付功能。
微信官方對模式二的解釋是這樣的“商戶后台系統先調用微信支付的統一下單接口,微信后台系統返回鏈接參數code_url,商戶后台系統將code_url值生成二維碼圖片,用戶使用微信客戶端掃碼后發起支付。注意:code_url有效期為2小時,過期后掃碼不能再發起支付”。看明白了吧就是我們首先要調用微信提供統一下單接口,得到一個關鍵信息code_url(至於這個code_url是什么鬼,我也不知道),然后我們通過自己的程序把這個URL生成一個二維碼,生成二維碼我這里用了Google的zxing庫。然后把這個二維碼顯示在你的PC端網頁上就行啦。這樣終端用戶一掃碼就支付啦,支付就完成啦,看到這里你肯定很激動,發現微信支付如此簡單,等等還有個事情我們還不知道,客戶知道付錢了,我們服務器端還不知道呢,以微信開發人員的智商他們早就想到這個問題了,所以讓你在調用統一下單接口的時候其中有個必填的參數就是回調URL,就是如果客戶端付款成功之后微信會通過這個URL向我們自己的服務器提交一些數據,然后我們后台解析這些數據,完成我們自己操作。這樣我們才知道客戶是否真的已經通過微信付款了。這樣整個流程才結束,這個就是模式二。微信用一個時序圖示這樣表示這個過程的。
表達起來比較復雜,看上去比較吃力,總結一下其實我們服務器該做的事情就如下件:
1. 通過統一下單接口傳入正確的參數(當然要包括我們的回調URL)與簽名驗證,從返回數據中得到code_url的對應數據
2. 根據code_url的數據我們自己生成一個二維碼圖片,顯示在瀏覽器網頁上
3. 在回調的URL中添加我們自己業務邏輯處理。
至此掃盲結束了,你終於知道掃碼支付什么個什么樣的流程了,下面我們就一起來扒扒它的相關API使用,做好每步處理。
二:開發過程
在開發代碼之前,請先准備幾件事情。
1. 添加ZXing的maven依賴 也可以直接在前端使用第三方庫 生成二維碼
2. 添加jdom的maven依賴
3.下載Java版本SDK演示程序,地址在這里
https://pay.weixin.qq.com/wiki/doc/api/native.php?chapter=11_1
我們需要MD5Util.java和XMLUtil.java兩個文件
4. 我們使用HttpClient版本是4.5.1,記得添加Maven依賴
上面准備工作做好以后,繼續往下看:
首先我們要調用微信的統一下單接口,我們點擊【API列表】中的統一下單會看到這樣頁面:
我們重點要關注和閱讀的內容我已經用紅色橢圓標注好了,首先閱讀【接口規則】里面的協議規范,開玩笑這個都不讀你就想做微信支付,這個就好比你要去泡妞,得先收集點基本背景信息,了解對方特點,不然下面還怎么溝通。事實證明只有會泡妞得程序員才是好銷售。跑題了我們接下來要看一下【場景介紹】中的案例與規范,只看一下記得一定要微信支付的LOGO下載下來,是為了最后放到我們自己的掃碼支付網頁上,這樣看上去比較專業一點。之后重點關注【模式二】
我們這里就是要采用模式二的方式實現PC端頁面掃碼支付功能。
微信官方對模式二的解釋是這樣的“商戶后台系統先調用微信支付的統一下單接口,微信后台系統返回鏈接參數code_url,商戶后台系統將code_url值生成二維碼圖片,用戶使用微信客戶端掃碼后發起支付。注意:code_url有效期為2小時,過期后掃碼不能再發起支付”。看明白了吧就是我們首先要調用微信提供統一下單接口,得到一個關鍵信息code_url(至於這個code_url是什么鬼,我也不知道),然后我們通過自己的程序把這個URL生成一個二維碼,生成二維碼我這里用了Google的zxing庫。然后把這個二維碼顯示在你的PC端網頁上就行啦。這樣終端用戶一掃碼就支付啦,支付就完成啦,看到這里你肯定很激動,發現微信支付如此簡單,等等還有個事情我們還不知道,客戶知道付錢了,我們服務器端還不知道呢,以微信開發人員的智商他們早就想到這個問題了,所以讓你在調用統一下單接口的時候其中有個必填的參數就是回調URL,就是如果客戶端付款成功之后微信會通過這個URL向我們自己的服務器提交一些數據,然后我們后台解析這些數據,完成我們自己操作。這樣我們才知道客戶是否真的已經通過微信付款了。
這樣整個流程才結束,這個就是模式二。微信用一個時序圖示這樣表示這個過程的。
package com........util;
import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.SortedMap;
import javax.servlet.http.HttpServletRequest; public class ConstantUtil { /** * 商家可以考慮讀取配置文件 */ public static String GATEURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";//獲取預支付id的接口url public static String QUERYURL = "https://api.mch.weixin.qq.com/pay/orderquery";//獲取微信支付訂單支付情況url public static String BODY="";//支付介紹看微信介紹 //微信掃碼支付配置--start--微信公眾平台APPID public static String WECHAT_APP_ID="";//公司提供 public static String WECHAT_MCH_ID="";//公司提供 public static String WECHAT_APP_KEY="";//公司提供微信公眾平台支付商戶平台系統內的API密鑰 public static String WECHAT_NOTIFY_URL="http://www.*******.com/weixinPayReturn";//微信掃碼支付配置 回調路徑--end-- public static String createSign(String Encoding, SortedMap<String, String> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + ConstantUtil.WECHAT_APP_KEY); String sign = MD5Util.MD5Encode(sb.toString(), Encoding).toUpperCase(); return sign; } public static boolean IsNumeric(String str) { return str.matches("\\d *"); } public static String parametersToXml(Map<String, String> parameters) { String xml = "<xml>"; Set es = parameters.entrySet(); Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String key = (String)entry.getKey(); String val = (String)entry.getValue(); if(IsNumeric(val)) { xml = xml + "<" + key + ">" + val + "</" + key + ">"; } else { xml = xml + "<" + key + "><![CDATA[" + val + "]]></" + key + ">"; } }
xml = xml + "</xml>"; return xml; } public static String generateString(int length) { StringBuffer sb = new StringBuffer(); Random random = new Random(); for (int i = 0; i < length; i++) { sb.append(ALLCHAR.charAt(random.nextInt(ALLCHAR.length()))); } return sb.toString(); } public static String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip; }
} |
先准備下單接口 window.location.href = "weixinPay?Oid=" + data; |
@RequestMapping(value = "/weixinPay", method = RequestMethod.GET, produces = MediaType.ALL_VALUE) public String weixinPay(Model model,String Oid) { System.out.println("\n*****************開啟微信掃碼界面******\n"); // 付款金額,必填 /*JSONObject jObject =new JSONObject();*/ long date = System.currentTimeMillis(); SortedMap<String, String> sParaTemp = new TreeMap<String, String>(); sParaTemp.put("appid", ConstantUtil.WECHAT_APP_ID); sParaTemp.put("mch_id", ConstantUtil.WECHAT_MCH_ID); sParaTemp.put("nonce_str", date + ConstantUtil.generateString(10));//最好是當前時間在隨機數 sParaTemp.put("sign_type", "MD5"); sParaTemp.put("body", ConstantUtil.BODY); sParaTemp.put("out_trade_no", Oid);//此處改成商城orderID sParaTemp.put("total_fee", "1"); //為一分錢 sParaTemp.put("spbill_create_ip", ConstantUtil.getIpAddress(request)); sParaTemp.put("trade_type", "NATIVE"); sParaTemp.put("notify_url", ConstantUtil.WECHAT_NOTIFY_URL);//notify_url String signString = ConstantUtil.createSign("utf-8", sParaTemp); sParaTemp.put("sign", signString); String paramXml = ConstantUtil.parametersToXml(sParaTemp); try {//一下發送請求至微信的下單接口 CloseableHttpClient httpClient = HttpClientBuilder.create().build(); HttpPost post = new HttpPost(ConstantUtil.GATEURL); post.addHeader("Content-Type", "text/xml; charset=UTF-8"); StringEntity xmlEntity = new StringEntity(paramXml, ContentType.APPLICATION_JSON);//UTF-8 post.setEntity(xmlEntity); CloseableHttpResponse httpResponse = httpClient.execute(post); String responseXML = EntityUtils.toString(((org.apache.http.HttpResponse) httpResponse).getEntity(), "UTF-8"); @SuppressWarnings("unchecked") Map<String, String> resultMap = XMLUtil.parseXmlToMap(responseXML); if (resultMap.get("return_code").equals("SUCCESS") && resultMap.get("result_code").equals("SUCCESS")) { String codeurl = resultMap.get("code_url"); if (codeurl != null && !"".equals(codeurl)) { model.addAttribute("data", codeurl);//一切正常返回一個url 頁面接收生成二維碼 } else { model.addAttribute("data", ""); } } post.releaseConnection(); } catch (Exception e) { e.printStackTrace(); } return "/weixinPay"; } //發送的請求主要是注意驗簽那塊。只要告訴提供的信息沒有問題。一切都ok |
頁面展示 weixinPay.jsp 獲取以上的data頁面使用第三方工具庫生成二維碼 <script src="${pageContext.request.contextPath}/resources/js/jquery-1.12.0.min.js" type="text/javascript"></script> <script src="${pageContext.request.contextPath}/resources/js/jquery.qrcode.min.js" type="text/javascript"></script> $(function(){ var str = toUtf8("${data}");//展示二維碼 $("#code").qrcode({ render: "canvas", // 渲染方式有table方式和canvas方式 width: 220, //默認寬度 height: 200, //默認高度 text: str, //二維碼內容 typeNumber: -1, //計算模式一般默認為-1 correctLevel: 2, //二維碼糾錯級別 background: "#ffffff", //背景顏色 foreground: "#000000" //二維碼顏色 });
}) |
到最后一步微信的二維碼就展示在頁面上了。 然后還有掃碼回調和訂單查詢不懂的可以留言 |