1.為什么會有兩種JS方法可以發起微信支付?
當你登陸微信公眾號之后,左邊有兩個菜單欄,一個是微信支付,一個是開發者中心。
在開發者中心中,有一個微信JS-SDK說明文檔。
在此說明文檔中,有一個發起微信支付的請求API
wx.chooseWXPay({ timestamp: 0, // 支付簽名時間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后台生成簽名使用的timeStamp字段名需大寫其中的S字符 nonceStr: '', // 支付簽名隨機串,不長於 32 位 package: '', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***) signType: '', // 簽名方式,默認為'SHA1',使用新版支付需傳入'MD5' paySign: '', // 支付簽名 success: function (res) { // 支付成功后的回調函數 } });
在微信支付菜單欄中,有一個使用教程。里面有一個使用JS API發起支付請求的小菜單。
進入之后,里面一個公眾號支付的菜單欄。在里面,有一個H5調起支付API的頁面。
它里面發起一個支付的代碼是:
function onBridgeReady(){ WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId" : "wx2421b1c4370ec43b", //公眾號名稱,由商戶傳入 "timeStamp":" 1395712654", //時間戳,自1970年以來的秒數 "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //隨機串 "package" : "prepay_id=u802345jgfjsdfgsdg888", "signType" : "MD5", //微信簽名方式: "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 }, function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回 ok,但並不保證它絕對可靠。 } ); } if (typeof WeixinJSBridge == "undefined"){ if( document.addEventListener ){ document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); }else if (document.attachEvent){ document.attachEvent('WeixinJSBridgeReady', onBridgeReady); document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); } }else{ onBridgeReady(); }
為什么JS有兩種方法可以調起微信支付呢?
我的理解:第一種方法是為了跟其他js API接口統一(里面可以調用很多微信的功能)而提供的。
第二種方法僅僅是針對微信支付這個功能提供的。
也就是說,按照這兩種方法來進行微信支付,都是可以的,但是兩種方法調用的方式和提供的參數不一樣。
2.微信支付有v2和v3版本的區別?
這種區別,是相對於getBrandWCPayRequest方法發起的微信支付。
當你登陸微信公眾平台,點擊微信支付。如果看到有技術升級的菜單欄,就證明你現在使用的是v2版本,如果你進行技術升級,那么,就會升級到v3版本。
v2版本和v3版本的發起微信支付的方式不一樣,因此,如果你之前使用了v2版本進行了微信支付的開發,那么你技術升級到v3版本后,就不能使用原來的v2版本的調用方式了,也就意味着,你的代碼需要改動。當然,你不升級的話,是沒有問題的。
v3版本比v2版本多了一些功能,比如:紅包,券等。
v2版本發起微信支付的方法:
WeixinJSBridge.invoke('getBrandWCPayRequest',{ "appId" : getAppId(), //公眾號ID "timeStamp" : getTimeStamp(), //時間戳,當前系統的時間,具體格式,請看API "nonceStr" : getNonceStr(), //隨機串,具體格式請看API "package" : getPackage(),//擴展包 "signType" : "SHA1", //微信簽名方式:sha1 "paySign" : getSign() //微信簽名 },function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功后返回ok,但並不保證它絕對可靠。 //因此微信團隊建議,當收到ok返回時,向商戶后台詢問是否收到交易成功的通知,若收到通知,前端展示交易成功的界面;若此時未收到通知,商戶后台主動調用查詢訂單接口,查詢訂單的當前狀態,並反饋給前端展示相應的界面。 } );
v3版本發起微信支付的方法跟v2的一樣,只是參數不一樣。
其中,package的值,v3是通過統一接口調用獲取到的。
signType,v3使用的是md5方式。
由於微信支付開發教程,已經按照v3版本的方式給出了,所以v2版本,我們就不說了,接下來,我們詳細講解v3版本的開發方式。
3.v3版本開發步驟
想要在前端發起微信支付,就必須有appId,timeStamp,nonceStr,package,signType,paySign這六個參數。
其中,appid,signType已經直接給出了。
timestamp和noncestr,可以根據文檔給出的規則算出來。
package是統一下單接口返回的prepay_id參數值,提交格式如:prepay_id=***。
所以,我們先來看下如何獲得此prepay_id。
我們先去看統一下單接口:
此接口的調用,是通過服務端的代碼進行調用的,也就是你的應用服務器,這里我們假設你使用的是java代碼。
接口鏈接
URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder
請求參數(只考慮必填的)
appid,mch_id,nonce_str,body,out_trade_no,total_fee,notify_url,trade_type,這幾個參數,我們可以直接獲得。
spbill_create_ip(終端IP)和sign(簽名)我們需要采取方式獲得。
獲取用戶的ip地址,前端可以通過以下方式獲取,然后傳給后台。后台也可以自己獲取(對java不熟)。
<script src="https://pv.sohu.com/cityjson?ie=utf-8"></script> var returnCitySN = {"cip": "210.21.236.135", "cid": "440300", "cname": "廣東省深圳市"};
最后,只剩下sign了。
我們進入到簽名算法文檔。大家認真仔細的查看此文檔,因為此簽名算法會使用幾次。
此文檔中有一個例子,大家按照此例子把自己要請求的參數按照字典序組裝起來,最后拼接API秘鑰。此秘鑰是在商戶平台中,賬戶設置,API安全中自己設置的,此秘鑰只能放在服務器端,也就是java代碼中,不能進行傳輸。之前的v2版本,就是放在前端(js代碼中的),不安全。
最后,用java代碼中的md5方法進行加密,加密完之后,轉化成大寫,就得到sign了。
最后,通過java代碼,把這些參數組成xml文件發給微信服務器(https://api.mch.weixin.qq.com/pay/unifiedorder)。
如果返回成功,就會得到以下這種形式的數據:
<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> <sign><![CDATA[7921E432F65EB8ED0CE9755F0E86D72F]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>
java代碼解析后,把prepay_id返回給前端。
這時,前端要發起微信支付,就只差paySign了。
此paySign,也必須按照簽名算法來得到。這里,我們同樣在java代碼中,按照調起微信支付getBrandWCPayRequest方法的請求參數進行簽名算法操作。
這里需要對appId,timeStamp,nonceStr,package,signType這5個請求參數進行組裝,然后拼接商戶平台秘鑰,md5加密,轉換成大寫,最后返回給前端。
這時,前端就可以調用此接口了。
上面的圖,我們省略第二步。
4.按照統一接口實現v3版本的微信支付。
在微信js-sdk中,有詳細的說明:所有需要使用JS-SDK的頁面必須先注入配置信息,否則將無法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用,目前Android微信客戶端不支持pushState的H5新特性,所以使用pushState來實現web app的頁面會導致簽名失敗,此問題會在Android6.2中修復)。
wx.config({ debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 appId: '', // 必填,公眾號的唯一標識 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串 signature: '',// 必填,簽名,見附錄1 jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2 });
這里的參數,我們只有signature不知道。
生成signature之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於調用微信JS接口的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。
- 參考以下文檔獲取access_token(有效期7200秒,開發者必須在自己的服務全局緩存access_token):../15/54ce45d8d30b6bf6758f68d2e95bc627.html
- 用第一步拿到的access_token 采用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
按照獲取access token的文檔,我們可以獲得
{"access_token":"ACCESS_TOKEN","expires_in":7200}
此文檔很容易看懂,AppID和AppSecret在微信公眾平台可獲取。
緊接着第二步,就可以獲得:
{ "errcode":0, "errmsg":"ok", "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", "expires_in":7200 }
里面的ticket就是jsapi_ticket。
最后,按照下面的簽名算法得到signature。
簽名算法 簽名生成規則如下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其后面部分) 。對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。這里需要注意的是所有參數名均為小寫字符。對string1作sha1加密,字段名和字段值都采用原始值,不進行URL 轉義。 即signature=sha1(string1)。 示例: noncestr=Wm3WZYTPz0wzccnW jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg timestamp=1414587457 url=https://mp.weixin.qq.com?params=value 步驟1. 對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1: jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=https://mp.weixin.qq.com?params=value 步驟2. 對string1進行sha1簽名,得到signature: 0f9de62fce790f9a083d5c99e95740ceb90c27ed 注意事項 簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。 簽名用的url必須是調用JS接口頁面的完整URL。 出於安全考慮,開發者必須在服務器端實現簽名的邏輯。 如出現invalid signature 等錯誤詳見附錄5常見錯誤及解決辦法。
配置完成之后,我們就可以在下面的函數中調用微信支付接口了。
wx.ready(function(){ wx.chooseWXPay({ timestamp: 0, // 支付簽名時間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付后台生成簽名使用的timeStamp字段名需大寫其中的S字符 nonceStr: '', // 支付簽名隨機串,不長於 32 位 package: '', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***) signType: '', // 簽名方式,默認為'SHA1',使用新版支付需傳入'MD5' paySign: '', // 支付簽名 success: function (res) { // 支付成功后的回調函數 } }); // config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。 });
然后,這里的支付簽名paySign和package按照前面的方式得來就行了。
總結:如果僅僅是實現微信支付這樣的功能,還是參考第三項。因為第四項多了一步,v2版本已經淘汰。
如果需要實現微信支付和其他微信功能,那么參考第四項。
里面的坑很多:記住,參數名的大小寫要一致,不管是前端還是后台,在簽名時,只要大小寫不一致,就會出錯。