| | |
| | | { |
| | | Task<LifePayRechargeReceiptsPageOutput<LifePayRechargeReceiptsListOutput>> GetLifePayRechargeReceiptsPage(LifePayRechargeReceiptsPageInput input); |
| | | |
| | | |
| | | Task AddUpdatePayRechargeReceipts(AddUpdatePayRechargeReceiptsInput input); |
| | | |
| | | Task AddLifePayExpensesReceipts(AddLifePayExpensesReceiptsInput input); |
| | | } |
| | |
| | | public decimal PayAmount { get; set; } |
| | | |
| | | public LifePayTypeEnum LifePayType { get; set; } |
| | | } |
| | | public class AddLifePayExpensesReceiptsInput |
| | | { |
| | | /// <summary> |
| | | /// 平台订单号 |
| | | /// </summary> |
| | | public string OrderNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 渠道流水号 |
| | | /// </summary> |
| | | public string OutOrderNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 支付渠道 |
| | | /// </summary> |
| | | public LifePayTypeEnum LifePayType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 收支类型 |
| | | /// </summary> |
| | | public ExpensesReceiptsTypeEnum ExpensesReceiptsType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交易金额 |
| | | /// </summary> |
| | | public decimal Amount { get; set; } |
| | | } |
| | |
| | | using Volo.Abp.Domain.Repositories; |
| | | using Microsoft.EntityFrameworkCore; |
| | | using ZeroD.Util; |
| | | using LifePayment.Domain; |
| | | |
| | | namespace LifePayment.Application.LifePay |
| | | { |
| | | public class LifePayOrderService : ApplicationService, ILifePayOrderService |
| | | { |
| | | private readonly IRepository<LifePayRechargeReceipts, Guid> _lifePayRechargeReceiptsRepository; |
| | | private readonly IRepository<LifePayExpensesReceipts, Guid> _lifePayExpensesReceiptsRepository; |
| | | private readonly IAliPayApi _aliPayApi; |
| | | private readonly IWxPayApi _wxPayApi; |
| | | |
| | | public LifePayOrderService( |
| | | IRepository<LifePayRechargeReceipts, Guid> lifePayRechargeReceiptsRepository) |
| | | IRepository<LifePayRechargeReceipts, Guid> lifePayRechargeReceiptsRepository, |
| | | IRepository<LifePayExpensesReceipts, Guid> lifePayExpensesReceiptsRepository, |
| | | IAliPayApi aliPayApi, |
| | | IWxPayApi wxPayApi) |
| | | { |
| | | _lifePayRechargeReceiptsRepository = lifePayRechargeReceiptsRepository; |
| | | _lifePayExpensesReceiptsRepository = lifePayExpensesReceiptsRepository; |
| | | _aliPayApi = aliPayApi; |
| | | _wxPayApi = wxPayApi; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | return result; |
| | | } |
| | | |
| | | //public async Task<decimal> GetTotalLifePayRechargeReceipts() |
| | | //{ |
| | | // var result = await _lifePayRechargeReceiptsRepository.Where(x => x.IsDeleted == false).SumAsync(x => x.RechargeAmount); |
| | | // return result; |
| | | //} |
| | | |
| | | |
| | | /// <summary> |
| | | /// 编辑充值流水 |
| | |
| | | await _lifePayRechargeReceiptsRepository.InsertAsync(payRechargeReceipts); |
| | | } |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 插入收支流水 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | public async Task AddLifePayExpensesReceipts(AddLifePayExpensesReceiptsInput input) |
| | | { |
| | | var repeat = await _lifePayExpensesReceiptsRepository.Where(x => x.ExpensesReceiptsType == input.ExpensesReceiptsType |
| | | && x.OrderNo == input.OrderNo).FirstOrDefaultAsync(); |
| | | if (repeat == null) |
| | | { |
| | | var data = new LifePayExpensesReceipts() |
| | | { |
| | | Id = Guid.NewGuid(), |
| | | OrderNo = input.OrderNo, |
| | | OutOrderNo = input.OutOrderNo, |
| | | LifePayType = input.LifePayType, |
| | | ExpensesReceiptsType = input.ExpensesReceiptsType, |
| | | Amount = input.Amount |
| | | }; |
| | | |
| | | switch (input.LifePayType) |
| | | { |
| | | case LifePayTypeEnum.AliPay: |
| | | if (input.ExpensesReceiptsType == ExpensesReceiptsTypeEnum.Expenses) |
| | | { |
| | | var query = await _aliPayApi.OrderInQuiry(new OrderInQuiryInput() { OutTradeNo = input.OrderNo }); |
| | | if (query.Code == AlipayResultCode.Success && query.TradeStatus == AlipayStatus.TRADESUCCESS) |
| | | { |
| | | await _lifePayExpensesReceiptsRepository.InsertAsync(data); |
| | | } |
| | | } |
| | | else |
| | | { |
| | | var query = await _aliPayApi.QueryAlipayTradeRefund(new OrderInQuiryInput() { OutTradeNo = input.OrderNo }); |
| | | if (query.Code == AlipayResultCode.Success && query.RefundStatus == AlipayRefundStatus.Success) |
| | | { |
| | | await _lifePayExpensesReceiptsRepository.InsertAsync(data); |
| | | } |
| | | } |
| | | break; |
| | | case LifePayTypeEnum.WxPay: break; |
| | | default: break; |
| | | } |
| | | |
| | | |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | break; |
| | | case LifePayTypeEnum.AliPay: |
| | | var aliRefundResult = await AliTradeRefund(new AlipayTradeRefundRequest() { OutTradeNo = order.OrderNo, RefundAmount = input.RefundPrice.ToString() }); |
| | | var aliRefundResult = await AliTradeRefund(new AlipayTradeRefundRequest() { OutTradeNo = order.OrderNo, OutRefundNo = outRefundNo, RefundAmount = input.RefundPrice.ToString() }); |
| | | if (aliRefundResult.Code != AlipayResultCode.Success) |
| | | { |
| | | throw new UserFriendlyException("退款失败:" + aliRefundResult.SubMsg); |
| | |
| | | /// </summary> |
| | | public string OutTradeNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 退款金额 |
| | | /// </summary> |
| | | public string RefundAmount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 退款订单号 |
| | | /// </summary> |
| | | public string OutRefundNo { get; set; } |
| | | } |
| | | } |
| | |
| | | AliPay = 20 |
| | | } |
| | | |
| | | |
| | | |
| | | public enum LifePayOrderTypeEnum |
| | | { |
| | | [Description("话费订单")] |
| | |
| | | |
| | | [Description("部分退款")] |
| | | 部分退款 = 20 |
| | | } |
| | | } |
| | | |
| | | public enum ExpensesReceiptsTypeEnum |
| | | { |
| | | [Description("收入")] |
| | | Expenses = 10, |
| | | |
| | | [Description("支出")] |
| | | Receipts = 20, |
| | | |
| | | } |
| | |
| | | using LifePayment.Domain.Shared; |
| | | using Microsoft.Extensions.Options; |
| | | using Nest; |
| | | using System; |
| | | using System.Collections; |
| | | using System.Collections.Generic; |
| | | using System.Threading.Tasks; |
| | | using Tea; |
| | | |
| | | namespace LifePayment.Domain |
| | | { |
| | |
| | | { |
| | | private readonly Config _options; |
| | | |
| | | protected Alipay.EasySDK.Kernel.Client _kernel; |
| | | public AliPayApi( |
| | | IOptionsMonitor<Config> optionsMonitor) |
| | | IOptionsMonitor<Config> optionsMonitor, Client kernel) |
| | | { |
| | | _options = optionsMonitor.CurrentValue; |
| | | _kernel = kernel; |
| | | } |
| | | |
| | | public async Task<AlipayTradePrecreateResponse> GetAliPayQRCode(GetPayQrCodeInput input) |
| | |
| | | return response; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 查询支付宝退款订单信息 |
| | | /// </summary> |
| | |
| | | |
| | | public async Task<AlipayTradeRefundResponse> TradeRefund(AlipayTradeRefundRequest input) |
| | | { |
| | | AlipayTradeRefundResponse response = await Factory.Payment.Common().RefundAsync(input.OutTradeNo,input.RefundAmount); |
| | | AlipayTradeRefundResponse response = await TradeRefund(input.OutTradeNo,input.OutRefundNo, input.RefundAmount); |
| | | return response; |
| | | } |
| | | |
| | | private async Task<AlipayTradeRefundResponse> TradeRefund(string outTradeNo, string outRefundNo, string refundAmount) |
| | | { |
| | | Dictionary<string, object> runtime_ = new Dictionary<string, object> |
| | | { |
| | | { |
| | | "ignoreSSL", |
| | | _kernel.GetConfig("ignoreSSL") |
| | | }, |
| | | { |
| | | "httpProxy", |
| | | _kernel.GetConfig("httpProxy") |
| | | }, |
| | | { "connectTimeout", 15000 }, |
| | | { "readTimeout", 15000 }, |
| | | { |
| | | "retry", |
| | | new Dictionary<string, int?> { { "maxAttempts", 0 } } |
| | | } |
| | | }; |
| | | TeaRequest _lastRequest = null; |
| | | Exception innerException = null; |
| | | long _now = DateTime.Now.Millisecond; |
| | | int _retryTimes = 0; |
| | | while (TeaCore.AllowRetry((IDictionary)runtime_["retry"], _retryTimes, _now)) |
| | | { |
| | | if (_retryTimes > 0) |
| | | { |
| | | int backoffTime = TeaCore.GetBackoffTime((IDictionary)runtime_["backoff"], _retryTimes); |
| | | if (backoffTime > 0) |
| | | { |
| | | TeaCore.Sleep(backoffTime); |
| | | } |
| | | } |
| | | |
| | | _retryTimes++; |
| | | try |
| | | { |
| | | TeaRequest teaRequest = new TeaRequest(); |
| | | Dictionary<string, string> dictionary = new Dictionary<string, string> |
| | | { |
| | | { "method", "alipay.trade.refund" }, |
| | | { |
| | | "app_id", |
| | | _kernel.GetConfig("appId") |
| | | }, |
| | | { |
| | | "timestamp", |
| | | _kernel.GetTimestamp() |
| | | }, |
| | | { "format", "json" }, |
| | | { "version", "1.0" }, |
| | | { |
| | | "alipay_sdk", |
| | | _kernel.GetSdkVersion() |
| | | }, |
| | | { "charset", "UTF-8" }, |
| | | { |
| | | "sign_type", |
| | | _kernel.GetConfig("signType") |
| | | }, |
| | | { |
| | | "app_cert_sn", |
| | | _kernel.GetMerchantCertSN() |
| | | }, |
| | | { |
| | | "alipay_root_cert_sn", |
| | | _kernel.GetAlipayRootCertSN() |
| | | } |
| | | }; |
| | | Dictionary<string, object> bizParams = new Dictionary<string, object> |
| | | { |
| | | { "out_trade_no", outTradeNo }, |
| | | { "out_request_no", outRefundNo }, |
| | | { "refund_amount", refundAmount } |
| | | }; |
| | | Dictionary<string, string> dictionary2 = new Dictionary<string, string>(); |
| | | teaRequest.Protocol = _kernel.GetConfig("protocol"); |
| | | teaRequest.Method = "POST"; |
| | | teaRequest.Pathname = "/gateway.do"; |
| | | teaRequest.Headers = new Dictionary<string, string> |
| | | { |
| | | { |
| | | "host", |
| | | _kernel.GetConfig("gatewayHost") |
| | | }, |
| | | { "content-type", "application/x-www-form-urlencoded;charset=utf-8" } |
| | | }; |
| | | teaRequest.Query = _kernel.SortMap(TeaConverter.merge<string>(new object[3] |
| | | { |
| | | new Dictionary<string, string> { |
| | | { |
| | | "sign", |
| | | _kernel.Sign(dictionary, bizParams, dictionary2, _kernel.GetConfig("merchantPrivateKey")) |
| | | } }, |
| | | dictionary, |
| | | dictionary2 |
| | | })); |
| | | teaRequest.Body = TeaCore.BytesReadable(_kernel.ToUrlEncodedRequestBody(bizParams)); |
| | | _lastRequest = teaRequest; |
| | | TeaResponse response = await TeaCore.DoActionAsync(teaRequest, runtime_); |
| | | Dictionary<string, object> respMap = await _kernel.ReadAsJsonAsync(response, "alipay.trade.refund"); |
| | | if (_kernel.IsCertMode()) |
| | | { |
| | | if (_kernel.Verify(respMap, _kernel.ExtractAlipayPublicKey(_kernel.GetAlipayCertSN(respMap)))) |
| | | { |
| | | return TeaModel.ToObject<AlipayTradeRefundResponse>(_kernel.ToRespModel(respMap)); |
| | | } |
| | | } |
| | | else if (_kernel.Verify(respMap, _kernel.GetConfig("alipayPublicKey"))) |
| | | { |
| | | return TeaModel.ToObject<AlipayTradeRefundResponse>(_kernel.ToRespModel(respMap)); |
| | | } |
| | | |
| | | throw new TeaException(new Dictionary<string, string> { { "message", "验签失败,请检查支付宝公钥设置是否正确。" } }); |
| | | } |
| | | catch (Exception ex) |
| | | { |
| | | if (TeaCore.IsRetryable(ex)) |
| | | { |
| | | innerException = ex; |
| | | continue; |
| | | } |
| | | |
| | | throw ex; |
| | | } |
| | | } |
| | | |
| | | throw new TeaUnretryableException(_lastRequest, innerException); |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | |
| | | |
| | | } |
| | | public class AlipayStatus |
| | | { |
| | | /// <summary> |
| | | /// 交易创建,等待买家付款 |
| | | /// </summary> |
| | | public const string WAITBUYERPAY = "WAIT_BUYER_PAY"; |
| | | |
| | | /// <summary> |
| | | /// 未付款交易超时关闭,或支付完成后全额退款 |
| | | /// </summary> |
| | | public const string TRADECLOSED = "TRADE_CLOSED"; |
| | | |
| | | /// <summary> |
| | | /// 交易支付成功 |
| | | /// </summary> |
| | | public const string TRADESUCCESS = "TRADE_SUCCESS"; |
| | | |
| | | /// <summary> |
| | | /// 交易结束,不可退款 |
| | | /// </summary> |
| | | public const string TRADEFINISHED = "TRADE_FINISHED"; |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 交易状态 |
| | | /// </summary> |
| | | public class AlipayRefundStatus |
| | | { |
| | | /// <summary> |
| | | /// 退款处理成功:REFUND_SUCCESS 未返回该字段表示退款请求未收到或者退款失败 |
| | | /// </summary> |
| | | public const string Success = "REFUND_SUCCESS"; |
| | | } |
| | | public class AlipayResultCode |
| | | { |
| | | public const string Success = "10000"; |
New file |
| | |
| | | using System; |
| | | using System.Collections.Generic; |
| | | using System.Linq; |
| | | using System.Text; |
| | | using System.Threading.Tasks; |
| | | using Volo.Abp.Domain.Entities.Auditing; |
| | | using Volo.Abp; |
| | | using LifePayment.Domain.Shared; |
| | | |
| | | namespace LifePayment.Domain.LifePay |
| | | { |
| | | /// <summary> |
| | | /// 收支流水 |
| | | /// </summary> |
| | | public class LifePayExpensesReceipts : FullAuditedEntity<Guid>, IDataUserFilter |
| | | { |
| | | public LifePayExpensesReceipts() |
| | | { |
| | | } |
| | | |
| | | /// <summary> |
| | | /// 平台订单号 |
| | | /// </summary> |
| | | public string OrderNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 渠道流水号 |
| | | /// </summary> |
| | | public string OutOrderNo { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 支付渠道 |
| | | /// </summary> |
| | | public LifePayTypeEnum LifePayType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 收支类型 |
| | | /// </summary> |
| | | public ExpensesReceiptsTypeEnum ExpensesReceiptsType { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交易金额 |
| | | /// </summary> |
| | | public decimal Amount { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 交易时间 |
| | | /// </summary> |
| | | public DateTime FinishTime { get; set; } |
| | | |
| | | /// <summary> |
| | | /// 拓展熟悉 |
| | | /// </summary> |
| | | public string ExtraProperties { get; set; } |
| | | } |
| | | } |
| | |
| | | |
| | | namespace LifePayment.Domain.LifePay |
| | | { |
| | | /// <summary> |
| | | /// 充值流水 |
| | | /// </summary> |
| | | public class LifePayRechargeReceipts : FullAuditedEntity<Guid>, IDataUserFilter |
| | | { |
| | | public LifePayRechargeReceipts() |
| | |
| | | |
| | | public virtual DbSet<LifePayRechargeReceipts> LifePayRechargeReceipts { get; set; } |
| | | |
| | | public virtual DbSet<LifePayExpensesReceipts> LifePayExpensesReceipts { get; set; } |
| | | |
| | | public virtual DbSet<Area> Area { get; set; } |
| | | |
| | | public virtual DbSet<User> Users { get; set; } |
| | |
| | | <param name="input"></param> |
| | | <returns></returns> |
| | | </member> |
| | | <member name="M:LifePayment.HttpApi.WxPayNotifyController.WxPayDomesticRefundsNotify(LifePayment.Application.Contracts.WxRechargeNotifyInput)"> |
| | | <summary> |
| | | 微信退款通知回调 |
| | | </summary> |
| | | <param name="input"></param> |
| | | <returns></returns> |
| | | </member> |
| | | </members> |
| | | </doc> |
| | |
| | | 充值凭证 |
| | | </summary> |
| | | </member> |
| | | <member name="P:LifePayment.Application.Contracts.AddLifePayExpensesReceiptsInput.OrderNo"> |
| | | <summary> |
| | | 平台订单号 |
| | | </summary> |
| | | </member> |
| | | <member name="P:LifePayment.Application.Contracts.AddLifePayExpensesReceiptsInput.OutOrderNo"> |
| | | <summary> |
| | | 渠道流水号 |
| | | </summary> |
| | | </member> |
| | | <member name="P:LifePayment.Application.Contracts.AddLifePayExpensesReceiptsInput.LifePayType"> |
| | | <summary> |
| | | 支付渠道 |
| | | </summary> |
| | | </member> |
| | | <member name="P:LifePayment.Application.Contracts.AddLifePayExpensesReceiptsInput.ExpensesReceiptsType"> |
| | | <summary> |
| | | 收支类型 |
| | | </summary> |
| | | </member> |
| | | <member name="P:LifePayment.Application.Contracts.AddLifePayExpensesReceiptsInput.Amount"> |
| | | <summary> |
| | | 交易金额 |
| | | </summary> |
| | | </member> |
| | | <member name="P:LifePayment.Application.Contracts.LifePayIntroInfoInput.LifePayType"> |
| | | <summary> |
| | | 生活缴费类型 |
| | |
| | | { |
| | | |
| | | private readonly ILifePayService _lifePayService; |
| | | private readonly ILifePayOrderService _lifePayOrderService; |
| | | |
| | | private readonly ILogger<AliPayNotifyController> _logger; |
| | | |
| | |
| | | |
| | | ILogger<AliPayNotifyController> logger, |
| | | |
| | | ILifePayService lifePayService) |
| | | ILifePayService lifePayService, |
| | | ILifePayOrderService lifePayOrderService) |
| | | { |
| | | _logger = logger; |
| | | _lifePayService = lifePayService; |
| | | _lifePayOrderService = lifePayOrderService; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | if (input.TradeStatus == LifePaymentConstant.AliPayStatus.支付成功) |
| | | { |
| | | await _lifePayService.LifePaySuccessHandler(input.OutTradeNo, input.TradeNo); |
| | | await _lifePayOrderService.AddLifePayExpensesReceipts(new AddLifePayExpensesReceiptsInput() { OrderNo = input.OutTradeNo, |
| | | OutOrderNo = input.TradeNo ,LifePayType = LifePayTypeEnum.AliPay,ExpensesReceiptsType = ExpensesReceiptsTypeEnum.Expenses, |
| | | Amount = input.ReceiptAmount.Value }); |
| | | } |
| | | } |
| | | else |
| | |
| | | |
| | | private readonly IWxPayApi _wxPayApi; |
| | | private readonly ILifePayService _lifePayService; |
| | | private readonly ILifePayOrderService _lifePayOrderService; |
| | | private readonly ILogger<WxPayNotifyController> _logger; |
| | | |
| | | public WxPayNotifyController( |
| | | IWxPayApi wxPayApi, |
| | | ILogger<WxPayNotifyController> logger, |
| | | ILifePayService lifePayService) |
| | | ILifePayService lifePayService, |
| | | ILifePayOrderService lifePayOrderService) |
| | | { |
| | | _wxPayApi = wxPayApi; |
| | | _logger = logger; |
| | | _lifePayService = lifePayService; |
| | | _lifePayOrderService = lifePayOrderService; |
| | | } |
| | | |
| | | /// <summary> |
| | |
| | | if (wxPayNotice.TradeState == LifePaymentConstant.WxPayStatus.支付成功) |
| | | { |
| | | await _lifePayService.LifePaySuccessHandler(wxPayNotice.OutTradeNo, wxPayNotice.TransactionId); |
| | | await _lifePayOrderService.AddLifePayExpensesReceipts(new AddLifePayExpensesReceiptsInput() |
| | | { |
| | | OrderNo = wxPayNotice.OutTradeNo, |
| | | OutOrderNo = wxPayNotice.TransactionId, |
| | | LifePayType = LifePayTypeEnum.WxPay, |
| | | ExpensesReceiptsType = ExpensesReceiptsTypeEnum.Expenses, |
| | | Amount = wxPayNotice.Amount.Total |
| | | }); |
| | | } |
| | | } |
| | | } |
| | |
| | | }; |
| | | } |
| | | |
| | | |
| | | /// <summary> |
| | | /// 微信退款通知回调 |
| | | /// </summary> |
| | | /// <param name="input"></param> |
| | | /// <returns></returns> |
| | | [HttpPost] |
| | | [UnitOfWork] |
| | | public async Task<WxRechargeNotifyResult> WxPayDomesticRefundsNotify(WxRechargeNotifyInput input) |
| | |
| | | switch (wxPayNotice.RefundStatus) |
| | | { |
| | | case LifePaymentConstant.WxPayRefundStatus.退款成功: |
| | | await _lifePayService.WxPayDomesticRefundsHandler(wxPayNotice.OutTradeNo, LifePayRefundStatusEnum.已退款); |
| | | break; |
| | | case LifePaymentConstant.WxPayRefundStatus.退款关闭: |
| | | await _lifePayService.WxPayDomesticRefundsHandler(wxPayNotice.OutTradeNo, LifePayRefundStatusEnum.已退款); |
| | | break; |