public void ProcessRefunds(IUnitOfWork db, string tag) { //NOTE: reprocess cancel request every 2 hours DateTime?maxLastAttemptDate = _time.GetUtcTime().AddHours(-4); var maxAttempts = 10; var returnActions = _actionService.GetUnprocessedByType(db, SystemActionType.UpdateOnMarketReturnOrder, maxLastAttemptDate, tag); foreach (var action in returnActions) { Order order = null; decimal refundAmount = 0M; Exception exception = null; try { var data = JsonConvert.DeserializeObject <ReturnOrderInput>(action.InputData); order = db.Orders.GetByOrderNumber(data.OrderNumber); if (order == null) { _log.Info(_api.Market + "_" + _api.MarketplaceId + ": Can't find orderId=" + data.OrderNumber); continue; } if (order.Market != (int)_api.Market || (!String.IsNullOrEmpty(_api.MarketplaceId) && order.MarketplaceId != _api.MarketplaceId)) { //_log.Info("skip order=" + data.OrderNumber + ", market=" + order.Market + ", marketplace=" + order.MarketplaceId); continue; } _log.Info("Order to refund: " + _api.Market + "_" + _api.MarketplaceId + ", actionId=" + action.Id + ", orderId=" + data.OrderNumber); data.MarketOrderId = order.MarketOrderId; data.Id = action.Id; data.PaymentTransactionId = order.PaymentInfo; #region Prepare Fee Data var orderIds = new List <long>() { order.Id }; var orderItems = db.OrderItems.GetAllAsDto().Where(oi => orderIds.Contains(oi.OrderId)).ToList(); var previousRefunds = db.SystemActions.GetAllAsDto().Where(a => a.Type == (int)SystemActionType.UpdateOnMarketReturnOrder && a.Tag == data.OrderNumber && a.Id != action.Id) .ToList(); var previousRefundDataList = previousRefunds.Select(i => JsonConvert.DeserializeObject <ReturnOrderInput>(i.InputData)).ToList(); foreach (var orderItem in orderItems) { var refundItem = data.Items.FirstOrDefault(oi => oi.ItemOrderId == orderItem.ItemOrderIdentifier); if (refundItem == null) { if (order.Market != (int)MarketType.Walmart) //NOTE: Skip Walmart, builded based on return request { data.Items.Add(new ReturnOrderItemInput() { ItemOrderId = orderItem.ItemOrderIdentifier, Quantity = 0, }); } } else { if (refundItem.RefundItemPrice > 0) { decimal alreadyRefundedItemSum = 0; foreach (var refundData in previousRefundDataList) { alreadyRefundedItemSum += refundData.Items.Where(i => i.ItemOrderId == orderItem.ItemOrderIdentifier).Sum(i => i.RefundItemPrice); } decimal totalItemSum = orderItems.Where(oi => oi.ItemOrderIdentifier == orderItem.ItemOrderIdentifier).Sum(oi => oi.ItemPaid ?? 0); decimal requestedRefundItemSum = refundItem.RefundItemPrice; var perItemPrice = (orderItem.ItemPaid ?? 0) / (decimal)orderItem.QuantityOrdered; var adjustment = refundItem.RefundItemPrice - refundItem.Quantity * perItemPrice; if (adjustment > 0) { refundItem.AdjustmentRefund = adjustment; } if (adjustment < 0) { refundItem.AdjustmentFee = -adjustment; } } else { refundItem.Quantity = 0; } } } //Group back for virtual styles data.Items = data.Items .GroupBy(i => String.Join("_", i.ItemOrderId.Split('_').Take(2))) .Select(g => new ReturnOrderItemInput() { ItemOrderId = g.Key, Quantity = g.Max(i => i.Quantity), Feedback = g.FirstOrDefault()?.Feedback, Notes = g.FirstOrDefault()?.Notes, Reason = g.FirstOrDefault()?.Reason, TotalPaidAmount = g.Sum(i => i.TotalPaidAmount), RefundItemPrice = g.Sum(i => i.RefundItemPrice), RefundShippingPrice = g.Sum(i => i.RefundShippingPrice), DeductPrepaidLabelCost = g.Sum(i => i.DeductPrepaidLabelCost), DeductShippingPrice = g.Sum(i => i.DeductShippingPrice), AdjustmentRefund = g.Sum(i => i.AdjustmentRefund), AdjustmentFee = g.Sum(i => i.AdjustmentFee), }) .ToList(); refundAmount = data.Items.Sum(i => i.AdjustmentRefund); #endregion var result = _api.RefundOrder(order.AmazonIdentifier, data); if (result.IsSuccess) { var output = new ReturnOrderOutput() { IsProcessed = true, ResultMessage = "Refunds by API" }; _actionService.SetResult(db, action.Id, SystemActionStatus.Done, output); db.Commit(); } else { _log.Info("Refund Fail: " + result.Message); var output = new ReturnOrderOutput() { IsProcessed = true, ResultMessage = "Fail: " + result.Message }; _actionService.SetResult(db, action.Id, action.AttemptNumber > maxAttempts ? SystemActionStatus.Fail : SystemActionStatus.None, output); db.Commit(); } } catch (Exception ex) { exception = ex; _log.Error("WalmartOrderRefund.Process", ex); } if (action.AttemptNumber > maxAttempts) { var output = new ReturnOrderOutput() { IsProcessed = true, ResultMessage = exception != null ? exception.Message : "" }; _actionService.SetResult(db, action.Id, SystemActionStatus.Fail, output); db.Commit(); db.OrderComments.Add(new OrderComment() { OrderId = order.Id, CreateDate = _time.GetAppNowTime(), Type = (int)CommentType.ReturnExchange, Message = "Refund failed, amount: $" + PriceHelper.RoundToTwoPrecision(refundAmount) }); db.Commit(); _emailService.SendSystemEmail( "System can't process refund, order #" + (order != null ? order.CustomerOrderId : "n/a"), "Details: " + (exception != null ? exception.Message : "-"), EmailHelper.RafiEmail + ";" + EmailHelper.RaananEmail, EmailHelper.SupportDgtexEmail); } } }