Przeglądaj źródła

支付宝退款

smj_7038 3 lat temu
rodzic
commit
b1f289b07a

+ 10 - 0
AliPay.SDK/Response/AlipayTradeFastpayRefundQueryResponse.cs

@@ -43,5 +43,15 @@ namespace Aop.Api.Response
         /// </summary>
         [XmlElement("trade_no")]
         public string TradeNo { get; set; }
+
+        /// <summary>
+        /// 退款状态。枚举值:
+        /// REFUND_SUCCESS 退款处理成功;
+        /// 未返回该字段表示退款请求未收到或者退款失败;
+        /// 注:如果退款查询发起时间早于退款时间,或者间隔退款发起时间太短,可能出现退款查询时还没处理成功,后面又处理成功的情况,建议商户在退款发起后间隔10秒以上再发起退款查询请求。
+        /// </summary>
+        [XmlElement("refund_status")]
+        public string RefundStatus { get; set; }        
+
     }
 }

+ 56 - 1
Central.Control.WebApi/App_Data/Central.Control.WebApi.xml

@@ -262,6 +262,18 @@
             </summary>
             <returns></returns>
         </member>
+        <member name="M:Central.Control.WebApi.Controllers.OrderController.OrderRefund(System.String)">
+            <summary>
+            申请退款
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Central.Control.WebApi.Controllers.OrderController.OrderRefundLoop">
+            <summary>
+            轮询查询退款信息,并更新订单的退款记录
+            </summary>
+            <returns></returns>
+        </member>
         <member name="M:Central.Control.WebApi.Controllers.OrderController.PayTest">
             <summary>
             支付测试
@@ -1697,6 +1709,18 @@
             </summary>
             <returns></returns>
         </member>
+        <member name="M:Central.Control.WebApi.Service.Interface.IOrderService.OrderRefund(System.String)">
+            <summary>
+            申请退款
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Central.Control.WebApi.Service.Interface.IOrderService.OrderRefundLoop">
+            <summary>
+            轮询查询退款信息,并更新订单的退款记录
+            </summary>
+            <returns></returns>
+        </member>
         <member name="M:Central.Control.WebApi.Service.Interface.IOrderService.PayTest">
             <summary>
             支付测试
@@ -1883,13 +1907,44 @@
             </summary>
             <returns></returns>
         </member>
+        <member name="M:Central.Control.WebApi.Service.OrderService.OrderRefund(System.String)">
+            <summary>
+            申请退款
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Central.Control.WebApi.Service.OrderService.OrderRefundLoop">
+            <summary>
+            轮询查询退款信息,并更新订单的退款记录
+            </summary>
+            <returns></returns>
+        </member>
         <member name="M:Central.Control.WebApi.Service.OrderService.PayWriteBack(Central.Control.WebApi.Models.Request.PayWriteBackRequestDto)">
             <summary>
-            支付回写(雏形,需要根据实际需要接入支付宝或微信)
+            支付回写(回写到实际表中
             </summary>
             <param name="req"></param>
             <returns></returns>
         </member>
+        <member name="M:Central.Control.WebApi.Service.OrderService.AliPayRefund(System.String,System.String,System.Decimal,System.String)">
+            <summary>
+            支付宝退款
+            </summary>
+            <param name="orderId"></param>
+            <param name="paySerialId"></param>
+            <param name="refundAmount"></param>
+            <param name="refundReason"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Central.Control.WebApi.Service.OrderService.AliPayRefundQuery(System.String,System.String,System.String)">
+            <summary>
+            支付宝退款查询
+            </summary>
+            <param name="orderId"></param>
+            <param name="paySerialId"></param>
+            <param name="deviceId"></param>
+            <returns></returns>
+        </member>
         <member name="M:Central.Control.WebApi.Service.OrderService.PayTest">
             <summary>
             支付测试

+ 22 - 0
Central.Control.WebApi/Controllers/OrderController.cs

@@ -126,6 +126,28 @@ namespace Central.Control.WebApi.Controllers
             return responseMessage;
         }
 
