Only this pageAll pages
Powered by GitBook
1 of 53

10.latest (LTS)

Loading...

Loading...

Commerce Products

Installation

Loading...

Loading...

Upgrading

Loading...

Loading...

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

How-To Guides

Loading...

Loading...

Loading...

Loading...

Key Concepts

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Tutorials

Loading...

Reference

Loading...

Upgrading Umbraco Commerce

Before upgrading, it is always advisable to take a complete backup of your site and database.

Get the latest version of Umbraco Commerce

To upgrade to the latest version of Umbraco Commerce you can use:

  • NuGet

  • Visual Studio

NuGet

  • NuGet installs the latest version of the package when you use the dotnet add package Umbraco.Commerce command unless you specify a package version: dotnet add package Umbraco.Commerce --version <VERSION>

  • After you have added a package reference to your project by executing the dotnet add package Umbraco.Commerce command in the directory that contains your project file, run dotnet restore to install the package.

Visual Studio

  1. Go to Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution... in Visual Studio, to upgrade Umbraco Commerce:

  2. Select Umbraco.Commerce.

  3. Select the latest version from the Version drop-down and click Install.

  4. When the command completes, open the .csproj file to make sure the package reference is updated:

<ItemGroup>
  <PackageReference Include="Umbraco.Commerce" Version="xx.x.x" />
</ItemGroup>

If you are using one or more of the below sub-packages, they also need to be upgraded as well:

Sub-package
Description

Umbraco.Commerce.Common

A shared project of common, non-Commerce-specific patterns and helpers.

Umbraco.Commerce.Core

Core Commerce functionality that doesn't require any infrastructure-specific dependencies.

Umbraco.Commerce.Infrastructure

Infrastructure-specific project containing implementations of core Commerce functionality.

Umbraco.Commerce.Persistence.SqlServer

Persistence-specific project containing implementations of core Commerce persistence functionality for SQL Server.

Umbraco.Commerce.Persistence.Sqllite

Persistence-specific project containing implementations of core Commerce persistence functionality for SQLite.

Umbraco.Commerce.Web

Core Commerce functionality that requires a web context.

Umbraco.Commerce.Cms

Core Commerce functionality that requires an Umbraco dependency.

Umbraco.Commerce.Cms.Web

The Commerce functionality for the Umbraco presentation layer.

Umbraco.Commerce.Cms.Web.UI

The static Commerce assets for the Umbraco presentation layer.

Umbraco.Commerce.Cms.Startup

The Commerce functionality for registering Commerce with Umbraco.

Umbraco.Commerce

The main Commerce package entry point package.

Umbraco Commerce Documentation

Browse the Umbraco Commerce documentation to learn more about the addon and how to use it.

Umbraco Commerce is the official Umbraco e-commerce addon for your Umbraco CMS website. It can be used to set up small webshops, while it can also be implemented for big-scale e-commerce solutions spanning multiple countries.

Extend Umbraco Commerce

Quick links

Are you looking for Vendr documentation?

The articles and topics covered on this documentation site are for Umbraco Commerce.

Using These Docs

If you require assistance you can use our support channels to seek assistance.

This article shows how to manually upgrade Umbraco Commerce to run the latest version. When upgrading Umbraco Commerce, be sure to also consult the notes to learn about potential breaking changes and common pitfalls.

Documentation for Vendr is located on the .

These docs are aimed at developers who have at least a basic understanding of , as well as C# and MVC principals.

version specific upgrade
Go behind the scenes
Vendr Documentation site
Umbraco

Choose between a selection of verified packages to extend the Umbraco Commerce implementation for your website.

You can integrate your Umbraco Commerce implementation with a series of different payment providers.

You can integrate your Umbraco Commerce implementation with a series of different shipping providers.

Umbraco Commerce Packages
Umbraco Commerce Payment Providers
Umbraco Commerce Shipping Providers

Migrate Umbraco Commerce Checkout

Detailed steps on how to migrate the Checkout package from Vendr to Umbraco Commerce.

Throughout the following steps, we will migrate the Checkout package from Vendr to Umbraco Commerce.

  1. Make a backup of any custom templates and Vendr UI configuration files.

  2. Make a note of all configuration values on the Vendr.Checkout Checkout node.

  3. Delete Vendr.Checkout generated checkout nodes.

    • Checkout

      • Customer Information

      • Shipping Method

      • Payment Method

      • Review Order

      • Process Payment

      • Order Confirmation

  4. Delete all Vendr.Checkout generated Document Types.

    • [Vendr Checkout] Page

      • [Vendr Checkout] Checkout Page

      • [Vendr Checkout] Checkout Step Page

  5. Delete all Vendr.Checkout generated Data Types.

    • [Vendr Checkout] Step Picker

    • [Vendr Checkout] Theme Color Picker

  6. Uninstall the Vendr.Checkout Nuget package:

dotnet remove package Vendr.Checkout
  1. Delete any remaining files and folders in the ~/App_Plugins/VendrCheckout directory.

  2. Install the Umbraco.Commerce.Checkout package:

dotnet add package Umbraco.Commerce.Checkout
  1. Locate the Umbraco Commerce Checkout dashboard in the Settings section

  2. Click the "Install" button to reinstall the Checkout components in the previous location.

  3. Copy any custom configuration files back into the solution.

  4. Copy any custom Views into the ~/Views/UmbracoCommerceCheckout/ folder.

Cover

Find all the information you need to get started using Umbraco Commerce with your Umbraco CMS implementation.

Cover

Looking to configure and implement something specific? Take a look through the How-to section where you might find a guide that fits your needs.

Cover

Looking to learn more about the different concepts and features of Umbraco Commerce? You can find detailed information about each of them in this section.

Introduction

Getting Started with Umbraco Commerce.

In this section, you will find information about the key steps necessary to get you started with Umbraco Commerce.

It is assumed that you have an Umbraco 10+ website configured, and ready to install Umbraco Commerce.

System Requirements

The minimum requirements for using Umbraco Commerce are as follows:

  • Umbraco CMS version 10+

  • SQL Server 2015+ Database

Versioning

Umbraco Configuration

Configuring Umbraco for Umbraco Commerce.

Before you can start to use Umbraco Commerce, you need to configure Umbraco to allow access to the relevant sections. The Umbraco Commerce UI is split over three sections within the Umbraco backoffice:

  • Settings for managing the different store settings.

  • Commerce for managing store-related content (orders, discounts, etc).

  • Content for managing the Umbraco Commerce products.

In order to access these sections, you will need to ensure a User account with the relevant permissions to do so. When logged in as Administrator, access to the Settings and Content sections is already granted.

To gain access to the Commerce section additional configuration is needed.

Creating a Commerce User Group

To gain access to the Commerce section, it is advised to create a new User Group called Commerce.

  1. Navigate to the User section of the Umbraco backoffice.

  2. Open the User Groups page.

  3. Create a new User Group called Commerce.

  4. Assign the relevant User accounts to that User Group.

  5. Allow that User Group access to the Commerce section as a whole.

Creating a custom User Group provides a way of managing Users who have access to the Commerce section, rather than allowing individual Users access.

Accessing the Commerce Section

Once created and assigned, you should be able to refresh the backoffice and see that we now have access to the new Commerce section.

Find detailed instructions on how to install the latest version of Umbraco in the .

SQLite is fine for testing, but not recommended for live deployments. See for more details.

This is an add-on product to Umbraco CMS. Umbraco Commerce follows the .

Learn more about .

Umbraco CMS documentation
Configuring SQLite support
versioning strategy laid out for Umbraco CMS
Getting Started
How-to Guides
Key Concepts
Users and User Groups in the Umbraco CMS Documentation

Installing Umbraco Commerce

Learn the steps needed in order to install Umbraco Commerce into your Umbraco CMS website.

Learn how to install Umbraco Commerce into your Umbraco CMS implementation.

You can also find information about how to upgrade and how to install and activate your Umbraco Commerce license.

NuGet Package Installation

To install Umbraco Commerce via NuGet you can run the following command directly in the NuGet Manager Console window:

PM> dotnet add package Umbraco.Commerce

Alternatively, you can also find and install the NuGet package via the NuGet Package Manager in Visual Studio. You will see a number of packages available, however, you will want to install the main Umbraco Commerce package.

For most sites using a single solution, the above will be all you need to install Umbraco Commerce into your project. When you have a more complex solution structure consisting of multiple projects, Umbraco Commerce is available in multiple sub-packages with varying dependencies.

Sub-package
Description

Umbraco.Commerce.Common

A shared project of common, non-Commerce-specific patterns and helpers.

Umbraco.Commerce.Core

Core Commerce functionality that doesn't require any infrastructure-specific dependencies.

Umbraco.Commerce.Infrastructure

Infrastructure-specific project containing implementations of core Commerce functionality.

Umbraco.Commerce.Persistence.SqlServer

Persistence-specific project containing implementations of core Commerce persistence functionality for SQL Server.

Umbraco.Commerce.Persistence.Sqllite

Persistence-specific project containing implementations of core Commerce persistence functionality for SQLite.

Umbraco.Commerce.Web

Core Commerce functionality that requires a web context.

Umbraco.Commerce.Cms

Core Commerce functionality that requires an Umbraco dependency.

Umbraco.Commerce.Cms.Web

The Commerce functionality for the Umbraco presentation layer.

Umbraco.Commerce.Cms.Web.UI

The static Commerce assets for the Umbraco presentation layer.

Umbraco.Commerce.Cms.Startup

The Commerce functionality for registering Commerce with Umbraco.

Umbraco.Commerce

The main Commerce package entry point package.

Installing a License

Release Notes

Get an overview of the things changed and fixed in each version of Umbraco Commerce.

If there are any breaking changes or other issues to be aware of when upgrading they are also noted here.

Release History

This section contains the release notes for Umbraco Commerce 10 including all changes for this version.

  • Fixed issue where order lines with a zero value would cause a concurrency exception due to the fact their prices aren't frozen but the order recalculation process was attempting to refreeze them.

  • Added a WithUmbracoBuilder extension for IUmbracoCommerceBuilder to allow access to the Umbraco configuration within an Umbraco Commerce registration.

10.0.7 (February 15th 2024)

10.0.6 (February 6th 2024)

  • Added licensing fallback to use any previously validated license within the last 7 days

  • Updated Umbraco.Licenses version dependency to the latest

  • Made WithUmbracoCommerceBuilder extension public to allow accessing the IUmbracoCommerceBuilder instance outside of the AddUmbracoCommerce call

  • Fixed Cross-site scripting (XSS) issue in email/print templates.

  • Fixed localization issue where -1 in querystrings would get incorrectly formatted. Root ids are now formatted with an invarient culture.

  • Allow overriding of SameSite/Path for Umbraco Commerce cookies.

  • Updated productSource resolution to check for both IPublishedContent and IEnumerable<IPublishedContent> as it depends on the picker used and what its return type is.

  • Updated default order number template from CART-{0} to ORDER-{0}.

  • Updated product adapter to resolve product details correctly from child node variants.

Legacy release notes

Version Specific Upgrade Notes

Version specific documentation for upgrading to new major versions of Umbraco Commerce.

This page covers specific upgrade documentation for when migrating to major 10 of Umbraco Commerce.

Version Specific Upgrade Notes History

Version 10 is the initial Long-term support (LTS) release of the Umbraco Commerce product. It contains a number of breaking changes from the previous, Vendr product.

Legacy version specific upgrade notes

Licensing

Umbraco Commerce is a commercial product. You can run an Umbraco Commerce unrestricted locally without the need a license. Running Umbraco Commerce on a public domain will display a warning banner in the backoffice and will limit the maximum number of orders (20). To remove these restrictions, you'll need to have a valid license.

How does it work?

Licenses are sold per backoffice domain and will also work on all subdomains. If you have alternative staging/qa environment domains, additional domains can be added to the license on request.

The licenses are not bound to a specific product version. They will work for all versions of the related product.

Let's say that you have a license configured for your domain, mysite.com, and you've requested two development domains, devdomain.com and devdomain2.com.

The license will cover the following domains:

  • localhost

  • *.local

  • *.mysite.com

  • www.mysite.com

  • devdomain.com

  • www.devdomain.com

  • devdomain2.com

  • www.devdomain2.com

You can have only 1 license per Umbraco installation.

What does a license cover?

There are a few differences as to what the licenses cover:

  • A single license covers the installation of Umbraco Commerce in 1 production backoffice domain, as well as in any requested development domains.

  • The production domain includes all subdomains (e.g. *.mysite.com).

  • The development domains work with or without the www subdomain.

  • The license allows for an unlimited number of orders.

  • The license also includes localhost and *.local as a valid domain.

This is an add-on domain for existing licenses. Refunds will not be given for this product.

Configuring your license

Add additional domains

Installing your license

Once you have received your license code it needs to be installed on your site.

  1. Open the root directory for your project files.

  2. Locate and open the appSettings.json file.

  3. Add your Umbraco Commerce license key to Umbraco:Licenses:Umbraco.Commerce:

You might run into issues when using a period in the product name when using environment variables. Use an underscore in the product name instead, to avoid problems.

Verify the license installation

You can verify that your license is successfully installed by logging into your project's backoffice and navigating to the settings section. Here you will see a licenses dashboard which should display the status of your license.

When and how to configure an UmbracoApplicationUrl

If you are running on a single domain for both your frontend and backend environments, it's not necessary to configure a UmbracoApplicationUrl.

If you have different domains for your frontend and backend, then it's advised that you configure an UmbracoApplicationUrl set to your backoffice URL. This helps the licensing engine know which URL should be used for validation checks. Without this configuration setting, the licensing engine will try and work out the domain to validate from the HTTP request object. This can lead to errors when switching between domains.

An UmbracoApplicationUrl can be configured in your appSettings.json file like so:

Configuring UmbracoApplicationUrl on Umbraco Cloud

If you are hosting on Umbraco Cloud you will find the configuration described above won't be reflected in your environment. The reason for this is that Umbraco Cloud sets this value as an environment variable set to the Cloud project domain (<your project>.umbraco.io). This overrides what is set via the appSettings.json file.

There are two options in this case:

  • Either the domains for each of your Cloud environments can be added to your license.

  • Or, for more control and to ensure this value is set correctly for other reasons, you can apply the configuration via code.

For example, in your Startup.cs file, you can add the following to the ConfigureServices method:

In practice, you will probably want to make this a bit more sophisticated. You can read the value from another configuration key, removing the need to hard-code it and have it set as appropriate in different environments. You can also move this code into a composer or an extension method if you prefer not to clutter up the Startup.ConfigureServices method.

Validating a license without an outgoing Internet connection

Some Umbraco installations will have a highly locked down production environment, with firewall rules that prevent outgoing HTTP requests. This will interfere with the normal process of license validation.

On start-up, and periodically whilst Umbraco is running, the license component used by Umbraco Commerce will make an HTTP POST request to https://license-validation.umbraco.com/api/ValidateLicense.

If it's possible to do so, the firewall rules should be adjusted to allow this request.

If such a change is not feasible, there is another approach you can use.

You will need to have a server, or serverless function, that is running and can make a request to the online license validation service. That needs to run on a daily schedule, making a request and relaying it onto the restricted Umbraco environment.

To set this up, firstly ensure you have a reference to Umbraco.Licenses version 10.1 or higher. If the version of Commerce you are using depends on an earlier version, you can add a direct package reference for Umbraco.Licenses.

Then configure a random string as an authorization key in configuration. This is used as protection to ensure only valid requests are handled. You can also disable the normal regular license checks - as there is no point in these running if they will be blocked:

Your Internet enabled server should make a request of the following form to the online license validation service:

The response should be relayed exactly via an HTTP request to your restricted Umbraco environment:

A header with a key of X-AUTH-KEY and value of the authorization key you have configured should be provided.

This will trigger the same processes that occur when the normal scheduled validation completes ensuring your product is considered licensed.

Umbraco Commerce is available via .

See the for details on how to install a license.

In this section, we have summarized the changes to Umbraco Commerce released in each version. Each version is presented with a link to the showing a list of issues resolved in the release. We also link to the individual issues themselves from the detail.

If you are upgrading to a new major version, check the breaking changes in the article.

(July 11th 2024)

Fixed issue with stock synchronizer prematurely looking up a store .

(July 3rd 2024)

Added pessimistic locking to the payment provider callback endpoint to prevent concurrency issues if the endpoint is called too many times at once .

Fixed percentage discounts not taking the stores rounding method into account during calculation .

Updated Order properties to trim whitespace around values to prevent unexpected behavior .

