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); }
public void IsValidTest() { var orderNo = OrderIdGen.New(); var invalidOrderNo = string.Join("", orderNo.Reverse()); Assert.IsTrue(OrderIdGen.IsValid(orderNo)); Assert.IsFalse(OrderIdGen.IsValid(invalidOrderNo)); }
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); }
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()); }
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); }
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)); }
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); }
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 }));