浏览代码

实现 微信支付APIv3-基础支付-退款 (#108)

* [WeChatPay.V3] 新增 退款申请

* [WeChatPay.V3] 新增 查询单笔退款

* [WeChatPay.V3] 新增 退款结果通知API

* [WebApplicationSample] 新增 WeChatPay.V3 退款申请
Roc 4 年之前
父节点
当前提交
f97d877faf
共有 22 个文件被更改,包括 962 次插入24 次删除
  1. 1 1
      samples/WebApplicationSample/Controllers/AlipayNotifyController.cs
  2. 0 7
      samples/WebApplicationSample/Controllers/WeChatPayNotifyController.cs
  3. 34 0
      samples/WebApplicationSample/Controllers/WeChatPayV3Controller.cs
  4. 26 5
      samples/WebApplicationSample/Controllers/WeChatPayV3NotifyController.cs
  5. 28 0
      samples/WebApplicationSample/Models/WeChatPayViewModel.cs
  6. 2 1
      samples/WebApplicationSample/Program.cs
  7. 0 3
      samples/WebApplicationSample/Startup.cs
  8. 6 0
      samples/WebApplicationSample/Views/WeChatPayV3/Index.cshtml
  9. 57 0
      samples/WebApplicationSample/Views/WeChatPayV3/Refund.cshtml
  10. 7 7
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/NotifyCiphertext.cs
  11. 34 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundAmount.cs
  12. 74 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundAmountResponse.cs
  13. 58 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundGoodsDetail.cs
  14. 42 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundNotifyAmount.cs
  15. 62 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundPromotionDetail.cs
  16. 81 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/WeChatPayRefundDomesticRefundsBodyModel.cs
  17. 96 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Notify/WeChatPayRefundDomesticRefundsNotify.cs
  18. 40 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Request/WeChatPayRefundDomesticRefundsOutRefundNoRequest.cs
  19. 33 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Request/WeChatPayRefundDomesticRefundsRequest.cs
  20. 129 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Response/WeChatPayRefundDomesticRefundsOutRefundNoResponse.cs
  21. 129 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Response/WeChatPayRefundDomesticRefundsResponse.cs
  22. 23 0
      src/Essensoft.AspNetCore.Payment.WeChatPay/V3/WeChatPayRefundStatus.cs

+ 1 - 1
samples/WebApplicationSample/Controllers/AlipayNotifyController.cs

@@ -96,7 +96,7 @@ namespace WebApplicationSample.Controllers
                         return AlipayNotifyResult.Failure;
                         return AlipayNotifyResult.Failure;
                 }
                 }
             }
             }
