Using a shopping cart on MVC sites

This page is part one of the Implementing a checkout process series.

The first step towards building a robust checkout process is the implementation of a shopping cart where customers can place offered products. Kentico provides, in the CMS.Ecommerce namespace (installed into your project as part of the Kentico.LibrariesNuGet package), a fully customizable IShoppingService interface, whose purpose is to facilitate shopping cart interaction and order creation. 

See our API reference for a complete overview of API exposed by the IShoppingService interface.

You can use the default implementation of this interface, the ShoppingService, to quickly and efficiently implement basic shopping cart and checkout functionality.

Managing a shopping cart on MVC sites

ShoppingService simplifies the implementation of all basic shopping cart related activities customers need to perform to complete the checkout process. The service handles manipulation of the current shopping cart, as well as the creation of the resulting order. To begin, create a new controller class utilizing ShoppingService and implement some common functionality needed to manage a shopping cart: 

  1. Open your MVC project in Visual Studio.

  2. Create a new controller for the shopping cart. In this series, we will use a controller named CheckoutController to house all action methods.

  3. Initialize an instance of IShoppingService in the controller’s constructor.

    
    
    
             /// <summary>
             /// Initializes an instance of the IShoppingService used to facilitate shopping cart interactions.
             /// </summary>
             public CheckoutController()
             {
                 // Initializes an instance of a service required to manage the shopping cart
                 // For real-world projects, we recommend using a dependency injection
                 // container to initialize service instances
                 shoppingService = Service.Resolve<IShoppingService>();
             }
    
    
    
     

    We recommend using a dependency injection container to initialize instances of used API services (e.g. IShoppingService).

The CheckoutController needs to process basic actions that customers require, namely:

Tip: To view the full code of a functional example, you can inspect and download the LearningKit project on GitHub. You can also run the LearningKit website by connecting the project to a Kentico database.

Displaying the shopping cart

To display a customer’s current shopping cart, add an action method that retrieves their cart, fills a view model representation of the cart, and displays it in a related view. To retrieve the shopping cart for the current customer, use the IShoppingService.GetCurrentShoppingCart method:




        /// <summary>
        /// Displays the customer's current shopping cart.
        /// </summary>
        public ActionResult ShoppingCart()
        {
            // Gets the current user's shopping cart
            ShoppingCartInfo currentCart = shoppingService.GetCurrentShoppingCart();

            // Initializes the shopping cart model
            ShoppingCartViewModel model = new ShoppingCartViewModel(currentCart);

            // Displays the shopping cart
            return View(model);
        }



Accessing the current shopping cart

You can customize how Kentico retrieves the current shopping cart, for example:

  • When the same shopping cart is used across multiple sites running under one Kentico instance
  • When an older saved shopping cart overrides the current shopping cart
  • What shopping cart information is removed when the shopping cart is retrieved from the database

See Retrieving the current shopping cart to learn more.

Create a view model (the example uses a ShoppingCartViewModel) to store select properties of the ShoppingCartInfo object that you want to display or otherwise require in a related view. 




    public class ShoppingCartViewModel
    {
        public IEnumerable<ShoppingCartItemViewModel> CartItems { get; set; }

        public string CurrencyFormatString { get; set; }

        public IEnumerable<string> CouponCodes { get; set; }

        public decimal TotalTax { get; set;}

        public decimal TotalShipping { get; set; }

        public decimal GrandTotal { get; set; }

        public decimal RemainingAmountForFreeShipping { get; set; }

        public bool IsEmpty { get; set; }

        /// <summary>
        /// Constructor for the ShoppingCartViewModel. 
        /// </summary>
        /// <param name="cart">A shopping cart object.</param>
        public ShoppingCartViewModel(ShoppingCartInfo cart)
        {
            // Creates a collection containing all lines from the given shopping cart
            CartItems = cart.CartProducts.Select((cartItemInfo) =>
            {
                return new ShoppingCartItemViewModel()
                {
                    CartItemUnits = cartItemInfo.CartItemUnits,
                    SKUName = cartItemInfo.SKU.SKUName,
                    TotalPrice = cartItemInfo.TotalPrice,
                    CartItemID = cartItemInfo.CartItemID,
                    SKUID = cartItemInfo.SKUID
                };
            });
            CurrencyFormatString = cart.Currency.CurrencyFormatString;
            CouponCodes = cart.CouponCodes.AllAppliedCodes.Select(x => x.Code);
            TotalTax = cart.TotalTax;
            TotalShipping = cart.TotalShipping;
            GrandTotal = cart.GrandTotal;
            RemainingAmountForFreeShipping = cart.CalculateRemainingAmountForFreeShipping();
            IsEmpty = cart.IsEmpty;
        }

    }



