using APT.Infrastructure.Core; using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Net.Security; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Threading.Tasks; using System.Xml.Linq; using APT.Infrastructure.Api; namespace APT.Utility { public class WeChatPayHelper { // private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); public static WeixinToken GetToken() { var appId = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]).ToString(); var secret = LibUtils.ToString(ConfigurationManager.WexinSettings["AppSecret"]).ToString(); var toenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + secret; var res = HttpMethods.Get(toenUrl); WeixinToken token = JsonConvert.DeserializeObject(res); if (token == null || !string.IsNullOrEmpty(token.errcode)) throw new Exception("获取token失败"); APT.Infrastructure.Api.AppContext.WxToken = token; return token; } public class WxMessageModel { public string touser { get; set; } public string template_id { get; set; } public dynamic data { get; set; } } public class WxMessageReturnModel { public int errcode { get; set; } public string errmsg { get; set; } } public static int SendWxMessage(string openid, string token, string tmpId, dynamic data) { var msgUrl = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=" + token; var sendObj = new WxMessageModel { template_id = tmpId, touser = openid, data = data, }; var sendStr = JsonConvert.SerializeObject(sendObj); var returnStr = HttpMethods.Post(msgUrl, sendStr); var retObj = JsonConvert.DeserializeObject(returnStr); return retObj.errcode; } public static PayRequesEntity Unifiedorder(string openid, int totalfee, string orderNo,string orderType, string macId, string wxPaykey) { //var PayUrl = AppSetting.Load().WechatPayStrings.FirstOrDefault().UnifiedorderURL; //获取统一下单参数 var param = GetUnifiedOrderParam(openid, orderNo, totalfee, orderType, macId, wxPaykey); //_logger.Info(() => "微信支付统一下单 发送参数为 " + param + ""); //统一下单后拿到的xml结果 var payResXML = HttpMethods.Post(LibUtils.ToString(ConfigurationManager.WexinSettings["UnifiedorderURL"]).ToString(), param); //_logger.Info(() => "微信支付统一下单 返回参数为 " + payResXML + ""); var payRes = XDocument.Parse(payResXML); var root = payRes.Element("xml"); var res = GetPayRequestParam(root, wxPaykey); //_logger.Info(() => "微信支付统一下单 参数实体为 " + JsonConvert.SerializeObject(res) + ""); return res; } /// /// 取消订单退款接口 /// /// /// /// /// /// public static bool Cancelorder(int totalfee, int refundfee, string orderNo, string code) { //var PayUrl = AppSetting.Load().WechatPayStrings.FirstOrDefault().UnifiedorderURL; //获取统一下单参数 var param = GetCancelOrderParam(orderNo, code, totalfee, refundfee); //_logger.Info(() => "微信支付统一下单 发送参数为 " + param + ""); //统一下单后拿到的xml结果 var payResXML = HttpMethods.PostByCert(param, LibUtils.ToString(ConfigurationManager.WexinSettings["CancelorderURL"]).ToString(), true, 5); //_logger.Info(() => "微信支付统一下单 返回参数为 " + payResXML + ""); var payRes = XDocument.Parse(payResXML); var root = payRes.Element("xml"); return GetCancelOrderParam(root); //_logger.Info(() => "微信支付统一下单 参数实体为 " + JsonConvert.SerializeObject(res) + ""); } /// /// 取消订单退款接口 /// /// /// /// /// /// public static bool CancelorderByTenant(int totalfee, int refundfee, string orderNo, string code, string orderType, string macId, string wxPaykey,string certpath,string certpassword) { //var PayUrl = AppSetting.Load().WechatPayStrings.FirstOrDefault().UnifiedorderURL; //获取统一下单参数 var param = GetCancelOrderParamByTenant(orderNo, code, totalfee, refundfee ,orderType, macId, wxPaykey); //_logger.Info(() => "微信支付统一下单 发送参数为 " + param + ""); //统一下单后拿到的xml结果 var payResXML = HttpMethods.PostByCertByTenant(param, LibUtils.ToString(ConfigurationManager.WexinSettings["CancelorderURL"]).ToString(), true, 5, certpath, certpassword); //_logger.Info(() => "微信支付统一下单 返回参数为 " + payResXML + ""); var payRes = XDocument.Parse(payResXML); var root = payRes.Element("xml"); return GetCancelOrderParam(root); //_logger.Info(() => "微信支付统一下单 参数实体为 " + JsonConvert.SerializeObject(res) + ""); } /// /// 微信转账到零钱接口 /// /// 单号 /// openId /// 用户名 /// 金额 /// 备注 /// public static bool PayforChange(string orderNo, string openid, string username, int fee, string desc) { //var PayUrl = AppSetting.Load().WechatPayStrings.FirstOrDefault().UnifiedorderURL; //获取统一下单参数 var param = GetPayforChangeParam(orderNo, openid, username, fee, desc); //_logger.Info(() => "微信支付统一下单 发送参数为 " + param + ""); //统一下单后拿到的xml结果 var payResXML = HttpMethods.PostByCert(param, LibUtils.ToString(ConfigurationManager.WexinSettings["PayForChangeUrl"]).ToString(), true, 5); //_logger.Info(() => "微信支付统一下单 返回参数为 " + payResXML + ""); var payRes = XDocument.Parse(payResXML); var root = payRes.Element("xml"); return GetCancelOrderParam(root); //_logger.Info(() => "微信支付统一下单 参数实体为 " + JsonConvert.SerializeObject(res) + ""); } /// /// 取统一下单的请求参数 /// /// /// private static string GetUnifiedOrderParam(string openid, string orderNo, int totalfee, string orderType, string mch_id, string wxPaykey) { string appid = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]); string secret = LibUtils.ToString(ConfigurationManager.WexinSettings["AppSecret"]); //string mch_id = LibUtils.ToString(ConfigurationManager.WexinSettings["Mch_ID"]); string ip = LibUtils.ToString(ConfigurationManager.WexinSettings["IP"]); string PayResulturl = LibUtils.ToString(ConfigurationManager.WexinSettings["WxPayResultUrl"]); string strcode = "充电-下单";////商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。 byte[] buffer = Encoding.UTF8.GetBytes(strcode); string body = Encoding.UTF8.GetString(buffer, 0, buffer.Length); System.Random Random = new System.Random(); var dic = new Dictionary { {"appid", appid}, {"mch_id", mch_id}, {"nonce_str", GetRandomString(20)/*Random.Next().ToString()*/}, {"body",body}, {"out_trade_no",orderNo+"_"+totalfee+"_" +orderType},//商户自己的订单号码 {"total_fee",totalfee.ToString()}, {"spbill_create_ip",ip},//服务器的IP地址 {"notify_url",PayResulturl},//异步通知的地址,不能带参数 {"trade_type","JSAPI" }, {"openid",openid} }; //加入签名 dic.Add("sign", GetSignStringByTenant(dic,wxPaykey)); var sb = new StringBuilder(); sb.Append(""); foreach (var d in dic) { sb.Append("<" + d.Key + ">" + d.Value + ""); } sb.Append(""); return sb.ToString(); } /// /// 取统一下单的请求参数 /// /// /// private static string GetCancelOrderParam(string orderNo, string code, int totalfee, int refundfee) { string appid = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]); //string secret = LibUtils.ToString(ConfigurationManager.WexinSettings["AppSecret"]); string mch_id = LibUtils.ToString(ConfigurationManager.WexinSettings["Mch_ID"]); string ip = LibUtils.ToString(ConfigurationManager.WexinSettings["IP"]); string WxPayCancelUrl = LibUtils.ToString(ConfigurationManager.WexinSettings["WxPayCancelUrl"]); string strcode = "充电-支付";////商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。 byte[] buffer = Encoding.UTF8.GetBytes(strcode); string body = Encoding.UTF8.GetString(buffer, 0, buffer.Length); System.Random Random = new System.Random(); var dic = new Dictionary { {"appid", appid}, {"mch_id", mch_id}, {"nonce_str", GetRandomString(20)/*Random.Next().ToString()*/}, {"out_trade_no",orderNo+"_"+totalfee},//商户自己的订单号码 {"out_refund_no",code+"@"+totalfee},//退款单号 {"total_fee",totalfee.ToString()}, {"refund_fee", refundfee.ToString()},//退款金额 { "notify_url",WxPayCancelUrl},//异步通知的地址,不能带参数 }; //加入签名 dic.Add("sign", GetSignString(dic)); var sb = new StringBuilder(); sb.Append(""); foreach (var d in dic) { sb.Append("<" + d.Key + ">" + d.Value + ""); } sb.Append(""); return sb.ToString(); } private static string GetCancelOrderParamByTenant(string orderNo, string code, int totalfee, int refundfee , string orderType, string mch_id, string wxPaykey) { string appid = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]); //string secret = LibUtils.ToString(ConfigurationManager.WexinSettings["AppSecret"]); //string mch_id = LibUtils.ToString(ConfigurationManager.WexinSettings["Mch_ID"]); string ip = LibUtils.ToString(ConfigurationManager.WexinSettings["IP"]); string WxPayCancelUrl = LibUtils.ToString(ConfigurationManager.WexinSettings["WxPayCancelUrl"]); string strcode = "充电-支付";////商品描述交易字段格式根据不同的应用场景按照以下格式:APP——需传入应用市场上的APP名字-实际商品名称,天天爱消除-游戏充值。 byte[] buffer = Encoding.UTF8.GetBytes(strcode); string body = Encoding.UTF8.GetString(buffer, 0, buffer.Length); System.Random Random = new System.Random(); var dic = new Dictionary { {"appid", appid}, {"mch_id", mch_id}, {"nonce_str", GetRandomString(20)/*Random.Next().ToString()*/}, {"out_trade_no",orderNo+"_"+totalfee+"_"+orderType},//商户自己的订单号码 {"out_refund_no",code+"@"+totalfee},//退款单号 {"total_fee",totalfee.ToString()}, {"refund_fee", refundfee.ToString()},//退款金额 { "notify_url",WxPayCancelUrl},//异步通知的地址,不能带参数 }; //加入签名 dic.Add("sign", GetSignStringByTenant(dic, wxPaykey)); var sb = new StringBuilder(); sb.Append(""); foreach (var d in dic) { sb.Append("<" + d.Key + ">" + d.Value + ""); } sb.Append(""); return sb.ToString(); } /// /// 取统一下单的请求参数 /// /// /// private static string GetPayforChangeParam(string orderNo, string openid, string username, int fee, string desc) { string appid = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]); //string secret = LibUtils.ToString(ConfigurationManager.AppSettings["AppSecret"]); string mch_id = LibUtils.ToString(ConfigurationManager.WexinSettings["Mch_ID"]); System.Random Random = new System.Random(); var dic = new Dictionary { {"mch_appid", appid}, {"mchid", mch_id}, {"nonce_str", GetRandomString(20)/*Random.Next().ToString()*/}, {"partner_trade_no",orderNo},//提现的订单号 {"openid",openid}, {"check_name","NO_CHECK"},//校验姓名:FORCE_CHECK,NO_CHECK:不校验 {"re_user_name",username},//用户姓名 {"amount", fee.ToString()},//退款金额 { "desc",desc},//异步通知的地址,不能带参数 }; //加入签名 dic.Add("sign", GetSignString(dic)); var sb = new StringBuilder(); sb.Append(""); foreach (var d in dic) { sb.Append("<" + d.Key + ">" + d.Value + ""); } sb.Append(""); return sb.ToString(); } /// /// 获取返回给小程序的支付参数 /// /// /// /// /// private static PayRequesEntity GetPayRequestParam(XElement root, string wxPaykey) { string appid = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]); //当return_code 和result_code都为SUCCESS时才有我们要的prepay_id if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS") { var res = new Dictionary { {"appId", appid}, {"timeStamp", Convert.ToInt64((DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}, {"nonceStr", GetRandomString(20)}, {"package", "prepay_id=" + root.Element("prepay_id").Value}, {"signType", "MD5"} }; //在服务器上签名 res.Add("paySign", GetSignStringByTenant(res, wxPaykey)); var payEntity = new PayRequesEntity { package = res["package"], nonceStr = res["nonceStr"], paySign = res["paySign"], signType = res["signType"], timeStamp = res["timeStamp"] }; return payEntity; } else { throw new Exception("微信返回错误:" + root.Element("err_code_des").Value); } return new PayRequesEntity(); } /// /// 取消订单退款接口 /// /// /// private static bool GetCancelOrderParam(XElement root) { string appid = LibUtils.ToString(ConfigurationManager.WexinSettings["AppId"]); //当return_code 和result_code都为SUCCESS时才有我们要的prepay_id if (root.Element("return_code").Value == "SUCCESS" && root.Element("result_code").Value == "SUCCESS") { return true; } else { throw new Exception("微信返回错误:" + root.ToString()); } } /// /// 从字符串里随机得到,规定个数的字符串. /// /// /// /// private static string GetRandomString(int CodeCount) { string allChar = "1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,G,H,i,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z"; string[] allCharArray = allChar.Split(','); string RandomCode = ""; int temp = -1; Random rand = new Random(); for (int i = 0; i < CodeCount; i++) { if (temp != -1) { rand = new Random(temp * i * ((int)DateTime.Now.Ticks)); } int t = rand.Next(allCharArray.Length - 1); while (temp == t) { t = rand.Next(allCharArray.Length - 1); } temp = t; RandomCode += allCharArray[t]; } return RandomCode; } private static string GetSignString(Dictionary dic) { string key = LibUtils.ToString(ConfigurationManager.WexinSettings["WxPayKey"]);//商户平台 API安全里面设置的KEY 32位长度 dic = dic.OrderBy(d => d.Key).ToDictionary(d => d.Key, d => d.Value);//排序 //连接字段 var sign = dic.Aggregate("", (current, d) => current + (d.Key + "=" + d.Value + "&")); sign += "key=" + key; System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); sign = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(sign))).Replace("-", null); return sign; } /// /// 运营商Key /// /// /// private static string GetSignStringByTenant(Dictionary dic,string key) { //string key = LibUtils.ToString(ConfigurationManager.WexinSettings["WxPayKey"]);//商户平台 API安全里面设置的KEY 32位长度 dic = dic.OrderBy(d => d.Key).ToDictionary(d => d.Key, d => d.Value);//排序 //连接字段 var sign = dic.Aggregate("", (current, d) => current + (d.Key + "=" + d.Value + "&")); sign += "key=" + key; System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create(); sign = BitConverter.ToString(md5.ComputeHash(Encoding.UTF8.GetBytes(sign))).Replace("-", null); return sign; } private static Object PostUnifiedOrder(string payUrl, string para) { string result = string.Empty; try { HttpClient client = new HttpClient(); HttpContent httpContent = new StringContent(para); httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); httpContent.Headers.ContentType.CharSet = "utf-8"; HttpResponseMessage hrm = client.PostAsync(payUrl, httpContent).Result; return hrm; } catch (Exception e) { result = e.Message; } return result; } } public class PayRequesEntity { /// /// 时间戳从1970年1月1日00:00:00至今的秒数,即当前的时间 /// public string timeStamp { get; set; } /// /// 随机字符串,长度为32个字符以下。 /// public string nonceStr { get; set; } /// /// 统一下单接口返回的 prepay_id 参数值 /// public string package { get; set; } /// /// 签名算法 /// public string signType { get; set; } /// /// 签名 /// public string paySign { get; set; } public dynamic order { get; set; } } /// /// Http方法 /// public class HttpMethods { /// /// 创建HttpClient /// /// public static HttpClient CreateHttpClient(string url, IDictionary cookies = null) { HttpClient httpclient; HttpClientHandler handler = new HttpClientHandler(); var uri = new Uri(url); if (cookies != null) { foreach (var key in cookies.Keys) { string one = key + "=" + cookies[key]; handler.CookieContainer.SetCookies(uri, one); } } //如果是发送HTTPS请求 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) { ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; httpclient = new HttpClient(handler); } else { httpclient = new HttpClient(handler); } return httpclient; } #region 同步请求 /// /// post 请求 /// /// 请求地址 /// 请求参数 /// public static string Post(string url, string jsonData) { HttpClient httpClient = CreateHttpClient(url); var postData = new StringContent(jsonData); postData.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); Task result = httpClient.PostAsync(url, postData).Result.Content.ReadAsStringAsync(); return result.Result; } public static string PostByCert(string xml, string url, bool isUseCert, int timeout) { System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接 string result = "";//返回结果 HttpWebRequest request = null; HttpWebResponse response = null; Stream reqStream = null; try { //设置最大连接数 ServicePointManager.DefaultConnectionLimit = 200; //设置https验证方式 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) { ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); } /*************************************************************** * 下面设置HttpWebRequest的相关属性 * ************************************************************/ request = (HttpWebRequest)WebRequest.Create(url); //request.UserAgent = USER_AGENT; request.Method = "POST"; request.Timeout = timeout * 1000; //设置POST的数据类型和长度 request.ContentType = "text/xml"; byte[] data = System.Text.Encoding.UTF8.GetBytes(xml); request.ContentLength = data.Length; //是否使用证书 if (isUseCert) { X509Certificate2 cert = new X509Certificate2(LibUtils.ToString(ConfigurationManager.WexinSettings["CertPath"]), LibUtils.ToString(ConfigurationManager.WexinSettings["CertPassword"])); request.ClientCertificates.Add(cert); } //往服务器写入数据 reqStream = request.GetRequestStream(); reqStream.Write(data, 0, data.Length); reqStream.Close(); //获取服务端返回 response = (HttpWebResponse)request.GetResponse(); //获取服务端返回数据 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); result = sr.ReadToEnd().Trim(); sr.Close(); } catch (System.Threading.ThreadAbortException e) { System.Threading.Thread.ResetAbort(); } catch (Exception e) { throw new Exception(e.Message); } finally { //关闭连接和流 if (response != null) { response.Close(); } if (request != null) { request.Abort(); } } return result; } public static string PostByCertByTenant(string xml, string url, bool isUseCert, int timeout, string certpath, string certpassword) { System.GC.Collect();//垃圾回收,回收没有正常关闭的http连接 string result = "";//返回结果 HttpWebRequest request = null; HttpWebResponse response = null; Stream reqStream = null; try { //设置最大连接数 ServicePointManager.DefaultConnectionLimit = 200; //设置https验证方式 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) { ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); } /*************************************************************** * 下面设置HttpWebRequest的相关属性 * ************************************************************/ request = (HttpWebRequest)WebRequest.Create(url); //request.UserAgent = USER_AGENT; request.Method = "POST"; request.Timeout = timeout * 1000; //设置POST的数据类型和长度 request.ContentType = "text/xml"; byte[] data = System.Text.Encoding.UTF8.GetBytes(xml); request.ContentLength = data.Length; //是否使用证书 if (isUseCert) { X509Certificate2 cert = new X509Certificate2(certpath,certpassword); request.ClientCertificates.Add(cert); } //往服务器写入数据 reqStream = request.GetRequestStream(); reqStream.Write(data, 0, data.Length); reqStream.Close(); //获取服务端返回 response = (HttpWebResponse)request.GetResponse(); //获取服务端返回数据 StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.UTF8); result = sr.ReadToEnd().Trim(); sr.Close(); } catch (System.Threading.ThreadAbortException e) { System.Threading.Thread.ResetAbort(); } catch (Exception e) { throw new Exception(e.Message); } finally { //关闭连接和流 if (response != null) { response.Close(); } if (request != null) { request.Abort(); } } return result; } private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { if (errors == SslPolicyErrors.None) return true; return false; } /// /// post 请求 /// /// 请求地址 /// 请求参数 /// public static string Post(string url, byte[] req) { HttpClient httpClient = CreateHttpClient(url); var postData = new ByteArrayContent(req); Task result = httpClient.PostAsync(url, postData).Result.Content.ReadAsStringAsync(); return result.Result; } /// /// post 请求 /// /// 请求地址 /// 请求参数 /// public static string Post(string url, Dictionary pairs) { HttpClient httpClient = CreateHttpClient(url); var postData = new FormUrlEncodedContent(pairs); var result = httpClient.PostAsync(url, postData).Result.Content.ReadAsStringAsync(); return result.Result; } /// /// get 请求 /// /// 请求地址 /// public static string Get(string url) { HttpClient httpClient = CreateHttpClient(url); Task result = httpClient.GetAsync(url).Result.Content.ReadAsStringAsync(); return result.Result; } #endregion #region 异步请求 /// /// post 请求 /// /// 请求地址 /// 请求参数 /// public static Task PostAsync(string url, string jsonData) { HttpClient httpClient = CreateHttpClient(url); var postData = new StringContent(jsonData); postData.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); var result = httpClient.PostAsync(url, postData); return result; } /// /// post 请求 /// /// 请求地址 /// 请求参数 /// public static Task PostAsync(string url, byte[] req) { HttpClient httpClient = CreateHttpClient(url); var postData = new ByteArrayContent(req); var result = httpClient.PostAsync(url, postData); return result; } /// /// post 请求 /// /// 请求地址 /// 请求参数 /// public static Task PostAsync(string url, Dictionary pairs) { HttpClient httpClient = CreateHttpClient(url); var postData = new FormUrlEncodedContent(pairs); var result = httpClient.PostAsync(url, postData); return result; } /// /// get 请求 /// /// 请求地址 /// public static Task GetAsync(string url) { HttpClient httpClient = CreateHttpClient(url); var result = httpClient.GetAsync(url); return result; } #endregion } }