(April 23rd 2024)

Fixed error in SearchOrder when searching with date ranges .

(April 8th 2024)

Fixed properties set in the background from an entity action lost when resaving the entity from the UI after the action (wasn't fully fixed in 10.0.9) .

Added support for localhost sub domains in dev license .

(March 27th 2024)

Fixed properties set in the background from an entity action lost when resaving the entity from the UI after the action .

Fixed unable to override cart editor view like you can the order editor view .

(March 3rd 2024)

Fixed issue with date range order searches not working correctly .

Fixed error in SafeLazy not taking null into account and so causing errors when an entity cache entry is evicted .

Incremented Newtonsoft.Json version dependency to 13.0.1 as 13.0.0 doesn't exist on NuGet anymore .

(December 13th 2023)

(November 1st 2023)

Use Microsoft.Data.SqlClient for migrations to support Azure connection strings .

Move system config files to system.{}.config.json to allow overriding as per the docs .

Updated order/cart editor config to allow template option for custom property rendering .

(October 18th 2023)

Fixed UI spelling mistakes as documented in issue .

Fixed issue where adding a product with a uniqueness property, and then adding the same product without a uniqueness property would replace the initial orderline, rather than adding a new one

(September 13th 2023)

(August 15th 2023)

(July 5th 2023)

.

You can find the release notes for Vendr in the .

If you are upgrading to a new minor or patch version, you can find information about the breaking changes in the article.

See the for full details.

You can find the version specific upgrade notes for versions out of support in the .

If you have multiple backoffice domains pointing at the same installation, you have the option to purchase and to your license.

You can look at the pricing, features, and purchase a license on the page. A member of the sales team will manage this process. You will need to provide all domains you wish to have covered by the license such as primary and development/staging/QA domains. You should then receive a license code to be installed in your solution.

If you require to add addition domains to the license, with your request and they will manage this process.

See the documentation for more details about this setting.

NuGet.Org
Commerce issue tracker
Version Specific Upgrade Notes
10.0.13
#536
10.0.12
#533
#506
#528
10.0.11
#496
10.0.10
#472
#493
10.0.9
#472
#474
10.0.8
#468
#466
#451
10.0.5
10.0.4
#443
#448
#446
10.0.3
#427
#438
10.0.2
10.0.1
10.0.0
Initial product launch
Change log file on GitHub
"Umbraco": {
  "Licenses": {
    "Umbraco.Commerce": "YOUR_LICENSE_KEY"
  }
}
"Umbraco_Commerce": "YOUR_LICENSE_KEY"
{
    "Umbraco": {
        "CMS": {
            "WebRouting": {
                "UmbracoApplicationUrl": "https://admin.my-custom-domain.com/"
            }
        }
    }
}
services.Configure<WebRoutingSettings>(o => o.UmbracoApplicationUrl = "<your application URL>");
  "Umbraco": {
    "Licenses": {
      "Umbraco.Commerce": "<your license key>"
    },
    "LicensesOptions": {
      "EnableScheduledValidation": false,
      "ValidatedLicenseRelayAuthKey": "<your authorization key>"
    }
POST https://license-validation.umbraco.com/api/ValidateLicense
{
    "ProductId": "Umbraco.Commerce",
    "LicenseKey": "<your license key>",
    "Domain": "<your licensed domain>"
}
POST http://<your umbraco environment>/umbraco/licenses/validatedLicense/relay?productId=<product id>&licenseKey=<license key>
Licensing page

Migrate custom Payment Providers

Follow the steps outlined below to migrate your custom payment providers to Umbraco Commerce.

Throughout the following steps, we will migrate custom payment providers used for Umbraco Commerce into Umbraco Commerce.

  1. Remove any installed Umbraco Commerce packages

dotnet remove package Umbraco.Commerce.Core
  1. Install the Umbraco.Commerce packages for the payment providers.

dotnet add package Umbraco.Commerce.Core
  1. Update any namespace references.

  2. Update project framework to net7.0.

The final step in the migration is to update all abstract async methods exposed by the base class. It needs to be updated to accept an additional CancellationToken cancellationToken = default parameter as the final method argument. Your Integrated Development Environment (IDE) should provide feedback on all the methods that have been updated.

Limit Order Line Quantity

How-To Guide to limit order line quantity in Umbraco Commerce.

In this guide, we will be looking at Validation events in Umbraco Commerce. These enabled you to limit order line quantity based on:

  • The existing stock value of the product, and

  • The existing quantity of the product in the cart.

ProductAddValidationHandler

When adding a product to the cart we need to verify that the product is in stock. We also need to verify that the customer does not already have the remaining quantities in the cart.

public class ProductAddValidationHandler : ValidationEventHandlerBase<ValidateOrderProductAdd>
{
    private readonly IProductService _productService;

    public ProductAddValidationHandler(IProductService productService)
    {
        _productService = productService;
    }

    public override void Validate(ValidateOrderProductAdd evt)
    {
        var order = evt.Order;
        var productReference = evt.ProductReference;

        var stock = _productService.GetProductStock(productReference);

        var totalQuantities = order?.OrderLines.Where(x => x.ProductReference == productReference).Sum(x => x.Quantity) ?? 0;

        if (stock.HasValue && totalQuantities >= stock.Value)
            evt.Fail($"Only {stock} quantities can be purchased for {productReference}.");
    }
}

OrderLineQuantityValidationHandler

When changing the order line quantity on the cart page, we need to ensure that the quantities being changed are in stock.

public class OrderLineQuantityValidationHandler : ValidationEventHandlerBase<ValidateOrderLineQuantityChange>
{
    private readonly IProductService _productService;

    public OrderLineQuantityValidationHandler(IProductService productService)
    {
        _productService = productService;
    }

    public override void Validate(ValidateOrderLineQuantityChange evt)
    {
        var orderLine = evt.OrderLine;
        var productReference = orderLine.ProductReference;

        var stock = _productService.GetProductStock(productReference);

        if (stock.HasValue && evt.Quantity.To > stock.Value)
            evt.Fail($"Only {stock} quantities can be purchased for {productReference}.");
    }
}

Register event handlers

Finally, we need to register the Umbraco Commerce event handlers via an IUmbracoCommerceBuilder extension.

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddEventHandlers(IUmbracoCommerceBuilder builder)
    {
        // Register event handlers
        builder.WithValidationEvent<ValidateOrderProductAdd>()
            .RegisterHandler<ProductAddValidationHandler>();

        builder.WithValidationEvent<ValidateOrderLineQuantityChange>()
            .RegisterHandler<OrderLineQuantityValidationHandler>();

        return builder;
    }
}
Release Notes
Migrate from Vendr to Umbraco Commerce guide
Legacy documentation on GitHub
Umbraco Commerce
reach out the sales team
Fixed Application URL
add additional domains

Migrate from Vendr to Umbraco Commerce

Learn how to migrate a Vendr solution to Umbraco Commerce.

This guide provides a step-by-step approach to migrating a default Vendr solution to Umbraco Commerce.

Upgrade to the latest version of Vendr before continuing with the migration.

Key changes

Before outlining the exact steps, there are a few key changes to be aware of.

These changes will dictate the steps to take in the process of migrating to Umbraco Commerce.

Project, Package, and Namespace changes

Vendr
Umbraco Commerce

Vendr.Common

Umbraco.Commerce.Common

Vendr.Core

Umbraco.Commerce.Core

Vendr.Infrastructure

Umbraco.Commerce.Infrastructure

Vendr.Persistence

Umbraco.Commerce.Persistence

Vendr.Persistence.Sqlite

Umbraco.Commerce.Persistence.Sqlite

Vendr.Persistence.SqlServer

Umbraco.Commerce.Persistence.SqlServer

Vendr.Umbraco

Umbraco.Commerce.Cms

Vendr.Umbraco.Web

Umbraco.Commerce.Cms.Web

Vendr.Web

Umbraco.Commerce.Web

Vendr.Web.UI

Umbraco.Commerce.Web.StaticAssets

Vendr.Umbraco.Startup

Umbraco.Commerce.Cms.Startup

Vendr

Umbraco.Commerce

C# Class changes
  • Namespace changes as documented above.

  • All classes containing the Vendr keyword are now updated to UmbracoCommerce.

    • Examples: IVendrApi is now IUmbracoCommerceApi and AddVendr() is now AddUmbracoCommerce().

JavaScript changes
  • All vendr modules have changed to umbraco.commerce modules.

  • All vendr prefixed directives, services, and resources are now prefixed with uc.

  • All vendr prefixed events now follow this format: Umbraco.Commerce.{target}.{action}.

UI Changes
  • All static UI assets are served via a Razor Compiled Library (RCL) and are no longer found in the App_Plugins folder.

  • The folder within App_Plugins still exists for the static assets that are user configurable.

  • The folder with App_Plugins has been renamed from Vendr to UmbracoCommerce.

  • UI Config files have changed from .js files to .json.

Step 1: Replace dependencies

In this first step, we will be replacing all existing Vendr dependencies with Umbraco Commerce dependencies.

  1. Remove any installed Vendr packages (including Payment Providers):

dotnet remove package Vendr
  1. Take a backup of any Vendr-specific templates and config files you will want to reuse:

  2. Delete the Vendr App_Plugins folder:

rmdir App_Plugins\Vendr
  1. Install Umbraco.Commerce:

dotnet add package Umbraco.Commerce
  1. Install Umbraco Commerce packages including any payment providers previously removed.

  2. Reapply any backed-up config files in their new App_Plugins location.

  3. Move any backed-up templates into the ~/Views/UmbracoCommerce/Templates/ folder.

  4. Rename any config files with a .json file extension rather than .

  5. Compile your project against .NET 7.0.

Step 2: Update namespaces and entity names

Step 3: Update the database

In this step, we will cover updating the database for Umbraco Commerce.

  1. Backup your database

  2. Rename database tables using the following query:

sp_rename vendrCurrency, umbracoCommerceCurrency;
sp_rename vendrTaxClass, umbracoCommerceTaxClass;
sp_rename vendrStock, umbracoCommerceStock;
sp_rename vendrOrderStatus, umbracoCommerceOrderStatus;
sp_rename vendrEmailTemplate, umbracoCommerceEmailTemplate;
sp_rename vendrPaymentMethod, umbracoCommercePaymentMethod;
sp_rename vendrShippingMethod, umbracoCommerceShippingMethod;
sp_rename vendrCountry, umbracoCommerceCountry;
sp_rename vendrRegion, umbracoCommerceRegion;
sp_rename vendrCurrencyAllowedCountry, umbracoCommerceCurrencyAllowedCountry;
sp_rename vendrPaymentMethodAllowedCountryRegion, umbracoCommercePaymentMethodAllowedCountryRegion;
sp_rename vendrPaymentMethodCountryRegionPrice, umbracoCommercePaymentMethodCountryRegionPrice;
sp_rename vendrPaymentMethodPaymentProviderSetting, umbracoCommercePaymentMethodPaymentProviderSetting;
sp_rename vendrShippingMethodAllowedCountryRegion, umbracoCommerceShippingMethodAllowedCountryRegion;
sp_rename vendrShippingMethodCountryRegionPrice, umbracoCommerceShippingMethodCountryRegionPrice;
sp_rename vendrTaxClassCountryRegionTaxRate, umbracoCommerceTaxClassCountryRegionTaxRate;
sp_rename vendrDiscount, umbracoCommerceDiscount;
sp_rename vendrDiscountCode, umbracoCommerceDiscountCode;
sp_rename vendrOrder, umbracoCommerceOrder;
sp_rename vendrOrderProperty, umbracoCommerceOrderProperty;
sp_rename vendrOrderLine, umbracoCommerceOrderLine;
sp_rename vendrOrderLineProperty, umbracoCommerceOrderLineProperty;
sp_rename vendrGiftCard, umbracoCommerceGiftCard;
sp_rename vendrOrderAppliedDiscountCode, umbracoCommerceOrderAppliedDiscountCode;
sp_rename vendrOrderAppliedGiftCard, umbracoCommerceOrderAppliedGiftCard;
sp_rename vendrStoreAllowedUserRole, umbracoCommerceStoreAllowedUserRole;
sp_rename vendrStoreAllowedUser, umbracoCommerceStoreAllowedUser;
sp_rename vendrFrozenPrice, umbracoCommerceFrozenPrice;
sp_rename vendrGiftCardProperty, umbracoCommerceGiftCardProperty;
sp_rename vendrActivityLog, umbracoCommerceActivityLog;
sp_rename vendrOrderPriceAdjustment, umbracoCommerceOrderPriceAdjustment;
sp_rename vendrOrderAmountAdjustment, umbracoCommerceOrderAmountAdjustment;
sp_rename vendrProductAttribute, umbracoCommerceProductAttribute;
sp_rename vendrProductAttributeValue, umbracoCommerceProductAttributeValue;
sp_rename vendrTranslatedValue, umbracoCommerceTranslatedValue;
sp_rename vendrProductAttributePreset, umbracoCommerceProductAttributePreset;
sp_rename vendrProductAttributePresetAllowedAttribute, umbracoCommerceProductAttributePresetAllowedAttribute;
sp_rename vendrOrderLineAttribute, umbracoCommerceOrderLineAttribute;
sp_rename vendrPrintTemplate, umbracoCommercePrintTemplate;
sp_rename vendrExportTemplate, umbracoCommerceExportTemplate;
sp_rename vendrStoreEntityTag, umbracoCommerceStoreEntityTag;
sp_rename vendrMigrations, umbracoCommerceMigrations;
sp_rename vendrStore, umbracoCommerceStore;
ALTER TABLE vendrCurrency RENAME TO umbracoCommerceCurrency;
ALTER TABLE vendrTaxClass RENAME TO umbracoCommerceTaxClass;
ALTER TABLE vendrStock RENAME TO umbracoCommerceStock;
ALTER TABLE vendrOrderStatus RENAME TO umbracoCommerceOrderStatus;
ALTER TABLE vendrEmailTemplate RENAME TO umbracoCommerceEmailTemplate;
ALTER TABLE vendrPaymentMethod RENAME TO umbracoCommercePaymentMethod;
ALTER TABLE vendrShippingMethod RENAME TO umbracoCommerceShippingMethod;
ALTER TABLE vendrCountry RENAME TO umbracoCommerceCountry;
ALTER TABLE vendrRegion RENAME TO umbracoCommerceRegion;
ALTER TABLE vendrCurrencyAllowedCountry RENAME TO umbracoCommerceCurrencyAllowedCountry;
ALTER TABLE vendrPaymentMethodAllowedCountryRegion RENAME TO umbracoCommercePaymentMethodAllowedCountryRegion;
ALTER TABLE vendrPaymentMethodCountryRegionPrice RENAME TO umbracoCommercePaymentMethodCountryRegionPrice;
ALTER TABLE vendrPaymentMethodPaymentProviderSetting RENAME TO umbracoCommercePaymentMethodPaymentProviderSetting;
ALTER TABLE vendrShippingMethodAllowedCountryRegion RENAME TO umbracoCommerceShippingMethodAllowedCountryRegion;
ALTER TABLE vendrShippingMethodCountryRegionPrice RENAME TO umbracoCommerceShippingMethodCountryRegionPrice;
ALTER TABLE vendrTaxClassCountryRegionTaxRate RENAME TO umbracoCommerceTaxClassCountryRegionTaxRate;
ALTER TABLE vendrDiscount RENAME TO umbracoCommerceDiscount;
ALTER TABLE vendrDiscountCode RENAME TO umbracoCommerceDiscountCode;
ALTER TABLE vendrOrder RENAME TO umbracoCommerceOrder;
ALTER TABLE vendrOrderProperty RENAME TO umbracoCommerceOrderProperty;
ALTER TABLE vendrOrderLine RENAME TO umbracoCommerceOrderLine;
ALTER TABLE vendrOrderLineProperty RENAME TO umbracoCommerceOrderLineProperty;
ALTER TABLE vendrGiftCard RENAME TO umbracoCommerceGiftCard;
ALTER TABLE vendrOrderAppliedDiscountCode RENAME TO umbracoCommerceOrderAppliedDiscountCode;
ALTER TABLE vendrOrderAppliedGiftCard RENAME TO umbracoCommerceOrderAppliedGiftCard;
ALTER TABLE vendrStoreAllowedUserRole RENAME TO umbracoCommerceStoreAllowedUserRole;
ALTER TABLE vendrStoreAllowedUser RENAME TO umbracoCommerceStoreAllowedUser;
ALTER TABLE vendrFrozenPrice RENAME TO umbracoCommerceFrozenPrice;
ALTER TABLE vendrGiftCardProperty RENAME TO umbracoCommerceGiftCardProperty;
ALTER TABLE vendrActivityLog RENAME TO umbracoCommerceActivityLog;
ALTER TABLE vendrOrderPriceAdjustment RENAME TO umbracoCommerceOrderPriceAdjustment;
ALTER TABLE vendrOrderAmountAdjustment RENAME TO umbracoCommerceOrderAmountAdjustment;
ALTER TABLE vendrProductAttribute RENAME TO umbracoCommerceProductAttribute;
ALTER TABLE vendrProductAttributeValue RENAME TO umbracoCommerceProductAttributeValue;
ALTER TABLE vendrTranslatedValue RENAME TO umbracoCommerceTranslatedValue;
ALTER TABLE vendrProductAttributePreset RENAME TO umbracoCommerceProductAttributePreset;
ALTER TABLE vendrProductAttributePresetAllowedAttribute RENAME TO umbracoCommerceProductAttributePresetAllowedAttribute;
ALTER TABLE vendrOrderLineAttribute RENAME TO umbracoCommerceOrderLineAttribute;
ALTER TABLE vendrPrintTemplate RENAME TO umbracoCommercePrintTemplate;
ALTER TABLE vendrExportTemplate RENAME TO umbracoCommerceExportTemplate;
ALTER TABLE vendrStoreEntityTag RENAME TO umbracoCommerceStoreEntityTag;
ALTER TABLE vendrMigrations RENAME TO umbracoCommerceMigrations;
ALTER TABLE vendrStore RENAME TO umbracoCommerceStore;
  1. Swap Vendr property editors for Umbraco Commerce property editors:

UPDATE umbracoDataType
SET propertyEditorAlias = REPLACE(propertyEditorAlias, 'Vendr.', 'Umbraco.Commerce.')
WHERE propertyEditorAlias LIKE 'Vendr.%'
  1. Swap the Vendr variants editor for the Umbraco Commerce variants editor in the block list data entry:

UPDATE umbracoPropertyData
SET textValue = REPLACE(textValue, 'Vendr.VariantsEditor', 'Umbraco.Commerce.VariantsEditor')
WHERE textValue LIKE '%Vendr.VariantsEditor%';
  1. Swap Vendr price/amount adjustments to Umbraco Commerce price/amount adjustments:

UPDATE umbracoCommerceOrderPriceAdjustment
SET type = REPLACE(type, 'Vendr.', 'Umbraco.Commerce.')
WHERE type LIKE '%Vendr.%';
UPDATE umbracoCommerceOrderAmountAdjustment
SET type = REPLACE(type, 'Vendr.', 'Umbraco.Commerce.')
WHERE type LIKE '%Vendr.%';
  1. Update template paths:

UPDATE umbracoCommerceEmailTemplate
SET templateView = REPLACE(templateView, '/App_Plugins/Vendr/templates/email', '/Views/UmbracoCommerce/Templates/Email')
WHERE templateView LIKE '%/Vendr/%';
UPDATE umbracoCommercePrintTemplate
SET templateView = REPLACE(templateView, '/App_Plugins/Vendr/templates/print', '/Views/UmbracoCommerce/Templates/Print')
WHERE templateView LIKE '%/Vendr/%';
UPDATE umbracoCommerceExportTemplate
SET templateView = REPLACE(templateView, '/App_Plugins/Vendr/templates/export', '/Views/UmbracoCommerce/Templates/Export')
WHERE templateView LIKE '%/Vendr/%';
  1. Update the migrations log:

UPDATE umbracoCommerceMigrations
SET migration = REPLACE(migration, 'Vendr.', 'Umbraco.Commerce.')
WHERE migration LIKE 'Vendr.%';
  1. Update the activity logs:

UPDATE umbracoCommerceActivityLog
SET eventType = REPLACE(eventType, 'vendr/', 'commerce/')
WHERE eventType LIKE 'vendr/%';

Step 4: Finalizing the migration

  1. Delete any obj/bin folders in your projects to ensure a clean build.

  2. Recompile all projects and ensure all dependencies are restored correctly

  3. Delete the existing Vendr license files in the umbraco\Licenses folder.

  4. Add your new Umbraco.Commerce license key to the appSettings.json file:

"Umbraco"" {
  "Licenses": {
    "Umbraco.Commerce": "YOUR_LICENSE_KEY"
  }
}
  1. Update any payment gateways that use a global webhook:

https://{site_url}/umbraco/commerce/payment/callback/{payment_provider_alias}/{payment_method_id}/
  1. Run the project.

It is highly recommended to ensure everything works as expected, before moving on to migrating packages and custom payment providers.

Further Migrations

If you have been using the Vendr Checkout package, you will need to follow some additional steps to migrate this package to Umbraco Commerce. Follow the link below for a complete guide:

Any custom payment providers used with Vendr also need to be migrated to Umbraco Commerce. Follow the link below to find detailed steps on how to perform this migration:

Overview

How-to Guides on how to perform specific tasks in Umbraco Commerce.

In this section, we will provide a series of How-To Guides, showcasing how to perform specific tasks within Umbraco Commerce.

Available guides

User Interface

The User Interface for Umbraco Commerce.

The Umbraco Commerce UI consists of a number of key areas, split over three sections within the Umbraco backoffice:

  • Settings for managing the different store settings.

  • Commerce for managing store-related content (orders, discounts, etc).

  • Content for managing the Umbraco Commerce products.

Settings Section

The Settings section is where the configuration of all Store settings is managed. From here you can manage how the Store works as well as what options will be available within the Store.

The UI for the Settings section consists of a Tree which lists all available Stores and their key areas available for configuration. It also contains a right-hand editor panel. This can either act as an editor interface or as a list view interface for listing items within that given configuration area.

Each Store has 8 key areas of configuration accessible within the Settings section:

  • Store: Each Store node contain Store level configuration settings.

  • Order Statuses contain the configuration of the different Statuses an order can be in. Think of these as an organizational structure for your Orders.

  • Shipping Methods contains the list of Shipping Methods available to a Store.

  • Payment Methods contains the list of Payment Methods available to a Store.

  • Countries contain the list of Countries the Store is able to trade with.

  • Currencies contain the list of accepted Currencies for the Store.

  • Taxes contains the list of Tax Classes and their Tax Rates for the Store.

  • Email Templates contains the list of Email Templates supported by the Store.

Commerce Section

The Commerce section contains a Tree to access the Stores and their different features, as well as a right-hand panel for managing the items.

Content Section

The Content section is where the Umbraco Commerce product nodes are managed. Managing products with Umbraco Commerce is similar to working with regular content nodes.

Configure SQLite support

How-To Guide to configure SQLite support for Umbraco Commerce.

Out of the box, Umbraco Commerce only supports SQL Server-based databases as this is the recommended database platform for live environments. To aid testing and rapid prototyping, however, Umbraco Commerce can be configured to use an SQLite database.

Whilst Umbraco Commerce does support SQLite for testing, we do not recommend using it in a live environment. Due to the high levels of active connections required to manage concurrent shopping carts, this is not something SQLite handles well at all.

Install SQLite dependencies

To add SQLite support, you will need to install the SQLite persistence layer NuGet package for Umbraco Commerce.

PM> dotnet add package Umbraco.Commerce.Persistence.Sqlite

Add .AddUmbracoCommerce() below .AddWebsite() in the Program.cs file.

.AddUmbracoCommerce(builder => {
    builder.AddSQLite();
})

After configuring Umbraco CMS with SQLite, Umbraco Commerce will automatically utilize the same database configuration. If you wish to install Umbraco Commerce into its own SQLite database you can configure its connection string in the appSettings.json like so:

{
    ...
    "ConnectionStrings": {
        "umbracoDbDSN": "Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Private;Foreign Keys=True;Pooling=True",
        "umbracoDbDSN_ProviderName": "Microsoft.Data.SQLite",
        "umbracoCommerceDbDSN": "Data Source=|DataDirectory|/Umbraco.Commerce.sqlite.db;Mode=ReadWrite;Foreign Keys=True;Pooling=True;Cache=Private",
        "umbracoCommerceDbDSN_ProviderName": "Microsoft.Data.SQLite"
    },
    ...
}

Calculators

Performing calculations with Calculators in Umbraco Commerce.

Calculators are small service implementations with the sole responsibility of calculating prices for a given aspect of an Order. There are five main Calculator service interfaces in Umbraco Commerce:

  • IShippingCalculator - Responsible for calculating the Shipping Method price/tax rate of a given Shipping Method.

  • IPaymentCalculator - Responsible for calculating the Payment Method price/tax rate of a given Payment Method.

  • IProductCalculator - Responsible for calculating the Product unit price/tax rate of a given Product.

  • IOrderLineCalculator - Responsible for calculating the price/tax rate of a given OrderLine.

  • IOrderCalculator - Responsible for calculating the entire Order.

All Calculator services can be replaced with alternative implementations should you wish to change how Umbraco Commerce performs its calculations.

Defining a Custom Calculator Implementation

The individual Calculator interfaces may differ but the process for defining a custom Calculator implementation is the same for all of them. It is possible to create a new class that implements the default system Calculator that you wish to replace. You can then override the relevant calculation methods.

Registering a custom Calculator implementation

You can upgrade your installation by installation the on top of the existing one.

You can find details on migrating the Checkout package as well as custom Payment Providers in the of this article.

Based on the outlined above update all Vendr references to the new Umbraco Commerce alternatives. Ensure you update any Views/Partials that also reference these.

Once the NuGet package is installed, you need to register SQLite support with Umbraco Commerce via the interface.

Calculators are interface using the AddUnique<TServiceInterface, TReplacementService>() method on the Services property. The TServiceInterface parameter in this case is the Calculator interface Type you wish to replace and TReplacementService is the Type of your custom Calculator implementation.

latest version
Migrate Umbraco Commerce Checkout
Migrate custom Payment Providers

Migrate from Vendr to Umbraco Commerce

Configure SQLite support

Limit Order Line Quantity

Use an alternative database for Umbraco Commerce tables

IUmbracoCommerceBuilder
Further Migrations section
Key Changes
public class MyProductCalculator : ProductCalculator
{
    public MyProductCalculator(ITaxService taxService, IStoreService storeService)
        : base(taxService, storeService)
    { }

    public override TaxRate CalculateProductTaxRate(IProductSnapshot productSnapshot, TaxSource taxSource, TaxRate fallbackTaxRate)
    {
        // Do custom tax rate calculation here
    }

    public override Price CalculateProductPrice(IProductSnapshot productSnapshot, Guid currencyId, TaxRate taxRate)
    {
        // Do custom price calculation here
    }
}
public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyServices(IUmbracoCommerceBuilder builder)
    {
        // Replacing the product calculator implementation
        builder.Services.AddUnique<IProductCalculator, MyProductCalculator>();

        // Return the builder to continue the chain
        return builder;
    }
}

Use an Alternative Database for Umbraco Commerce Tables

How-To Guide to configure using an alternative database for the tables of Umbraco Commerce.

By default, Umbraco Commerce will use the same database as Umbraco to store its data. As e-commerce and content management have different database needs, it may be beneficial to house the Umbraco Commerce database tables in an alternative database.

To do this, you can configure a Umbraco Commerce-specific connection string in your app settings ConnectionStrings section using the umbracoCommerceDbDSN prefix.

{
    ...
    "ConnectionStrings": {
        "umbracoDbDSN": "Server=umbracoServerAddress;Database=myUmbracoDb;User Id=myUsername;Password=myPassword;",
        "umbracoDbDSN_ProviderName": "Microsoft.Data.SqlClient",
        "umbracoCommerceDbDSN": "Server=umbracoCommerceServerAddress;Database=myUmbracoCommerceDb;User Id=myUsername;Password=myPassword;",
        "umbracoCommerceDbDSN_ProviderName": "Microsoft.Data.SqlClient"
    },
    ...
}

When Umbraco Commerce runs, it will perform all of its migrations and operations against this database instead of the default Umbraco database.

registered via the IUmbracoCommerceBuilder

Bulk Actions

Perform bulk operations on entities in Umbraco Commerce.

You might need to execute a custom action for each entity in a selection while extending Umbraco Commerce. For example, being able to trigger label printing for a series of orders, or printing physical gift cards for specific gift card entities.

Umbraco Commerce allows extending the different table views, adding in Bulk Actions to the bulk action bar that appears when you select multiple items. Out of the box all list views contain at minimum a Delete bulk action.

Injecting a Bulk Action

Bulk actions are client-side concepts and so additional bulk actions are injected with JavaScript in an AngularJS configuration module.

To create a configuration module you can create a custom folder in the App_Plugins directory and create a JavaScript file to hold your configuration in.

  1. Create a custom folder in the App_Plugins directory.

  2. Create a JavaScript file with this new folder.

App_Plugins\MyPlugin\backoffice\config\umbraco-commerce-bulk-actions-config.js
  1. Register the file in a package.manifest file within the same folder.

App_Plugins\MyPlugin\package.manifest
  1. Add the following JSON to the package.manifest file:

{
    "javascript": [
        "~/App_Plugins/MyPlugin/backoffice/config/umbraco-commerce-bulk-actions-config.js"
    ]
}
  1. Inject a bulk action inside the umbraco-commerce-bulk-actions-config.js by adding the following:

// Define an angular module that has a dependency on `umbraco.commerce`
angular.module('myModule', ['umbraco.commerce'])
    .config(['ucActionsProvider', function (ucActionsProvider) {
        ucActionsProvider.bulkActions.push(['myResource', function (myResource)
        {
            return {
                name: 'My Action',
                icon: 'icon-box',
                itemAction: function (bulkItem) {
                    return myResource.doAction(bulkItem.id);
                },
                condition: function (ctx) {
                    return ctx.entityType == 'Order'
                },
                sortOrder: 110
            }
        }]);
    }]);

// Add your module as a dependency of the main umbraco module
angular.module('umbraco').requires.push('myModule');

Once created, the bulk action will be displayed in the bulk actions bar for the configured entities.

Bulk Action Options

Property
Description

name

Name of your bulk action that will be displayed in the bulk action button.

icon

Icon for your bulk action that will be displayed in the bulk action button next to the name.

sortOrder

The order in which to display this action in the bulk actions bar. System bulk actions sort orders are in multiples of 100 in order to allow the positioning of items between system bulk actions.

Method
Description

configure(items)

A function to run before the bulk operation in order to provide configuration for the bulk action. Returns a Promise that returns an object which is then passed to the item/bulk action methods.

itemAction(item, config)

Individual action to perform per selected item. A status will be displayed after each processed item shows progress. Returns a Promise.

bulkAction(items, config)

Single action to be performed for all selected items in one go. Returns a Promise.

getConfirmMessage(total)

A function that can provide a message to display before a bulk action is triggered should confirmation be required for the action to run. Returns a Promise that returns a string.

getStatusMessage(count, total)

Function used to provide a status message after each item has been processed. Displayed in the bulk actions bar after each itemAction has been called. Returns a Promise that returns a string.

getSuccessMessage(total)

A function to return a success message after all bulk actions have been performed. Returns a Promise that returns a string.

condition(context)

As all bulk actions are registered globally for all entity types, the condition function can be used to filter when, and for which entities a bulk action will display.

Only an itemAction or a bulkAction method can be defined for a bulk action configuration. If both are present, the bulkAction will be used and the itemAction will be ignored. If processing of items can be done individually, it is better to use the itemAction in order to provide user feedback. The bulkAction can only be used where items need to be processed in a single action.

Important Notes

  • Most methods apart from itemAction or bulkAction are optional. If methods aren't present, a default implementation will be used. Where the methods trigger, specific functionality such as the configure or getConfirmMessage methods will become disabled.

  • The array-based syntax for registering is a bulk action with angular dependencies. Each bulk action is registered as an array, where all dependencies are defined first and then a factory function is defined last which returns the actual bulk action definition.

  • Whilst these docs outline how to define a bulk action, you will likely need to register further resources or services that can perform the given bulk operation and include these as a dependency for your action.

Examples

The following section display an example of a bulk action with dialog configuration step:

