---
title: Customizing product prices
related:
  - https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products.md
  - https://docs.kentico.com/13/e-commerce-features/configuring-on-line-stores/configuring-products.md
  - https://docs.kentico.com/13/custom-development.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 retrieves [product](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products.md) prices. For example, you can:

- Adjust the system to load price values from a custom database column
- Integrate an external system that fully provides prices for products and their various configurations

To customize the retrieval of prices:

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 [pricing customization interfaces](#pricing-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 (or use a _Factory_ class to serve instances dynamically in certain cases).

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

## Pricing customization interfaces

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

### Product price source

The price source implementation determines how the system gets product prices on the most basic level. The default Xperience price source loads product prices from the _SKUPrice_ (standard price) or _SKURetailPrice_ (list price) columns of the _COM\_SKU_ database table.

This type of customization allows you to change the source of the product price (for example a database column) while keeping all other parts of the default Xperience price processing.

- **ISKUPriceSource** –  implementations must contain the _GetPrice_ method, which returns a price value (decimal) for a specified product and currency.
- **ISKUPriceSourceFactory** – serves a separate instance of a class implementing _ISKUPriceSource_ for each site. Must be implemented to use custom _ISKUPriceSource_ implementations. Requires the following methods:

  - **GetSKUPriceSource** – returns an _ISKUPriceSource_ instance for loading standard product prices.
  - **GetSKUListPriceSource** – returns an _ISKUPriceSource_ instance for loading product list prices.

    > **Info:** When writing the _GetSKUPriceSource_ and _GetSKUListPriceSource_ methods, you can either return instances of different _ISKUPriceSource_ implementations or use a single implementation with constructor parameters for switching between the standard price and list price.

See: [Example - Loading product prices from custom columns](#example-loading-product-prices-from-custom-columns)

### Product pricing service

The system uses a pricing service to calculate the prices of products within a specific context, including adjustments from [product options](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products/working-with-product-options.md) (of the _Attribute_ and _Text_ type) and product unit [discounts](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/discounts.md) (catalog discounts and volume discounts). The default Xperience pricing service uses the _ISKUPriceSource_ and _ISKUPriceSourceFactory_ implementations to get the basic price, adds adjustments for any assigned product options, and then applies all valid product discounts.

Creating a custom implementation of the **IProductPricingService** interface allows you to fully replace the default Xperience price processing, for example using an external pricing service.

Implementations of _IProductPricingService_ must contain the **GetPrices** method. The method provides the following parameters:

- The product (_SKUInfo_) – can be a standard product, [product variant](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products/working-with-product-variants.md), or [product option](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products/working-with-product-options.md) of the _Products_ type.
- Applied product options of the _Attribute_ or _Text_ type (_IEnumerable_).
- A **PriceParameters** object holding additional data related to the price context, with the following properties:

  - **Currency** – the currency (_CurrencyInfo_) in which the price is requested.
  - **Quantity** – the number of product units within the pricing context (can be used to calculate custom volume discounts).
  - **SiteID** – an identifier of the site on which the price is calculated.
  - **User** – the user object (_UserInfo_) for which the prices are 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 prices are 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 price is calculated (can be used to check the validity of product discounts). For most price calculations, the value is equal to the current time. When calculating prices for existing orders, the value is equal to the date and time when the order was created.

The _GetPrices_ method must return a **ProductPrices** object, which holds the following properties:

- **StandardPrice** – the calculated price before any discounts are applied.
- **Price** – the calculated final price including discounts.
- **AppliedDiscounts** – a summary of any applied discounts (represented by a _ValuesSummary_ object containing discount name and value pairs).
- **Currency** – the currency (_CurrencyInfo_) in which the prices were calculated. Typically, use the currency passed in the _PriceParameters.Currency_ parameter of the _GetPrices_ method.

See: [Example - Using an external pricing service](#example-using-an-external-pricing-service)

### Catalog price calculation

The system uses an additional API layer to calculate the prices displayed in product catalogs on the live website. The default Xperience catalog price calculation uses the _IProductPricingService_ implementation to get product prices, adds adjustments for any selected product accessories ([product options](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products/working-with-product-options.md) of the _Products_ type), and then uses the _ICatalogTaxCalculator_ implementation to get information about tax values (see [Customizing tax calculation](https://docs.kentico.com/13/e-commerce-features/customizing-on-line-stores/customizing-tax-calculation.md)).

Customize the catalog price calculation if you need to adjust how prices are displayed in the live site catalog, without affecting the price calculations during the checkout process.

- **ICatalogPriceCalculator**
- **ICatalogPriceCalculatorFactory** – serves a separate instance of a class implementing _ICatalogPriceCalculator_ for each site. Must be implemented to use custom _ICatalogPriceCalculator_ implementations.

Implementations of _ICatalogPriceCalculator_ must contain two methods:

- **GetPrices** – calculates the prices displayed on the live site in product catalogs.
- **GetAdjustment** – calculates the price adjustment value for selectors of product options. Returns a _decimal_ price adjustment value, and provides parameters representing the given product option (_SKUInfo_), parent product or product variant (_SKUInfo_), and the current shopping cart (_ShoppingCartInfo_).

The **GetPrices** method provides the following parameters:

- The product or [product variant](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products/working-with-product-variants.md) (_SKUInfo_).
- Applied product options (_IEnumerable_) of all types.
- The current shopping cart (_ShoppingCartInfo_) of the user browsing the catalog, which provides any required context data, such as the user object or selected currency.

The _GetPrices_ method must return a **ProductCatalogPrices** object, which holds the following properties:

- **StandardPrice** – the calculated price before any discounts are applied.
- **Price** – the calculated price including discounts.
- **ListPrice** – the list price (typically not a calculated price, but a value representing the recommended retail price).
- **Tax** – the tax value calculated for the _Price_ (depending on the site's  _Prices include tax_ [setting](https://docs.kentico.com/13/configuring-xperience/managing-sites/configuring-settings-for-sites/settings-e-commerce.md), the value either represents the tax that will be added to the price in the checkout process, or the tax component extracted from the price).
- **Discounts** – a summary of any applied discounts (represented by a _ValuesSummary_ object containing discount name and value pairs).
- **Currency** – the currency (_CurrencyInfo_) in which the prices were calculated. Typically, use the currency passed in the _ShoppingCartInfo.Currency_ parameter of the _GetPrices_ method.

## Example – Loading product prices from custom columns

The following example demonstrates how to customize the system to load product prices from a different database column than the default _SKUPrice_. The example loads prices from a custom _SKUCustomPrice_ column for products that have their [internal status](https://docs.kentico.com/13/e-commerce-features/managing-on-line-stores/products/product-statuses.md) set to _Special offer_.

Start by adding a custom column (field) for product objects:

1. Open the **Modules** application.
2. Edit the **E-commerce** module.
3. Switch to the **Classes** tab.
4. Edit the **SKU** class.
5. Switch to the **Fields** tab.
6. [Create a new field](https://docs.kentico.com/13/custom-development/extending-the-administration-interface/developing-form-controls/reference-field-editor.md) based on your needs.

   - The example uses _SKUCustomPrice_ as the **Field name**.
   - We strongly recommend using _**Decimal number**_ as the **Data type** of fields storing price values.
   - You can use the default _**Price selector**_ as the field's **Form control** if you wish to show the field in editing forms in the product administration interface.
7. Click **Save**.

The custom price column is ready for use. By default, store administrators can edit the value in the **Products** application when viewing products on the **General** tab, under the **Custom properties** section.

![Editing the value of a custom price column in the Products application](https://docs.kentico.com/docsassets/13/customizing-product-prices/custom_price_column_editing.png "Editing the value of a custom price column in the Products application")

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 custom implementations of the **ISKUPriceSource** and **ISKUPriceSourceFactory** interfaces:

1. Add a new class under the custom project, implementing the _ISKUPriceSource_ interface.

   ```csharp

   using CMS.Ecommerce;
   using CMS.Helpers;

   public class CustomSKUPriceSource : ISKUPriceSource
   {
       private readonly ISiteMainCurrencySource mainCurrencySource;
       private readonly ICurrencyConverter currencyConverter;
       private readonly bool getListPrice;    

       /// <summary>
       /// The first two constructor parameters accept instances of currency-related classes from CustomSKUPriceSourceFactory.
       /// The third parameter determines whether the price source loads the list price instead of the standard price.
       /// </summary>
       public CustomSKUPriceSource(ISiteMainCurrencySource mainCurrencySource, ICurrencyConverter currencyConverter, bool getListPrice)
       {
           this.mainCurrencySource = mainCurrencySource;
           this.currencyConverter = currencyConverter;
           this.getListPrice = getListPrice;
       }

       /// <summary>
       /// Loads the basic price value for a specified product and currency.
       /// The returned price is then further processed by the system.
       /// </summary>
       public decimal GetPrice(SKUInfo sku, CurrencyInfo currency)
       {
           // Gets the basic price according to custom logic
           decimal price = GetCustomPrice(sku, currency);

           // Uses the default Xperience currency API to convert the price
           // from the site's main currency into the requested currency (if necessary)
           string siteMainCurrencyCode = mainCurrencySource.GetSiteMainCurrencyCode(sku.SKUSiteID);
           return currencyConverter.Convert(price, siteMainCurrencyCode, currency.CurrencyCode);
       }

       /// <summary>
       /// Gets the basic product price. You can replace this method with any type of custom logic.
       /// </summary>
       private decimal GetCustomPrice(SKUInfo sku, CurrencyInfo currency)
       {
           // If the list price is requested, loads the value from the product's default 'SKURetailPrice' column
           if (getListPrice)
           {
               return sku.SKURetailPrice;
           }

           // Gets the product's internal status
           InternalStatusInfo status = InternalStatusInfo.Provider.Get(sku.SKUInternalStatusID);

           // Checks whether the product has the 'specialoffer' internal status
           if ((status != null) && (status.InternalStatusName.ToLowerInvariant() == "specialoffer"))
           {
               // Gets the product's price from a custom database column - SKUCustomPrice
               return ValidationHelper.GetDecimal(sku.GetValue("SKUCustomPrice"), 0);
           }

           // If none of the custom conditions are fulfilled, gets the price from the default 'SKUPrice' column
           return sku.SKUPrice;
       }
   }

   ```
2. Add a new class under the custom project, implementing the _ISKUPriceSourceFactory_ interface.

   ```csharp

   using CMS;
   using CMS.Ecommerce;

   // Registers the custom implementation of ISKUPriceSourceFactory
   [assembly: RegisterImplementation(typeof(ISKUPriceSourceFactory), typeof(CustomSKUPriceSourceFactory))]

   public class CustomSKUPriceSourceFactory : ISKUPriceSourceFactory
   {
       private readonly ISiteMainCurrencySource mainCurrencySource;
       private readonly ICurrencyConverterFactory currencyConverterFactory;

       /// <summary>
       /// Constructor.
       /// The system automatically supplies instances of the ISiteMainCurrencySource and ICurrencyConverterFactory implementations.
       /// </summary>
       public CustomSKUPriceSourceFactory(ISiteMainCurrencySource mainCurrencySource, ICurrencyConverterFactory currencyConverterFactory)
       {
           this.mainCurrencySource = mainCurrencySource;
           this.currencyConverterFactory = currencyConverterFactory;
       }

       /// <summary>
       /// Provides a ISKUPriceSource instance for loading standard product prices.
       /// </summary>
       public ISKUPriceSource GetSKUPriceSource(int siteId)
       {
           // Returns an instance of the custom price source implementation
           // The first two parameters provide instances of currency-related classes required by CustomSKUPriceSource
           // The third parameter configures CustomSKUPriceSource to load standard prices
           return new CustomSKUPriceSource(mainCurrencySource, currencyConverterFactory.GetCurrencyConverter(siteId), getListPrice: false);
       }

       /// <summary>
       /// Provides a ISKUPriceSource instance for loading product list prices.
       /// </summary>
       public ISKUPriceSource GetSKUListPriceSource(int siteId)
       {
           // Returns an instance of the custom price source implementation
           // The first two parameters provide instances of currency-related classes required by CustomSKUPriceSource
           // The third parameter configures CustomSKUPriceSource to load list prices
           return new CustomSKUPriceSource(mainCurrencySource, currencyConverterFactory.GetCurrencyConverter(siteId), getListPrice: true);
       }
   }

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

The registered **CustomSKUPriceSourceFactory** class provides an instance of **CustomSKUPriceSource**, with a parameter that indicates whether the standard price or list price is requested. The **CustomSKUPriceSource** implementation ensures that products with the _Special offer_ internal status load their basic price from the _SKUCustomPrice_ column instead of the default _SKUPrice_. The basic price is then further processed by the system, and 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)).

The sample code also demonstrates how to use the default implementations of currency-related interfaces to convert the returned price value for stores using multiple currencies.

## Example – Using an external pricing service

The following example demonstrates how to use a custom **IProductPricingService** implementation to integrate a 3rd-party service that fully provides prices for products. In this example, the returned price values are static, but you can replace the code with calls to the API of an external pricing service. Product data can be supplied to the pricing service from the parameters of the **GetPrices** method, which is required in all implementations of the _IProductPricingService_ interface.

1. Recreate or reuse the custom project from the [previous example](#example-loading-product-prices-from-custom-columns).
2. Create a new class under the custom project, implementing the _IProductPricingService_ interface.
3. Save all changes and **Build** the custom project.

```csharp

using System.Collections.Generic;

using CMS;
using CMS.Ecommerce;

// Registers the custom implementation of IProductPricingService
[assembly: RegisterImplementation(typeof(IProductPricingService), typeof(CustomProductPricingService))]

public class CustomProductPricingService : IProductPricingService
{
    /// <summary>
    /// Calculates the price for a specified product and product option configuration.
    /// Additional data related to the price context is provided in the PriceParameters.
    /// Returns a 'ProductPrices' object containing the calculated price and information about any applied discounts.
    /// </summary>
    public ProductPrices GetPrices(SKUInfo sku, IEnumerable<SKUInfo> options, PriceParameters priceParams)
    {
        // Gets a custom price for the basic product
        decimal standardPrice = GetCustomPrice(sku, priceParams);

        // Gets a custom price total for any product options
        decimal optionsPrice = 0;
        if (options != null)
        {
            foreach (SKUInfo option in options)
            {
                optionsPrice += GetCustomPrice(option, priceParams);
            }
        }

        decimal priceWithOptions = standardPrice + optionsPrice;

        // Gets a ValuesSummary object containing information about any discounts used by the custom pricing logic
        ValuesSummary appliedDiscounts = GetCustomDiscounts(sku, priceWithOptions, priceParams);

        // Sums up the total discount value
        decimal discountValue = 0;
        foreach (SummaryItem discount in appliedDiscounts)
        {
            discountValue += discount.Value;
        }

        // Returns the calculated product prices. The constructor parameters are:
        // price - the final price of the product
        // standardPrice - the price before any discounts
        // currency - the CurrencyInfo object representing the currency in which the prices were calculated.
        // discounts - a ValuesSummary object containing information about applied discounts
        return new ProductPrices(priceWithOptions - discountValue, priceWithOptions, priceParams.Currency, appliedDiscounts);
    }

    /// <summary>
    /// Gets a custom price for the specified product.
    /// The example only provides a static number, but you can use any type of custom logic,
    /// e.g. call the API of a 3rd-party pricing service.
    /// </summary>
    private decimal GetCustomPrice(SKUInfo sku, PriceParameters priceParams)
    {
        // Returns a price for product options (the currency is specified in the PriceParameters)
        if (sku.IsProductOption)
        {
            return 5m;
        }

        // Returns a base price for products (the currency is specified in the PriceParameters)
        return 50m;
    }

    /// <summary>
    /// Collects information about discounts applied by the custom pricing logic.
    /// The example only returns a basic set of discounts, but you can use any type of custom logic,
    /// e.g. based on data returned by a 3rd-party pricing service.
    /// </summary>
    private ValuesSummary GetCustomDiscounts(SKUInfo sku, decimal price, PriceParameters priceParams)
    {
        var appliedDiscounts = new ValuesSummary();

        // Applies a custom discount of '5' (the currency is specified in the PriceParameters)
        var discount = new SummaryItem
        {
            Name = "Custom discount",
            Value = 5m
        };
        appliedDiscounts.Add(discount);

        // Applies a 10% volume discount if the product quantity is more than 5
        if (priceParams.Quantity > 5)
        {
            var volumeDiscount = new SummaryItem
            {
                Name = "Custom volume discount",
                Value = price * 0.1m
            };
            appliedDiscounts.Add(volumeDiscount);
        }

        return appliedDiscounts;
    }
}

```

The registered **CustomProductPricingService** implementation fully overrides the default Xperience pricing logic. The customization affects both 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)) and price calculations during the checkout process.

> **Note:** **Note**: This example does not convert prices for stores using multiple currencies. See the [previous example](#example-loading-product-prices-from-custom-columns) for information about implementing currency conversions in custom code.

> **Tip:** **Caching external price data**
>
> We _**strongly recommend that you implement caching**_ for any data retrieved from external pricing services. The system may request the price data very frequently when displaying product catalogs, particularly on sites with heavy traffic.
>
> To learn about the custom caching options provided by the Xperience API, see: [Caching in custom code](https://docs.kentico.com/13/configuring-xperience/configuring-caching/caching-in-custom-code.md)

> **Tip:** **Using the default pricing functionality**
>
> If you do not wish to completely replace the default Xperience pricing functionality, but only need to make minor adjustments, you can access and use the default implementations of related interfaces:
>
> 1. Add a constructor for your custom _IProductPricingService_ class with **ISKUPriceSourceFactory** and **IProductDiscountService** parameters.
> 2. Use the instances obtained from the parameters to get the default basic price and discounts.
