Data.cs 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. using System.Collections.Generic;
  2. using System.Security.Cryptography;
  3. using System.Text;
  4. using System.Xml;
  5. using LitJson;
  6. using WayneCloud.Models.Models;
  7. namespace Wechat.PayAPI
  8. {
  9. /// <summary>
  10. /// 微信支付协议接口数据类,所有的API接口通信都依赖这个数据结构,
  11. /// 在调用接口之前先填充各个字段的值,然后进行接口通信,
  12. /// 这样设计的好处是可扩展性强,用户可随意对协议进行更改而不用重新设计数据结构,
  13. /// 还可以随意组合出不同的协议数据包,不用为每个协议设计一个数据包结构
  14. /// </summary>
  15. public class WxPayData
  16. {
  17. public WxPayData()
  18. {
  19. }
  20. //采用排序的Dictionary的好处是方便对数据包进行签名,不用再签名之前再做一次排序
  21. private SortedDictionary<string, object> m_values = new SortedDictionary<string, object>();
  22. /**
  23. * 设置某个字段的值
  24. * @param key 字段名
  25. * @param value 字段值
  26. */
  27. public void SetValue(string key, object value)
  28. {
  29. m_values[key] = value;
  30. }
  31. /**
  32. * 根据字段名获取某个字段的值
  33. * @param key 字段名
  34. * @return key对应的字段值
  35. */
  36. public object GetValue(string key)
  37. {
  38. object o = null;
  39. m_values.TryGetValue(key, out o);
  40. return o;
  41. }
  42. /**
  43. * 判断某个字段是否已设置
  44. * @param key 字段名
  45. * @return 若字段key已被设置,则返回true,否则返回false
  46. */
  47. public bool IsSet(string key)
  48. {
  49. object o = null;
  50. m_values.TryGetValue(key, out o);
  51. if (null != o)
  52. return true;
  53. else
  54. return false;
  55. }
  56. /**
  57. * @将Dictionary转成xml
  58. * @return 经转换得到的xml串
  59. * @throws WxPayException
  60. **/
  61. public string ToXml()
  62. {
  63. //数据为空时不能转化为xml格式
  64. if (0 == m_values.Count)
  65. {
  66. Log.Error(this.GetType().ToString(), "WxPayData数据为空!");
  67. throw new WxPayException("WxPayData数据为空!");
  68. }
  69. string xml = "<xml>";
  70. foreach (KeyValuePair<string, object> pair in m_values)
  71. {
  72. //字段值不能为null,会影响后续流程
  73. if (pair.Value == null)
  74. {
  75. continue;
  76. }
  77. if (pair.Value.GetType() == typeof(int))
  78. {
  79. xml += "<" + pair.Key + ">" + pair.Value + "</" + pair.Key + ">";
  80. }
  81. else if (pair.Value.GetType() == typeof(string))
  82. {
  83. xml += "<" + pair.Key + ">" + "<![CDATA[" + pair.Value + "]]></" + pair.Key + ">";
  84. }
  85. else//除了string和int类型不能含有其他数据类型
  86. {
  87. Log.Error(this.GetType().ToString(), "WxPayData字段数据类型错误!");
  88. throw new WxPayException("WxPayData字段数据类型错误!");
  89. }
  90. }
  91. xml += "</xml>";
  92. return xml;
  93. }
  94. /**
  95. * @将xml转为WxPayData对象并返回对象内部的数据
  96. * @param string 待转换的xml串
  97. * @return 经转换得到的Dictionary
  98. * @throws WxPayException
  99. */
  100. public SortedDictionary<string, object> FromXml(WxPayConfig config, string xml)
  101. {
  102. if (string.IsNullOrEmpty(xml))
  103. {
  104. Log.Error(this.GetType().ToString(), "将空的xml串转换为WxPayData不合法!");
  105. throw new WxPayException("将空的xml串转换为WxPayData不合法!");
  106. }
  107. XmlDocument xmlDoc = new XmlDocument();
  108. xmlDoc.LoadXml(xml);
  109. XmlNode xmlNode = xmlDoc.FirstChild;//获取到根节点<xml>
  110. XmlNodeList nodes = xmlNode.ChildNodes;
  111. foreach (XmlNode xn in nodes)
  112. {
  113. XmlElement xe = (XmlElement)xn;
  114. m_values[xe.Name] = xe.InnerText;//获取xml的键值对到WxPayData内部的数据中
  115. }
  116. try
  117. {
  118. //2015-06-29 错误是没有签名
  119. if(m_values["return_code"] != "SUCCESS")
  120. {
  121. return m_values;
  122. }
  123. CheckSign(config);//验证签名,不通过会抛异常
  124. }
  125. catch(WxPayException ex)
  126. {
  127. throw new WxPayException(ex.Message);
  128. }
  129. return m_values;
  130. }
  131. /**
  132. * @Dictionary格式转化成url参数格式
  133. * @ return url格式串, 该串不包含sign字段值
  134. */
  135. public string ToUrl()
  136. {
  137. string buff = "";
  138. foreach (KeyValuePair<string, object> pair in m_values)
  139. {
  140. if (pair.Value != null)
  141. {
  142. if (pair.Key != "sign" && pair.Value.ToString() != "")
  143. {
  144. buff += pair.Key + "=" + pair.Value + "&";
  145. }
  146. }
  147. }
  148. buff = buff.Trim('&');
  149. return buff;
  150. }
  151. /**
  152. * @Dictionary格式化成Json
  153. * @return json串数据
  154. */
  155. public string ToJson()
  156. {
  157. string jsonStr = JsonMapper.ToJson(m_values);
  158. return jsonStr;
  159. }
  160. /**
  161. * @values格式化成能在Web页面上显示的结果(因为web页面上不能直接输出xml格式的字符串)
  162. */
  163. public string ToPrintStr()
  164. {
  165. string str = "";
  166. foreach (KeyValuePair<string, object> pair in m_values)
  167. {
  168. if (pair.Value == null)
  169. {
  170. Log.Error(this.GetType().ToString(), "WxPayData内部含有值为null的字段!");
  171. throw new WxPayException("WxPayData内部含有值为null的字段!");
  172. }
  173. str += string.Format("{0}={1}<br>", pair.Key, pair.Value.ToString());
  174. }
  175. Log.Debug(this.GetType().ToString(), "Print in Web Page : " + str);
  176. return str;
  177. }
  178. /**
  179. * @生成签名,详见签名生成算法
  180. * @return 签名, sign字段不参加签名
  181. */
  182. public string MakeSign(WxPayConfig config)
  183. {
  184. //转url格式
  185. string str = ToUrl();
  186. //在string后加入API KEY
  187. str += "&key=" + config.KEY;
  188. //MD5加密
  189. var md5 = MD5.Create();
  190. var bs = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
  191. var sb = new StringBuilder();
  192. foreach (byte b in bs)
  193. {
  194. sb.Append(b.ToString("x2"));
  195. }
  196. //所有字符转为大写
  197. return sb.ToString().ToUpper();
  198. }
  199. /**
  200. *
  201. * 检测签名是否正确
  202. * 正确返回true,错误抛异常
  203. */
  204. public bool CheckSign(WxPayConfig config)
  205. {
  206. //如果没有设置签名,则跳过检测
  207. if (!IsSet("sign"))
  208. {
  209. Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!");
  210. throw new WxPayException("WxPayData签名存在但不合法!");
  211. }
  212. //如果设置了签名但是签名为空,则抛异常
  213. else if(GetValue("sign") == null || GetValue("sign").ToString() == "")
  214. {
  215. Log.Error(this.GetType().ToString(), "WxPayData签名存在但不合法!");
  216. throw new WxPayException("WxPayData签名存在但不合法!");
  217. }
  218. //获取接收到的签名
  219. string return_sign = GetValue("sign").ToString();
  220. //在本地计算新的签名
  221. string cal_sign = MakeSign(config);
  222. if (cal_sign == return_sign)
  223. {
  224. return true;
  225. }
  226. Log.Error(this.GetType().ToString(), "WxPayData签名验证错误!");
  227. throw new WxPayException("WxPayData签名验证错误!");
  228. }
  229. /**
  230. * @获取Dictionary
  231. */
  232. public SortedDictionary<string, object> GetValues()
  233. {
  234. return m_values;
  235. }
  236. }
  237. public class WxPayDataWithResult
  238. {
  239. public WxPayData PayData {get; set;}
  240. public int Status { get; set; }
  241. }
  242. }