微信支付APP支付.net

之前公司说要为APP开发一个微信支付,由于没写过支付类的服务端支持,在网上找过不少代码,也走了不少弯路,网上关于在.net微信支付这一块大多比较坑,故整理出来给大家分享!废话不多说,上代码!

看到支付流程,前端是APP的话我们后端只需要处理 生成商户订单、调用微信统一下单API处理后返回参数给前端APP、最后处理支付结果。

发送请求的类如下:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Data;
  4 using System.IO;
  5 using System.Linq;
  6 using System.Net;
  7 using System.Net.Security;
  8 using System.Reflection;
  9 using System.Security.Cryptography.X509Certificates;
 10 using System.Text;
 11 using System.Web;
 12 using System.Xml.Serialization;
 13 
 14 namespace Controllers
 15 {
 16     public class WeiXin_PayMent
 17     {
 18         public static PayType _payConfig = GetPay();//初始化支付参数
 19 
 20         /// <summary>
 21         /// 获取支付参数
 22         /// </summary>
 23         /// <returns></returns>
 24         private static PayType GetPay()
 25         {
 26             PayType data = new PayType();
 27             try
 28             {
 29                 var result = LJL_Cloud.BLL.BLLPay.GetInstance().getPay(2);//访问数据库获取支付参数配置 你们写自己获取参数的方法
 30                 if (result != null && result.Rows.Count > 0)
 31                 {
 32                     var row = result.Rows[0];
 33                     data.PayAccount = row["PayAccount"].ToString();
 34                     data.CooperationID = row["CooperationID"].ToString();
 35                     data.PayUrl = row["PayUrl"].ToString();
 36                     data.NotifyUrl = row["NotifyUrl"].ToString();
 37                 }
 38             }
 39             catch (Exception)
 40             {
 41                 data.PayAccount = "";//公众帐号ID
 42                 data.CooperationID = "";//商户号
 43                 data.PayUrl = "https://api.mch.weixin.qq.com/pay/unifiedorder";//支付请求地址
 44                 data.NotifyUrl = "";//请求回调地址
 45             }
 46             return data;
 47         }
 48 
 49         private WeiXin_Config _config;
 50         public WeiXin_PayMent(WeiXin_Config config)
 51         {
 52             this._config = config;
 53 
 54             //
 55             // TODO: 在此处添加构造函数逻辑
 56             //
 57         }
 58 
 59         /// <summary>
 60         /// 生成随机字符串
 61         /// </summary>
 62         /// <returns></returns>
 63         public static string GetNonce()
 64         {
 65             string str = "1234567890qwertyuiopasdfghjklzxcvbnm";
 66             Random r = new Random();
 67             StringBuilder result = new StringBuilder();
 68             for (int i = 0; i < 32; i++)
 69             {
 70                 int m = r.Next(0, str.Length);
 71                 string s = str.Substring(m, 1);
 72                 result.Append(s);
 73             }
 74             return result.ToString();
 75         }
 76 
 77         /// <summary>
 78         /// 签名算法
 79         /// sign不参与签名
 80         /// </summary>
 81         /// <returns></returns>
 82         public static string Sign(object pars)
 83         {
 84             /*获取所有字段并添加到字典中并排序*/
 85             var dic = pars.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
 86                 .Where(x => x.GetValue(pars) != null && x.Name != "sign")//参数值为空或者为sign不参与签名
 87                 .ToDictionary(k => k.Name, v => v.GetValue(pars).ToString())//转换为dictionary
 88                .OrderBy(x => x.Key);//ASII排序
 89             StringBuilder sb = new StringBuilder();
 90             foreach (var item in dic)
 91                 sb.AppendFormat("{0}={1}&", item.Key, item.Value);
 92             /*拼接API密匙*/
 93             string strSignTemp = sb.ToString() + "key=" + _payConfig.VerifyCode;
 94             string sign = GetMd5Str32(strSignTemp);//MD5加密
 95             return sign;
 96         }
 97 
 98 
 99         /// <summary>
100         /// 32位MD5加密
101         /// 加密后为大写
102         /// </summary>
103         /// <param name="ConvertString"></param>
104         /// <returns></returns>
105         public static string GetMd5Str32(string str)
106         {
107             return System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(str, "MD5").ToUpper();
108         }
109 
110         /// <summary>
111         /// 处理参数
112         /// 返回微信接口需要的XML文档
113         /// </summary>
114         public string HandleParams()
115         {
116             this._config.nonce_str = GetNonce();//生成随机数
117             this._config.sign = Sign(this._config);//生成签名
118 
119             StringBuilder xml = new StringBuilder();
120             xml.Append("<xml>");
121             var list = this._config.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance)
122                 .Where(x => x.GetValue(this._config) != null);
123             foreach (var item in list)
124             {
125                 xml.AppendFormat("<{0}><![CDATA[{1}]]></{0}>", item.Name, item.GetValue(this._config).ToString());
126             }
127             xml.Append("</xml>");
128             return xml.ToString();
129         }
130 
131         public WeiXin_Result PostOrder()
132         {
133             var xmlPars = HandleParams();//经过处理的请求参数 XML
134             /*发送请求*/
135             HttpWebResponse res = CreatePostHttpResponse(_payConfig.PayUrl, xmlPars, null, null, Encoding.UTF8, null);
136             string result = string.Empty;
137             using (StreamReader sr = new StreamReader(res.GetResponseStream()))
138             {
139                 result = sr.ReadToEnd();
140             }
141             /*处理返回结果*/
142             WeiXin_Result entityResult = null;
143             try
144             {
145                 using (StringReader sr = new StringReader("<?xml version=\"1.0\"?>" + result))//必须加上XML头文件 不加会报错
146                 {
147                     XmlSerializer xmldes = new XmlSerializer(typeof(WeiXin_Result));
148                     entityResult = xmldes.Deserialize(sr) as WeiXin_Result;
149                 }
150             }
151             catch (InvalidOperationException)
152             {
153 
154             }
155             return entityResult;
156         }
157 
158         /// <summary>  
159         /// 创建POST方式的HTTP请求  
160         /// </summary>  
161         /// <param name="url">请求的URL</param>  
162         /// <param name="parameters">随同请求POST的参数名称及参数值字典</param>  
163         /// <param name="timeout">请求的超时时间</param>  
164         /// <param name="userAgent">请求的客户端浏览器信息,可以为空</param>  
165         /// <param name="requestEncoding">发送HTTP请求时所用的编码</param>  
166         /// <param name="cookies">随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空</param>  
167         /// <returns></returns>  
168         public static HttpWebResponse CreatePostHttpResponse(string url, string parameters, int? timeout, string userAgent, Encoding requestEncoding, CookieCollection cookies)
169         {
170             if (string.IsNullOrEmpty(url))
171             {
172                 throw new ArgumentNullException("url");
173             }
174             if (requestEncoding == null)
175             {
176                 throw new ArgumentNullException("requestEncoding");
177             }
178             HttpWebRequest request = null;
179             //如果是发送HTTPS请求  
180             if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase))
181             {
182                 ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult);
183                 request = WebRequest.Create(url) as HttpWebRequest;
184                 request.ProtocolVersion = HttpVersion.Version10;
185             }
186             else
187             {
188                 request = WebRequest.Create(url) as HttpWebRequest;
189             }
190             request.Method = "POST";
191             request.ContentType = "application/x-www-form-urlencoded";
192 
193             if (!string.IsNullOrEmpty(userAgent))
194             {
195                 request.UserAgent = userAgent;
196             }
197             else
198             {
199                 request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";
200             }
201 
202             if (timeout.HasValue)
203             {
204                 request.Timeout = timeout.Value;
205             }
206             if (cookies != null)
207             {
208                 request.CookieContainer = new CookieContainer();
209                 request.CookieContainer.Add(cookies);
210             }
211             //如果需要POST数据  
212             if (!string.IsNullOrEmpty(parameters))
213             {
214                 byte[] data = requestEncoding.GetBytes(parameters);
215                 using (Stream stream = request.GetRequestStream())
216                 {
217                     stream.Write(data, 0, data.Length);
218                 }
219             }
220             return request.GetResponse() as HttpWebResponse;
221         }
222 
223         private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
224         {
225             return true; //总是接受  
226         } 
227     }
228     /// <summary>
229     /// 请求下单接口参数实体
230     /// </summary>
231     public class WeiXin_Config
232     {
233         private LJL_Cloud.Model.PayType _payConfig = WeiXin_PayMent._payConfig;
234         public WeiXin_Config()
235         {
236             this.appid = _payConfig.PayAccount;//获取公众帐号ID
237             this.mch_id = _payConfig.CooperationID;//获取商户号
238             this.fee_type = "CNY";//货币类型
239             this.spbill_create_ip = "";//本机IP地址
240             this.notify_url = _payConfig.NotifyUrl;//支付成功后消息通知地址
241             this.trade_type = "APP";//交易类型
242         }
243         /// <summary>
244         /// 微信分配的公众账号ID(企业号corpid即为此appId)not null
245         /// </summary>
246         public string appid;
247         /// <summary>
248         /// 微信支付分配的商户号 not null
249         /// </summary>
250         public string mch_id;
251         /// <summary>
252         /// 终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB" null
253         /// </summary>
254         public string device_info;
255         /// <summary>
256         /// 随机字符串,不长于32位。推荐随机数生成算法 not null
257         /// </summary>
258         public string nonce_str;
259         /// <summary>
260         /// 签名,详见签名生成算法 not null
261         /// </summary>
262         public string sign;
263         /// <summary>
264         /// 商品或支付单简要描述 not null
265         /// </summary>
266         public string body;
267         /// <summary>
268         /// 商品名称明细列表 null
269         /// </summary>
270         public string detail;
271         /// <summary>
272         /// 附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据 null
273         /// </summary>
274         public string attach;
275         /// <summary>
276         /// 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号 not null
277         /// </summary>
278         public string out_trade_no;
279         /// <summary>
280         /// 符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型 null
281         /// </summary>
282         public string fee_type;
283         /// <summary>
284         /// 订单总金额,单位为分,详见支付金额 not null
285         /// </summary>
286         public int total_fee;
287         /// <summary>
288         /// APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。 not null
289         /// </summary>
290         public string spbill_create_ip;
291         /// <summary>
292         /// 订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010  null
293         /// </summary>
294         public string time_start;
295         /// <summary>
296         /// 订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010 null
297         /// </summary>
298         public string time_expire;
299         /// <summary>
300         /// 商品标记 null
301         /// </summary>
302         public string goods_tag;
303         /// <summary>
304         /// 通知地址 not null
305         /// </summary>
306         public string notify_url;
307         /// <summary>
308         /// 取值如下:JSAPI,NATIVE,APP,详细说明见参数规定 not null
309         /// </summary>
310         public string trade_type;
311         /// <summary>
312         /// trade_type=NATIVE,此参数必传 null
313         /// </summary>
314         public string product_id;
315         /// <summary>
316         /// no_credit--指定不能使用信用卡支付 null
317         /// </summary>
318         public string limit_pay;
319         /// <summary>
320         /// trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号userid转openid接口】进行转换 null
321         /// </summary>
322         public string openid;
323     }
324 
325     /// <summary>
326     /// 微信统一下单接口返回参数实体
327     /// </summary>
328     [XmlRoot("xml")]
329     public class WeiXin_Result
330     {
331         /// <summary>
332         /// 通信标识 SUCCESS
333         /// </summary>
334         [XmlElement("return_code")]
335         public string return_code { get; set; }
336 
337         /// <summary>
338         /// 返回信息,如非空,为错误原因
339         /// </summary>
340         [XmlElement("return_msg")]
341         public string return_msg { get; set; }
342 
343         /// <summary>
344         /// 调用接口提交的公众账号ID
345         /// </summary>
346         [XmlElement("appid")]
347         public string appid { get; set; }
348 
349         /// <summary>
350         /// 调用接口提交的商户号
351         /// </summary>
352         [XmlElement("mch_id")]
353         public string mch_id { get; set; }
354 
355         /// <summary>
356         /// 调用接口提交的终端设备号,
357         /// </summary>
358         [XmlElement("device_info")]
359         public string device_info { get; set; }
360 
361         /// <summary>
362         /// 微信返回的随机字符串
363         /// </summary>
364         [XmlElement("nonce_str")]
365         public string nonce_str { get; set; }
366 
367 
368         /// <summary>
369         /// 微信返回的签名,详见签名算法
370         /// </summary>
371         [XmlElement("sign")]
372         public string sign { get; set; }
373 
374         /// <summary>
375         /// SUCCESS/FAIL
376         /// </summary>
377         [XmlElement("result_code")]
378         public string result_code { get; set; }
379 
380         /// <summary>
381         /// 错误代码
382         /// </summary>
383         [XmlElement("err_code")]
384         public string err_code { get; set; }
385 
386         /// <summary>
387         /// 错误代码描述
388         /// </summary>
389         [XmlElement("err_code_des")]
390         public string err_code_des { get; set; }
391 
392 
393         /// <summary>
394         /// 交易类型
395         /// </summary>
396         [XmlElement("trade_type")]
397         public string trade_type { get; set; }
398 
399         /// <summary>
400         /// 预支付交易会话标识
401         /// </summary>
402         [XmlElement("prepay_id")]
403         public string prepay_id { get; set; }
404 
405         /// <summary>
406         /// 二维码链接
407         /// </summary>
408         [XmlElement("code_url")]
409         public string code_url { get; set; }
410     }
411 
412     /// <summary>
413     /// 返回手机所需要的参数
414     /// </summary>
415     public class WeiXin_Result_APP
416     {
417         public string appid;//公众账号ID
418         public string partnerid;//商户号
419         public string prepayid;//预支付交易会话ID
420         public string package;//扩展字段
421         public string noncestr;//随机字符串
422         public string timestamp;//时间戳
423         public string sign;//签名
424     }
425 }

