Пример #1
0
        public async Task CreateUnifedOrderTestAsync()
        {
            if (string.IsNullOrEmpty(_wechatConfig.MerchantId) || string.IsNullOrEmpty(_wechatConfig.MerchantSecret))
            {
                return;
            }

            var wechatPay = new WeChatPayService(_wechatConfig);
            var model     = new WeChatPayOrderModel {
                OpenId    = _openId,
                AppId     = _wechatConfig.MobilePlatformAppId,
                IPAddress = "127.0.0.1",
                Body      = "Test",
                Amount    = 0.01m,
                NotifyUrl = "https://bigfridge.xingyisoftware.com/pay/callback/wechat",
            };

            //JsApi
            model.TradeType = WeChatPayTradeType.JsApi;
            model.OrderNo   = OrderIdGen.New();

            var result1 = await wechatPay.CreateUnifedOrderAsync(model);

            Assert.IsTrue(result1.Ok);
            Assert.IsNotNull(result1.Data.PrepayId);

            //Native
            model.TradeType = WeChatPayTradeType.Native;
            model.OrderNo   = OrderIdGen.New();

            var result2 = await wechatPay.CreateUnifedOrderAsync(model);

            Assert.IsTrue(result2.Ok);
            Assert.IsNotNull(result2.Data.PrepayId);
        }
Пример #2
0
        public void IsValidTest()
        {
            var orderNo        = OrderIdGen.New();
            var invalidOrderNo = string.Join("", orderNo.Reverse());

            Assert.IsTrue(OrderIdGen.IsValid(orderNo));
            Assert.IsFalse(OrderIdGen.IsValid(invalidOrderNo));
        }
Пример #3
0
        public void TryParseTest()
        {
            var orderNo        = OrderIdGen.New();
            var invalidOrderNo = string.Join("", orderNo.Reverse());
            var shouldBeTrue   = OrderIdGen.TryParse(orderNo, out var t1);
            var shouldBeFalse  = OrderIdGen.TryParse(invalidOrderNo, out _);

            Assert.IsTrue(shouldBeTrue);
            Assert.IsFalse(shouldBeFalse);
            Assert.IsTrue(DateTime.Now.Subtract(t1).TotalSeconds < 2);
        }
Пример #4
0
        public void NewTest()
        {
            var n    = 10000;
            var list = new List <string>();

            for (var i = 0; i < n; i++)
            {
                list.Add(OrderIdGen.New());
            }
            Assert.AreEqual(n, list.Distinct().Count());
        }
Пример #5
0
        public async Task RefundTestAsync()
        {
            if (string.IsNullOrEmpty(_wechatConfig.MerchantId) || string.IsNullOrEmpty(_wechatConfig.MerchantSecret))
            {
                return;
            }

            var payedOrderNo       = "DIB710795325592";
            var newRefundRequestNo = OrderIdGen.New();

            var wechatPay = new WeChatPayService(_wechatConfig);
            var result    = await wechatPay.RefundAsync(_wechatConfig.MobilePlatformAppId, payedOrderNo, newRefundRequestNo, 0.1m, 0.01m, "UnitTest");

            Assert.IsTrue(result.Ok);
            Assert.AreEqual(0.01m, result.Data.RefundAmount);
        }