-            catch(AlipayException ex)
+            catch (AlipayException ex)
             {
             {
                 Console.WriteLine("出现异常: " + ex.Message);
                 Console.WriteLine("出现异常: " + ex.Message);
                 return AlipayNotifyResult.Failure;
                 return AlipayNotifyResult.Failure;

+ 0 - 7
samples/WebApplicationSample/Controllers/WeChatPayNotifyController.cs

@@ -1,10 +1,8 @@
 using System;
 using System;
-using System.IO;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Essensoft.AspNetCore.Payment.WeChatPay;
 using Essensoft.AspNetCore.Payment.WeChatPay;
 using Essensoft.AspNetCore.Payment.WeChatPay.V2;
 using Essensoft.AspNetCore.Payment.WeChatPay.V2;
 using Essensoft.AspNetCore.Payment.WeChatPay.V2.Notify;
 using Essensoft.AspNetCore.Payment.WeChatPay.V2.Notify;
-using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using Microsoft.Extensions.Options;
 
 
@@ -31,9 +29,6 @@ namespace WebApplicationSample.Controllers
         {
         {
             try
             try
             {
             {
-                Request.EnableBuffering();
-
-                Request.Body.Seek(0, SeekOrigin.Begin);
                 var notify = await _client.ExecuteAsync<WeChatPayUnifiedOrderNotify>(Request, _optionsAccessor.Value);
                 var notify = await _client.ExecuteAsync<WeChatPayUnifiedOrderNotify>(Request, _optionsAccessor.Value);
                 if (notify.ReturnCode == WeChatPayCode.Success)
                 if (notify.ReturnCode == WeChatPayCode.Success)
                 {
                 {
@@ -63,8 +58,6 @@ namespace WebApplicationSample.Controllers
         {
         {
             try
             try
             {
             {
-                Request.EnableBuffering();
-
                 var notify = await _client.ExecuteAsync<WeChatPayRefundNotify>(Request, _optionsAccessor.Value);
                 var notify = await _client.ExecuteAsync<WeChatPayRefundNotify>(Request, _optionsAccessor.Value);
                 if (notify.ReturnCode == WeChatPayCode.Success)
                 if (notify.ReturnCode == WeChatPayCode.Success)
                 {
                 {

+ 34 - 0
samples/WebApplicationSample/Controllers/WeChatPayV3Controller.cs

@@ -417,5 +417,39 @@ namespace WebApplicationSample.Controllers
             ViewData["response"] = response.Body;
             ViewData["response"] = response.Body;
             return View();
             return View();
         }
         }
+
+        /// <summary>
+        /// 退款申请
+        /// </summary>
+        [HttpGet]
+        public IActionResult Refund()
+        {
+            return View();
+        }
+
+        /// <summary>
+        /// 退款申请
+        /// </summary>
+        /// <param name="viewModel"></param>
+        [HttpPost]
+        public async Task<IActionResult> Refund(WeChatPayV3RefundViewModel viewModel)
+        {
+            var model = new WeChatPayRefundDomesticRefundsBodyModel()
+            {
+                TransactionId = viewModel.TransactionId,
+                OutTradeNo = viewModel.OutTradeNo,
+                OutRefundNo = viewModel.OutRefundNo,
+                NotifyUrl = viewModel.NotifyUrl,
+                Amount = new RefundAmount { Refund = viewModel.RefundAmount, Total = viewModel.TotalAmount, Currency = viewModel.Currency }
+            };
+
+            var request = new WeChatPayRefundDomesticRefundsRequest();
+            request.SetBodyModel(model);
+
+            var response = await _client.ExecuteAsync(request, _optionsAccessor.Value);
+
+            ViewData["response"] = response.Body;
+            return View();
+        }
     }
     }
 }
 }

+ 26 - 5
samples/WebApplicationSample/Controllers/WeChatPayV3NotifyController.cs

@@ -1,10 +1,8 @@
 using System;
 using System;
-using System.IO;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using Essensoft.AspNetCore.Payment.WeChatPay;
 using Essensoft.AspNetCore.Payment.WeChatPay;
 using Essensoft.AspNetCore.Payment.WeChatPay.V3;
 using Essensoft.AspNetCore.Payment.WeChatPay.V3;
 using Essensoft.AspNetCore.Payment.WeChatPay.V3.Notify;
 using Essensoft.AspNetCore.Payment.WeChatPay.V3.Notify;
-using Microsoft.AspNetCore.Http;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.AspNetCore.Mvc;
 using Microsoft.Extensions.Options;
 using Microsoft.Extensions.Options;
 
 
@@ -31,9 +29,6 @@ namespace WebApplicationSample.Controllers
         {
         {
             try
             try
             {
             {
-                Request.EnableBuffering();
-
-                Request.Body.Seek(0, SeekOrigin.Begin);
                 var notify = await _client.ExecuteAsync<WeChatPayTransactionsNotify>(Request, _optionsAccessor.Value);
                 var notify = await _client.ExecuteAsync<WeChatPayTransactionsNotify>(Request, _optionsAccessor.Value);
                 if (notify.TradeState == WeChatPayTradeState.Success)
                 if (notify.TradeState == WeChatPayTradeState.Success)
                 {
                 {
@@ -49,5 +44,31 @@ namespace WebApplicationSample.Controllers
                 return WeChatPayNotifyResult.Failure;
                 return WeChatPayNotifyResult.Failure;
             }
             }
         }
         }
+
+
+        /// <summary>
+        /// 退款结果通知
+        /// </summary>
+        [Route("refund")]
+        [HttpPost]
+        public async Task<IActionResult> Refund()
+        {
+            try
+            {
+                var notify = await _client.ExecuteAsync<WeChatPayRefundDomesticRefundsNotify>(Request, _optionsAccessor.Value);
+                if (notify.RefundStatus == WeChatPayRefundStatus.Success)
+                {
+                    Console.WriteLine("OutTradeNo: " + notify.OutTradeNo);
+                    return WeChatPayNotifyResult.Success;
+                }
+
+                return WeChatPayNotifyResult.Failure;
+            }
+            catch (WeChatPayException ex)
+            {
+                Console.WriteLine("出现异常: " + ex.Message);
+                return WeChatPayNotifyResult.Failure;
+            }
+        }
     }
     }
 }
 }

+ 28 - 0
samples/WebApplicationSample/Models/WeChatPayViewModel.cs

@@ -357,4 +357,32 @@ namespace WebApplicationSample.Models
         [Display(Name = "receivers")]
         [Display(Name = "receivers")]
         public string Receivers { get; set; }
         public string Receivers { get; set; }
     }
     }
+
+    public class WeChatPayV3RefundViewModel
+    {
+        [Required]
+        [Display(Name = "out_refund_no")]
+        public string OutRefundNo { get; set; }
+
+        [Display(Name = "transaction_id")]
+        public string TransactionId { get; set; }
+
+        [Display(Name = "out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        [Display(Name = "notify_url")]
+        public string NotifyUrl { get; set; }
+
+        [Required]
+        [Display(Name = "amount.refund")]
+        public int RefundAmount { get; set; }
+
+        [Required]
+        [Display(Name = "amount.total")]
+        public int TotalAmount { get; set; }
+
+        [Required]
+        [Display(Name = "currency")]
+        public string Currency { get; set; }
+    }
 }
 }

+ 2 - 1
samples/WebApplicationSample/Program.cs

@@ -14,7 +14,8 @@ namespace WebApplicationSample
             Host.CreateDefaultBuilder(args)
             Host.CreateDefaultBuilder(args)
                 .ConfigureWebHostDefaults(webBuilder =>
                 .ConfigureWebHostDefaults(webBuilder =>
                 {
                 {
-                    webBuilder.UseStartup<Startup>();
+                    webBuilder.UseStartup<Startup>()
+                              .UseUrls("http://*:5000"); // 默认监听 5000 端口。
                 });
                 });
     }
     }
 }
 }

