Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

266 rindas
8.9 KiB

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