原文路徑:https://blog.csdn.net/javaYouCome/article/details/79473743javascript
寫這篇文章的目的有2個,一是本身的項目剛開發完微信支付功能,趁熱回個爐溫習一下,二也是幫助像我這樣對微信支付不熟悉,反覆看了多天文檔仍是隻知其一;不知其二,原理都沒摸清,更不要說實現了。本覺得網上的微信開發教程會和「java的重寫與重載」同樣鋪天蓋地,可搜出來的結果,要麼是PHP的教程(微信支付官網推薦就是PHP),要麼星星點點就那麼幾篇,想對比的看看思路都成問題,官網下載的JAVA-SDK-DEMO也恕我技術低下,看的糊里糊塗。等本身開發完的那一刻,才豁然開朗,才知道走通完支付這條路的過程走了多少彎路,我是第一次接觸支付,想必大部分能看這篇文章的兄弟也是被微信官方文檔給繞的出不來纔出此下策,內容有誤請指正。好了這回真正的正題了:php
步驟一:獲取微信支付四大參數html
首先要想支持微信支付,必須擁有兩個帳號:①微信公衆已認證的服務號,而且須要開通微信支付該能(必須是企業纔有資格申請,請你找你家產品去申請吧),②微信商戶平臺帳號;這兩個帳號一個不能少。此處已默認你已有上兩個帳號。前端
此處是帳號模板,請參考:java
微信公衆平臺:帳戶:con*******om 登陸密碼 ******算法
公衆APPID:wx15*********a8spring
APPSECEPT : c210***************892d7json
微信商戶平臺:帳戶:149**********6742 登陸密碼:******api
商戶ID:14******42瀏覽器
API密鑰:5d5************b35b
其中比較很差找的是商戶的API密鑰:在商戶平臺的帳戶中心下:須要用戶自行下載證書及安裝,(略)
至此咱們須要的APPID(appid),APPSECEPT(appsecret),商戶ID(mch_id),API密鑰(paternerKey),四個重要參數已拿到,括號中是咱們代碼所用的變量名稱請提早熟悉。
步驟二:平臺配置
1.配置支付目錄:商戶平臺:
配置此目錄是代碼中「微信支付」所在頁面的地址,能夠是目錄不必定是全路徑-如http://www.wangtao.com/order/-----此一級域名須要ICP備案。
點擊添加
2.配置受權域名:微信公衆平臺:
支付過程須要獲取用戶openid,必須通過網頁受權配置才能夠,要否則獲取不到openid。
點擊設置,按說明設置
步驟三:開發流程:
微信支付原理(說白了就是調用官方文檔的「統一下單」接口,以後將微信服務器返回的參數作個加工後,返回到前臺(JSP頁面),就OK了。我們要作的就是千方百計的湊齊統一下單的全部參數「而已」,「而已」,「而已」,這個而已也就是最大的挑戰)。全部參數解釋請參考:官方文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1;
我們只考慮必填參數,其餘參數能夠就看你的了。
先來看看全部參數:
其中的必填參數有:
1. appid APPID (已有)
2. mch_id 商戶ID (已有)
3. nonce_str 隨機字符串
4. sign 簽名
5. body 所支付的名稱
6. out_trade_no 我們本身所提供的訂單號,須要惟一
7. total_fee 支付金額
8. spbill_create_ip IP地址
9. notify_url 回調地址
10. trade_type 支付類型
11. openid 支付人的微信公衆號對應的惟一標識
只要把這11個湊齊就齊活,如今我們從第3個開始一個一個的獲取;在這以前先從官網把公衆號支付的sdk下載下來,如圖
主要是用其中的WXPayUtil工具類中的一些方法。固然其餘的類我看不懂,要是看懂了,就不至於這麼費勁了。
好了開始我們的取值之旅了:
1. appid APPID (已有)
2. mch_id 商戶ID (已有)
3. nonce_str 隨機字符串用WXPayUtil中的generateNonceStr()便可,就是生成UUID的方法;
4. sign 簽名 用WXPayUtil中的generateSignature(finalMap<String, String> data, String key)方法,data是將除了sign外,其餘10個參數放到map中,key是四大配置參數中的API祕鑰(paternerKey)(這裏不要着急管它,最後處理它);
5. body 所支付的名稱
6. out_trade_no 本身後臺生成的訂單號,只要保證惟一就好:如「2018013000001」
7. total_fee 支付金額 單位:分,爲了測試此值給1,表示支付1分錢
8. spbill_create_ip IP地址 網上不少ip的方法,本身找,此處測試給「127.0.0.1」
9. notify_url 回調地址:這是微信支付成功後,微信那邊會帶着一大堆參數(XML格式)請求這個地址屢次,這個地址作咱們業務處理如:修改訂單狀態,贈送積分等。Ps:支付還沒成功還想這麼遠幹嗎,最後再說。地址要公網能夠訪問。
10. trade_type 支付類型 我們是公衆號支付此處給「JSAPI」
11. openid 支付人的微信公衆號對應的惟一標識,每一個人的openid在不一樣的公衆號是不同的,這11個參數裏,最費勁的就是他了,其餘的幾乎都已經解決,如今開發獲得這個參數。
得到openid的部份內容應該不屬於微信支付的範疇,屬於微信公衆號網頁受權的東西,詳情請參考微信網頁受權:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
得到openid步驟:
第一步:用戶贊成受權,獲取code
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
注意:1. redirect_uri參數:受權後重定向的回調連接地址, 請使用 urlEncode 對連接進行處理。
2. scope:用snsapi_base 。
經過此連接能夠獲取code,能夠在一個空頁面設置一個a標籤,連接至其redirect_uri的地址。點擊a標籤,便可連接到redirect_uri的地址,並攜帶code。
<a href="https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx15c*********&redirect_uri=http%3a%2f%2fwww.***.com%2fpay.jsp&response_type=code&cope=snsapi_base#wechat_redirect">去支付頁面pay.jsp並攜帶code</a>
第二步:經過code換取網頁受權access_token(其實微信支付就沒有必要獲取access_token了,我們只要其中openid,不是要用戶信息,此步結果已經就含有我們須要的openid了)
獲取code後,請求如下連接獲取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
上一步的code有了,對於此連接的參數就容易了。但是在頁面上如何處理是個問題,我是在pay.jsp頁面加載完成後將獲取code當作參數傳異步到後臺,在後臺中用http相關類發送get請求(能夠自行網上查找)。返回的JSON結果爲:
-
{ "access_token":"ACCESS_TOKEN",
-
-
"refresh_token":"REFRESH_TOKEN",
-
"openid":"OPENID",//就是它,只要這個值
-
好了,access_token是有了,不過我們不關心它,我們關心的是openid,有了它一就回到我們「統一下單」接口裏,全部的參數已經就位就等發送了。在回顧下11個必填參數:
1. appid APPID (已有)
2. mch_id 商戶ID (已有)
3. nonce_str 隨機字符串用WXPayUtil中的generateNonceStr()便可,就是生成UUID的方法;
4. sign 簽名 用WXPayUtil中的publicstatic String generateSignature(final Map<String, String> data, Stringkey)方法,data是將除了sign外,其餘10個參數放到map中,key是四大配置參數中的API祕鑰(paternerKey)(此時能夠處理它了,不過其餘10個參數都有了,它就easy了,先new一個map,依次put其餘10個參數,就能夠用generateSignature方法了,獲得了sign後,不要忘記再將sign put到只有10個參數的map中,這樣才能湊齊最後的第11個參數。準備召喚神龍吧。);
5. body 所支付的名稱
6. out_trade_no 本身後臺生成的訂單號,只要保證惟一就好:如「2018013000001」
7. total_fee 支付金額 單位:分,爲了測試此值給1,表示支付1分錢
8. spbill_create_ip IP地址 網上不少ip的方法,本身找,此處測試給「127.0.0.1」
9. notify_url 回調地址:這是微信支付成功後,微信那邊會帶着一大堆參數(XML格式)請求這個地址屢次,這個地址作咱們業務處理如:修改訂單狀態,贈送積分等。Ps:支付還沒成功還想這麼遠幹嗎,最後再說。地址要公網能夠訪問。
10. trade_type 支付類型 我們是公衆號支付此處給「JSAPI」
11. openid (已有)
好了,準備工做完成,開始發送POST請求吧,上面提到網上找到的get請求的方法,此處用到post請求的方法,請求微信"統一下單接口https://api.mch.weixin.qq.com/pay/unifiedorder。發送前先用WXPayUtil工具類中的public static String mapToXml(Map<String,String> data)方法將有11個參數的map轉成XML格式。發送後會返回String類型的返回值,若是你夠幸運的話應該會獲得XML的字符串:
-
-
<return_code><![CDATA[SUCCESS]]></return_code>
-
<return_msg><![CDATA[OK]]></return_msg>
-
<appid><![CDATA[wx2421b1c4370ec43b]]></appid>
-
<mch_id><![CDATA[10000100]]></mch_id>
-
<nonce_str><![CDATA[IITRi8Iabbblz1Jc]]></nonce_str>
-
<openid><![CDATA[oUpF8uMuAJO_M2pxb1Q9zNjWeS6o]]></openid>
-
<sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign>
-
<result_code><![CDATA[SUCCESS]]></result_code>
-
<prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>
-
<trade_type><![CDATA[JSAPI]]></trade_type>
-
若是你獲得了以上的字符串,那麼先恭喜你,堅持看到這,說明你的耐心仍是不錯的,由於「統一下單」接口調用完畢,但是並無什麼實際的效果,由於微信裏想出現支付的界面是在前臺完成的如今我們還在後臺玩耍,前面提到的我是頁面加載完成時異步到後臺的,我們要返回異步的結果了,好了趁熱繼續吧。「統一下單」這麼費勁的完成其實搞那麼麻煩,就是爲了獲得上面紅色的prepay_id(丫的,就這麼一個參數給我們搞的都想說***了)。
先用WXPayUtil類中的public static Map<String, String> xmlToMap(String strXML)方法,將剛纔返回的XML格式的字符串轉成map(爲了方便取值)。map.get(「prepay_id」)就獲得了prepay_id的值(好比獲得的是:「wx2018…250…9981…666」),記住它,先保留此值。
看看前臺都須要接收哪些值吧。
6個參數,我們仍是一個一個分析:
1. appId:四大參數之一的APPID;
2. timestamp:時間戳(newDate()便可)
3. nonceStr:隨機字符串,再次用WXPayUtil中的generateNonceStr()便可;
4. package:就tm是它用到了prepay_id,可是還不是直接取值,還非要固定格式的,值的格式例如:」prepay_id= wx2018…250…9981…666」
5. signType:寫MD5就好
6. paySign:又來了仍是簽名算法 ,按照上面的方法,用WXPayUtil中的publicstatic String generateSignature(final Map<String, String> data, Stringkey)方法,data是將除了paySign外,其餘5個參數放到map中,key是四大配置參數中的API祕鑰(paternerKey),獲得了paySign後,不要忘記再將paySign put到只有5個參數的map中,這樣才能湊齊最後的第6個參數。);
注意:此處有個小bug,不少人會被坑的很慘,不注意就掉坑裏,我是掉進去了,就是最關鍵的第4個參數package,眼熟不眼熟,這tm是JAVA的關鍵字,不能用來當變量名。
全部的參數有了,返回給前端的方法有不少,簡易用springMVC的@ResponseBody註解,便可將這個有6個參數的map按json格式傳給前端。好了,後臺工做完成。
前端的工做就容易多了,格式比較固定由於是微信固定格式,因此直接貼出個人代碼,你只要更換觸發支付的事件和異步的地址便可.
前端簡單來講:1.一個空jsp頁面上有個a標籤,用來獲取code,並跳轉到pay.jsp(上面提到過)。
2.pay.jsp中須要異步到後臺須要帶code參數,pay.jsp中頁面的地址上帶着code,想獲取code的方法不少,拋磚引引玉:(定義一個按鈕,按鈕上綁定一個code的屬性值是頁面連接的code的值,用EL表達式取的參數值,點擊按鈕觸發點擊事件)。
3.接收後臺傳過來值,調用固定方法。
Pay.jsp中內容只有一個」微信支付」的按鈕,和js的代碼,如下是js內容(獲取code方法能夠修改),其它內容不要修改
-
<!—pay.jsp中點擊」微信支付」按鈕執行pay()方法>
-
-
<input id="code"type="button" value="微信支付"onclick="pay()" code="${param.code }"/>
-
<script type=
"text/javascript">
-
var appId,timeStamp,nonceStr,package,signType,paySign;
-
-
var code = $("#code").attr("code");
-
-
var url = "http://異步地址?code="+code+";
-
$.get(url,function(result) {
-
-
timeStamp = result.timeStamp;
-
nonceStr = result.nonceStr;
-
package = result.package;
-
signType = result.signType;
-
paySign = result.paySign;
-
-
if (typeof WeixinJSBridge == "undefined") {
-
if (document.addEventListener) {
-
document.addEventListener('WeixinJSBridgeReady',
-
-
} else if (document.attachEvent) {
-
document.attachEvent('WeixinJSBridgeReady',
-
-
document.attachEvent('onWeixinJSBridgeReady',
-
-
-
-
-
-
-
-
-
-
-
-
function onBridgeReady(){
-
WeixinJSBridge.invoke( 'getBrandWCPayRequest', {
-
"appId":appId, //公衆號名稱,由商戶傳入
-
"timeStamp":timeStamp, //時間戳,自1970年以來的秒數
-
"nonceStr":nonceStr, //隨機串
-
-
"signType":signType, //微信簽名方式:
-
-
-
-
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
-
-
-
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){
-
-
}else if(res.err_msg == "get_brand_wcpay_request:fail"){
-
-
WeixinJSBridge.call('closeWindow');
-
} //使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。
-
-
-
-
如下是後臺部分
-
-
-
-
-
-
-
@RequestMapping(value="orders", method = RequestMethod.GET)
-
-
public Map orders(HttpServletRequest request,String code) {
-
-
-
String getopenid_url = https:
-
-
"appid="+你appid+"&secret="+你secret+"&code="+code+"&grant_type=authorization_code";
-
-
String openIdStr = HttpRequest.sendGet(getopenid_url, param);
-
JSONObject json = JSONObject.parseObject(openIdStr);
-
String openId = json.getString(
"openid");
-
-
-
Map<String, String> paraMap =
new HashMap<String, String>();
-
-
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.getRemoteAddr();
-
-
-
String[] ips = ip.split(
",");
-
-
-
-
paraMap.put(
"appid", 你appid);
-
paraMap.put(
"body", "堯舜商城-訂單結算");
-
paraMap.put(
"mch_id", 你mchId);
-
paraMap.put(
"nonce_str", WXPayUtil.generateNonceStr());
-
paraMap.put(
"openid", openId);
-
paraMap.put(
"out_trade_no", 你的訂單號);
-
paraMap.put(
"spbill_create_ip", ip);
-
paraMap.put(
"total_fee",」1」);
-
paraMap.put(
"trade_type", "JSAPI");
-
paraMap.put(
"notify_url",www.*******.com
-
String sign = WXPayUtil.generateSignature(paraMap, paternerKey);
-
paraMap.put(
"sign", sign);
-
String xml = WXPayUtil.mapToXml(paraMap);
-
-
-
String unifiedorder_url = https:
-
-
String xmlStr = HttpRequest.sendPost(unifiedorder_url, xml);
-
-
-
-
if (xmlStr.indexOf("SUCCESS") != -1) {
-
Map<String, String> map = WXPayUtil.xmlToMap(xmlStr);
-
prepay_id = (String) map.get(
"prepay_id");
-
-
Map<String, String> payMap =
new HashMap<String, String>();
-
payMap.put(
"appId", appid);
-
payMap.put(
"timeStamp", WXPayUtil.getCurrentTimestamp()+"");
-
payMap.put(
"nonceStr", WXPayUtil.generateNonceStr());
-
payMap.put(
"signType", "MD5");
-
payMap.put(
"package", "prepay_id=" + prepay_id);
-
String paySign = WXPayUtil.generateSignature(payMap, paternerKey);
-
payMap.put(
"paySign", paySign);
-
-
-
-
-
-
如下是網上找的一個發送get和post的類.僅供參考:提早導入相關jar包可網上查找(很容易)
-
import java.io.BufferedReader;
-
import java.io.IOException;
-
import java.io.InputStreamReader;
-
import java.io.PrintWriter;
-
-
import java.net.URLConnection;
-
-
-
-
public class HttpRequest {
-
-
-
-
-
-
-
-
-
-
public static String sendGet(String url, String param) {
-
-
BufferedReader in =
null;
-
-
String urlNameString = url +
"?" + param;
-
System.out.println(urlNameString);
-
URL realUrl =
new URL(urlNameString);
-
-
URLConnection connection = realUrl.openConnection();
-
-
connection.setRequestProperty(
"accept", "*/*");
-
connection.setRequestProperty(
"connection", "Keep-Alive");
-
connection.setRequestProperty(
"user-agent",
-
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
-
-
-
-
Map<String, List<String>> map = connection.getHeaderFields();
-
-
for (String key : map.keySet()) {
-
System.out.println(key +
"--->" + map.get(key));
-
-
-
in =
new BufferedReader(new InputStreamReader(
-
connection.getInputStream()));
-
-
while ((line = in.readLine()) != null) {
-
-
-
-
System.out.println(
"發送GET請求出現異常!" + e);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
public static String sendPost(String url, String param) {
-
-
BufferedReader in =
null;
-
-
-
URL realUrl =
new URL(url);
-
-
URLConnection conn = realUrl.openConnection();
-
-
conn.setRequestProperty(
"accept", "*/*");
-
conn.setRequestProperty(
"connection", "Keep-Alive");
-
conn.setRequestProperty(
"user-agent",
-
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)");
-
-
-
-
-
out =
new PrintWriter(conn.getOutputStream());
-
-
-
-
-
-
-
new InputStreamReader(conn.getInputStream()));
-
-
while ((line = in.readLine()) != null) {
-
-
-
-
System.out.println(
"發送 POST 請求出現異常!"+e);
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
----------------------------------------------------------------------------------
如下是微信支付成功後,向本身項目發送請求,本身來作一些業務處理。
-
-
-
-
-
-
-
-
-
-
public class WxPayController {
-
-
private OrdersService ordersService;
-
-
private AccountService accountService;
-
-
private PointService pointService;
-
-
@RequestMapping("callback")
-
public String callBack(HttpServletRequest request,HttpServletResponse response){
-
-
-
-
is = request.getInputStream();
-
String xml = WXPayUtil.inputStream2String(is,
"UTF-8");
-
Map<String, String> notifyMap = WXPayUtil.xmlToMap(xml);
-
-
if(notifyMap.get("return_code").equals("SUCCESS")){
-
if(notifyMap.get("result_code").equals("SUCCESS")){
-
String ordersSn = notifyMap.get(
"out_trade_no");
-
String amountpaid = notifyMap.get(
"total_fee");
-
BigDecimal amountPay = (
new BigDecimal(amountpaid).divide(new BigDecimal("100"))).setScale(2);
-
-
-
-
-
-
-
Orders order = ordersService.selectOrdersBySn(ordersSn);
-
-
order.setLastmodifieddate(
new Date());
-
order.setVersion(order.getVersion().add(BigDecimal.ONE));
-
order.setAmountpaid(amountPay);
-
-
int num = ordersService.updateOrders(order);
-
-
String amount = amountPay.setScale(
0, BigDecimal.ROUND_FLOOR).toString();
-
-
-
-
Member member = accountService.findObjectById(order.getMemberId());
-
accountService.updateMemberByGrowth(amount, member);
-
-
-
-
-
pointService.updateMemberPointAndLog(amount, member,
"購買商品,訂單號爲:"+ordersSn);
-
-
-
-
-
-
-
-
response.getWriter().write(
"<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>");
-
-
-
-
-
-
-
以上純手寫,若有疏漏請聯繫,請大神勿噴.謝謝,以上是公衆號支付(在微信裏),有時間的話我再寫一個H5頁面的微信支付,請期待.