+ 0 - 3
samples/WebApplicationSample/Startup.cs

@@ -41,10 +41,7 @@ namespace WebApplicationSample
             else
             else
             {
             {
                 app.UseExceptionHandler("/Home/Error");
                 app.UseExceptionHandler("/Home/Error");
-                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
-                app.UseHsts();
             }
             }
-            app.UseHttpsRedirection();
             app.UseStaticFiles();
             app.UseStaticFiles();
 
 
             app.UseRouting();
             app.UseRouting();

+ 6 - 0
samples/WebApplicationSample/Views/WeChatPayV3/Index.cshtml

@@ -84,5 +84,11 @@
             <td><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/bill/chapter3_3.shtml" target="_blank">https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx</a></td>
             <td><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wxpay/pay/bill/chapter3_3.shtml" target="_blank">https://api.mch.weixin.qq.com/v3/billdownload/file?token=xxx</a></td>
             <td><a asp-controller="WeChatPayV3" asp-action="BillDownload">立即测试</a></td>
             <td><a asp-controller="WeChatPayV3" asp-action="BillDownload">立即测试</a></td>
         </tr>
         </tr>
+        <tr>
+            <th scope="row">12</th>
+            <td>退款申请</td>
+            <td><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml" target="_blank">https://api.mch.weixin.qq.com/v3/refund/domestic/refunds</a></td>
+            <td><a asp-controller="WeChatPayV3" asp-action="Refund">立即测试</a></td>
+        </tr>
     </tbody>
     </tbody>
 </table>
 </table>

+ 57 - 0
samples/WebApplicationSample/Views/WeChatPayV3/Refund.cshtml

@@ -0,0 +1,57 @@
+@model WeChatPayV3RefundViewModel
+@{
+    ViewData["Title"] = "申请退款";
+}
+<nav aria-label="breadcrumb">
+    <ol class="breadcrumb">
+        <li class="breadcrumb-item"><a asp-controller="WeChatPayV3" asp-action="Index">微信支付</a></li>
+        <li class="breadcrumb-item active" aria-current="page">@ViewData["Title"]</li>
+    </ol>
+</nav>
+<br />
+<div class="card">
+    <div class="card-body">
+        <form asp-controller="WeChatPayV3" asp-action="Refund">
+            <div asp-validation-summary="All" class="text-danger"></div>
+            <div class="form-group">
+                <label asp-for="OutRefundNo"></label>
+                <input type="text" class="form-control" asp-for="OutRefundNo" value="@DateTime.Now.ToString("yyyyMMddHHmmssfff")" />
+            </div>
+            <div class="form-group">
+                <label asp-for="TransactionId"></label>
+                <input type="text" class="form-control" asp-for="TransactionId" />
+            </div>
+            <div class="form-group">
+                <label asp-for="OutTradeNo"></label>
+                <input type="text" class="form-control" asp-for="OutTradeNo" />
+            </div>
+            <div class="form-group">
+                <label asp-for="NotifyUrl"></label>
+                <input type="text" class="form-control" asp-for="NotifyUrl" value="http://domain.com/wechatpay/v3/notify/refund">
+            </div>
+            <div class="form-group">
+                <label asp-for="RefundAmount"></label>
+                <input type="text" class="form-control" asp-for="RefundAmount" value="1" />
+            </div>
+            <div class="form-group">
+                <label asp-for="TotalAmount"></label>
+                <input type="text" class="form-control" asp-for="TotalAmount" value="1" />
+            </div>
+            <div class="form-group">
+                <label asp-for="Currency"></label>
+                <input type="text" class="form-control" asp-for="Currency" value="CNY" />
+            </div>
+            <button type="submit" class="btn btn-primary">提交请求</button>
+        </form>
+        <hr />
+        <form class="form-horizontal">
+            <div class="form-group">
+                <label>Response:</label>
+                <textarea class="form-control" rows="10">@ViewData["response"]</textarea>
+            </div>
+        </form>
+    </div>
+</div>
+@section Scripts {
+    @{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
+}

+ 7 - 7
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/NotifyCiphertext.cs

@@ -31,6 +31,13 @@ namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
         [JsonPropertyName("event_type")]
         [JsonPropertyName("event_type")]
         public string EventType { get; set; }
         public string EventType { get; set; }
 
 
+        /// <summary>
+        /// 概要
+        /// 示例值:支付成功
+        /// </summary>
+        [JsonPropertyName("summary")]
+        public string Summary { get; set; }
+
         /// <summary>
         /// <summary>
         /// 通知数据类型
         /// 通知数据类型
         /// 通知的资源数据类型,支付成功通知为encrypt-resource
         /// 通知的资源数据类型,支付成功通知为encrypt-resource
@@ -46,12 +53,5 @@ namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
         /// </summary>
         /// </summary>
         [JsonPropertyName("resource")]
         [JsonPropertyName("resource")]
         public Resource Resource { get; set; }
         public Resource Resource { get; set; }
-
-        /// <summary>
-        /// 概要
-        /// 示例值:支付成功
-        /// </summary>
-        [JsonPropertyName("summary")]
-        public string Summary { get; set; }
     }
     }
 }
 }

+ 34 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundAmount.cs

