MicroPay.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  1. using Gateway.Payment.Shared;
  2. using System;
  3. using System.Security.Cryptography.X509Certificates;
  4. using System.Threading;
  5. using Microsoft.Extensions.DependencyInjection;
  6. using Microsoft.Extensions.Logging;
  7. namespace Wechat.PayAPI
  8. {
  9. public class MicroPay
  10. {
  11. public static ILogger Log = Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance;
  12. /**
  13. * 刷卡支付完整业务流程逻辑
  14. * @param body 商品描述
  15. * @param total_fee 总金额
  16. * @param bill_number 商户订单号
  17. * @param auth_code 支付授权码
  18. * @param trade_status 支付授权码
  19. * @throws WxPayException
  20. * @return 刷卡支付结果
  21. */
  22. public static WxPayData Run(PaymentOrder order, out TradeStatusEnum trade_status)
  23. {
  24. //string body, string total_fee, string bill_number, string auth_code
  25. //set the default trade_status to PAYERROR
  26. trade_status = TradeStatusEnum.PAYERROR;
  27. WxPayData data = new WxPayData();
  28. data.SetValue("auth_code", order.AuthCode);//授权码
  29. data.SetValue("body", order.Title);//商品描述
  30. data.SetValue("total_fee", Convert.ToInt32(order.NetAmount * 100));//总金额
  31. data.SetValue("out_trade_no", order.BillNumber);//商户订单号
  32. WxPayData result = WxPayApi.Micropay(data, (WxPayConfig)order.Config, 20); //提交被扫支付,接收返回结果
  33. //如果提交被扫支付接口调用失败,则抛异常
  34. if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL")
  35. {
  36. string returnMsg = result.IsSet("return_msg") ? result.GetValue("return_msg").ToString() : "";
  37. Log.LogError("MicroPay", "Micropay API interface call failure, result : " + result.ToXml());
  38. throw new WxPayException("Micropay API interface call failure, return_msg : " + returnMsg);
  39. }
  40. //签名验证
  41. result.CheckSign((WxPayConfig)order.Config);
  42. Log.LogDebug("MicroPay", "Micropay response check sign success");
  43. //刷卡支付直接成功
  44. if (result.GetValue("return_code").ToString() == "SUCCESS" &&
  45. result.GetValue("result_code").ToString() == "SUCCESS")
  46. {
  47. Log.LogDebug("MicroPay", "Micropay business success, result : " + result.ToXml());
  48. trade_status = TradeStatusEnum.SUCCESS;
  49. return result;
  50. }
  51. /******************************************************************
  52. * 剩下的都是接口调用成功,业务失败的情况
  53. * ****************************************************************/
  54. //1)业务结果明确失败
  55. if (result.GetValue("err_code").ToString() != "USERPAYING" &&
  56. result.GetValue("err_code").ToString() != "SYSTEMERROR")
  57. {
  58. Log.LogError("MicroPay", "micropay API interface call success, business failure, result : " + result.ToXml());
  59. return result;
  60. }
  61. //2)不能确定是否失败,需查单
  62. //用商户订单号去查单
  63. string out_trade_no = data.GetValue("out_trade_no").ToString();
  64. //确认支付是否成功,每隔一段时间查询一次订单,共查询10次
  65. int queryTimes = 10;//查询次数计数器
  66. while (queryTimes-- > 0)
  67. {
  68. int succResult = 0;//查询结果
  69. WxPayData queryResult = Query(out_trade_no, (WxPayConfig)order.Config, out succResult, Log);
  70. result = queryResult;
  71. //如果需要继续查询,则等待2s后继续
  72. if (succResult == 2)
  73. {
  74. Thread.Sleep(2000);
  75. continue;
  76. }
  77. //查询成功,返回订单查询接口返回的数据
  78. else if (succResult == 1)
  79. {
  80. Log.LogDebug("MicroPay", "Mircopay success, return order query result : " + queryResult.ToXml());
  81. trade_status = TradeStatusEnum.SUCCESS;
  82. return queryResult;
  83. }
  84. //订单交易失败,直接返回刷卡支付接口返回的结果,失败原因会在err_code中描述
  85. else
  86. {
  87. Log.LogError("MicroPay", "Micropay failure, return micropay result : " + result.ToXml());
  88. //return result;
  89. }
  90. }
  91. return result;
  92. ////确认失败,则撤销订单
  93. //Log.Error("MicroPay", "Micropay failure, Reverse order is processing...");
  94. //if(!Cancel(out_trade_no))
  95. //{
  96. // Log.Error("MicroPay", "Reverse order failure");
  97. // throw new WxPayException("Reverse order failure!");
  98. //}
  99. }
  100. /**
  101. * 统一下单后查询支付状态
  102. * @param body 商品描述
  103. * @param total_fee 总金额
  104. * @param bill_number 商户订单号
  105. * @param auth_code 支付授权码
  106. * @param trade_status 支付授权码
  107. * @throws WxPayException
  108. * @return 刷卡支付结果
  109. */
  110. public static WxPayData RunQuery(PaymentOrder order, int count, int interval)
  111. {
  112. //用商户订单号去查单
  113. string out_trade_no = order.BillNumber;
  114. WxPayData result = null;
  115. //确认支付是否成功,每隔一段时间查询一次订单,共查询count次
  116. while (count-- > 0)
  117. {
  118. int succResult = 0;//查询结果
  119. result = Query(out_trade_no, (WxPayConfig)order.Config, out succResult, Log);
  120. //如果需要继续查询,则等待2s后继续
  121. if (succResult == 2)
  122. {
  123. Thread.Sleep(interval);
  124. continue;
  125. }
  126. //查询成功,返回订单查询接口返回的数据
  127. else if (succResult == 1)
  128. {
  129. Log.LogDebug("MicroPay", "Mircopay success, return order query result : " + result.ToXml());
  130. return result;
  131. }
  132. //订单交易失败,直接返回刷卡支付接口返回的结果,失败原因会在err_code中描述
  133. else
  134. {
  135. Log.LogError("MicroPay", "Micropay failure, return micropay result : " + result.ToXml());
  136. //return result;
  137. }
  138. }
  139. return result;
  140. ////确认失败,则撤销订单
  141. //Log.Error("MicroPay", "Micropay failure, Reverse order is processing...");
  142. //if(!Cancel(out_trade_no))
  143. //{
  144. // Log.Error("MicroPay", "Reverse order failure");
  145. // throw new WxPayException("Reverse order failure!");
  146. //}
  147. }
  148. /**
  149. *
  150. * 查询订单情况
  151. * @param string out_trade_no 商户订单号
  152. * @param int succCode 查询订单结果:0表示订单不成功,1表示订单成功,2表示继续查询
  153. * @return 订单查询接口返回的数据,参见协议接口
  154. */
  155. public static WxPayData Query(string out_trade_no, WxPayConfig config, out int succCode, ILogger Log)
  156. {
  157. WxPayData queryOrderInput = new WxPayData();
  158. queryOrderInput.SetValue("out_trade_no", out_trade_no);
  159. WxPayData result = WxPayApi.OrderQuery(queryOrderInput, config);
  160. if (result.GetValue("return_code").ToString() == "SUCCESS"
  161. && result.GetValue("result_code").ToString() == "SUCCESS")
  162. {
  163. //支付成功
  164. if (result.GetValue("trade_state").ToString() == "SUCCESS")
  165. {
  166. succCode = 1;
  167. return result;
  168. }
  169. //用户支付中,需要继续查询
  170. else if (result.GetValue("trade_state").ToString() == "USERPAYING")
  171. {
  172. succCode = 2;
  173. return result;
  174. }
  175. }
  176. //如果返回错误码为“此交易订单号不存在”则直接认定失败
  177. if (result.IsSet("err_code") && (result.GetValue("err_code").ToString() == "ORDERNOTEXIST"))
  178. {
  179. succCode = 0;
  180. }
  181. else
  182. {
  183. //如果是系统错误,则后续继续
  184. succCode = 2;
  185. }
  186. return result;
  187. }
  188. /**
  189. *
  190. * 撤销订单,如果失败会重复调用10次
  191. * @param string out_trade_no 商户订单号
  192. * @param depth 调用次数,这里用递归深度表示
  193. * @return false表示撤销失败,true表示撤销成功
  194. */
  195. public static bool Cancel(string out_trade_no, WxPayConfig config, X509Certificate2 certification, int depth = 0)
  196. {
  197. if (depth > 10)
  198. {
  199. return false;
  200. }
  201. WxPayData reverseInput = new WxPayData();
  202. reverseInput.SetValue("out_trade_no", out_trade_no);
  203. WxPayData result = WxPayApi.Reverse(reverseInput, config, certification);
  204. //接口调用失败
  205. if (result.GetValue("return_code").ToString() != "SUCCESS")
  206. {
  207. return false;
  208. }
  209. //如果结果为success且不需要重新调用撤销,则表示撤销成功
  210. if (result.GetValue("result_code").ToString() == "SUCCESS" && result.GetValue("recall").ToString() == "N")
  211. {
  212. return true;
  213. }
  214. else if (result.GetValue("recall").ToString() == "Y")
  215. {
  216. return Cancel(out_trade_no, config, certification, ++depth);
  217. }
  218. //用户支付中,2s后再次发起撤销
  219. else if (result.GetValue("err_code").ToString() == "USERPAYING")
  220. {
  221. Thread.Sleep(2 * 1000);
  222. return Cancel(out_trade_no, config, certification, ++depth);
  223. }
  224. return false;
  225. }
  226. /**
  227. * 统一下单业务流程逻辑
  228. * @param body 商品描述
  229. * @param total_fee 总金额
  230. * @param bill_number 商户订单号
  231. * @param trade_status 支付授权码
  232. * @throws WxPayException
  233. * @return 统一下单返回二维码连接结果
  234. */
  235. public static WxPayData UnifiedOrder(PaymentOrder order, out TradeStatusEnum trade_status)
  236. {
  237. //string body, string total_fee, string bill_number, string auth_code
  238. //set the default trade_status to PAYERROR
  239. trade_status = TradeStatusEnum.PAYERROR;
  240. WxPayData data = new WxPayData();
  241. data.SetValue("body", order.Title);//商品描述
  242. data.SetValue("total_fee", Convert.ToInt32(order.NetAmount * 100));//总金额
  243. if (order.Optional.ContainsKey("mobilePayId"))
  244. {
  245. data.SetValue("trade_type", "JSAPI");//交易类型
  246. data.SetValue("openid", order.Optional["mobilePayId"]);
  247. }
  248. else
  249. {
  250. data.SetValue("trade_type", "NATIVE");//交易类型
  251. }
  252. data.SetValue("out_trade_no", order.BillNumber);//商户订单号
  253. data.SetValue("product_id", "1");
  254. WxPayData result = WxPayApi.UnifiedOrder(data, (WxPayConfig)order.Config); //提交被扫支付,接收返回结果
  255. //如果提交被扫支付接口调用失败,则抛异常
  256. if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL")
  257. {
  258. string returnMsg = result.IsSet("return_msg") ? result.GetValue("return_msg").ToString() : "";
  259. Log.LogError("UnifiedOrder", "UnifiedOrder API interface call failure, result : " + result.ToXml());
  260. throw new WxPayException("UnifiedOrder API interface call failure, return_msg : " + returnMsg);
  261. }
  262. //签名验证
  263. result.CheckSign((WxPayConfig)order.Config);
  264. Log.LogDebug("UnifiedOrder", "UnifiedOrder response check sign success");
  265. //统一下单成功
  266. if (result.GetValue("return_code").ToString() == "SUCCESS" &&
  267. result.GetValue("result_code").ToString() == "SUCCESS")
  268. {
  269. Log.LogDebug("UnifiedOrder", "UnifiedOrder business success, result : " + result.ToXml());
  270. trade_status = TradeStatusEnum.SUCCESS;
  271. return result;
  272. }
  273. /******************************************************************
  274. * 剩下的都是接口调用成功,业务失败的情况
  275. * ****************************************************************/
  276. //1)业务结果明确失败
  277. if (result.GetValue("err_code").ToString() != "USERPAYING" &&
  278. result.GetValue("err_code").ToString() != "SYSTEMERROR")
  279. {
  280. Log.LogError("UnifiedOrder", "UnifiedOrder API interface call success, business failure, result : " + result.ToXml());
  281. return result;
  282. }
  283. return result;
  284. }
  285. }
  286. }