最近負責的一些項目開發,都用到了微信支付(微信公衆號支付、微信H5支付、微信掃碼支付)。在開發的過程當中,在調試支付的過程當中,或多或少都遇到了一些問題,今天總結下,分享,留存。代碼在文章結尾處,有須要的同窗能夠下載看下。php
先說注意的第一點,全部支付的第一步都是請求統一下單,統一下單,統一下單,請求URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder。node
再說一個微信官方提供的一個很重要的工具,微信支付接口簽名校驗工具(網址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1),此工具旨在幫助開發者檢測調用【微信支付接口API】時發送的請求參數中生成的簽名是否正確,提交相關信息後可得到簽名校驗結果。特別實用!特別實用!特別實用!簽名只要正確了,一切就OK了!web
第一部分 微信公衆號支付算法
微信公衆號支付須要配置的參數有:APPID(微信公衆號開發者ID)、APPSECRET(微信公衆號開發者密碼)、MCHID(商戶ID)、KEY(商戶密鑰)。json
微信公衆號支付應用的場景是在微信內部的H5環境中使用的支付方式。由於要經過網頁受權獲取用戶的OpenId,因此必需要配置網頁受權域名。同時要配置JS接口安全域名。api
JsApiConfig.cs數組
1 using System.Web; 2 using System.Text; 3 using System.IO; 4 using System.Net; 5 using System; 6 using System.Xml; 7 using System.Collections.Generic; 8 using Gwbnsh.Common; 9 10 namespace Gwbnsh.API.Payment.wxpay 11 { 12 public class JsApiConfig 13 { 14 #region 字段 15 private string partner = string.Empty; 16 private string key = string.Empty; 17 private string appid = string.Empty; 18 private string appsecret = string.Empty; 19 private string redirect_url = string.Empty; 20 private string notify_url = string.Empty; 21 #endregion 22 23 public JsApiConfig(int site_payment_id) 24 { 25 Model.site_payment model = new BLL.site_payment().GetModel(site_payment_id); //站點支付方式 26 if (model != null) 27 { 28 Model.payment payModel = new BLL.payment().GetModel(model.payment_id); //支付平臺 29 Model.sites siteModel = new BLL.sites().GetModel(model.site_id); //站點配置 30 Model.sysconfig sysConfig = new BLL.sysconfig().loadConfig(); //系統配置 31 32 partner = model.key1; //商戶號(必須配置) 33 key = model.key2; //商戶支付密鑰,參考開戶郵件設置(必須配置) 34 appid = model.key3; //綁定支付的APPID(必須配置) 35 appsecret = model.key4; //公衆賬號secert(僅JSAPI支付的時候須要配置) 36 37 //獲取用戶的OPENID回調地址及登陸後的回調地址 38 redirect_url = "http://m.gwbnsh.net.cn/hd/SellPhone" + payModel.return_url; 39 notify_url = "http://m.gwbnsh.net.cn/hd/SellPhone" + payModel.notify_url; 40 } 41 } 42 } 43 44 #region 屬性 45 /// <summary> 46 /// 商戶號(必須配置) 47 /// </summary> 48 public string Partner 49 { 50 get { return partner; } 51 set { partner = value; } 52 } 53 54 /// <summary> 55 /// 獲取或設交易安全校驗碼 56 /// </summary> 57 public string Key 58 { 59 get { return key; } 60 set { key = value; } 61 } 62 63 /// <summary> 64 /// 綁定支付的APPID(必須配置) 65 /// </summary> 66 public string AppId 67 { 68 get { return appid; } 69 set { appid = value; } 70 } 71 72 /// <summary> 73 /// 公衆賬號secert(僅JSAPI支付的時候須要配置) 74 /// </summary> 75 public string AppSecret 76 { 77 get { return appsecret; } 78 set { appsecret = value; } 79 } 80 81 /// <summary> 82 /// 獲取用戶的OPENID回調地址 83 /// </summary> 84 public string Redirect_url 85 { 86 get { return redirect_url; } 87 } 88 89 /// <summary> 90 /// 獲取服務器異步通知頁面路徑 91 /// </summary> 92 public string Notify_url 93 { 94 get { return notify_url; } 95 } 96 97 #endregion 98 } 99 }
JsApiPay.cs瀏覽器
1 using System; 2 using System.Collections.Generic; 3 using System.Web; 4 using System.Net; 5 using System.IO; 6 using System.Text; 7 using Gwbnsh.Common; 8 9 namespace Gwbnsh.API.Payment.wxpay 10 { 11 public class JsApiPay 12 { 13 /** 14 * 15 * 測速上報 16 * @param string interface_url 接口URL 17 * @param int timeCost 接口耗時 18 * @param WxPayData inputObj參數數組 19 */ 20 public static void ReportCostTime(int paymentId, string interface_url, int timeCost, WxPayData inputObj) 21 { 22 //若是僅失敗上報 23 if (inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" && 24 inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS") 25 { 26 return; 27 } 28 29 //上報邏輯 30 WxPayData data = new WxPayData(); 31 data.SetValue("interface_url", interface_url); 32 data.SetValue("execute_time_", timeCost); 33 //返回狀態碼 34 if (inputObj.IsSet("return_code")) 35 { 36 data.SetValue("return_code", inputObj.GetValue("return_code")); 37 } 38 //返回信息 39 if (inputObj.IsSet("return_msg")) 40 { 41 data.SetValue("return_msg", inputObj.GetValue("return_msg")); 42 } 43 //業務結果 44 if (inputObj.IsSet("result_code")) 45 { 46 data.SetValue("result_code", inputObj.GetValue("result_code")); 47 } 48 //錯誤代碼 49 if (inputObj.IsSet("err_code")) 50 { 51 data.SetValue("err_code", inputObj.GetValue("err_code")); 52 } 53 //錯誤代碼描述 54 if (inputObj.IsSet("err_code_des")) 55 { 56 data.SetValue("err_code_des", inputObj.GetValue("err_code_des")); 57 } 58 //商戶訂單號 59 if (inputObj.IsSet("out_trade_no")) 60 { 61 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no")); 62 } 63 //設備號 64 if (inputObj.IsSet("device_info")) 65 { 66 data.SetValue("device_info", inputObj.GetValue("device_info")); 67 } 68 69 try 70 { 71 Report(paymentId, data); 72 } 73 catch (WxPayException ex) 74 { 75 //不作任何處理 76 } 77 } 78 79 /** 80 * 81 * 測速上報接口實現 82 * @param WxPayData inputObj 提交給測速上報接口的參數 83 * @param int timeOut 測速上報接口超時時間 84 * @throws WxPayException 85 * @return 成功時返回測速上報接口返回的結果,其餘拋異常 86 */ 87 public static WxPayData Report(int paymentId, WxPayData inputObj, int timeOut = 1) 88 { 89 JsApiConfig jsApiConfig = new JsApiConfig(paymentId); 90 string url = "https://api.mch.weixin.qq.com/payitil/report"; 91 //檢測必填參數 92 if (!inputObj.IsSet("interface_url")) 93 { 94 throw new WxPayException("接口URL,缺乏必填參數interface_url!"); 95 } 96 if (!inputObj.IsSet("return_code")) 97 { 98 throw new WxPayException("返回狀態碼,缺乏必填參數return_code!"); 99 } 100 if (!inputObj.IsSet("result_code")) 101 { 102 throw new WxPayException("業務結果,缺乏必填參數result_code!"); 103 } 104 if (!inputObj.IsSet("user_ip")) 105 { 106 throw new WxPayException("訪問接口IP,缺乏必填參數user_ip!"); 107 } 108 if (!inputObj.IsSet("execute_time_")) 109 { 110 throw new WxPayException("接口耗時,缺乏必填參數execute_time_!"); 111 } 112 113 inputObj.SetValue("appid", jsApiConfig.AppId);//公衆帳號ID 114 inputObj.SetValue("mch_id", jsApiConfig.Partner);//商戶號 115 inputObj.SetValue("user_ip", DTRequest.GetIP());//終端ip 116 inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商戶上報時間 117 inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串 118 inputObj.SetValue("sign", inputObj.MakeSign(jsApiConfig.Key));//簽名 119 string xml = inputObj.ToXml(); 120 121 string response = HttpService.Post(xml, url, false, timeOut); 122 123 WxPayData result = new WxPayData(); 124 result.FromXml(response, jsApiConfig.Key); 125 return result; 126 } 127 128 /** 129 * 生成時間戳,標準北京時間,時區爲東八區,自1970年1月1日 0點0分0秒以來的秒數 130 * @return 時間戳 131 */ 132 public static string GenerateTimeStamp() 133 { 134 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 135 return Convert.ToInt64(ts.TotalSeconds).ToString(); 136 } 137 138 /** 139 * 生成隨機串,隨機串包含字母或數字 140 * @return 隨機串 141 */ 142 public static string GenerateNonceStr() 143 { 144 return Guid.NewGuid().ToString().Replace("-", ""); 145 } 146 147 /// <summary> 148 /// 接收從微信支付後臺發送過來的數據暫不驗證簽名 149 /// </summary> 150 /// <returns>微信支付後臺返回的數據</returns> 151 public static WxPayData GetNotifyData() 152 { 153 //接收從微信後臺POST過來的數據 154 System.IO.Stream s = HttpContext.Current.Request.InputStream; 155 int count = 0; 156 byte[] buffer = new byte[1024]; 157 StringBuilder builder = new StringBuilder(); 158 while ((count = s.Read(buffer, 0, 1024)) > 0) 159 { 160 builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 161 } 162 s.Flush(); 163 s.Close(); 164 s.Dispose(); 165 166 //轉換數據格式並驗證簽名 167 WxPayData data = new WxPayData(); 168 try 169 { 170 data.FromXml(builder.ToString()); 171 } 172 catch (WxPayException ex) 173 { 174 //如有錯誤,則當即返回結果給微信支付後臺 175 WxPayData res = new WxPayData(); 176 res.SetValue("return_code", "FAIL"); 177 res.SetValue("return_msg", ex.Message); 178 HttpContext.Current.Response.Write(res.ToXml()); 179 HttpContext.Current.Response.End(); 180 } 181 182 return data; 183 } 184 185 /** 186 * 187 * 查詢訂單 188 * @param WxPayData inputObj 提交給查詢訂單API的參數 189 * @param int timeOut 超時時間 190 * @throws WxPayException 191 * @return 成功時返回訂單查詢結果,其餘拋異常 192 */ 193 public static WxPayData OrderQuery(int paymentId, WxPayData inputObj, int timeOut = 6) 194 { 195 string sendUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; 196 //檢測必填參數 197 if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id")) 198 { 199 throw new WxPayException("訂單查詢接口中,out_trade_no、transaction_id至少填一個!"); 200 } 201 JsApiConfig jsApiConfig = new JsApiConfig(paymentId); 202 inputObj.SetValue("appid", jsApiConfig.AppId);//公衆帳號ID 203 inputObj.SetValue("mch_id", jsApiConfig.Partner);//商戶號 204 inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串 205 inputObj.SetValue("sign", inputObj.MakeSign(jsApiConfig.Key));//簽名 206 string xml = inputObj.ToXml(); 207 var startTime = DateTime.Now; //開始時間 208 string response = HttpService.Post(xml, sendUrl, false, timeOut);//調用HTTP通訊接口提交數據 209 var endTime = DateTime.Now; //結束時間 210 int timeCost = (int)((endTime - startTime).TotalMilliseconds); //計算所用時間 211 //將xml格式的數據轉化爲對象以返回 212 WxPayData result = new WxPayData(); 213 result.FromXml(response, jsApiConfig.Key); 214 ReportCostTime(paymentId, sendUrl, timeCost, result);//測速上報 215 return result; 216 } 217 218 } 219 }
第二部分 微信H5支付安全
微信H5支付是微信官方2017年上半年剛剛對外開放的支付模式,它主要應用於在手機網站在移動瀏覽器(非微信環境)調用微信支付的場景。服務器
注意:微信H5支付須要在微信支付商戶平臺單獨申請開通,不然沒法使用。
微信H5支付的流程比較簡單,就是拼接請求的xml數據,進行統一下單,獲取到支付的mweb_url,而後請求這個url網址就行。
H5Config.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace Gwbnsh.API.Payment.wxpay 7 { 8 /// <summary> 9 /// 移動端非微信瀏覽器支付 10 /// </summary> 11 public class H5Config 12 { 13 #region 字段 14 private string partner = string.Empty; 15 private string key = string.Empty; 16 private string appid = string.Empty; 17 private string notify_url = string.Empty; 18 #endregion 19 20 public H5Config(int site_payment_id) 21 { 22 Model.site_payment model = new BLL.site_payment().GetModel(site_payment_id); //站點支付方式 23 if (model != null) 24 { 25 Model.payment payModel = new BLL.payment().GetModel(model.payment_id); //支付平臺 26 Model.sites siteModel = new BLL.sites().GetModel(model.site_id); //站點配置 27 Model.sysconfig sysConfig = new BLL.sysconfig().loadConfig(); //系統配置 28 29 partner = model.key1; //商戶號(必須配置) 30 key = model.key2; //商戶支付密鑰,參考開戶郵件設置(必須配置) 31 appid = model.key3; //綁定支付的APPID(必須配置) 32 notify_url = ""; 33 } 34 } 35 36 #region 屬性 37 /// <summary> 38 /// 商戶號(必須配置) 39 /// </summary> 40 public string Partner 41 { 42 get { return partner; } 43 set { partner = value; } 44 } 45 46 /// <summary> 47 /// 獲取或設交易安全校驗碼 48 /// </summary> 49 public string Key 50 { 51 get { return key; } 52 set { key = value; } 53 } 54 55 /// <summary> 56 /// 綁定支付的APPID(必須配置) 57 /// </summary> 58 public string AppId 59 { 60 get { return appid; } 61 set { appid = value; } 62 } 63 64 /// <summary> 65 /// 獲取服務器異步通知頁面路徑 66 /// </summary> 67 public string Notify_url 68 { 69 get { return notify_url; } 70 } 71 72 #endregion 73 } 74 }
H5Pay.cs
1 using Gwbnsh.Common; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Text; 6 using System.Web; 7 8 namespace Gwbnsh.API.Payment.wxpay 9 { 10 public class H5Pay 11 { 12 /** 13 * 14 * 測速上報 15 * @param string interface_url 接口URL 16 * @param int timeCost 接口耗時 17 * @param WxPayData inputObj參數數組 18 */ 19 public static void ReportCostTime(int paymentId, string interface_url, int timeCost, WxPayData inputObj) 20 { 21 //若是僅失敗上報 22 if (inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" && 23 inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS") 24 { 25 return; 26 } 27 28 //上報邏輯 29 WxPayData data = new WxPayData(); 30 data.SetValue("interface_url", interface_url); 31 data.SetValue("execute_time_", timeCost); 32 //返回狀態碼 33 if (inputObj.IsSet("return_code")) 34 { 35 data.SetValue("return_code", inputObj.GetValue("return_code")); 36 } 37 //返回信息 38 if (inputObj.IsSet("return_msg")) 39 { 40 data.SetValue("return_msg", inputObj.GetValue("return_msg")); 41 } 42 //業務結果 43 if (inputObj.IsSet("result_code")) 44 { 45 data.SetValue("result_code", inputObj.GetValue("result_code")); 46 } 47 //錯誤代碼 48 if (inputObj.IsSet("err_code")) 49 { 50 data.SetValue("err_code", inputObj.GetValue("err_code")); 51 } 52 //錯誤代碼描述 53 if (inputObj.IsSet("err_code_des")) 54 { 55 data.SetValue("err_code_des", inputObj.GetValue("err_code_des")); 56 } 57 //商戶訂單號 58 if (inputObj.IsSet("out_trade_no")) 59 { 60 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no")); 61 } 62 //設備號 63 if (inputObj.IsSet("device_info")) 64 { 65 data.SetValue("device_info", inputObj.GetValue("device_info")); 66 } 67 68 try 69 { 70 Report(paymentId, data); 71 } 72 catch (WxPayException ex) 73 { 74 //不作任何處理 75 } 76 } 77 78 /** 79 * 80 * 測速上報接口實現 81 * @param WxPayData inputObj 提交給測速上報接口的參數 82 * @param int timeOut 測速上報接口超時時間 83 * @throws WxPayException 84 * @return 成功時返回測速上報接口返回的結果,其餘拋異常 85 */ 86 public static WxPayData Report(int paymentId, WxPayData inputObj, int timeOut = 1) 87 { 88 H5Config h5Config = new H5Config(paymentId); 89 string url = "https://api.mch.weixin.qq.com/payitil/report"; 90 //檢測必填參數 91 if (!inputObj.IsSet("interface_url")) 92 { 93 throw new WxPayException("接口URL,缺乏必填參數interface_url!"); 94 } 95 if (!inputObj.IsSet("return_code")) 96 { 97 throw new WxPayException("返回狀態碼,缺乏必填參數return_code!"); 98 } 99 if (!inputObj.IsSet("result_code")) 100 { 101 throw new WxPayException("業務結果,缺乏必填參數result_code!"); 102 } 103 if (!inputObj.IsSet("user_ip")) 104 { 105 throw new WxPayException("訪問接口IP,缺乏必填參數user_ip!"); 106 } 107 if (!inputObj.IsSet("execute_time_")) 108 { 109 throw new WxPayException("接口耗時,缺乏必填參數execute_time_!"); 110 } 111 112 inputObj.SetValue("appid", h5Config.AppId);//公衆帳號ID 113 inputObj.SetValue("mch_id", h5Config.Partner);//商戶號 114 inputObj.SetValue("user_ip", DTRequest.GetIP());//終端ip 115 inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商戶上報時間 116 inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串 117 inputObj.SetValue("sign", inputObj.MakeSign(h5Config.Key));//簽名 118 string xml = inputObj.ToXml(); 119 120 string response = HttpService.Post(xml, url, false, timeOut); 121 122 WxPayData result = new WxPayData(); 123 result.FromXml(response, h5Config.Key); 124 return result; 125 } 126 127 /** 128 * 生成時間戳,標準北京時間,時區爲東八區,自1970年1月1日 0點0分0秒以來的秒數 129 * @return 時間戳 130 */ 131 public static string GenerateTimeStamp() 132 { 133 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 134 return Convert.ToInt64(ts.TotalSeconds).ToString(); 135 } 136 137 /** 138 * 生成隨機串,隨機串包含字母或數字 139 * @return 隨機串 140 */ 141 public static string GenerateNonceStr() 142 { 143 return Guid.NewGuid().ToString().Replace("-", ""); 144 } 145 /// <summary> 146 /// 接收從微信支付後臺發送過來的數據未驗證簽名 147 /// </summary> 148 /// <returns>微信支付後臺返回的數據</returns> 149 public static WxPayData GetNotifyData() 150 { 151 //接收從微信後臺POST過來的數據 152 System.IO.Stream s = HttpContext.Current.Request.InputStream; 153 int count = 0; 154 byte[] buffer = new byte[1024]; 155 StringBuilder builder = new StringBuilder(); 156 while ((count = s.Read(buffer, 0, 1024)) > 0) 157 { 158 builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 159 } 160 s.Flush(); 161 s.Close(); 162 s.Dispose(); 163 164 //轉換數據格式暫不驗證簽名 165 WxPayData data = new WxPayData(); 166 try 167 { 168 data.FromXml(builder.ToString()); 169 } 170 catch (WxPayException ex) 171 { 172 //若簽名錯誤,則當即返回結果給微信支付後臺 173 WxPayData res = new WxPayData(); 174 res.SetValue("return_code", "FAIL"); 175 res.SetValue("return_msg", ex.Message); 176 HttpContext.Current.Response.Write(res.ToXml()); 177 HttpContext.Current.Response.End(); 178 } 179 180 return data; 181 } 182 183 /** 184 * 185 * 查詢訂單 186 * @param WxPayData inputObj 提交給查詢訂單API的參數 187 * @param int timeOut 超時時間 188 * @throws WxPayException 189 * @return 成功時返回訂單查詢結果,其餘拋異常 190 */ 191 public static WxPayData OrderQuery(int paymentId, WxPayData inputObj, int timeOut = 6) 192 { 193 string sendUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; 194 //檢測必填參數 195 if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id")) 196 { 197 throw new WxPayException("訂單查詢接口中,out_trade_no、transaction_id至少填一個!"); 198 } 199 H5Config h5Config = new H5Config(paymentId); 200 inputObj.SetValue("appid", h5Config.AppId);//公衆帳號ID 201 inputObj.SetValue("mch_id", h5Config.Partner);//商戶號 202 inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串 203 inputObj.SetValue("sign", inputObj.MakeSign(h5Config.Key));//簽名 204 string xml = inputObj.ToXml(); 205 var startTime = DateTime.Now; //開始時間 206 string response = HttpService.Post(xml, sendUrl, false, timeOut);//調用HTTP通訊接口提交數據 207 var endTime = DateTime.Now; //結束時間 208 int timeCost = (int)((endTime - startTime).TotalMilliseconds); //計算所用時間 209 //將xml格式的數據轉化爲對象以返回 210 WxPayData result = new WxPayData(); 211 result.FromXml(response, h5Config.Key); 212 ReportCostTime(paymentId, sendUrl, timeCost, result);//測速上報 213 return result; 214 } 215 216 } 217 }
第三部分 微信掃碼支付
微信掃碼支付通常應用的場景是PC端電腦支付。微信掃碼支付可分爲兩種模式,根據支付場景選擇相應模式。通常狀況下的PC端掃碼支付選擇的是模式二,須要注意的是模式二無回調函數。
【模式一】商戶後臺系統根據微信支付規則連接生成二維碼,連接中帶固定參數productid(可定義爲產品標識或訂單號)。用戶掃碼後,微信支付系統將productid和用戶惟一標識(openid)回調商戶後臺系統(須要設置支付回調URL),商戶後臺系統根據productid生成支付交易,最後微信支付系統發起用戶支付流程。
【模式二】商戶後臺系統調用微信支付【統一下單API】生成預付交易,將接口返回的連接生成二維碼,用戶掃碼後輸入密碼完成支付交易。注意:該模式的預付單有效期爲2小時,過時後沒法支付。
微信掃碼支付最友好的解決方案就是支付完成以後經過JS設置監聽函數,經過該函數完成跳轉。可參考的代碼以下:
NativeConfig.cs
1 using System.Web; 2 using System.Text; 3 using System.IO; 4 using System.Net; 5 using System; 6 using System.Xml; 7 using System.Collections.Generic; 8 using Gwbnsh.Common; 9 10 namespace Gwbnsh.API.Payment.wxpay 11 { 12 public class NativeConfig 13 { 14 #region 字段 15 private string partner = string.Empty; 16 private string key = string.Empty; 17 private string appid = string.Empty; 18 private string notify_url = string.Empty; 19 #endregion 20 21 public NativeConfig(int site_payment_id) 22 { 23 Model.site_payment model = new BLL.site_payment().GetModel(site_payment_id); //站點支付方式 24 if (model != null) 25 { 26 Model.payment payModel = new BLL.payment().GetModel(model.payment_id); //支付平臺 27 Model.sites siteModel = new BLL.sites().GetModel(model.site_id); //站點配置 28 Model.sysconfig sysConfig = new BLL.sysconfig().loadConfig(); //系統配置 29 30 partner = model.key1; //商戶號(必須配置) 31 key = model.key2; //商戶支付密鑰,參考開戶郵件設置(必須配置) 32 appid = model.key3; //綁定支付的APPID(必須配置) 33 notify_url = ""; 34 } 35 } 36 37 #region 屬性 38 /// <summary> 39 /// 商戶號(必須配置) 40 /// </summary> 41 public string Partner 42 { 43 get { return partner; } 44 set { partner = value; } 45 } 46 47 /// <summary> 48 /// 獲取或設交易安全校驗碼 49 /// </summary> 50 public string Key 51 { 52 get { return key; } 53 set { key = value; } 54 } 55 56 /// <summary> 57 /// 綁定支付的APPID(必須配置) 58 /// </summary> 59 public string AppId 60 { 61 get { return appid; } 62 set { appid = value; } 63 } 64 65 /// <summary> 66 /// 獲取服務器異步通知頁面路徑 67 /// </summary> 68 public string Notify_url 69 { 70 get { return notify_url; } 71 } 72 73 #endregion 74 } 75 }
NativePay.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Web; 4 using System.Net; 5 using System.IO; 6 using System.Text; 7 using Gwbnsh.Common; 8 9 namespace Gwbnsh.API.Payment.wxpay 10 { 11 public class NativePay 12 { 13 /** 14 * 15 * 測速上報 16 * @param string interface_url 接口URL 17 * @param int timeCost 接口耗時 18 * @param WxPayData inputObj參數數組 19 */ 20 public static void ReportCostTime(int paymentId, string interface_url, int timeCost, WxPayData inputObj) 21 { 22 //若是僅失敗上報 23 if (inputObj.IsSet("return_code") && inputObj.GetValue("return_code").ToString() == "SUCCESS" && 24 inputObj.IsSet("result_code") && inputObj.GetValue("result_code").ToString() == "SUCCESS") 25 { 26 return; 27 } 28 29 //上報邏輯 30 WxPayData data = new WxPayData(); 31 data.SetValue("interface_url", interface_url); 32 data.SetValue("execute_time_", timeCost); 33 //返回狀態碼 34 if (inputObj.IsSet("return_code")) 35 { 36 data.SetValue("return_code", inputObj.GetValue("return_code")); 37 } 38 //返回信息 39 if (inputObj.IsSet("return_msg")) 40 { 41 data.SetValue("return_msg", inputObj.GetValue("return_msg")); 42 } 43 //業務結果 44 if (inputObj.IsSet("result_code")) 45 { 46 data.SetValue("result_code", inputObj.GetValue("result_code")); 47 } 48 //錯誤代碼 49 if (inputObj.IsSet("err_code")) 50 { 51 data.SetValue("err_code", inputObj.GetValue("err_code")); 52 } 53 //錯誤代碼描述 54 if (inputObj.IsSet("err_code_des")) 55 { 56 data.SetValue("err_code_des", inputObj.GetValue("err_code_des")); 57 } 58 //商戶訂單號 59 if (inputObj.IsSet("out_trade_no")) 60 { 61 data.SetValue("out_trade_no", inputObj.GetValue("out_trade_no")); 62 } 63 //設備號 64 if (inputObj.IsSet("device_info")) 65 { 66 data.SetValue("device_info", inputObj.GetValue("device_info")); 67 } 68 69 try 70 { 71 Report(paymentId, data); 72 } 73 catch (WxPayException ex) 74 { 75 //不作任何處理 76 } 77 } 78 79 /** 80 * 81 * 測速上報接口實現 82 * @param WxPayData inputObj 提交給測速上報接口的參數 83 * @param int timeOut 測速上報接口超時時間 84 * @throws WxPayException 85 * @return 成功時返回測速上報接口返回的結果,其餘拋異常 86 */ 87 public static WxPayData Report(int paymentId, WxPayData inputObj, int timeOut = 1) 88 { 89 NativeConfig nativeConfig = new NativeConfig(paymentId); 90 string url = "https://api.mch.weixin.qq.com/payitil/report"; 91 //檢測必填參數 92 if (!inputObj.IsSet("interface_url")) 93 { 94 throw new WxPayException("接口URL,缺乏必填參數interface_url!"); 95 } 96 if (!inputObj.IsSet("return_code")) 97 { 98 throw new WxPayException("返回狀態碼,缺乏必填參數return_code!"); 99 } 100 if (!inputObj.IsSet("result_code")) 101 { 102 throw new WxPayException("業務結果,缺乏必填參數result_code!"); 103 } 104 if (!inputObj.IsSet("user_ip")) 105 { 106 throw new WxPayException("訪問接口IP,缺乏必填參數user_ip!"); 107 } 108 if (!inputObj.IsSet("execute_time_")) 109 { 110 throw new WxPayException("接口耗時,缺乏必填參數execute_time_!"); 111 } 112 113 inputObj.SetValue("appid", nativeConfig.AppId);//公衆帳號ID 114 inputObj.SetValue("mch_id", nativeConfig.Partner);//商戶號 115 inputObj.SetValue("user_ip", DTRequest.GetIP());//終端ip 116 inputObj.SetValue("time", DateTime.Now.ToString("yyyyMMddHHmmss"));//商戶上報時間 117 inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串 118 inputObj.SetValue("sign", inputObj.MakeSign(nativeConfig.Key));//簽名 119 string xml = inputObj.ToXml(); 120 121 string response = HttpService.Post(xml, url, false, timeOut); 122 123 WxPayData result = new WxPayData(); 124 result.FromXml(response, nativeConfig.Key); 125 return result; 126 } 127 128 /** 129 * 生成時間戳,標準北京時間,時區爲東八區,自1970年1月1日 0點0分0秒以來的秒數 130 * @return 時間戳 131 */ 132 public static string GenerateTimeStamp() 133 { 134 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 135 return Convert.ToInt64(ts.TotalSeconds).ToString(); 136 } 137 138 /** 139 * 生成隨機串,隨機串包含字母或數字 140 * @return 隨機串 141 */ 142 public static string GenerateNonceStr() 143 { 144 return Guid.NewGuid().ToString().Replace("-", ""); 145 } 146 /// <summary> 147 /// 接收從微信支付後臺發送過來的數據未驗證簽名 148 /// </summary> 149 /// <returns>微信支付後臺返回的數據</returns> 150 public static WxPayData GetNotifyData() 151 { 152 //接收從微信後臺POST過來的數據 153 System.IO.Stream s = HttpContext.Current.Request.InputStream; 154 int count = 0; 155 byte[] buffer = new byte[1024]; 156 StringBuilder builder = new StringBuilder(); 157 while ((count = s.Read(buffer, 0, 1024)) > 0) 158 { 159 builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 160 } 161 s.Flush(); 162 s.Close(); 163 s.Dispose(); 164 165 //轉換數據格式暫不驗證簽名 166 WxPayData data = new WxPayData(); 167 try 168 { 169 data.FromXml(builder.ToString()); 170 } 171 catch (WxPayException ex) 172 { 173 //若簽名錯誤,則當即返回結果給微信支付後臺 174 WxPayData res = new WxPayData(); 175 res.SetValue("return_code", "FAIL"); 176 res.SetValue("return_msg", ex.Message); 177 HttpContext.Current.Response.Write(res.ToXml()); 178 HttpContext.Current.Response.End(); 179 } 180 181 return data; 182 } 183 184 /** 185 * 186 * 查詢訂單 187 * @param WxPayData inputObj 提交給查詢訂單API的參數 188 * @param int timeOut 超時時間 189 * @throws WxPayException 190 * @return 成功時返回訂單查詢結果,其餘拋異常 191 */ 192 public static WxPayData OrderQuery(int paymentId, WxPayData inputObj, int timeOut = 6) 193 { 194 string sendUrl = "https://api.mch.weixin.qq.com/pay/orderquery"; 195 //檢測必填參數 196 if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id")) 197 { 198 throw new WxPayException("訂單查詢接口中,out_trade_no、transaction_id至少填一個!"); 199 } 200 NativeConfig nativeConfig = new NativeConfig(paymentId); 201 inputObj.SetValue("appid", nativeConfig.AppId);//公衆帳號ID 202 inputObj.SetValue("mch_id", nativeConfig.Partner);//商戶號 203 inputObj.SetValue("nonce_str", GenerateNonceStr());//隨機字符串 204 inputObj.SetValue("sign", inputObj.MakeSign(nativeConfig.Key));//簽名 205 string xml = inputObj.ToXml(); 206 var startTime = DateTime.Now; //開始時間 207 string response = HttpService.Post(xml, sendUrl, false, timeOut);//調用HTTP通訊接口提交數據 208 var endTime = DateTime.Now; //結束時間 209 int timeCost = (int)((endTime - startTime).TotalMilliseconds); //計算所用時間 210 //將xml格式的數據轉化爲對象以返回 211 WxPayData result = new WxPayData(); 212 result.FromXml(response, nativeConfig.Key); 213 ReportCostTime(paymentId, sendUrl, timeCost, result);//測速上報 214 return result; 215 } 216 217 } 218 }
如下爲掃碼支付、H5支付以及公衆號支付須要用到的共同類:
HttpService.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Web; 4 using System.Net; 5 using System.IO; 6 using System.Text; 7 using System.Net.Security; 8 using System.Security.Authentication; 9 using System.Security.Cryptography.X509Certificates; 10 11 namespace Gwbnsh.API.Payment.wxpay 12 { 13 /// <summary> 14 /// http鏈接基礎類,負責底層的http通訊 15 /// </summary> 16 public class HttpService 17 { 18 public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 19 { 20 //直接確認,不然打不開 21 return true; 22 } 23 24 public static string Post(string xml, string url, bool isUseCert, int timeout) 25 { 26 System.GC.Collect();//垃圾回收,回收沒有正常關閉的http鏈接 27 28 string result = "";//返回結果 29 30 HttpWebRequest request = null; 31 HttpWebResponse response = null; 32 Stream reqStream = null; 33 34 try 35 { 36 //設置最大鏈接數 37 ServicePointManager.DefaultConnectionLimit = 200; 38 //設置https驗證方式 39 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 40 { 41 ServicePointManager.ServerCertificateValidationCallback = 42 new RemoteCertificateValidationCallback(CheckValidationResult); 43 } 44 45 /*************************************************************** 46 * 下面設置HttpWebRequest的相關屬性 47 * ************************************************************/ 48 request = (HttpWebRequest)WebRequest.Create(url); 49 50 request.Method = "POST"; 51 request.Timeout = timeout * 1000; 52 53 //設置代理服務器 54 /*WebProxy proxy = new WebProxy(); //定義一個網關對象 55 proxy.Address = new Uri(WxPayConfig.PROXY_URL); //網關服務器端口:端口 56 request.Proxy = proxy;*/ 57 58 //設置POST的數據類型和長度 59 request.ContentType = "text/xml"; 60 byte[] data = System.Text.Encoding.UTF8.GetBytes(xml); 61 request.ContentLength = data.Length; 62 63 //是否使用證書 64 /*if (isUseCert) 65 { 66 string path = HttpContext.Current.Request.PhysicalApplicationPath; 67 X509Certificate2 cert = new X509Certificate2(path + WxPayConfig.SSLCERT_PATH, WxPayConfig.SSLCERT_PASSWORD); 68 request.ClientCertificates.Add(cert); 69 }*/ 70 71 //往服務器寫入數據 72 reqStream = request.GetRequestStream(); 73 reqStream.Write(data, 0, data.Length); 74 reqStream.Close(); 75 76 //獲取服務端返回 77 response = (HttpWebResponse)request.GetResponse(); 78 79 //獲取服務端返回數據 80 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 81 result = sr.ReadToEnd().Trim(); 82 sr.Close(); 83 } 84 catch (System.Threading.ThreadAbortException e) 85 { 86 System.Threading.Thread.ResetAbort(); 87 } 88 catch (WebException e) 89 { 90 throw new WxPayException(e.ToString()); 91 } 92 catch (Exception e) 93 { 94 throw new WxPayException(e.ToString()); 95 } 96 finally 97 { 98 //關閉鏈接和流 99 if (response != null) 100 { 101 response.Close(); 102 } 103 if (request != null) 104 { 105 request.Abort(); 106 } 107 } 108 return result; 109 } 110 111 /// <summary> 112 /// 處理http GET請求,返回數據 113 /// </summary> 114 /// <param name="url">請求的url地址</param> 115 /// <returns>http GET成功後返回的數據,失敗拋WebException異常</returns> 116 public static string Get(string url) 117 { 118 System.GC.Collect(); 119 string result = ""; 120 121 HttpWebRequest request = null; 122 HttpWebResponse response = null; 123 124 //請求url以獲取數據 125 try 126 { 127 //設置最大鏈接數 128 ServicePointManager.DefaultConnectionLimit = 200; 129 //設置https驗證方式 130 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 131 { 132 ServicePointManager.ServerCertificateValidationCallback = 133 new RemoteCertificateValidationCallback(CheckValidationResult); 134 } 135 136 /*************************************************************** 137 * 下面設置HttpWebRequest的相關屬性 138 * ************************************************************/ 139 request = (HttpWebRequest)WebRequest.Create(url); 140 141 request.Method = "GET"; 142 143 //設置代理 144 /*WebProxy proxy = new WebProxy(); 145 proxy.Address = new Uri(WxPayConfig.PROXY_URL); 146 request.Proxy = proxy;*/ 147 148 //獲取服務器返回 149 response = (HttpWebResponse)request.GetResponse(); 150 151 //獲取HTTP返回數據 152 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 153 result = sr.ReadToEnd().Trim(); 154 sr.Close(); 155 } 156 catch (System.Threading.ThreadAbortException e) 157 { 158 System.Threading.Thread.ResetAbort(); 159 } 160 catch (WebException e) 161 { 162 throw new WxPayException(e.ToString()); 163 } 164 catch (Exception e) 165 { 166 throw new WxPayException(e.ToString()); 167 } 168 finally 169 { 170 //關閉鏈接和流 171 if (response != null) 172 { 173 response.Close(); 174 } 175 if (request != null) 176 { 177 request.Abort(); 178 } 179 } 180 return result; 181 } 182 } 183 }
WxPayData.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Web; 4 using System.Xml; 5 using System.Security.Cryptography; 6 using System.Text; 7 using Gwbnsh.Common; 8 9 namespace Gwbnsh.API.Payment.wxpay 10 { 11 /// <summary> 12 /// 微信支付協議接口數據類,全部的API接口通訊都依賴這個數據結構, 13 /// 在調用接口以前先填充各個字段的值,而後進行接口通訊, 14 /// 這樣設計的好處是可擴展性強,用戶可隨意對協議進行更改而不用從新設計數據結構, 15 /// 還能夠隨意組合出不一樣的協議數據包,不用爲每一個協議設計一個數據包結構 16 /// </summary> 17 public class WxPayData 18 { 19 public WxPayData() 20 { 21 22 } 23 24 //採用排序的Dictionary的好處是方便對數據包進行簽名,不用再簽名以前再作一次排序 25 private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>(); 26 27 /** 28 * 設置某個字段的值 29 * @param key 字段名 30 * @param value 字段值 31 */ 32 public void SetValue(string key, object value) 33 { 34 m_values[key] = value; 35 } 36 37 /** 38 * 根據字段名獲取某個字段的值 39 * @param key 字段名 40 * @return key對應的字段值 41 */ 42 public object GetValue(string key) 43 { 44 object o = null; 45 m_values.TryGetValue(key, out o); 46 return o; 47 } 48 49 /** 50 * 判斷某個字段是否已設置 51 * @param key 字段名 52 * @return 若字段key已被設置,則返回true,不然返回false 53 */ 54 public bool IsSet(string key) 55 { 56 object o = null; 57 m_values.TryGetValue(key, out o); 58 if (null != o) 59 return true; 60 else 61 return false; 62 } 63 64 /** 65 * @將Dictionary轉成xml 66 * @return 經轉換獲得的xml串 67 * @throws WxPayException 68 **/ 69 public string ToXml() 70 { 71 //數據爲空時不能轉化爲xml格式 72 if (0 == m_values.Count) 73 { 74 throw new WxPayException("WxPayData數據爲空!"); 75 } 76 77 string xml = "<xml>"; 78 foreach (KeyValuePair<string, object> pair in m_values) 79 { 80 //字段值不能爲null,會影響後續流程 81 if (pair.Value == null) 82 { 83 throw new WxPayException("WxPayData內部含有值爲null的字段!"); 84 } 85 86 if (pair.Value.GetType() == typeof(int)) 87 { 88 xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">"; 89 } 90 else if (pair.Value.GetType() == typeof(string)) 91 { 92 xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">"; 93 } 94 else//除了string和int類型不能含有其餘數據類型 95 { 96 throw new WxPayException("WxPayData字段數據類型錯誤!"); 97 } 98 } 99 xml += "</xml>"; 100 return xml; 101 } 102 103 /** 104 * @接收從微信後臺POST過來的數據(未驗證簽名) 105 * @return 經轉換獲得的Dictionary 106 * @throws WxPayException 107 */ 108 public SortedDictionary<string, object> GetRequest() 109 { 110 //接收從微信後臺POST過來的數據 111 System.IO.Stream s = HttpContext.Current.Request.InputStream; 112 int count = 0; 113 byte[] buffer = new byte[1024]; 114 StringBuilder builder = new StringBuilder(); 115 while ((count = s.Read(buffer, 0, 1024)) > 0) 116 { 117 builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); 118 } 119 s.Flush(); 120 s.Close(); 121 s.Dispose(); 122 123 if (string.IsNullOrEmpty(builder.ToString())) 124 { 125 throw new WxPayException("將空的xml串轉換爲WxPayData不合法!"); 126 } 127 128 XmlDocument xmlDoc = new XmlDocument(); 129 xmlDoc.LoadXml(builder.ToString()); 130 XmlNode xmlNode = xmlDoc.FirstChild;//獲取到根節點<xml> 131 XmlNodeList nodes = xmlNode.ChildNodes; 132 foreach (XmlNode xn in nodes) 133 { 134 XmlElement xe = (XmlElement)xn; 135 m_values[xe.Name] = xe.InnerText;//獲取xml的鍵值對到WxPayData內部的數據中 136 } 137 138 return m_values; 139 } 140 141 /** 142 * @將xml轉爲WxPayData對象並返回對象內部的數據 143 * @param string 待轉換的xml串 144 * @return 經轉換獲得的Dictionary 145 * @throws WxPayException 146 */ 147 public SortedDictionary<string, object> FromXml(string xml, string key) 148 { 149 if (string.IsNullOrEmpty(xml)) 150 { 151 throw new WxPayException("將空的xml串轉換爲WxPayData不合法!"); 152 } 153 154 XmlDocument xmlDoc = new XmlDocument(); 155 xmlDoc.LoadXml(xml); 156 XmlNode xmlNode = xmlDoc.FirstChild;//獲取到根節點<xml> 157 XmlNodeList nodes = xmlNode.ChildNodes; 158 foreach (XmlNode xn in nodes) 159 { 160 XmlElement xe = (XmlElement)xn; 161 m_values[xe.Name] = xe.InnerText;//獲取xml的鍵值對到WxPayData內部的數據中 162 } 163 164 try 165 { 166 //2015-06-29 錯誤是沒有簽名 167 if (m_values["return_code"] != "SUCCESS") 168 { 169 return m_values; 170 } 171 CheckSign(key);//驗證簽名,不經過會拋異常 172 } 173 catch (Exception ex) 174 { 175 throw new WxPayException(ex.Message); 176 } 177 178 return m_values; 179 } 180 /** 181 * @將xml轉爲WxPayData對象並返回對象內部的數據(未驗證簽名) 182 * @param string 待轉換的xml串 183 * @return 經轉換獲得的Dictionary 184 * @throws WxPayException 185 */ 186 public SortedDictionary<string, object> FromXml(string xml) 187 { 188 if (string.IsNullOrEmpty(xml)) 189 { 190 throw new WxPayException("將空的xml串轉換爲WxPayData不合法!"); 191 } 192 193 XmlDocument xmlDoc = new XmlDocument(); 194 xmlDoc.LoadXml(xml); 195 XmlNode xmlNode = xmlDoc.FirstChild;//獲取到根節點<xml> 196 XmlNodeList nodes = xmlNode.ChildNodes; 197 foreach (XmlNode xn in nodes) 198 { 199 XmlElement xe = (XmlElement)xn; 200 m_values[xe.Name] = xe.InnerText;//獲取xml的鍵值對到WxPayData內部的數據中 201 } 202 203 try 204 { 205 //2015-06-29 錯誤是沒有簽名 206 if (m_values["return_code"] != "SUCCESS") 207 { 208 return m_values; 209 } 210 } 211 catch (Exception ex) 212 { 213 throw new WxPayException(ex.Message); 214 } 215 216 return m_values; 217 } 218 /** 219 * @Dictionary格式轉化成url參數格式 220 * @ return url格式串, 該串不包含sign字段值 221 */ 222 public string ToUrl() 223 { 224 string buff = ""; 225 foreach (KeyValuePair<string, object> pair in m_values) 226 { 227 if (pair.Value == null) 228 { 229 throw new WxPayException("WxPayData內部含有值爲null的字段!"); 230 } 231 232 if (pair.Key != "sign" && pair.Value.ToString() != "") 233 { 234 buff += pair.Key + "=" + pair.Value + "&"; 235 } 236 } 237 buff = buff.Trim('&'); 238 return buff; 239 } 240 241 242 /** 243 * @Dictionary格式化成Json 244 * @return json串數據 245 */ 246 public string ToJson() 247 { 248 string jsonStr = JsonHelper.ObjectToJSON(m_values); 249 return jsonStr; 250 } 251 252 /** 253 * @values格式化成能在Web頁面上顯示的結果(由於web頁面上不能直接輸出xml格式的字符串) 254 */ 255 public string ToPrintStr() 256 { 257 string str = ""; 258 foreach (KeyValuePair<string, object> pair in m_values) 259 { 260 if (pair.Value == null) 261 { 262 throw new WxPayException("WxPayData內部含有值爲null的字段!"); 263 } 264 265 str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString()); 266 } 267 return str; 268 } 269 270 /** 271 * @生成簽名,詳見簽名生成算法 272 * @return 簽名, sign字段不參加簽名 273 */ 274 public string MakeSign(string key) 275 { 276 //轉url格式 277 string str = ToUrl(); 278 //在string後加入API KEY 279 str += "&key=" + key; 280 //MD5加密 281 var md5 = MD5.Create(); 282 var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str)); 283 var sb = new StringBuilder(); 284 foreach (byte b in bs) 285 { 286 sb.Append(b.ToString("x2")); 287 } 288 //全部字符轉爲大寫 289 return sb.ToString().ToUpper(); 290 } 291 292 /** 293 * 294 * 檢測簽名是否正確 295 * 正確返回true,錯誤拋異常 296 */ 297 public bool CheckSign(string key) 298 { 299 //若是沒有設置簽名,則跳過檢測 300 if (!IsSet("sign")) 301 { 302 throw new WxPayException("WxPayData簽名存在但不合法!"); 303 } 304 //若是設置了簽名可是簽名爲空,則拋異常 305 else if (GetValue("sign") == null || GetValue("sign").ToString() == "") 306 { 307 throw new WxPayException("WxPayData簽名存在但不合法!"); 308 } 309 310 //獲取接收到的簽名 311 string return_sign = GetValue("sign").ToString(); 312 313 //在本地計算新的簽名 314 string cal_sign = MakeSign(key); 315 316 if (cal_sign == return_sign) 317 { 318 return true; 319 } 320 321 throw new WxPayException("WxPayData簽名驗證錯誤!"); 322 } 323 324 /** 325 * @獲取Dictionary 326 */ 327 public SortedDictionary<string, object> GetValues() 328 { 329 return m_values; 330 } 331 } 332 }
WxPayException.cs
1 using System; 2 using System.Collections.Generic; 3 using System.Web; 4 5 namespace Gwbnsh.API.Payment.wxpay 6 { 7 public class WxPayException : Exception 8 { 9 public WxPayException(string msg) : base(msg) 10 { 11 12 } 13 } 14 }
最後,總結一下上述幾種支付方式須要注意的點。
1. 全部的支付參數都須要到微信支付商戶平臺(pay.weixin.qq.com)配置參數。
2. 微信公衆號支付、微信掃碼支付須要在微信公衆號裏面申請開通;H5支付須要在微信商戶平臺申請開通。
3. 僅有公衆號支付和掃碼支付一模式需配置支付域名,H5無需配置域名,可是使用的網站域名和申請時填寫的要一致。
4. 全部使用JS API方式發起支付請求的連接地址,都必須在當前頁面所配置的支付受權目錄之下。
5. 當公衆平臺接到掃碼支付請求時,會回調當前頁面所配置的支付回調連接傳遞訂單信息。
如下爲源碼,包含aspx頁面文件和詳細使用說明:下載