@@ -0,0 +1,34 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
+{
+    /// <summary>
+    /// 退款金额信息
+    /// </summary>
+    public class RefundAmount : WeChatPayObject
+    {
+        /// <summary>
+        /// 退款金额
+        /// 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额。
+        /// 示例值:888
+        /// </summary>
+        [JsonPropertyName("refund")]
+        public int Refund { get; set; }
+
+        /// <summary>
+        /// 原订单金额
+        /// 原支付交易的订单总金额,币种的最小单位,只能为整数。
+        /// 示例值:888
+        /// </summary>
+        [JsonPropertyName("total")]
+        public int Total { get; set; }
+
+        /// <summary>
+        /// 退款币种
+        /// 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
+        /// 示例值:CNY
+        /// </summary>
+        [JsonPropertyName("currency")]
+        public string Currency { get; set; }
+    }
+}

+ 74 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundAmountResponse.cs

@@ -0,0 +1,74 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
+{
+    /// <summary>
+    /// 退款金额信息
+    /// </summary>
+    public class RefundAmountResponse : WeChatPayObject
+    {
+        /// <summary>
+        /// 订单金额
+        /// 订单总金额,单位为分。
+        /// 示例值:100
+        /// </summary>
+        [JsonPropertyName("total")]
+        public int Total { get; set; }
+
+        /// <summary>
+        /// 退款金额
+        /// 退款标价金额,单位为分,可以做部分退款。
+        /// 示例值:100
+        /// </summary>
+        [JsonPropertyName("refund")]
+        public int Refund { get; set; }
+
+        /// <summary>
+        /// 用户支付金额
+        /// 现金支付金额,单位为分,只能为整数。
+        /// 示例值:90
+        /// </summary>
+        [JsonPropertyName("payer_total")]
+        public int PayerTotal { get; set; }
+
+        /// <summary>
+        /// 用户退款金额
+        /// 退款给用户的金额,不包含所有优惠券金额。
+        /// 示例值:90
+        /// </summary>
+        [JsonPropertyName("payer_refund")]
+        public int PayerRefund { get; set; }
+
+        /// <summary>
+        /// 应结退款金额
+        /// 去掉非充值代金券退款金额后的退款金额,单位为分,退款金额=申请退款金额-非充值代金券退款金额,退款金额<=申请退款金额。
+        /// 示例值:100
+        /// </summary>
+        [JsonPropertyName("settlement_refund")]
+        public int SettlementRefund { get; set; }
+
+        /// <summary>
+        /// 应结订单金额
+        /// 应结订单金额=订单金额-免充值代金券金额,应结订单金额<=订单金额,单位为分。
+        /// 示例值:100
+        /// </summary>
+        [JsonPropertyName("settlement_total")]
+        public int SettlementTotal { get; set; }
+
+        /// <summary>
+        /// 优惠退款金额
+        /// 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为现金,说明详见代金券或立减优惠,单位为分。
+        /// 示例值:10
+        /// </summary>
+        [JsonPropertyName("discount_refund")]
+        public int DiscountRefund { get; set; }
+
+        /// <summary>
+        /// 退款币种
+        /// 符合ISO 4217标准的三位字母代码,目前只支持人民币:CNY。
+        /// 示例值:CNY
+        /// </summary>
+        [JsonPropertyName("currency")]
+        public string Currency { get; set; }
+    }
+}

+ 58 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundGoodsDetail.cs

@@ -0,0 +1,58 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
+{
+    /// <summary>
+    /// 退款商品
+    /// </summary>
+    public class RefundGoodsDetail : WeChatPayObject
+    {
+        /// <summary>
+        /// 商户侧商品编码
+        /// 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("merchant_goods_id")]
+        public string MerchantGoodsId { get; set; }
+
+        /// <summary>
+        /// 微信侧商品编码
+        /// 微信支付定义的统一商品编号(没有可不传)
+        /// 示例值:1001
+        /// </summary>
+        [JsonPropertyName("wechatpay_goods_id")]
+        public string WeChatPayGoodsId { get; set; }
+
+        /// <summary>
+        /// 商品名称
+        /// 商品的实际名称
+        /// 示例值:iPhone6s 16G
+        /// </summary>
+        [JsonPropertyName("goods_name")]
+        public string GoodsName { get; set; }
+
+        /// <summary>
+        /// 商品单价
+        /// 商品单价金额,单位为分
+        /// 示例值:528800
+        /// </summary>
+        [JsonPropertyName("unit_price")]
+        public int UnitPrice { get; set; }
+
+        /// <summary>
+        /// 商品退款金额
+        /// 商品退款金额,单位为分。
+        /// 示例值:528800
+        /// </summary>
+        [JsonPropertyName("refund_amount")]
+        public int RefundAmount { get; set; }
+
+        /// <summary>
+        /// 商品退货数量
+        /// 单品的退款数量,单位为分。
+        /// 示例值:1
+        /// </summary>
+        [JsonPropertyName("refund_quantity")]
+        public int RefundQuantity { get; set; }
+    }
+}

+ 42 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundNotifyAmount.cs

