Merging carts when a customer logs in

It’s quite common when a customer browses your site without logging in – either she/he intentionally does that, or just forget about logging in. The customer might add some items to carts and even checks out, but then is asked to log in or remember to log in. What would happen?

By default, Episerver Commerce will do as following:

  • Attach all orders made by that section to logged in customer. (I once placed an order in a famous retailer in Sweden without logging in, and then I asked their customer service to link that order to my account so I can track it easier. To my surprise, it cannot be done! That’s why I personally appreciate this feature.)
  • Merge all carts to existing carts, by name and market. So if I’m currently in US market and I added a item to that cart, and the US-cart linked to my account already have 2 other items, then when I log in, my US-cart will contain 3 items.
  • Merge all wishlist to existing wishlists, also per market. (The name for wishlist, by default, is fixed to “Wishlist”)

These default behaviors make senses in most of the cases. However, your business might have different requirements. For example, you don’t want to merge carts, you want to replace them. (I’ve heard a customer wants to do that). So instead of having 3 items in cart after logging in, the customer only has 1 newest item. Or you just don’t want to do anything, if the customer logs in, he or she will get items in the existing cart (that would be quite strange behavior, but that might be something your boss asked to). How?

Like most of other features in Episerver Commerce, that can be customized (that’s why it’s called a Framework). If you are using Commerce 10.4.0 or earlier, however, the customization can be a bit obscure. The ability to change the behaviors when a customer logs in is hidden in a small IHttpModule: ProfileModule. You can replace this section

  <system.webServer>
    <modules>
<add name="ProfileModule" type="EPiServer.Business.Commerce.HttpModules.ProfileModule, EPiServer.Business.Commerce" />
</modules>
<system.webServer>

with your own implementation. You can create a class like below, and replace the type of “ProfileModule” with the full name of your class (including the assembly)

internal class MyProfileModule : IHttpModule
    {      
        /// <summary>
        /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
        /// </summary>
        public void Dispose()
        {
        }

        /// <summary>
        /// Initializes a module and prepares it to handle requests.
        /// </summary>
        /// <param name="context">An <see cref="T:System.Web.HttpApplication"/> that provides access to the methods, properties, and events common to all application objects within an ASP.NET application</param>
        public void Init(HttpApplication context)
        {
            if (context == null)
            {
                return;
            }
            for (var i = 0; i < context.Modules.Count; i++)
            {
                var profileModule = context.Modules[i] as System.Web.Profile.ProfileModule;
                if (profileModule == null)
                {
                    continue;
                }
                profileModule.MigrateAnonymous += Profile_MigrateAnonymous;
            }
        }

        /// <summary>
        /// Handles the MigrateAnonymous event of the Profile control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="pe">The <see cref="System.Web.Profile.ProfileMigrateEventArgs"/> instance containing the event data.</param>
        static void Profile_MigrateAnonymous(Object sender, ProfileMigrateEventArgs pe)
        {
          //Do your stuffs here
        }
    }

Beware that by replacing the HttpModule, you’re also removing the non-cart related functionalities that Episerver might put there. Is there a better way to do that, without risking doing something wrong? Then Commerce 10.4.1 and newer might help you.

Commerce 10.4.1 comes with a new interface – IProfileMigrator, and it looks like this:

    /// <summary>
    /// Migrates the orders, carts, wishlist when an anonymous user logs in.
    /// </summary>
    public interface IProfileMigrator
    {
        /// <summary>
        /// Migrates the orders.
        /// </summary>
        /// <param name="anonymousId">The anonymous identifier.</param>
        void MigrateOrders(Guid anonymousId);

        /// <summary>
        /// Migrates the carts.
        /// </summary>
        /// <param name="anonymousId">The anonymous identifier.</param>
        void MigrateCarts(Guid anonymousId);

        /// <summary>
        /// Migrates the wishlists.
        /// </summary>
        /// <param name="anonymousId">The anonymous identifier.</param>
        void MigrateWishlists(Guid anonymousId);
    }

What you can do is to implement a class for this interface, and then register that class in your IConfigurableModule:

    /// <summary>
    /// Initialization module to handle the initialization of Commerce Azure.
    /// </summary>
    [ModuleDependency(typeof(CommerceInitialization))]
    [InitializableModule]
    public class InitializationModule : IConfigurableModule
    {
        public void ConfigureContainer(ServiceConfigurationContext context)
        {
            context.Services.AddSingleton<IProfileMigrator, MyProfileMigrator>();
        }

        public void Initialize(InitializationEngine context)
        {
        }

        public void Uninitialize(InitializationEngine context)
        {
        }
    }

There is an supposed internal class by Episerver (ProfileMigrator). You can inherit from this class and override the behavior you want to change. Note that the class itself can be changed without a major version, so beware about that.

Leave a Reply

Your email address will not be published. Required fields are marked *