Skip to main content
Version: 8.7.x

Shipping Calculation

Overview​

The Shipping Calculation SPI (Service Provider Interface) enables the integration of third-party shipping provider APIs, such as FedEx or UPS (United Parcel Service), with Self-Managed Commerce. With this integration, customers can view the available shipping options and cost associated with each shipping option during the checkout flow.

The default Elastic Path shipping calculation plugin calculates shipping cost based on the shipping region and shipping service level information available in the database. The customer service representatives can access and configure this plug-in in Self-Managed Commerce Manager.

The custom shipping options are integrated using SPIs. Self-Managed Commerce invokes this plug-in through an API and the shipping option service is integrated with Self-Managed Commerce through SPI.

The shipping calculation API provides the following capabilities:

  • Unpriced Shipping Option lookup: Provides shipping options without a shipping cost
  • Priced Shipping Option lookup: Provides shipping options with a shipping cost
  • Default Shipping Option lookup for Store: Provides default Elastic Path shipping option

Caching Service​

The caching service in the shipping calculation API implementation reduces the requests Cortex makes to the third-party shipping calculation APIs. The caching service caches the responses from third-party services and passes only new requests to the third-party services. This service reduces third-party calls that Cortex makes. For more information, see Cortex Caching.

Shipping Calculation Options​

Elastic Path provides two shipping options to the user, priced shipping options and unpriced shipping options. The implementations of ShippingOptionService and PricingSnapshotService call the ShippingCalculationService classes. Both services return a ShippingOptionResult object, which contains a list of ShippingOption objects. Each ShippingOption object represents a shipping provider such as FedEx, which is available for the order. The following diagram illustrates the workflow for unpriced shipping options and priced shipping options:

Workflow for unpriced shipping options and priced shipping options

Unpriced Shipping Options​

Unpriced shipping options provide the details of the available shipping options. Unpriced shipping options are returned when a user does not provide an address to ship the order, or if the shipping prices cannot be calculated before placing an order. For example, an unregistered user or incomplete purchase workflow.

Workflow​

  1. Cortex calls the ShippingOptionService class.

    The ShippingOptionService class is the high-level API that takes Commerce Engine domain objects, such as ShoppingCart and Address.

  2. The ShippingOptionService class calls one of the following:

    • CachingShippingCalculationService

      If ShippingOptionService call is already made for the same shopping cart with the same details, CachingShippingCalculationService returns the cached shipping cost, ShippingOptionResult.

    • ShippingCalculationService

      For a new cart, the request is passed to this service to get the shipping cost. The ShippingCalculationService uses Data Transfer Objects (DTO) to decouple the underlying calculation from the Commerce Engine

  3. ShippingCalculationService calls UnpricedShippingCalculationPluginSelector.

  4. UnpricedShippingCalculationPluginSelector selects the default Elastic Path shipping calculation plug-in or a third-party shipping calculation plug-in as required and returns ShippingOptionResult.

    This request returns the available shipping options even if the shipping address and cart details are not provided.

  5. The shipping calculation plug-in returns the shipping cost to Cortex.

Priced Shipping Options​

Priced shipping options are provided to a user before completing payment for an order to calculate total price of the order. Priced shipping options are available only if the customer provides the shipping address when completing the order. The PricingSnapshotService examines the shopping cart and returns the pricing details of the shopping cart.

ShoppingCartPricingSnapshot getPricingSnapshotForCart(ShoppingCart shoppingCart);