@@ -0,0 +1,42 @@
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
+{
+    /// <summary>
+    /// 金额信息
+    /// </summary>
+    public class RefundNotifyAmount : WeChatPayObject
+    {
+        /// <summary>
+        /// 订单金额
+        /// 订单总金额,单位为分,只能为整数,详见支付金额
+        /// 示例值:999
+        /// </summary>
+        [JsonPropertyName("total")]
+        public int Total { get; set; }
+
+        /// <summary>
+        /// 退款金额
+        /// 退款金额,币种的最小单位,只能为整数,不能超过原订单支付金额,如果有使用券,后台会按比例退。
+        /// 示例值:999
+        /// </summary>
+        [JsonPropertyName("refund")]
+        public int Refund { get; set; }
+
+        /// <summary>
+        /// 用户支付金额
+        /// 用户实际支付金额,单位为分,只能为整数,详见支付金额
+        /// 示例值:999
+        /// </summary>
+        [JsonPropertyName("payer_total")]
+        public int PayerTotal { get; set; }
+
+        /// <summary>
+        /// 用户退款金额
+        /// 退款给用户的金额,不包含所有优惠券金额
+        /// 示例值:999
+        /// </summary>
+        [JsonPropertyName("payer_refund")]
+        public int PayerRefund { get; set; }
+    }
+}

+ 62 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/RefundPromotionDetail.cs

@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
+{
+    /// <summary>
+    /// 优惠退款信息
+    /// </summary>
+    public class RefundPromotionDetail : WeChatPayObject
+    {
+        /// <summary>
+        /// 券ID
+        /// 券或者立减优惠id。
+        /// 示例值:109519
+        /// </summary>
+        [JsonPropertyName("promotion_id")]
+        public string PromotionId { get; set; }
+
+        /// <summary>
+        /// 优惠范围
+        /// GLOBAL:全场代金券
+        /// SINGLE:单品优惠
+        /// 示例值:SINGLE
+        /// </summary>
+        [JsonPropertyName("scope")]
+        public string Scope { get; set; }
+
+        /// <summary>
+        /// 优惠类型
+        /// 枚举值:
+        /// COUPON:代金券,需要走结算资金的充值型代金券
+        /// DISCOUNT:优惠券,不走结算资金的免充值型优惠券
+        /// 示例值:DISCOUNT
+        /// </summary>
+        [JsonPropertyName("type")]
+
+        public string Type { get; set; }
+
+        /// <summary>
+        /// 优惠券面额
+        /// 用户享受优惠的金额(优惠券面额=微信出资金额+商家出资金额+其他出资方金额 ),单位为分。
+        /// 示例值:5
+        /// </summary>
+        [JsonPropertyName("amount")]
+        public int Amount { get; set; }
+
+        /// <summary>
+        /// 优惠退款金额
+        /// 优惠退款金额<=退款金额,退款金额-代金券或立减优惠退款金额为用户支付的现金,说明详见代金券或立减优惠,单位为分。
+        /// 示例值:100
+        /// </summary>
+        [JsonPropertyName("refund_amount")]
+        public int RefundAmount { get; set; }
+
+        /// <summary>
+        /// 商品列表
+        /// 优惠商品发生退款时返回商品信息。
+        /// </summary>
+        [JsonPropertyName("goods_detail")]
+        public List<RefundGoodsDetail> GoodsDetail { get; set; }
+    }
+}

+ 81 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Domain/WeChatPayRefundDomesticRefundsBodyModel.cs

@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain
+{
+    /// <summary>
+    /// 基础支付 - 退款申请 - 请求JSON参数
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml">JSAPI支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_9.shtml">APP支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_9.shtml">H5支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_9.shtml">Native支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_9.shtml">小程序支付 - 退款申请</a></para>
+    /// 最新更新时间:2021.01.15
+    /// </summary>
+    public class WeChatPayRefundDomesticRefundsBodyModel : WeChatPayObject
+    {
+        /// <summary>
+        /// 微信支付订单号
+        /// 原支付交易对应的微信订单号。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("transaction_id")]
+        public string TransactionId { get; set; }
+
+        /// <summary>
+        /// 商户订单号
+        /// 原支付交易对应的商户订单号。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        /// <summary>
+        /// 商户退款单号
+        /// 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_refund_no")]
+        public string OutRefundNo { get; set; }
+
+        /// <summary>
+        /// 退款原因
+        /// 若商户传入,会在下发给用户的退款消息中体现退款原因。
+        /// 示例值:商品已售完
+        /// </summary>
+        [JsonPropertyName("reason")]
+        public string Reason { get; set; }
+
+        /// <summary>
+        /// 退款结果回调url
+        /// 异步接收微信支付退款结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。 如果参数中传了notify_url,则商户平台上配置的回调地址将不会生效,优先回调当前传的这个地址。
+        /// 示例值:https://weixin.qq.com
+        /// </summary>
+        [JsonPropertyName("notify_url")]
+        public string NotifyUrl { get; set; }
+
+        /// <summary>
+        /// 退款资金来源
+        /// 若传递此参数则使用对应的资金账户退款,否则默认使用未结算资金退款(仅对老资金流商户适用)。
+        /// 枚举值:
+        /// AVAILABLE:可用余额账户
+        /// 示例值:AVAILABLE
+        /// </summary>
+        [JsonPropertyName("funds_account")]
+        public string FundsAccount { get; set; }
+
+        /// <summary>
+        /// 金额信息
+        /// 订单金额信息。
+        /// </summary>
+        [JsonPropertyName("amount")]
+        public RefundAmount Amount { get; set; }
+
+        /// <summary>
+        /// 退款商品
+        /// 指定商品退款需要传此参数,其他场景无需传递。
+        /// </summary>
+        [JsonPropertyName("goods_detail")]
+        public List<RefundGoodsDetail> GoodsDetail { get; set; }
+    }
+}