+        /// <summary>
+        /// 申请退款
+        /// </summary>
+        /// <returns></returns>
+        [HttpPut]
+        [Route("orderrefund/{orderId}")]
+        public ApiResult OrderRefund(string orderId)
+        {
+            return _orderService.OrderRefund(orderId);
+        }
+
+        /// <summary>
+        /// 轮询查询退款信息,并更新订单的退款记录
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet]
+        [Route("orderrefundloop")]
+        public ApiResult OrderRefundLoop()
+        {
+            return _orderService.OrderRefundLoop();
+        }
+
         /// <summary>
         /// 支付测试
         /// </summary>

+ 12 - 0
Central.Control.WebApi/Service/Interface/IOrderService.cs

@@ -53,6 +53,18 @@ namespace Central.Control.WebApi.Service.Interface
         /// <returns></returns>
         string AliPayNotify();
 
+        /// <summary>
+        /// 申请退款
+        /// </summary>
+        /// <returns></returns>
+        ApiResult OrderRefund(string orderId);
+
+        /// <summary>
+        /// 轮询查询退款信息,并更新订单的退款记录
+        /// </summary>
+        /// <returns></returns>
+        ApiResult OrderRefundLoop();
+
         /// <summary>
         /// 支付测试
         /// </summary>

+ 198 - 1
Central.Control.WebApi/Service/OrderService.cs

@@ -14,6 +14,7 @@ using Com.Alipay;
 using Com.Alipay.Business;
 using Com.Alipay.Domain;
 using Com.Alipay.Model;
+using F2FPayDll.Domain;
 using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
@@ -459,7 +460,108 @@ namespace Central.Control.WebApi.Service
         }
 
         /// <summary>