调用方式(调用微信预付单接口):

 

 1           
           //初始化参数
           WeiXin_Config config = new WeiXin_Config 2 { 3 body = body,//支付的请求主体 4 out_trade_no = orderID,//商户唯一订单号 5 total_fee = priceInt,//支付价格 单位分 6 }; 7 WeiXin_PayMent payMent = new WeiXin_PayMent(config); 8 var result = payMent.PostOrder();//调用微信统一下单接口 9 /*处理返回结果*/ 10 if (result.return_code == "SUCCESS")//通讯是否成功 11 { 12 /*通讯成功*/ 13 if (result.result_code == "SUCCESS")//交易是否成功 14 { 15 resultMsg.Result = OperateResult.Success; 16 resultMsg.ResultMsg = "下单成功"; 17 TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); 18 var timeStamp = Convert.ToInt64(ts.TotalSeconds).ToString(); 19 var data = new WeiXin_Result_APP 20 { 21 appid = result.appid, 22 partnerid = result.mch_id, 23 prepayid = result.prepay_id, 24 package = "Sign=WXPay", 25 noncestr = WeiXin_PayMent.GetNonce(), 26 timestamp = timeStamp, 27 }; 28 data.sign = WeiXin_PayMent.Sign(data); 29 resultMsg.PageData = data; 30 } 31 else 32 { 33 throw new OperateException(OperateResult.Fail, result.err_code_des/*错误描述*/); 34 } 35 } 36 else 37 { 38 /*通讯失败*/ 39 throw new OperateException(OperateResult.Fail, result.return_msg/*错误描述*/); 40 }

 

回调页面:

 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Web;
  5 using System.Web.UI;
  6 using System.Web.UI.WebControls;  8 using System.IO;
  9 using System.Xml.Serialization;
 10 
 11 namespace Controllers.Pay
 12 {
 13     public partial class WeiXin_Notify_URL : System.Web.UI.Page
 14     {
 15         protected void Page_Load(object sender, EventArgs e)
 16         {
 17             #region 获取参数
 18             System.IO.StreamReader reader = new System.IO.StreamReader(Request.InputStream);
 19             String xmlData = reader.ReadToEnd();
 20             Parmas entityResult;
 21             using (StringReader sr = new StringReader("<?xml version=\"1.0\"?>" + xmlData))//必须加上XML头文件 不加会报错
 22             {
 23                 XmlSerializer xmldes = new XmlSerializer(typeof(Parmas));
 24                 entityResult = xmldes.Deserialize(sr) as Parmas;//解析回调参数
 25             }
 26             #endregion
 27 
 28             #region 业务处理
 29             /*签名验证 防止数据泄漏导致出现“假通知”,造成资金损失*/
 30             var sign = WeiXin_PayMent.Sign(entityResult);
 31             if (entityResult.sign != sign)
 32             {
 33                 Response.End();
 34             }
 35             string result = "FAIL";
 36             if (entityResult.return_code == "SUCCESS")
 37             {
 38                 /*通知可能会多次发送给商户系统,必须处理重复的通知*/
 39                 //订单号P开头为商品 S开头为服务费
 40                 //更改商品订单状态
 41                 //支付成功 记录日志
 42             }
 43             else
 44             {
 45                 //支付失败 记录日志
 46             }
 47             #endregion
 48 
 49             #region 处理业务后返回同步返回结果给微信 注意格式为XML
 50             // return_code SUCCESS/FAIL SUCCESS表示商户接收通知成功并校验成功 必填参数
 51             // return_msg 返回信息,如非空,为错误原因:签名失败、参数格式校验错误
 52             string strResult = string.Format("<xml><return_code>{0}</return_code></xml>", result);
 53             Response.Write(strResult);
 54             Response.End();
 55             #endregion
 56 
 57         }
 58     }
 59     /// <summary>
 60     /// 微信回调地址返回参数
 61     /// </summary>
 62     [XmlRoot("xml")]
 63     public class Parmas
 64     {
 65         [XmlElement("return_code")]
 66         public string return_code ;//返回状态码
 67         [XmlElement("return_msg")]
 68         public string return_msg ;//返回信息
 69         [XmlElement("appid")]
 70         public string appid ;//公众账号ID
 71         [XmlElement("mch_id")]
 72         public string mch_id ;//商户号
 73         [XmlElement("device_info")]
 74         public string device_info ;//设备号
 75         [XmlElement("nonce_str")]
 76         public string nonce_str ; //随机字符串
 77         [XmlElement("sign")]
 78         public string sign ;//签名
 79         [XmlElement("result_code")]
 80         public string result_code ; //业务结果
 81         [XmlElement("err_code")]
 82         public string err_code ;//错误代码
 83         [XmlElement("err_code_des")]
 84         public string err_code_des ;//错误代码描述
 85         [XmlElement("openid")]
 86         public string openid ;//用户标识
 87         [XmlElement("is_subscribe")]
 88         public string is_subscribe ;//是否关注公众账号
 89         [XmlElement("trade_type")]
 90         public string trade_type ;//交易类型
 91         [XmlElement("bank_type")]
 92         public string bank_type ;//付款银行
 93         [XmlElement("total_fee")]
 94         public string total_fee ;//总金额
 95         [XmlElement("fee_type")]
 96         public string fee_type ;//货币种类
 97         [XmlElement("cash_fee")]
 98         public string cash_fee ;//现金支付金额
 99         [XmlElement("cash_fee_type")]
100         public string cash_fee_type ;//现金支付货币类型
101         [XmlElement("coupon_fee")]
102         public string coupon_fee ;//代金券或立减优惠金额
103         [XmlElement("coupon_count")]
104         public string coupon_count ;//代金券或立减优惠使用数量
105         [XmlElement("coupon_id_$n")]
106         public string coupon_id_n ;//代金券或立减优惠ID          标记
107         [XmlElement("coupon_fee_$n")]
108         public string coupon_fee_n ;//单个代金券或立减优惠支付金额
109         [XmlElement("transaction_id")]
110         public string transaction_id ;//微信支付订单号
111         [XmlElement("out_trade_no")]
112         public string out_trade_no ;//商户订单号
113         [XmlElement("attach")]
114         public string attach ;//商家数据包
115         [XmlElement("time_end")]
116         public string time_end ;//支付完成时间
117     }
118 }

 就到这里了,有什么不明白或指教请在下面留言或参考开发者文档 https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1 !谢谢!

posted @ 2016-06-12 15:24  占据你心的不是我。  阅读(1530)  评论(2)    收藏  举报