+ 96 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Notify/WeChatPayRefundDomesticRefundsNotify.cs

@@ -0,0 +1,96 @@
+using System.Text.Json.Serialization;
+using Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Notify
+{
+    /// <summary>
+    /// 基础支付 - 退款结果通知API
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_11.shtml">JSAPI支付 - 退款结果通知API</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_11.shtml">APP支付 - 退款结果通知API</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_11.shtml">H5支付 - 退款结果通知API</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_11.shtml">Native支付 - 退款结果通知API</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_11.shtml">小程序支付 - 退款结果通知API</a></para>
+    /// 最新更新时间:2021.01.15
+    /// </summary>
+    public class WeChatPayRefundDomesticRefundsNotify : WeChatPayNotify
+    {
+        /// <summary>
+        /// 直连商户号
+        /// 直连商户的商户号,由微信支付生成并下发。
+        /// 示例值:1230000109
+        /// </summary>
+        [JsonPropertyName("mchid")]
+        public string MchId { get; set; }
+
+        /// <summary>
+        /// 商户订单号
+        /// 返回的商户订单号
+        /// 示例值: 1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        /// <summary>
+        /// 微信订单号
+        /// 微信支付订单号
+        /// 示例值: 1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("transaction_id")]
+        public string TransactionId { get; set; }
+
+        /// <summary>
+        /// 商户退款单号
+        /// 商户退款单号
+        /// 示例值: 1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_refund_no")]
+        public string OutRefundNo { get; set; }
+
+        /// <summary>
+        /// 微信退款单号
+        /// 微信退款单号
+        /// 示例值: 1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("refund_id")]
+        public string RefundId { get; set; }
+
+        /// <summary>
+        /// 退款状态
+        /// 退款状态,枚举值:
+        /// SUCCESS:退款成功
+        /// CLOSE:退款关闭
+        /// ABNORMAL:退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【服务商平台—>交易中心】,手动处理此笔退款
+        /// 示例值:SUCCESS
+        /// </summary>
+        [JsonPropertyName("refund_status")]
+        public string RefundStatus { get; set; }
+
+        /// <summary>
+        /// 退款成功时间
+        /// 1、退款成功时间,遵循rfc3339标准格式,格式为YYYY-MM-DDTHH:mm:ss+TIMEZONE,YYYY-MM-DD表示年月日,T出现在字符串中,表示time元素的开头,HH:mm:ss表示时分秒,TIMEZONE表示时区(+08:00表示东八区时间,领先UTC 8小时,即北京时间)。例如:2015-05-20T13:29:35+08:00表示,北京时间2015年5月20日13点29分35秒。
+        /// 2、当退款状态为退款成功时返回此参数。
+        /// 示例值:2018-06-08T10:34:56+08:00
+        /// </summary>
+        [JsonPropertyName("success_time")]
+        public string SuccessTime { get; set; }
+
+        /// <summary>
+        /// 退款入账账户
+        /// 取当前退款单的退款入账方。
+        /// 1、退回银行卡:{银行名称}{卡类型}{卡尾号}
+        /// 2、退回支付用户零钱: 支付用户零钱
+        /// 3、退还商户: 商户基本账户、商户结算银行账户
+        /// 4、退回支付用户零钱通:支付用户零钱通
+        /// 示例值:招商银行信用卡0403
+        /// </summary>
+        [JsonPropertyName("user_received_account")]
+        public string UserReceivedAccount { get; set; }
+
+        /// <summary>
+        /// 金额信息
+        /// 金额信息
+        /// </summary>
+        [JsonPropertyName("amount")]
+        public RefundNotifyAmount Amount { get; set; }
+    }
+}

+ 40 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Request/WeChatPayRefundDomesticRefundsOutRefundNoRequest.cs

@@ -0,0 +1,40 @@
+using Essensoft.AspNetCore.Payment.WeChatPay.V3.Response;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Request
+{
+    /// <summary>
+    /// 基础支付 - 查询单笔退款
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml">JSAPI支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_10.shtml">APP支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_10.shtml">H5支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_10.shtml">Native支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_10.shtml">小程序支付 - 查询单笔退款</a></para>
+    /// 最新更新时间:2021.1.15
+    /// </summary>
+    public class WeChatPayRefundDomesticRefundsOutRefundNoRequest : IWeChatPayGetRequest<WeChatPayRefundDomesticRefundsOutRefundNoResponse>
+    {
+        private WeChatPayObject queryModel;
+
+        /// <summary>
+        /// 商户退款单号
+        /// 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        public string OutRefundNo { get; set; }
+
+        public string GetRequestUrl()
+        {
+            return $"https://api.mch.weixin.qq.com/v3/refund/domestic/refunds/{OutRefundNo}";
+        }
+
+        public WeChatPayObject GetQueryModel()
+        {
+            return queryModel;
+        }
+
+        public void SetQueryModel(WeChatPayObject queryModel)
+        {
+            this.queryModel = queryModel;
+        }
+    }
+}

+ 33 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Request/WeChatPayRefundDomesticRefundsRequest.cs