angular.module('myModule', ['umbraco.commerce'])
    .config(['ucActionsProvider', function (ucActionsProvider) {
        ucActionsProvider.bulkActions.push(['$q', 'editorService', 'myResource', function ($q, editorService, myResource)
        {
            return {
                name: 'My Action',
                icon: 'icon-box',
                configure: function (selected) {
                    return $q(function (resolve, reject) {
                        editorService.open({
                            view: '/app_plugins/myplugin/views/dialogs/config.html',
                            size: 'small',
                            config: {
                                items: selected
                            },
                            submit: function (model) {
                                editorService.close();
                                resolve(model);
                            },
                            close: function () {
                                editorService.close();
                                reject();
                            }
                        });
                    });
                },
                bulkAction: function (items) {
                    var ids = items.map(itm => itm.id);
                    return myResource.doAction(ids);
                },
                condition: function (ctx) {
                    return ctx.entityType == 'Order'
                },
                sortOrder: 110
            }
        }]);
    }]);

angular.module('umbraco').requires.push('myModule');

Discount Rules / Rewards

Define when a Discount should apply and what should be the Reward in Umbraco Commerce.

Discounts in Umbraco Commerce are defined using a series of rules and reward builders that let you configure the following:

  • When a Discount should apply.

  • What the Reward should be for that Discount.

These builders come with a handful of the most common Rules and Rewards that should suit the majority of web stores' needs. When need to create your own Rules or Rewards then these are extendable via a Provider model allowing you to incorporate your own custom logic.

Discount Rules

There are two types of Discount Rules in Umbraco Commerce:

  • Order Discount Rules: Determine whether a discount should apply to an Order. Returns a Fulfilled/Unfulfilled status depending on whether the Rule logic has been met.

  • Order Line Discount Rules: Determine whether a discount should apply to an Order Line within an Order. Returns a Fulfilled/Unfulfilled status depending on whether the Rule logic has been met. Where the status is Fulfilled, a list of all Order Lines that are fulfilled by this Rule is also returned.

Example: Custom Order Discount Rule Provider

An example of an Order Discount Rule Provider would look something like this:

[DiscountRuleProvider("myCustomOrderRule", "My Custom Order Rule")]
public class MyCustomOrderRuleProvider : OrderDiscountRuleProviderBase<MyCustomOrderRuleProviderSettings>
{
    public override DiscountRuleResult ValidateRule(DiscountRuleContext ctx, MyCustomOrderRuleProviderSettings settings)
    {
        if (/* Some custom logic */)
            return Fulfilled();
        
        return Unfulfilled();
    }
}

public class MyCustomOrderRuleProviderSettings
{
    [DiscountRuleProviderSetting(Key = "priceType",
        Name = "Price Type",
        Description = "The type of price to compare against")]
    public OrderPriceType PriceType { get; set; }

    ...
}

All Order Discount Rule Providers inherit from a base class OrderDiscountRuleProviderBase<TSettings>. TSettings is the type of a Plain Old Class Object (POCO) model class representing the Discount Rule Providers settings.

The class must be decorated with DiscountRuleProviderAttribute which defines the Discount Rule Providers alias and name, and can also specify a description or icon to be displayed in the backoffice. The DiscountRuleProviderAttribute is also responsible for defining a labelView for the Provider.

Rule Providers have a ValidateRule method that accepts a DiscountRuleContext as well as an instance of the Providers TSettings settings model. Inside this you can perform your custom logic, returning a DiscountRuleResult to notify Umbraco Commerce of the Rule outcome.

If the passed-in context (which contains a reference to the Order) meets the Rule's criteria, then a fulfilled DiscountRuleResult can be returned by calling return Fulfilled();. Alternatively, if the Order didn't meet the Rules criteria an unfulfilled DiscountRuleResult can be returned by calling return Unfulfilled();.

Example: Custom Order Line Discount Rule Provider

An example of an Order Line Discount Rule Provider would look something like this:

[DiscountRuleProvider("myCustomOrderLineRule", "My Custom Order Line Rule")]
public class MyCustomOrderLineRuleProvider : OrderLineDiscountRuleProviderBase<MyCustomOrderLineRuleProviderSettings>
{
    public override DiscountRuleResult ValidateRule(DiscountRuleContext ctx, MyCustomOrderLineRuleProviderSettings settings)
    {
        if (/* Some custom logic */)
            return Fulfilled(fulfilledOrderLines);
        
        return Unfulfilled();
    }
}

public class MyCustomOrderLineRuleProviderSettings
{
    [DiscountRuleProviderSetting(Key = "priceType",
        Name = "Price Type",
        Description = "The type of price to compare against")]
    public OrderPriceType PriceType { get; set; }

    ...
}

All Order Line Discount Rule Providers inherit from a base class OrderLineDiscountRuleProviderBase<TSettings> and follows much the same requirements as the Order Discount Rule Provider defined above. Where they differ is in the ValidateRule method implementation and when a fulfilled DiscountRuleResult is returned. In this case, an Order Line Discount Rule returns a collection of Order Lines processed by the Rule that have met the rules criteria. Whether the rules are met, is checked by calling return Fulfilled(fulfilledOrderLines);.

Discount Rewards

Example: Custom Discount Reward Provider

An example of a Discount Reward Provider would look something like this:

[DiscountRewardProvider("myDiscountReward", "My Discount Reward")]
public class MyDiscountRewardProvider : DiscountRewardProviderBase<MyDiscountRewardProviderSettings>
{
    public override DiscountRewardCalculation CalculateReward(DiscountRewardContext ctx, MyDiscountRewardProviderSettings settings)
    {
        var result = new DiscountRewardCalculation();

        // Some custom calculation logic goes here 

        return result;
    }
}

public class MyDiscountRewardProviderSettings
{
    [DiscountRewardProviderSetting(Key = "priceType",
        Name = "Price Type",
        Description = "The price that will be affected by this reward")]
    public OrderPriceType PriceType { get; set; }

    ...
}

All Discount Reward Providers inherit from a base class DiscountRewardProviderBase<TSettings>. TSettings is the Type of a POCO model class representing the Discount Reward Providers settings.

The class must be decorated with DiscountRewardProviderAttribute which defines the Discount Reward Providers alias and name. It can also specify a description or icon to be displayed in the Umbraco Commerce backoffice. The DiscountRewardProviderAttribute is responsible for defining a labelView for the Provider.

Reward Providers have a CalculateReward method that accepts a DiscountRewardContext as well as an instance of the Providers TSettings settings model. Inside this, you can perform your custom calculation logic, returning a DiscountRewardCalculation instance that defines any Reward values to apply to the Order.

// Add a shipping total discount
result.ShippingTotalPriceAdjustments.Add(new DiscountAdjustment(ctx.Discount, price));

// Add a subtotal discount
result.SubtotalPriceAdjustments.Add(new DiscountAdjustment(ctx.Discount, price));

Common Features

Settings Objects

Label Views

Both the DiscountRuleProviderAttribute and the DiscountRewardProviderAttribute allow you to define a labelView for the Provider. It should be the path to an Angular JS view file that will be used to render a label in the Rule/Reward Builder UI. Where no labelView is supplied, one will be looked for by convention at the following location:

~/app_plugins/umbracocommerce/views/discount/{Type}/labelViews/{ProviderAlias}.html

Type is either rules or rewards, depending on the Type of Provider it refers to. ProviderAlias is the alias of the Provider.

The Rule/Reward Label View should provide a user-friendly summary of its settings to display in the relevant Builder UI.

The Label View file will be passed a model property which will be a JavaScript representation of the given Providers settings object.

<span ng-if="model.priceType">Order {{ model.priceType | umbracoCommerceSplitCamelCase }} Discount</span>

Base Currency

Base Currency for standardized reporting in Umbraco Commerce.

Within Umbraco Commerce we have support for showing analytics reports, including summaries of sales figures. At the same time, Umbraco Commerce also supports orders being placed in multiple currencies. These pose a problem of how to display a succinct sales figure when the orders are placed in multiple currencies. The answer to this is the store's Base Currency.

When you configure a store you need to assign a base currency to it. This currency is there to identify which currency the store should use as its basis for reports and sales figures. This will be used regardless of whatever currency the order was placed in.

When a store has a base currency configured, any order placed will track the price of the order in the customer's chosen currency. It will also track the current exchange rate between that currency and the store's base currency. Whenever a report is run the order total prices will be converted using this exchange rate. This means that they can all be automatically presented in the single base currency of the store.

Currency Exchange Rates

Umbraco Commerce uses an ICurrencyExchangeRateService to retrieve the most up-to-date rate to be able to track the current exchange rate. This is done for each individual order.

Out of the box, Umbraco Commerce comes with a number of available services you can choose to use. Some are free services, whilst others require a paid subscription.

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyServices(IUmbracoCommerceBuilder builder)
    {
        // Register the fixer tax service with your API key
        builder.Services.AddUnique<ICurrencyExchangeRateService>(new FixerCurrencyExchangeRateService("YOUR_FIXER_API_KEY"));
        
        // Return the builder to continue the chain
        return builder;
    }
}

Historic Orders

Umbraco Commerce has a background service that will attempt to ensure that all historic orders without an exchange rate defined get updated. This is done in case the third-party APIs fail and we need a method of cleaning data. It is also done in case the store base currency is ever changed. In this case, we need to re-process all orders again with the newly selected base currency.

The currency exchange rate background task will run once every 24 hours or after 20 seconds after an app pool recycle.

Get to know the main features

Learn everything you need to know about the main features and concepts of Umbraco Commerce.

In this section, we will look at all the key concepts you will need to understand in order to work with Umbraco Commerce. Many of the concepts are based upon how Umbraco functions, although multiple Umbraco Commerce-specific concepts require your attention.

Order Calculation State

Calculation context in Umbraco Commerce.

The Calculation Process

When an order asks to be re-calculated, this triggers a calculation pipeline which in turn runs a series of calculation tasks. It then calls a number of extendable calculators in order to work out the orders' different prices. Throughout this process, Umbraco Commerce needs to keep track of all these prices as they change. At the same time, it also needs to ensure that the calculation is transactional in case something goes wrong. To accomplish both of these requirements we use a temporary state object called OrderCalculation to store all the information. Only at the end of the calculation, if everything was successful, we can copy those calculated prices back to the order.

Accessing Price Values

In the different calculation extension points, Umbraco Commerce will often pass you both an Order object and the OrderCalculation object. We pass the order to get you access to any information held on it that you may need for calculations, such as custom properties. This shouldn't be used for accessing any price-related values of the order.

As mentioned above, in order to maintain data integrity during the calculation process, the order itself is not updated until the end. This means that any calculations based on the order entities' price values would be based on the orders' previously calculated price values.

In order to base your calculation on the current calculated price values you should instead access the OrderCalculation object.

The OrderCalculation Object

From the OrderCalculation object you can access the different order prices, including order line calculations. The order line calculations are stored in a dictionary. In this dictionary, the key is the order line's ID, and the value is an OrderLineCalculation object holding the calculated prices.

By using the prices from the OrderCalculation object you can ensure that your calculation is based on the most up-to-date values for the order.

You should always base your price on the OrderCalculation object's price values when the following applies:

  • Your values are based on another price held on an order

  • You have access to an OrderCalculation an object that isn't null.

It should also only fall back to the order entity if there is no OrderCalculation available.

Dependency Injection

Minimizing dependencies via dependency injection with Umbraco Commerce.

Dependency Injection (DI) can be an intimidating subject. DI reduces the number of hard-coded dependencies within a codebase by providing a means to define dependencies independently and have them "injected" dynamically. These dependencies are often exposed as interfaces, rather than concrete types. This enables them to be swapped out or replaced with minimal effort.

The ability to "swap out" dependencies is used in Umbraco Commerce in a number of places to allow developers to provide alternative implementations of specific features. This could be the ability to:

  • Swap out the default Product Calculator to change how product prices are calculated.

  • Swap out the default Order Number Generator should you wish to provide an alternative order numbering strategy.

Umbraco Commerce makes heavy use of the dependency injection mechanism in Umbraco to manage many of the features. It is important to understand how to work with the registration process.

Registering Dependencies

Registering dependencies is an important ability to understand as this is used to register Umbraco Commerce event handlers and to extend system pipelines.

To register a dependency you need to do so via the IUmbracoBuilder interface. This is exposed within the main Startup class, inside the ConfigureServices method between the AddComposers() method call and the Build() method call.

You can also add your registration logic inside an IUmbracoBuilder extension method and then call that within the ConfigureServices method. This is the recommended approach.

Registering a dependency is achieved by working with the IUmbracoBuilder API:

Replacing Dependencies

Like it is possible to add new dependencies it is also possible to replace existing dependencies. This could be dependencies such as the different Calculators available in Umbraco Commerce.

Where a feature is replaceable, replacing that dependency is also achieved via the IUmbracoBuilder API:

Injecting Dependencies

As well as registering dependencies, you will also need to know how to access Umbraco Commerce dependencies from within your Controllers. To do this, we add parameters to our Controllers constructor for the dependencies we require. Then, the IoC container will inject them automatically for us.

See the section below for more information on Settings objects.

See the section below for more information on Label Views.

See the documentation for more information on Settings objects.

See the section below for more information on Label Views.

See the documentation for more information on Settings objects.

ExchangeRatesApiCurrencyExchangeRateService uses the free API and is the default option.

FixerCurrencyExchangeRateService uses the API which is a reliable paid option (with a reasonable free plan).

CurrencyLayerCurrencyExchangeRateService uses the API which is another reliable paid option (with a reasonable free plan).

If you wish to change the currency exchange rate service used, you can do so via the approach. This is used to override the default service configuration. For services that require configuration to be passed in, such as service API keys, you'll need to use the factory-based override as follows:

When extending the calculation process of Umbraco Commerce, either by custom or custom it is important to be aware of the OrderCalculation object.

What follows are examples of common tasks you'll need to be able to perform via the DI container in order to work effectively with Umbraco Commerce. For more detailed documentation, it is highly recommended that you read the .

Settings Objects
Settings Objects
exchangeratesapi.io
fixer.io
currencylayer.com
dependency injection
Settings Objects
Label views
Label views
public class OrderCalculation
{
    public Dictionary<Guid, OrderLineCalculation> OrderLines { get; }

    public Dictionary<string, Amount> GiftCardAmounts { get; }

    public List<string> FulfilledDiscountCodes { get; }

    public List<FulfilledDiscount> FulfilledDiscounts { get; }

    public TaxRate TaxRate { get; set; }

    public OrderSubtotalPrice SubtotalPrice { get; set; }

    public TaxRate ShippingTaxRate { get; set; }

    public TotalPrice ShippingTotalPrice { get; set; }

    public TaxRate PaymentTaxRate { get; set; }

    public TotalPrice PaymentTotalPrice { get; set; }

    public OrderTotalPrice TotalPrice { get; set; }
}

public class OrderLineCalculation
{
    public Dictionary<Guid, OrderLineCalculation> OrderLines { get; }

    public TaxRate TaxRate { get; set; }

    public OrderLineUnitPrice UnitPrice { get; set; }

    public OrderLineTotalPrice TotalPrice { get; set; }

    public Price RollingSubOrderLinesTotalPrice { get; set; }

    public Price RollingSubOrderLinesTotalDiscountPrice { get; set; }
}
public class Startup
{
    ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddUmbraco(_env, _config)
            .AddBackOffice()
            .AddWebsite()
            .AddComposers()
            // Append your dependencies here...
            .Build();
    }
    ...
}
public static class UmbracoBuilderExtensions
{
    public static IUmbracoBuilder AddMyDependencies(this IUmbracoBuilder builder)
    {
        // Register my dependencies here via the builder parameter
        ...

        // Return the builder to continue the chain
        return builder;
    }
}
public class Startup
{
    ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddUmbraco(_env, _config)
            .AddBackOffice()
            .AddWebsite()
            .AddComposers()
            .AddMyDependencies()
            .Build();
    }
    ...
}
public static class UmbracoBuilderExtensions
{
    public static IUmbracoBuilder AddMyDependencies(this IUmbracoBuilder builder)
    {
        // Register a singleton dependency
        builder.Services.AddSingleton<IMySingletonService, MySingletonService>();

        // Register a transient dependency
        builder.Services.AddTransient<IMyTransientService, MyTransientService>();

        // Return the builder to continue the chain
        return builder;
    }
}
public static class UmbracoBuilderExtensions
{
    public static IUmbracoBuilder AddMyDependencies(this IUmbracoBuilder builder)
    {
        // Replacing the product calculator implementation
        builder.Services.AddUnique<IProductCalculator, MyProductCalculator>();

        // Replacing the default product adapter
        builder.Services.AddUnique<IProductAdapter, MyProductAdapter>();

        // Return the builder to continue the chain
        return builder;
    }
}
using Umbraco.Commerce.Core.Api;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Web.Common.Controllers;