Workflow​

  1. Cortex calls the ShippingCalculationService, which calls the PricingSnapshotService.

    This service returns the ShoppingCartPricingSnapshot with the cart details.

  2. PricingSnapshotService calls one of the following:

    • ShippingCalculationService

      For a new cart, the request is passed to this service to get the shipping cost.

      final ShoppingCartPricingSnapshot shoppingCartPricingSnapshot = pricingSnapshotService.getPricingSnapshotForCart(shoppingCart);
      final PricedShippableItemContainer<PricedShippableItem> pricedShippableItemContainer =
      pricedShippableItemContainerTransformer.apply(shoppingCart, shoppingCartPricingSnapshot);

      ShippingCalculationResult pricedShippingOptions = cachingShippingCalculationService.getPricedShippingOptions(pricedShippableItemContainer);
    • CachingShippingCalculationService

      If ShippingOption service call is already made for the same shopping cart with the same details, CachingShippingCalculationService returns the cached shipping cost

  3. ShippingCalculationService calls ShippingCalculationPluginSelector.

  4. ShippingCalculationPluginSelector selects the default Elastic Path shipping calculation plug-in or a third-party shipping calculation plug-in as required.

  5. The shipping cost is returned to Cortex.

Shipping Calculator Implementation​

You can implement a shipping calculation plug-in by creating a class that extends AbstractShippingCalculationPlugin and implements the following ShippingCalculationCapability interfaces:

  • ShippingCostCalculationCapability

    No methods are defined. A marker interface to designate which capability interfaces are shipping calculation ones.

  • ShippingOptionListCapability

    getUnpricedShippingOptions(ShippableItemContainer<?> unpricedShippableItemContainer);

    Returns a List<ShippingOption> object that lists all shipping options for an unpriced lookup.

  • ShippingOptionListAllCapability

    getAllShippingOptions(String storeCode, Locale locale);

    Returns a List<ShippingOption> object that lists all shipping options for the specific store.

  • ShippingOptionListPerDestinationCapability

    getUnpricedShippingOptions(ShippingAddress destinationAddress, String storeCode, Locale locale);

    Returns a List<ShippingOption> object that lists all shipping options for the given store and destination address

Caching Implementation​

Caching is implemented in the com.elasticpath.shipping.connectivity.service.cache package. The caching service consists of the following:

  • The CachingShippingCalculationServiceImpl class, which implements ShippingCalculationService

    The ShippingCalculationService implementation provides cached shipping options to the user through the ShippingOptionService.

  • The ShippingCalculationResultCacheKey interface and a default implementation of ShippingCalculationResultCacheKeyImpl interface

    The ShippingCalculationResultCacheKeyImpl implementation defines information as a key to retrieve the cached shipping option.

  • The ShippingCalculationResultCacheKeyBuilder interface and default implementation of ShippingCalculationResultCacheKeyBuilder interface

    Use this class to create cache keys for shipping options

Selection of a Shipping Calculation Plugin​

The selection of a shipping calculation plugin is defined in the PricedShippingCalculationPluginSelector and UnpricedShippingCalculationPluginSelector interfaces in the com.elasticpath.shipping.connectivity.service.selector package. The default implementation of these interfaces returns the default Elastic Path shipping calculator plugin.

A selector might choose different plugins based on the Elastic Path store being serviced for store-specific shipping plugins. A selector can also choose a different plugin depending on what is being shipped. For example, if you want high value goods to be shipped by a particular shipping provider such as FedEx rather than USPS (United States Postal Service), you can implement that functionality in the selector class.

When implementing a new shipping calculation plugin, you must override the default implementation of the following methods to return the shipping calculator plugin implementation:

  • PricedShippingCalculationPluginSelector.getPricedShippingCalculationPlugin()
  • UnpricedShippingCalculationPluginSelector.getUnpricedShippingCalculationPlugin()

These implementations must provide logic to select your shipping calculation plugin, however the default setting must be to select Elastic Path’s default shipping calculator plugin.

Creating an Implementation of Shipping Calculation API​

Elastic Path provides new plugins, such as shipping-calculation-connectivity-api and shipping-calculation-plugin-epcommerce, to support shipping calculation API framework. The shipping-calculation-connectivity-api bundle defines the interfaces that a new plugin needs to implement. The custom plugin replaces the shipping-calculation-plugin-epcommerce bundle.

To implement a new plugin, you must replace the following dependency with the new dependencies in the cortex and cm-libs module’s POM.xml:

