.Net后台实现微信APP支付
Posted 那么远这么近
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了.Net后台实现微信APP支付相关的知识,希望对你有一定的参考价值。
上一节分享了微信小程序支付的后台,这一节来分享一下微信APP支付的后台。微信APP支付和微信小程序差别不大,微信APP支付后台不需要微信登录凭证、后台下单时交易类型(trade_type)不再是"JSAPI",而是“APP”、商户后台传递给支付端的下单参数也有所不同。由于微信小程序支付和APP支付使用的APPID不同,索性直接写了两套支付,不再在代码里区分究竟该使用小程序支付的配置参数还是APP支付的参数。
官方是这样介绍的
具体实现:
在WePay文件夹下新建AppPay文件夹(微信支付的公共类在上一节),用于存放微信APP支付用到的类,新建AppPayConfig类
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 using System.Web.Configuration; 7 8 namespace App.Pay.WePay.XcxPay 9 { 10 public class XcxPayConfig : WePayConfig 11 { 12 //=======【基本信息设置】===================================== 13 /* 微信公众号信息配置 14 * APPID:绑定支付的APPID(必须配置) 15 * MCHID:商户号(必须配置) 16 * KEY:商户支付密钥,参考开户邮件设置(必须配置) 17 * APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置) 18 */ 19 /// 小程序支付 20 public static string APPID = WebConfigurationManager.AppSettings["XcxAppID"].ToString(); 21 public static string MCHID = WebConfigurationManager.AppSettings["XcxMchID"].ToString(); 22 public static string KEY = WebConfigurationManager.AppSettings["XcxKey"].ToString(); 23 public static string APPSECRET = WebConfigurationManager.AppSettings["XcxAppSecret"].ToString(); 24 25 //=======【证书路径设置】===================================== 26 /* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要) 27 */ 28 public const string SSLCERT_PATH = "cert/apiclient_cert.p12"; 29 public const string SSLCERT_PASSWORD = "1233410002"; 30 31 //=======【支付结果通知url】===================================== 32 /* 支付结果通知回调url,用于商户接收支付结果 33 */ 34 public static string NOTIFY_URL = WebConfigurationManager.AppSettings["XcxNotifyUrl"].ToString(); 35 36 // log记录 37 public static string LogPath = WebConfigurationManager.AppSettings["XcxLog"].ToString(); 38 } 39 }
新建AppPayHttpService类
1 using System; 2 using System.Collections.Generic; 3 using System.IO; 4 using System.Linq; 5 using System.Net; 6 using System.Net.Security; 7 using System.Security.Cryptography.X509Certificates; 8 using System.Text; 9 using System.Threading.Tasks; 10 using System.Web; 11 12 namespace App.Pay.WePay.AppPay 13 { 14 public class AppPayHttpService 15 { 16 private static Log Log = new Log(AppPayConfig.LogPath); 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 + AppPayConfig.SSLCERT_PATH, AppPayConfig.SSLCERT_PASSWORD); 68 request.ClientCertificates.Add(cert); 69 //Log.Debug("WxPayApi", "PostXml used cert"); 70 } 71 72 //往服务器写入数据 73 reqStream = request.GetRequestStream(); 74 reqStream.Write(data, 0, data.Length); 75 reqStream.Close(); 76 77 //获取服务端返回 78 response = (HttpWebResponse)request.GetResponse(); 79 80 //获取服务端返回数据 81 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 82 result = sr.ReadToEnd().Trim(); 83 sr.Close(); 84 } 85 catch (System.Threading.ThreadAbortException e) 86 { 87 Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting."); 88 Log.Error("Exception message: {0}", e.Message); 89 System.Threading.Thread.ResetAbort(); 90 } 91 catch (WebException e) 92 { 93 Log.Error("HttpService", e.ToString()); 94 if (e.Status == WebExceptionStatus.ProtocolError) 95 { 96 Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 97 Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 98 } 99 throw new WePayException(e.ToString()); 100 } 101 catch (Exception e) 102 { 103 Log.Error("HttpService", e.ToString()); 104 throw new WePayException(e.ToString()); 105 } 106 finally 107 { 108 //关闭连接和流 109 if (response != null) 110 { 111 response.Close(); 112 } 113 if (request != null) 114 { 115 request.Abort(); 116 } 117 } 118 return result; 119 } 120 121 /// <summary> 122 /// 处理http GET请求,返回数据 123 /// </summary> 124 /// <param name="url">请求的url地址</param> 125 /// <returns>http GET成功后返回的数据,失败抛WebException异常</returns> 126 public static string Get(string url) 127 { 128 System.GC.Collect(); 129 string result = ""; 130 131 HttpWebRequest request = null; 132 HttpWebResponse response = null; 133 134 //请求url以获取数据 135 try 136 { 137 //设置最大连接数 138 ServicePointManager.DefaultConnectionLimit = 200; 139 //设置https验证方式 140 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 141 { 142 ServicePointManager.ServerCertificateValidationCallback = 143 new RemoteCertificateValidationCallback(CheckValidationResult); 144 } 145 146 /*************************************************************** 147 * 下面设置HttpWebRequest的相关属性 148 * ************************************************************/ 149 request = (HttpWebRequest)WebRequest.Create(url); 150 151 request.Method = "GET"; 152 153 //设置代理 154 //WebProxy proxy = new WebProxy(); 155 //proxy.Address = new Uri(WxPayConfig.PROXY_URL); 156 //request.Proxy = proxy; 157 158 //获取服务器返回 159 response = (HttpWebResponse)request.GetResponse(); 160 161 //获取HTTP返回数据 162 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 163 result = sr.ReadToEnd().Trim(); 164 sr.Close(); 165 } 166 catch (System.Threading.ThreadAbortException e) 167 { 168 Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting."); 169 Log.Error("Exception message: {0}", e.Message); 170 System.Threading.Thread.ResetAbort(); 171 } 172 catch (WebException e) 173 { 174 Log.Error("HttpService", e.ToString()); 175 if (e.Status == WebExceptionStatus.ProtocolError) 176 { 177 Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 178 Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 179 } 180 throw new WePayException(e.ToString()); 181 } 182 catch (Exception e) 183 { 184 Log.Error("HttpService", e.ToString()); 185 throw new WePayException(e.ToString()); 186 } 187 finally 188 { 189 //关闭连接和流 190 if (response != null) 191 { 192 response.Close(); 193 } 194 if (request != null) 195 { 196 request.Abort(); 197 } 198 } 199 return result; 200 } 201 } 202 }
新建AppPayData类
1 using LitJson; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Security.Cryptography; 6 using System.Text; 7 using System.Threading.Tasks; 8 using System.Xml; 9 10 namespace App.Pay.WePay.AppPay 11 { 12 /// <summary> 13 /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构, 14 /// 在调用接口之前先填充各个字段的值,然后进行接口通信, 15 /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构, 16 /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构 17 /// </summary> 18 public class AppPayData 19 { 20 private Log Log = new Log(AppPayConfig.LogPath); 21 22 public AppPayData() 23 { 24 } 25 26 //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序 27 private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>(); 28 29 /** 30 * 设置某个字段的值 31 * @param key 字段名 32 * @param value 字段值 33 */ 34 public void SetValue(string key, object value) 35 { 36 m_values[key] = value; 37 } 38 39 /** 40 * 根据字段名获取某个字段的值 41 * @param key 字段名 42 * @return key对应的字段值 43 */ 44 public object GetValue(string key) 45 { 46 object o = null; 47 m_values.TryGetValue(key, out o); 48 return o; 49 } 50 51 /** 52 * 判断某个字段是否已设置 53 * @param key 字段名 54 * @return 若字段key已被设置,则返回true,否则返回false 55 */ 56 public bool IsSet(string key) 57 { 58 object o = null; 59 m_values.TryGetValue(key, out o); 60 if (null != o) 61 return true; 62 else 63 return false; 64 } 65 66 /** 67 * @将Dictionary转成xml 68 * @return 经转换得到的xml串 69 * @throws WePayException 70 **/ 71 public string ToXml() 72 { 73 //数据为空时不能转化为xml格式 74 if (0 == m_values.Count) 75 { 76 Log.Error(this.GetType().ToString(), "WxPayData数据为空!"); 77 throw new WePayException("WxPayData数据为空!"); 78 } 79 80 string xml = "<xml>"; 81 foreach (KeyValuePair<string, object> pair in m_values) 82 { 83 //字段值不能为null,会影响后续流程 84 if (pair.Value == null) 85 { 86 Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!"); 87 throw new WePayException("WxPayData内部含有值为null的字段!"); 88 } 89 90 if (pair.Value.GetType() == typeof(int)) 91 { 92 xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">"; 93 } 94 else if (pair.Value.GetType() == typeof(string)) 95 { 96 xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">"; 97 } 98 else//除了string和int类型不能含有其他数据类型 微信APP支付-Java后台实现