The ShoppingCartViewModel stores the following ShoppingCartInfo properties:

  • CartItems – A collection of items (products) contained in the shopping cart.

    Note that each product is itself represented by a view model. We strongly recommend creating a separate view model for each database object you want to send to a view template. View models give you full control over the data transferred to and from your views, and can help avoid potential security vulnerabilities. Fill the model only with data you wish to display or otherwise require for the functionality of your view. 

    The ShoppingCartItemViewModel contains the following properties:

    • SKUName – full name of the product as specified during its creation.

    • CartItemUnits – quantity of the item in the current shopping cart.

    • TotalPrice – total sum of the item given in the cart’s currency.

    • CartItemID – ID of the given item, used when creating an order from the shopping cart during the final step.

      
      
      
            public class ShoppingCartItemViewModel
            {
                public string SKUName { get; set; }
      
                public int SKUID { get; set; }
      
                public int CartItemUnits { get; set; }
      
                public decimal TotalPrice { get; set; }
      
                public int CartItemID { get; set; }
            }
      
      
      
        
  • CurrencyFormatString – a string literal used to format a numeric value based on the shopping cart’s selected currency.

  • CouponCodes – a collection of applied coupon code string literals. See Working with coupon codes for more information about coupon codes in general.

    See Adding coupon code inputs to pages in MVC to learn more about implementing coupon code support for the shopping cart.

  • TotalTax – stores the total tax amount that needs to be paid for products currently in the shopping cart. The total tax  value is computed from tax classes assigned to particular products, product variants, and shipping options.

  • TotalShipping – total shipping to be paid for the products in the current shopping cart. This value is calculated based on the cart’s selected shipping option.

  • GrandTotal – total amount to be paid after all taxes, discounts, and gift cards have been applied.

  • RemainingAmountForFreeShipping – holds the remaining amount required for the order to qualify for any free shipping offers.

  • IsEmpty – indicates whether the shopping cart contains any products.

In the connected view, use ShoppingCartViewModel to display the shopping cart’s contents. For example:




@if (Model.IsEmpty)
{
    <span>Your shopping cart is empty.</span>
}
else
{
    <ul>
        @* Loops through all shopping cart items. *@
        @foreach (ShoppingCartItemViewModel cartItem in Model.CartItems)
        {
            @* Displays the shopping cart item's properties. *@
            <li>
                @cartItem.CartItemUnits&times; @cartItem.SKUName ... @String.Format(Model.CurrencyFormatString, cartItem.TotalPrice)
                @* Allows the item to be removed. *@
                @using (Html.BeginForm("RemoveItem", "Checkout", FormMethod.Post))
                {
                    @Html.Hidden("ItemId", cartItem.CartItemID)
                    <input type="submit" value="Remove" />
                }
            </li>
        }
    </ul>
    @* Allows all items to be removed. *@
    @Html.ActionLink("Remove all products", nameof(CheckoutController.RemoveAllItems))
}



The excerpt above iterates over all products contained in the shopping cart and displays the name, quantity, and total price of each product.

Evaluating and saving the shopping cart

Methods from the default implementation of the IShoppingService handle shopping cart saving and evaluation automatically.

When working with objects of the CMS.Ecommerce.ShoppingCartInfo type, you may need to manually handle recalculation and saving of the shopping cart in certain cases (e.g. when extending the default functionality, or implementing a custom variant of the IShoppingService interface). Otherwise your shopping carts may display or store incorrect values.

Perform these operations by calling the following methods:

  • ShoppingCartInfo.Evaluate() – fully recalculates the shopping cart’s values and totals based on its current content and properties (discounts, taxes, shipping, etc.). You need to call the Evaluate method after you modify the shopping cart content or set any properties that could affect the calculation results.
  • IShoppingService.SaveCart() – validates and saves the current user’s shopping cart object to the database. For example, call this method prior to checkout – the validation may remove items which get sold out before an order is completed.

Adding a product to the shopping cart