<dependency>
<groupId>com.elasticpath</groupId>
<artifactId>shipping-calculation-plugin-epcommerce</artifactId>
</dependency>
<dependency>
<groupId>com.elasticpath</groupId>
<artifactId>shipping-calculation-epcommerce</artifactId>
</dependency>
  1. Create a new module in the extensions module for your shipping calculation API called Shipping Calculation Plugins.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
    <groupId>com.elasticpath.extensions</groupId>
    <artifactId>ext-commerce-engine-parent</artifactId>
    <version>0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <name>Shipping Calculation Plugins</name>
    <artifactId>ext-shipping-calculation-plugins-parent</artifactId>
    <packaging>pom</packaging>

    <modules>
    <module>Name of the custom plugin folder</module>
    </modules>
    </project>
  2. Add Shipping Calculation Plugins as a module in the extensions/pom.xml file.

  3. Create a directory within the Shipping Calculation Plugins directory for your specific plugin such as custom_shipping_calculation_plugin.

  4. Add the following POM file to the plugin:

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
    <artifactId>core</artifactId>
    <groupId>com.elasticpath</groupId>
    <version>0-SNAPSHOT</version>
    </parent>


    <name>EP Shipping Calculation Plugin</name>

    <dependencies>
    <dependency>
    <groupId>com.elasticpath</groupId>
    <artifactId>ep-money</artifactId>
    </dependency>
    <dependency>
    <groupId>com.elasticpath</groupId>
    <artifactId>shipping-calculation-connectivity-api</artifactId>
    </dependency>
    <dependency>
    <groupId>com.elasticpath</groupId>
    <artifactId>shipping-calculation-epcommerce</artifactId>
    </dependency>

    <dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    </dependency>
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    </dependency>

    <dependency>
    <groupId>com.elasticpath</groupId>
    <artifactId>ep-test-utils</artifactId>
    </dependency>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    </dependency>
    <dependency>
    <groupId>org.assertj</groupId>
    <artifactId>assertj-core</artifactId>
    </dependency>
    <dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <artifactId>maven-checkstyle-plugin</artifactId>
    </plugin>
    <plugin>
    <artifactId>maven-pmd-plugin</artifactId>
    </plugin>
    <plugin>
    <groupId>org.apache.felix</groupId>
    <artifactId>maven-bundle-plugin</artifactId>
    <extensions>true</extensions>
    </plugin>
    </plugins>
    </build>
    </project>
  5. Create standard Maven directories in the following structure in this module:

    src/
    main/
    java/
    resources/
    test/
    java/
    resources/
  6. Create a class to extend AbstractShippingCalculationPlugin.

    /*
    package com.elasticpath.shipping.connectivity.spi;

    import java.io.Serializable;

    import com.elasticpath.shipping.connectivity.spi.capability.ShippingCalculationCapability;

    /**
    * Service Provider Interface for extension classes implementing shipping calculation plugins.
    */
    public abstract class AbstractShippingCalculationPlugin implements ShippingCalculationPlugin, Serializable {
    /**
    * Serial Version UID.
    */
    private static final long serialVersionUID = 1L;

    @Override
    public <T extends ShippingCalculationCapability> T getCapability(final Class<T> capability) {
    if (hasCapability(capability)) {
    return capability.cast(this);
    }
    return null;
    }

    @Override
    public <T extends ShippingCalculationCapability> boolean hasCapability(final Class<T> capability) {
    return capability.isAssignableFrom(this.getClass());
    }

    }
  7. Implement ShippingCostCalculationCapability, ShippingOptionListCapability, ShippingOptionListAllCapability, and ShippingOptionListPerDestinationCapability.

    Implement shipping calculation capability as in the following example:

    public Money calculateShippingCost(final ShippingServiceLevel shippingServiceLevel,
    final Collection<? extends ShippableItem> shippableItems,
    final Money shippableItemsSubtotal,
    final Currency currency) {
    return shippingServiceLevel.getShippingCostCalculationMethod()
    .calculateShippingCost(shippableItems, shippableItemsSubtotal, currency, productSkuLookup);
    }
  8. Add the plugin into the Cortex and Commerce Manager modules.