public Runner(
            IIoCContainer container,
            EventBroker eventBroker,
            IApplicationLifetime applicationLifetime,
            DistributedLockService distributedLockService,
            string enabledSetting)
        {
            if (string.IsNullOrEmpty(enabledSetting) || !bool.TryParse(enabledSetting, out bool enabled) || !enabled)
            {
                this.Log().Information("MediaMapper not enabled. Set web.config AppSetting \"MediaMapperEnabled\" to \"true\" to enable.");
                return;
            }

            var subscription = eventBroker.Subscribe <FileCreated>(EventScope.Local, ev =>
            {
                using (Solution.Instance.SystemToken.Use())
                    using (distributedLockService.AcquireLock(lockKey, TimeSpan.FromSeconds(10)))
                    {
                        foreach (var m in container.ResolveAll <IMediaMapper>().Where(r => r.GetUploadFolder()?.SystemId == ev.Item.FolderSystemId))
                        {
                            m.Map();
                        }
                    }
            });

            applicationLifetime.ApplicationStarted.Register(() =>
            {
                _task = Task.Run(() =>
                {
                    using (Solution.Instance.SystemToken.Use())
                    {
                        while (!_cancellationTokenSource.Token.IsCancellationRequested)
                        {
                            if (SleepUnlessCancelled(5, _cancellationTokenSource.Token))
                            {
                                return;
                            }

                            using (distributedLockService.AcquireLock(lockKey, TimeSpan.FromSeconds(10)))
                            {
                                foreach (var m in container.ResolveAll <IMediaMapper>())
                                {
                                    m.Map();
                                }
                            }
                        }
                    }
                }, _cancellationTokenSource.Token);
            });

            applicationLifetime.ApplicationStopping.Register(() =>
            {
                _cancellationTokenSource.Cancel();
                subscription.Dispose();
            });
        }
        private PriceCalculatorResult GetPriceFromErp(Guid variantSystemId)
        {
            var cacheKey = $"{nameof(ERPPriceCalculatorImpl)}:{variantSystemId}";

            if (_distributedMemoryCacheService.TryGet <PriceCalculatorResult>(cacheKey, out var price))
            {
                return(price);
            }

            using (_distributedLockService.AcquireLock(cacheKey, TimeSpan.FromSeconds(10)))
            {
                // Try getting value from cache again since it was likely added while we were waiting for the lock
                if (_distributedMemoryCacheService.TryGet <PriceCalculatorResult>(cacheKey, out price))
                {
                    return(price);
                }

                // Test the effect of the cache by faking a slow ERP API taking one second to get a variants price:
                Thread.Sleep(800);

                price = new PriceCalculatorResult
                {
                    ListPrice     = 100,
                    VatPercentage = (decimal)0.25
                };

                _distributedMemoryCacheService.Set(cacheKey, price);
            }

            return(price);
        }
