diff --git a/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs b/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs index 3e4ca008..90045564 100644 --- a/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs +++ b/examples/DancingGoat-K13Ecommerce/Controllers/TestController.cs @@ -1,4 +1,5 @@ using Kentico.Xperience.K13Ecommerce.Orders; +using Kentico.Xperience.K13Ecommerce.Products; using Kentico.Xperience.K13Ecommerce.ShoppingCart; using Kentico.Xperience.K13Ecommerce.StoreApi; @@ -55,7 +56,7 @@ public async Task TestUpdateOrder([FromServices] IOrderService or order.OrderShippingOption = new KShippingOption { ShippingOptionId = 2 }; order.OrderPaymentOption = new KPaymentOption { PaymentOptionId = 1 }; - + order.OrderPaymentResult = new KPaymentResult { PaymentIsCompleted = true, @@ -67,5 +68,19 @@ public async Task TestUpdateOrder([FromServices] IOrderService or return Ok(); } + + public async Task TestCartCacheIssue([FromServices] IShoppingService shoppingService, [FromServices] IProductService productService) + { + //first ensure that ShoppingCartGUID cookie is set to existing cart from previous session + + var prices = await productService.GetProductPrices(33);//set real SKUID + // now empty cart is cached in K13 + + var cart = await shoppingService.GetCurrentShoppingCartContent(); + + // now when cart is empty and cookie is removed -> invalid + + return Json(cart); + } } #endif diff --git a/src/Kentico.Xperience.K13Ecommerce/KenticoStoreServiceCollectionExtensions.cs b/src/Kentico.Xperience.K13Ecommerce/KenticoStoreServiceCollectionExtensions.cs index 0725e1d0..81e6be8a 100644 --- a/src/Kentico.Xperience.K13Ecommerce/KenticoStoreServiceCollectionExtensions.cs +++ b/src/Kentico.Xperience.K13Ecommerce/KenticoStoreServiceCollectionExtensions.cs @@ -2,6 +2,7 @@ using IdentityModel.Client; +using Kentico.Web.Mvc; using Kentico.Xperience.Ecommerce.Common.ContentItemSynchronization; using Kentico.Xperience.K13Ecommerce.Activities; using Kentico.Xperience.K13Ecommerce.Admin; @@ -45,6 +46,10 @@ public static IServiceCollection AddKenticoStoreServices(this IServiceCollection { services.AddOptions().Bind(configuration.GetSection("CMS" + nameof(KenticoStoreConfig))); + services.Configure(options => + options.CookieConfigurations.Add(ShoppingCartClientStorage.ShoppingCartKey, CookieLevel.Essential) + ); + // default cache for token management services.AddDistributedMemoryCache(); //add token management config diff --git a/src/Kentico.Xperience.K13Ecommerce/ShoppingCart/ShoppingCartClientStorage.cs b/src/Kentico.Xperience.K13Ecommerce/ShoppingCart/ShoppingCartClientStorage.cs index 95fdfd1c..54802b41 100644 --- a/src/Kentico.Xperience.K13Ecommerce/ShoppingCart/ShoppingCartClientStorage.cs +++ b/src/Kentico.Xperience.K13Ecommerce/ShoppingCart/ShoppingCartClientStorage.cs @@ -9,7 +9,7 @@ namespace Kentico.Xperience.K13Ecommerce.ShoppingCart; internal class ShoppingCartClientStorage (ICookieAccessor cookieAccessor, IConversionService conversionService) : IShoppingCartClientStorage { - private const string ShoppingCartKey = "ShoppingCartGUID"; + public const string ShoppingCartKey = "ShoppingCartGUID"; /// @@ -19,7 +19,13 @@ internal class ShoppingCartClientStorage /// public void SetCartGuid(Guid cartGuid) => cookieAccessor.Set(ShoppingCartKey, cartGuid.ToString(), - new CookieOptions { HttpOnly = true, Expires = DateTimeOffset.Now.AddMonths(1), SameSite = SameSiteMode.Strict }); + new CookieOptions + { + IsEssential = true, + HttpOnly = true, + Expires = DateTimeOffset.Now.AddMonths(1), + SameSite = SameSiteMode.Strict + }); /// diff --git a/src/Kentico.Xperience.StoreApi/ShoppingCart/StoreApiCurrentShoppingCartService.cs b/src/Kentico.Xperience.StoreApi/ShoppingCart/StoreApiCurrentShoppingCartService.cs index dab1639c..e4e2b8e4 100644 --- a/src/Kentico.Xperience.StoreApi/ShoppingCart/StoreApiCurrentShoppingCartService.cs +++ b/src/Kentico.Xperience.StoreApi/ShoppingCart/StoreApiCurrentShoppingCartService.cs @@ -99,16 +99,19 @@ public void SetCurrentShoppingCart(ShoppingCartInfo cart) /// Candidate cart or null when no candidate cart found. protected ShoppingCartInfo GetCandidateCart(ref bool anonymize, ref bool evaluationIsNeeded) { + //custom code starts + var shoppingCartGuid = GetCurrentCartGuid(); + var cart = mCartCache.GetCart(); - if (cart != null) + if (cart != null && (shoppingCartGuid == Guid.Empty || cart.ShoppingCartGUID != Guid.Empty)) { + // Return cached cart only when requested ShoppingCartGuid is empty otherwise cached cart cannot be empty (ShoppingCartGUID is empty GUID). + // Cache can contain empty cart from another request where cart identifier is not sent, like request for price calculation which works with empty cart return cart; } evaluationIsNeeded = true; - //custom code starts - var shoppingCartGuid = GetCurrentCartGuid(); cart = shoppingCartGuid != Guid.Empty ? mCartRepository.GetCart(shoppingCartGuid) : null; //Don't anonymize cart because getting cart from client is only way how to get it when it isn't cached. //On client app (xbyK) session storage is used as primary option and cookie as secondary, so some anonymize flag in REST api