using DynamicQuery.Net.Dto.Input;
|
using LifePayment.Application.Contracts;
|
using LifePayment.Domain;
|
using LifePayment.Domain.Models;
|
using LifePayment.Domain.Shared;
|
using MailKit.Search;
|
using Microsoft.EntityFrameworkCore;
|
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Options;
|
using Nest;
|
using Newtonsoft.Json;
|
using NPOI.OpenXmlFormats.Dml.Chart;
|
using NPOI.SS.Formula.Functions;
|
using StackExchange.Redis;
|
using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Security.Cryptography;
|
using System.Threading.Tasks;
|
using Volo.Abp;
|
using Volo.Abp.Application.Services;
|
using Volo.Abp.Domain.Entities;
|
using Volo.Abp.Domain.Repositories;
|
using Volo.Abp.EventBus.Distributed;
|
using ZeroD.Util;
|
using ZeroD.Util.Fadd;
|
using static LifePayment.Domain.Shared.LifePaymentConstant;
|
using static IdentityServer4.Models.IdentityResources;
|
|
namespace LifePayment.Application;
|
|
public class LifePayService : ApplicationService, ILifePayService
|
{
|
private readonly IDistributedEventBus _distributedEventBus;
|
private readonly ILogger<LifePayService> _logger;
|
|
private readonly IRepository<LifePayRate, Guid> _lifePayRateRepository;
|
private readonly IRepository<LifePayOrder, Guid> _lifePayOrderRepository;
|
private readonly IRepository<LifePayUser, Guid> _lifePayUserRepository;
|
|
private readonly IAliPayApi _aliPayApi;
|
private readonly IWxPayApi _wxPayApi;
|
private readonly WxPayOption _wxPayOptions;
|
|
private readonly IACOOLYManager _aCOOLYManager;
|
|
public LifePayService(IDistributedEventBus distributedEventBus,
|
ILogger<LifePayService> logger,
|
IACOOLYManager aCOOLYManager,
|
IRepository<LifePayRate, Guid> lifePayRateRepository,
|
IRepository<LifePayOrder, Guid> lifePayOrderRepository,
|
IRepository<LifePayUser, Guid> lifePayUserRepository,
|
IAliPayApi aliPayApi,
|
IWxPayApi wxPayApi,
|
IOptions<WxPayOption> wxPayOptions)
|
{
|
_logger = logger;
|
_aCOOLYManager = aCOOLYManager;
|
_lifePayRateRepository = lifePayRateRepository;
|
_lifePayOrderRepository = lifePayOrderRepository;
|
_lifePayUserRepository = lifePayUserRepository;
|
_aliPayApi = aliPayApi;
|
_wxPayApi = wxPayApi;
|
_wxPayOptions = wxPayOptions.Value;
|
_distributedEventBus = distributedEventBus;
|
}
|
|
#region 查询
|
|
/// <summary>
|
/// 获取电费面值
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<ElectricParValueResponse> GetElectricParValue()
|
{
|
return await _aCOOLYManager.ElectricParValue(new ACOOLYRequestBaseInput());
|
}
|
|
/// <summary>
|
/// 获取电费充值区域
|
/// </summary>
|
/// <returns></returns>
|
public async Task<ElectricSupportAreaResponse> GetElectricSupportArea()
|
{
|
return await _aCOOLYManager.GetElectricSupportArea(new ACOOLYRequestBaseInput());
|
}
|
|
/// <summary>
|
/// 获取话费面值
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<PhoneParValueResponse> GetPhoneParValue()
|
{
|
return await _aCOOLYManager.PhoneParValue(new ACOOLYRequestBaseInput());
|
}
|
|
/// <summary>
|
/// 获取折扣
|
/// </summary>
|
/// <returns></returns>
|
public async Task<List<LifePayRateListOutput>> GetRate()
|
{
|
return await _lifePayRateRepository.Select(x => new LifePayRateListOutput() { Rate = x.Rate, RateType = x.RateType })
|
.ToListAsync();
|
}
|
|
/// <summary>
|
/// 获取用户分页数据
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<PageOutput<UserListOutput>> GetUserPage(PageInput input)
|
{
|
return await _lifePayUserRepository.Select(x =>
|
new UserListOutput()
|
{
|
Id = x.Id,
|
PhoneNumber = x.PhoneNumber,
|
CreationTime = x.CreationTime,
|
LastLoginTime = x.LastLoginTime
|
})
|
.GetPageResult(input.PageModel);
|
}
|
|
/// <summary>
|
/// 获取订单分页数据
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<PageOutput<LifePayOrderListOutput>> GetLifePayOrderPage(QueryLifePayOrderListInput input)
|
{
|
return await _lifePayOrderRepository.Where(x => x.PayStatus != LifePayStatusEnum.未支付)
|
.WhereIf(input.BeginFinishTime.HasValue, x => x.FinishTime >= input.BeginFinishTime)
|
.WhereIf(input.EndFinishTime.HasValue, x => x.FinishTime <= input.EndFinishTime)
|
.WhereIf(input.BeginPayTime.HasValue, x => x.PayTime >= input.BeginPayTime)
|
.WhereIf(input.EndPayTime.HasValue, x => x.PayTime <= input.EndPayTime)
|
.WhereIf(input.LifePayOrderStatus.HasValue, x => x.LifePayOrderStatus == input.LifePayOrderStatus.Value)
|
.WhereIf(input.PayStatus.HasValue, x => x.PayStatus == input.PayStatus.Value)
|
.WhereIf(input.LifePayOrderType.HasValue, x => x.LifePayOrderType == input.LifePayOrderType.Value)
|
.WhereIf(input.UserId.HasValue, x => x.UserId == input.UserId.Value)
|
.Select(x =>
|
new LifePayOrderListOutput
|
{
|
DiscountAmount = x.DiscountAmount,
|
FinishTime = x.FinishTime,
|
Id = x.Id,
|
LifePayOrderStatus = x.LifePayOrderStatus,
|
LifePayOrderType = x.LifePayOrderType,
|
LifePayType = x.LifePayType,
|
OrderNo = x.OrderNo,
|
PayAmount = x.PayAmount,
|
PhoneNumber = x.PhoneNumber,
|
RechargeAmount = x.RechargeAmount,
|
UserId = x.UserId,
|
OutOrderNo = x.OutOrderNo,
|
PayStatus = x.PayStatus,
|
PayTime = x.PayTime,
|
RefundCredentialsImgUrl = x.RefundCredentialsImgUrl,
|
CreationTime = x.CreationTime
|
})
|
.GetPageResult(input.PageModel);
|
}
|
|
/// <summary>
|
/// 获取我的订单分页数据
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<PageOutput<UserLifePayOrderOutput>> GetUserLifePayOrderPage(QueryLifePayOrderListInput input)
|
{
|
if (!input.UserId.HasValue)
|
{
|
return new PageOutput<UserLifePayOrderOutput>();
|
}
|
|
return await _lifePayOrderRepository.Where(x => x.PayStatus != LifePayStatusEnum.未支付)
|
.WhereIf(input.BeginFinishTime.HasValue, x => x.FinishTime >= input.BeginFinishTime)
|
.WhereIf(input.EndFinishTime.HasValue, x => x.FinishTime <= input.EndFinishTime)
|
.WhereIf(input.BeginPayTime.HasValue, x => x.PayTime >= input.BeginPayTime)
|
.WhereIf(input.EndPayTime.HasValue, x => x.PayTime <= input.EndPayTime)
|
.WhereIf(input.LifePayOrderStatus.HasValue, x => x.LifePayOrderStatus == input.LifePayOrderStatus.Value)
|
.WhereIf(input.PayStatus.HasValue, x => x.PayStatus == input.PayStatus.Value)
|
.WhereIf(input.UserId.HasValue, x => x.UserId == input.UserId.Value)
|
.WhereIf(input.LifePayOrderType.HasValue, x => x.LifePayOrderType == input.LifePayOrderType)
|
.Select(x =>
|
new UserLifePayOrderOutput
|
{
|
DiscountAmount = x.DiscountAmount,
|
FinishTime = x.FinishTime,
|
Id = x.Id,
|
LifePayOrderStatus = x.LifePayOrderStatus,
|
LifePayOrderType = x.LifePayOrderType,
|
LifePayType = x.LifePayType,
|
OrderNo = x.OrderNo,
|
PayAmount = x.PayAmount,
|
RechargeAmount = x.RechargeAmount,
|
PayStatus = x.PayStatus,
|
PayTime = x.PayTime,
|
OrderParamDetailJsonStr = x.OrderParamDetailJsonStr,
|
CreationTime = x.CreationTime,
|
RefundTime = x.RefundTime
|
})
|
.GetPageResult(input.PageModel);
|
}
|
|
/// <summary>
|
/// 根据订单号获取支付状态
|
/// </summary>
|
/// <param name="orderNo"></param>
|
/// <returns></returns>
|
public async Task<LifePayStatusEnum> GetPayStatusByOrderNo(string orderNo)
|
{
|
return await _lifePayOrderRepository.Where(x => x.OrderNo == orderNo)
|
.Select(x => x.PayStatus)
|
.FirstOrDefaultAsync();
|
}
|
|
#endregion
|
|
#region 操作
|
|
/// <summary>
|
/// 创建生活缴费话费订单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<CreateLifePayOrderOutput> CreateLifePayPhoneOrder(CreateLifePayOrderInput<LifePhoneData> input)
|
{
|
var user = await _lifePayUserRepository.FirstOrDefaultAsync(x => x.Id == input.UserId);
|
CheckExtensions.IfTrueThrowUserFriendlyException(user == null, "用户不存在,请先登录再操作");
|
|
var rate = await GetRate();
|
CheckExtensions.IfTrueThrowUserFriendlyException(rate.IsNullOrEmpty(), "未配置折扣");
|
|
var amount = CalculateAmount(input.ProductData.ParValue, rate.FirstOrDefault(x => x.RateType == LifePayRateTypeEnum.默认话费折扣).Rate);
|
|
var orderInput = new CreateLifePayOrderInput
|
{
|
OrderNo = CreateOrderNo(),
|
LifePayOrderStatus = LifePayOrderStatusEnum.待确认,
|
LifePayOrderType = LifePayOrderTypeEnum.话费订单,
|
//LifePayType = input.LifePayType,
|
OrderParamDetailJsonStr = JsonConvert.SerializeObject(input.ProductData),
|
UserId = user.Id,
|
PayStatus = LifePayStatusEnum.未支付,
|
PhoneNumber = user.PhoneNumber,
|
PayAmount = amount.PayAmont,
|
DiscountAmount = amount.DiscountAmount,
|
RechargeAmount = amount.RechargeAmount,
|
};
|
|
await CreateLifePayOrder(orderInput);
|
|
var result = new CreateLifePayOrderOutput()
|
{
|
OrderNo = orderInput.OrderNo,
|
};
|
return result;
|
}
|
|
/// <summary>
|
/// 创建生活缴费电费订单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<CreateLifePayOrderOutput> CreateLifePayElectricOrder(CreateLifePayOrderInput<LifeElectricData> input)
|
{
|
var user = await _lifePayUserRepository.FirstOrDefaultAsync(x => x.Id == input.UserId);
|
CheckExtensions.IfTrueThrowUserFriendlyException(user == null, "用户不存在,请先登录再操作");
|
|
var rate = await GetRate();
|
CheckExtensions.IfTrueThrowUserFriendlyException(rate.IsNullOrEmpty(), "未配置折扣");
|
|
var amount = CalculateAmount(input.ProductData.ParValue, rate.FirstOrDefault(x => x.RateType == LifePayRateTypeEnum.默认电费折扣).Rate);
|
|
var orderInput = new CreateLifePayOrderInput
|
{
|
OrderNo = CreateOrderNo(),
|
LifePayOrderStatus = LifePayOrderStatusEnum.待确认,
|
LifePayOrderType = LifePayOrderTypeEnum.电费订单,
|
// LifePayType = input.LifePayType,
|
OrderParamDetailJsonStr = JsonConvert.SerializeObject(input.ProductData),
|
UserId = user.Id,
|
PayStatus = LifePayStatusEnum.未支付,
|
PhoneNumber = user.PhoneNumber,
|
PayAmount = amount.PayAmont,
|
DiscountAmount = amount.DiscountAmount,
|
RechargeAmount = amount.RechargeAmount,
|
};
|
|
await CreateLifePayOrder(orderInput);
|
|
var result = new CreateLifePayOrderOutput()
|
{
|
OrderNo = orderInput.OrderNo,
|
};
|
return result;
|
}
|
|
/// <summary>
|
/// 设置生活缴费支付类型
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<string> SetLifePayOrderPayType(SetLifePayOrderPayTypeInput input, string ip)
|
{
|
var order = await _lifePayOrderRepository.Where(x => x.OrderNo == input.OrderNo).FirstOrDefaultAsync();
|
CheckExtensions.IfTrueThrowUserFriendlyException(order == null, "订单不存在");
|
|
CheckExtensions.IfTrueThrowUserFriendlyException(order.LifePayType.HasValue, "当前订单已选择支付类型");
|
|
order.LifePayType = input.LifePayType;
|
|
await _lifePayOrderRepository.UpdateAsync(order);
|
|
var desc = "生活缴费-";
|
switch (order.LifePayOrderType)
|
{
|
case LifePayOrderTypeEnum.话费订单:
|
desc += "话费";
|
break;
|
case LifePayOrderTypeEnum.电费订单:
|
desc += "电费";
|
break;
|
default:
|
break;
|
}
|
|
#if DEBUG
|
//var payUrl = await GetPayQRCode(order.LifePayType.Value, order.OrderNo, desc, 0.01m, ip, input.H5Type);
|
var payUrl = await GetPayQRCode(order.LifePayType.Value, order.OrderNo, desc, order.PayAmount, ip, input.H5Type);
|
#else
|
//var payUrl = await GetPayQRCode(order.LifePayType.Value, order.OrderNo, desc, 0.01m, ip, input.H5Type);
|
var payUrl = await GetPayQRCode(order.LifePayType.Value, order.OrderNo, desc, order.PayAmount, ip, input.H5Type);
|
#endif
|
|
return payUrl;
|
}
|
|
/// <summary>
|
/// 设置生活缴费支付类型
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<ModelPaymentMiniPay> GetPayOrderForJsAPI(GetPayOrderForJsAPIInput input, string ip)
|
{
|
var order = await _lifePayOrderRepository.Where(x => x.OrderNo == input.OrderNo).FirstOrDefaultAsync();
|
CheckExtensions.IfTrueThrowUserFriendlyException(order == null, "订单不存在");
|
|
CheckExtensions.IfTrueThrowUserFriendlyException(order.LifePayType.HasValue, "当前订单已选择支付类型");
|
|
order.LifePayType = input.LifePayType;
|
await _lifePayOrderRepository.UpdateAsync(order);
|
|
var desc = "生活缴费-";
|
switch (order.LifePayOrderType)
|
{
|
case LifePayOrderTypeEnum.话费订单:
|
desc += "话费";
|
break;
|
case LifePayOrderTypeEnum.电费订单:
|
desc += "电费";
|
break;
|
default:
|
break;
|
}
|
|
|
//var result = await PayTransactionsJsAPI(input.OpenId, input.Attach, 0.01m, input.OrderNo, desc);
|
//return result;
|
|
// var result = await PayTransactionsJsAPI(input.OpenId, input.Attach, order.PayAmount, input.OrderNo, desc);
|
|
var result = await PayTransactionsJsAPI(input.OpenId, input.Attach, order.PayAmount, input.OrderNo, desc);
|
return result;
|
//var payUrl = await GetPayQRCode(order.LifePayType.Value, order.OrderNo, desc, 0.01m, ip, input.H5Type);
|
//var payUrl = await GetPayQRCode(order.LifePayType.Value, order.OrderNo, desc, order.PayAmount, ip, input.H5Type);
|
|
|
|
}
|
|
/// <summary>
|
/// 创建生活缴费订单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task CreateLifePayOrder(CreateLifePayOrderInput input)
|
{
|
CheckExtensions.IfTrueThrowUserFriendlyException(input.RechargeAmount < 0.01m || input.PayAmount < 0.01m || input.DiscountAmount < 0, "订单金额错误");
|
|
var entity = ObjectMapper.Map<CreateLifePayOrderInput, LifePayOrder>(input);
|
await _lifePayOrderRepository.InsertAsync(entity);
|
}
|
|
/// <summary>
|
/// 创建ACOOLY话费订单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<(string Code, string RequestNo, string ACOOLYOrderNo)> CreateACOOLYPhoneOrder(LifePhoneData input, string orderNo)
|
{
|
var requestInput = new ConfirmPhoneOrderRequestInput()
|
{
|
IspCode = input.IspCode,
|
ParValue = input.ParValue,
|
Phone = input.Phone,
|
OutOrderNo = orderNo,
|
Name = input.Name
|
};
|
var result = await _aCOOLYManager.ConfirmPhoneOrder(requestInput);
|
#if DEBUG
|
|
_logger.LogInformation($"CreateACOOLYPhoneOrder:{JsonConvert.SerializeObject(result)}");
|
|
#endif
|
|
CheckExtensions.IfTrueThrowUserFriendlyException(!result.Success || (result.Code != ACOOLYConstant.Code.SUCCESS && result.Code != ACOOLYConstant.Code.PROCESSING),
|
"调用ACOOLY接口ConfirmElectricOrder返回错误:" + JsonConvert.SerializeObject(result));
|
|
return (result.Code, requestInput.RequestNo, result.PhoneChargeOrder.BusiOrderNo);
|
}
|
|
/// <summary>
|
/// 创建ACOOLY电费订单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<(string Code, string RequestNo, string ACOOLYOrderNo)> CreateACOOLYElectricOrder(LifeElectricData input, string orderNo)
|
{
|
var requestInput = new ConfirmElectricOrderRequestInput()
|
{
|
City = input.City,
|
ElectricAccount = input.ElectricAccount,
|
ElectricAccountType = input.ElectricAccountType,
|
ElectricType = input.ElectricType,
|
ParValue = input.ParValue,
|
Province = input.Province,
|
SixID = input.SixID,
|
OutOrderNo = orderNo
|
};
|
var result = await _aCOOLYManager.ConfirmElectricOrder(requestInput);
|
#if DEBUG
|
|
_logger.LogInformation($"CreateACOOLYElectricOrder:{JsonConvert.SerializeObject(result)}");
|
|
#endif
|
|
CheckExtensions.IfTrueThrowUserFriendlyException(!result.Success || (result.Code != ACOOLYConstant.Code.SUCCESS && result.Code != ACOOLYConstant.Code.PROCESSING),
|
"调用ACOOLY接口ConfirmElectricOrder返回错误:" + JsonConvert.SerializeObject(result));
|
|
return (result.Code, requestInput.RequestNo, result.ElectricChargeOrder.BusiOrderNo);
|
}
|
|
public async Task<ModelPaymentMiniPay> PayTransactionsJsAPI(string openid, string order_guid, decimal amount, string outTradeNo, string description)
|
{
|
string time_expire = DateTime.Now.AddMinutes(5).ToString("yyyy-MM-ddTHH:mm:ss") + "+08:00";//订单失效时间
|
ModelMiniPayRequest req = new ModelMiniPayRequest
|
{
|
|
TimeExpire = time_expire,
|
Appid = _wxPayOptions.AppID,
|
Mchid = _wxPayOptions.Mchid,
|
Attach = order_guid,
|
Description = description,
|
OutTradeNo = outTradeNo,
|
Amount = new Model_MiniPay_Amount
|
{
|
Total = Convert.ToInt32(100 * amount),
|
Currency = "CNY"
|
},
|
NotifyUrl = $"{_wxPayOptions.NotifyUrl}{LifePaymentConstant.WxRechargeNotifySectionUrl}",
|
Payer = new Model_MiniPay_Payer
|
{
|
OpenId = openid
|
}
|
};
|
var timestamp = DateTimeOffset.Now.ToUnixTimeSeconds();
|
string nonce = Guid.NewGuid().ToString();
|
|
var res = await _wxPayApi.PayTransactionsJsAPI(req);
|
string package = "prepay_id=" + res.PrepayId;
|
ModelPaymentMiniPay info = new ModelPaymentMiniPay();
|
if (res != null)
|
{
|
|
string paytext = BuildSignByPay(_wxPayOptions.AppID, timestamp, nonce, package);
|
string paysign = _wxPayApi.GeneratePaySignByKey(paytext);
|
|
info.Timestamp = timestamp.ToString();
|
info.NonceStr = nonce;
|
info.Package = package;
|
info.SignType = "RSA";
|
info.PaySign = paysign;
|
info.TimeExpire = time_expire;
|
}
|
return info;
|
}
|
|
/// <summary>
|
/// 构造签名串
|
/// </summary>
|
/// <param name="appId">小程序的AppID</param>
|
/// <param name="timestamp">时间戳</param>
|
/// <param name="nonceStr">随机字符串</param>
|
/// <param name="package">package值</param>
|
/// <returns>签名串</returns>
|
public string BuildSignByPay(string appId, long timestamp, string nonceStr, string package)
|
{
|
string format = $"{appId}\n{timestamp}\n{nonceStr}\n{package}\n";
|
return format;
|
}
|
|
/// <summary>
|
/// 支付成功回调处理
|
/// </summary>
|
/// <param name="orderNo"></param>
|
/// <param name="outOrderNo"></param>
|
/// <returns></returns>
|
public async Task LifePaySuccessHandler(string orderNo, string outOrderNo)
|
{
|
var order = await _lifePayOrderRepository.Where(x => x.OrderNo == orderNo).FirstOrDefaultAsync();
|
CheckExtensions.IfTrueThrowUserFriendlyException(order == null, "订单不存在");
|
|
if (order.PayStatus == LifePayStatusEnum.已支付)
|
{
|
return;
|
}
|
|
order.PayStatus = LifePayStatusEnum.已支付;
|
order.PayTime = DateTime.Now;
|
order.OutOrderNo = outOrderNo;
|
try
|
{
|
var result = (Code: "Fail", RequestNo: "", ACOOLYOrderNo: "");
|
switch (order.LifePayOrderType)
|
{
|
case LifePayOrderTypeEnum.话费订单:
|
result = await CreateACOOLYPhoneOrder(JsonConvert.DeserializeObject<LifePhoneData>(order.OrderParamDetailJsonStr), order.OrderNo);
|
break;
|
case LifePayOrderTypeEnum.电费订单:
|
result = await CreateACOOLYElectricOrder(JsonConvert.DeserializeObject<LifeElectricData>(order.OrderParamDetailJsonStr), order.OrderNo);
|
break;
|
default:
|
break;
|
}
|
|
order.LifePayOrderStatus = LifePayOrderStatusEnum.待确认;
|
//SetOrderStatus(order, result.Code);
|
order.OutRequestNo = result.RequestNo.IsNullOrEmpty() ? null : result.RequestNo;
|
order.ACOOLYOrderNo = result.ACOOLYOrderNo.IsNullOrEmpty() ? null : result.ACOOLYOrderNo;
|
}
|
catch (Exception ex)
|
{
|
_logger.LogError(ex, "处理生活缴费支付成功回调时异常");
|
order.LifePayOrderStatus = LifePayOrderStatusEnum.已失败;
|
order.PayStatus = LifePayStatusEnum.待退款;
|
}
|
_logger.LogError("生活缴费订单状态:" + order.LifePayOrderStatus.ToString());
|
await _lifePayOrderRepository.UpdateAsync(order);
|
}
|
|
/// <summary>
|
/// ACOOLYO订单通知处理
|
/// </summary>
|
/// <param name="orderNo"></param>
|
/// <param name="outOrderNo"></param>
|
/// <returns></returns>
|
public async Task ACOOLYOrderNotifyHandler(string orderNo, string acoolyOrderNo, LifePayOrderStatusEnum status)
|
{
|
var order = await _lifePayOrderRepository.Where(x => x.OrderNo == orderNo).FirstOrDefaultAsync();
|
CheckExtensions.IfTrueThrowUserFriendlyException(order == null, "订单不存在");
|
|
if (order.LifePayOrderStatus == LifePayOrderStatusEnum.已完成)
|
{
|
return;
|
}
|
|
order.LifePayOrderStatus = status;
|
if (acoolyOrderNo.IsNotNullOrEmpty())
|
{
|
order.ACOOLYOrderNo = acoolyOrderNo;
|
}
|
|
if (order.LifePayOrderStatus == LifePayOrderStatusEnum.已完成)
|
{
|
order.FinishTime = DateTime.Now;
|
}
|
|
if (order.LifePayOrderStatus == LifePayOrderStatusEnum.已失败 && order.PayStatus != LifePayStatusEnum.已退款)
|
{
|
order.PayStatus = LifePayStatusEnum.待退款;
|
}
|
|
await _lifePayOrderRepository.UpdateAsync(order);
|
}
|
|
/// <summary>
|
/// 退款生活缴费订单
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
/// <exception cref="UserFriendlyException"></exception>
|
public async Task RefundLifePayOrder(RefundLifePayOrderInput input)
|
{
|
var order = await _lifePayOrderRepository.FirstOrDefaultAsync(x => x.Id == input.Id);
|
CheckExtensions.IfTrueThrowUserFriendlyException(order == null, "订单不存在");
|
|
if (order.PayStatus == LifePayStatusEnum.已退款)
|
{
|
return;
|
}
|
|
if (order.LifePayOrderStatus != LifePayOrderStatusEnum.已失败 && order.PayStatus != LifePayStatusEnum.待退款)
|
{
|
throw new UserFriendlyException("当前订单状态无法退款");
|
}
|
|
order.PayStatus = LifePayStatusEnum.已退款;
|
order.RefundCredentialsImgUrl = input.RefundCredentialsImgUrl;
|
order.RefundTime = DateTime.Now;
|
|
await _lifePayOrderRepository.UpdateAsync(order);
|
|
#region 记录日志
|
|
await PublishLifePayOrderHistoryEvent("退款", "退款", order.Id);
|
|
#endregion
|
|
}
|
|
#endregion
|
|
#region 私有
|
|
private string CreateOrderNo()
|
{
|
return "JF" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + GlobalRandom.GetRandomNum(4);
|
}
|
|
private (decimal PayAmont, decimal DiscountAmount, decimal RechargeAmount) CalculateAmount(decimal amount, decimal rate)
|
{
|
var payAmount = decimal.Round(amount * rate, 2, MidpointRounding.AwayFromZero);
|
CheckExtensions.IfTrueThrowUserFriendlyException(payAmount < 0.01m, "支付金额错误");
|
|
return (payAmount, amount - payAmount, amount);
|
}
|
|
private void SetOrderStatus(LifePayOrder order, string code)
|
{
|
switch (code)
|
{
|
case ACOOLYConstant.Code.SUCCESS:
|
order.LifePayOrderStatus = LifePayOrderStatusEnum.已完成;
|
order.FinishTime = DateTime.Now;
|
break;
|
case ACOOLYConstant.Code.PROCESSING:
|
order.LifePayOrderStatus = LifePayOrderStatusEnum.待确认;
|
break;
|
default:
|
break;
|
}
|
}
|
|
/// <summary>
|
/// 记录相关的操作历史
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
private async Task PublishLifePayOrderHistoryEvent(string operateContent, string operateName, Guid relationId, int? tableType = TableType.LifePayOrder)
|
{
|
var recordEto = new RecordOperateHistoryEto
|
{
|
CreatorName = CurrentUser.Name,
|
OperateContent = operateContent,
|
OperateName = operateName,
|
RelationId = relationId,
|
TableType = tableType
|
};
|
|
await _distributedEventBus.PublishAsync(recordEto, false);
|
}
|
|
/// <summary>
|
/// 获取支付二维码
|
/// </summary>
|
/// <param name="payType"></param>
|
/// <param name="outTradeNo"></param>
|
/// <param name="description"></param>
|
/// <param name="amount"></param>
|
/// <returns></returns>
|
/// <exception cref="UserFriendlyException"></exception>
|
public async Task<string> GetPayQRCode(LifePayTypeEnum payType, string outTradeNo, string description, decimal amount, string ip, string h5Type)
|
{
|
var codeUrl = string.Empty;
|
switch (payType)
|
|
|
|
{
|
case LifePayTypeEnum.AliPay:
|
codeUrl = await GetAliPayQRCode(outTradeNo, description, amount);
|
|
break;
|
case LifePayTypeEnum.WxPay:
|
codeUrl = await GetWxPayQRCode(outTradeNo, description, amount); //native
|
// codeUrl = await GetWxPayH5Url(outTradeNo, description, amount, ip, h5Type); //h5
|
break;
|
default:
|
throw new UserFriendlyException(string.Format(CustomeErrorMessage.NotImplementedWhenSometing, "获取支付二维码"));
|
}
|
|
return codeUrl;
|
}
|
|
/// <summary>
|
/// 获取微信支付二维码
|
/// </summary>
|
/// <param name="outTradeNo"></param>
|
/// <param name="description"></param>
|
/// <param name="amount"></param>
|
/// <returns></returns>
|
/// <exception cref="UserFriendlyException"></exception>
|
private async Task<string> GetWxPayQRCode(string outTradeNo, string description, decimal amount)
|
{
|
CheckExtensions.IfTrueThrowUserFriendlyException(amount <= 0,
|
CustomeErrorMessage.SometingMustSometing,
|
"获取支付二维码时金额", "大于0");
|
|
var res = await _wxPayApi.PayTransactionsNative(new PayTransactionsNativeInput
|
{
|
Appid = _wxPayOptions.AppID,
|
Mchid = _wxPayOptions.Mchid,
|
Description = description,
|
OutTradeNo = outTradeNo,
|
Amount = new PayAmount
|
{
|
Total = Convert.ToInt32(100 * amount),
|
},
|
NotifyUrl = $"{_wxPayOptions.NotifyUrl}{LifePaymentConstant.WxRechargeNotifySectionUrl}",
|
});
|
|
if (res == null || res.CodeUrl.IsNullOrEmpty())
|
{
|
_logger.LogError($"获取微信支付二维码异常:{(res == null ? "返回参数为空" : $"{res.Code} --{res.Message}")}");
|
throw new UserFriendlyException(string.Format(CustomeErrorMessage.SometingError, "获取支付二维码时"));
|
}
|
|
return res.CodeUrl;
|
}
|
|
/// <summary>
|
/// 获取微信支付二维码
|
/// </summary>
|
/// <param name="outTradeNo"></param>
|
/// <param name="description"></param>
|
/// <param name="amount"></param>
|
/// <returns></returns>
|
/// <exception cref="UserFriendlyException"></exception>
|
private async Task<string> GetWxPayH5Url(string outTradeNo, string description, decimal amount, string ip, string h5Type)
|
{
|
CheckExtensions.IfTrueThrowUserFriendlyException(amount <= 0,
|
CustomeErrorMessage.SometingMustSometing,
|
"获取支付二维码时金额", "大于0");
|
|
var res = await _wxPayApi.PayTransactionsH5(new PayTransactionsNativeH5
|
{
|
Appid = _wxPayOptions.AppID,
|
Mchid = _wxPayOptions.Mchid,
|
Description = description,
|
OutTradeNo = outTradeNo,
|
Amount = new PayAmount
|
{
|
Total = Convert.ToInt32(100 * amount),
|
},
|
NotifyUrl = $"{_wxPayOptions.NotifyUrl}{LifePaymentConstant.WxRechargeNotifySectionUrl}",
|
SceneInfo = new H5SceneInfo() { PayerClientIp = ip, H5Info = new H5Info() { Type = h5Type } }
|
});
|
|
if (res == null || res.H5Url.IsNullOrEmpty())
|
{
|
_logger.LogError($"获取微信支付Url异常:{(res == null ? "返回参数为空" : $"{res.Code} --{res.Message}")}");
|
throw new UserFriendlyException(string.Format(CustomeErrorMessage.SometingError, "获取支付Url时"));
|
}
|
|
return res.H5Url;
|
}
|
|
/// <summary>
|
/// 获取支付宝二维码
|
/// </summary>
|
/// <param name="outTradeNo"></param>
|
/// <param name="description"></param>
|
/// <param name="amount"></param>
|
/// <returns></returns>
|
/// <exception cref="UserFriendlyException"></exception>
|
private async Task<string> GetAliPayQRCode(string outTradeNo, string description, decimal amount)
|
{
|
CheckExtensions.IfTrueThrowUserFriendlyException(amount < 0.01M,
|
CustomeErrorMessage.SometingMustSometing,
|
"获取支付二维码时金额", "大于0.01");
|
|
var res = await _aliPayApi.GetAliPayQRCode(new GetPayQrCodeInput
|
{
|
Subject = description,
|
OutTradeNo = outTradeNo,
|
TotalAmount = amount,
|
});
|
|
if (res == null || res.QrCode.IsNullOrEmpty())
|
{
|
_logger.LogError($"获取支付宝支付二维码异常:{(res == null ? "返回参数为空" : $"{res.Code} --{res.Msg}")}");
|
throw new UserFriendlyException(string.Format(CustomeErrorMessage.SometingError, "获取支付二维码时"));
|
}
|
|
return res.QrCode;
|
}
|
/// <summary>
|
/// 构造待签名字符串
|
/// </summary>
|
/// <param name="method">HTTP请求方法</param>
|
/// <param name="uri">API接口的相对路径</param>
|
/// <param name="timestamp">时间戳(秒级)</param>
|
/// <param name="nonce">随机字符串</param>
|
/// <param name="body">请求体的JSON字符串</param>
|
/// <returns>待签名字符串</returns>
|
public static string BuildSignByUniOrder(string method, string uri, long timestamp, string nonce, string body)
|
{
|
string message = $"{method}\n{uri}\n{timestamp}\n{nonce}\n{body}\n";
|
return message;
|
}
|
|
|
#endregion
|
}
|