Customizing the Order Website Order Flow

Tags: 229 views 0

How to create custom order flows for the Order Website.

Overview

The order flow relies on mapping named routes in the route configuration and then putting them together into one or more order flows.

Configuration of the order flow happens in MyTheme\App_Start\OrderFlowConfig.cs and route configuration in MyTheme\App_Start\RouteConfig.cs.

The following controller/action pairs are currently possible to use as part of an order flow:

  • Domains/Index
  • ProductListing/Index
  • Account/Index
  • Checkout/Index

Keep in mind

Currently the Account and Checkout steps are required to be the second to last and last steps in an order flow. They are included in the order flow to hook in to order flow validation and to get access to order flow presentation data like previous and next actions.

All routes which should be part of an order flow must be mapped with the RouteCollection extension method MapOrderFlowRoute.

All order flows must have a name. This name is used when deciding what order flow is currently applicable. You can also specify one order flow as the default order flow. The resolution order of what order flow to use is the following:

  1. If there is a query string ?flow={orderFlowName} and there exists an order flow with that name, use that order flow.
  2. Use any existing order flow with the same name as the application hostname, e.g. store.example.com.
  3. Use the default order flow.

Re-ordering Existing Order Flow Steps

The Default theme has a single default order flow named DefaultFlow that contains the following steps: Domains => HostingPackage => Account => Checkout.

As mentioned above, the Account and Checkout steps should always be located in the last two steps of the order flow. So for re-ordering the existing DefaultFlow order flow the only realistic option left is to switch places between Domains and HostingPackage.

Start by configuring the the new order flow in MyTheme\App_Start\OrderFlowConfig.cs. The final configuration will look something like this (with each step explained below):

public static void RegisterOrderFlows(OrderFlowCollection orderFlows)
{
    // 1.
    orderFlows.Clear();

    // 2.
    var orderFlow = new OrderFlow("MyOrderFlow", new [] {"HostingPackage", "Domains", "Account", "Checkout"});

    // 3.
    orderFlow.AddRouteNameAlias("OrderFlowStart", "HostingPackage");

    // 4.
    orderFlows.Add(orderFlow, true);
}
  1. To re-order the one order flow which you have, completely remove it from the existing order flows.
  2. Define the new order flow MyOrderFlow with HostingPackage placed before Domains.
  3. Add an alias from the OrderFlowStart route to HostingPackage (see more below) to have the first step in the order flow accessible at the root path of the application.
  4. Add MyOrderFlow to the order flows used by the application and set it as default.
  5. In step 3 above, you added an alias from the OrderFlowStart route to HostingPackage. However, in the default route config the OrderFlowStart route is set to resolve to Domains, so you also have to add a change to MyTheme\App_Start\RouteConfig.cs:
    public static void RegisterRoutes(RouteCollection routes)
    {
        // 1.
        routes.Remove(routes["OrderFlowStart"]);
    
        // 2.
        routes.MapOrderFlowRoute(
            name: "OrderFlowStart",
            url: "",
            defaults: new
            {
                controller = "ProductListing",
                action = "Index",
                query = "HostingPackage",
                viewName = "HostingPackage"
            }
        );
    }
    
    1. Remove the original OrderFlowStart route.
    2. Define the new OrderFlowStart route with the same defaults as the HostingPackage route, but with a new url value to match the root URL of the application.

Adding a New Order Flow Step

Continuing with the example from Re-ordering Existing Order Flow Steps we might want to add a page offering extra services as the final step before Account and Checkout. This will add a new route to a page listing the extra services and add that route to the order flow.

    1. Start by adding the following route to the route configuration:
      routes.MapOrderFlowRoute(
          name: "ExtraService",
          url: "Addons",
          defaults: new
          {
              controller = "ProductListing",
              action = "Index",
              query = "ExtraService",
              viewName = "ExtraService"
          }
      );
      
    2. Note the following about the route configuration options:
      • name: The route is named ExtraService, which is also the name which must be used in the order flow configuration.
      • url: How the route is represented to the end user is not dependent on the route name so in this example we chose /Addons instead.
      • controller and action: These are pages for listing products.
      • query: By default the ProductListing.Index action list’s products with the category specified in query.
      • viewName: If you want to implement a different view for this page you must add the Themes\MyTheme\Views\ProductListing\ExtraService.cshtml later since it is not part of the Default theme. You should also add new resource translations for the step title and description (see Changing the Order Flow Presentation).
    3. Add the new page to the order flow:
      var orderFlow = new OrderFlow("MyOrderFlow", new [] {"HostingPackage", "Domains", "ExtraService", "Account", "Checkout"});
      

