WxPayApi.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. using System;
  2. using System.Threading.Tasks;
  3. namespace WingPaymentService.Common.WeChat
  4. {
  5. public class WxPayApi
  6. {
  7. private WxPayHttpRequest _httpRequest = new WxPayHttpRequest();
  8. /**
  9. * 提交被扫支付API
  10. * 收银员使用扫码设备读取微信用户刷卡授权码以后,二维码或条码信息传送至商户收银台,
  11. * 由商户收银台或者商户后台调用该接口发起支付。
  12. * @param WxPayData inputObj 提交给被扫支付API的参数
  13. * @param int timeOut 超时时间
  14. * @throws WxPayException
  15. * @return 成功时返回调用结果,其他抛异常
  16. */
  17. public async Task<WxPayData> Micropay(WxPayData inputObj, int timeOut = 10)
  18. {
  19. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/pay/micropay";
  20. //检测必填参数
  21. if (!inputObj.IsSet("body"))
  22. {
  23. throw new WxPayException("提交被扫支付API接口中,缺少必填参数body!");
  24. }
  25. else if (!inputObj.IsSet("out_trade_no"))
  26. {
  27. throw new WxPayException("提交被扫支付API接口中,缺少必填参数out_trade_no!");
  28. }
  29. else if (!inputObj.IsSet("total_fee"))
  30. {
  31. throw new WxPayException("提交被扫支付API接口中,缺少必填参数total_fee!");
  32. }
  33. else if (!inputObj.IsSet("auth_code"))
  34. {
  35. throw new WxPayException("提交被扫支付API接口中,缺少必填参数auth_code!");
  36. }
  37. inputObj.SetValue("spbill_create_ip", WxPayConfig.GetConfig().GetIp());//终端ip
  38. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  39. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  40. inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
  41. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  42. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  43. string xml = inputObj.ToXml();
  44. var start = DateTime.Now;//请求开始时间
  45. //Log.Debug("WxPayApi", "MicroPay request : " + xml);
  46. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口以提交数据到API
  47. //Log.Debug("WxPayApi", "MicroPay response : " + response);
  48. var end = DateTime.Now;
  49. int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
  50. //将xml格式的结果转换为对象以返回
  51. WxPayData result = new WxPayData();
  52. result.FromXml(response);
  53. return result;
  54. }
  55. /**
  56. *
  57. * 查询订单
  58. * @param WxPayData inputObj 提交给查询订单API的参数
  59. * @param int timeOut 超时时间
  60. * @throws WxPayException
  61. * @return 成功时返回订单查询结果,其他抛异常
  62. */
  63. public async Task<WxPayData> OrderQuery(WxPayData inputObj, int timeOut = 6)
  64. {
  65. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/pay/orderquery";
  66. //检测必填参数
  67. if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
  68. {
  69. throw new WxPayException("订单查询接口中,out_trade_no、transaction_id至少填一个!");
  70. }
  71. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  72. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  73. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  74. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  75. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  76. string xml = inputObj.ToXml();
  77. var start = DateTime.Now;
  78. //Log.Debug("WxPayApi", "OrderQuery request : " + xml);
  79. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口提交数据
  80. //Log.Debug("WxPayApi", "OrderQuery response : " + response);
  81. var end = DateTime.Now;
  82. int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
  83. //将xml格式的数据转化为对象以返回
  84. WxPayData result = new WxPayData();
  85. result.FromXml(response);
  86. return result;
  87. }
  88. /**
  89. *
  90. * 撤销订单API接口
  91. * @param WxPayData inputObj 提交给撤销订单API接口的参数,out_trade_no和transaction_id必填一个
  92. * @param int timeOut 接口超时时间
  93. * @throws WxPayException
  94. * @return 成功时返回API调用结果,其他抛异常
  95. */
  96. public async Task<WxPayData> Reverse(WxPayData inputObj, int timeOut = 6)
  97. {
  98. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/secapi/pay/reverse";
  99. //检测必填参数
  100. if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
  101. {
  102. throw new WxPayException("撤销订单API接口中,参数out_trade_no和transaction_id必须填写一个!");
  103. }
  104. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  105. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  106. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  107. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  108. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  109. string xml = inputObj.ToXml();
  110. var start = DateTime.Now;//请求开始时间
  111. //Log.Debug("WxPayApi", "Reverse request : " + xml);
  112. string response = await _httpRequest.Post(xml, url, timeOut);
  113. //Log.Debug("WxPayApi", "Reverse response : " + response);
  114. var end = DateTime.Now;
  115. int timeCost = (int)((end - start).TotalMilliseconds);
  116. WxPayData result = new WxPayData();
  117. result.FromXml(response);
  118. return result;
  119. }
  120. /**
  121. *
  122. * 申请退款
  123. * @param WxPayData inputObj 提交给申请退款API的参数
  124. * @param int timeOut 超时时间
  125. * @throws WxPayException
  126. * @return 成功时返回接口调用结果,其他抛异常
  127. */
  128. public async Task<WxPayData> Refund(WxPayData inputObj, int timeOut = 6)
  129. {
  130. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/secapi/pay/refund";
  131. //检测必填参数
  132. if (!inputObj.IsSet("out_trade_no") && !inputObj.IsSet("transaction_id"))
  133. {
  134. throw new WxPayException("退款申请接口中,out_trade_no、transaction_id至少填一个!");
  135. }
  136. else if (!inputObj.IsSet("out_refund_no"))
  137. {
  138. throw new WxPayException("退款申请接口中,缺少必填参数out_refund_no!");
  139. }
  140. else if (!inputObj.IsSet("total_fee"))
  141. {
  142. throw new WxPayException("退款申请接口中,缺少必填参数total_fee!");
  143. }
  144. else if (!inputObj.IsSet("refund_fee"))
  145. {
  146. throw new WxPayException("退款申请接口中,缺少必填参数refund_fee!");
  147. }
  148. //else if (!inputObj.IsSet("op_user_id"))
  149. //{
  150. // throw new WxPayException("退款申请接口中,缺少必填参数op_user_id!");
  151. //}
  152. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  153. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  154. inputObj.SetValue("nonce_str", Guid.NewGuid().ToString().Replace("-", ""));//随机字符串
  155. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  156. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  157. string xml = inputObj.ToXml();
  158. var start = DateTime.Now;
  159. //Log.Debug("WxPayApi", "Refund request : " + xml);
  160. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口提交数据到API
  161. //Log.Debug("WxPayApi", "Refund response : " + response);
  162. var end = DateTime.Now;
  163. int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
  164. //将xml格式的结果转换为对象以返回
  165. WxPayData result = new WxPayData();
  166. result.FromXml(response);
  167. return result;
  168. }
  169. /**
  170. *
  171. * 查询退款
  172. * 提交退款申请后,通过该接口查询退款状态。退款有一定延时,
  173. * 用零钱支付的退款20分钟内到账,银行卡支付的退款3个工作日后重新查询退款状态。
  174. * out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个
  175. * @param WxPayData inputObj 提交给查询退款API的参数
  176. * @param int timeOut 接口超时时间
  177. * @throws WxPayException
  178. * @return 成功时返回,其他抛异常
  179. */
  180. public async Task<WxPayData> RefundQuery(WxPayData inputObj, int timeOut = 6)
  181. {
  182. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/pay/refundquery";
  183. //检测必填参数
  184. if (!inputObj.IsSet("out_refund_no") && !inputObj.IsSet("out_trade_no") &&
  185. !inputObj.IsSet("transaction_id") && !inputObj.IsSet("refund_id"))
  186. {
  187. throw new WxPayException("退款查询接口中,out_refund_no、out_trade_no、transaction_id、refund_id四个参数必填一个!");
  188. }
  189. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  190. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  191. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  192. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  193. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  194. string xml = inputObj.ToXml();
  195. var start = DateTime.Now;//请求开始时间
  196. //Log.Debug("WxPayApi", "RefundQuery request : " + xml);
  197. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口以提交数据到API
  198. //Log.Debug("WxPayApi", "RefundQuery response : " + response);
  199. var end = DateTime.Now;
  200. int timeCost = (int)((end - start).TotalMilliseconds);//获得接口耗时
  201. //将xml格式的结果转换为对象以返回
  202. WxPayData result = new WxPayData();
  203. result.FromXml(response);
  204. return result;
  205. }
  206. /**
  207. * 下载对账单
  208. * @param WxPayData inputObj 提交给下载对账单API的参数
  209. * @param int timeOut 接口超时时间
  210. * @throws WxPayException
  211. * @return 成功时返回,其他抛异常
  212. */
  213. public async Task<WxPayData> DownloadBill(WxPayData inputObj, int timeOut = 6)
  214. {
  215. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/pay/downloadbill";
  216. //检测必填参数
  217. if (!inputObj.IsSet("bill_date"))
  218. {
  219. throw new WxPayException("对账单接口中,缺少必填参数bill_date!");
  220. }
  221. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  222. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  223. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  224. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  225. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  226. string xml = inputObj.ToXml();
  227. //Log.Debug("WxPayApi", "DownloadBill request : " + xml);
  228. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口以提交数据到API
  229. //Log.Debug("WxPayApi", "DownloadBill result : " + response);
  230. WxPayData result = new WxPayData();
  231. //若接口调用失败会返回xml格式的结果
  232. if (response.Substring(0, 5) == "<xml>")
  233. {
  234. result.FromXml(response);
  235. }
  236. //接口调用成功则返回非xml格式的数据
  237. else
  238. result.SetValue("result", response);
  239. return result;
  240. }
  241. /**
  242. *
  243. * 转换短链接
  244. * 该接口主要用于扫码原生支付模式一中的二维码链接转成短链接(weixin://wxpay/s/XXXXXX),
  245. * 减小二维码数据量,提升扫描速度和精确度。
  246. * @param WxPayData inputObj 提交给转换短连接API的参数
  247. * @param int timeOut 接口超时时间
  248. * @throws WxPayException
  249. * @return 成功时返回,其他抛异常
  250. */
  251. public async Task<WxPayData> ShortUrl(WxPayData inputObj, int timeOut = 6)
  252. {
  253. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/tools/shorturl";
  254. //检测必填参数
  255. if (!inputObj.IsSet("long_url"))
  256. {
  257. throw new WxPayException("需要转换的URL,签名用原串,传输需URL encode!");
  258. }
  259. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  260. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  261. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  262. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  263. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  264. string xml = inputObj.ToXml();
  265. var start = DateTime.Now;//请求开始时间
  266. //Log.Debug("WxPayApi", "ShortUrl request : " + xml);
  267. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口以提交数据到API
  268. //Log.Debug("WxPayApi", "ShortUrl response : " + response);
  269. var end = DateTime.Now;
  270. int timeCost = (int)((end - start).TotalMilliseconds);
  271. WxPayData result = new WxPayData();
  272. result.FromXml(response);
  273. return result;
  274. }
  275. /**
  276. *
  277. * 统一下单
  278. * @param WxPaydata inputObj 提交给统一下单API的参数
  279. * @param int timeOut 超时时间
  280. * @throws WxPayException
  281. * @return 成功时返回,其他抛异常
  282. */
  283. public async Task<WxPayData> UnifiedOrder(WxPayData inputObj, int timeOut = 6)
  284. {
  285. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/pay/unifiedorder";
  286. //检测必填参数
  287. if (!inputObj.IsSet("out_trade_no"))
  288. {
  289. throw new WxPayException("缺少统一支付接口必填参数out_trade_no!");
  290. }
  291. else if (!inputObj.IsSet("body"))
  292. {
  293. throw new WxPayException("缺少统一支付接口必填参数body!");
  294. }
  295. else if (!inputObj.IsSet("total_fee"))
  296. {
  297. throw new WxPayException("缺少统一支付接口必填参数total_fee!");
  298. }
  299. else if (!inputObj.IsSet("trade_type"))
  300. {
  301. throw new WxPayException("缺少统一支付接口必填参数trade_type!");
  302. }
  303. //关联参数
  304. if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("openid"))
  305. {
  306. throw new WxPayException("统一支付接口中,缺少必填参数openid!trade_type为JSAPI时,openid为必填参数!");
  307. }
  308. if (inputObj.GetValue("trade_type").ToString() == "JSAPI" && !inputObj.IsSet("product_id"))
  309. {
  310. throw new WxPayException("统一支付接口中,缺少必填参数product_id!trade_type为JSAPI时,product_id为必填参数!");
  311. }
  312. inputObj.SetValue("notify_url", WxPayConfig.GetConfig().GetNotifyUrl());//异步通知url
  313. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  314. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  315. inputObj.SetValue("spbill_create_ip", WxPayConfig.GetConfig().GetIp());//终端ip
  316. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  317. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  318. //签名
  319. inputObj.SetValue("sign", inputObj.MakeSign());
  320. string xml = inputObj.ToXml();
  321. var start = DateTime.Now;
  322. //Log.Debug("WxPayApi", "UnfiedOrder request : " + xml);
  323. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口以提交数据到API
  324. //Log.Debug("WxPayApi", "UnfiedOrder response : " + response);
  325. var end = DateTime.Now;
  326. int timeCost = (int)((end - start).TotalMilliseconds);
  327. WxPayData result = new WxPayData();
  328. result.FromXml(response);
  329. return result;
  330. }
  331. /**
  332. *
  333. * 关闭订单
  334. * @param WxPayData inputObj 提交给关闭订单API的参数
  335. * @param int timeOut 接口超时时间
  336. * @throws WxPayException
  337. * @return 成功时返回,其他抛异常
  338. */
  339. public async Task<WxPayData> CloseOrder(WxPayData inputObj, int timeOut = 6)
  340. {
  341. string url = $"{WxPayConfig.GetConfig().GetServerUrl()}/pay/closeorder";
  342. //检测必填参数
  343. if (!inputObj.IsSet("out_trade_no"))
  344. {
  345. throw new WxPayException("关闭订单接口中,out_trade_no必填!");
  346. }
  347. inputObj.SetValue("appid", WxPayConfig.GetConfig().GetAppId());//公众账号ID
  348. inputObj.SetValue("mch_id", WxPayConfig.GetConfig().GetMchId());//商户号
  349. inputObj.SetValue("nonce_str", GenerateNonceStr());//随机字符串
  350. inputObj.SetValue("sign_type", WxPayData.SignTypeHmacSha256);//签名类型
  351. inputObj.SetValue("sign", inputObj.MakeSign());//签名
  352. string xml = inputObj.ToXml();
  353. var start = DateTime.Now;//请求开始时间
  354. string response = await _httpRequest.Post(xml, url, timeOut);//调用HTTP通信接口以提交数据到API
  355. var end = DateTime.Now;
  356. int timeCost = (int)((end - start).TotalMilliseconds);
  357. WxPayData result = new WxPayData();
  358. result.FromXml(response);
  359. return result;
  360. }
  361. /**
  362. * 生成随机串,随机串包含字母或数字
  363. * @return 随机串
  364. */
  365. public static string GenerateNonceStr()
  366. {
  367. RandomGenerator randomGenerator = new RandomGenerator();
  368. return randomGenerator.GetRandomUInt().ToString();
  369. }
  370. }
  371. }