namespace MyProject.Web.Controllers
{
    public class HomeController : RenderController
    {
        private readonly IUmbracoCommerceApi _umbracoCommerceApi;

        public HomeController(IUmbracoCommerceApi umbracoCommerceApi, ILogger<HomeController> logger, 
            ICompositeViewEngine compositeViewEngine, IUmbracoContextAccessor umbracoContextAccessor)
            : base(logger, compositeViewEngine, umbracoContextAccessor)
        {
            _umbracoCommerceApi = umbracoCommerceApi;
        }

        public  override IActionResult Index()
        {
            // Work with the _umbracoCommerceApi here

            return CurrentTemplate(CurrentPage);
        }
    }
}

Umbraco Properties

Umbraco Commerce is based on Umbraco CMS, which means that some of the things you'll be working with are native to the core product.

Calculators

Learn more about the different calculators that Umbraco Commerce ships with and how you can customize and extend them.

Product Variants

With Umbraco Commerce you can create a large array of different product variants. Learn more about how this all works.

calculators
pipeline tasks
Umbraco CMS Dependency Injection and IoC documentation

Fluent API

Faster development thanks to the Fluent API of Umbraco Commerce.

Writing fluently

You could perform a write operation as follows:

_uowProvider.Execute(uow =>
{
    // Fetch the currency
    var currency = _currencyService.GetCurrency(currencyId);

    // Convert the currency into it's Writable form
    var writableCurrency = currency.AsWritable(uow);

    // Perform the write operation
    writableCurrency.SetName("New Name");

    // Persist the changes to the database
    _currencyService.SaveCurrency(currency);

    // Close the transaction
    uow.Complete();
});

This could be simplified further by defining these actions fluently, chaining all of the entity methods into a succinct command sequence as follows:

_uowProvider.Execute(uow =>
{
    var currency = _currencyService.GetCurrency(currencyId)
        .AsWritable(uow)
        .SetName("New Name");

    _currencyService.SaveCurrency(currency);

    uow.Complete();
});

We know not everyone likes to write their code fluently and so the Umbraco Commerce Fluent API is an optional feature. Both code examples above are valid coding styles that will both work as well as each other. The Fluent API is an opt-in layer of syntax sugar that developers can use depending on their preferred style of coding.

An added side effect of having is that all of an entity's write operations are now performed via methods. This is instead of property setters, enabling to us convert Umbraco Commerce's write API in a fluent API.

ReadOnly and Writable entities

Pipelines

Performing sequential tasks with Pipelines in Umbraco Commerce.

Pipelines allow a series of tasks to be performed in a set sequence. This is done with the input of a given task being the output of the preceding task. It allows a result to be built up as an input is passed through these individual tasks, instead of being calculated in one go.

The Pipelines feature provides an approach to insert additional steps into the process as pipeline tasks can be added or removed from the pipeline sequence.

Where Pipelines is used, it allows an additional point at which developers can interject some custom logic, tweaking how Umbraco Commerce works.

Consider these use-case examples:

  • An additional task could be injected into the CalculateOrderPipeline to alter how an Order is calculated.

  • A task could be injected into the EmailSendPipeline to add a dynamic attachment to an email.

Example Pipeline task

An example of a Pipeline task would look something like this:

public class AddCustomAttachmentTask : PipelineTaskWithTypedArgsBase<EmailSendPipelineArgs, EmailContext>
{
    public override PipelineResult<EmailContext> Execute(EmailSendPipelineArgs args)
    {
        var attachment = new Attachment(File.OpenRead("path\to\license.lic"), "license.lic");

        args.EmailContext.MailMessage.Attachments.Add(attachment);

        return Ok(args.EmailContext);
    }
}

All Pipeline tasks inherit from a base class PipelineTaskWithTypedArgsBase<TPipelineArgs, TModel>. TPipelineArgs is the type of arguments supported by the pipeline and TModel is the pipeline's return model Type. You then need to implement an Execute method that accepts an instance of the argument's type as input and expects a PipelineResult<TModel> as its output. Inside this method, you can perform your custom logic as required. To complete the pipeline task, you can call Ok(TModel) if the task was successful. This will pass in the updated TModel instance to returnæ. Otherwise, you can call Fail() to fail the whole pipeline.

Registering a Pipeline task

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyPipelineTasks(IUmbracoCommerceBuilder builder)
    {
        // Add our custom pipeline tasks
        builder.WithSendEmailPipeline()
            .Add<LogEmailSentTask>();

        // Return the builder to continue the chain
        return builder;
    }
}

You can also control the order of when Pipeline tasks run, before or after another task, by appending them via the InsertBefore<TTask>() or InsertAfter<TTask>() methods respectively.

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyPipelineTasks(IUmbracoCommerceBuilder builder)
    {
        // Register AddCustomAttachmentTask to execute before the RaiseSendingEventTask
        builder.WithSendEmailPipeline()
            .InsertBefore<RaiseSendingEventTask, AddCustomAttachmentTask>();

        // Register LogEmailSentTask to execute after the RaiseSendingEventTask
        builder.WithSendEmailPipeline()
            .InsertAfter<RaiseSendingEventTask, LogEmailSentTask>();

        // Return the builder to continue the chain
        return builder;
    }
}

All pipelines occur within a . In case a Pipeline task fails, the whole pipeline will fail and no changes will persist.

Pipeline tasks are interface using the appropriate With{PipelineName}Pipeline() builder extension method. This is done to identify the pipeline you want to extend. You can then call the Add<TTask>() method to add your task to the end of that pipeline.

Unit of Work
registered via the IUmbracoCommerceBuilder

Payment Forms

Preparing to enter a Payment Providers payment gateway in Umbraco Commerce.

In Umbraco Commerce, a Payment Form is a form that is displayed immediately prior to redirecting to the Payment Gateway for payment processing. This is usually displayed on some kind of review page, allowing a final review of the Order before commencing payment.

The role of the Payment Form is to perform two tasks:

  • Prepare the Order for the Payment Gateway - This includes initializing the Orders transaction info and assigning the Order with an Order Number. It's also at this time that the Order is assigned to a Member if there is currently a logged-in session. This task may also involve passing information to the Payment Gateway to create a session, which the customer will complete in the next step. This is dependent on the Payment Provider implementation.

  • Redirect to the Payment Gateway - The configured Payment Provider will return a Form that contains all the relevant information the Payment Gateway needs. This includes the Forms action attribute is set to post to a page on the Payment Gateways server, starting the payment capture process.

An Order's Order Number is assigned at the point of the Payment Form being rendered. This is to ensure that an Order has an Order Number prior to redirecting to the Payment Gateway. When the customer is redirected to the Confirmation page, there is always an Order Number to display

The reason this is necessary is that many Payment Gateways finalize Orders asynchronously via webhooks. This means that it is possible that the customer will be redirected to the Confirmation page prior to actual finalization. This is why we set it early to ensure it is always available.

It can happen that a customer cancels a payment mid-way through the capture process and returns to the Order to make modifications. In these cases, a new Order Number will be assigned at the point of re-displaying the Payment Form.

Example Payment Form

An example of displaying a Payment Form would look something like this:

@using(await Html.BeginPaymentFormAsync(currentOrder)) {
    <button type="submit">Continue to Payment</button>
}

The Payment Form is rendered using a using statement to wrap any additional form elements you wish to add, such as a submit button.

It's important to know that the Payment Form by default doesn't contain any button inputs to submit the Form. These must be supplied by the implementer. This is to ensure that the form will work with the design of the Site in question, giving developers more freedom.

Events

Listening for changes within Umbraco Commerce.

Much like the standard events in .NET, Umbraco Commerce has an events system to notify you when certain things happen within the application. However, Umbraco Commerce differs slightly in the types of events that are fired and how you register your event handlers.

In Umbraco Commerce, there are two main types of events you can create handlers for. Both are explained in detail below.

Validation events

Validation events are events that fire immediately before a change is about to be made to an entity. These events allow you to inject your own logic to decide whether an action should be possible or not. We already have a number of validation handlers built in to maintain the consistency of your data. Validation events allow you to extend this behavior with your own rules.

Example: Validation event handler

An example of a Validation event handler would look something like this:

public class MyOrderProductAddValidationHandler : ValidationEventHandlerBase<ValidateOrderProductAdd>
{
    public override void Validate(ValidateOrderProductAdd evt)
    {
        if (evt.ProductReference == "MyProductRef" && evt.Quantity % 10 != 0)
            evt.Fail("This product can only be purchased in increments of 10");
    }
}

All Validation event handlers inherit from a base class ValidationEventHandlerBase<TEvent> where TEvent is the Type of the event the handler is for. They then have a Validate method that accepts an instance of the event type, and inside which you can perform your custom logic. If the event fails the validation logic, you can call evt.Fail("Your message here") to block the related action from happening and have a ValidationException be thrown. This can then be captured in the front end to display a friendly error message.

Registering a Validation event handler

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyEventHandlers(IUmbracoCommerceBuilder builder)
    {
        // Register my event handlers
        builder.WithValidationEvent<ValidateOrderProductAdd>()
            .RegisterHandler<MyOrderProductAddValidationHandler>();

        // Return the builder to continue the chain
        return builder;
    }
}

You can control the order of when Validation event handlers run, before or after another Validation event handler. This is done by registering them via the RegisterHandlerBefore<THandler>() or RegisterHandlerAfter<THandler>() methods respectively.

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyEventHandlers(IUmbracoCommerceBuilder builder)
    {
        // Register MyOrderProductAddValidationHandler to execute before the SomeOtherValidationHandler handler
        builder.WithValidationEvent<ValidateOrderProductAdd>()
            .RegisterHandlerBefore<SomeOtherValidationHandler, MyOrderProductAddValidationHandler>();

        // Register MyOrderProductAddValidationHandler to execute after the SomeOtherValidationHandler handler
        builder.WithValidationEvent<ValidateOrderProductAdd>()
            .RegisterHandlerAfter<SomeOtherValidationHandler, MyOrderProductAddValidationHandler>();

        // Return the builder to continue the chain
        return builder;
    }
}

Notification events

Notification events are events that fire, often immediately before or after an action is executed. It provides you the ability to run custom logic to react to that action occurring. This is useful for scenarios such as sending emails when an Order is finalized or allowing you to synchronize stock updates with an external system.

Notification events won't allow you to change the behavior of how Umbraco Commerce runs. They provide you with an effective means of reacting when changes occur.

Example: Notification event handler

An example of a Notification event handler would look something like this:

public class MyOrderFinalizedHandler : NotificationEventHandlerBase<OrderFinalizedNotification>
{
    public override void Handle(OrderFinalizedNotification evt)
    {
        // Implement your custom logic here
    }
}

All Notification event handlers inherit from a base class NotificationEventHandlerBase<TEvent> where TEvent is the Type of the event the handler is for. They then have a Handle method that accepts an instance of the event type, and inside which you can perform your custom logic.

Registering a Notification event handler

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyEventHandlers(IUmbracoCommerceBuilder builder)
    {
        // Register my event handlers
        builder.WithNotificationEvent<OrderFinalizedNotification>()
            .RegisterHandler<MyOrderFinalizedHandler>();

        // Return the builder to continue the chain
        return builder;
    }
}

You can also control the order of when Notification event handlers run by registering them via the RegisterHandlerBefore<THandler>() or RegisterHandlerAfter<THandler>() methods respectively.

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyEventHandlers(IUmbracoCommerceBuilder builder)
    {
        // Register MyOrderFinalizedHandler to execute before the SomeOtherNotificationHandler handler
        builder.WithNotificationEvent<OrderFinalizedNotification>()
            .RegisterHandlerBefore<SomeOtherNotificationHandler, MyOrderFinalizedHandler>();

        // Register MyOrderFinalizedHandler to execute after the SomeOtherNotificationHandler handler
        builder.WithNotificationEvent<OrderFinalizedNotification>()
            .RegisterHandlerAfter<SomeOtherNotificationHandler, MyOrderFinalizedHandler>();

        // Return the builder to continue the chain
        return builder;
    }
}

Price/Amount Adjustments

Learn about adjusting prices in Umbraco Commerce.

In some cases, you may want to tweak the figures of an order. It could be reducing the price of a product if a customer purchases a given amount of a product. To handle this, Umbraco Commerce has the concept of Price/Amount Adjustments. What adjustments allow you to do is create a record/log of any changes that occur to a price/amount throughout the calculation process. Umbraco Commerce uses the adjustments in the calculation process to work out its final pricing and provides this list of the adjustments on the order. This makes it clear exactly how the price was calculated.

Umbraco Commerce has two types of adjustments:

  • Price Adjustment - Adjusts one of the orders' price properties (discounts, fees).

  • Amount Adjustment - Adjusts the final transaction amount of the order (gift cards, loyalty points).

Creating Custom Adjustments

Adjustments are applied using a IPriceAdjuster or IAmountAdjuster with developers able to create their own adjusters to apply custom adjustments.

Adjusters apply adjustments to the given price they wish to affect. Adjustments are strongly typed and each adjuster should define their own adjustment type, providing properties to collect any relevant information for the adjustment. This "metadata" gets serialized with the adjustment as is constantly available when accessing the given adjustment.

Adjustments inherit from either PriceAdjustment<TSelf> or AmountAdjustment<TSelf> depending on the type of adjustment being applied. Both base classes follow a similar structure, the difference being whether the adjustment value is a Price or Amount.

Once defined, the adjuster should be registered with the DI container to enable Umbraco Commerce to be aware of it and include it in the calculation process.

Creating a Commerce User Group in Umbraco
Commerce Section in Umbraco Navigation
Installing Umbraco Commerce via the NuGet Package Manager
Umbraco Commerce License Dashboard
Umbraco Commerce Settings - Editor View
Umbraco Commerce Settings - List View
Umbraco Commerce Orders View
Umbraco Commerce Order Editor
Umbraco Commerce Store Picker Dialog
Bulk Action Button
Discount Rule Label Views

Events in Umbraco Commerce are registered via the interface, rather than via static event delegates. This has a number of advantages, such as being able to control the order of when event handlers are fired. It also allows us to inject dependencies into the event handlers making it a much more decoupled approach to eventing.

Validation event handlers are interface using the WithValidationEvent<TEvent>() builder extension method. This is done to identify the event you want to handle and then call the RegisterHandler<THandler>() method to register your handler(s) for that event.

Notification event handlers are interface using the WithNotificationEvent<TEvent>() builder extension method. This is used to identify the event you want to handle and then call the RegisterHandler<THandler>() method to register your handler(s) for that event.

IUmbracoCommerceBuilder
registered via the IUmbracoCommerceBuilder
registered via the IUmbracoCommerceBuilder
public class MyPriceAdjuster : PriceAdjusterBase
{
    public override void ApplyPriceAdjustments(PriceAdjusterArgs args)
    {
        // Calculate Adjustment
        // Discount adjustments should be negative
        // where as Fee adjustments should be positive

        // Create a £10 discount
        var price = new Price(-8.33, -1.67, args.Order.CurrencyId);
        var adjustment = new MyAdjustment("My Discount", "MD-001", price);

        // Add the adjustment to the sub total price
        args.SubtotalPriceAdjustments.Add(adjustment);
    }
}
[Serializable]
public class MyAdjustment : PriceAdjustment<MyAdjustment>
{
    public string MyAdjustmentRef { get; set; }

    // A parameterless constructor is required for cloning
    public MyAdjustment()
        : base()
    { }

    // Additional helper constructors
    public MyAdjustment (string name, string reference, Price adjustment)
        : base(name, adjustment)
    {
        MyAdjustmentRef = reference;
    }
}
public abstract class PriceAdjustment<TSelf> 
{
    public Type Type { get; }
    public string Name { get; }
    public Price Price { get; }
    public Price OriginalPrice { get; }
}
public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyServices(IUmbracoCommerceBuilder builder)
    {
        // Register the price adjuster
        builder.WithPriceAdjusters()
            .Append<MyPriceAdjuster>();

        // Return the builder to continue the chain
        return builder;
    }
}

Product Variants

Creating product variants with Umbraco Commerce.

Product variants are the ability to define variants of a given product. If a product was available in multiple color options, you would create a primary product with product variants for each of the color options.

Out of the box, Umbraco Commerce supports two types of product variant setups.

Child Variants

Child variants are where the product variants are set up as child nodes below the primary product. Generally speaking, this setup is only sustainable for single variant options, where there is only one differing option between the variants.

By using child variants the only thing you need to create is your own variant nodes as you already do in Umbraco.