예제 #3
0
            public PaymentWidgetResult GetWidget(OrderCarrier order, PaymentInfoCarrier paymentInfo)
            {
                var urlHelper        = new UrlHelper(HttpContext.Current.Request.RequestContext);
                var paymentAccountId = _paymentWidgetService.GetPaymentAccountId(paymentInfo.PaymentMethod);
                var klarnaCheckout   = LitiumKcoApi.CreateFrom(
                    paymentAccountId,
                    (orderCarrier, payment, kcoOrder) => _paymentConfig.UpdateDataSentToKlarna(urlHelper, orderCarrier, payment, kcoOrder));

                var checkoutOrder = string.IsNullOrEmpty(paymentInfo.TransactionNumber) ? null : klarnaCheckout.FetchKcoOrder(order);

                if (checkoutOrder == null || checkoutOrder.KlarnaOrderStatus == KlarnaOrderStatus.Incomplete)
                {
                    using (_distributedLockService.AcquireLock($"{nameof(PaymentWidgetService)}:{order.ID}", TimeSpan.FromMinutes(1)))
                    {
                        _paymentInfoCalculator.CalculateFromCarrier(order, _securityToken);
                        var args = CreatePaymentArgs(order, paymentAccountId);

                        checkoutOrder = klarnaCheckout.CreateOrUpdateKcoOrder(order, args);
                    }
                }

                switch (checkoutOrder.KlarnaOrderStatus)
                {
                case KlarnaOrderStatus.Incomplete:
                    return(new PaymentWidgetResult
                    {
                        Id = nameof(PaymentMethod.KlarnaCheckout),
                        IsChangeable = true,
                        ResponseString = checkoutOrder.HtmlSnippet,
                    });

                case KlarnaOrderStatus.Error:
                    throw new Exception(checkoutOrder.HtmlSnippet);

                case KlarnaOrderStatus.Authorized:
                case KlarnaOrderStatus.Cancelled:
                case KlarnaOrderStatus.Captured:
                case KlarnaOrderStatus.Complete:
                case KlarnaOrderStatus.Created:
                    _cartAccessor.Cart.Clear();
                    return(new PaymentWidgetResult
                    {
                        Id = nameof(PaymentMethod.KlarnaCheckout),
                        IsChangeable = false,
                        ResponseString = checkoutOrder.HtmlSnippet,
                    });
                }

                throw new Exception(checkoutOrder.HtmlSnippet);
            }
        private PriceCalculatorResult GetPriceFromErp(Guid variantSystemId)
        {
            var cacheKey = $"{nameof(ErpPriceCalculatorDecorator)}:{variantSystemId}";

            if (_distributedMemoryCacheService.TryGet <PriceCalculatorResult>(cacheKey, out var price))
            {
                _logger.LogDebug("Getting price from CACHE for variant {VariantSystemId}", variantSystemId);
                return(price);
            }

            using (_distributedLockService.AcquireLock(cacheKey, TimeSpan.FromSeconds(10)))
            {
                // Try getting value from cache again since it may have been added
                // from another thread/app while we were waiting for the lock
                if (_distributedMemoryCacheService.TryGet(cacheKey, out price))
                {
                    _logger.LogDebug("Getting price from CACHE for variant {VariantSystemId}", variantSystemId);
                    return(price);
                }

                _logger.LogDebug("Getting price from ERP for variant {VariantSystemId}", variantSystemId);

                // Test the effect of the cache by faking a slow API:
                Thread.Sleep(500);

                price = new PriceCalculatorResult
                {
                    PriceExcludingVat = 100,
                    PriceIncludesVat  = false,
                    VatRate           = (decimal)0.25
                };

                _distributedMemoryCacheService.Set(cacheKey, price);
            }

            return(price);
        }
 public void AcquiringNonExpiredAndActiveLockFromDifferentOwnerThrowsTimeoutException()
 {
     CreateNonExpiredActiveLock("Other Machine");
     Assert.Throws <TimeoutException>(() => _distributedLockService.AcquireLock(LockName, TimeSpan.FromHours(1), TimeSpan.Zero));
 }
