---
title: Customizing discounts
related:
  - https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts.md
  - https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/configuring-discount-rules.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

The Xperience E-commerce Solution allows you to customize how the system applies [discounts](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts.md) to products and orders. For example, you can:

- Define and apply custom discounts (for example discounts provided by an external system or application)
- Adjust or override the functionality of the default discount types

> **Note:** **Note**: If you only need to set up custom conditions under which discounts are applied, you may be able to achieve your goal by creating [discount macro rules](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/configuring-discount-rules.md).

To customize discounts:

1. Prepare an assembly (_Class Library_ project) with class discovery enabled in your Xperience solution (or use an existing one). See [Adding custom assemblies](https://docs.kentico.com/13/custom-development/adding-custom-assemblies.md).

   - Reference the project from both your live site and Xperience administration (_CMSApp_) projects.
2. Create new classes that implement one or more of the [discount customization interfaces](#discount-customization-interfaces) (described below).
3. Implement all methods required by the given interfaces.
4. Register your implementations of the interfaces using the **RegisterImplementation** assembly attribute.

When you reload the website, the system uses the custom implementations that you registered instead of the default discount functionality.

## Discount customization interfaces

> **Info:** All mentioned interfaces can be found in the **CMS.Ecommerce** namespace.

### Product-level discounts (catalog and volume)

The system uses the following interfaces to load discounts that apply to individual units of products (includes [Catalog](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-catalog-discounts.md) and [Volume](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-volume-discounts.md) discounts by default):

- **ICatalogDiscountSource** – implementations must contain the _GetDiscounts_ method, which returns an _IEnumerable_ collection of _DiscountInfo_ objects (representing applied catalog discounts) for a specified product (_SKUInfo_). Additional data related to the discount calculation context is available in the method's _PriceParameters_ parameter.
- **IVolumeDiscountSource** – implementations must contain the _GetDiscount_ method, which returns a _VolumeDiscountInfo_ object for a specified product (_SKUInfo_) and unit quantity. Return _null_ to apply no volume discount.
- **IProductDiscountSource** – encapsulates the overall retrieval of product-level discounts. By default uses the _ICatalogDiscountSource_ and _IVolumeDiscountSource_ implementations to load catalog and volume discounts. Implementations of _IProductDiscountSource_ must contain the _GetDiscounts_ method, which returns an _IEnumerable_ collection of _DiscountCollection_ objects (representing applied discounts) for a specified product (_SKUInfo_). Additional data related to the discount calculation context is available in the method's _PriceParameters_ parameter.

The **PriceParameters** parameter available in the methods of _ICatalogDiscountSource_ and _IProductDiscountSource_ provides the following properties:

- **Currency** – the currency (_CurrencyInfo_) in which the discount is calculated (for discounts with fixed values).
- **Quantity** – the number of product units.
- **SiteID** – an identifier of the site on which the discount is calculated.
- **User** – the user object (_UserInfo_) for which the discount is calculated. Is _null_ for anonymous customers who are not registered as website users.
- **Customer** – the [customer](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/customers.md) object (_CustomerInfo_) for which the discount is calculated. Is _null_ when calculating prices for anonymous visitors before they enter customer details in the checkout process.
- **CalculationDate** – a _DateTime_ value storing the time for which the discount is calculated (can be used to check the validity of discounts). For most calculations, the value is equal to the current time. When calculating discounts for existing orders, the value is equal to the date and time when the order was created.

See the following customization examples:

- [Example - Adding a custom product discount](#example-adding-a-custom-product-discount)
- [Example - Using custom volume discounts](#example-using-custom-volume-discounts)

> **Info:** The E-commerce API uses the following additional interfaces to connect the discount sources with the overall product price calculation logic (see [Customizing product prices](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/product-related-customizing/customizing-product-prices.md) to learn more):
>
> - **IDiscountApplicator** – general service for applying multiple discounts (also used for [order discounts](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-order-discounts.md) and [free shipping offers](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-free-shipping-offers.md)). Implementations must contain the _ApplyDiscounts_ method, which performs the following functions:
>   - Processes parameters that specify a base price (_decimal_) and an _IEnumerable_ collection of _DiscountCollection_ objects (representing applied discounts).
>   - Returns a summary of the applied discounts as a _ValuesSummary_ object containing discount name and value pairs.
> - **IProductDiscountService** – builds the overall summary of applied product-level discounts. Implementations must contain the _GetProductDiscounts_ method, which returns a _ValuesSummary_ object for a specified product (_SKUInfo_), base price (_decimal_), and price calculation parameters (_PriceParameters_). The default _IProductDiscountService_ implementation gets the collection of _DiscountCollection_ objects using the registered _IProductDiscountSource_, and then creates the _ValuesSummary_ using the _IDiscountApplicator_ implementation.
>
> Customization of these interfaces is not required or recommended for typical discount customization scenarios.

[> Back to the discount interface list](#customizingdiscounts-discountinterfacelist)

### Product coupons

The system uses the **IProductCouponSource** interface to load discounts that require a coupon code and apply to shopping cart lines (each line represents one or more units of a product). See [Working with product coupons](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-product-coupons.md) to learn about the default functionality.

> **Note:** The **IProductCouponSource** interface is only intended for customization scenarios that utilize the default Xperience product coupon objects (i.e. objects represented by the _MultiBuyDiscountInfo_ class in the API and managed in the _Product coupons_ application of the administration interface).
>
> If you need to integrate product coupons from an external source or service, we recommend adding a custom step (_IShoppingCartCalculator_ implementation) into the overall shopping cart calculation process. See [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) for more information.

Implementations of _IProductCouponSource_ must contain the **GetDiscounts** method. The method provides a **DiscountsParameters** parameter with the following properties:

- **CouponCodes** – a collection of [coupon codes](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-coupon-codes.md) that the customer added to the order.
- **Currency** – the currency (_CurrencyInfo_) in which the discount is calculated.
- **DueDate** – a _DateTime_ value used to evaluate the validity of discounts. For most calculations, the value is equal to the current time. When calculating discounts for existing orders, the value is equal to the date and time when the order was created.
- **SiteID** – an identifier of the site on which the discount is calculated.
- **User** – the user object (_UserInfo_) for which the discount is calculated. Is _null_ for anonymous customers who are not registered as website users.

The _GetDiscounts_ method must return an _IEnumerable_ collection of **IMultiBuyDiscount** objects representing the applied discounts (we recommended using instances of the default **MultiBuyDiscount** class).

> **Info:** The E-commerce API additionally uses the **IMultiBuyDiscountsApplicator** and **IProductCouponService** interfaces to connect the product coupon source with the overall shopping cart and order calculation logic. However, customization of these interfaces is not required for typical scenarios. For such advanced cases, we instead recommend adding a custom step (_IShoppingCartCalculator_ implementation) into the overall shopping cart calculation process (see [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) for more information).

[> Back to the discount interface list](#customizingdiscounts-discountinterfacelist)

### Buy X Get Y discounts

The system uses the **IMultiBuyDiscountSource** interface to load discounts that work based on combinations of products and apply to shopping cart lines (each line represents one or more units of a product). See [Working with Buy X Get Y discounts](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-buy-x-get-y-discounts.md) to learn about the default functionality.

Implementations of _IMultiBuyDiscountSource_ must contain the **GetDiscounts** method, which provides a **DiscountsParameters** parameter (see the [Product coupons](#product-coupons) section to learn about the available properties). The _GetDiscounts_ method must return an _IEnumerable_ collection of **IMultiBuyDiscount** objects representing the applied discounts (we recommended using instances of the default **MultiBuyDiscount** class).

If you need to modify how the system evaluates whether the content of a shopping cart is eligible for a Buy X Get Y discount, create a custom implementation of the **IMultiBuyDiscountsEvaluator** interface. We recommend using the following general approach:

1. Add a custom class that inherits from the **MultiBuyDiscountsEvaluator** class (the system's default implementation).
2. Override the virtual methods that you wish to change.
3. Register your custom class using the **RegisterImplementation** assembly attribute.

   ```csharp

   using CMS;
   using CMS.Core;
   using CMS.Ecommerce;

   // Registers the custom implementation of IMultiBuyDiscountsEvaluator
   // Set the 'Lifestyle.Transient' lifetime when registering the IMultiBuyDiscountsEvaluator implementation
   [assembly: RegisterImplementation(typeof(IMultiBuyDiscountsEvaluator), typeof(CustomMultiBuyDiscountsEvaluator), Lifestyle = Lifestyle.Transient)]

   public class CustomMultiBuyDiscountsEvaluator : MultiBuyDiscountsEvaluator
   {
       ...
   }

   ```

   > **Info:** The E-commerce API additionally uses the **IMultiBuyDiscountsApplicator** and **IMultiBuyDiscountsService** interfaces to connect the Buy X Get Y functionality with the overall shopping cart and order calculation logic. However, customization of these interfaces is not required for typical scenarios. For such advanced cases, we recommend implementing your custom discount functionality as a separate step (_IShoppingCartCalculator_ implementation) in the overall shopping cart calculation process. See [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) for more information.

[> Back to the discount interface list](#customizingdiscounts-discountinterfacelist)

### Order discounts

The system uses the **IOrderDiscountSource** interface to load discounts that apply to entire orders/purchases ([Order discounts](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-order-discounts.md) by default). Implementations must contain the **GetDiscounts** method, which provides the following parameters:

- A **CalculatorData** object holding data related to the shopping cart or order to which the discount is applied. See [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) for more information.
- A decimal value matching the total price of the order (before shipping costs, taxes and gift cards).

The _GetDiscounts_ method must return an _IEnumerable_ collection of **DiscountCollection** objects representing the applied discounts.

> **Info:** The E-commerce API uses the general **IDiscountApplicator** interface to connect the order discount source with the overall shopping cart and order calculation logic (see [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) to learn more).
>
> Implementations of _IDiscountApplicator_ must contain the _ApplyDiscounts_ method, which performs the following functions:
>
> - Processes parameters that specify a base price (_decimal_) and an _IEnumerable_ collection of _DiscountCollection_ objects (representing applied discounts).
> - Returns a summary of the applied discounts as a _ValuesSummary_ object containing discount name and value pairs.
>
> Customization of this interface is not required or recommended for typical discount customization scenarios.

[> Back to the discount interface list](#customizingdiscounts-discountinterfacelist)

### Shipping discounts

The system uses the **IShippingDiscountSource** interface to load discounts that apply to [shipping](https://docs.kentico.com/13/e-commerce-features/configuring-on-line-stores/configuring-shipping-options.md) costs ([Free shipping offers](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-free-shipping-offers.md) by default). Implementations must contain the following methods:

- **GetDiscounts** – returns an _IEnumerable_ collection of _IDiscount_ objects representing the applied discounts. Provides the following parameters:
  - A **CalculatorData** object holding data related to the shopping cart or order to which the shipping discount is applied. See [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) for more information.
  - A decimal value matching the total price of the order (before taxes and gift cards).
- **GetRemainingAmountForFreeShipping** – returns a decimal value indicating how much the order's price would need to increase to fulfill the conditions of a shipping discount. Return 0 if there is no valid shipping discount or if the shipping is already free. Provides the same parameters as the _GetDiscounts_ method.

> **Info:** The E-commerce API uses the following additional interfaces to connect the shipping discount source with the overall shopping cart and order calculation logic (see [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) to learn more).
>
> - **IDiscountApplicator** – general service for applying multiple discounts (also used for product-level discounts and order discounts). Implementations must contain the _ApplyDiscounts_ method, which performs the following functions:
>
>   - Processes parameters that specify a base price (_decimal_) and an _IEnumerable_ collection of _DiscountCollection_ objects (representing applied discounts).
>   - Returns a summary of the applied discounts as a _ValuesSummary_ object containing discount name and value pairs.
> - **IShippingPriceService** – calculates the final shipping price. The default _IShippingPriceService_ implementation gets shipping discounts using the registered _IShippingtDiscountSource_, and then creates the required shipping discount summary using the _IDiscountApplicator_ implementation.
>
> Customization of the _IDiscountApplicator_ interface is not required or recommended for typical discount customization scenarios.
>
> To learn more about customization of shipping cost customizations in general, refer to the [Shipping-related customizing](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shipping-related-customizing.md) chapter.

[> Back to the discount interface list](#customizingdiscounts-discountinterfacelist)

### Gift cards

The system uses the **IGiftCardSource** interface to load gift cards that reduce the final price of orders (see [Working with gift cards](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-gift-cards.md) to learn about the default functionality).

> **Note:** The **IGiftCardSource** interface is only intended for customization scenarios that utilize the default Xperience gift card objects (i.e. gift cards represented by the _GiftCardInfo_ class in the API and managed in the _Gift cards_ application of the administration interface).
>
> If you need to integrate gift cards from an external source or service, we recommend adding a custom step (_IShoppingCartCalculator_ implementation) into the overall shopping cart calculation process. See [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md) for more information.

Implementations of _IGiftCardSource_ must contain the **GetGiftCards** method, which provides the following parameters:

- A **CalculatorData** object holding data related to the shopping cart or order to which the gift card is applied (see [Customizing the shopping cart calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/shopping-cart-related-customizing/customizing-the-shopping-cart-calculation.md)). For example, use the _CalculatorData.Request.CouponCodes_ property to get a collection of [coupon codes](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-coupon-codes.md) that the customer added to the order.
- A decimal value matching the total price of the order (including other discounts, shipping costs and taxes).

The _GetGiftCards_ method must return an _IEnumerable_ collection of **GiftCardApplication** objects representing the applied gift cards.

[> Back to the discount interface list](#customizingdiscounts-discountinterfacelist)

## Example – Adding a custom product discount

The following example demonstrates how to implement a custom product-level discount. This customization applies all valid catalog and volume discounts from Xperience, and then additionally reduces the price of all products by 10% for customers who are registered users on the website.

Prepare an assembly (_Class Library_ project) with class discovery enabled in your Xperience solution (or use an existing one). See [Adding custom assemblies](https://docs.kentico.com/13/custom-development/adding-custom-assemblies.md).

- Reference the project from both your live site and Xperience administration (_CMSApp_) projects.

Continue by creating a custom implementation of the **IProductDiscountSource** interface:

1. Add a new class under the custom project, inheriting from the default **ProductDiscountSource** class (and implementing the _IProductDiscountSource_ interface).

   ```csharp

   using System.Collections.Generic;

   using CMS;
   using CMS.Ecommerce;

   // Registers the custom implementation of IProductDiscountSource
   [assembly: RegisterImplementation(typeof(IProductDiscountSource), typeof(CustomProductDiscountSource))]

   public class CustomProductDiscountSource : ProductDiscountSource, IProductDiscountSource
   {
       /// <summary>
       /// Constructor with parameters that accept instances of services required by the ProductDiscountSource base class.
       /// </summary>
       public CustomProductDiscountSource(ICatalogDiscountSource catalogDiscountSource, IVolumeDiscountSource volumeDiscountSource, ISiteMainCurrencySource mainCurrencySource, ICurrencyConverterFactory currencyConverterFactory, IRoundingServiceFactory roundingServiceFactory) 
           : base(catalogDiscountSource, volumeDiscountSource, mainCurrencySource, currencyConverterFactory, roundingServiceFactory)
       {
       }

       /// <summary>
       /// Creates a collection of product-level discounts that apply to a specified product.
       /// Additional data related to the price and purchase context is provided in the PriceParameters.
       /// </summary>
       public override IEnumerable<DiscountCollection> GetDiscounts(SKUInfo sku, decimal standardPrice, PriceParameters priceParams)
       {
           var discountGroups = new List<DiscountCollection>();

           // Adds the default Xperience catalog and volume discounts (using methods from the ProductDiscountSource base class)
           AddCatalogDiscounts(discountGroups, sku, priceParams);
           AddVolumeDiscounts(discountGroups, sku, priceParams);

           // Adds a custom discount
           // The custom discount uses multiplicative stacking with any applied catalog or volume discounts
           // (i.e. the custom discount is calculated from the price reduced by other discounts)
           AddCustomDiscounts(discountGroups, sku, priceParams);

           return discountGroups;
       }

       /// <summary>
       /// Adds a custom discount for a specified product.
       /// </summary>
       private void AddCustomDiscounts(List<DiscountCollection> discountGroups, SKUInfo sku, PriceParameters priceParams)
       {
           // Adds the discount only for customers who are registered users on the website
           if (priceParams.User != null)
           {
               // Creates a 10% custom discount (using a method from the ProductDiscountSource base class)
               IDiscount customDiscount = CreatePercentageProductDiscount("Discount for registered users", 0.1m, priceParams);

               // Adds the custom discount to the collection of overall product discounts
               var discountCollection = new DiscountCollection(new[] { customDiscount });
               discountGroups.Add(discountCollection);
           }
       }    
   }

   ```
2. Save all changes and **Build** the custom project.

The registered **CustomProductDiscountSource** implementation extends the Xperience product-level discounts. The custom discount source retains the default catalog and volume discount functionality by inheriting from the **ProductDiscountSource** class (the default implementation of _IProductDiscountSource_). The added custom discount affects both price calculations during the checkout process and prices displayed in product catalogs on the live site (when using the [appropriate API](https://docs.kentico.com/13/e-commerce-features/developing-on-line-stores/displaying-product-listings.md)).

## Example – Using custom volume discounts

The following example demonstrates how to override the default volume discount functionality of Xperience. The example defines the volume discount rules directly in the code, but you can use the same basic approach to load the discount parameters from an external source or service.

1. Recreate or reuse the custom project from the [previous example](#example-adding-a-custom-product-discount).
2. Add a new class under the custom project, implementing the _IVolumeDiscountSource_ interface.
3. Save all changes and **Build** the custom project.

```csharp

using CMS;
using CMS.Ecommerce;

// Registers the custom implementation of IVolumeDiscountSource
[assembly: RegisterImplementation(typeof(IVolumeDiscountSource), typeof(CustomVolumeDiscountSource))]

public class CustomVolumeDiscountSource : IVolumeDiscountSource
{

    /// <summary>
    /// Returns an appropriate volume discount for a specified product (SKU) and unit quantity.
    /// </summary>
    public VolumeDiscountInfo GetDiscount(SKUInfo sku, decimal quantity)
    {
        // No volume discount applies if the unit quantity is lower than 3
        if (quantity < 3)
        {
            return null;
        }

        // Defines a custom volume discount
        // The discount value is percentage-based, calculated according to the purchased unit quantity
        var customVolumeDiscount = new VolumeDiscountInfo
        {
            VolumeDiscountSKUID = sku.SKUID,
            VolumeDiscountMinCount = 3,
            VolumeDiscountIsFlatValue = false,
            VolumeDiscountValue = GetDiscountValue(quantity)
        };

        return customVolumeDiscount;        
    }

    /// <summary>
    /// Calculates the volume discount value based on the unit quantity.
    /// </summary>
    private decimal GetDiscountValue(decimal quantity)
    {
        // 5% discount if the unit quantity is at least 3, but below 5
        if (quantity < 5)
        {
            return 5m;
        }
        // 10% discount if the unit quantity is at least 5, but below 10
        if (quantity < 10)
        {
            return 10m;
        }
        // 15% discount if the unit quantity is at least 10, but below 15
        if (quantity < 15)
        {
            return 15m;
        }
        // 20% discount for 15 or more units
        return 20m;
    }
}

```

The registered **CustomVolumeDiscountSource** implementation fully replaces the default Xperience volume discounts. When a customer adds the required number of product units to their shopping cart, the system automatically applies the corresponding discount to the unit price. Any [volume discounts](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts/working-with-volume-discounts.md) defined in the Xperience administration interface no longer apply.