Adding a product to the user’s current shopping cart can be done via the IShoppingService.AddItemToCart method. The method automatically performs basic validation (valid product, quantity, etc.) on the product, inserts it into the shopping cart, and evaluates the cart. For example, this action can be triggered when a user adds an item to the shopping cart from the product catalogue or product details page.




        /// <summary>
        /// Adds products to the customer's current shopping cart.
        /// </summary>
        /// <param name="itemSkuId">ID of the added item (its SKU object).</param>
        /// <param name="itemUnits">Quantity of the item to be added.</param>
        [HttpPost]
        public ActionResult AddItem(int itemSkuId, int itemUnits)
        {
            // Adds the specified number of units of a given product to the current shopping cart
            shoppingService.AddItemToCart(itemSkuId, itemUnits);

            // Displays the shopping cart
            return RedirectToAction("ShoppingCart");
        }



Updating a shopping cart item

Update of an item from the user’s current shopping cart is facilitated by the IShoppingService.UpdateItem method. The method updates the specified item’s quantity and evaluates the cart. This action can be triggered by, for example, a click on an Update button in a view displaying the shopping cart’s contents.




        /// <summary>
        /// Updates the quantity of an item in the customer's current shopping cart.
        /// </summary>
        /// <param name="itemID">ID of the shopping cart item to update.</param>
        /// <param name="itemUnits">Desired quantity of the shopping cart item being updated.</param>
        [HttpPost]
        public ActionResult UpdateItem(int itemID, int itemUnits)
        {
            // Updates the quantity of a given product with the specified number of units
            // If the quantity is set to zero, removes the product from the shopping cart
            shoppingService.UpdateItemQuantity(itemID, itemUnits);

            // Displays the shopping cart
            return RedirectToAction("ShoppingCart");
        }



Removing a shopping cart item

Removal of a an item from the user’s current shopping cart is handled by IShoppingServiceService.RemoveItemFromCart. When removing a bundle, the method automatically removes all products belonging to the bundle. For example, you can provide this option when displaying an overview of all items in a shopping cart.




        /// <summary>
        /// Removes a shopping cart item from the customer's current shopping cart.
        /// </summary>
        /// <param name="itemID">ID of the item to be removed.</param>
        [HttpPost]
        public ActionResult RemoveItem(int itemID)
        {
            // Removes a specified product from the shopping cart
            shoppingService.RemoveItemFromCart(itemID);

            // Displays the shopping cart
            return RedirectToAction("ShoppingCart");
        }



Removing all items from the shopping cart

Behaving similarly to the example above, IShoppingService.RemoveAllItemsFromCart removes all products from the user’s current shopping cart. For example, you can provide this option when displaying an overview of all items in a shopping cart.  




        /// <summary>
        /// Removes all products from the customer's current shopping cart.
        /// </summary>
        public ActionResult RemoveAllItems()
        {
            // Removes all products from the current shopping cart
            shoppingService.RemoveAllItemsFromCart();

            // Displays the shopping cart
            return RedirectToAction(nameof(CheckoutController.ShoppingCart));
        }



Moving to the next step of the checkout process

Implement an action enabling users to continue with the checkout process. The shopping cart needs to be validated and saved before continuing to the next step. This ensures the shopping cart’s contents are still valid at the time the action is executed (all contained products are available for purchase, there is a sufficient quantity of them in stock, discounts and coupon codes are correctly applied, etc.):




        /// <summary>
        /// Validates the shopping cart and proceeds to the next checkout step.
        /// </summary>
        [HttpPost]
        [ActionName("ShoppingCart")]
        public ActionResult ShoppingCartCheckout()
        {
            // Gets the current user's shopping cart
            ShoppingCartInfo cart = shoppingService.GetCurrentShoppingCart();

            // Validates the shopping cart
            var cartValidator = ShoppingCartInfoProvider.ValidateShoppingCart(cart);

            // If the validation is successful, redirects to the next step of the checkout process
            if (!cartValidator.Any())
            {
                // Saves the validated shopping cart before redirecting the checkout step
                // This prevents loss of data between requests.
                shoppingService.SaveCart();

                return RedirectToAction("DeliveryDetails");
            }

            // If the validation fails, redirects back to the shopping cart
            return RedirectToAction("ShoppingCart");
        }



Continue with the checkout process implementation in the next part of this series: Gathering customer details during checkout.