예제 #6
0
        public void PlaceOrder([NotNull] IPaymentWidgetOrder paymentWidgetOrder, bool isConfirmation)
        {
            this.Log().Debug("Provider order status {providerOrderStatus}.", paymentWidgetOrder.OrderStatus);
            Order        order        = null;
            OrderCarrier orderCarrier = null;

            if (paymentWidgetOrder.IsCompleted)
            {
                using (_distributedLockService.AcquireLock($"{nameof(PaymentWidgetService)}:{paymentWidgetOrder.ProviderOrderId}", TimeSpan.FromMinutes(1)))
                {
                    //there is a time gap between when we last fetched order from Klarna to the time of aquiring the distributed lock.
                    //just before the distributed lock was taken by this machine (but after this machine had fetched checkout order from klarna),
                    //another machine in the server farm may have got the lock, and created the order and exited releasing the distributed lock.
                    //That would cause this machine to aquire the lock, but we cannot use the existing checkoutorder because its out-dated.
                    //therefore we have to refetch it one more time!.
                    paymentWidgetOrder.Refresh();

                    if (paymentWidgetOrder.IsCompleted)
                    {
                        //save the order only if it is not saved already.
                        order = TryFindOrder();
                        if (order == null)
                        {
                            this.Log().Debug("Create order.");
                            // Replace the order carrier in the session from the one received from Klarna plugin.
                            // The order carrier contains the inforamtion of the order that will be created.
                            _cartAccessor.Cart.OrderCarrier = paymentWidgetOrder.OrderCarrier;

                            var channel = _channelService.Get(_cartAccessor.Cart.OrderCarrier.ChannelID);
                            if (channel?.WebsiteLanguageSystemId != null)
                            {
                                CultureInfo.CurrentUICulture = _languageService.Get(channel.WebsiteLanguageSystemId.Value)?.CultureInfo ?? CultureInfo.CurrentUICulture;
                            }

                            order = _cartAccessor.Cart.PlaceOrder(_securityToken, out var paymentInfos);
                            this.Log().Debug("Order created, order id {orderId}, external order id {externalOrderId}.", order.ID, order.ExternalOrderID);
                        }

                        var paymentInfo = order.PaymentInfo.FirstOrDefault(x => x.PaymentProviderName == paymentWidgetOrder.PaymentProviderName);
                        if (paymentInfo != null)
                        {
                            // If the Litium call to payment provider to "notify order created" fails, the payment will be in following states.
                            // so execute payment need to be called again, to notify klarna of Litium order id.
                            if (paymentInfo.PaymentStatus == PaymentStatus.Init ||
                                paymentInfo.PaymentStatus == PaymentStatus.ExecuteReserve ||
                                paymentInfo.PaymentStatus == PaymentStatus.Pending)
                            {
                                var checkoutFlowInfo = _cartAccessor.Cart.CheckoutFlowInfo;
                                checkoutFlowInfo.SetValue("ProviderOrderIsCreated", true);
                                var channel = _channelService.Get(order.ChannelID);
                                CultureInfo.CurrentUICulture = _languageService.Get(channel.WebsiteLanguageSystemId.GetValueOrDefault())?.CultureInfo ?? CultureInfo.CurrentUICulture;
                                checkoutFlowInfo.SetValue(CheckoutConstants.ClientLanguage, CultureInfo.CurrentUICulture.Name);
                                checkoutFlowInfo.SetValue(CheckoutConstants.ClientTwoLetterISOLanguageName, CultureInfo.CurrentUICulture.TwoLetterISOLanguageName);

                                this.Log().Debug("Execute payment.");
                                var result = paymentInfo.ExecutePayment(checkoutFlowInfo, _securityToken);
                                checkoutFlowInfo.SetValue(CheckoutConstants.ExecutePaymentResult, result);
                                this.Log().Debug($"Payment executed{(result.Success ? string.Empty : " with error: " + result.ErrorMessage)}.");

                                // The order carrier has changed, refetch the checkoutOrder with updated data.
                                orderCarrier = order.GetAsCarrier(true, true, true, true, true, true);
                                paymentWidgetOrder.Update(orderCarrier);
                                this.Log().Debug("Provider order status changed to {providerOrderStatus}.", paymentWidgetOrder.OrderStatus);
                            }
                        }
                    }
                }
            }

            if (isConfirmation && paymentWidgetOrder.IsCreated)
            {
                var placedOrder = order ?? TryFindOrder();
                if (placedOrder != null)
                {
                    _cartAccessor.Cart.OrderCarrier = orderCarrier ?? placedOrder.GetAsCarrier(true, true, true, true, true, true);
                    if (order == null)
                    {
                        this.Log().Debug("Processing target group event for external order id {externalOrderId}.", placedOrder.ExternalOrderID);
                        _targetGroupEngine.Process(new OrderEvent {
                            Order = placedOrder
                        });
                    }
                    else
                    {
                        this.Log().Debug("Target group processing for external order id {externalOrderId} was executed with the order creation.", placedOrder.ExternalOrderID);
                    }
                }
            }

            Order TryFindOrder()
            {
                var checkoutOrderCarrier = paymentWidgetOrder.OrderCarrier;
                var hasExternalOrderId   = !string.IsNullOrEmpty(checkoutOrderCarrier.ExternalOrderID);
                var foundOrder           = hasExternalOrderId
                    ? _moduleECommerce.Orders.GetOrder(checkoutOrderCarrier.ExternalOrderID, _securityToken)
                    : _moduleECommerce.Orders.GetOrder(checkoutOrderCarrier.ID, _securityToken);

                if (foundOrder == null)
                {
                    if (hasExternalOrderId)
                    {
                        this.Log().Debug("No order exists for external order external id {externalOrderId}.", checkoutOrderCarrier.ExternalOrderID);
                    }
                    else
                    {
                        this.Log().Debug("No order exists for order id {orderId}.", checkoutOrderCarrier.ID);
                    }

                    var transactionNumber = checkoutOrderCarrier.PaymentInfo.FirstOrDefault()?.TransactionNumber;
                    if (!string.IsNullOrEmpty(transactionNumber))
                    {
                        this.Log().Debug("Try to find order based on transactionNumber {transactionNumber}.", transactionNumber);
                        foundOrder = _moduleECommerce.Orders.GetOrdersByTransactionNumber(paymentWidgetOrder.PaymentProviderName, transactionNumber, _securityToken).FirstOrDefault();
                        if (foundOrder == null)
                        {
                            this.Log().Debug("No order based on transactionNumber {transactionNumber} was found.", transactionNumber);
                        }
                    }
                    else
                    {
                        this.Log().Debug("No transaction number exists, or payment info missing. No order was found.");
                    }
                }

                if (foundOrder != null)
                {
                    this.Log().Debug("Persisted order is found, order id {orderId}, external order id {externalOrderId}.", foundOrder.ID, foundOrder.ExternalOrderID);
                }
                return(foundOrder);
            }
        }