年末了,反而工做更忙了,我從15年11月開始寫《1024伐木累》系列小說和爆笑對白,獲得了不少身邊的技術好友的支持,如今爆笑對白已經有愈來愈多的朋友一塊兒幫着寫段子,整理,包括小說內容的編輯工做等。雖然《1024伐木累》獲得的褒貶不一,可是,對我來講,堅持的過程仍是快樂的。我一直有一個願望,也願意一直堅持着嘗試下去,包括我在公司作技術管理同樣,傳遞給身邊程序員小夥伴的思想只有一個:快快樂樂工做,健健康康編碼。我想可以參與到技術開發中來的朋友,都但願如此。因此,小說的目的,是但願處於不一樣開發階段的朋友,找到本身的方向,最終實現本身的人生價值。而爆笑對白的製做,就是想讓更多的程序工做者獲得快樂,哪怕只是瞬間的。php
迴歸主題,16年1月初我對微信開發比較好奇,因爲本身是一個比較喜歡錢的人,因此對支付功能頗爲衝動,就用公司信息在微信平臺申請了一個服務號,還給騰訊打賞了300大洋作了下認證,抽空看了下微信支付官方的文檔,大概瞭解了一下微信支付的流程以及開發過程,說實話,雖然看完了,可是對微信官方提供的文檔有些不滿,感受不清不楚的,可是大概的支付方式已經瞭解,這時候,個人懶勁又來了,在網上找到了作微信對接的C#SDK:Senparc.Weixin.MP。這個類庫的封裝,在我看來,仍是不錯的,基本囊貨了微信的功能,而且在不斷的完善。類庫是開源的,每一個功能都有寫簡單的單元測試,看起來一目瞭然。主要對接DLL對應的功能以下:前端
公衆號+微信支付 SDK:Senparc.Weixin.MP.dll程序員
企業號 SDK:Senparc.Weixin.QY.dll算法
開放平臺 SDK:Senparc.Weixin.Open.dll api
官方地址:http://weixin.senparc.com/ 瀏覽器
固然,咱們要完成公衆號微信支付功能的開發,須要使用Senparc.Weixin.MP.dll這個DLL,查閱了一下官方提供的DEMO以及教程,並無載入微信支付相關的說明,沒辦法,既然拿到源碼了,本身找吧。微信
打開Senparc.Weixin.MP.sln,根據英文文件夾名稱的分類,能夠初步判斷,關於微信支付,被封裝在TenPayLib文件夾中,可是我還發現,裏面存在名稱叫「TenPayLibV3」的文件夾,那如何選擇呢?網上搜索了一下,得出這個結論:2014年9月10號以前申請的爲v2版,以後申請的爲v3版。我用來測試微信支付的服務號是在16年剛申請,而且經過驗證的,那麼果斷使用V3吧。微信開發
打開TenPayLibV3文件夾:app
這裏發現多個類庫,每個都是作什麼的呢?咱們這裏不一一敘述,感興趣的朋友能夠下載來看,每個類的文件頭都有功能說明與描述,對照微信官方支付說明,咱們直接開始作支付。dom
進入微信公衆號,點擊功能菜單中的微信支付:並相應點擊 使用教程-公衆號支付
迅速對文檔內容重溫、瀏覽,以方便在Senparc.Weixin.MP.dll中查找相應的功能。
先配置支付受權目錄,添加支付測試白名單,支付目錄只支持三個,而且域名必須通過ICP備案。受權目錄的做用是,若是要發起微信支付請求,請求的連接地址必須在受權目錄下,不然身份無效,支付不能成功。測試白名單中添加的我的微信號,才能完成微信支付測試目錄支付的測試,不在白名單中人員發起支付申請,支付不能成功。
配置完成後,如何調用呢?咱們繼續看官方說明:H5調起支付API
「在微信瀏覽器裏面打開H5網頁中執行JS調起支付。接口輸入輸出數據格式爲JSON。
注意:WeixinJSBridge內置對象在其餘瀏覽器中無效。
列表中參數名區分大小,大小寫錯誤簽名驗證會失敗。」
OK,這裏說明了幾個事情,第一必須在微信瀏覽器進行;第二,參數區分大小寫;第三,數據格式爲JSON。
官方說明,只要在頁面中調用以下腳本,便可開啓微信支付功能:
1 function onBridgeReady(){ 2 WeixinJSBridge.invoke( 3 'getBrandWCPayRequest', { 4 "appId" : "wx2421b1c4370ec43b", //公衆號名稱,由商戶傳入 5 "timeStamp":" 1395712654", //時間戳,自1970年以來的秒數 6 "nonceStr" : "e61463f8efa94090b1f366cccfbbb444", //隨機串 7 "package" : "prepay_id=u802345jgfjsdfgsdg888", 8 "signType" : "MD5", //微信簽名方式: 9 "paySign" : "70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信簽名 10 }, 11 function(res){ 12 if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回 ok,但並不保證它絕對可靠。 13 } 14 ); 15 } 16 if (typeof WeixinJSBridge == "undefined"){ 17 if( document.addEventListener ){ 18 document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false); 19 }else if (document.attachEvent){ 20 document.attachEvent('WeixinJSBridgeReady', onBridgeReady); 21 document.attachEvent('onWeixinJSBridgeReady', onBridgeReady); 22 } 23 }else{ 24 onBridgeReady(); 25 }
個人調用代碼:由於我要在點擊按鈕確認支付以後,在調用微信支付進行後續操做,把官方代碼提出到方法中
function onBridgeReady() { WeixinJSBridge.invoke( 'getBrandWCPayRequest', { "appId": $('#APPID').val(), //公衆號名稱,由商戶傳入 "timeStamp": $('#Timestamp').val(), //時間戳,自1970年以來的秒數 "nonceStr": $('#Noncestr').val(), //隨機串 "package": $('#package').val(), "signType": "MD5", //微信簽名方式: "paySign": $('#paySign').val() //微信簽名 }, function (res) { if (res.err_msg == "get_brand_wcpay_request:ok") { //支付成功,後續自行處理 } else { //支付取消,或者其餘錯誤,自行處理 } } ); }
好吧,那這堆參數是從哪來的,都是啥玩意兒?咱們逐個分析一下:
appId:這個作微信開發都應該知道,公衆號在開發者菜單就能找到
timeStamp:時間戳,官方描述爲「自1970年以來的秒數」,不用擔憂,確定能從支付類庫裏找到
nonceStr:官方解釋是隨機串「e61463f8efa94090b1f366cccfbbb444」,靠啥玩意兒?詳見隨機數生成算法,原來就是一套加密規則和算法,作過URL請求接口的朋友應該知道,有些公司JSON串的簽名方式和這比較相似。
package:預支付ID,調用官方API統一下單接口能夠得到
signType:字符串"MD5"
paySign:官方解釋是微信簽名「70EA570631E4BB79628FBCA90534C63FF7FADD89」,好吧,我忍了,在看下簽名生成算法,看來和隨機串一個鳥樣
到這裏,官方的接口說明已經瞭解的很清楚了,那麼下面就要解決調用微信支付的這幾個參數了,經過Senparc.Weixin.MP.dll應該如何使用呢?既然須要先調用統一下單接口獲取預支付訂單ID,好吧,咱們先來研究一下,如何得到這個ID吧。
官方給出了詳細說明,咱們不在贅述,各參數研究按照上述接口的方式自行研究解決,惟一區別在於,調用官方接口須要傳入一個XML,那很好辦,拼接一下就能夠了,預支付調用方法以下:
//這裏經過官方的一個實體,用戶自行使用,我這裏是直接讀取的CONFIG文件 private static Senparc.Weixin.MP.TenPayLibV3.TenPayV3Info tenPayV3Info = new Senparc.Weixin.MP.TenPayLibV3.TenPayV3Info(ConfigurationManager.AppSettings["corpId"], ConfigurationManager.AppSettings["corpSecret"], ConfigurationManager.AppSettings["mch_id"] , ConfigurationManager.AppSettings["key"], ConfigurationManager.AppSettings["v3url"]); /// <summary> /// 微信預支付 /// </summary> /// <param name="attach"></param> /// <param name="body"></param> /// <param name="openid"></param> /// <param name="price"></param> /// <param name="orderNum"></param> /// <returns></returns> public static string PayInfo(string attach, string body, string openid, string price, string orderNum = "1833431773763549") { RequestHandler requestHandler = new RequestHandler(HttpContext.Current); //微信分配的公衆帳號ID(企業號corpid即爲此appId) requestHandler.SetParameter("appid", tenPayV3Info.AppId); //附加數據,在查詢API和支付通知中原樣返回,該字段主要用於商戶攜帶訂單的自定義數據 requestHandler.SetParameter("attach", attach); //商品或支付單簡要描述 requestHandler.SetParameter("body", body); //微信支付分配的商戶號 requestHandler.SetParameter("mch_id", tenPayV3Info.MchId); //隨機字符串,不長於32位。 requestHandler.SetParameter("nonce_str", TenPayUtil.GetNoncestr()); //接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數。 requestHandler.SetParameter("notify_url", tenPayV3Info.TenPayV3Notify); //trade_type=JSAPI,此參數必傳,用戶在商戶公衆號appid下的惟一標識。 requestHandler.SetParameter("openid", openid); //商戶系統內部的訂單號,32個字符內、可包含字母,本身生成 requestHandler.SetParameter("out_trade_no", orderNum); //APP和網頁支付提交用戶端ip,Native支付填調用微信支付API的機器IP。 requestHandler.SetParameter("spbill_create_ip", "127.0.0.1"); //訂單總金額,單位爲分,作過銀聯支付的朋友應該知道,表明金額爲12位,末位分分 requestHandler.SetParameter("total_fee", price); //取值以下:JSAPI,NATIVE,APP,咱們這裏使用JSAPI requestHandler.SetParameter("trade_type", "JSAPI"); //設置KEY requestHandler.SetKey(tenPayV3Info.Key); requestHandler.CreateMd5Sign(); requestHandler.GetRequestURL(); requestHandler.CreateSHA1Sign(); string data = requestHandler.ParseXML(); requestHandler.GetDebugInfo(); //獲取並返回預支付XML信息 return TenPayV3.Unifiedorder(data); } }
好的,拿到預支付訂單的返回數據,一切又都好辦了,根據返回參數的不一樣,自行解決,咱們只關心調用正確的過程,操做繼續,在返回的正確XML數據中,咱們獲取到了 <prepay_id><![CDATA[wx201411101639507cbf6ffd8b0779950874]]></prepay_id>(官方示例),好的,開始在頁面作支付吧!
這裏,我封裝了一個實體,用來傳輸經常使用的數據,固然,各位也能夠參考Senparc.Weixin.MP.dll提供的實體類。
public class ShareInfo { string corpId = string.Empty; public string CorpId { get { return corpId; } set { corpId = value; } } string ticket = string.Empty; public string Ticket { get { return ticket; } set { ticket = value; } } string noncestr = string.Empty; public string Noncestr { get { return noncestr; } set { noncestr = value; } } string timestamp = string.Empty; public string Timestamp { get { return timestamp; } set { timestamp = value; } } private string paySign = string.Empty; public string PaySign { get { return paySign; } set { paySign = value; } } private string package = string.Empty; public string Package { get { return package; } set { package = value; } } }
咱們繼續,來看一下支付接口須要用到的參數如何獲取:
public static ShareInfo GetPayInfo(string prepayid) { shareInfo = new ShareInfo(); //檢查是否已經註冊jssdk if (!JsApiTicketContainer.CheckRegistered(corpId)) { JsApiTicketContainer.Register(corpId, corpSecret); } JsApiTicketResult jsApiTicket = JsApiTicketContainer.GetTicketResult(corpId); JSSDKHelper jssdkHelper = new JSSDKHelper(); shareInfo.Ticket = jsApiTicket.ticket; shareInfo.CorpId = corpId.ToLower(); shareInfo.Noncestr = JSSDKHelper.GetNoncestr().ToLower(); shareInfo.Timestamp = JSSDKHelper.GetTimestamp().ToLower(); shareInfo.Package="prepay_id=" + prepayid.ToLower(); RequestHandler requestHandler = new RequestHandler(HttpContext.Current); requestHandler.SetParameter("appId", shareInfo.CorpId); requestHandler.SetParameter("timeStamp", shareInfo.Timestamp); requestHandler.SetParameter("nonceStr", shareInfo.Noncestr); requestHandler.SetParameter("package", shareInfo.Package); requestHandler.SetParameter("signType", "MD5"); requestHandler.SetKey(tenPayV3Info.Key); requestHandler.CreateMd5Sign(); requestHandler.GetRequestURL(); requestHandler.CreateSHA1Sign(); shareInfo.PaySign = (requestHandler.GetAllParameters()["sign"]).ToString(); return shareInfo; }
這樣,支付接口須要用到的參數,就都封裝在ShareInfo裏了,好吧,調用以後,咱們回到頁面的後置代碼中,或者你採用的ORM對應代碼中去,將參數輸出到頁面
//處理頁面支付調用信息 ShareInfo shareInfo = TenPayModule.GetPayInfo(prepayid); System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"Noncestr\" runat=\"server\" value=\"{0}\" />", shareInfo.Noncestr)); System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"Timestamp\" runat=\"server\" value=\"{0}\" />", shareInfo.Timestamp)); System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"APPID\" runat=\"server\" value=\"{0}\" />", shareInfo.CorpId)); System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"paySign\" runat=\"server\" value=\"{0}\" />", shareInfo.PaySign)); System.Web.HttpContext.Current.Response.Write(string.Format("<input type=\"hidden\" id=\"package\" runat=\"server\" value=\"{0}\" />", shareInfo.Package));
好的,寫到這裏,你們參照上面的JS代碼,就能夠完成整個的支付功能了。最後,再附送一個生成商家訂單號的方法,代碼以下:
public string GetOrderNumber() { string Number = DateTime.Now.ToString("yyMMddHHmmss"); return Number + Next(1000, 1).ToString(); } private static int Next(int numSeeds, int length) { byte[] buffer = new byte[length]; System.Security.Cryptography.RNGCryptoServiceProvider Gen = new System.Security.Cryptography.RNGCryptoServiceProvider(); Gen.GetBytes(buffer); uint randomResult = 0x0; for (int i = 0; i < length; i++) { randomResult |= ((uint)buffer[i] << ((length - 1 - i) * 8)); } return (int)(randomResult % numSeeds); }
好了,一切都是這樣的簡單,一兩個小時,微信支付輕鬆搞定!