Select version
v7.7 v7.6 v7.5 v7.3 v7.1 v7.0 v6.8 v6.7 v6.6 v6 v5 v4 v3 v2

Building a Custom Shipping Method Service

One of my favorite features of uCommerce is the extensible framework lurking underneath the surface. Using the framework you have the opportunity to add, modify, or even completely replace the out of the box functionality of uCommerce. Shipping methods are no exception to this rule. Specifically you can build custom shipping method services to enable custom calculation of shipping costs during the checkout flow as discussed in Shipping Methods Explained.

This article covers adding a new shipping method service to uCommerce and having it show up the admin UI like so.

Image

The Framework

Before you get into the nitty gritty of building your first custom shipping method service you’ll need a little background about the surrounding framework to understand what’s going on.

Your shipping method service first needs to be configured in uCommerce via web.config (more on that later), once written the uCommerce pipelines will make sure and trigger your service when appropriate. By default shipping methods services are triggered in the Basket Pipeline by the task called CalculateShippingCostForShipments. If you’re interested in more information about pipeline please see the uCommerce Pipelines Explained article.

Notice that the tasks ends on Shipments plural. uCommerce supports multiple shipments per order and will call the proper shipping method service for each shipment based on the shipping method selected for it.

Image

Pretty simple stuff. Now for the fun part: Writing your own shipping method service.

The IShippingMethodService Interface

This interface is what uCommerce uses to be able to calculate shipment costs. As you can see from the code it’s pretty simple. Basically you’ll receive the shipment as created using either the XSLT or .NET API and you’ll have the opportunity to loop through the relevant order lines, which are linked from the shipment. Lets see what’s involved to create a simple volume based IShippingMethodService.

Volume Based Shipping Method Service

Volume and weight based shipping pricing are probably the most common shipping scenarios for an online store so lets try and build one of them as an example. First you’ll need to create a new Visual Studio project and add a reference to the UCommerce.dll assembly. You will also need a reference to the Subsonic.Core.dll assembly. With that in place you’re ready to start implementing the service.

The Methods and Properties

    
    /// <summary>
    /// Interface for calculating shipping fees
    /// </summary>
    public interface IShippingMethodService
    {
    	/// <summary>
    	/// Calculates the shipping fee for a particular <see cref="Shipment"/>
    	/// </summary>
    	/// <param name="shipment"></param>
    	/// <returns></returns>
    	Money CalculateShippingPrice(Shipment shipment);
    
    	/// <summary>
    	/// Validate an exising <see cref="Shipment"/> for shipping
    	/// </summary>
    	/// <param name="shipment"></param>
    	/// <returns></returns>
    	bool ValidateForShipping(Shipment shipment);
    }
    

As you can see you just need to implement two methods and a single property to get it going. The names of the methods should make it fairly obvious what they’re for. ValidateForShipping might require some explanation.

As discussed in Shipping Method Explained each shipping method can be configured to available only to certain stores and for shipment only to certain countries. The ValidateForShipment enforces these rules. If you just need an unrestricted shipment you can go ahead and always return true, but be aware that users might be confused if they set up a shipping method in one way but it doesn’t actually check for the conditions thus behaving differently from what they expect.

CalculateShippingPrice Method

First up lets calculate the shipping price for the shipment:

    public Money CalculateShippingPrice(Shipment shipment)
    {
        // First sum up the total weight for the shipment.
        // We're assumning that a custom order line property 
        // was set on the order line prior when the product was added to the order line.
        decimal totalWeight = 0;
        foreach (OrderLine orderline in shipment.OrderLines)
            totalWeight += orderline.Quantity * Convert.ToDecimal(orderline["Weight"]);
     
        decimal shippingPrice = 0;
        if (totalWeight > 10)
            shippingPrice = 100;
        else if (totalWeight > 20)
            shippingPrice = 200;
        else
            shippingPrice = 300;
     
        // To instantiate a new Money object we need the currency,
        // which is set on the purchase order. To get the currency
        // we move through Shipment -> OrderLines -> PurchasrOrder -> Currency
        return new Money(shippingPrice, shipment.PurchaseOrder.BillingCurrency);
    }
    

Of course the business rule in this particular case is very simplistic, but you get the idea.

ValidateForShipping Method

