MicroPay.cs 14 KB

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