ASP.Net項(xiàng)目中實(shí)現(xiàn)微信APP支付功能
最近挺忙的,沒(méi)時(shí)間寫(xiě)東西。然后在弄微信APP支付,網(wǎng)上的搜索一趟,都比較凌亂,我也遇到一些坑,不過(guò)也算弄好了,記錄分享一下。
1、準(zhǔn)備各種調(diào)用接口需要的參數(shù),配置app.config。
<!--AppID--> <add key="AppID" value="" /> <!--AppSecret--> <add key="AppSecret" value="" /> <!--商戶(hù)號(hào)--> <add key="MchID" value="" /> <!--APIKey--> <add key="APIKey" value="" />
2、調(diào)用統(tǒng)一下單API,直接上代碼了。
/// <summary> /// Builds the request. /// </summary> /// <returns></returns> public static string BuildRequest() { var dicParam = CreateParam(); var signString = CreateURLParamString(dicParam); var preString = signString + "&key=" + ConfigHelper.APIKey; var sign = Sign(preString, "utf-8").ToUpper(); dicParam.Add("sign", sign); return BuildForm(dicParam); } /// <summary> /// Generates the out trade no. /// </summary> /// <returns></returns> private static string GenerateOutTradeNo() { var ran = new Random(); return $"{ConfigHelper.MchID}{DateTime.Now:yyyyMMddHHmmss}{ran.Next(999)}"; } /// <summary> /// Signs the specified prestr. /// </summary> /// <param name="prestr">The prestr.</param> /// <param name="_input_charset">The input charset.</param> /// <returns></returns> private static string Sign(string prestr, string _input_charset) { var sb = new StringBuilder(32); MD5 md5 = new MD5CryptoServiceProvider(); var t = md5.ComputeHash(Encoding.GetEncoding(_input_charset).GetBytes(prestr)); foreach (var t1 in t) { sb.Append(t1.ToString("x").PadLeft(2, '0')); } return sb.ToString(); } /// <summary> /// Creates the parameter. /// </summary> /// <returns></returns> private static SortedDictionary<string, string> CreateParam() { const string amount = "1"; double dubamount; double.TryParse(amount, out dubamount); var notify_url = ConfigHelper.WebSiteUrl + "/api/v1/testWeiXin"; //支付完成后的回調(diào)處理頁(yè)面 const string detail = "xxxx"; var dic = new SortedDictionary<string, string> { {"appid", ConfigHelper.AppID},//賬號(hào)ID {"mch_id", ConfigHelper.MchID},//商戶(hù)號(hào) {"nonce_str", Guid.NewGuid().ToString().Replace("-", "")},//隨機(jī)字符串 {"body", detail}, //商品描述 {"out_trade_no", GenerateOutTradeNo()},//商戶(hù)訂單號(hào) {"total_fee", (dubamount * 100).ToString(CultureInfo.InvariantCulture)},//總金額 {"spbill_create_ip", GeneralHelper.GetIP()},//終端IP {"notify_url", notify_url},//通知地址 {"trade_type", "APP"}//交易類(lèi)型 }; return dic; } /// <summary> /// Creates the URL parameter string. /// </summary> /// <param name="dicArray">The dic array.</param> /// <returns></returns> private static string CreateURLParamString(SortedDictionary<string, string> dicArray) { var prestr = new StringBuilder(); foreach (var temp in dicArray.OrderBy(o => o.Key)) { prestr.Append(temp.Key + "=" + temp.Value + "&"); } var nLen = prestr.Length; prestr.Remove(nLen - 1, 1); return prestr.ToString(); } /// <summary> /// Builds the form. /// </summary> /// <param name="dicParam">The dic parameter.</param> /// <returns></returns> private static string BuildForm(SortedDictionary<string, string> dicParam) { var sbXML = new StringBuilder(); sbXML.Append("<xml>"); foreach (var temp in dicParam) { sbXML.Append("<" + temp.Key + ">" + temp.Value + "</" + temp.Key + ">"); } sbXML.Append("</xml>"); return sbXML.ToString(); } /// <summary> /// Froms the XML. /// </summary> /// <param name="xml">The XML.</param> /// <returns></returns> /// <exception cref="Exception">將空的xml串轉(zhuǎn)換為WxPayData不合法!</exception> public static SortedDictionary<string, string> FromXml(string xml) { var sortDic = new SortedDictionary<string, string>(); if (string.IsNullOrEmpty(xml)) { throw new Exception("將空的xml串轉(zhuǎn)換為WxPayData不合法!"); } var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); var xmlNode = xmlDoc.FirstChild;//獲取到根節(jié)點(diǎn)<xml> var nodes = xmlNode.ChildNodes; foreach (XmlNode xn in nodes) { var xe = (XmlElement)xn; if (!sortDic.ContainsKey(xe.Name)) sortDic.Add(xe.Name, xe.InnerText); } return sortDic; } /// <summary> /// Posts the specified URL. /// </summary> /// <param name="url">The URL.</param> /// <param name="content">The content.</param> /// <param name="contentType">Type of the content.</param> /// <returns></returns> /// <exception cref="Exception">POST請(qǐng)求錯(cuò)誤" + e</exception> public static string Post(string url, string content, string contentType = "application/x-www-form-urlencoded") { string result; try { using (var client = new HttpClient()) { client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(contentType)); var stringContent = new StringContent(content, Encoding.UTF8); var response = client.PostAsync(url, stringContent).Result; result = response.Content.ReadAsStringAsync().Result; } } catch (Exception e) { throw new Exception("POST請(qǐng)求錯(cuò)誤" + e); } return result; }
3、生成預(yù)付訂單,獲取prepay_id。
/// <summary> /// Gets the value from dic. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="dic">The dic.</param> /// <param name="key">The key.</param> /// <returns></returns> public static T GetValueFromDic<T>(IDictionary<string, string> dic, string key) { string val; dic.TryGetValue(key, out val); var returnVal = default(T); if (val != null) returnVal = (T)Convert.ChangeType(val, typeof(T)); return returnVal; } /// <summary> /// Builds the application pay. /// </summary> /// <param name="prepayid">The prepayid.</param> /// <returns></returns> public static string BuildAppPay(string prepayid) { var dicParam = CreateWapAndAppPayParam(prepayid); var signString = CreateURLParamString(dicParam); var preString = signString + "&key=" + ConfigHelper.APIKey; var sign = Sign(preString, "utf-8").ToUpper(); dicParam.Add("sign", sign); return JsonConvert.SerializeObject( new { appid = dicParam["appid"], partnerid = dicParam["partnerid"], prepayid = dicParam["prepayid"], package = dicParam["package"], noncestr = dicParam["noncestr"], timestamp = dicParam["timestamp"], sign = dicParam["sign"] }); } /// <summary> /// Creates the wap and application pay parameter. /// </summary> /// <param name="prepayId">The prepay identifier.</param> /// <returns></returns> private static SortedDictionary<string, string> CreateWapAndAppPayParam(string prepayId) { var dic = new SortedDictionary<string, string> { {"appid", ConfigHelper.AppID},//公眾賬號(hào)ID {"partnerid", ConfigHelper.MchID},//商戶(hù)號(hào) {"prepayid", prepayId},//預(yù)支付交易會(huì)話ID {"package", "Sign=WXPay"},//擴(kuò)展字段 {"noncestr", Guid.NewGuid().ToString().Replace("-", "")},//隨機(jī)字符串 { "timestamp", (Convert.ToInt32((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalSeconds)).ToString() }//時(shí)間戳 }; return dic; } /// <summary> /// Validatons the query result. /// </summary> /// <param name="dic">The dic.</param> /// <returns></returns> public static bool ValidatonQueryResult(SortedDictionary<string, string> dic) { var result = false; if (dic.ContainsKey("return_code") && dic.ContainsKey("return_code")) { if (dic["return_code"] == "SUCCESS" && dic["result_code"] == "SUCCESS") result = true; } if (result) return true; var sb = new StringBuilder(); foreach (var item in dic.Keys) { sb.Append(item + ":" + dic[item] + "|"); } return false; }
4、調(diào)用獲取支付信息,給到APP發(fā)起支付操作。
var requestXml = WeiXinUtil.BuildRequest(); var resultXml = WeiXinUtil.Post("https://api.mch.weixin.qq.com/pay/unifiedorder", requestXml); var dic = WeiXinUtil.FromXml(resultXml); string returnCode; dic.TryGetValue("return_code", out returnCode); if (returnCode == "SUCCESS") { var prepay_id = WeiXinUtil.GetValueFromDic<string>(dic, "prepay_id"); if (!string.IsNullOrEmpty(prepay_id)) { var payInfo = JsonConvert.DeserializeObject<WeiXinUtil.WxPayModel>(WeiXinUtil.BuildAppPay(prepay_id)); json.Add(new JProperty("appid", payInfo.appid)); json.Add(new JProperty("partnerid", payInfo.partnerid)); json.Add(new JProperty("prepayid", payInfo.prepayid)); json.Add(new JProperty("package", payInfo.package)); json.Add(new JProperty("noncestr", payInfo.noncestr)); json.Add(new JProperty("timestamp", payInfo.timestamp)); json.Add(new JProperty("sign", payInfo.sign)); json.Add(new JProperty("code", 0)); json.Add(new JProperty("msg", "成功")); return this.Jsonp(json.ToString()); } else { json.Add(new JProperty("code", 40028)); json.Add(new JProperty("msg", "支付錯(cuò)誤:" + WeiXinUtil.GetValueFromDic<string>(dic, "err_code_des"))); return this.Jsonp(json.ToString()); } } else { return this.Jsonp(ApiException.OrderFailed()); }
5、APP支付完成,獲得回調(diào)信息,就OK了。
var context = this.HttpContext; var request = context.Request; var verifyResult = false; var requestXml = WeiXinUtil.GetRequestXmlData(request); var dic = WeiXinUtil.FromXml(requestXml); var returnCode = WeiXinUtil.GetValueFromDic<string>(dic, "return_code"); if (!string.IsNullOrEmpty(returnCode) && returnCode == "SUCCESS")//通訊成功 { var result = WeiXinUtil.WePayNotifyValidation(dic); if (result) { var transactionid = WeiXinUtil.GetValueFromDic<string>(dic, "transaction_id"); if (!string.IsNullOrEmpty(transactionid)) { var queryXml = WeiXinUtil.BuildQueryRequest(transactionid, dic); var queryResult = WeiXinUtil.Post("https://api.mch.weixin.qq.com/pay/orderquery", queryXml); var queryReturnDic = WeiXinUtil.FromXml(queryResult); if (WeiXinUtil.ValidatonQueryResult(queryReturnDic))//查詢(xún)成功 { verifyResult = true; var status = WeiXinUtil.GetValueFromDic<string>(dic, "result_code"); if (!string.IsNullOrEmpty(status) && status == "SUCCESS") { var order = new Order() { OrderNumber = WeiXinUtil.GetValueFromDic<string>(dic, "out_trade_no"), TransactionId = transactionid, ProductPrice = WeiXinUtil.GetValueFromDic<decimal>(dic, "total_fee") / 100, TradeType = WeiXinUtil.GetValueFromDic<string>(dic, "trade_type"), BankType = WeiXinUtil.GetValueFromDic<string>(dic, "bank_type"), PayDate = DateTime.Parse(WeiXinUtil.GetValueFromDic<string>(dic, "time_end")), StatusId = 1, IsPresent = false, AddDate = DateTime.Now, IsDelete = false }; CURD.Add(order, ConfigHelper.WriteDB); WeiXinUtil.BuildReturnXml("OK", "成功"); } } else WeiXinUtil.BuildReturnXml("FAIL", "訂單查詢(xún)失敗"); } else WeiXinUtil.BuildReturnXml("FAIL", "支付結(jié)果中微信訂單號(hào)不存在"); } else WeiXinUtil.BuildReturnXml("FAIL", "簽名失敗"); } else { string returnmsg; dic.TryGetValue("return_msg", out returnmsg); throw new Exception("異步通知錯(cuò)誤:" + returnmsg); } return verifyResult; /// <summary> /// Gets the request XML data. /// </summary> /// <param name="request">The request.</param> /// <returns></returns> public static string GetRequestXmlData(HttpRequestBase request) { var stream = request.InputStream; int count; var buffer = new byte[1024]; var builder = new StringBuilder(); while ((count = stream.Read(buffer, 0, 1024)) > 0) { builder.Append(Encoding.UTF8.GetString(buffer, 0, count)); } stream.Flush(); stream.Close(); return builder.ToString(); } /// <summary> /// Wes the pay notify validation. /// </summary> /// <param name="dic">The dic.</param> /// <returns></returns> public static bool WePayNotifyValidation(SortedDictionary<string, string> dic) { var sign = GetValueFromDic<string>(dic, "sign"); if (dic.ContainsKey("sign")) { dic.Remove("sign"); } var tradeType = GetValueFromDic<string>(dic, "trade_type"); var preString = CreateURLParamString(dic); if (string.IsNullOrEmpty(tradeType)) { var preSignString = preString + "&key=" + ConfigHelper.APIKey; var signString = Sign(preSignString, "utf-8").ToUpper(); return signString == sign; } else return false; } /// <summary> /// Builds the query request. /// </summary> /// <param name="transactionId">The transaction identifier.</param> /// <param name="dic">The dic.</param> /// <returns></returns> public static string BuildQueryRequest(string transactionId, SortedDictionary<string, string> dic) { var dicParam = CreateQueryParam(transactionId); var signString = CreateURLParamString(dicParam); var key = ConfigHelper.APIKey; var preString = signString + "&key=" + key; var sign = Sign(preString, "utf-8").ToUpper(); dicParam.Add("sign", sign); return BuildForm(dicParam); } /// <summary> /// Creates the query parameter. /// </summary> /// <param name="transactionId">The transaction identifier.</param> /// <returns></returns> private static SortedDictionary<string, string> CreateQueryParam(string transactionId) { var dic = new SortedDictionary<string, string> { {"appid", ConfigHelper.AppID},//公眾賬號(hào)ID {"mch_id", ConfigHelper.MchID},//商戶(hù)號(hào) {"nonce_str", Guid.NewGuid().ToString().Replace("-", "")},//隨機(jī)字符串 {"transaction_id", transactionId}//微信訂單號(hào) }; return dic; } /// <summary> /// Builds the return XML. /// </summary> /// <param name="code">The code.</param> /// <param name="returnMsg">The return MSG.</param> /// <returns></returns> public static string BuildReturnXml(string code, string returnMsg) { return $"<xml><return_code><![CDATA[[code]]]></return_code><return_msg><![CDATA[{returnMsg}]]></return_msg></xml>"; }
6、總結(jié):這個(gè)可以直接拿來(lái)用了,反反復(fù)復(fù)測(cè)試了很多遍,遇到的問(wèn)題有關(guān)于錢(qián),還有簽名的問(wèn)題,調(diào)試都解決了。繼續(xù)解決問(wèn)題,積累經(jīng)驗(yàn)。
到此這篇關(guān)于ASP.Net項(xiàng)目中實(shí)現(xiàn)微信APP支付功能的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解ASP.NET MVC 利用Razor引擎生成靜態(tài)頁(yè)
本篇文章主要介紹了ASP.NET MVC 利用Razor引擎生成靜態(tài)頁(yè),詳細(xì)的介紹了原理和步驟,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-03-03ASP.NET中readonly與const的區(qū)別詳解
如果你學(xué)過(guò)ASP.NET理論知識(shí)都會(huì)知道,在ASP.NET中 readonly和const修飾的變量都是恒量,它們的值是不可以被修改的。但是他們之間到底有什么區(qū)別?下面小編就它們的區(qū)別用例子來(lái)進(jìn)行說(shuō)明。2015-10-10.NET 開(kāi)發(fā)環(huán)境搭建圖文詳解
這篇文章主要介紹了.NET 開(kāi)發(fā)環(huán)境搭建,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11.NET Core創(chuàng)建一個(gè)控制臺(tái)(Console)程序
這篇文章主要為大家詳細(xì)介紹了.NET Core如何創(chuàng)建一個(gè)控制臺(tái)程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04.net core下對(duì)于附件上傳下載的實(shí)現(xiàn)示例
本篇文章主要介紹了.net core下對(duì)于附件上傳下載的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03asp.net中將js的返回值賦給asp.net控件的小例子
要做一個(gè)顯示用戶(hù)在線停留時(shí)間的功能,拖了一個(gè)label控件用于顯示時(shí)間,而時(shí)間是通過(guò)js來(lái)實(shí)現(xiàn)的,現(xiàn)在要把js的返回值賦給label,方法如下:2013-03-03詳解如何在ASP.NET Core中使用IHttpClientFactory
這篇文章主要介紹了詳解如何在ASP.NET Core中使用IHttpClientFactory,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-02-02asp.net基于JWT的web api身份驗(yàn)證及跨域調(diào)用實(shí)踐
這篇文章主要介紹了asp.net基于JWT的web api身份驗(yàn)證及跨域調(diào)用實(shí)踐,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-07-07C#.net 微信公眾賬號(hào)接口開(kāi)發(fā)
這篇文章主要介紹了C#.net 微信公眾賬號(hào)接口開(kāi)發(fā),需要的朋友可以參考下2016-05-05