Creating Multiple Domain Based Order Flows

It is possible to create multiple order flows in AtomiaStore and also to connect them to the hostname of the application. E.g. you might want to have two different order flows for selling shared hosting and VPS located at hosting.store.example.com and vps.store.example.com respectively. Assuming that Atomia Billing has been set up for this and that the routes have been added you could configure the order flows as follows:

public static void RegisterOrderFlows(OrderFlowCollection orderFlows)
{
    orderFlows.Clear();

    var sharedHostingFlow = new OrderFlow("hosting.store.example.com", new[] { "Domains", "HostingPackage", "Account", "Checkout" });
    sharedHostingFlow.AddRouteNameAlias("OrderFlowStart", "Domains");

    var vpsFlow = new OrderFlow("vps.store.example.com", new[] { "Domains", "VPS", "Account", "Checkout" });
    vpsFlow.AddRouteNameAlias("OrderFlowStart", "Domains");

    orderFlows.Add(sharedHostingFlow, true);
    orderFlows.Add(vpsFlow);
}

Note the following regarding this configuration:

      • If you want to have a single entry point for the two order flows, e.g. the Domains page, and then let the customer choose what flow to continue with you must yourself manually set up the links to the next step in the respective order flow (see Changing the Order Flow Presentation below).
      • The hosting.store.example.com flow is set as default, so if you for example also have store.example.com available the customers that enter from there will get the shared hosting flow. You can also choose to not have a default order flow.
      • If you for some reason want to switch order flow but keep the customer at the same domain you could do it by adding the ?flow={orderFlowName} query string with the name of the other order flow as orderFlowName.

Useful tip

Hostnames based order flows go well together with hostname named shops in Atomia Billing. You can use the them in AtomiaStore by using the IShopNameProvider implementation Atomia.Store.PublicBillingApi.DomainShopNameProvider. Naturally, this also requires you to have correctly named shops in Atomia Billing, e.g. vps.store.example.com and hosting.store.example.com in the example above.

Changing the Order Flow Presentation

Views that are part of the order flow have access to order flow data of the type Atomia.Store.AspNetMvc.Models.OrderFlowModel via ViewBag.OrderFlow.

The Default theme uses this data in the step specific _Actions.cshtml partial views to render buttons to move between steps and uses the shared partial view _Progress.cshtml to present the order flow progress.

Each order flow step has the following properties:

      • Title
      • Description
      • StepNumber
      • Previous
      • Next

Title and Description are populated by resource strings from App_GlobalResources\Common.*.resx with names on the form StepTitle{Name} and StepDescription{Name}. StepNumber is the 1-based index of the step in the order flow.

The steps also have the Previous and Next properties, which are the names of the previous and next steps in the order flow. If the value of either property is an empty string it means that there is no step in that direction, e.g. Checkout always has an empty Next property.

The Previous and Next properties match named routes so they can be used e.g. with the RouteLink HTML-helper to render links to another step.

@Html.RouteLink(Html.CommonResource("Back"), orderFlow.CurrentStep.Previous, new { flow = orderFlow.Name }, new { id = "back_step" })

The above example renders a link to the previous step and also sets the flow query string to the value from orderFlow.Name.

Keep in mind

There is a property IsQueryStringBased on the OrderFlowModel which flags that the current order flow has been selected by a query string, and that any links to other steps in the order flow should have the flow query string added as well.

Validating the Order Flow Steps

The steps in an order flow can be validated by implementing the Atomia.Store.AspNetMvc.Ports.IOrderFlowValidator interface.

The ValidateOrderFlowStep method gets called for the current step, and if the step is not valid there is an automatic redirect to the previous step.

The purpose of this is to prevent users from going directly to an order flow step that does not make sense.

There is a default implementation in Atomia.Store.Themes.Default.Adapters.OrderFlowValidator that checks that the cart is not empty in the Account step. It also checks that the cart is not empty and that contact data is not empty in the Checkout step.

Keep in mind

The order flow validation is currently limited to the backend. Each view is responsible for checking if the customer should be allowed to proceed to the next step. The views are also responsible for handling changes to the cart, e.g. if the customer removes all items while on the last Checkout step.

Was this helpful?