375
搜狐
為微信開發填坑:微信網頁支付的開發流程及填坑技巧
微信支付在整個微信體係中承擔著相當重要的一個角色,甚至開辟了國內乃至世界獨有的全民支付模式。現在大到商場超市,小到街邊攤販,都用到了微信支付,所以微信支付的開發隻會越來越多,越來越應該成為每個開發人員需要掌握的一項經驗,之所以沒有說是技能,是因為微信開發本身並沒有涉及到新的技術,隻是按照微信官方給出的協議和接口,做了技術對接而已。
一旦提到技術對接,必然會涉及到文檔、接口、參數這些雙方交互規則。然而,不知道出於何種原因,微信支付這塊的文檔相當混亂,某些地方語焉不詳,甚至demo也隻是片段代碼,想要從無到有的把微信支付打通有相當大的難度。難在於不知道從何下手。也正因為如此,網上有大量的教程文檔來說明微信支付如何開發,但是要麼隻是寥寥幾筆說下關鍵點,要麼隻是做個框架性的總結,目前為止,幾乎沒有從準備工作到支付打通的一個全麵的文檔。為了讓後麵做微信支付的朋友不再浪費大量的時間和精力,所以記錄下這篇微信開發的實戰教程。
為了方便大家開發是拿來參考,我會完全按照開發流程來記錄微信網頁支付的開發過程,主要從文檔、接口、開發三個部分來說明。一篇文章很難解決所有問題,所以在文末,我會留下技術交流的方式,以便大家互相討論。
文檔
開發在接觸到一個新的技術點的時候,最直接的做法就是查看官方文檔。但是微信關於支付這塊的文檔有兩套,關於網頁支付這塊有兩個版本,這就無形中挖了很多坑,隻能一遍一遍的試錯和查資料才能避過。到目前為止官方也沒有考慮整合優化一下這塊的文檔。
同時,網上不少趟過坑的網友也把自己的開發經曆寫成文檔發布出來,這也是一個很大的幫助。
所以,整個過程,我用到的文檔有三個:
微信公眾平台首頁最下方提供的開發文檔(文檔一)
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
這裏,主要用到該文檔對JSAPI在網頁上初始化配置,以及調起微信支付這兩部分的代碼。
登錄微信公眾平台後,最後一個菜單“接口權限”裏的微信支付接口,點擊進去的文檔(文檔二)
https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
這裏用到統一下單接口的說明,以及該接口中的參數生成規則。
網友提供的經驗分享文檔(文檔三)
https://blog.sina.com.cn/s/blog_48422a050102w364.html
這裏用到網頁支付的整個流程以及每個流程中需要用到的接口及參數
注意:隻要研究透這三個文檔,重點看我標注的地方就可以,這樣就不用花大量的時間在網上搜索教程,並且很多教程要麼語焉不詳,要麼錯誤百出,會造成誤導,浪費更多的時間。(文檔三也有一個錯誤,導致我浪費了半個下午的時間,後麵我會提到。)
接口
整個支付過程中,需要用到的官方接口有三個:
統一下單接口是微信支付的核心接口,主要是將訂單信息發送給微信服務端,微信服務端將訂單信息存儲後,返回的參數中有一個參數prepay_id,這個參數就是當前訂單在微信服務端的唯一標記,可以理解為主鍵ID。這也是我們調用這個接口,需要獲取的主要參數。
獲取accessToken接口:
String getTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="
這個接口的作用很單一,就是獲取accessToken參數,以備下一個參數使用。其中有兩個請求參數,分別是微信公眾平台的APPID和APPSECRET,在開發者模式下可以拿到,如下圖:
獲取jsapi_ticket接口:
String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken
這個接口的作用也是獲取一個參數ticket,以備後麵生成簽名時用,請求參數accessToken已經通過上一個接口獲得,所以可以直接獲取目標參數ticket。
整個支付過程,需要跟微信服務端交互的官方接口,隻有這三個,以上三個接口的具體使用場景,下麵會詳細講到,這裏隻需要了解一下。
開發
整個開發流程如下:
流程圖
基礎配置
登錄微信公眾號後台,開通微信支付功能
申請微信支付功能。開通微信支付功能時,需要提供各種資質和聯係方式,按照要求申請即可,這裏不會有任何問題。
微信支付菜單如下圖:
微信支付開通之後,如下圖:
開發配置
還是在微信支付這個功能頁麵,第二個頁簽,需要配置支付授權目錄。
注意:支付授權目錄:實際上用戶發起微信支付後,我們的服務端訪問處理微信支付的那個請求路徑的上一層。
舉個例子:用戶在微信公眾號上發起請求後,會調用我們服務端的地址:
https://www.test.com/wechat/pay/send 。
那麼,他的支付授權目錄就是:
https://www.test.com/wechat/pay/。
如果沒有配置這個目錄,或者配置的目錄與實際支付目錄不一致,支付都會失敗,並且報的錯誤是:{"errMsg":"chooseWXPay:fail"}。支付授權目錄可以配置五個,如果真的拿不準,可以多配置幾個,但是這樣會存在安全風險,慎用。
安全域名
微信公眾平台升級過很多次,在2015年的時候,還沒有強製填寫安全域名。這次做微信支付時,遇到了這個問題。
在開發中,跟微信交互都是通過我方服務端的接口,所以需要在微信公眾平台對接口的域名進行安全設置,方法如下:
登錄微信公眾平台,在左側菜單中找 設置》公眾號設置》功能設置。在這裏,有三個需要填寫域名的地方,業務域名,JS接口安全域名,網頁授權域名。微信支付隻跟JS接口安全域名相關,但是我們把這三個地方的安全域名全部都填寫下。
在菜單中選擇“公眾號設置”
選擇功能設置
注意:
這裏的域名必須是有備案的域名,否則在填寫的時候無法驗證通過;
在配置域名時,需要將官方提供的驗證文件放在服務端接口項目根目錄下,這個按照要求處理即可。
本地調試
在所有的軟件開發中,都需要在本地一邊開發一邊調試。但是,微信公眾號開發比較特殊,無法直接在本地調試。因為必須在微信公眾平台上配置服務端的接口地址,微信公眾號上的所有請求,都會通過這個接口地址來調用服務端的服務。微信公眾平台是外網,本地環境是內網,不能直接通過外網直接調用內網接口,所以,這裏必須用到內網映射,將本地的內網映射成一個外網,這樣就可以通過這個映射調用本地服務,進行接口調試。
內網映射,也被叫做內網穿透。實現方式一般有兩種:
通過第三方代理,將內網地址和端口映射到一個外網地址和端口。現在有很多免費的內網映射工具,最主流的是ngrok,用法可以自己百度。但是2015年以後,微信公眾號要求接口域名必須備案,因為ngrok映射的外網域名是沒有經過備案的,所以,ngrok就無法使用了。目前隻能去購買第三方收費版的內網映射服務,我們這次用的花生殼的服務。
如果公司有網絡專線,同時有外網IP,就非常簡單,將已經備案的域名解析到自己的外網IP,通過路由器進行內網映射就可以。
關於內網映射是另外一個話題,這裏就不做贅述,感興趣的話我們可以單開話題討論。
接口開發
以上準備工作完成以後,就可以正式進入開發階段。因為不同的支付場景支付的流程可能會有差異,這裏抽取出主要的流程舉例。
支付相關頁麵三個:
支付三個頁麵(wx_pay.jsp):收集openid和支付金額,並傳遞給訂單頁麵。
訂單頁麵(wx_order.jsp):封裝訂單,封裝微信支付參數,發起微信支付請求。
訂單頁麵(wx_success.jsp):成功信息展示。
流程如下:
流程圖
支付頁麵開發:
這個頁麵需要拿到openid和支付金額兩個參數傳遞給訂單頁麵去使用,支付金額是用戶輸入的內容,直接獲取就可以,獲取openid最直接的方式就是通過網頁授權接口獲取。將支付頁麵的訪問地址配置成微信公眾號的底部菜單,當用戶點擊菜單時,進入微信支付頁麵,我們通過這個請求,就可以拿到openid,或者直接調用網頁授權接口來獲取openid。
獲取網頁授權的接口文檔:
網頁授權有兩種模式,(基礎授權)和 (用戶信息授權),我們這裏隻需要拿openid,所以(基礎授權)就可以。
獲取openid比較簡單,也有什麼坑,所以嚴格按照上麵的文檔說明去獲取,不會有任何問題。
訂單頁麵開發:
微信網頁支付的核心就在這一步,這裏也是坑最多,流程最複雜的地方,如果把這裏搞明白了,微信網頁支付基本上就算完成了80%。
訂單支付分為兩部分,一個是前端頁麵,一個是後端業務接口。微信網頁支付主要是依賴前端js方法實現,後端接口僅是給前端js方法提供必要的參數。
前端頁麵(wx_order.jsp)基本結構如下:
引入微信支付的js版jdk
調用微信支付
添加支付按鈕
確認付款
這裏做了一個ajax的異步請求,在訂單頁麵,當用戶確認支付信息無誤,就點擊確認付款按鈕,這時調用後端接口,後端接口進行“封裝訂單,封裝微信支付參數”的處理後,將微信支付參數返回到前端頁麵。如果參數無誤,則會自動調起微信支付,用戶輸入支付密碼後,完成支付。
這裏,主要是wx.config和wx.chooseWXPay兩個方法,第一個方法進行微信支付驗證,驗證用戶身份,支付參數是否合法。當第一個方法驗證通過後,就可以調用wx.chooseWXPay,如果這個方法的參數也都準確無誤,則會自動彈出微信支付頁麵。
所以,關鍵就在這兩個方法中的參數。
wx.config所需參數:
debug: false, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。
timestamp: payReq.timestamp , // 必填,生成簽名的時間戳
nonceStr: payReq.noncestr, // 必填,生成簽名的隨機串
signature: payReq.signature,// 必填,簽名,見附錄1
jsApiList: [ chooseWXPay ] //必填,需要使用的JS接口列表,所有JS接口列表見附錄2
這裏需要我們獲取的參數有timestamp,nonceStr,signature 其他參數都是現成的,直接用就可以。
wx.chooseWXPay所需參數:
timestamp:payReq.timestamp,//支付簽名時間戳,注意微信jssdk中的所有使用timestamp字段均為小寫。但最新版的支付後台生成簽名使用的timeStamp字段名需大寫其中的S字符
nonceStr:payReq.noncestr, // 支付簽名隨機串,不長於 32 位
package: payReq.prepayId,//統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=*)
signType: MD5 , // 簽名方式,默認為 SHA1 ,使用新版支付需傳入 MD5
paySign: payReq.paySign, // 支付簽名
這裏需要我們獲取的參數有:timestamp,nonceStr,package,paySign其他參數都是現成的,直接用就可以。
合並起來,我們總共要獲取的參數是:timestamp,nonceStr,package,signature,paySign
這些參數,我們通過後端接口獲取,然後傳遞給前端頁麵。當用戶在訂單頁麵點擊確認支付後,ajax請求後端接口,後端接口可以接到參數:openid,訂單金額。封裝訂單信息根據自身的業務封裝對象即可。重點說前端頁麵所需參數的獲取。
調用“統一下單”接口,獲取參數:timestamp,nonceStr,package
接口文檔:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
timestamp,nonceStr:按照文檔中“統一下單”接口中參數的算法,可以直接生成。
package:這個參數獲取比較麻煩,文檔中需要用到兩個特殊的參數:mch_id(商戶號),key(商戶密碼),這兩個參數要去商戶平台拿。另外需要一個簽名參數sign,這個需要用其他參數合並生成。
商戶號:
商戶KEY:
注意:登錄商戶平台的瀏覽器,必須是IE內核,否則無法登陸。
拿到這些參數後,按照簽名算法,可以得到sign,注意,此處是MD5加密。
最後,按照統一下單接口的文檔,傳遞參數調用接口,在返回結果中可以拿到參數名為:的參數值,就是package。
signature:獲取這個簽名參數,sha1加密,需要參數,所有的簽名算法都是一樣的,隻是所需要的參數不一樣,所以,這裏的關鍵就是獲取。這塊比較繞,通過查閱大量文檔最終確定了下麵的關係:
獲取signature需要用到參數獲取參數需要調用前麵說的第三個接口,並且用到參數獲取參數需要調用前麵說的第二個接口。
所以,按照這個順序依次獲取,當拿到參數後,我們來生成簽名signature。
String signatureStr = "jsapi_ticket=" + ticket + "&noncestr=" + payReq.get("noncestr").toString()
signatureStr = Encoder.SHA1Encode(signatureStr.getBytes());
這裏payReq是上麵統一下單接口的返回結果集,url是當前接口的URL路徑。將簽名拚出來之後,直接加密,就拿到了我們要的簽名。
paySign:簽名,MD5加密。
整個簽名生成相對容易些,因為需要用到的參數現在已經全部具備了,隻需要拚接簽名,然後加密即可。
String paySignStr = "appId=" + APP_ID + "&nonceStr=" + payReq.get("noncestr").toString() + "&package="
這裏注意兩點,為了方便,我自己把需要用到的參數全部放到了payReq對象中,所以很多參數都是從這裏取出來的。另外,prepayId這個參數key的拚接不太一樣,需要這樣來處理:
payReq.put("prepayId", "prepay_id=" + payReq.get("prepayId")); 可以看出來,key的參數名後麵,要加上“=”,這個坑也趟了很久才出來。
同時,關於timeStamp中S大小寫的問題,很多文檔說的模煳不清,這裏做一下總結:
隻需要記住一點,所有使用timeStamp的地方,隻有在生成paySignStr簽名時,用的到timeStamp作為key的S是大寫,其他地方,都小寫。
至此,微信網頁支付所需要的所有參數獲取完畢,隻需要將這些參數從服務端返回給前端頁麵的兩個JS方法(wx.config和wx.chooseWXPay),當在頁麵點擊支付按鈕時,調用這兩個方法,就可以看到期待已久的微信支付彈層。
總結
微信網頁支付技術上基本上沒什麼難度,難的是流程複雜以及官方文檔的不足。為了拿到一個參數,需要調用兩個接口獲取,這種情況之前也是沒遇見過。所以,我們這裏隻需要記住幾個關鍵點:
兩個方法(wx.config和wx.chooseWXPay)
五個參數(timestamp,nonceStr,package,signature,paySign)
三個接口
三個頁麵以及三個文檔。
另外需要注意的,就是簽名的生成,整個過程用到了三個簽名:
第一個簽名sign。在統一下單接口中的請求參數中,sign做為這個接口的請求參數之一,需要用其他參數生成之後,才能作為請求參數再去請求,這裏用的是MD5加密。
第二個簽名signature。這個簽名是給wx.config用的,加密方式是SHA256。
第三個簽名paySign。這個簽名是給wx.chooseWXPay用的,這裏用的是MD5加密。
備注:這裏說明一下前麵提到的文檔三中的一處錯誤,其中提到的參數“appid”,應該是“appId”,i是需要大寫的。這個細節很容易被忽視,我已第一時間通知作者,希望能盡快更正。
隻要在把以上幾個關鍵點搞清楚,那麼微信網頁支付就非常簡單,而為了搞清楚這些,我花了大量的時間並查閱了各種文檔,同時跟微信商戶支付的技術支持溝通了五六封郵件,最終才調試通過。
最後更新:2017-10-08 00:02:03