When a child variant is added Umbraco Commerce checks the primary product node for any properties that can't be found on the variant child node.

Complex variants are where products vary by multiple possible options, such as by size, color, and fit. Complex variants tend to create a lot of variant products which makes the child variants approach impractical.

For complex variants, Umbraco Commerce comes with a variants property editor which will handle a lot of this complexity for you. You can set up a variant element type to use as your data blueprint for your variant products. This can then be linked to the property editor. The variants property editor will use this as the data structure for your variants. You will be presented with the relevant UI to input the product details.

To aid with the setup of the complex variants, Umbraco Commerce has the Product Attributes concept which defines the individual options that make up your product variants. This could be colors, sizes, and fits. Each product attribute is made up of a label and as many values as needed.

Product attributes are used by the complex variants property editor allowing you to select the combinations of product variants you wish to create. It will automatically generate the product variant entries for you, ready for product information updating.

This approach is how most of is set up.

For more information on how you can setup Complex Variants, head to the article.

For more information on how you can setup Product attributes, head to the article.

the official Demo store
Complex Variants
Complex Variants

Payment Providers

Accepting payments via Payment Providers in Umbraco Commerce.

Payment Providers are how Umbraco Commerce is able to accept multiple different methods of payment on a Site. Their job is to provide a standard interface between third-party payment gateways and Umbraco Commerce itself. This is done in order to allow the passing of information between the two platforms.

How the integrations work is often different for each payment gateway. The Umbraco Commerce Payment Providers add a flexible interface that should be able to work with most payment gateways.

Example Payment Provider

An example of a bare-bones Payment Provider would look something like this:

[PaymentProvider("my-payment-provider-alias", "My Payment Provider Name", "My Payment Provider Description")]
public class MyPaymentProvider :  AsyncPaymentProviderBase<MyPaymentProviderSettings>
{
    public MyPaymentProvider(UmbracoCommerceContext umbracoCommerce)
        : base(umbracoCommerce)
    { }

    ...
}

public class MyPaymentProviderSettings
{
    [PaymentProviderSetting(Name = "Continue URL", 
        Description = "The URL to continue to after this provider has done processing. eg: /continue/",
        SortOrder = 100)]
    public string ContinueUrl { get; set; }

    ...
}

All Payment Providers inherit from a base class AsyncPaymentProviderBase<TSettings>. TSettings is the type of a Plan Old Class Object (POCO) model class representing the Payment Providers settings. The class must be decorated with PaymentProviderAttribute which defines the Payment Providers alias, name and description, and can also specify an icon to be displayed in the Umbraco Commerce backoffice.

The settings class consists of a series of properties, each decorated with a PaymentProviderSettingAttribute defining a name, description, and possible angular editor view file. These will all be used to dynamically build an editor interface for the given settings in the backoffice.

Payment Provider Responsibilities

There are two main responsibilities of a Payment Provider, and those are:

  • Payment Capture - Capturing the initial Order payment and finalizing the Order.

  • Payment Management - Managing a payment post Order finalization, such as being able to Capture authorized payments or Refunding captured payments.

Payment Capture

The Payment Capture workflow can be the hardest part of a Payment Provider. This is due to the fact that no two payment gateways are alike. Therefore it can be difficult to figure out how best to implement the gateway into the provider format.

Generally, there are three methods within a Payment Provider that you may need to implement, and each one has a specific responsibility.

  • GenerateForm - The GenerateForm method is responsible for generating an HTML form that will redirect the customer to the given payment gateway payment form. In this method you may need to communicate with the payment gateway in order to initialize a payment, letting the payment gateway know how much to capture. This often results in some kind of code or redirect URL being returned which will need to be embedded into the generated form. The generated form is then usually displayed on a checkout Review page, the last page before payment is captured and will have an implementer-defined Continue to Payment button to submit the form and redirect the customer to the gateway.

  • ProcessCallback - The ProcessCallback method is responsible for handling the response coming back from the payment gateway and processing whether the payment was successful or not. This can sometimes occur synchronously, if the payment gateway sends information back as part of the confirmation page redirect, or can occur asynchronously if the payment gateway sends the information back via an out-of-band webhook request.

  • GetOrderReference - The GetOrderReference method is responsible for extracting an order reference number from a request when the payment gateway uses an asynchronous webhook to finalize an Order and it uses a global webhook URL strategy for all notifications rather than a notification URL per transaction. Where a webhook URL can be passed per transaction, then Umbraco Commerce provides you with a unique callback URL you can register with the gateway that already identifies the order reference as part of the URL parameters, making implementing this method unnecessary.

* denotes a required method implementation.

What follows is a generalized diagram in order to help in visualizing when each of these methods is called within a regular checkout flow.

Payment Management

In addition to the initial payment capture flow, Payment Providers can also be set up to manage the payment post-checkout. This could be Capturing Authorized transactions or Refunding Captured transactions.

These features are optional and not required for Payment Provider developers to implement. They allow store owners to manage payments directly in the backoffice rather than through the payment gateway's portal when performing these types of actions.

The implementable management methods are:

  • FetchPaymentStatus - The FetchPaymentStatus method communicates with the 3rd party payment gateway in order to fetch the current status of the given transaction.

  • CapturePayment - The CapturePayment method communicates with the 3rd party payment gateway to capture a previously authorized payment associated with the given transaction.

  • CancelPayment - The CancelPayment method communicates with the 3rd party payment gateway to cancel a previously authorized payment associated with the given transaction.

  • RefundPayment - The RefundPayment method communicates with the 3rd party payment gateway to refund a previously captured payment associated with the given transaction.

For each implemented method above, developers should also implement a corresponding boolean property returning a true value. This is to let Umbraco Commerce know that the given feature is supported by the Payment Provider.

  • CanFetchPaymentStatus

  • CanCapturePayments

  • CanCancelPayments

  • CanRefundPayments

Payment Provider Meta Data

For all implemented methods of a Payment Provider, all method return types support the returning of additional Meta Data. This is to allow Payment Providers to capture and store relevant information. This information will aid the provider in doing its job, or for storing useful reference information to display for the retailer.

Meta Data is stored in Orders Properties collections. Prefix your Meta Data keys with the Payment Providers alias to prevent possible conflicts.

Meta Data Definitions

The Meta Data that is returned from the Payment Provider is useful for the retailer. The Payment Provider can also be used to display Meta Data descriptions and information in the backoffice. This is done by exposing a TransactionMetaDataDefinitions property consisting of a list of TransactionMetaDataDefinition values. Each of the values defines the alias, name and optional description of a Meta Data entry.

public override IEnumerable<TransactionMetaDataDefinition> TransactionMetaDataDefinitions => new[]{
    new TransactionMetaDataDefinition("stripeSessionId", "Stripe Session ID"),
    new TransactionMetaDataDefinition("stripePaymentIntentId", "Stripe Payment Intent ID"),
    new TransactionMetaDataDefinition("stripeChargeId", "Stripe Charge ID"),
    new TransactionMetaDataDefinition("stripeCardCountry", "Stripe Card Country")
};

Product Bundles

Creating bundles of products with Umbraco Commerce.

Occasionally you may need to create a product with multiple sub-products. A good example of this is when buying a computer where you may pick the computer as the main product. You can then choose the different components to make up the computer, such as the hard disk options. The final order line then becomes the composite order line of the selected primary product and all its sub-product options. To achieve this kind of configurable product in Umbraco Commerce, we can use a feature called product bundling.

Creating a Bundle

To create a bundle, we first add the primary product to an order as we normally would. In addition to the product/quantity information, we also provide a unique bundleId to identify that adding this product should create a bundle order line.

// Define a unique bundle id for the order line
var bundleId = "MyUniqueBundleId";

// Add the primary product to the order giving it a bundle ID
order.AddProduct(productReference, productQuantity, bundleId);

Adding Sub Products to a Bundle

With the primary product added as a bundle, we can then add sub-products to that bundle by calling one of the AddProductToBundle order methods.

// Define a unique bundle id for the order line
var bundleId = "MyUniqueBundleId";

// Add the primary product to the order giving it a bundle ID
order.AddProduct(productReference, productQuantity, bundleId);

// Add a sub product to the bundle by calling a AddProductToBundle method
// passing in the same bundle ID as the primary order line
order.AddProductToBundle(bundleId, subProductReference, subProductQuantity);

Order Line Price Calculation

By adding sub-products to a bundle, Umbraco Commerce knows to automatically sum up all the sub-product prices together. It will then add them to the unit price of the primary order line for you. This means that there is nothing extra you need to do in the calculation process.

Displaying Bundles in the Back-Office

As you can imagine, product bundles could get rather large making it a little difficult to display them in the backoffice. Umbraco Commerce bundles order lines together in a collapsible user interface. This gives you a clear view of your orders whilst still being able to drill into the detail of the items purchased.

Payment Provider Capture Workflow

Any returned Meta Data from a Payment Provider method will be stored against the Order in its collection. Should you need to retrieve these values from other areas of the Payment Provider, you can use the passed-in Orders Properties collection.

Transaction Meta Data
Product bundles in the backoffice
Properties

Price Freezing

Freezing prices for shopping carts in Umbraco Commerce.

Price Freezing in Umbraco Commerce is the ability to freeze prices for products that are added to the shopping cart. Umbraco Commerce takes a snapshot of a product's price once it's added to the shopping card. This is done in order to ensure the price is honored for the life of the shopping cart. This process prevents a customer's shopping cart from suddenly changing in value should a price change occur whilst their cart session is in progress.

A product's price is frozen from the point it is added to the current Order, and only for the current Currency of the Order. Should the Customer change the Currency of their Order, then a new snapshot of the product price will be taken for that Currency.

Controlling Price Freezing

There are times when you may wish to control when a frozen price should expire. This could be if a product was incorrectly priced, or if you have rules on how long an Order-session is allowed to maintain price.

On these occasions, you can force frozen prices to expire by using the IPriceFreezerService and its ThawPrices method.

All frozen prices have an OrderId property and a Key that uniquely identifies them. For product prices, this key consists of a generated token of the following format {StoreId}_{OrderId}_{ProductReference}. In addition, the product prices Currency, and date of the freeze are also tracked. It is important to know these details as we can use all of these attributes to target which prices we wish to thaw.

For example, to thaw all prices for a product with the reference c0296b75-1764-4f62-b59c-7005c2348fdd we could call:

_priceFreezerService.ThawPrices(partialKey: "c0296b75-1764-4f62-b59c-7005c2348fdd");

Or to thaw all prices for a given Currency that are greater than 30 days old we could call:

_priceFreezerService.ThawPrices(currencyId: currency.Id, olderThan: DateTime.Now.AddDays(-30));

Search Specifications

Learn more about the flexible search functionaities in Umbraco Commerce.

Providing a search API for developers to be able to search for entities that match given criteria is a bit of a balancing act. You want to provide a flexible API to allow for meaningful results to be returned but at the same time, you don't want to allow every possible search combination as this can lead to performance problems.

The way we have addressed this is by using the Specification pattern.

Specifications

Specifications are a programming design pattern that allows you to encapsulate business rules in blocks that can be chained together to define boolean logic.

What this means is that we can provide a series of specifications for the types of queries we are able to support in a performant way and allow developers to chain these together in whatever combination they require in order to create dynamic filters for entity searches.

Searching

To perform a search using specifications you'll need to use one of the search methods on the given entity service that accepts a Func<IEntityQuerySpecificationFactory, IQuerySpecification<Entity>> parameter. This parameter type might look complex, but its use should be pretty straightforward thanks to the use of delegates.

To use one of the search methods, the implementation will look something like the following:

var results = _orderService.SearchOrders(
    (where) => where
        .FromStore(storeId)
        .And(where.HasOrderNumber(orderNumber).Or(where.ByCustomer(customerEmail))))

The above is an example, but it demonstrates the use of a delegate method that then uses a fluent specifications API to build up a query filter. The query filter itself can be made up of many different individual queries which themselves can be grouped using AND and OR query logic.

Because the API is fluent it is also self-documenting, with Visual Studio intellisense able to guide developers through all the available specifications.

Ordering Results

Alongside the query specifications documented above, we also have to sort specifications that allow a similar fluent API for defining the order in which results are returned. These are passed in a similar way to the search methods as demonstrated below.

var results = _orderService.SearchOrders(
    (where) => where
        .FromStore(storeId)
        .And(where.HasOrderNumber(orderNumber).Or(where.ByCustomer(customerEmail))),
    (orderBy) => orderBy
        .FinalizedDate(Sort.Descending)
        .Then(orderBy.CreateDate(Sort.Descending)))

Product Adapters

Converting product sources into understandable products for Umbraco Commerce.

The role of a Product Adapter in Umbraco Commerce is to provide an interface between a product information source and convert it into a standardized format. This is done to prevent the need for Umbraco Commerce to be tied to that source.

What this means for developers is that Product Adapters allow you to hook in alternative product information sources that may not be Umbraco node based. You may hold your product information in a third-party database table. A custom Product Adapter would then allow Umbraco Commerce to interface with that custom data in the same way it would the default Umbraco node data.

Example Product Adapter

An example of a Product Adapter would look something like this:

public class MyCustomProductAdapter : IProductAdapter
{
    public IProductSnapshot GetProductSnapshot(string productReference, string languageIsoCode)
    {
        // Lookup a product by productReference and convert to IProductSnapshot
    }

    public IProductSnapshot GetProductSnapshot(string productReference, string productVariantReference, string languageIsoCode)
    {
        // Lookup a product by productVariantReference and convert to IProductSnapshot
    }

    public bool TryGetProductReference(Guid storeId, string sku, out string productReference, out string productVariantReference)
    {
        // Try lookup a product / variant reference by store + sku
    }
}

All Product Adapters implement the IProductAdapter interface which requires three method implementations:

  • Two GetProductSnapshot methods that retrieve a Product Snapshot for either a product or product variant by reference parameters.

  • A TryGetProductReference method which retrieves a product/variant reference for a product that belongs to a given storeId and has the given sku.

A Product Snapshot consists of the following properties in order to present a Product to Umbraco Commerce in a standard way.

public interface IProductSnapshot
{
    // The unique reference for the product
    string ProductReference { get; }

    // The unique reference for the variant (if this is a variant snapshot)
    string ProductVariantReference { get; }

    // The unique SKU for this product/variant
    string Sku { get; }

    // The name of this product/variant
    string Name { get; }

    // The ID of the store this product/variant belongs to
    Guid StoreId { get; }

    // An optional Tax Class ID for this product/variant
    Guid? TaxClassId { get; }

    // Any properties exposed by this product/variant that should be copied to the orderline
    IDictionary<string, string> Properties { get; }

    // Any variant attributes for this product (if this is a variant snapshot)
    IEnumerable<AttributeCombination> Attributes { get; }

    // The available prices for this product/variant
    IEnumerable<ProductPrice> Prices { get; }

    // Flag indicating whether this product is a gift card product
    bool IsGiftCard { get; }
}

Support editable carts

To allow Umbraco Commerce to search for products/variants to add to a cart via the backoffice, Product Adapters can implement 3 additional methods. This can also be done to support editable carts.

public class MyCustomProductAdapter : ProductAdapterBase
{
    ... 

    public override PagedResult<IProductSummary> SearchProductSummaries(Guid storeId, string languageIsoCode, string searchTerm, long currentPage = 1, long itemsPerPage = 50)
    {
        // Search for products matching the given search term and convert to a IProductSummary
    }

    public override IEnumerable<Attribute> GetProductVariantAttributes(Guid storeId, string productReference, string languageIsoCode)
    {
        // Lookup the in-use product attributes of a primary product
    }

    public override PagedResult<IProductVariantSummary> SearchProductVariantSummaries(Guid storeId, string productReference, string languageIsoCode, string searchTerm, IDictionary<string, IEnumerable<string>> attributes, long currentPage = 1, long itemsPerPage = 50)
    {
        // Search for product variants matching the given search term and/or the given attributes and convert to a IProductVariantSummary
    }
}

The IProductSummary, Attribute and IProductVariantSummary consists of the following properties in order to present a Product to Umbraco Commerce in a standard way.

public interface IProductSnapshot
{
    // The unique reference for the product
    string Reference { get; }

    // The unique SKU for this product 
    string Sku { get; }

    // The name of this product 
    string Name { get; }

    // The available prices for this product 
    IEnumerable<ProductPrice> Prices { get; }

    // Flag indicating whether this product has variants
    bool HasVariants { get; }
}

public class Attribute 
{
    // The alias of the attribute
    public string Alias { get; }