Next up is the ValidateForShippingMethod, which is actually called prior to CalculateShippingPrice to validate that the shipping method is validate for the current purchase order. The SinglePriceShippingMethod which comes out of the box has rules to ensure that the shipping method has a shipping address set and that the shipment is set to be delivered to one of the allowed countries for the shipping method.

    public bool ValidateForShipping(Shipment shipment)
    {
        if (shipment.ShipmentAddress.AddressId <= 0)
            throw new InvalidOperationException("Cannot validate shipment for country. Remember to set the shipping address for shipment.");
    
        var shippingMethod = shipment.ShippingMethod;
        if (shippingMethod == null)
            throw new InvalidOperationException(
                "Cannot validate destination country for shipment. It does not contain a shipping method. Remember to add a shipping method to your shipment before validating.");
    
        return ValidateShippingDestination(shipment.ShipmentAddress,
                                    shippingMethod);
    }
    
    /// <summary>
    /// Validates the order lines according to their desired destination and configured contries for shipping method
    /// </summary>
    /// <returns></returns>
    protected virtual bool ValidateShippingDestination(OrderAddress shippingAddress, ShippingMethod shippingMethod)
    {
        var eligibleCountries = shippingMethod.EligibleCountries;
    
        // No eligible countries exist - so the shipment isn't valid
        if (eligibleCountries == null) return false;
    
        return eligibleCountries.SingleOrDefault(x => x == shippingAddress.Country) != null;
    }
    
    

VolumeShippingMethodService

    public class VolumeShippingMethodService : IShippingMethodService
    {
        public Money CalculateShippingPrice(Shipment shipment)
        {
            // First sum up the total weight for the shipment.
            // We're assumning that a custom order line property 
            // was set on the order line prior when the product was added to the order line.
            decimal totalWeight = 0;
            foreach (OrderLine orderline in shipment.OrderLines)
                totalWeight += orderline.Quantity * Convert.ToDecimal(orderline["Weight"]);
    
            decimal shippingPrice = 0;
            if (totalWeight > 10)
                shippingPrice = 100;
            else if (totalWeight > 20)
                shippingPrice = 200;
            else
                shippingPrice = 300;
    
            // To instantiate a new Money object we need the currency,
            // which is set on the purchase order. To get the currency
            // we move through Shipment -> OrderLines -> PurchasrOrder -> Currency
            return new Money(shippingPrice, shipment.PurchaseOrder.BillingCurrency);
        }
    
        public bool ValidateForShipping(Shipment shipment)
        {
            if (shipment.ShipmentAddress != null)
                throw new InvalidOperationException("Cannot validate shipment for country. Remember to set the shipping address for shipment.");
    
            var shippingMethod = shipment.ShippingMethod;
            if (shippingMethod == null)
                throw new InvalidOperationException(
                    "Cannot validate destination country for shipment. It does not contain a shipping method. Remember to add a shipping method to your shipment before validating.");
    
            return ValidateShippingDestination(shipment.ShipmentAddress,
                                        shippingMethod);
        }
    
        /// <summary>
        /// Validates the order lines according to their desired destination and configured contries for shipping method
        /// </summary>
        /// <returns></returns>
        protected virtual bool ValidateShippingDestination(OrderAddress shippingAddress, ShippingMethod shippingMethod)
        {
            var eligibleCountries = shippingMethod.EligibleCountries;
    
            // No eligible countries exist - so the shipment isn't valid
            if (eligibleCountries == null) return false;
    
            return eligibleCountries.SingleOrDefault(x => x == shippingAddress.Country) != null;
        }
    }
    

Registering Your ShippingMethodService with uCommerce

You must also register the service by adding a new file to uCommerce/Apps/MyApp called Shipping.config with the following component:

    
    <configuration>
    	<components>
    		<component id="VolumeShippingService"
    			service="UCommerce.Transactions.Shipping.IShippingMethodService, UCommerce"
    			type="MyUCommerceApp.VolumeShippingMethod, MyUCommerceApp"/>
    	</components>
    </configuration>
    

With the app updated you’ll now be able to select your new shipping method service in the UI:

Image

Summing It All Up

Creating your own shipping method service to handle custom calculations takes as little effort as implementing two methods and a property. Once it’s registered with uCommerce users take over and set up the shipping method to their liking. Becuase your shipping method service is rolled into a separate assembly it’s very straightforward to share between projects.

If you’d lie more information about Shipping Method please take a look at the article Shipping Methods Explained.

uCommerce Aps - Studsgade 29B - 8000 Aarhus C - Denmark
Phone +45 61 799 997 - Email info@ucommerce.dk uCommerce logo