예제 #1
0
        /// <summary>
        /// 发起退款
        /// </summary>
        /// <param name="sourceSn"></param>
        /// <param name="amount"></param>
        /// <param name="refundReason"></param>
        /// <returns></returns>
        public async Task Refund(string sourceSn, int amount, string refundReason)
        {
            if (amount <= 0)
            {
                throw new Exception($"金额错误");
            }
            var order = await DbContext.Set <PrepayOrders>().Include(r => r.RefundOrders)
                        .FirstOrDefaultAsync(r => r.SourceSn == sourceSn);

            if (order == null || order.Status != PrepayOrderStatus.Succeed)
            {
                throw new Exception("支付状态错误");
            }
            if (order.PayAmount <= order.RefundOrders.Sum(r => r.RefundAmount))
            {
                throw new Exception("订单已全部退款完成");
            }
            if (amount > order.PayAmount)
            {
                throw new Exception("退款金额错误");
            }

            var mch = MchOptions.Get(order.MchId);

            if (mch == null)
            {
                throw new Exception($"商户数据异常,无法退款,mchId:{order.MchId}");
            }

            var payment = Payments.FirstOrDefault(r => r.Channel == order.PayChannel.Value);

            if (payment == null)
            {
                throw new Exception($"支付通道错误,order channel:{order.PayChannel}");
            }

            string localRefundNo = Guid.NewGuid().ToString("n");

            using var locker = Locker.Lock(Lockers.RefundOrderCreate.Format(order.Id), 3);
            using var tran   = DbContext.Database.BeginTransaction();
            try
            {
                RefundOrders refundOrder = new RefundOrders
                {
                    PrepayOrderId = order.Id,
                    CreateTime    = DateTime.Now.GetLongDate(),
                    LocalRefundNo = localRefundNo,
                    RefundStatus  = RefundStatus.RefundProcessing,
                    RefundAmount  = amount
                };
                DbContext.Add(refundOrder);
                await DbContext.SaveChangesAsync();

                // 发起退款请求
                await payment.Refund(order.AppId, order.MchId, order.CurrentPayLocalTradeNo, localRefundNo,
                                     order.Amount, amount, refundReason);

                tran.Commit();
            }
            catch (Exception ex)
            {
                tran.Rollback();
                throw ex;
            }
        }
예제 #2
0
        /// <summary>
        /// 准备支付,请求支付数据
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task <AppPrepayDto> Prepay(string userId, OrderPrepayInput input)
        {
            using var locker = Locker.Lock(Lockers.PrepayOrderGetPayData.Format(input.PrepayOrderSn), 3);
            var order = await DbContext.Set <PrepayOrders>()
                        .FirstOrDefaultAsync(o => o.Sn == input.PrepayOrderSn && o.UserId == userId);

            if (order == null)
            {
                throw new Exception("订单不存在");
            }
            if (order.Status != PrepayOrderStatus.Waiting)
            {
                throw new Exception("订单状态错误");
            }

            var now = DateTime.Now.GetLongDate();
            var mch = MchOptions.GetByName(order.MchName, input.Channel.Convert2Type());

            if (mch == null)
            {
                throw new Exception("channel 参数错误");
            }

            var dataProvider = ServiceProvider.GetServices <IPrepayOrderDataProvider>()
                               .SingleOrDefault(p => p.SourceType == order.SourceType);

            if (dataProvider == null)
            {
                throw new Exception("无效来源或者来源没有数据提供");
            }

            var canpay = await dataProvider.CanPay(userId.ParseTo <int>(), order.SourceSn);

            if (!canpay.canPay)
            {
                throw new Exception(canpay.errorReason ?? "订单异常");
            }

            var cacheKey = prepayCacheKey.Format(order.Id, input.Channel, input.AppId, mch.MchId);
            // 缓存中查看有没有支付对应的支付数据,有就直接返回,没有就调用对应的支付通道并获取
            var prepayData = await DistributedCache.GetObjectWithJsonAsync <PrepayData>(cacheKey);

            if (prepayData == null)
            {
                var oldLocalTradeNo = order.CurrentPayLocalTradeNo;
                var newLocalTradeNo = Guid.NewGuid().ToString("n");
                var payParams       = new PayParams()
                {
                    OrderId      = order.Id,
                    OrderSn      = order.Sn,
                    LocalTradeNo = newLocalTradeNo,
                    Amount       = order.PayAmount,
                    Title        = order.Title,
                    Attach       = order.Attach,
                    ClientIp     = input.ClientIp,
                    TraderId     = input.TraderId,
                    RedirectUrl  = input.RedirectUrl,
                    Channel      = input.Channel,
                    MchId        = mch.MchId,
                    AppId        = input.AppId
                };
                var payment = Payments.FirstOrDefault(r => r.Channel == input.Channel);
                if (payment == null)
                {
                    throw new Exception("无效的支付方式");
                }

                if (!oldLocalTradeNo.IsNullOrWhiteSpace())
                {
                    try
                    {
                        // 每次请求新的支付数据时,都将旧的预付单关闭掉,避免重复支付
                        await payment.CloseOrder(input.AppId, mch.MchId, oldLocalTradeNo);
                    }
                    catch
                    {
                        // ignored
                    }
                }

                order.CurrentPayLocalTradeNo = newLocalTradeNo;
                prepayData = await payment.GetPrepayData(payParams);

                await DistributedCache.SetObjectWithJsonAsync(cacheKey, prepayData, DateTimeOffset.Now.AddHours(1));
            }

            try
            {
                var result = new AppPrepayDto
                {
                    PayData = prepayData.PayData
                };

                order.PayChannel       = input.Channel;
                order.PrepayOriginData = prepayData.PrepayOriginData;
                order.MchId            = mch.MchId;
                order.LastPrepayTime   = now;
                order.AppId            = input.AppId;

                await DbContext.SaveChangesAsync();

                return(result);
            }
            catch (Exception e)
            {
                throw new Exception("支付数据生成失败", e);
            }
        }