    // The name of the attribute
    public string Name { get; }

    // The attribute values
    IEnumerable<AttributeValue> Values { get; }
}

public class AttributeValue
{
    // The alias of the attribute value
    public string Alias { get; }

    // The name of the attribute value
    public string Name { get; }

}

public interface IProductVariantSnapshot
{
    // The unique reference for the product variant
    string Reference { get; }

    // The unique SKU for this product variant
    string Sku { get; }

    // The name of this product variant
    string Name { get; }

    // The available prices for this product variant
    IEnumerable<ProductPrice> Prices { get; }

    // The collection of attribute alias pairs of this product variant
    IReadOnlyDictionary<string, string> Attributes { get; }
}

Registering a Product Adapter

public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyServices(IUmbracoCommerceBuilder builder)
    {
        // Replacing the default Product Adapter implementation
        builder.Services.AddUnique<IProductAdapter, MyCustomProductAdapter>();

        // Return the builder to continue the chain
        return builder;
    }
}

typeProduct Adapters are interface using the AddUnique<IProductAdapter, TReplacementAdapter>() method on the Services property. The TReplacementAdapter parameter is the type of our custom Product Adapter implementation.

registered via the IUmbracoCommerceBuilder

ReadOnly and Writable Entities

Great performance and simplified change tracking using ReadOnly and Writable entities in Umbraco Commerce.

When working with the Umbraco Commerce entities, it's important to know that all entities come in two states, ReadOnly and Writable. By default, all Umbraco Commerce API methods will return entities in their ReadOnly state. This means that when you are accessing Umbraco Commerce entities directly from an API endpoint you are able to read and iterate over its properties. You won't, however, be able to make changes to that entity without first converting it into its Writable state.

Why have ReadOnly and Writable entities?

The reason why we have split entities in this way for a number of reasons, however, the two primary factors are:

  • Making APIs fast by default - By returning ReadOnly entities by default we can ensure all API methods are as fast as possible by feeding values directly out of our caching layer. Because the entities can't change it means we don't have to laden the entities with extra change tracking logic, we can feed out the cached values directly and only worry about that logic when the entities become Writable.

  • Simplified change tracking - When we convert a ReadOnly entity to its writable state, internally we take a deep clone of that state so that changes can occur within a scoped "sandbox". At the same time, we retain a copy of the original state meaning when it comes time to persist those changes we have two copies of the state we can perform a comparison on, simplifying the whole change tracking process.

Converting a ReadOnly entity into a Writable entity

To convert a ReadOnly entity into its Writable form, we achieve this by calling the entities AsWritable(uow) method, passing in a valid Unit of Work instance to perform the write operations on. Once we have a Writable entity, we can then perform the write operations we desire and persist those changes back to the database.

_uowProvider.Execute(uow =>
{
    // Fetch the currency
    var currency = _currencyService.GetCurrency(currencyId);

    // Convert the currency into it's Writable form
    var writableCurrency = currency.AsWritable(uow);

    // Peform our write operation
    writableCurrency.SetName("New Name");

    // Persist the changes to the database
    _currencyService.SaveCurrency(currency);

    // Close our transaction
    uow.Complete();
});

All write operations must occur within a Unit of Work so by passing in a Unit of Work instance into the entities AsWritable method, we are ensuring that you are in fact within an active Unit of Work.

Properties

Order and Order Line metadata in Umbraco Commerce.

There is little information that Umbraco Commerce needs to know about a product in order for it to do its job. There are, however, times when developers require the ability to store additional information against an Order or Order Line. This could be the billing/shipping address of an Order, or any specific configuration details of a given Product on an Order Line.

To help facilitate this Umbraco Commerce has the concept of a Properties collection on both the Order entity and the Order Line entity respectively. The Properties collection of these entities can be thought of as a general store for additional information required by an implementation, but not strictly required by Umbraco Commerce itself.

Anything you need to remember about an Order / Order Line can be stored in its Properties collection.

Setting Properties

Property values can either be a string, or a Umbraco Commerce PropertyValue which allows you to define a value as being Server Side Only. This means that it won't be returned via non-server APIs or Read Only meaning it can't be updated once set.

System Properties

On occasions where Umbraco Commerce needs to capture some information about an Order or Order Line, it uses the Properties collection to store this information. It's useful to know what these properties are as you should avoid using these system-related property keys.

Order System Properties

Order Line System Properties

Automatic Properties

Umbraco Commerce has a built-in mechanism that can be configured to automatically copy properties from a Product information source to the Order Line automatically. This is done by using the Product Property Aliases field on the Store settings screen.

When a Product is added to the Order containing a comma-separated list of property aliases, the property values are automatically copied to the Order Lines Properties collection.

This is useful for occasions such as rendering out the Order Lines on a Cart page and you have Product information you want to display. By copying it to the Order Lines Properties collection, you have instant access to those properties without the need to re-fetch the original Product entity.

Product Uniqueness Properties

Another use of the Properties collection for an Order Line is that of identifying product "Uniqueness".

Umbraco Commerce uses Product Uniqueness to identify either of the two:

  • Whether a Product is added to a Cart should be considered as a Quantity increase on an existing Order Line

  • Whether it should be considered as a unique product combination and so should be given an Order Line of its own.

A good example of this is when you have configurable products, such as customizable T-Shirt designs. In this case, each unique configuration should be considered as its own Order Line so that you can manage the specific configurations.

Product uniqueness is configured via the Product Uniqueness Property Aliases field on the Store setting screen.

When set to a comma-separated list of property aliases and a Product is added to an Order, the properties are compared against all pre-existing Order Lines for that Product. Should their values be different, then a unique Order Line will be created for that Product.

To set a Property on an Order or Order Line, it needs to be . Then it's a case of calling one of the related property setting methods:

Alias
Description
Alias
Description
// Set a single property
order.SetProperty("propertyAlias", "Property Value");

// Set multiple properties at once
order.SetProperties(new Dictionary<string, string>{
    { "propertyAlias1", "Property Value 1" },
    { "propertyAlias2", "Property Value 2" },
    { "propertyAlias3", "Property Value 3" }
})

// Remove a property
order.RemoveProperty("propertyAlias");
// Set a string property
order.SetProperty("propertyAlias", "Property Value");

// Set a PropertyValue property as Read Only
order.SetProperty("propertyAlias", new PropertyValue("Property Value", isReadOnly: true));

email

The email address of the person placing the order. Is where order.CustomerInfo.Email reads it's value from.

firstName

The first name of the person placing the order. Is where order.CustomerInfo.FirstName reads it's value from.

lastName

The last name of the person placing the order. Is where order.CustomerInfo.LastName reads it's value from.

Product Property Aliases Configuration
Product Uniqueness Property Aliases Configuration
in its Writable state
Product Attributes
Complex Variants

sku

Settings Objects

Strongly typed Settings objects in Umbraco Commerce.

There are places in Umbraco Commerce where you can use Settings Objects to pass configuration to a Provider, such as Discount Rule Providers, Reward Providers, and Payment Providers.

The settings objects have a number of responsibilities.

  • Typed Settings Model - The type represents a strongly typed settings model the given Provider accepts. Any stored settings in the database will be deserialized to this type before being passed to the Provider for processing. This provides strongly typed access to the relevant configuration settings.

  • JavaScript Settings Model - The settings object also defines the JavaScript settings model passed to the Provider editor UI, using either the settings Property name as the object property key, or using the key property of the Setting Attribute declared on the given Property.

UI Scaffolding

An important element of the Settings object is UI Scaffolding. UI Scaffolding is where Umbraco Commerce reads a series of Settings Attributes defined on your Settings object properties in order to dynamically build a User Interface for that Providers settings.

An example of a Discount Reward Settings Object might look something like this:

public class MyDiscountRewardProviderSettings
{
    [DiscountRewardProviderSetting(Key = "nodeId",
        Name = "Product Node",
        Description = "The product to discount the price of",
        View = "contentpicker",
        Config = "{ startNodeId: -1, multiPicker: false, idType: 'udi' }")]
    public Udi NodeId { get; set; }

    ...
}

Attributes define a property key, name, description to display in the UI as well as an optional view and config option to define the Umbraco property editor to use to edit the given property. If no view is defined, one will attempt to automatically be chosen based on the properties value type.

An example of a generated UI built from these properties would look something like this:

Default Values

To define default values for a settings object, you can assign a value to a property in your model and Umbraco Commerce will automatically fall back to that value if no explicit value is defined.

public class MyDiscountRewardProviderSettings
{
    [DiscountRewardProviderSetting(Key = "title", Name = "Title", Description = "A friendly title for this item"]
    public string Title { get; set; } = "Untitled";

    ...
}

Complex Variants

Creating complex variants with Umbraco Commerce.

The Commerce Complex Variants feature is powered by a new Variants Editor property editor that you can attach to your product content nodes. The editor itself is based on the Umbraco Block List editor format so under the hood we make use of the new data structure.

We also make use of Umbraco’s block editor APIs. You can add supporting data needed to record against your variants simply by defining a document type and linking it with the editor. By basing the editor on the block editor data structure, we can take advantage of improvements made in Umbraco Commerce. An example is optimized persistence/searching.

The Variants Editor isn’t just a regular property editor. Managing variant data is a complex task and having variants mingled in with the product content fields would be distracting. So a bit of Umbraco magic is used to allow the editor to render itself as a content app. By doing this it gives a focused tab on which to manage complex-variants and allows to create a much richer content management experience.

All you have to do is add the variants editor as a property on your product Document Type and Umbraco Commerce hooks up the rest.

Product Attributes

Before you can go creating variants, there is another concept that you need to understand and that is product attributes.

Product attributes are essentially lists of options that can be used to create your variant combinations. For example, things like color, size, or fit if you were selling clothing.

Each product attribute consists of a name and a series of attribute values for the given options. So for example, color attribute might have a series of values like red, and blue, and size might have values of large, medium, and small.

In order to manage these product attributes, we’ve created a new Options node inside the Commerce section, beneath each store. From this section, you can define as many product attributes + values as you need. If you're working with a multi-lingual setup, you can provide label translations.

Product Attribute Presets

Linked with product attributes, there is also the concept of product attribute presets.

What product attribute presets do is allow to define groups of product attributes/values based on a specific theme. Then they are displayed at the point of product variant creation. This is where you can choose from a smaller, focused list of product attributes than if you were just presented with every possible option.

Creating Variants

With the product attributes defined (and optional product attribute presets), and the variants editor defined (on product Document Type), you can start creating product variants.

With the product node open, you’ll now see the new Variants content app in the top right corner. From there you can click the Create Product Variants button to launch the create dialog.

From the create dialog, you’ll be presented with a list of product attributes so that you can select all the combinations you want to create variants. If you have setup any product attribute presets, these will be presented first.

Selecting a preset will show a smaller list of product attributes/values to choose from. To create the variants, check the checkbox against the attribute values and click Select. Then rows will be automatically created in the variants table for every combination of the selected attributes.

Managing Variants

With variants defined in the variants table, you can manage the content for each variant by clicking the SKU of the row. Then it will launch the content editor for that variant. From here you’ll be presented with all the fields defined on your variants Document Type and can add and save the information required.

In the variants table view, we've also added filtering features so you can filter by attribute values. You can also search for specific variant SKUs to easily locate items. Additionally, the table also supports sorting on the table columns, so you can also order the results as you need.

You can change a variant attribute combination at any time by clicking the cog icon on the row. Then you can select a new combination. Lastly, you can remove a variant by clicking the trash can icon against the row.

Variant Fields

Whilst you are free to add any fields you like to your variant, there are a few fields that you might want/need to add.

The only required field is an SKU field with the alias sku. All the following fields are optional, with Umbraco Commerce falling back to the parent node if one isn’t found.

  • Price [price] - The fixed price of the product variant. Should use a Commerce Price input field.

  • Price Adjustment [priceAdjustment] - Used instead of a price field to create a dynamic price by adding the adjustment amount to the parent product price. Should use a Commerce Price input field and can contain negative values.

  • Stock [stock]- Allows for individual variant stock management.

Value Converter

Once the variants are defined, you’ll then want to be able to access that data on the frontend. To do this the variants editor comes with a built-in value converter. This allows you to access a strongly typed collection of all the defined variants from the parent product node.

The property value will be of type ProductVariantCollection that contains a series of ProductVariantItem entities. Each of these entities has a Config property which contains details of the product attribute combination for the variant. It also contains a content property of type IPublishedElement from which you can access the variant's data. The Content property can also be cast to a models builder model type for strongly typed access to the variant content too.

public class ProductVariantItem
{
    public Udi ContentUdi { get; }
    public IPublishedElement Content { get; }
    public ProductVariantConfig Config { get; }
}
public class ProductVariantConfig
{
    public IDictionary<string, string> Attributes { get; set; }
}

Umbraco Commerce also ships with a helper extension method on the ProductVariantCollection class, GetInUseProductAttributes(storeId).This provides a convenient way to get a list of all attributes + values used by the variants collection. It comes in handy when rendering out the list of options on the front end, ensuring only attributes in use are displayed.

public class InUseProductAttribute
{
    public string Alias { get; }
    public ReadOnlyTranslatedValue<string> Name { get; }
    public IReadOnlyCollection<InUseProductAttributeValue> Values { get; }
}
public class InUseProductAttributeValue
{
    public string Alias { get; }
    public ReadOnlyTranslatedValue<string> Name { get; }
}
public class ReadOnlyTranslatedValue<T>
{
    public T GetDefaultValue()
    public bool HasValue(string languageIsoCode)
    public T GetValue(string languageIsoCode, bool fallbackToDefault = true)
    public bool TryGetValue(string languageIsoCode, out T value)
    public T this[string languageIsoCode]
}

API Updates

The last piece of the complex variants puzzle is a few updates to Umbraco Commerce's API.

This is largely around the AddProduct methods on the Order entity which now have additional signatures. These signatures take both a productReference and a productVariantReference which must both be supplied when adding a variant item to an order.

Order lines have also been updated to expose a new Attribute property. This provides a collection of attribute combinations for the order lines product so that these can be rendered on carts and checkouts. The "uniqueness" logic for an order line has also been updated to take these attributes into account.

With both of these changes, updates have also been made to the IProductAdapter/IProductSnapshot interfaces and built-in implementations. This is in order to support product variants and attributes, as it has the product and price freezer services.

The built-in stock property editor has also had a slight overhaul in order to support both regular products and product variants. If anyone needs to provide an alternative implementation, a new IStockService interface has been created as well.

The SKU of the product, extracted from the product node via the .

UI Scaffold - The settings object defines metadata on its properties via an Attribute implementing UmbracoCommerceSettingAttribute, each Provider type has its own attribute type in case they require additional config, for example DiscountRewardProviderSettingAttribute, DiscountRuleProviderSettingAttribute or PaymentProviderSettingAttribute. The attributes are used to dynamically build the AngularJS-based UI for the given Provider configuration. See the section below for more information on UI Scaffolding.

Discount Rule UI
Variants editor table view
Product attributes
Product attributes values
Product attribute presets
Product attribute preset values
Create variants presets
Create variants attributes
Edit variant
Variant filtering
Product Adapter
UI Scaffolding

Umbraco Properties

Key Umbraco node properties used by Umbraco Commerce.

Umbraco Commerce uses Umbraco nodes as its source of information. In order for Umbraco Commerce to gather the information it needs, it requires that a number of properties are defined at different locations. These properties must have specific property aliases.

Properties

Alias
Type
Description

store

Umbraco.Commerce.StorePicker

Often placed on the site root node, but can be placed on any node higher than the product nodes themselves, this property links the website to a specific Umbraco Commerce store configuration.

productName

Textstring

Optional product node property that allows you to define an explicit product name other than the product nodes .Name property, which will be used as fallback.

sku

Textstring

Product node property defining the unique SKU of the product.

price

Umbraco.Commerce.Price

Product node property defining the prices for the product.

stock

Umbraco.Commerce.Stock

Product node property defining the stock level of the product.

taxClass

Umbraco.Commerce.StoreEntityPicker

Optional product node property that allows you to define an explicit Tax Class for the product, should it differ from the stores default.

isGiftCard

True/False

Optional product node property that defined whether the product node should be considered a Gift Card product, in which case it triggers the automatic generation of a Gift Card in the backoffice and emails it directly to the customer on checkout.

productSource

ContentPicker

Optional product node property allowing you to link a product to another product outside of it's hierarchy to be used as it's source of product information.

Overview

Step-by-Step Tutorials on getting started with Umbraco Commerce.

In this section, we will provide a series of follow-along tutorials on how to implement Umbraco Commerce. These will be aimed at getting you set up and running without going into heavy theoretical details. Once you are familiar with working with Umbraco Commerce, you can use the other sections of the documentation to gain a more in-depth knowledge of the different things you'll have implemented.

Tax Sources

Identifying the source of taxation of an Order within Umbraco Commerce.

A Tax Source identifies which geographic location an Order should use in order to calculate its tax liability. Depending on the country that the web store is operating in and the country, an order is being purchased from/shipping to, this can dictate how your taxes should be calculated.

To aid with this Umbraco Commerce allows the Tax Source of a Store to be configured via the implementation of a Tax Source Factory. The Tax Source Factory is responsible for determining the source of Tax given the billing and shipping country of an Order.

Out of the box, Umbraco Commerce comes with two Tax Source Factory implementations:

  • DestinationTaxSourceFactory - (Default) Sets the Tax Source as being the destination country where an Order will be shipped to.

  • OriginTaxSourceFactory - Sets the Tax Source as being the origin country where an Order was billed to.

Changing the Tax Source Factory

Umbraco Commerce Builder

Learn more about the different options for configured Umbraco Commerce.

When it comes to configuring and extending Umbraco Commerce, such as by registering your own event handlers, we achieve this with the IUmbracoCommerceBuilder interface that can be accessed via a delegate function passed into the AddUmbracoCommerce() extension method called on the IUmbracoBuilder interface when explicitly registering Umbraco Commerce.

Registering Dependencies

Unit of Work

Transactional updates using the Unit of Work pattern in Umbraco Commerce.

Unit of Work

Creating a Unit of Work

Once you have access to either of these entry points, you can define a Unit of Work as follows

The anatomy of a Unit of Work is an Execute method call on the IUnitOfWorkProvider instance which accepts a delegate function with a uow argument. Inside the delegate, we perform our tasks and confirm the Unit of Work as complete by calling uow.Complete(). If we fail to call uow.Complete() either due to forgetting to add the uow.Complete() call or due to an exception in our code, then any write operations that occur within that code block will not be persisted in the database.

Unit of Work Best Practice

When using a Unit of Work it is best practice that you should perform all write operations inside a single Unit of Work and not create individual Units of Work per write operation.

Perform all write operations in a single Unit of Work

It is not recommended to create a Unit of Work per write operation.

UI Config Files

Customizing the UI in Umbraco Commerce.

With Umbraco Commerce, there are minimal rules about what information you are required to record about an Order, however, this does pose a problem for how we provide a User Interface for managing carts and orders when we don't know exactly what properties you are going to be recording.

Supported UI Config Files

The configuration files supported by Umbraco Commerce are.

  • cart.list.config.json - Cart list view configuration.

  • cart.editor.config.json - Cart editor view configuration.

  • order.list.config.json - Order list view configuration.

  • order.editor.config.json - Order editor view configuration.

If there are no cart config files defined, then Umbraco Commerce will fall back to the order config files.

Assigning a UI Config File to a Store

To assign a UI config file to a Store, this is done by file name convention. This is where configs are looked for in App_Plugins/UmbracoCommerce/config with the following file name format {storeAlias}.{entityType}.{viewType}.config.json. If no store-specific file is found, it will fallback into the default {entityType}.{viewType}.config.json.

Cart/Order List Config Files

With these configuration files, you can customize the columns displayed in the Cart/Order list view.

Example Cart/Order List Config File

The following properties are supported:

Properties configured to display in the list view will appear in order, after the cart name column.

Cart/Order Editor Config Files

With these configuration files, you can customize the Cart/Order Editor interface to suit your particular needs.

Example Cart/Order Editor Config File

An example of an Order Editor config file would look something like this:

The Cart/Order Editor config file is broken up into a series of sections, each of which relates to a particular section of the Cart/Order Editor User Interface.

Order Line Config Options

The Order Line config block configures which Order Line properties should be viewable and/or manageable within the Cart/Order Editor UI. For each Order Line Property, you can provide the following options:

Properties configured to display in the Order Line Summary will be displayed inline next to the "Order Lines SKU" as follows:

Where there are editable Order Line Properties for an Order Line, a pencil icon is displayed next to the Order Lines Product name which when clicked will open out the Order Line Property editor interface for that Order Line.

Customer Config Options

The Customer config block configures which Cart/Order properties relate to a Cart/Orders customer information. The following properties are supported.

Any missing property definition in this config block will disable that property from displaying/being editable.

For each property, the following config options are available:

A fully configured Customer config block will produce a Customer summary like so:

Clicking the Customer Details Edit button will display an editing interface like so:

Billing Config Options

The Billing config block configures which Cart/Order properties relate to a Cart/Orders billing information. The following properties are supported.

In addition to these properties, the order.PaymentInfo Country/Region will be associated with the billing address.

Any missing property definition in this config block will disable that property from displaying/being editable.

For each property, the following config options are available:

A fully configured Billing config block will produce a Billing Address summary like so:

Clicking the Customer Details Edit button will display an editing interface like so:

Shipping Config Options

The Shipping config block configures which Cart/Order properties relate to a Cart/Orders shipping information. The following properties are supported.

In addition to these properties, the order.ShippingInfo Country/Region will be associated with the shipping address.

Any missing property definition in this config block will disable that property from displaying/being editable.

For each property, except enabled or sameAsBilling, the following config options are available:

For the sameAsBilling property, the following config options are available:

A fully configured Shipping config block, where the sameAsBilling property is false, will produce a Shipping Address summary like so:

Where as, a fully configured Shipping config block, where the sameAsBilling property is true, will produce a Shipping Address summary like so:

Clicking the Customer Details Edit button will display an editing interface like so:

If the sameAsBilling toggle switch is toggled, the appropriate Cart/Order property will be toggled between the configured Properties true/false values, and the editor interface will be collapsed like so:

Notes Config Options

The Notes config block configures which Cart/Order properties relate to a Cart/Orders note information. The following properties are supported.

Any missing property definition in this config block will disable that property from displaying/being editable.

For each property the following config options are available:

A fully configured Notes config block will produce an editor interface like so:

Additional Info Config Options

The Additional Info config block configures any other Cart/Order properties you wish to display in the Cart/Order editor interface in the Additional Info section. For each Order Property to display you can provide the following options:

A fully configured Additional Info config block will produce an Additional Info summary interface like so:

Clicking the Additional Info Edit button will display an editing interface like so:

Custom Cart/Order Editor View

If you wish to entirely replace the Cart/Order Editor view with a custom implementation you can create a Cart/Order Editor Config file with a single view config option which points to the path of an alternative AngularJS view file to use for editing the Order.

Tax Source Factories are interface using the AddUnique<ITaxSourceFactory, TReplacementTaxSourceFactory>() method on the Services property where the TReplacementTaxSourceFactory parameter is the type of your replacement Tax Source Factory implementation.

The IUmbracoCommerceBuilder interface gives you access to the current IServiceCollection and IConfiguration to allow you to register dependencies like you would with the but its primary use case would be to access Umbraco Commerce's own collection builders, such as for registering validation or notification events, and any other Umbraco Commerce-specific configuration APIs.

As per the , whilst you can register your dependencies directly within this configuration delegate, you may prefer to group your dependencies registration code into an extension method.

When working with Umbraco Commerce's API it is important that data integrity is maintained should any errors occur. In order to achieve this Umbraco Commerce uses the to effectively create a transaction that wraps around sections of your code ensuring that all Umbraco Commerce write operations that occur within that code block must succeed and be persisted in their entirety, otherwise, none of them should, and the database should rollback to its state prior to when those changes were made.

Creating a unit of work will require access to Umbraco Commerce's IUnitOfWorkProvider which can be , or can also be accessed via the UoW property on the IUmbraco CommerceApi helper.

In order to allow this flexibility whilst still providing the ability to view and manage carts and orders in the backoffice, Umbraco Commerce supports a number of UI config files to map to its UI elements.

Name
Description
Name
Description
Key
Description
Name
Description
Key
Description
Name
Description
Key
Description
Name
Description
Name
Description
Key
Description
Name
Description
Name
Description
public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyServices(IUmbracoCommerceBuilder builder)
    {
        // Replacing the default Tax Source Factory implementation
        builder.Services.AddUnique<ITaxSourceFactory, OriginTaxSourceFactory>();

        // Return the builder to continue the chain
        return builder;
    }
}
public class Startup
{
    ...
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddUmbraco(_env, _config)
            .AddBackOffice()
            .AddWebsite()
            .AddUmbracoCommerce(umbracoCommerceBuilder => {
                // Configure Umbraco Commerce here
            })
            .AddComposers()
            .Build();
    }
    ...
}
...
.AddUmbracoCommerce(umbracoCommerceBuilder => {
    
    // Register validation events
    umbracoCommerceBuilder.WithValidationEvent<ValidateOrderProductAdd>()
            .RegisterHandler<MyOrderProductAddValidationHandler>();

})
...
public static class UmbracoCommerceUmbracoBuilderExtensions
{
    public static IUmbracoCommerceBuilder AddMyDependencies(this IUmbracoCommerceBuilder builder)
    {
        // Register my dependencies here via the builder parameter
        ...

        // Return the builder to continue the chain
        return builder;
    }
}
...
.AddUmbracoCommerce(umbracoCommerceBuilder => {
    
    umbracoCommerceBuilder.AddMyDependencies();

})
...
_uowProvider.Execute(uow =>
{
    // Perform your write operations here

    uow.Complete();
});
_uowProvider.Execute(uow =>
{
    // Create a Country
    var country = Country.Create(uow, storeId, "DK", "Denmark");

    _countryService.Save(country);

    // Create a Currency
    var currency = Currency.Create(uow, storeId, "DKK", "Danish Kroner", "da-DK");

    _currencyService.Save(currency);

    uow.Complete();
});
_uowProvider.Execute(uow =>
{
    // Create a Country
    var country = Country.Create(uow, storeId, "DK", "Denmark");

    _countryService.Save(country);

    uow.Complete();
});

_uowProvider.Execute(uow =>
{
    // Create a Currency
    var currency = Currency.Create(uow, storeId, "DKK", "Danish Kroner", "da-DK");

    _currencyService.Save(currency);

    uow.Complete();
});
{
    properties: [
        { alias: "color", header: "Color", placeholder: "Undefined" },
        { alias: "size", header: "Size", placeholder: "Undefined", align: "right" }
    ]
}

alias

The alias of the Order property to use

label

A label to display as the table column header

align

Sets the alignment of the column. Can be left (default), center or right

placeholder

The placeholder value to display if there is now Order property value

template

An angular template to use for rendering the property value. Defaults to {{ properties['alias'].value }}

{
    orderLine: {
        properties: [
            { alias: "color", label: "Color", isReadOnly: true, showInOrderLineSummary: true },
            { alias: "size", label: "Size", isReadOnly: true }
        ]
    },
    customer: {
        // Firstname, Lastname and Email are already known
        company: { alias: "company", label: "Company Name" },
        taxCode: { alias: "taxCode", label: "Tax Code" },
        telephone: { alias: "telephone", label: "Telephone" },
    },
    billing: {
        addressLine1: { alias: "billingAddressLine1", label: "Street Address Line 1" },
        addressLine2: { alias: "billingAddressLine2", label: "Street Address Line 2" },
        city: { alias: "billingCity", label: "City" },
        zipCode: { alias: "billingZipCode", label: "Zip Code" },
        telephone: { alias: "billingTelephone", label: "Telephone" },
        // Country and Region are already known
    },
    shipping: {
        enabled: true,
        sameAsBilling: { alias: "shippingSameAsBilling", label: "Same as billing", trueValue: "1", falseValue: "0" },
        firstName: { alias: "shippingFirstName", label: "First Name" },
        lastName: { alias: "shippingLastName", label: "Last Name" },
        addressLine1: { alias: "shippingAddressLine1", label: "Street Address Line 1" },
        addressLine2: { alias: "shippingAddressLine2", label: "Street Address Line 2" },
        city: { alias: "shippingCity", label: "City" },
        zipCode: { alias: "shippingZipCode", label: "Zip Code" },
        telephone: { alias: "shippingTelephone", label: "Telephone" },
        // Country and Region are already known
    },
    notes: {
        customerNotes: { alias: "comments", label: "Customer Comments" },
        internalNotes: { alias: "notes", label: "Internal Notes" }
    },
    additionalInfo: [
        { alias: "ipAddress", label: "IP Address", isReadOnly: true }
    ]
}

alias

The alias of the Order Line property

label

A friendly label to display for this property in the editor interface

description

A friendly description to display for this property in the editor interface

template

An angular template to use for rendering the property value. Defaults to {{ properties['alias'].value }}

isReadOnly

Sets the property as read only and so doesn't provide a means of editing the value in the editor interface (Default: false)

showInOrderLineSummary

Sets whether to display this property in the Order Lines summary next to the SKU in the Order editor interface (Default: true)

view

Sets the name or path of a Property Editor to use when editing this property

config

Defines a JSON config for the Property Editor if required

firstName

The customers first name. Uses the order.CustomerInfo.FirstName system property

lastName

The customers last name. Uses the order.CustomerInfo.LastName system property

email

The customers email address. Uses the order.CustomerInfo.Email system property

company

The company the customer works for

taxCode

The tax code of the company the customer works for

telephone

The contact telephone number of the customer

alias

The alias of the Order property to use

label

A friendly label to display for this property in the editor interface

description

A friendly description to display for this property in the editor interface

view

Sets the name or path of a Property Editor to use when editing this property

config

Defines a JSON config for the Property Editor if required

addressLine1

Line 1 of the billing address

addressLine2

Line 2 of the billing address

city

The City of the billing address

zipCode

The Zip/Postal Code of the billing address

telephone

The telephone number of the billing address

alias

The alias of the Order property to use

label

A friendly label to display for this property in the editor interface

description

A friendly description to display for this property in the editor interface

view

Sets the name or path of a Property Editor to use when editing this property

config

Defines a JSON config for the Property Editor if required

enabled

Sets whether the collection of shipping information is enabled or not. If set to false not shipping info will be displayed (Default: true)

sameAsBilling

Determines the Order property to use as a flag to indicate the shipping address is the same as the billing address

firstName

The first name of the shipping contact

lastName

The last name of the shipping contact

addressLine1

Line 1 of the shipping address

addressLine2

Line 2 of the shipping address

city

The City of the shipping address

zipCode

The Zip/Postal Code of the shipping address

telephone

The telephone number of the shipping address

alias

The alias of the Order property to use

label

A friendly label to display for this property in the editor interface

description

A friendly description to display for this property in the editor interface

view

Sets the name or path of a Property Editor to use when editing this property

config

Defines a JSON config for the Property Editor if required

alias

The alias of the Order property to use

label

A friendly label to display next to a toggle switch for this property in the editor interface

trueValue

The value to expect for a true value

falseValue

The value to expect for a false value

customerNotes

The property to use for customer provided notes

internalNotes

The property to use to store internal notes that shouldn't be sent to the customer

alias

The alias of the Order property to use

label

A friendly label to display for this property in the editor interface

description

A friendly description to display for this property in the editor interface

alias

The alias of the Order property

label

A friendly label to display for this property in the editor interface

description

A friendly description to display for this property in the editor interface

template

An angular template to use for rendering the property value. Defaults to {{ properties['alias'].value }}

isReadOnly

Sets the property as read only and so doesn't provide a means of editing the value in the editor interface (Default: false)

view

Sets the name or path of a Property Editor to use when editing this property

config

Defines a JSON config for the Property Editor if required

{
    view: '/app_plugins/myplugin/views/ordereditor/ordereditor.html'
}
Order List Properties
Order Line Summary
Order Line Property Editing
Order Line Property Editing
Order Line Property Editing
Order Billing Address Summary
Order Billing Address Editor
Order Shipping Address Summary
Order Shipping Address Summary - Same as Billing
Order Shipping Address Editor
Order Shipping Address Editor - Same as Billing
Order Notes Editor
Additional Info Summary
Additional Info Editor
registered via the IUmbracoCommerceBuilder
Dependency Injection docs
Unit of Work pattern
injected into your Controller directly
Order/Order Line Properties
IUmbracoBuilder interface

Go behind the scenes

Explore the core services and methods in Umbraco Commerce, used for extending the product.

You can find all the reference documentation for Umbraco Commerce on GitHub. We might eventually move this to this or another site.

Umbraco Commerce Reference Documentation