官方給出的小程序支付交互流程圖如下,其實也比較清晰描述了整個過程
宏記(微信號:hongji8410)為了驗證這一過程,特地去實操了一下,現整理了下整個過程如下:
首先要準備幾個東西
小程序APPID(appid)
商戶號(mchid)
商家設置的密鑰(key)
在配置小程序時的密鑰(secret)
即 小程序->服務端——>微信服務端->服務端->小程序
小程序端發起三次請求,兩次向微信服務器(wx.login和wx.requestPayment),中間一次向服務端(wx.request)
服務端發起兩次請求與加密處理成簽名,兩次都是向微信服務器請求,一次請求獲取openid,一次統一下單獲取prepay_id,加密處理主要是通過MD5然后生成簽名參數sign
簽名參數sign主要有兩個用處,一個是向微信服務端請求預支付時需要用到這個參數,而另一個是返回小程序端調用確認支付里需要用到,而簽名的生成都是通過MD5加密appid+openid/prepay_id等參數而成
調用wx.login得到jscode,通過wx.request把jscode傳送給后臺
wx.login({ success:function(res){ if (res.code) { //這里得到jscode即可傳到后臺進行處理 wx.request({ url:'https://www.voopAPI/getData?js_code='+res.code data: {}, method: 'GET', success: function(res){ //后臺處理預支付成功,在這就能得到paySign等確認支付需要的參數 } }) }; }; }); |
首先根據傳過來的jsCode向微信服務器請求session_key和openid
public string GetSessionKeyOpenId(string appid, string secret, string js_code) { var url = string.Format("https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code", appid, secret, js_code); var request = WebRequest.Create(url) as HttpWebRequest; var response = request.GetResponse(); var respStream = response.GetResponseStream(); var res = string.Empty; using (var reader = new StreamReader(respStream, Encoding.UTF8)) { res = reader.ReadToEnd(); } return res; } |
返回數據
{\”session_key\”:\”a6Td9fWKZx8LkPFCsGA==\”,\”expires_in\”:7200,\”openid\”:\”oujEK0QoA0wI_DDyE5660\”}
其次根據openid,appid,mch_id等參數封裝成一個字符串后用MD5加密后向微信服務器統一下單
var paramter = new WXPayParameters(); paramter.AppId = shopinfo.AppId; paramter.Body = shopinfo.ShopName + "-" + orderpool.OrderPoolCode; paramter.MchId = shopinfo.MchId; paramter.Nonce = WXHandleBll.GetNoncestr(); paramter.Notify_Url = shopinfo.Notify_Url; paramter.Out_Trade_No = orderpool.OrderPoolCode; paramter.Spbill_Create_Ip = "127.0.0.1"; paramter.Total_Fee = "1";//(orderpool.NetAmt).ToString(); 暫時1分,用于測試 paramter.Trade_Type = shopinfo.Trade_type; paramter.Key = shopinfo.Key; var param = WXHandleBll.GetOrderUnifiedParam(reslogion.openid, paramter); |
MD5加密得出簽名sign
public string GetOrderUnifiedParam(string openid, WXPayParameters param) { //參與統一下單簽名的參數,除最后的key外,已經按參數名ASCII碼從小到大排序 var unifiedorderSignParam = string.Format("appid={0}&body={1}&mch_id={2}&nonce_str={3}¬ify_url={4}&openid={5}&out_trade_no={6}&spbill_create_ip={7}&total_fee={8}&trade_type={9}&key={10}" , param.AppId, param.Body, param.MchId, param.Nonce, param.Notify_Url, openid, param.Out_Trade_No, param.Spbill_Create_Ip, param.Total_Fee, param.Trade_Type, param.Key); //MD5 var unifiedorderSign = GetMD5(unifiedorderSignParam).ToUpper(); //構造統一下單的請求參數 var requestParam = string.Format(@"<xml> <appid>{0}</appid> <body>{1}</body> <mch_id>{2}</mch_id> <nonce_str>{3}</nonce_str> <notify_url>{4}</notify_url> <openid>{5}</openid> <out_trade_no>{6}</out_trade_no> <spbill_create_ip>{7}</spbill_create_ip> <total_fee>{8}</total_fee> <trade_type>{9}</trade_type> <sign>{10}</sign> </xml> ", param.AppId, param.Body, param.MchId, param.Nonce, param.Notify_Url, openid, param.Out_Trade_No, param.Spbill_Create_Ip, param.Total_Fee, param.Trade_Type, unifiedorderSign); return requestParam; } |
將要MD5的字符串
appid=w362e8457f1ca&body=中國特館-O2017041200015&mch_id=14107872&nonce_str=819F462C255C424244317¬ify_url=http://yourdomain.com/notifyurl&openid=oujEK0QoAI_DDyE5660&out_trade_no=O20170015&spbill_create_ip=127.0.0.1&total_fee=1&trade_type=JSAPI&key=4537751044EE2113393A
MD5后的簽名
DFWGFGF81D73DBA86C645D8A3
簽名規則注意事項(must)
參數名ASCII碼從小到大排序(字典序);
如果參數的值為空不參與簽名;
參數名區分大小寫;
驗證調用返回或微信主動通知簽名時,傳送的sign參數不參與簽名,將生成的簽名與該sign值作校驗。
微信接口可能增加字段,驗證簽名時必須支持增加的擴展字段
再著,向微信服務再次進行請求(統一下單),獲取預支付信息
var RespayXML = WXHandleBll.DoPost(param, "https://api.mch.weixin.qq.com/pay/unifiedorder");
發送的XML樣式
<xml>\r\n <appid>w3e843377f9a</appid> \r\n <body>中國館-O2017041200015</body>\r\n <mch_id>1445576552</mch_id> \r\n <nonce_str>819F465763A252422644317</nonce_str>\r\n <notify_url>http://voopaip.com/notifyurl</notify_url>\r\n<openid>oujEK0Q3oAuwI_DDyE5660</openid>\r\n <out_trade_no>O2017200015</out_trade_no>\r\n <spbill_create_ip>127.0.0.1</spbill_create_ip>\r\n <total_fee>1</total_fee>\r\n <trade_type>JSAPI</trade_type>\r\n <sign>A5381D73DBA86D3DC645D8A3</sign>\r\n </xml>\r\n 最后,根據統一下單請求返回的參數prepay_id再加上appid等參數封裝成一個字符串后用MD5再得出一個簽名參數,并且處理其它參數后全部返回小程序端所需要的參數 //3.統一下單后拿到的xml結果 var payRes = XDocument.Parse(RespayXML); var ResXML = payRes.Element("xml"); //序列化相應參數返回給小程序 var res = WXHandleBll.GetPayRequestParam(ResXML, paramter.AppId, paramter.Key); public WXPayRequesEntity GetPayRequestParam(XElement root, string appid, string key) { //當return_code 和result_code都為SUCCESS時才有我們要的prepay_id if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS") { var package = "prepay_id=" + root.Element("prepay_id").Value; var nonceStr = GetNoncestr(); var signType = "MD5"; var timeStamp = Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString(); var paySignParam = string.Format("appId={0}&nonceStr={1}&package={2}&signType={3}&timeStamp={4}&key={5}", appid, nonceStr, package, signType, timeStamp, key); var paySign = GetMD5(paySignParam).ToUpper(); var payEntity = new WXPayRequesEntity { package = package, nonceStr = nonceStr, paySign = paySign, signType = signType, timeStamp = timeStamp }; return payEntity; } return new WXPayRequesEntity(); }
返回XML樣式
<xml> <return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg> <appid><![CDATA[w2e84759cca]]></appid> <mch_id><![CDATA[141008772]]></mch_id> <nonce_str><![CDATA[brDijFx44Xx4Nl]]></nonce_str> <sign><![CDATA[690985862E39F75F03D447E3C9]]></sign> <result_code><![CDATA[SUCCESS]]></result_code> <prepay_id><![CDATA[w201702219c9a14b79b753854012]]></prepay_id> <trade_type><![CDATA[JSAPI]]></trade_type> </xml>
wx.requestPayment({ timeStamp: res.data.Body.Data.timeStamp, nonceStr: res.data.Body.Data.nonceStr, package: res.data.Body.Data.package, signType: 'MD5', paySign: res.data.Body.Data.paySign, success: function (res) { //確認成功 } }); |
收集的一些支付坑,方便遇到問題可供查閱
微信支付接口要嚴格區分大小寫。在獲取預付單數據的時候,timestamp沒有區分大小寫,導致支付的收銀臺一直調用不起來。
簽名操作,一定是要配合appId,appid商戶號KEY是否正確,參與簽名的字符串是否按照要求排序,是否是UTF8格式(實在不行可重置一下),在具體簽名方法說明中,可以看出key是在簽名參數按照ASCII大小排序完再拼接上去的,
返回錯誤說total_fee參數為空,如果total_fee參數不為空,可能是package格式不對應該為”prepay_id=”+prepay_id
統一下單接口是xml(這個不只是小程序,公眾號也是),返回值也是xml格式需要自己獲取prepay_id,
簽名算法要帶上key,最后要轉換成大些
微信支付的sign算法也要帶上appid(這個不科學,深坑)
簽名算法一定不要用json拼接key
簽名MD5加密,網上有些算法是錯誤的,自己寫完還需要在線MD5加密工具進行校驗(我采坑一下午,怎么看我寫的怎么對,就是出不來,原因就是MD5工具使用錯誤,坑爹- - )
統一下單簽名appid,wx.requestPayment簽名appId(大小寫必須區分,真是找瞎我鈦合金狗眼- - )
wx.requestPayment中package參數必須是package:”prepay_id=wx21**“,不然,會出現調用支付JSAPI缺少appid/total_fee
total_fee為分,并且是int
生成隨機數和時間戳一定要保證簽名與上傳參數一致
微信小程序trade_type=JSAPI,openid參數必傳
wx.requestPayment生成簽名有appId,請求的時候沒有appId