Пример #6
0
        public async Task <Result <List <OrderRefund> > > ProceedRefund(int orderId, RefundReason refundReason, OrderStatus?setOrderStatus = null, decimal?requestRefundAmount = null)
        {
            if (_me.IsAnonymous)
            {
                return(new Failure <List <OrderRefund> >("需要先登录"));
            }

            var order = _db.Orders
                        .Include(x => x.Payments).ThenInclude(x => x.Refunds)
                        .Where(x => x.BuyerId == _me.Id)
                        .Where(x => x.Id == orderId)
                        .SingleOrDefault();

            if (order == null)
            {
                return(new Failure <List <OrderRefund> >("退款失败,未找到订单"));
            }

            const int daysToRefundAfterOrderCompleted = 30;

            if (order.CompletedTime.HasValue && order.CompletedTime < DateTime.Now.AddDays(daysToRefundAfterOrderCompleted))
            {
                return(new Failure <List <OrderRefund> >($"只能在订单完成后的 {daysToRefundAfterOrderCompleted} 天内进行退款"));
            }

            if (_alipay == null && order.Payments.Any(x => x.Choise == PaymentChoise.Alipay))
            {
                throw new ArgumentNullException(nameof(_alipay));
            }
            if (_wechat == null && order.Payments.Any(x => x.Choise == PaymentChoise.WeChat))
            {
                throw new ArgumentNullException(nameof(_wechat));
            }

            var totalPaid = order.Payments
                            .Where(x => x.Status == PaymentStatus.Paid)
                            .Sum(x => x.Amount);

            var refundedBefore = order.Payments.SelectMany(x => x.Refunds)
                                 .Where(x => x.Status != RefundStatus.Cancelled)
                                 .Sum(x => x.Amount);

            var refundMaxAllowed = totalPaid - refundedBefore;

            if (requestRefundAmount.HasValue && requestRefundAmount.Value > refundMaxAllowed)
            {
                return(new Failure <List <OrderRefund> >("超出总共可退款金额"));
            }

            var refundRemained = requestRefundAmount ?? refundMaxAllowed;
            var pending        = new List <OrderRefund>();

            //遍历该订单下的付款记录,依次轮询并使用可退款金额,并插入待退款记录(OrderRefund)
            foreach (var payment in order.Payments)
            {
                if (payment.Status != PaymentStatus.Paid)
                {
                    continue;
                }

                var refundable = payment.Amount - payment.Refunds.Where(x => x.Status != RefundStatus.Cancelled).Sum(x => x.Amount);
                var refunding  = Math.Min(refundRemained, refundable);

                var refund = new OrderRefund {
                    RefundNo = OrderIdGen.New(),
                    Amount   = refunding,
                    Reason   = refundReason,
                    Status   = RefundStatus.Await
                };

                payment.Refunds.Add(refund);
                pending.Add(refund);

                refundRemained -= refunding;

                if (refundRemained <= 0)
                {
                    break;
                }
            }

            order.Logs.Add(new OrderLog {
                Remarks = $"发生退款 {refundRemained:f2} 元"
            });

            if (setOrderStatus.HasValue && setOrderStatus != order.Status)
            {
                order.Logs.Add(new OrderLog {
                    StatusFrom      = order.Status,
                    StatusChangedTo = setOrderStatus,
                    Remarks         = $"因退款,订单状态发生变化"
                });
                order.Status = setOrderStatus.Value;
            }

            //先保存一次数据
            await _db.Normalize().SaveChangesAsync();

            //保存之后,再实际请求API进行退款;若退款成功,再改变新增退款记录的状态并保存
            foreach (var refund in pending)
            {
                Result result = null !;

                switch (refund.OriginalPayment.Choise)
                {
                case PaymentChoise.WeChat:
                    result = _wechat !.PayService().Refund(
                        refund.OriginalPayment.AppId !,
                        refund.OriginalPayment.PaymentNo,
                        refund.RefundNo,
                        refund.OriginalPayment.Amount,
                        refund.Amount,
                        refund.Reason.ToLabel()
                        );
                    break;

                case PaymentChoise.Alipay:
                    result = _alipay !.Refund(
                        refund.OriginalPayment.PaymentNo,
                        refund.RefundNo,
                        refund.Amount,
                        refund.Reason.ToLabel()
                        );
                    break;
                }

                refund.Status        = result.Ok ? RefundStatus.Refunded : RefundStatus.Checking;
                refund.ResultMessage = result.Message;
            }

            //再次保存数据,更新刚新插入 OrderRefund 的状态
            await _db.Normalize().SaveChangesAsync();

            //返回执行结果
            if (pending.Any(x => x.Status != RefundStatus.Refunded))
            {
                var choises = string.Join("及", pending.Select(x => x.OriginalPayment.Choise).Distinct().ToArray());
                return(new Success <List <OrderRefund> >($"已提交退款,但{choises}返回信息未成功", pending));
            }
            return(new Success <List <OrderRefund> >(pending));
        }
