Refund.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. using System;
  2. using System.Security.Cryptography.X509Certificates;
  3. using Microsoft.Extensions.Logging;
  4. namespace Wechat.PayAPI
  5. {
  6. public class Refund
  7. {
  8. public static Microsoft.Extensions.Logging.ILogger Log = Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance;
  9. private const int MAX_REFUND_RETRY_TIMES = 5;
  10. /***
  11. * 申请退款完整业务流程逻辑
  12. * @param transaction_id 微信订单号(优先使用)
  13. * @param out_trade_no 商户订单号
  14. * @param total_fee 订单总金额
  15. * @param refund_fee 退款金额
  16. * @return 退款结果
  17. */
  18. public static WxPayData Run(string transaction_id, Gateway.Payment.Shared.PaymentOrder order, out Gateway.Payment.Shared.TradeStatusEnum trade_status)
  19. {
  20. // string out_trade_no, string total_fee, string refund_fee
  21. //set the default trade_status to PAYERROR
  22. trade_status = Gateway.Payment.Shared.TradeStatusEnum.PAYERROR;
  23. string out_refund_no = WxPayApi.GenerateOutTradeNo((Gateway.Payment.Shared.WxPayConfig)order.Config);
  24. Log.LogInformation("Refund", "Refund is processing, out_refund_no = " + out_refund_no);
  25. int retry_count = 0;
  26. bool use_unsettled_fund = true;
  27. while (true)
  28. {
  29. WxPayData result = RequestRefund(transaction_id,
  30. order.BillNumber,
  31. order.TotalAmount.ToString(),
  32. order.NetAmount.ToString(),
  33. out_refund_no,
  34. use_unsettled_fund,
  35. (Gateway.Payment.Shared.WxPayConfig)order.Config,
  36. (X509Certificate2)order.Certification);
  37. //如果提交退款接口调用失败,则抛异常
  38. if (!result.IsSet("return_code") || result.GetValue("return_code").ToString() == "FAIL")
  39. {
  40. string returnMsg = result.IsSet("return_msg") ? result.GetValue("return_msg").ToString() : "";
  41. Log.LogError("MicroPay", "Micropay API interface call failure, result : " + result.ToXml());
  42. throw new WxPayException("Micropay API interface call failure, return_msg : " + returnMsg);
  43. }
  44. //签名验证
  45. result.CheckSign((Gateway.Payment.Shared.WxPayConfig)order.Config);
  46. Log.LogDebug("MicroPay", "Micropay response check sign success");
  47. //退款成功
  48. if (result.GetValue("return_code").ToString() == "SUCCESS" &&
  49. result.GetValue("result_code").ToString() == "SUCCESS")
  50. {
  51. Log.LogDebug("MicroPay", "Micropay refund success, result : " + result.ToXml());
  52. trade_status = Gateway.Payment.Shared.TradeStatusEnum.SUCCESS;
  53. return result;
  54. }
  55. /******************************************************************
  56. * 剩下的都是接口调用成功,业务失败的情况
  57. * ****************************************************************/
  58. //1)业务结果明确失败
  59. if (result.GetValue("err_code").ToString() != "BIZERR_NEED_RETRY" &&
  60. result.GetValue("err_code").ToString() != "SYSTEMERROR" &&
  61. result.GetValue("err_code").ToString() != "NOTENOUGH")
  62. {
  63. Log.LogError("MicroPay", "micropay API interface call success, business failure, result : " + result.ToXml());
  64. return result;
  65. }
  66. //2) 使用相同退款单号重试
  67. if (result.GetValue("err_code").ToString() == "NOTENOUGH")
  68. {
  69. use_unsettled_fund = !use_unsettled_fund;
  70. Log.LogDebug("MicroPay", "Switched to use the other fund source for refund, use_unsettled_fund = " + use_unsettled_fund);
  71. }
  72. retry_count++;
  73. if (retry_count <= MAX_REFUND_RETRY_TIMES)
  74. {
  75. Log.LogDebug("MicroPay", "Micropay refund retry, retry_count : " + retry_count);
  76. }
  77. else
  78. {
  79. Log.LogError("MicroPay", "Micropay refund max retry times exceeded! Result : " + result.ToXml());
  80. return result;
  81. }
  82. }
  83. }
  84. private static WxPayData RequestRefund(
  85. string transaction_id,
  86. string out_trade_no,
  87. string total_fee,
  88. string refund_fee,
  89. string out_refund_no,
  90. bool use_unsettled_fund,
  91. Gateway.Payment.Shared.WxPayConfig config,
  92. X509Certificate2 certification
  93. )
  94. {
  95. if (config == null)
  96. {
  97. throw new WxPayException("微信支付,缺少WxPayConfig!");
  98. }
  99. WxPayData data = new WxPayData();
  100. if (!string.IsNullOrEmpty(transaction_id))//微信订单号存在的条件下,则已微信订单号为准
  101. {
  102. data.SetValue("transaction_id", transaction_id);
  103. }
  104. else//微信订单号不存在,才根据商户订单号去退款
  105. {
  106. data.SetValue("out_trade_no", out_trade_no);
  107. }
  108. data.SetValue("total_fee", Convert.ToInt32(Decimal.Parse(total_fee) * 100));//订单总金额
  109. data.SetValue("refund_fee", Convert.ToInt32(Decimal.Parse(refund_fee) * 100));//退款金额
  110. data.SetValue("out_refund_no", out_refund_no);//商户退款单号
  111. data.SetValue("op_user_id", config.MCHID);//操作员,默认为商户号
  112. if (use_unsettled_fund)
  113. {
  114. data.SetValue("refund_account", "REFUND_SOURCE_UNSETTLED_FUNDS");
  115. }
  116. else
  117. {
  118. data.SetValue("refund_account", "REFUND_SOURCE_RECHARGE_FUNDS");
  119. }
  120. WxPayData result = WxPayApi.Refund(data, config, certification);//提交退款申请给API,接收返回数据
  121. return result;
  122. }
  123. }
  124. }