-        /// 支付回写(雏形,需要根据实际需要接入支付宝或微信)
+        /// 申请退款
+        /// </summary>
+        /// <returns></returns>
+        public ApiResult OrderRefund(string orderId)
+        {
+            var order = _dbContent.Set<YW_Order>().FirstOrDefault(p => p.IsDelete == 0 && p.Id == orderId);
+
+            if (order.PayStatus != PayStatusEnum.RefundApply)
+            {
+                // 订单状态不可退款
+                return new ApiResult(ApiStatusCode.InvalidParameter, "当前订单状态不可退款");
+            }
+            ApiResult result = null;
+            switch (order.PayWay)
+            {
+                case PayWayEnum.WeChatPay:
+                    result = new ApiResult(ApiStatusCode.Forbidden, "微信支付暂未接入,无法发起退款");
+                    break;
+                case PayWayEnum.AliPay:
+                    // 支付宝退款
+                    result = AliPayRefund(order.Id, order.PaySerialId, order.Price, "客户发起退款");
+                    break;
+                default:
+                    result = new ApiResult(ApiStatusCode.Forbidden, "未知支付方式,无法在线自动退款");
+                    break;
+            }
+
+            if (result.IsSuccess)
+            {
+                // 修改订单状态
+                order.PayStatus = PayStatusEnum.Refunding;
+                order.OrderStatus = OrderStatusEnum.Cancel;
+                order.ModifyDT = DateTime.Now;
+                order.ModifyBY = "用户申请";
+                _dbContent.SaveChanges();
+                // 写入订单流程记录
+                _ywLogService.WriteOrderProcess(order.Id, order.DeviceId, OrderStatusEnum.Cancel, "申请退款", "用户申请");
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 轮询查询退款信息,并更新订单的退款记录
+        /// </summary>
+        /// <returns></returns>
+        public ApiResult OrderRefundLoop()
+        {
+            // 1、查询出所有退款中的订单
+            // 2、请求支付宝退款接口查询是否已退款成功
+            // 3、退款成功或失败时,回写到订单中
+            var modifyDt = DateTime.Now.AddMinutes(-2);// 查询2分钟以前的订单,因为退款需要一定时间,马上查询会返回失败
+            var order = _dbContent.Set<YW_Order>().Where(p => p.IsDelete == 0 && p.PayStatus == PayStatusEnum.RefundApply && p.ModifyDT < modifyDt).OrderBy(p => p.ModifyDT).FirstOrDefault();
+            if (order == null)
+            {
+                return new ApiResult(ApiStatusCode.InvalidParameter, "无退款中订单");
+            }
+            // 更新modifydt以便下次捞取别的订单
+            order.ModifyDT = DateTime.Now;
+
+            ApiResult result = null;
+            switch (order.PayWay)
+            {
+                case PayWayEnum.WeChatPay:
+                    result = new ApiResult(ApiStatusCode.Forbidden, "微信支付暂未接入,无法进行查询");
+                    break;
+                case PayWayEnum.AliPay:
+                    // 查询支付宝退款
+                    result = AliPayRefundQuery(order.Id, order.PaySerialId, order.DeviceId);
+                    break;
+                default:
+                    result = new ApiResult(ApiStatusCode.Forbidden, "未知支付方式,无法进行查询");
+                    break;
+            }
+
+            if (result.IsSuccess)
+            {
+                // 修改订单状态
+                order.PayStatus = PayStatusEnum.Refunded;
+                order.OrderStatus = OrderStatusEnum.Cancel;
+                order.ModifyDT = DateTime.Now;
+                order.ModifyBY = "用户申请";
+                // 写入订单流程记录
+                _ywLogService.WriteOrderProcess(order.Id, order.DeviceId, OrderStatusEnum.Cancel, "退款成功", "用户申请");
+            }
+            else
+            {
+                // 退款失败,将订单更新成错误,并记录订单日志
+                order.PayStatus = PayStatusEnum.Fail;
+                order.OrderStatus = OrderStatusEnum.Cancel;
+                order.ModifyDT = DateTime.Now;
+                order.ModifyBY = "用户申请";
+                // 写入订单日志
+                _ywLogService.WriteOrderLog(order.Id, "订单退款失败", result.Message, "用户申请");
+            }
+            _dbContent.SaveChanges();
+            return result;
+        }
+
+        #region privatemethods
+
+        /// <summary>
+        /// 支付回写(回写到实际表中)
         /// </summary>
         /// <param name="req"></param>
         /// <returns></returns>
@@ -524,6 +626,99 @@ namespace Central.Control.WebApi.Service
             return new ApiResult();
         }
 
+        /// <summary>
+        /// 支付宝退款
+        /// </summary>
+        /// <param name="orderId"></param>
+        /// <param name="paySerialId"></param>
+        /// <param name="refundAmount"></param>
+        /// <param name="refundReason"></param>
+        /// <returns></returns>
+        private ApiResult AliPayRefund(string orderId,string paySerialId,decimal refundAmount, string refundReason)
+        {
+            AlipayTradeRefundContentBuilder builder = new AlipayTradeRefundContentBuilder()
+            {
+                trade_no = paySerialId,
+                out_trade_no = orderId,
+                refund_amount = refundAmount.ToString(),
+                out_request_no = string.Empty,
+                refund_reason = refundReason,
+            };
+            ApiResult result = null;
+            // 支付宝退款
+            _log4NetHelper.Error($"[AliPayRefund]-tradeRefund-start:request:{JsonConvert.SerializeObject(builder)}");
+            AlipayF2FRefundResult refundResult = serviceClient.tradeRefund(builder);
+            _log4NetHelper.Error($"[AliPayRefund]-tradeRefund-end:request:{JsonConvert.SerializeObject(refundResult)}");
+
+            switch (refundResult.Status)
+            {
+                case ResultEnum.SUCCESS:
+                    // 退款成功-记录日志
+                    _ywLogService.WriteOrderLog(orderId, "申请退款", $"申请退款成功:退款金额{refundResult.response?.SendBackFee}", "用户申请");
+                    result = new ApiResult();
+                    break;
+                default:
+                    result = new ApiResult(ApiStatusCode.InternalServerError, "请求支付宝退款失败" + refundResult.response?.SubMsg);
+                    // 退款失败-记录订单日志
+                    _ywLogService.WriteOrderLog(orderId, "申请退款", $"申请退款失败:{refundResult.response?.SubMsg}", "用户申请");
+                    // 请求失败,记录log4net日志
+                    _log4NetHelper.Error($"[AliPayRefund]-tradeRefund失败:request:{JsonConvert.SerializeObject(builder)};response:{JsonConvert.SerializeObject(refundResult)}");
+                    break;
+            }
+            return result;
+        }
+
+        /// <summary>
+        /// 支付宝退款查询
+        /// </summary>
+        /// <param name="orderId"></param>
+        /// <param name="paySerialId"></param>
+        /// <param name="deviceId"></param>
+        /// <returns></returns>
+        private ApiResult AliPayRefundQuery(string orderId, string paySerialId, string deviceId)
+        {
+            var builder = new AlipayTradeFastpayRefundQueryContentBuilder()
+            {
+                trade_no = paySerialId,
+                out_trade_no = orderId,
+                out_request_no = deviceId
+            };
+            ApiResult result = null;
+            // 支付宝退款
+            _log4NetHelper.Error($"[AliPayRefund]-AliPayRefundQuery-start:request:{JsonConvert.SerializeObject(builder)}");
+            var refundQueryResult = serviceClient.tradeFastpayRefundQuery(builder);
+            _log4NetHelper.Error($"[AliPayRefund]-AliPayRefundQuery-end:request:{JsonConvert.SerializeObject(refundQueryResult)}");
+
+            switch (refundQueryResult.Status)
+            {
+                case ResultEnum.SUCCESS:
+                    string content = "成功";
+                    if (refundQueryResult.response.RefundStatus == "REFUND_SUCCESS")
+                    {
+                        // 退款成功
+                        result = new ApiResult();
+                        content = $"退款成功:退款金额{refundQueryResult.response?.RefundAmount}";
+                    }
+                    else
+                    {
+                        // 退款失败
+                        result = new ApiResult(ApiStatusCode.InternalServerError, "请求支付宝退款失败" + refundQueryResult.response?.SubMsg);
+                        content = $"退款失败:{refundQueryResult.response?.SubMsg}";
+                    }
+                    // 记录订单日志
+                    _ywLogService.WriteOrderLog(orderId, "退款查询", content, "用户申请");
+                    break;
+                default:
+                    result = new ApiResult(ApiStatusCode.InternalServerError, "请求支付宝退款失败" + refundQueryResult.response?.SubMsg);
+                    // 退款失败-记录订单日志
+                    _ywLogService.WriteOrderLog(orderId, "退款查询", $"退款失败:{refundQueryResult.response?.SubMsg}", "用户申请");
+                    // 请求失败,记录log4net日志
+                    _log4NetHelper.Error($"[AliPayRefund]-AliPayRefundQuery失败:request:{JsonConvert.SerializeObject(builder)};response:{JsonConvert.SerializeObject(refundQueryResult)}");
+                    break;
+            }
+            return result;
+        }
+
         /// <summary>
         /// 支付测试
         /// </summary>
@@ -684,5 +879,7 @@ namespace Central.Control.WebApi.Service
             return ret;
         }
 
