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); }
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)); }
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); } }