@@ -0,0 +1,33 @@
+using Essensoft.AspNetCore.Payment.WeChatPay.V3.Response;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Request
+{
+    /// <summary>
+    /// 基础支付 - 退款申请
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml">JSAPI支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_9.shtml">APP支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_9.shtml">H5支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_9.shtml">Native支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_9.shtml">小程序支付 - 退款申请</a></para>
+    /// 最新更新时间:2021.01.15
+    /// </summary>
+    public class WeChatPayRefundDomesticRefundsRequest : IWeChatPayPostRequest<WeChatPayRefundDomesticRefundsResponse>
+    {
+        private WeChatPayObject bodyModel;
+
+        public string GetRequestUrl()
+        {
+            return "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
+        }
+
+        public WeChatPayObject GetBodyModel()
+        {
+            return bodyModel;
+        }
+
+        public void SetBodyModel(WeChatPayObject bodyModel)
+        {
+            this.bodyModel = bodyModel;
+        }
+    }
+}

+ 129 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Response/WeChatPayRefundDomesticRefundsOutRefundNoResponse.cs

@@ -0,0 +1,129 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Response
+{
+    /// <summary>
+    /// 基础支付 - 查询单笔退款 - 返回参数
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_10.shtml">JSAPI支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_10.shtml">APP支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_10.shtml">H5支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_10.shtml">Native支付 - 查询单笔退款</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_10.shtml">小程序支付 - 查询单笔退款</a></para>
+    /// 最新更新时间:2021.1.15
+    /// </summary>
+    public class WeChatPayRefundDomesticRefundsOutRefundNoResponse : WeChatPayResponse
+    {
+        /// <summary>
+        /// 微信支付退款号
+        /// 微信支付退款号。
+        /// 示例值:50000000382019052709732678859
+        /// </summary>
+        [JsonPropertyName("refund_id")]
+        public string RefundId { get; set; }
+
+        /// <summary>
+        /// 商户退款单号
+        /// 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_refund_no")]
+        public string OutRefundNo { get; set; }
+
+        /// <summary>
+        /// 微信支付订单号
+        /// 微信支付交易订单号。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("transaction_id")]
+        public string TransactionId { get; set; }
+
+        /// <summary>
+        /// 商户订单号
+        /// 原支付交易对应的商户订单号。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        /// <summary>
+        /// 退款渠道
+        /// 枚举值:
+        /// ORIGINAL:原路退款
+        /// BALANCE:退回到余额
+        /// OTHER_BALANCE:原账户异常退到其他余额账户
+        /// OTHER_BANKCARD:原银行卡异常退到其他银行卡
+        /// 示例值:ORIGINAL
+        /// </summary>
+        [JsonPropertyName("channel")]
+        public string Channel { get; set; }
+
+        /// <summary>
+        /// 退款入账账户
+        /// 取当前退款单的退款入账方,有以下几种情况:
+        /// 1)退回银行卡:{银行名称}{卡类型}{卡尾号}
+        /// 2)退回支付用户零钱: 支付用户零钱
+        /// 3)退还商户: 商户基本账户商户结算银行账户
+        /// 4)退回支付用户零钱通: 支付用户零钱通。
+        /// 示例值:招商银行信用卡0403
+        /// </summary>
+        [JsonPropertyName("user_received_account")]
+        public string UserReceivedAccount { get; set; }
+
+        /// <summary>
+        /// 退款成功时间
+        /// 退款成功时间,当退款状态为退款成功时有返回。
+        /// 示例值:2020-12-01T16:18:12+08:00
+        /// </summary>
+        [JsonPropertyName("success_time")]
+        public string SuccessTime { get; set; }
+
+        /// <summary>
+        /// 退款创建时间
+        /// 退款受理时间。
+        /// 示例值:2020-12-01T16:18:12+08:00
+        /// </summary>
+        [JsonPropertyName("create_time")]
+        public string CreateTime { get; set; }
+
+        /// <summary>
+        /// 退款状态
+        /// 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
+        /// 枚举值:
+        /// SUCCESS:退款成功
+        /// CLOSED:退款关闭
+        /// PROCESSING:退款处理中
+        /// ABNORMAL:退款异常
+        /// 示例值:SUCCESS
+        /// </summary>
+        [JsonPropertyName("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 资金账户
+        /// 退款所使用资金对应的资金账户类型。 枚举值:
+        /// UNSETTLED : 未结算资金
+        /// AVAILABLE : 可用余额
+        /// UNAVAILABLE : 不可用余额
+        /// OPERATION : 运营户
+        /// 示例值:UNSETTLED
+        /// </summary>
+        [JsonPropertyName("funds_account")]
+        public string FundsAccount { get; set; }
+
+        /// <summary>
+        /// 金额信息
+        /// 金额详细信息。
+        /// </summary>
+        [JsonPropertyName("amount")]
+        public RefundAmountResponse Amount { get; set; }
+
+        /// <summary>
+        /// 优惠退款信息
+        /// 优惠退款信息。
+        /// </summary>
+        [JsonPropertyName("promotion_detail")]
+        public List<RefundPromotionDetail> PromotionDetail { get; set; }
+    }
+}

+ 129 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/Response/WeChatPayRefundDomesticRefundsResponse.cs

@@ -0,0 +1,129 @@
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Essensoft.AspNetCore.Payment.WeChatPay.V3.Domain;
+
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3.Response
+{
+    /// <summary>
+    /// 基础支付 - 退款申请 - 返回参数
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_9.shtml">JSAPI支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_9.shtml">APP支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_9.shtml">H5支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_9.shtml">Native支付 - 退款申请</a></para>
+    /// <para><a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_9.shtml">小程序支付 - 退款申请</a></para>
+    /// 最新更新时间:2021.01.15
+    /// </summary>
+    public class WeChatPayRefundDomesticRefundsResponse : WeChatPayResponse
+    {
+        /// <summary>
+        /// 微信支付退款号
+        /// 微信支付退款号。
+        /// 示例值:50000000382019052709732678859
+        /// </summary>
+        [JsonPropertyName("refund_id")]
+        public string RefundId { get; set; }
+
+        /// <summary>
+        /// 商户退款单号
+        /// 商户系统内部的退款单号,商户系统内部唯一,只能是数字、大小写字母_-|*@ ,同一退款单号多次请求只退一笔。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_refund_no")]
+        public string OutRefundNo { get; set; }
+
+        /// <summary>
+        /// 微信支付订单号
+        /// 微信支付交易订单号。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("transaction_id")]
+        public string TransactionId { get; set; }
+
+        /// <summary>
+        /// 商户订单号
+        /// 原支付交易对应的商户订单号。
+        /// 示例值:1217752501201407033233368018
+        /// </summary>
+        [JsonPropertyName("out_trade_no")]
+        public string OutTradeNo { get; set; }
+
+        /// <summary>
+        /// 退款渠道
+        /// 枚举值:
+        /// ORIGINAL:原路退款
+        /// BALANCE:退回到余额
+        /// OTHER_BALANCE:原账户异常退到其他余额账户
+        /// OTHER_BANKCARD:原银行卡异常退到其他银行卡
+        /// 示例值:ORIGINAL
+        /// </summary>
+        [JsonPropertyName("channel")]
+        public string Channel { get; set; }
+
+        /// <summary>
+        /// 退款入账账户
+        /// 取当前退款单的退款入账方,有以下几种情况:
+        /// 1)退回银行卡:{银行名称}{卡类型}{卡尾号}
+        /// 2)退回支付用户零钱: 支付用户零钱
+        /// 3)退还商户: 商户基本账户商户结算银行账户
+        /// 4)退回支付用户零钱通: 支付用户零钱通。
+        /// 示例值:招商银行信用卡0403
+        /// </summary>
+        [JsonPropertyName("user_received_account")]
+        public string UserReceivedAccount { get; set; }
+
+        /// <summary>
+        /// 退款成功时间
+        /// 退款成功时间,当退款状态为退款成功时有返回。
+        /// 示例值:2020-12-01T16:18:12+08:00
+        /// </summary>
+        [JsonPropertyName("success_time")]
+        public string SuccessTime { get; set; }
+
+        /// <summary>
+        /// 退款创建时间
+        /// 退款受理时间。
+        /// 示例值:2020-12-01T16:18:12+08:00
+        /// </summary>
+        [JsonPropertyName("create_time")]
+        public string CreateTime { get; set; }
+
+        /// <summary>
+        /// 退款状态
+        /// 退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款。
+        /// 枚举值:
+        /// SUCCESS:退款成功
+        /// CLOSED:退款关闭
+        /// PROCESSING:退款处理中
+        /// ABNORMAL:退款异常
+        /// 示例值:SUCCESS
+        /// </summary>
+        [JsonPropertyName("status")]
+        public string Status { get; set; }
+
+        /// <summary>
+        /// 资金账户
+        /// 退款所使用资金对应的资金账户类型。 枚举值:
+        /// UNSETTLED : 未结算资金
+        /// AVAILABLE : 可用余额
+        /// UNAVAILABLE : 不可用余额
+        /// OPERATION : 运营户
+        /// 示例值:UNSETTLED
+        /// </summary>
+        [JsonPropertyName("funds_account")]
+        public string FundsAccount { get; set; }
+
+        /// <summary>
+        /// 金额信息
+        /// 金额详细信息。
+        /// </summary>
+        [JsonPropertyName("amount")]
+        public RefundAmountResponse Amount { get; set; }
+
+        /// <summary>
+        /// 优惠退款信息
+        /// 优惠退款信息。
+        /// </summary>
+        [JsonPropertyName("promotion_detail")]
+        public List<RefundPromotionDetail> PromotionDetail { get; set; }
+    }
+}

+ 23 - 0
src/Essensoft.AspNetCore.Payment.WeChatPay/V3/WeChatPayRefundStatus.cs

@@ -0,0 +1,23 @@
+namespace Essensoft.AspNetCore.Payment.WeChatPay.V3
+{
+    /// <summary>
+    /// 退款状态
+    /// </summary>
+    public static class WeChatPayRefundStatus
+    {
+        /// <summary>
+        /// 退款成功
+        /// </summary>
+        public const string Success = "SUCCESS";
+
+        /// <summary>
+        /// 退款关闭
+        /// </summary>
+        public const string Close = "CLOSE";
+
+        /// <summary>
+        /// 退款异常,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往【服务商平台—>交易中心】,手动处理此笔退款
+        /// </summary>
+        public const string Abnormal = "ABNORMAL";
+    }
+}