Пример #7
0
        public void PaymentTransactionTest()
        {
            if (string.IsNullOrEmpty(_alipayOptions.PrivateKey) || string.IsNullOrEmpty(_alipayOptions.AlipayPublicKey))
            {
                return;
            }

            var alipay     = new AlipayService(_alipayOptions);
            var tradeModel = new AlipayOrderModel {
                Amount  = 0.1m,
                OrderNo = OrderIdGen.New(),
                Subject = "UnitTest"
            };
            var paymentUrl = alipay.GenerateAlipayPaymentUrl(tradeModel).DesktopPagePaymentUrl;

            //Payment url is opened up in the default browser
            Process.Start(new ProcessStartInfo(paymentUrl)
            {
                UseShellExecute = true
            });

            ////////////////////////////////////////////////////////////////////////////////////////
            ///// THIS TEST REQUIRES MANUAL OPERATION, USE DEBUG MODE AND SET BREAK POINT HERE /////
            ////////////////////////////////////////////////////////////////////////////////////////

            //Open the url in browser
            //Pay manually in the opened page, then continue
            var queryResult = alipay.QueryOrder(tradeModel.OrderNo);

            Assert.AreEqual(tradeModel.Amount, queryResult.Data.Amount);

            var remainedAmount = tradeModel.Amount;

            //Refund 0.01
            var refundAmount    = 0.01m;
            var refundRequestNo = OrderIdGen.New();
            var refundResult    = alipay.Refund(tradeModel.OrderNo, refundRequestNo, refundAmount, "Test");

            Assert.IsTrue(refundResult.Ok);
            Assert.AreEqual(refundAmount, refundResult.Data.RefundAmount);

            remainedAmount -= refundAmount;

            //Query refund 0.01
            var queryRefundResult = alipay.QueryRefund(tradeModel.OrderNo, refundRequestNo);

            Assert.AreEqual(refundAmount, queryRefundResult.Data.RefundAmount);

            //Refund another 0.01
            var refundAmount2    = 0.01m;
            var refundRequestNo2 = OrderIdGen.New();
            var refundResult2    = alipay.Refund(tradeModel.OrderNo, refundRequestNo2, refundAmount2, "Test");

            Assert.IsTrue(refundResult2.Ok);
            Assert.AreEqual(refundAmount2, refundResult2.Data.RefundAmount);
            Assert.AreEqual(refundAmount + refundAmount2, refundResult2.Data.AggregatedRefundAmount);

            remainedAmount -= refundAmount2;

            //Query another refund 0.01
            var queryRefundResult2 = alipay.QueryRefund(tradeModel.OrderNo, refundRequestNo2);

            Assert.AreEqual(refundAmount2, queryRefundResult2.Data.RefundAmount);

            //Expected failure refund
            var refundAmount3 = tradeModel.Amount;
            var refundResult3 = alipay.Refund(tradeModel.OrderNo, OrderIdGen.New(), refundAmount3, "Test");

            Assert.IsFalse(refundResult3.Ok);
            Assert.IsNotNull(refundResult3.Message);

            //Refund all remained amount
            var refundResult4 = alipay.Refund(tradeModel.OrderNo, OrderIdGen.New(), remainedAmount, "Test");

            Assert.IsTrue(refundResult4.Ok);
            Assert.AreEqual(tradeModel.Amount, refundResult4.Data.AggregatedRefundAmount);
        }
Пример #8
0
        public async Task <Result <Order> > CreateOrder(PaymentChoise paymentChoise, OrderReceiverAddress addr)
        {
            if (_me.IsAnonymous)
            {
                return(new Failure <Order>("需要先登录"));
            }

            var cartItems = _db.OrderCartItems
                            .Include(x => x.Product)
                            .Where(x => x.Selected)
                            .Where(x => x.BuyerId == _me.Id)
                            .ToList();

            if (cartItems.Count == 0)
            {
                return(new Failure <Order>("没有选中待结算的商品"));
            }

            var outOfStock = cartItems.FirstOrDefault(x => x.Product.Stock <= x.Quantity);

            if (outOfStock != null)
            {
                return(new Failure <Order>("有商品缺货,不足购买数量,请返回购物车查看"));
            }

            //Create Order
            var order = new Order {
                BuyerId     = _me.Id,
                OrderNo     = OrderIdGen.New(),
                TotalAmount = cartItems.Sum(x => x.Quantity * x.Product.ActualPrice),
                Status      = OrderStatus.AwaitPay
            };

            //Init the first OrderLog of this order
            order.Logs.Add(new OrderLog {
                StatusChangedTo = OrderStatus.AwaitPay,
                Remarks         = "确认下单"
            });

            //Set OrderReceiverAddress
            order.ReceiverAddress = new OrderReceiverAddress {
                Province           = addr.Province,
                City               = addr.City,
                District           = addr.District,
                DetailAddress      = addr.DetailAddress,
                ContactName        = addr.ContactName,
                ContactPhoneNumber = addr.ContactPhoneNumber,
                Lat = addr.Lat,
                Lon = addr.Lon,
            };

            //Move OrderCartItems to OrderItems
            order.Items.AddRange(cartItems.Select(x => new OrderItem {
                ProductId            = x.ProductId,
                Quantity             = x.Quantity,
                InstantProductCode   = x.Product !.ProductCode,
                InstantProductName   = x.Product.ProductName,
                InstantOriginalPrice = x.Product.OriginalPrice,
                InstantActualPrice   = x.Product.ActualPrice,
                InstantExpectedTip   = x.Product.ExpectedTip,
                InstantVariationJson = x.VariationJson,
                Remarks = x.Remarks
            }));