MicroPay.cs 14 KB

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