+        #endregion
+
     }
 }

+ 47 - 0
F2FPayDll/Business/AlipayF2FRefundQueryResult.cs

@@ -0,0 +1,47 @@
+using Aop.Api.Response;
+using Com.Alipay.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace F2FPayDll.Business
+{
+    public class AlipayF2FRefundQueryResult
+    {
+        public AlipayF2FRefundQueryResult()
+        {
+            //
+            // TODO: 在此处添加构造函数逻辑
+            //
+        }
+
+        public AlipayTradeFastpayRefundQueryResponse response { get; set; }
+
+        public ResultEnum Status
+        {
+            get
+            {
+                if (response != null)
+                {
+                    if (response.Code == ResultCode.SUCCESS)
+                    {
+                        return ResultEnum.SUCCESS;
+                    }
+                    if (response.Code == ResultCode.ERROR)
+                    {
+                        return ResultEnum.UNKNOWN;
+                    }
+                    else
+                        return ResultEnum.FAILED;
+                }
+                else
+                {
+                    return ResultEnum.UNKNOWN;
+                }
+            }
+        }
+
+    }
+}

+ 25 - 2
F2FPayDll/Business/AlipayTradeImpl.cs

@@ -7,7 +7,8 @@ using System.Threading;
 using Com.Alipay.Model;
 using Com.Alipay.Business;
 using System;
-
+using F2FPayDll.Domain;
+using F2FPayDll.Business;
 
 namespace Com.Alipay
 {
@@ -163,12 +164,34 @@ namespace Com.Alipay
             }
         }
 
