原文連結:https://www.2cto.com/weixin/201507/412752.html
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版本已經淘汰。
如果需要實作微信支付和其他微信功能,那麼參考第四項。
裡面的坑很多:記住,參數名的大小寫要一緻,不管是前端還是背景,在簽名時,隻要大小寫不一緻,就會出錯。