1、吐槽篇php
一個字——坑!兩個字——很坑!三個字——很是坑!首先,微信支付接口做爲微信開發接口的一部分,居然有一本書那麼厚的官方文檔,共36頁,更重要的是,這36頁還不能把開發的流程說清楚,描述過於分散,過分分類,致使遇到一個問題的時候很難定位,雖然文檔前面給了時序圖,可是開發流程的時序化仍然不夠,讓人以爲十分混亂。原本接手這個任務的時候時間就很是的緊,想着找個demo撐死一兩天就完事了,沒想到最終耗費了接近一個星期,除去中間別的事耽誤的時間也用了整整五天的時間,在網上查找資料的時候也看到不少人說一個問題卡好幾天甚至幾周的狀況,實在是難以接受。html
2、開發流程前端
我這裏就一併按順序總結了。首先我用的是JS-SDK,即官方文檔所描述的方式去作,必須說明的是,雖然是JS-SDK,可是整個簽名過程不可能只在JS裏完成,因爲安全性問題,微信提供的接口JS是沒法跨域調用的,必須在後臺進行調用再將結果發給前臺。OK,個人開發的宏觀狀況是,前端使用JS-SDK,後端使用JAVA tomcat。ajax
整體流程是:①受權部分:後端經過微信接口拿到access_token,再拿到ticket(jsapi_ticket),給前臺,前臺經過js-sdk將jsapi_ticket,noncestr,timestamp,url簽名,獲取受權。數據庫
②支付部分:前端獲取code,給後端,後端經過code訪問微信接口拿到openid,經過openid等一堆key信息和回調url拿到prepayid,把prepayid,timestamp,noncestr給前臺,前臺通把這些東西以及簽名提交,發起支付。json
③回調部分:支付成功後,支付部分填寫的url的對應方法會執行,拿到後從參數中拿到支付成功後的信息,校驗簽名,發送成功結果給支付發起端。後端
④完成部分:前端收到成功的消息,自定義跳轉頁面。api
一、受權部分跨域
js部分:tomcat
wx.config({ debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。 appId: '', // 必填,公衆號的惟一標識 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串 signature: '',// 必填,簽名,見附錄1 jsApiList: ["chooseWXPay"] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2 });
這是發起受權申請的部分,signature須要簽名的字段包括:
"jsapi_ticket="+ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
說明:
①jsapi_ticket獲取:
須要調用接口獲取,調用接口不能用js(跨域),因此在後臺進行,接口調用使用https://api.weixin.qq.com/cgi-bin/ticket/getticket,GET方法便可,參數是"access_token="+access_token +"&type=JSAPI"
②access_token獲取:
調用https://api.weixin.qq.com/cgi-bin/token,一樣使用GET方法,參數是"appid="+appid+"&secret="+secret+"&grant_type=client_credential",只要參數不錯,就能拿到jsapi_ticket了。
(後面獲取opendid的時候也會拿到一個access_token,但不是同一個!)
③nonceStr、timestamp在JS生成便可,記得簽名時候的timestamp和nonceStr要和參數填寫的同樣。
④簽名規則:
就是在上面那一串東西后面加上"&key=xxxx",而後MD5加密處理後,變成大寫,key是商戶的key,千萬別搞錯了,不是appkey。規則具體看這裏。
⑤appId,這裏I是大寫的
⑥url的獲取:
這個url不須要urlencode,可是必須是當前發起支付頁的url,若url中有#號,只取#前面的。下面代碼直接拿走吧!
location.href.split("#")[0]
⑦invalid signature:如⑥所說,url輸入的不對,url要動態獲取
⑧invalid url domain :url和appid沒綁定,要在微信公衆號那裏的功能配置裏配置,別把安全域名和業務域名搞混了……(我就在這裏卡了半天)
配置成功後,就會進入
wx.ready(function(){
}
這個函數裏面,而且若是debug打開,能看到configok的反饋。
二、支付部分
爲了確保wx.config成功後再支付(不然不可能支付),請把下面函數放到wx.ready裏面:
wx.chooseWXPay({ timestamp: 0, // 支付簽名時間戳,注意微信jssdk中的全部使用timestamp字段均爲小寫。但最新版的支付後臺生成簽名使用的timeStamp字段名需大寫其中的S字符 nonceStr: '', // 支付簽名隨機串,不長於 32 位 package: '', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***) signType: '', // 簽名方式,默認爲'SHA1',使用新版支付需傳入'MD5' paySign: '', // 支付簽名 success: function (res) { // 支付成功後的回調函數 } });
首先說明填寫要怎麼填,別的應該都好理解,package這個東西有點不明因此,這個東西官方文檔叫作擴展包,可是在整個官方文檔裏卻又找不到擴展包要填什麼,只知道確定要填"package=prepay_id=xxxx",我翻查了舊版微信支付的demo等,知道了這個擴展包主要是填寫一些選填的東西,例如幣種、編碼等,但這並非必須的,用最簡單的"package=prepay_id=xxxx"填寫就能夠了。
先給出要簽名的字符串列表
String signstr = "appid="+appid+"&body="+body+"&mch_id="+mch_id+"&nonce_str="+nonce_str+"¬ify_url="+ notify_url +"&openid="+openid+"&out_trade_no="+out_trade_no+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+total_fee+"&trade_type=JSAPI"+"&key="+appkey;
說明
①簽名必須在後端完成,前端timestamp和nonceStr必須和後端簽名時用的值一致。
②notify_url不須要urlencode,回調地址最好不要加參數(回調地址是支付成功後通知的那個地址接口,接口內容本身完成)
③openid的獲取:
能夠看官方文檔,這些文檔都不是一個文檔,因此很容易找不到。
具體作法:
首先,在客戶端點擊支付後,必須先跳轉到這個地址:
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,是你使用JS-SDK的頁面的地址,也就是wx.chooseWXPay所在頁面的地址,必須進行urlencode,跳轉過去後能在url中尾部看到?code=CODE&state=STATE,把CODE從url中取出來。
2)使用GET方法調用這個接口https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code,把剛取到的code填進去,還有別的參數不要填錯了。
3) 返回的json格式中就能取到openid了。
④prepayid的獲取:
這部分仍然在後臺進行,使用post而且是XML格式發送數據,也就是說發送一段XML格式的字符串就是了,格式以下:
String text = "<xml>"+ "<appid>"+appid+"</appid>"+ "<body>"+body+"</body>"+ "<mch_id>"+mch_id+"</mch_id>"+ " <nonce_str>"+nonce_str+"</nonce_str>"+ " <notify_url>"+notify_url+"</notify_url>"+ "<openid>"+openid+"</openid>"+ "<out_trade_no>"+out_trade_no+"</out_trade_no>"+ "<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+ "<total_fee>"+total_fee+"</total_fee>"+ "<trade_type>"+"JSAPI"+"</trade_type>"+ "<sign>"+sign+"</sign>"+ "</xml>";
說明:
1)spbill_create_ip是支付終端的IP地址
2)notify_url不用urlencode
3)total_fee不能是小數,由於單位是分
4)簽名上面已經給了,就是用key,value方式把除了sign之外的全部字符串連起來,最後加上"&key=xxx",而後MD5再轉大寫。
5)out_trade_no,這個是本身標記本身商品號的一個號,不能重複,因此建議用時間戳+一些本身的規則。
6)body是中文時報簽名錯誤:首先確保中文是utf-8,而後轉成iso8859-1,能夠用下面這句代碼:
text = new String(text.getBytes(), "iso8859-1");
注意:①假如是本身在本地調用接口,寫死一箇中文去測試,請確保你的頁面編碼是utf-8,像我這種不太注意的,原本頁面用了GBK編碼,因此怎麼試都不成功,怎麼查編輯文件用什麼編碼,自行谷歌百度本身的編輯器的設置編碼在哪裏。
②tomcat的編碼,若是是從ajax把中文的body傳到後臺,請注意tomcat的編碼設置,確保拿到的中文是utf-8。
7)上面的作好了,就能夠拿到prepayid了,將prepayid和本身生成的nocestr和timestamp以及剛纔生成的簽名sign傳回給前臺,而且填到wx.chooseWXPay裏,嘗試支付。
8)「商戶簽名錯誤」:假如已經拿驗算工具查過,簽名是正確的,仍然報錯,那必然是這個緣由——timeStamp,S是大寫的!!。雖然官網已經作出了提醒,可是繞了那麼多圈以後你早就不記得這件事了,或者說沒搞清楚,沒錯,簽名的時候timeStamp的S必須是大寫的,而後前臺的wx.chooseWXPay裏的timestamp是小寫的,就是那麼的神奇!
9)當前的頁面URL未註冊:
微信公衆帳戶裏的微信支付-支付測試,看看填寫的地址是否是支付所在的地址。
10)系統繁忙,正在升級,請稍後再試:
基本上不會是系統繁忙,這是我整個過程遇到微信接口惟一的一次報錯錯誤,請檢查簽名參數,着重檢查中文編碼問題。
三、支付部分
終於,突太重重難關,彈出支付圖標而且能夠支付了,可是怪並無打完,你的回調接口可能還沒完善,這篇文章對回調部分的描述挺不錯的,網上能下到JAVA的DEMO,但有些部分已經不能用了,主要不能用的部分是拿到數據的時候須要用inputstream去處理,而不能經過request直接讀取。
回調要要作的事:
①拿到反饋數據
只要支付成功了,微信會推送一個XML格式的數據過來,JAVA請用下面的代碼解析出來
public static byte[] readBytes(InputStream in) throws IOException { byte[] temp = new byte[in.available()]; byte[] result = new byte[0]; int size = 0; while ((size = in.read(temp)) != -1) { byte[] readBytes = new byte[size]; System.arraycopy(temp, 0, readBytes, 0, size); result = SystemUtil.mergeArray(result,readBytes); } return result; }
②校驗簽名
微信支付傳過來數據,但未必是對的,因此把裏面的簽名拿出來,把別的變量拿出用keyvalue一樣的方式加密再和簽名對比,同樣的就是對的。
③返回SUCCESS
④微信會在30分鐘內進行8次回調,這是擔憂你網絡問題等拿不到支付後的數據,因此你要判斷這個東西是否已經成功處理過了
⑤處理:不外乎就是拿出訂單號等,寫入本身的數據庫裏。
四、完成部分
wx.chooseWXPay({ timestamp: 0, // 支付簽名時間戳,注意微信jssdk中的全部使用timestamp字段均爲小寫。但最新版的支付後臺生成簽名使用的timeStamp字段名需大寫其中的S字符 nonceStr: '', // 支付簽名隨機串,不長於 32 位 package: '', // 統一支付接口返回的prepay_id參數值,提交格式如:prepay_id=***) signType: '', // 簽名方式,默認爲'SHA1',使用新版支付需傳入'MD5' paySign: '', // 支付簽名 success: function (res) { // 支付成功後的回調函數 } });
看到「支付成功後的回調函數」了嗎?就在這裏面寫完成後跳轉的頁面,location.href=xxx,完結了~
大功告成……唉,真很差意思說大功,像支付寶同樣的話也就一天半天的事,這個卻整了那麼久。
給官方的建議是,Demo可否出如今顯眼的位置,可否作一個針對DEMO的文檔或者說明(例如這個demo是適合哪一個版本的都沒說),對DEMO作相應修改或者填寫各類key和id就好了,頂多再教一下配置些什麼,畢竟不少東西都是死的,不須要用戶所有敲一遍,只不過按照規則填就是了。