+        /// <summary>
+        /// 退款查询
+        /// </summary>
+        /// <param name="builder"></param>
+        /// <returns></returns>
+        public AlipayF2FRefundQueryResult tradeFastpayRefundQuery(AlipayTradeFastpayRefundQueryContentBuilder builder)
+        {
+            AlipayF2FRefundQueryResult refundResult = new AlipayF2FRefundQueryResult();
+            try
+            {
+                AlipayTradeFastpayRefundQueryRequest refundRequest = new AlipayTradeFastpayRefundQueryRequest();
+                refundRequest.BizContent = builder.BuildJson();
+                refundResult.response = client.Execute(refundRequest);
+                return refundResult;
+            }
+            catch
+            {
+                refundResult.response = null;
+                return refundResult;
+            }
+        }
+
+
         public AlipayF2FPrecreateResult tradePrecreate(AlipayTradePrecreateContentBuilder builder)
         {               
             AlipayF2FPrecreateResult payResult = new AlipayF2FPrecreateResult();
             try
             {
-
                 AlipayTradePrecreateRequest payRequest = new AlipayTradePrecreateRequest();
                 payRequest.BizContent = builder.BuildJson();
                 

+ 9 - 0
F2FPayDll/Business/IAlipayTradeService.cs

@@ -1,6 +1,8 @@
 using Aop.Api.Response;
 using Com.Alipay.Business;
 using Com.Alipay.Domain;
+using F2FPayDll.Business;
+using F2FPayDll.Domain;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -23,6 +25,13 @@ namespace Com.Alipay
         // 当面付2.0交易退货
          AlipayF2FRefundResult tradeRefund(AlipayTradeRefundContentBuilder builder);
 
+        /// <summary>
+        /// 退款查询
+        /// </summary>
+        /// <param name="builder"></param>
+        /// <returns></returns>
+        AlipayF2FRefundQueryResult tradeFastpayRefundQuery(AlipayTradeFastpayRefundQueryContentBuilder builder);
+
         // 当面付2.0预下单
          AlipayF2FPrecreateResult tradePrecreate(AlipayTradePrecreateContentBuilder builder);
          AlipayF2FPrecreateResult tradePrecreate(AlipayTradePrecreateContentBuilder builder, string notify_url);

+ 42 - 0
F2FPayDll/Domain/AlipayTradeFastpayRefundQueryContentBuilder.cs

@@ -0,0 +1,42 @@
+using Com.Alipay.Domain;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace F2FPayDll.Domain
+{
+    public class AlipayTradeFastpayRefundQueryContentBuilder : JsonBuilder
+    {
+        /// <summary>
+        /// 支付宝交易号。 和商户订单号不能同时为空(二选一)
+        /// </summary>
+        public string trade_no { get; set; }
+        /// <summary>
+        /// 商户订单号。(二选一)
+        /// 订单支付时传入的商户订单号,和支付宝交易号不能同时为空。 trade_no,out_trade_no如果同时存在优先取trade_no
+        /// </summary>
+        public string out_trade_no { get; set; }
+        /// <summary>
+        /// 退款请求号。(必传)
+        /// 请求退款接口时,传入的退款请求号,如果在退款请求时未传入,则该值为创建交易时的商户订单号。
+        /// </summary>
+        public string out_request_no { get; set; }
+
+
+        public AlipayTradeFastpayRefundQueryContentBuilder()
+        {
+            //
+            // TODO: 在此处添加构造函数逻辑
+            //
+        }
+
+        public override bool Validate()
+        {
+            throw new NotImplementedException();
+        }
+
+
+    }
+}

+ 2 - 0
F2FPayDll/F2FPayDll.csproj

@@ -47,6 +47,7 @@
     <Compile Include="Business\AlipayF2FPayResult.cs" />
     <Compile Include="Business\AlipayF2FPrepayResult.cs" />
     <Compile Include="Business\AlipayF2FQueryResult.cs" />
+    <Compile Include="Business\AlipayF2FRefundQueryResult.cs" />
     <Compile Include="Business\AlipayF2FRefundResult.cs" />
     <Compile Include="Business\AlipayMonitorImpl.cs" />
     <Compile Include="Business\Alipaynotify.cs" />
@@ -57,6 +58,7 @@
     <Compile Include="Business\IAlipayMonitor.cs" />
     <Compile Include="Business\IAlipayTradeService.cs" />
     <Compile Include="Domain\AlipayMonitorContentBuilder.cs" />
+    <Compile Include="Domain\AlipayTradeFastpayRefundQueryContentBuilder.cs" />
     <Compile Include="Domain\AlipayTradePayContentBuilder.cs" />
     <Compile Include="Domain\AlipayTradePrecreateContentBuilder.cs" />
     <Compile Include="Domain\AlipayTradeQueryCententBuilder.cs" />