.Net后台实现微信APP支付

一年好景君须记,最是橙黄橘绿时。这篇文章主要讲述.Net后台实现微信APP支付相关的知识,希望能为你提供帮助。
上一节分享了微信小程序支付的后台,这一节来分享一下微信APP支付的后台。微信APP支付和微信小程序差别不大,微信APP支付后台不需要微信登录凭证、后台下单时交易类型(trade_type)不再是"JSAPI",而是“APP”、商户后台传递给支付端的下单参数也有所不同。由于微信小程序支付和APP支付使用的APPID不同,索性直接写了两套支付,不再在代码里区分究竟该使用小程序支付的配置参数还是APP支付的参数。
官方是这样介绍的

.Net后台实现微信APP支付

文章图片

具体实现:
在WePay文件夹下新建AppPay文件夹(微信支付的公共类在上一节),用于存放微信APP支付用到的类,新建AppPayConfig类
.Net后台实现微信APP支付

文章图片
.Net后台实现微信APP支付

文章图片
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 { 10public class XcxPayConfig : WePayConfig 11{ 12//=======【基本信息设置】===================================== 13/* 微信公众号信息配置 14* APPID:绑定支付的APPID(必须配置) 15* MCHID:商户号(必须配置) 16* KEY:商户支付密钥,参考开户邮件设置(必须配置) 17* APPSECRET:公众帐号secert(仅JSAPI支付的时候需要配置) 18*/ 19/// 小程序支付 20public static string APPID = WebConfigurationManager.AppSettings["XcxAppID"].ToString(); 21public static string MCHID = WebConfigurationManager.AppSettings["XcxMchID"].ToString(); 22public static string KEY = WebConfigurationManager.AppSettings["XcxKey"].ToString(); 23public static string APPSECRET = WebConfigurationManager.AppSettings["XcxAppSecret"].ToString(); 24 25//=======【证书路径设置】===================================== 26/* 证书路径,注意应该填写绝对路径(仅退款、撤销订单时需要) 27*/ 28public const string SSLCERT_PATH = "cert/apiclient_cert.p12"; 29public const string SSLCERT_PASSWORD = "1233410002"; 30 31//=======【支付结果通知url】===================================== 32/* 支付结果通知回调url,用于商户接收支付结果 33*/ 34public static string NOTIFY_URL = WebConfigurationManager.AppSettings["XcxNotifyUrl"].ToString(); 35 36// log记录 37public static string LogPath = WebConfigurationManager.AppSettings["XcxLog"].ToString(); 38} 39 }

View Code新建AppPayHttpService类
.Net后台实现微信APP支付

文章图片
.Net后台实现微信APP支付

文章图片
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 { 14public class AppPayHttpService 15{ 16private static Log Log = new Log(AppPayConfig.LogPath); 17 18public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) 19{ 20//直接确认,否则打不开 21return true; 22} 23 24public static string Post(string xml, string url, bool isUseCert, int timeout) 25{ 26System.GC.Collect(); //垃圾回收,回收没有正常关闭的http连接 27 28string result = ""; //返回结果 29 30HttpWebRequest request = null; 31HttpWebResponse response = null; 32Stream reqStream = null; 33 34try 35{ 36//设置最大连接数 37ServicePointManager.DefaultConnectionLimit = 200; 38//设置https验证方式 39if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 40{ 41ServicePointManager.ServerCertificateValidationCallback = 42new RemoteCertificateValidationCallback(CheckValidationResult); 43} 44 45/*************************************************************** 46* 下面设置HttpWebRequest的相关属性 47* ************************************************************/ 48request = (HttpWebRequest)WebRequest.Create(url); 49 50request.Method = "POST"; 51request.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的数据类型和长度 59request.ContentType = "text/xml"; 60byte[] data = https://www.songbingjia.com/android/System.Text.Encoding.UTF8.GetBytes(xml); 61request.ContentLength = data.Length; 62 63//是否使用证书 64if (isUseCert) 65{ 66string path = HttpContext.Current.Request.PhysicalApplicationPath; 67X509Certificate2 cert = new X509Certificate2(path + AppPayConfig.SSLCERT_PATH, AppPayConfig.SSLCERT_PASSWORD); 68request.ClientCertificates.Add(cert); 69//Log.Debug("WxPayApi", "PostXml used cert"); 70} 71 72//往服务器写入数据 73reqStream = request.GetRequestStream(); 74reqStream.Write(data, 0, data.Length); 75reqStream.Close(); 76 77//获取服务端返回 78response = (HttpWebResponse)request.GetResponse(); 79 80//获取服务端返回数据 81StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 82result = sr.ReadToEnd().Trim(); 83sr.Close(); 84} 85catch (System.Threading.ThreadAbortException e) 86{ 87Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting."); 88Log.Error("Exception message: {0}", e.Message); 89System.Threading.Thread.ResetAbort(); 90} 91catch (WebException e) 92{ 93Log.Error("HttpService", e.ToString()); 94if (e.Status == WebExceptionStatus.ProtocolError) 95{ 96Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 97Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 98} 99throw new WePayException(e.ToString()); 100} 101catch (Exception e) 102{ 103Log.Error("HttpService", e.ToString()); 104throw new WePayException(e.ToString()); 105} 106finally 107{ 108//关闭连接和流 109if (response != null) 110{ 111response.Close(); 112} 113if (request != null) 114{ 115request.Abort(); 116} 117} 118return result; 119} 120 121/// < summary> 122/// 处理http GET请求,返回数据 123/// < /summary> 124/// < param name="url"> 请求的url地址< /param> 125/// < returns> http GET成功后返回的数据,失败抛WebException异常< /returns> 126public static string Get(string url) 127{ 128System.GC.Collect(); 129string result = ""; 130 131HttpWebRequest request = null; 132HttpWebResponse response = null; 133 134//请求url以获取数据 135try 136{ 137//设置最大连接数 138ServicePointManager.DefaultConnectionLimit = 200; 139//设置https验证方式 140if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) 141{ 142ServicePointManager.ServerCertificateValidationCallback = 143new RemoteCertificateValidationCallback(CheckValidationResult); 144} 145 146/*************************************************************** 147* 下面设置HttpWebRequest的相关属性 148* ************************************************************/ 149request = (HttpWebRequest)WebRequest.Create(url); 150 151request.Method = "GET"; 152 153//设置代理 154//WebProxy proxy = new WebProxy(); 155//proxy.Address = new Uri(WxPayConfig.PROXY_URL); 156//request.Proxy = proxy; 157 158//获取服务器返回 159response = (HttpWebResponse)request.GetResponse(); 160 161//获取HTTP返回数据 162StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); 163result = sr.ReadToEnd().Trim(); 164sr.Close(); 165} 166catch (System.Threading.ThreadAbortException e) 167{ 168Log.Error("HttpService", "Thread - caught ThreadAbortException - resetting."); 169Log.Error("Exception message: {0}", e.Message); 170System.Threading.Thread.ResetAbort(); 171} 172catch (WebException e) 173{ 174Log.Error("HttpService", e.ToString()); 175if (e.Status == WebExceptionStatus.ProtocolError) 176{ 177Log.Error("HttpService", "StatusCode : " + ((HttpWebResponse)e.Response).StatusCode); 178Log.Error("HttpService", "StatusDescription : " + ((HttpWebResponse)e.Response).StatusDescription); 179} 180throw new WePayException(e.ToString()); 181} 182catch (Exception e) 183{ 184Log.Error("HttpService", e.ToString()); 185throw new WePayException(e.ToString()); 186} 187finally 188{ 189//关闭连接和流 190if (response != null) 191{ 192response.Close(); 193} 194if (request != null) 195{ 196request.Abort(); 197} 198} 199return result; 200} 201} 202 }

View Code【.Net后台实现微信APP支付】新建AppPayData类
.Net后台实现微信APP支付

文章图片
.Net后台实现微信APP支付

文章图片
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> 18public class AppPayData 19{ 20private Log Log = new Log(AppPayConfig.LogPath); 21 22public AppPayData() 23{ 24} 25 26//采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序 27private SortedDictionary< string, object> m_values = new SortedDictionary< string, object> (); 28 29/** 30* 设置某个字段的值 31* @param key 字段名 32* @param value 字段值 33*/ 34public void SetValue(string key, object value) 35{ 36m_values[key] = value; 37} 38 39/** 40* 根据字段名获取某个字段的值 41* @param key 字段名 42* @return key对应的字段值 43*/ 44public object GetValue(string key) 45{ 46object o = null; 47m_values.TryGetValue(key, out o); 48return o; 49} 50 51/** 52* 判断某个字段是否已设置 53* @param key 字段名 54* @return 若字段key已被设置,则返回true,否则返回false 55*/ 56public bool IsSet(string key) 57{ 58object o = null; 59m_values.TryGetValue(key, out o); 60if (null != o) 61return true; 62else 63return false; 64} 65 66/** 67* @将Dictionary转成xml 68* @return 经转换得到的xml串 69* @throws WePayException 70**/ 71public string ToXml() 72{ 73//数据为空时不能转化为xml格式 74if (0 == m_values.Count) 75{ 76Log.Error(this.GetType().ToString(), "WxPayData数据为空!"); 77throw new WePayException("WxPayData数据为空!"); 78} 79 80string xml = "< xml> "; 81foreach (KeyValuePair< string, object> pair in m_values) 82{ 83//字段值不能为null,会影响后续流程 84if (pair.Value =https://www.songbingjia.com/android/= null) 85{ 86Log.Error(this.GetType().ToString(),"WxPayData内部含有值为null的字段!"); 87throw new WePayException("WxPayData内部含有值为null的字段!"); 88} 89 90if (pair.Value.GetType() == typeof(int)) 91{ 92xml += "< " + pair.Key + "> " + pair.Value + "< /" + pair.Key + "> "; 93} 94else if (pair.Value.GetType() == typeof(string)) 95{ 96xml += "< " + pair.Key + "> " + "< ![CDATA[" + pair.Value + "]]> < /" + pair.Key + "> "; 97} 98else//除了string和int类型不能含有其他数据类型

    推荐阅读