|
|
using GDZZ.Application.Entity;
|
|
|
using GDZZ.Core.Entity;
|
|
|
using GDZZ.Core;
|
|
|
using Microsoft.AspNetCore.Mvc;
|
|
|
using System;
|
|
|
using System.Threading.Tasks;
|
|
|
using Furion.EventBus;
|
|
|
using Microsoft.Extensions.Options;
|
|
|
using Microsoft.AspNetCore.Http;
|
|
|
using Furion.DependencyInjection;
|
|
|
using Furion.DynamicApiController;
|
|
|
using GDZZ.Application.Service.WXPay.Dto;
|
|
|
using TenPayOldV3 = Senparc.Weixin.TenPay.V3.TenPayV3;
|
|
|
using Senparc.Weixin;
|
|
|
using Senparc.Weixin.TenPay.V3;
|
|
|
using Senparc.Weixin.TenPay;
|
|
|
using Senparc.CO2NET.Utilities;
|
|
|
using Senparc.Weixin.Sample.CommonService.TemplateMessage;
|
|
|
using System.IO;
|
|
|
using System.Text;
|
|
|
using Senparc.Weixin.Exceptions;
|
|
|
using Microsoft.AspNetCore.Authorization;
|
|
|
using System.Collections.Generic;
|
|
|
using Mapster;
|
|
|
using Furion.FriendlyException;
|
|
|
using Senparc.CO2NET.Cache.Redis;
|
|
|
using GDZZ.Core.Service;
|
|
|
using System.Linq.Dynamic.Core.Tokenizer;
|
|
|
using System.Diagnostics;
|
|
|
using Microsoft.AspNetCore.Hosting;
|
|
|
using Furion.JsonSerialization;
|
|
|
using Yitter.IdGenerator;
|
|
|
|
|
|
namespace GDZZ.Application.Service.WXPay
|
|
|
{
|
|
|
[ApiDescriptionSettings("Application", Name = "WXPay", Order = 1)]
|
|
|
public class WXPayService : IWXPayService, IDynamicApiController, ITransient
|
|
|
{
|
|
|
|
|
|
|
|
|
private readonly IHostingEnvironment hostingEnvironment;
|
|
|
private readonly SqlSugarRepository<BaseUser> Baseuser; // wx用户仓储
|
|
|
private readonly SqlSugarRepository<SysUser> _sysUserRep; // 用户表仓储
|
|
|
private readonly SqlSugarRepository<SysTenant> _sysTenantRep; //租户仓储
|
|
|
private readonly SqlSugarRepository<SeIF> self; //职业仓储
|
|
|
private readonly SqlSugarRepository<Consume> ComsumeRep; //消费记录仓储
|
|
|
private readonly SqlSugarRepository<Balance> balance; //余额仓储
|
|
|
private readonly SqlSugarRepository<MiniRecharge> rechargeRep; //充值仓储
|
|
|
private readonly SqlSugarRepository<MiniPayTake> payTakeRep; //支付仓储
|
|
|
private readonly SqlSugarRepository<ReFund> refundRep; //退款仓储
|
|
|
private readonly SqlSugarRepository<Taking> takingRep; //提现仓储
|
|
|
|
|
|
private readonly WechatOAuth _wechatOAuth; //微信权限服务
|
|
|
private readonly IHttpContextAccessor _httpContextAccessor; //http服务
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取配置文件
|
|
|
/// </summary>
|
|
|
private readonly SenparcWeixinSetting _oauthConfig;
|
|
|
|
|
|
|
|
|
|
|
|
public WXPayService(
|
|
|
IOptions<OAuthOptions> options,
|
|
|
SqlSugarRepository<BaseUser> Baseuser,
|
|
|
SqlSugarRepository<SysTenant> sysTenantRep,
|
|
|
SqlSugarRepository<SysUser> sysUserRep,
|
|
|
SqlSugarRepository<Balance> balance,
|
|
|
SqlSugarRepository<Consume> ComsumeRep,
|
|
|
SqlSugarRepository<SeIF> Self,
|
|
|
SqlSugarRepository<MiniRecharge> rechargeRep,
|
|
|
SqlSugarRepository<MiniPayTake> payTakeRep,
|
|
|
SqlSugarRepository<ReFund> refundRep,
|
|
|
SqlSugarRepository<Taking> takingRep,
|
|
|
WechatOAuth wechatOAuth,
|
|
|
IHostingEnvironment hostingEnvironment,
|
|
|
IHttpContextAccessor _httpContextAccessor,
|
|
|
IEventPublisher eventPublisher)
|
|
|
{
|
|
|
this.self = Self;
|
|
|
this.refundRep= refundRep;
|
|
|
this.balance = balance;
|
|
|
this.Baseuser = Baseuser;
|
|
|
this.ComsumeRep = ComsumeRep;
|
|
|
this._sysUserRep = sysUserRep;
|
|
|
this._sysTenantRep = sysTenantRep;
|
|
|
this.rechargeRep = rechargeRep;
|
|
|
this.payTakeRep = payTakeRep;
|
|
|
this._wechatOAuth = wechatOAuth;
|
|
|
this.takingRep= takingRep;
|
|
|
this._oauthConfig = options.Value.SenparcWeixin;
|
|
|
this.hostingEnvironment = hostingEnvironment;
|
|
|
this._httpContextAccessor = _httpContextAccessor;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 查询余额
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet]
|
|
|
[Route("Mini/v1/GetUserBalance")]
|
|
|
public async Task<BalanceOut> GetUserBalance()
|
|
|
{
|
|
|
var balan = await this.balance.AsQueryable().Filter("TenantId", true).SingleAsync(x => x.UserID == UserManager.UserId);
|
|
|
if (balan == null)
|
|
|
return null;
|
|
|
return new BalanceOut()
|
|
|
{
|
|
|
Amount = balan.Amount,
|
|
|
UserID = UserManager.UserId,
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 充值记录查询
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet]
|
|
|
[Route("Mini/v1/GetRechargeList")]
|
|
|
public async Task<List<RechargeOut>> GetRechargeList()
|
|
|
{
|
|
|
var rechargeLiist =await this.rechargeRep.Where(x => x.CreatedUserId == UserManager.UserId).ToListAsync();
|
|
|
return rechargeLiist.Adapt<List<RechargeOut>>();
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 修改余额
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet]
|
|
|
[Route("Mini/v1/UpBalance")]
|
|
|
public async Task<dynamic> UpBalance(decimal Consume,long? ResumeID)
|
|
|
{
|
|
|
|
|
|
this.balance.CurrentBeginTran();
|
|
|
var comrep = await this.ComsumeRep.Where(x=>x.ResumeID== ResumeID).FirstAsync();
|
|
|
if(!comrep.IsNullOrZero()) //已经消费过了
|
|
|
return "";
|
|
|
|
|
|
var ban = await this.balance.Where(x => x.UserID == UserManager.UserId).FirstAsync();
|
|
|
if (ban == null)
|
|
|
throw Oops.Oh(ErrorCode.xg1002);
|
|
|
|
|
|
ban.Amount -= Consume;
|
|
|
if (ban.Amount < 0)
|
|
|
throw Oops.Oh(ErrorCode.B1001);
|
|
|
await this.ComsumeRep.InsertAsync(new Consume()
|
|
|
{
|
|
|
CAmount= Consume,
|
|
|
Surplus = ban.Amount,
|
|
|
Sort =(int)ConsumeEnum.Contact,
|
|
|
ResumeID = ResumeID
|
|
|
});
|
|
|
this.balance.CurrentCommitTran();
|
|
|
return await this.balance.AsUpdateable(ban).IgnoreColumns(ignoreAllNullColumns: true).ExecuteCommandAsync()>0;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// 查询消费记录
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpGet]
|
|
|
[Route("Mini/v1/GetBalance")]
|
|
|
public async Task<List<ConsumeOut>> GetBalance()
|
|
|
{
|
|
|
var comsume = await this.ComsumeRep.Where(x => x.CreatedUserId == UserManager.UserId).OrderByDescending(x=>x.CreatedTime).ToListAsync();
|
|
|
return comsume.Adapt<List<ConsumeOut>>();
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region 小程序支付
|
|
|
|
|
|
/// <summary>
|
|
|
///微信小程序支付
|
|
|
/// </summary>
|
|
|
/// <param name="authUserInput">商品Id</param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
[Route("Mini/v1/wxpay")]
|
|
|
public async Task<dynamic> WxPay(AuthUserInput authUserInput)
|
|
|
{
|
|
|
|
|
|
var recharge = await this.rechargeRep.InsertReturnEntityAsync(new MiniRecharge()
|
|
|
{
|
|
|
PaymentMoney = authUserInput.Money,
|
|
|
Status = RechargeEnum.NoFinis,
|
|
|
Type = RechargeTypeEnum.StoredValue,
|
|
|
TotalPrice = authUserInput.Money
|
|
|
});
|
|
|
|
|
|
var payTake = await this.payTakeRep.InsertReturnEntityAsync(new MiniPayTake()
|
|
|
{
|
|
|
PaymentMoney = authUserInput.Money,
|
|
|
Type = Enum.PayTypeEnum.BUY,
|
|
|
OrderId = recharge.Id,
|
|
|
PaySn = "",
|
|
|
PayStatus = PayStatusEnum.NotPaying
|
|
|
});
|
|
|
|
|
|
int pMoney = (int)(authUserInput.Money * 100);
|
|
|
|
|
|
string sp_billno =
|
|
|
$"{Config.SenparcWeixinSetting.TenPayV3_MchId}{SystemTime.Now:yyyyMMddHHmmss}{TenPayV3Util.BuildRandomStr(6)}";
|
|
|
string timeStamp = TenPayV3Util.GetTimestamp();
|
|
|
string nonceStr = TenPayV3Util.GetNoncestr();
|
|
|
|
|
|
TenPayV3UnifiedorderRequestData xmlDataInfo = new TenPayV3UnifiedorderRequestData(Config.SenparcWeixinSetting.WxOpenAppId,
|
|
|
Config.SenparcWeixinSetting.TenPayV3_MchId, "余额充值", sp_billno,
|
|
|
pMoney,
|
|
|
"127.0.0.1",
|
|
|
Config.SenparcWeixinSetting.TenPayV3_TenpayNotify, TenPayV3Type.JSAPI, authUserInput.OpenID,
|
|
|
Config.SenparcWeixinSetting.TenPayV3_Key, nonceStr, null, null, null, null, payTake.OrderId.ToString());
|
|
|
Console.WriteLine(xmlDataInfo.PackageRequestHandler.ParseXML());
|
|
|
var result = TenPayOldV3.Unifiedorder(xmlDataInfo);//调用统一订单接口
|
|
|
string packageStr = "prepay_id=" + result.prepay_id;
|
|
|
|
|
|
|
|
|
return new
|
|
|
{
|
|
|
success = true,
|
|
|
result.prepay_id,
|
|
|
appId = Config.SenparcWeixinSetting.WxOpenAppId,
|
|
|
timeStamp,
|
|
|
nonceStr,
|
|
|
package = packageStr,
|
|
|
signType = "MD5",
|
|
|
paySign = TenPayV3.GetJsPaySign(Config.SenparcWeixinSetting.WxOpenAppId, timeStamp, nonceStr,
|
|
|
packageStr, Config.SenparcWeixinSetting.TenPayV3_Key)
|
|
|
};
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 卡消费支付
|
|
|
/// </summary>
|
|
|
/// <param name="authUserInput">商品Id</param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
[Route("Mini/v1/wxCardPay")]
|
|
|
public async Task<dynamic> wxCardPay(AuthUserInput authUserInput)
|
|
|
{
|
|
|
|
|
|
var payres = await this.payTakeRep.AsQueryable().Where(x => x.Type == Enum.PayTypeEnum.BUY && (DateTime.Now - x.CreatedTime.Value).TotalDays < 30 && x.CreatedUserId == UserManager.UserId).FirstAsync();
|
|
|
|
|
|
if (!payres.IsEmpty())
|
|
|
return "已经购买";
|
|
|
|
|
|
var payTake = await this.payTakeRep.InsertReturnEntityAsync(new MiniPayTake()
|
|
|
{
|
|
|
PaymentMoney = authUserInput.Money,
|
|
|
OrderId = YitIdHelper.NextId(),
|
|
|
Type = Enum.PayTypeEnum.BUY,
|
|
|
PaySn = "",
|
|
|
PayStatus = PayStatusEnum.NotPaying
|
|
|
});
|
|
|
|
|
|
int pMoney = (int)(authUserInput.Money );
|
|
|
|
|
|
string sp_billno =
|
|
|
$"{Config.SenparcWeixinSetting.TenPayV3_MchId}{SystemTime.Now:yyyyMMddHHmmss}{TenPayV3Util.BuildRandomStr(6)}";
|
|
|
string timeStamp = TenPayV3Util.GetTimestamp();
|
|
|
string nonceStr = TenPayV3Util.GetNoncestr();
|
|
|
|
|
|
TenPayV3UnifiedorderRequestData xmlDataInfo = new TenPayV3UnifiedorderRequestData(Config.SenparcWeixinSetting.WxOpenAppId,
|
|
|
Config.SenparcWeixinSetting.TenPayV3_MchId, "开卡支付", sp_billno,
|
|
|
pMoney,
|
|
|
"127.0.0.1",
|
|
|
Config.SenparcWeixinSetting.TenPayV3_TenpayNotify, TenPayV3Type.JSAPI, authUserInput.OpenID,
|
|
|
Config.SenparcWeixinSetting.TenPayV3_Key, nonceStr, null, null, null, null, payTake.OrderId.ToString());
|
|
|
Console.WriteLine(xmlDataInfo.PackageRequestHandler.ParseXML());
|
|
|
var result = TenPayOldV3.Unifiedorder(xmlDataInfo);//调用统一订单接口
|
|
|
string packageStr = "prepay_id=" + result.prepay_id;
|
|
|
|
|
|
|
|
|
return new
|
|
|
{
|
|
|
success = true,
|
|
|
result.prepay_id,
|
|
|
appId = Config.SenparcWeixinSetting.WxOpenAppId,
|
|
|
timeStamp,
|
|
|
nonceStr,
|
|
|
package = packageStr,
|
|
|
signType = "MD5",
|
|
|
paySign = TenPayV3.GetJsPaySign(Config.SenparcWeixinSetting.WxOpenAppId, timeStamp, nonceStr,
|
|
|
packageStr, Config.SenparcWeixinSetting.TenPayV3_Key)
|
|
|
};
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 微信小程序支付回调
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
[Route("/Mini/NotifyUrl")]
|
|
|
[UnifyResult(typeof(string))]
|
|
|
[AllowAnonymous]
|
|
|
public async Task<ActionResult> 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
|
|
|
{
|
|
|
|
|
|
//验证请求是否从微信发过来(安全)
|
|
|
if (resHandler.IsTenpaySign() && return_code.ToUpper() == "SUCCESS")
|
|
|
{
|
|
|
//res = "success";//正确的订单处理
|
|
|
//直到这里,才能认为交易真正成功了,可以进行数据库操作,但是别忘了返回规定格式的消息!
|
|
|
Console.WriteLine("回调成功");
|
|
|
//attach
|
|
|
var paymentId = long.Parse(resHandler.GetParameter("attach"));
|
|
|
//业务处理
|
|
|
var paytake = await this.payTakeRep.Where(x => x.OrderId == paymentId).SingleAsync();
|
|
|
|
|
|
if (paytake.IsEmpty())
|
|
|
throw Oops.Oh("无此订单,回调失败");
|
|
|
|
|
|
|
|
|
this.payTakeRep.BeginTran();
|
|
|
paytake.PayStatus = PayStatusEnum.Paying;
|
|
|
|
|
|
switch (paytake.Type)
|
|
|
{
|
|
|
case Enum.PayTypeEnum.RECHARGE:
|
|
|
var recharge = await this.rechargeRep.Where(x => x.Id == paymentId).SingleAsync();
|
|
|
if (!recharge.IsEmpty())
|
|
|
{
|
|
|
recharge.Status = RechargeEnum.Finish;
|
|
|
this.rechargeRep.Update(recharge);
|
|
|
var balan = await this.balance.AsQueryable().Filter("TenantId", true).Where(x => x.UserID == paytake.CreatedUserId).SingleAsync();
|
|
|
if (!balan.IsEmpty())
|
|
|
{
|
|
|
balan.Amount = recharge.PaymentMoney;
|
|
|
this.balance.Update(balan);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
this.balance.Insert(new Balance()
|
|
|
{
|
|
|
Amount = recharge.PaymentMoney,
|
|
|
UserID = (long)paytake.CreatedUserId
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
break;
|
|
|
case Enum.PayTypeEnum.BUY:
|
|
|
break;
|
|
|
default:
|
|
|
throw Oops.Oh("订单类型错误");
|
|
|
|
|
|
}
|
|
|
this.payTakeRep.Update(paytake);
|
|
|
|
|
|
content.Content = string.Format(@"<xml>
|
|
|
<return_code><![CDATA[{0}]]></return_code>
|
|
|
<return_msg><![CDATA[{1}]]></return_msg>
|
|
|
</xml>", return_code, return_msg);
|
|
|
this.SendTemplate(0, return_code, resHandler);
|
|
|
this.payTakeRep.CommitTran();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
content.Content = "回调失败";
|
|
|
|
|
|
}
|
|
|
|
|
|
this.LogRecord(resHandler);
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
this.payTakeRep.RollbackTran();
|
|
|
content.Content = ex.Message;
|
|
|
}
|
|
|
return content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
#region 小程序退款
|
|
|
/// <summary>
|
|
|
///微信小程序退款
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
[Route("Mini/v1/refund")]
|
|
|
public async Task<dynamic> 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 = "退款请求完成",
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 退款回调
|
|
|
/// </summary>
|
|
|
[HttpPost("RefundNotifyUrl")]
|
|
|
[UnifyResult(typeof(string))]
|
|
|
public async Task<ActionResult> 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 (await this.refundRep.UpdateAsync(refun) > 0)
|
|
|
{
|
|
|
content.Content = string.Format(@"<xml>
|
|
|
<return_code><![CDATA[{0}]]></return_code>
|
|
|
<return_msg><![CDATA[{1}]]></return_msg>
|
|
|
</xml>", return_code, return_msg);
|
|
|
return content;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
content.Content = "回调处理失败!";
|
|
|
}
|
|
|
}
|
|
|
catch (Exception e)
|
|
|
{
|
|
|
|
|
|
content.Content = e.Message;
|
|
|
}
|
|
|
return content;
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
#region 商户
|
|
|
/// <summary>
|
|
|
/// 提现审批
|
|
|
/// </summary>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
[Route("/Mini/v1/Approval")]
|
|
|
public async Task<bool> Approval(ApprovalInput approvalInput)
|
|
|
{
|
|
|
this.takingRep.BeginTran();
|
|
|
var taking = await this.takingRep.AsQueryable().FirstAsync(x => x.Id == approvalInput.ID);
|
|
|
if (taking == null)
|
|
|
throw Oops.Oh(ErrorCode.xg1002);
|
|
|
var balance = await this.balance.AsQueryable().FirstAsync(x => x.UserID == taking.CreatedUserId);
|
|
|
|
|
|
if (taking.Status == Enum.TakingEnum.FINISHED)
|
|
|
throw Oops.Oh("重复审核");
|
|
|
|
|
|
try
|
|
|
{
|
|
|
if (!approvalInput.Examine && taking.Status != Enum.TakingEnum.FAIL)
|
|
|
{
|
|
|
//回退金额 修改状态
|
|
|
taking.Status = Enum.TakingEnum.FAIL;
|
|
|
balance.Amount += taking.Money;
|
|
|
taking.Message = "申请提现失败!请联系客服";
|
|
|
}
|
|
|
else if(approvalInput.Examine)
|
|
|
{
|
|
|
var hostting = this.hostingEnvironment.ContentRootPath;
|
|
|
//读取商户信息
|
|
|
var appid = Config.SenparcWeixinSetting.TenPayV3_AppId;
|
|
|
var mchid = Config.SenparcWeixinSetting.TenPayV3_MchId;
|
|
|
var serialno = Config.SenparcWeixinSetting.TenPayV3_SerialNumber;
|
|
|
string url = string.Format("https://api.mch.weixin.qq.com/v3/transfer/batches");
|
|
|
string partnerTradeNo = "tk" + DateTime.Now.ToString("yyyyMMddHHmmfff");
|
|
|
JsApiPay jsApiPay = new JsApiPay();
|
|
|
|
|
|
int amount = Convert.ToInt32(taking.Money * 100);
|
|
|
JsPay result = await jsApiPay.WithDrawsToWx(url, appid, mchid, serialno, taking.OpenID, partnerTradeNo, amount, hostting);
|
|
|
if (result.code.IsEmpty())
|
|
|
{
|
|
|
taking.Status = Enum.TakingEnum.FINISHED;
|
|
|
taking.WXOrderNo = result.out_batch_no;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
taking.Status = Enum.TakingEnum.FAIL;
|
|
|
balance.Amount += taking.Money;
|
|
|
taking.Message = result.message;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
await this.takingRep.UpdateAsync(taking);
|
|
|
this.balance.Update(balance);
|
|
|
this.takingRep.CommitTran();
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
this.takingRep.RollbackTran();
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
/// 提现申请
|
|
|
/// </summary>
|
|
|
/// <param name="takingApply"></param>
|
|
|
/// <returns></returns>
|
|
|
[HttpPost]
|
|
|
[Route("Mini/v1/TakingApply")]
|
|
|
public async Task<bool> TakingApply(TakingApplyInput takingApply)
|
|
|
{
|
|
|
//纪录申请
|
|
|
var bRes = await this.balance.AsQueryable().FirstAsync(x => x.UserID == UserManager.UserId);
|
|
|
if (bRes == null)
|
|
|
throw Oops.Oh(ErrorCode.xg1002);
|
|
|
if (bRes.Amount < takingApply.TransferAmount)
|
|
|
throw Oops.Oh(ErrorCode.xg1002);
|
|
|
|
|
|
try
|
|
|
{
|
|
|
this.balance.BeginTran();
|
|
|
bRes.Amount -= takingApply.TransferAmount;
|
|
|
await this.takingRep.InsertAsync(new Taking()
|
|
|
{
|
|
|
OrderNo = YitIdHelper.NextId(),
|
|
|
Money = takingApply.TransferAmount,
|
|
|
Status = Enum.TakingEnum.ACCEPTED,
|
|
|
OpenID = takingApply.OpenID,
|
|
|
Remarks = takingApply.Remarks
|
|
|
});
|
|
|
await this.balance.UpdateAsync(bRes);
|
|
|
this.balance.CommitTran();
|
|
|
}
|
|
|
catch (Exception)
|
|
|
{
|
|
|
this.balance.RollbackTran();
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
return true ;
|
|
|
}
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
#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;
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
private void LogRecord(ResponseHandler resHandler)
|
|
|
{
|
|
|
|
|
|
#region 记录日志
|
|
|
|
|
|
var logDir = ServerUtility.ContentRootMapPath(string.Format("~/App_Data/TenPayNotify/{0}", SystemTime.Now.ToString("yyyyMMdd")));
|
|
|
if (!Directory.Exists(logDir))
|
|
|
{
|
|
|
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
|
|
|
}
|
|
|
}
|