---
title: Coupon codes
related:
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions.md
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions/catalog-discounts.md
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions/order-discounts.md
  - https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions/free-shipping.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).

> **License:** Advanced license required.
>
> Features described on this page require the Xperience by Kentico **Advanced** license tier.

Coupon codes (also called discount codes or promo codes) allow you to require customers to enter a specific code to receive a promotion discount. This gives marketers control over when and how discounts are applied, enabling targeted campaigns and limited-time offers.

Coupon codes are not a separate type of discount. They are an **additional redemption requirement** that can be configured for any [catalog discount](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions/catalog-discounts.md) or [order discount](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions/order-discounts.md). When a promotion requires a coupon code, it is only applied if the customer provides the matching code during checkout. To learn how to create promotions, see [Add a coupon code to a promotion](https://docs.kentico.com/documentation/business-users/manage-commerce-stores.md#add-a-coupon-code-to-a-promotion).

> **Info:** **Multi-use discount codes**
>
> Coupon codes in Xperience are **generic, multi-use codes** that can be used by any customer, any number of times. They are only limited by the promotion's **Active from** and **Active to** dates. Once a promotion becomes active, any customer who knows the code can use it.
>
> The system does **not** support single-use, unique, or customer-targeted coupon codes. If you need one-time use codes or per-customer code limits, you must implement custom validation logic in your application.

> **Tip:** Discount codes are **case-insensitive**. Customers can enter _winter20_, _WINTER20_, or _Winter20_ and all will match a promotion configured with the code _WINTER20_.

## Pass coupon codes to price calculation

For coupon-based promotions to work, you must pass the customer's entered coupon codes to [price calculation](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/price-calculation/implementation.md). This is done through the `CouponCodes` property on the price calculation request.

If you do not set the `CouponCodes` property on your `PriceCalculationRequest`, promotions that require coupon codes are **never evaluated** and will not apply to the cart, even if the customer has entered valid codes. Storing coupon codes in your cart data model is not sufficient on its own.

> **Info:** Coupon code validation is one part of the overall promotion requirements evaluation. The system first checks [customer eligibility](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/promotions.md#customer-eligibility) and then validates coupon requirements. A promotion is only applied if the customer passes both checks and the promotion's corresponding `IsApplicable` implementation.

### Add coupon codes to the calculation request

When calling the [price calculation service](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/price-calculation/implementation.md#calculate-prices), include any coupon codes the customer has entered:

```csharp title="Pass coupon codes to price calculation"
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

// ...

/// <summary>
/// Calculates prices for a shopping cart with coupon codes applied.
/// </summary>
public async Task<PriceCalculationResult> CalculateWithCoupons(
    ShoppingCartDataModel cart,
    int memberId,
    CancellationToken cancellationToken)
{
    // Build the price calculation request
    PriceCalculationRequest calculationRequest = new PriceCalculationRequest
    {
        Items = cart.Items.Select(item => new PriceCalculationRequestItem
        {
            ProductIdentifier = item.ProductIdentifier,
            Quantity = item.Quantity
        }).ToList(),
        BuyerIdentifier = BuyerIdentifier.FromMemberId(memberId),
        LanguageName = "en",
        Mode = PriceCalculationMode.ShoppingCart,
        // Pass any coupon codes entered by the customer
        CouponCodes = cart.CouponCodes
    };

    // Calculate prices - promotions requiring matching coupon codes are applied
    PriceCalculationResult result =
        await priceCalculationService.Calculate(calculationRequest, cancellationToken);

    return result;
}
```

The price calculation pipeline automatically checks each promotion's coupon requirements. Promotions requiring a coupon code are only evaluated if a matching code is present in the `CouponCodes` collection.

### Access applied coupon codes from calculation results

You can access the actually applied coupon codes from the price calculation result through the `PromotionData` property:

```csharp title="Access coupon codes from calculation result"
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

// ...

/// <summary>
/// Demonstrates how to access applied coupon codes from the price calculation result.
/// Coupon codes are available in the PromotionData of the calculation result.
/// </summary>
public void AccessAppliedCouponCodes(PriceCalculationResult calculationResult)
{
    // Access coupon codes from catalog promotions (per-item discounts)
    foreach (var item in calculationResult.Items)
    {
        var appliedCatalogPromotion = item.PromotionData.CatalogPromotionCandidates
            .FirstOrDefault(candidate => candidate.Applied);

        if (appliedCatalogPromotion != null)
        {
            // CouponCode property contains the code that triggered the promotion,
            // or null if the promotion was applied automatically
            string? couponCode = appliedCatalogPromotion.CouponCode;
        }
    }

    // Access coupon codes from order promotions
    var appliedOrderPromotion = calculationResult.PromotionData.OrderPromotionCandidates
        .FirstOrDefault(candidate => candidate.Applied);

    if (appliedOrderPromotion != null)
    {
        // CouponCode property contains the code that triggered the promotion,
        // or null if the promotion was applied automatically
        string? couponCode = appliedOrderPromotion.CouponCode;
    }
}
```

The `CouponCode` property on applied promotion candidates contains the coupon code that triggered the promotion, or `null` if the promotion was applied automatically.

### Pass coupon codes to order creation

When creating an order, include the coupon codes so they are passed to price calculation during order creation:

```csharp title="Pass coupon codes to order creation"
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

// ...

/// <summary>
/// Creates order data with coupon codes for order creation.
/// </summary>
public OrderData CreateOrderDataWithCoupons(
    ShoppingCartDataModel cart,
    int memberId,
    string orderNumber,
    AddressDto billingAddress,
    int paymentMethodId,
    int shippingMethodId)
{
    OrderData orderData = new OrderData
    {
        BuyerIdentifier = BuyerIdentifier.FromMemberId(memberId),
        OrderNumber = orderNumber,
        LanguageName = "en",
        BillingAddress = billingAddress,
        PaymentMethodId = paymentMethodId,
        ShippingMethodId = shippingMethodId,
        // Include the coupon codes so they are passed to price calculation
        // during order creation and persisted with the order
        CouponCodes = cart.CouponCodes,
        OrderItems = cart.Items.Select(item => new OrderItem
        {
            ProductIdentifier = item.ProductIdentifier,
            Quantity = item.Quantity
        }).ToList()
    };

    return orderData;
}
```

## Implement coupon code entry in the shopping cart

To allow customers to enter coupon codes on your live site, you need to:

1. Store entered coupon codes in your shopping cart data model.
2. Provide a live site UI for entering and removing codes in your store.
3. Pass the codes to price calculation.

### Store coupon codes in the cart

Add a collection to store coupon codes in your [shopping cart](https://docs.kentico.com/documentation/developers-and-admins/digital-commerce-setup/checkout-process.md#manage-the-current-shopping-cart) data model:

```csharp title="Shopping cart data model with coupon codes"
/// <summary>
/// Coupon codes entered by the customer.
/// </summary>
public ICollection<string> CouponCodes { get; set; } = new List<string>();
```

### Handle coupon code actions

Create controller actions to add coupon codes:

```csharp title="Controller actions for coupon codes"
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc;

// ...

/// <summary>
/// Adds a coupon code to the shopping cart.
/// </summary>
[HttpPost]
public async Task<IActionResult> AddCoupon(string couponCode, CancellationToken cancellationToken = default)
{
    Console.WriteLine($"Applying {couponCode}");

    if (string.IsNullOrWhiteSpace(couponCode))
    {
        return RedirectToAction(nameof(Index));
    }

    // Calls a helper service to handle coupon operations
    await _shoppingCartService.AddCouponCode(couponCode.Trim(), cancellationToken);

    return RedirectToAction(nameof(Index));
}
```

```csharp title="ShoppingCartService.AddCouponCode"
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

using Kentico.Commerce.Web.Mvc;

// ...

private readonly ICurrentShoppingCartRetriever _shoppingCartRetriever;
private readonly ICurrentShoppingCartCreator _shoppingCartCreator;
private readonly ICurrentShoppingCartDiscardHandler _shoppingCartDiscardHandler;
private readonly ICurrentShoppingCartMemberSignInHandler _shoppingCartMemberSignInHandler;

public ShoppingCartService(
    ICurrentShoppingCartRetriever shoppingCartRetriever,
    ICurrentShoppingCartCreator shoppingCartCreator,
    ICurrentShoppingCartDiscardHandler shoppingCartDiscardHandler,
    ICurrentShoppingCartMemberSignInHandler shoppingCartMemberSignInHandler)
{
    _shoppingCartRetriever = shoppingCartRetriever;
    _shoppingCartCreator = shoppingCartCreator;
    _shoppingCartDiscardHandler = shoppingCartDiscardHandler;
    _shoppingCartMemberSignInHandler = shoppingCartMemberSignInHandler;
}

/// <summary>
/// Adds a coupon code to the shopping cart.
/// </summary>
public async Task AddCouponCode(string couponCode, CancellationToken cancellationToken = default)
{
    var cart = await GetOrCreateCart(cancellationToken);
    var cartData = cart.GetShoppingCartDataModel();

    // Check if the code is already applied (case-insensitive)
    if (!cartData.CouponCodes.Contains(couponCode, StringComparer.OrdinalIgnoreCase))
    {
        cartData.CouponCodes.Add(couponCode);
        cart.StoreShoppingCartDataModel(cartData);
        cart.Update();
    }
}

/// <summary>
/// Gets the current shopping cart or creates a new one if it doesn't exist
/// using <see cref="ICurrentShoppingCartRetriever"/> and <see cref="ICurrentShoppingCartCreator"/>.
/// </summary>
public async Task<ShoppingCartInfo> GetOrCreateCart(CancellationToken cancellationToken = default)
{
    var cart = await _shoppingCartRetriever.Get(cancellationToken);
    cart ??= await _shoppingCartCreator.Create(cancellationToken);
    return cart;
}
```

And to remove coupon codes:

```csharp title="Controller actions for coupon codes"
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Mvc;

// ...

/// <summary>
/// Removes a coupon code from the shopping cart.
/// </summary>
[HttpPost]
public async Task<IActionResult> RemoveCoupon(string couponCode, CancellationToken cancellationToken = default)
{
    if (string.IsNullOrWhiteSpace(couponCode))
    {
        return RedirectToAction(nameof(Index));
    }

    // Calls a helper service to handle coupon operations
    await _shoppingCartService.RemoveCouponCode(couponCode.Trim(), cancellationToken);

    return RedirectToAction(nameof(Index));
}
```

```csharp title="ShoppingCartService.RemoveCouponCode"
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using CMS.Commerce;

using Kentico.Commerce.Web.Mvc;

// ...

private readonly ICurrentShoppingCartRetriever _shoppingCartRetriever;
private readonly ICurrentShoppingCartCreator _shoppingCartCreator;
private readonly ICurrentShoppingCartDiscardHandler _shoppingCartDiscardHandler;
private readonly ICurrentShoppingCartMemberSignInHandler _shoppingCartMemberSignInHandler;

public ShoppingCartService(
    ICurrentShoppingCartRetriever shoppingCartRetriever,
    ICurrentShoppingCartCreator shoppingCartCreator,
    ICurrentShoppingCartDiscardHandler shoppingCartDiscardHandler,
    ICurrentShoppingCartMemberSignInHandler shoppingCartMemberSignInHandler)
{
    _shoppingCartRetriever = shoppingCartRetriever;
    _shoppingCartCreator = shoppingCartCreator;
    _shoppingCartDiscardHandler = shoppingCartDiscardHandler;
    _shoppingCartMemberSignInHandler = shoppingCartMemberSignInHandler;
}

/// <summary>
/// Removes a coupon code from the shopping cart.
/// Implementation-dependent based on the site's shopping cart model.
/// </summary>
public async Task RemoveCouponCode(string couponCode, CancellationToken cancellationToken = default)
{
    var cart = await _shoppingCartRetriever.Get(cancellationToken);
    if (cart == null)
    {
        return;
    }

    var cartData = cart.GetShoppingCartDataModel();

    // Find and remove the coupon code
    var codeToRemove = cartData.CouponCodes
        .FirstOrDefault(c => string.Equals(c, couponCode, StringComparison.OrdinalIgnoreCase));

    // Updates the cart in the database
    if (codeToRemove != null)
    {
        cartData.CouponCodes.Remove(codeToRemove);
        cart.StoreShoppingCartDataModel(cartData);
        cart.Update();
    }
}
```

### Display coupon code UI

Add a form for entering coupon codes and display applied codes. A basic interface handling coupon actions should include:

- A form with a text input field and a submit button where customers can enter a discount code.
- A list displaying all currently applied coupon codes from the shopping cart.
- An option to remove applied coupons.
