diff --git a/GDZZ.Application/Entity/ReFund.cs b/GDZZ.Application/Entity/ReFund.cs
new file mode 100644
index 0000000..97f7c97
--- /dev/null
+++ b/GDZZ.Application/Entity/ReFund.cs
@@ -0,0 +1,43 @@
+using System;
+using SqlSugar;
+using System.ComponentModel;
+using GDZZ.Core.Entity;
+namespace GDZZ.Application.Entity
+{
+ ///
+ /// 退款表
+ ///
+ [SugarTable("Mini_Refund")]
+ [Description("退款表")]
+ public class ReFund : DEntityBase
+ {
+ ///
+ /// 退款金额
+ ///
+ public decimal Amount { get; set; }
+ ///
+ /// 退款状态
+ ///
+ public RefundStatusEnum State { get; set; }
+ ///
+ /// 退款消息
+ ///
+ public string Message { get; set; }
+ ///
+ /// 退款单号
+ ///
+ public string PayNo { get; set; }
+ ///
+ /// 商户退款单号
+ ///
+ public string refundNo { get; set; }
+ ///
+ /// 微信订单号
+ ///
+ public string TransactionNo { get; set; }
+ ///
+ /// 微信退款单号
+ ///
+ public string RefundID { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/GDZZ.Application/Enum/RefundStatusEnum.cs b/GDZZ.Application/Enum/RefundStatusEnum.cs
new file mode 100644
index 0000000..fe7b2c9
--- /dev/null
+++ b/GDZZ.Application/Enum/RefundStatusEnum.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GDZZ.Application
+{
+ ///
+ /// 退款状态
+ ///
+ public enum RefundStatusEnum
+ {
+ ///
+ /// 退款中
+ ///
+ [Description("退款中")] REDIND = 0,
+ ///
+ /// 退款失败
+ ///
+ [Description("退款失败")] REFAILED =1,
+ ///
+ /// 退款成功
+ ///
+ [Description("退款成功")] RESUCCESS = 2,
+ }
+}
diff --git a/GDZZ.Application/GDZZ.Application.xml b/GDZZ.Application/GDZZ.Application.xml
index 8815606..0b5c5fc 100644
--- a/GDZZ.Application/GDZZ.Application.xml
+++ b/GDZZ.Application/GDZZ.Application.xml
@@ -614,6 +614,46 @@
推荐标题
+
+
+ 退款表
+
+
+
+
+ 退款金额
+
+
+
+
+ 退款状态
+
+
+
+
+ 退款消息
+
+
+
+
+ 退款单号
+
+
+
+
+ 商户退款单号
+
+
+
+
+ 微信订单号
+
+
+
+
+ 微信退款单号
+
+
职业表
@@ -764,6 +804,26 @@
储值
+
+
+ 退款状态
+
+
+
+
+ 退款中
+
+
+
+
+ 退款失败
+
+
+
+
+ 退款成功
+
+
已发布
@@ -1293,6 +1353,17 @@
+
+
+ 微信小程序退款
+
+
+
+
+
+ 退款回调
+
+
折扣
@@ -1358,6 +1429,26 @@
公司信息
+
+
+ 订单编号
+
+
+
+
+ 退款单号
+
+
+
+
+ 真实支付金额
+
+
+
+
+ 退款消息
+
+
基础用户服务
diff --git a/GDZZ.Application/Service/Auth/DTO/WxRefundInput.cs b/GDZZ.Application/Service/Auth/DTO/WxRefundInput.cs
new file mode 100644
index 0000000..5fb8388
--- /dev/null
+++ b/GDZZ.Application/Service/Auth/DTO/WxRefundInput.cs
@@ -0,0 +1,35 @@
+using GDZZ.Application.Enum;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GDZZ.Application
+{
+ public class WxRefundInput
+ {
+
+ ///
+ /// 订单编号
+ ///
+ public long OrderID { get; set; }
+
+ ///
+ /// 退款单号
+ ///
+ public string PayNo { get; set; }
+
+ ///
+ /// 真实支付金额
+ ///
+ public decimal RealPic { get; set; }
+
+ ///
+ /// 退款消息
+ ///
+ public string Message { get; set; }
+
+
+ }
+}
diff --git a/GDZZ.Application/Service/WXPay/WXPayService.cs b/GDZZ.Application/Service/WXPay/WXPayService.cs
index ee413c4..80cb57d 100644
--- a/GDZZ.Application/Service/WXPay/WXPayService.cs
+++ b/GDZZ.Application/Service/WXPay/WXPayService.cs
@@ -23,7 +23,7 @@ using Microsoft.AspNetCore.Authorization;
using System.Collections.Generic;
using Mapster;
using Furion.FriendlyException;
-
+using Senparc.CO2NET.Cache.Redis;
namespace GDZZ.Application.Service.WXPay
{
@@ -42,7 +42,7 @@ namespace GDZZ.Application.Service.WXPay
private readonly SqlSugarRepository rechargeRep; //充值仓储
private readonly SqlSugarRepository payTakeRep; //支付仓储
-
+ private readonly SqlSugarRepository refundRep; //退款仓储
private readonly WechatOAuth _wechatOAuth; //微信权限服务
private readonly IHttpContextAccessor _httpContextAccessor; //http服务
@@ -66,11 +66,13 @@ namespace GDZZ.Application.Service.WXPay
SqlSugarRepository Self,
SqlSugarRepository rechargeRep,
SqlSugarRepository payTakeRep,
+ SqlSugarRepository refundRep,
WechatOAuth wechatOAuth,
IHttpContextAccessor _httpContextAccessor,
IEventPublisher eventPublisher)
{
this.self = Self;
+ this.refundRep= refundRep;
this.balance = balance;
this.Baseuser = Baseuser;
this.ComsumeRep = ComsumeRep;
@@ -158,6 +160,10 @@ namespace GDZZ.Application.Service.WXPay
}
+
+
+ #region 小程序支付
+
///
///微信小程序支付
///
@@ -192,7 +198,7 @@ namespace GDZZ.Application.Service.WXPay
string nonceStr = TenPayV3Util.GetNoncestr();
TenPayV3UnifiedorderRequestData xmlDataInfo = new TenPayV3UnifiedorderRequestData(Config.SenparcWeixinSetting.WxOpenAppId,
- Config.SenparcWeixinSetting.TenPayV3_MchId,"余额充值", sp_billno,
+ Config.SenparcWeixinSetting.TenPayV3_MchId, "余额充值", sp_billno,
pMoney,
"127.0.0.1",
Config.SenparcWeixinSetting.TenPayV3_TenpayNotify, TenPayV3Type.JSAPI, authUserInput.OpenID,
@@ -218,6 +224,7 @@ namespace GDZZ.Application.Service.WXPay
}
+
///
/// 微信小程序支付回调
///
@@ -226,23 +233,21 @@ namespace GDZZ.Application.Service.WXPay
[Route("/Mini/NotifyUrl")]
[UnifyResult(typeof(string))]
[AllowAnonymous]
- public async Task NotifyUrl()
+ public async Task NotifyUrl()
{
-
+ ResponseHandler resHandler = new ResponseHandler(this._httpContextAccessor.HttpContext);
+ ContentResult content = new ContentResult();
+ string return_code = resHandler.GetParameter("return_code");
+ string return_msg = resHandler.GetParameter("return_msg");
+ resHandler.SetKey(Config.SenparcWeixinSetting.TenPayV3_Key);
try
{
- ResponseHandler resHandler = new ResponseHandler(this._httpContextAccessor.HttpContext);
-
- string return_code = resHandler.GetParameter("return_code");
- string return_msg = resHandler.GetParameter("return_msg");
- //var res = "";
- resHandler.SetKey(Config.SenparcWeixinSetting.TenPayV3_Key);
//验证请求是否从微信发过来(安全)
if (resHandler.IsTenpaySign() && return_code.ToUpper() == "SUCCESS")
{
//res = "success";//正确的订单处理
- //直到这里,才能认为交易真正成功了,可以进行数据库操作,但是别忘了返回规定格式的消息!
+ //直到这里,才能认为交易真正成功了,可以进行数据库操作,但是别忘了返回规定格式的消息!
Console.WriteLine("回调成功");
//attach
var paymentId = long.Parse(resHandler.GetParameter("attach"));
@@ -273,65 +278,201 @@ namespace GDZZ.Application.Service.WXPay
});
}
}
+ content.Content = string.Format(@"
+
+
+ ", return_code, return_msg);
+ this.SendTemplate(0, return_code, resHandler);
}
else
{
- Console.WriteLine("回调失败");
- //res = "wrong";//错误的订单处理
+ content.Content = "回调失败";
+
}
- /* 这里可以进行订单处理的逻辑 */
+ this.LogRecord(resHandler);
+ }
+ catch (Exception ex)
+ {
+ content.Content = ex.Message;
+ }
+ return content;
+
+ }
+ #endregion
- //发送支付成功的模板消息
- try
- {
- string appId = Config.SenparcWeixinSetting.TenPayV3_AppId;//与微信公众账号后台的AppId设置保持一致,区分大小写。
- string openId = resHandler.GetParameter("openid");
- var templateData = new WeixinTemplate_PaySuccess("https://weixin.senparc.com", "微信支付 V2 购买商品", "状态:" + return_code);
- Senparc.Weixin.WeixinTrace.SendCustomLog("支付成功模板消息参数", appId + " , " + openId);
+ #region 小程序退款
+ ///
+ ///微信小程序退款
+ ///
+ ///
+ [HttpPost]
+ [Route("Mini/v1/refund")]
+ public async Task WxRefund(WxRefundInput wxRefundInput)
+ {
+ //查询订单
+ var order = this.payTakeRep.FirstOrDefault(x => x.OrderId == wxRefundInput.OrderID);
+
+ if (order == null)
+ return new Exception("查询无此订单,退款失败");
+
+ //生成退款单
+ var res = await this.refundRep.InsertReturnEntityAsync(new ReFund()
+ {
+ State = RefundStatusEnum.REDIND,
+ Amount = order.PaymentMoney,
+ Message = "退款审核中",
+ PayNo = order.Id.ToString(),
+ });
+ if (res == null)
+ return new Exception("生成退款单失败");
+
+ string notifyUrl = string.Format(Config.SenparcWeixinSetting.TenPayV3_TenpayNotify, "RefundNotifyUrl");
+ string opUserId = Config.SenparcWeixinSetting.TenPayV3_MchId;
+ //退款金额
+ int refundFee = (int)(order.PaymentMoney * 100);
+ //支付时的总金额
+ int totalFee = (int)(order.PaymentMoney * 100);
+
+ string outRefundNo = res.Id.ToString();//新退款单号
+ string outTradeNo = order.OrderId.ToString();
+ var nonceStr = TenPayV3Util.GetNoncestr();
+
+ var dataInfo = new TenPayV3RefundRequestData(Config.SenparcWeixinSetting.WxOpenAppId, Config.SenparcWeixinSetting.TenPayV3_MchId,
+ Config.SenparcWeixinSetting.TenPayV3_Key, null, nonceStr, null, outTradeNo, outRefundNo, totalFee, refundFee, opUserId, null,
+ notifyUrl: notifyUrl);
+
+ return new
+ {
+ message = "退款请求完成",
+ };
+ }
+
+
+ ///
+ /// 退款回调
+ ///
+ [HttpPost("RefundNotifyUrl")]
+ [UnifyResult(typeof(string))]
+ public async Task RefundNotifyUrl()
+ {
+ ResponseHandler resHandler = new ResponseHandler(this._httpContextAccessor.HttpContext);
+ ContentResult content = new ContentResult();
+ string return_code = resHandler.GetParameter("return_code");
+ string return_msg = resHandler.GetParameter("return_msg");
+ var mch_key = Senparc.Weixin.Config.SenparcWeixinSetting.TenPayV3_Key;
+ try
+ {
+ if (return_code.ToUpper() == "SUCCESS")
+ {
+ string req_info = resHandler.GetParameter("req_info");
+ var decodeReqInfo = TenPayV3Util.DecodeRefundReqInfo(req_info, mch_key);
+ var decodeDoc = System.Xml.Linq.XDocument.Parse(decodeReqInfo);
+ var refundNotifyXml = decodeDoc.Serialize();
+
+ //获取接口中需要用到的信息
+ string out_trade_no = decodeDoc.Root.Element("out_trade_no").Value;
+ string transaction_id = decodeDoc.Root.Element("transaction_id").Value;
+ string refund_id = decodeDoc.Root.Element("refund_id").Value;
+ int total_fee = int.Parse(decodeDoc.Root.Element("total_fee").Value);
+ int refund_fee = int.Parse(decodeDoc.Root.Element("refund_fee").Value);
+ string out_refund_no = decodeDoc.Root.Element("out_refund_no").Value;
+
+
+ var refun = this.refundRep.FirstOrDefault(x => x.PayNo == out_refund_no);
+ refun.PayNo = out_refund_no;
+ refun.Amount = ((float)refund_fee.ParseToInt() / 100).ParseToDecimal();
+ refun.Message = "退款完成";
+ refun.RefundID = refund_id;
+ refun.refundNo = out_refund_no;
+ refun.TransactionNo = transaction_id;
+ refun.State = RefundStatusEnum.RESUCCESS;
+
+ if (this.refundRep.Update(refun) > 0)
+ {
+ content.Content = string.Format(@"
+
+
+ ", return_code, return_msg);
+ return content;
+ }
- var result = Senparc.Weixin.MP.AdvancedAPIs.TemplateApi.SendTemplateMessage(appId, openId, templateData);
}
- catch (Exception ex)
+ else
{
- Senparc.Weixin.WeixinTrace.SendCustomLog("支付成功模板消息", ex.ToString());
+ content.Content = "回调处理失败!";
}
+ }
+ catch (Exception e)
+ {
- #region 记录日志
+ content.Content = e.Message;
+ }
+ return content;
+ }
- var logDir = ServerUtility.ContentRootMapPath(string.Format("~/App_Data/TenPayNotify/{0}", SystemTime.Now.ToString("yyyyMMdd")));
- if (!Directory.Exists(logDir))
- {
- Directory.CreateDirectory(logDir);
- }
+ #endregion
- var logPath = Path.Combine(logDir, string.Format("{0}-{1}-{2}.txt", SystemTime.Now.ToString("yyyyMMdd"), SystemTime.Now.ToString("HHmmss"), Guid.NewGuid().ToString("n").Substring(0, 8)));
- using (var fileStream = System.IO.File.OpenWrite(logPath))
- {
- var notifyXml = resHandler.ParseXML();
-
- fileStream.Write(Encoding.Default.GetBytes(notifyXml), 0, Encoding.Default.GetByteCount(notifyXml));
- fileStream.Close();
- }
- #endregion
- string xml = string.Format(@"
-
-
- ", return_code, return_msg);
- return xml;
+ #region 附加服务
+
+ public void SendTemplate(int type, string return_code, ResponseHandler resHandler)
+ {
+
+ switch (type)
+ {
+
+ case 0:
+ //发送支付成功的模板消息
+ try
+ {
+ string appId = Config.SenparcWeixinSetting.TenPayV3_AppId;//与微信公众账号后台的AppId设置保持一致,区分大小写。
+ string openId = resHandler.GetParameter("openid");
+ var templateData = new WeixinTemplate_PaySuccess("https://weixin.senparc.com", "微信支付 V2 购买商品", "状态:" + return_code);
+
+ Senparc.Weixin.WeixinTrace.SendCustomLog("支付成功模板消息参数", appId + " , " + openId);
+
+ var result = Senparc.Weixin.MP.AdvancedAPIs.TemplateApi.SendTemplateMessage(appId, openId, templateData);
+ }
+ catch (Exception ex)
+ {
+ Senparc.Weixin.WeixinTrace.SendCustomLog("支付成功模板消息", ex.ToString());
+ }
+ break;
+ default:
+ break;
}
- catch (Exception ex)
+
+ }
+
+
+ private void LogRecord(ResponseHandler resHandler)
+ {
+
+ #region 记录日志
+
+ var logDir = ServerUtility.ContentRootMapPath(string.Format("~/App_Data/TenPayNotify/{0}", SystemTime.Now.ToString("yyyyMMdd")));
+ if (!Directory.Exists(logDir))
{
- WeixinTrace.WeixinExceptionLog(new WeixinException(ex.Message, ex));
- throw;
+ Directory.CreateDirectory(logDir);
}
+ var logPath = Path.Combine(logDir, string.Format("{0}-{1}-{2}.txt", SystemTime.Now.ToString("yyyyMMdd"), SystemTime.Now.ToString("HHmmss"), Guid.NewGuid().ToString("n").Substring(0, 8)));
+
+ using (var fileStream = System.IO.File.OpenWrite(logPath))
+ {
+ var notifyXml = resHandler.ParseXML();
+
+ fileStream.Write(Encoding.Default.GetBytes(notifyXml), 0, Encoding.Default.GetByteCount(notifyXml));
+ fileStream.Close();
+ }
+ #endregion
}
+ #endregion
}
}