C#版微信公众号支付|微信H5支付|微信扫码支付问题汇总及解决方案总结

最近负责的一些项目开发,都用到了微信支付(微信公众号支付、微信H5支付、微信扫码支付)。在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存。代码在文章结尾处,有需要的同学可以下载看下。

先说注意的第一点,所有支付的第一步都是请求统一下单,统一下单,统一下单,请求URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder。

再说一个微信官方提供的一个很重要的工具,微信支付接口签名校验工具(网址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=20_1),此工具旨在帮助开发者检测调用【微信支付接口API】时发送的请求参数中生成的签名是否正确,提交相关信息后可获得签名校验结果。特别实用!特别实用!特别实用!签名只要正确了,一切就OK了!

第一部分 微信公众号支付

微信公众号支付需要配置的参数有:APPID(微信公众号开发者ID)、APPSECRET(微信公众号开发者密码)、MCHID(商户ID)、KEY(商户密钥)。

微信公众号支付应用的场景是在微信内部的H5环境中使用的支付方式。因为要通过网页授权获取用户的OpenId,所以必须要配置网页授权域名。同时要配置JS接口安全域名。

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 }
JsApiConfig.cs

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 }
JsApiPay.cs

第二部分 微信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 }
H5Config.cs

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 }
H5Pay.cs

第三部分 微信扫码支付

微信扫码支付一般应用的场景是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 }
NativeConfig.cs

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 }
NativePay.cs

以下为扫码支付、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 }
HttpService.cs

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 }
WxPayData.cs

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 }
WxPayException.cs

最后,总结一下上述几种支付方式需要注意的点。

1. 所有的支付参数都需要到微信支付商户平台(pay.weixin.qq.com)配置参数。

2. 微信公众号支付、微信扫码支付需要在微信公众号里面申请开通;H5支付需要在微信商户平台申请开通。

3. 仅有公众号支付和扫码支付一模式需配置支付域名,H5无需配置域名,但是使用的网站域名和申请时填写的要一致。

4. 所有使用JS API方式发起支付请求的链接地址,都必须在当前页面所配置的支付授权目录之下。

5. 当公众平台接到扫码支付请求时,会回调当前页面所配置的支付回调链接传递订单信息。

以下为源码,包含aspx页面文件和详细使用说明:下载

原文地址:https://www.cnblogs.com/soulmate/p/7874351.html