微信支付接口之心酸
微信接口,对于某些人来说,很容易,宝宝自己尝试了一下,瞬间感觉泪奔呀,唉。
首先,统一下单接口:
[HttpPost] [Route("~/api/WXPay/UnifiedOrder")] public HttpResponseMessage UnifiedOrder() { try { NameValueCollection param = Request.Content.ReadAsFormDataAsync().Result; string body = param["body"].ToString();//简单描述 string out_trade_no = param["out_trade_no"].ToString(); string total_fee = param["total_fee"].ToString(); string spbill_create_ip = param["spbill_create_ip"].ToString(); var stringADict = new Dictionary<string, string>(); stringADict.Add("appid", "这里是公众号ID"); stringADict.Add("mch_id", "商户号"); stringADict.Add("device_info", device_info); stringADict.Add("nonce_str", CreateNonce_str()); stringADict.Add("body", body); stringADict.Add("out_trade_no", out_trade_no); stringADict.Add("total_fee", total_fee); stringADict.Add("spbill_create_ip", spbill_create_ip); stringADict.Add("notify_url", "回调函数"); stringADict.Add("trade_type", "APP"); var sign = Sign(stringADict, "签名字符串");//生成签名字符串 var postdata = GeneralPostdata(stringADict, sign); var url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; var client = new HttpClient(); var result = client.PostAsync(url, new StringContent(postdata)).Result; if (!result.IsSuccessStatusCode) return null; dynamic doc = new DynamicXml(result.Content.ReadAsStringAsync().Result); JObject obj = new JObject(); var a = doc.return_code.Value; var c = doc.return_msg.Value; if (doc.return_code.Value == "SUCCESS" && doc.result_code.Value == "SUCCESS") { obj.Add("prepay_id", doc.prepay_id.Value); obj.Add("trade_type", doc.trade_type.Value); obj.Add("sign", doc.sign.Value); obj.Add("nonce_str", doc.nonce_str.Value); obj.Add("result_code", doc.result_code.Value); obj.Add("return_code", doc.return_code.Value); obj.Add("return_msg", doc.return_msg.Value); obj.Add("RetCode", 0); obj.Add("RetMsg", ""); } else { obj.Add("RetCode", -1); obj.Add("doc", doc.return_code.ToString() + doc.return_msg.ToString()); } return Request.CreateResponse(obj); } catch (Exception e) { JObject obj = new JObject(); obj.Add("RetCode", "-1"); obj.Add("ErrorDescription", e.ToString()); obj.Add("RetMsg", "错误代码:" + Guid.NewGuid().ToString()); return Request.CreateResponse(obj); } }
在这里,必须要说明的是DynamicXml,在这个动态读取XML的时候,可坑死宝宝了。
public class DynamicXml : DynamicObject, IEnumerable { private readonly List<XElement> _elements; public DynamicXml(string text) { var doc = XDocument.Parse(text); _elements = new List<XElement> { doc.Root }; } protected DynamicXml(XElement element) { _elements = new List<XElement> { element }; } protected DynamicXml(IEnumerable<XElement> elements) { _elements = new List<XElement>(elements); } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = null; if (binder.Name == "Value") result = _elements[0].Value; else if (binder.Name == "Count") result = _elements.Count; else { var attr = _elements[0].Attribute(XName.Get(binder.Name)); if (attr != null) result = attr; else { var items = _elements.Descendants(XName.Get(binder.Name)); if (items == null || items.Count() == 0) return false; result = new DynamicXml(items); } } return true; } public IEnumerator GetEnumerator() { foreach (var element in _elements) yield return new DynamicXml(element); } /// <summary> /// /// </summary> /// <param name="binder"></param> /// <param name="indexes"></param> /// <param name="result"></param> /// <returns></returns> public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { int ndx = (int)indexes[0]; result = new DynamicXml(_elements[ndx]); return true; } /// <summary> /// /// </summary> /// <returns></returns> public override string ToString() { StringWriter message = new StringWriter(); foreach (var item in _elements) message.WriteLine("{0}:\t{1}", item.Document, item.Value); return message.ToString(); } }
因为刚开始没有加上ToString和TryGetIndex的重写,然后呢,解析了一天,都不知道哪里的错。唉
OK,微信回调接口
[HttpPost] [Route("~/api/WXPay/Notify")] public HttpResponseMessage Notify() { try { byte[] a = Request.Content.ReadAsByteArrayAsync().Result; string b = Encoding.UTF8.GetString(a); var sParam = GetRequestPost(b); if (sParam.Count <= 0) { throw new ArgumentNullException(); } JObject obj = new JObject(); obj.Add("RetCode", 0); obj.Add("RetMsg", ""); if (sParam["return_code"] == "SUCCESS" && sParam["result_code"] == "SUCCESS") { var sign = sParam["sign"]; var signValue = Sign(sParam, "签名字符串"); bool isVerify = sign == signValue; if (isVerify) { var out_trade_no = sParam["out_trade_no"]; //具体的后期业务操作 } obj.Add("return_code", "success"); } else { obj.Add("return_code", "fail"); } return Request.CreateResponse(HttpStatusCode.OK, obj); } catch (ArgumentNullException e) { JObject obj = new JObject(); obj.Add("RetCode", "-1"); obj.Add("RequestMsg", "暂无参数!"); return Request.CreateResponse(obj); } catch (Exception e) { JObject obj = new JObject(); obj.Add("RetCode", "-1"); obj.Add("ErrorDescription", e.ToString()); obj.Add("RetMsg", "错误代码:" + Guid.NewGuid().ToString()); return Request.CreateResponse(obj); } }
对于这里的GetRequestPost方法,其实就是组合生成一个标准的xml字符串。
private SortedDictionary<string, string> GetRequestPost(string xml) { SortedDictionary<string, string> sArray = new SortedDictionary<string, string>(); dynamic resultXml = new DynamicXml(xml); sArray.Add("appid", resultXml.appid.Value); sArray.Add("bank_type", resultXml.bank_type.Value); sArray.Add("cash_fee", resultXml.cash_fee.Value); sArray.Add("fee_type", resultXml.fee_type.Value); sArray.Add("is_subscribe", resultXml.is_subscribe.Value); sArray.Add("mch_id", resultXml.mch_id.Value); sArray.Add("nonce_str", resultXml.nonce_str.Value); sArray.Add("openid", resultXml.openid.Value); sArray.Add("out_trade_no", resultXml.out_trade_no.Value); sArray.Add("time_end", resultXml.time_end.Value); sArray.Add("total_fee", resultXml.total_fee.Value); sArray.Add("transaction_id", resultXml.transaction_id.Value); sArray.Add("trade_type", resultXml.trade_type.Value); sArray.Add("sign", resultXml.sign.Value); sArray.Add("result_code", resultXml.result_code.Value); sArray.Add("return_code", resultXml.return_code.Value); return sArray; }
附,后面的辅助方法,包含签名生成工具等。
public static string GeneralPostdata(IDictionary<string, string> postdataDict, string sign) { var sb2 = new StringBuilder(); sb2.Append("<xml>"); foreach (var sA in postdataDict.OrderBy(x => x.Key)) { sb2.Append("<" + sA.Key + ">") .Append(Transfer(sA.Value)) .Append("</" + sA.Key + ">"); } sb2.Append("<sign>").Append(sign).Append("</sign>"); sb2.Append("</xml>"); return sb2.ToString(); } public static string Transfer(string instr) { if (instr == null) return ""; return instr.Replace("&", "&").Replace("<", "<") .Replace(">", ">").Replace("\"", """); } /// <summary> /// 生成签名 /// 签名在线验证工具: /// http://mch.weixin.qq.com/wiki/tools/signverify/ /// </summary> /// <param name="stringADict">参与签名生成的参数列表</param> /// <param name="partnerKey">商家私钥</param> /// <returns></returns> public static string Sign(IDictionary<string, string> stringADict, string partnerKey) { var sb = new StringBuilder(); foreach (var sA in stringADict.OrderBy(x => x.Key)) { if (string.IsNullOrEmpty(sA.Value)) continue; if (string.Compare(sA.Key, "sign", true) == 0) continue; sb.Append(sA.Key).Append("=").Append(sA.Value).Append("&"); } var string1 = sb.ToString(); sb.Append("key=").Append(partnerKey); var stringSignTemp = sb.ToString(); var sign = MD5(stringSignTemp, "UTF-8").ToUpper(); return sign; } public static string MD5(string encypStr, string charset = "UTF-8") { string retStr; MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider(); byte[] inputBye; byte[] outputBye; try { inputBye = Encoding.GetEncoding(charset).GetBytes(encypStr); } catch { inputBye = Encoding.GetEncoding("GB2312").GetBytes(encypStr); } outputBye = m5.ComputeHash(inputBye); retStr = System.BitConverter.ToString(outputBye); retStr = retStr.Replace("-", "").ToUpper(); return retStr; }
OK,只是一个简单的记录,可以直接套用的,当然,对于.net来说,特别注意的就是那个DynamicXML那个类,太尼玛坑爹了,百毒了半天,然后看了半天,终于晓得问题了,唉。不喜勿喷。谢谢
其中,很多包含借鉴网上的东西,如有版权牵扯,请及时联系本人,谢谢。