Checkout and Order Processing
This section contains information about the technical design of checkout and order processing.
Checkout process refers to the sequence of steps taken by the user to provide information required to complete the purchase of products. The user steps typically include supplying billing and shipping addresses, selecting delivery options, and providing payment information.
Order processing includes the synchronous processing of the order carried out by the CheckoutService
, as well as subsequent asynchronous actions that update the order.
Synchronous Order Processing
To complete the checkout process, an order needs to be created and transitioned through a number of synchronous steps before it can be sent to fulfillment. This synchronous order processing is implemented by the CheckoutService
. To process an order, the CheckoutService
requires a ShoppingCart and an OrderPayment
. The ShoppingCart
essentially represents an order for products, complete with the customer and shipping information. The OrderPayment
provides information about how the customer will pay for the items in the cart. CheckoutService.checkout()
is invoked with these two parameters and will throw an exception if anything goes wrong. The exception may be an EpServiceException
, or any of the standard exceptions thrown by implementors of the PaymentGateway
interface (see Payment Processing).
The main responsibility of the checkout operation is to process the payment and persist the order. It is assumed that customer and product information can be changed at any time; therefore, all information about the order, product, customer, and payment must be stored as copies in separate database tables.
Checkout Actions
Synchronous order processing is performed by checkout action classes that implement one of the CheckoutAction
, ReversibleCheckoutAction
or FinalizeCheckoutAction
interfaces.
Checkout actions can be broken down into three subgroups of actions which occur after the customer submits his/her purchase
- Setup actions that implement an
execute()
method. - Reversible actions that implement
execute()
androllback()
methods. - Finalize actions that implement an
execute()
method.
The checkout actions are defined in three separate lists that are injected into the abstractCheckoutServiceDelegate
bean in the checkout.xml
file in ep-core
.
<util:list id="setupActions" list-class="java.util.ArrayList">
<ref bean="preCheckoutCheckoutAction" />
<ref bean="shippingInformationCheckoutAction" />
<ref bean="giftCertificateBalanceCheckerCheckoutAction" />
<ref bean="updateCustomerCheckoutAction" />
</util:list>
<util:list id="reversibleActions" list-class="java.util.ArrayList">
<ref bean="createNewOrderCheckoutAction" />
<ref bean="populateOrderDataCheckoutAction" />
<ref bean="createGiftCertificatesCheckoutAction" />
<ref bean="populateTemplateOrderPaymentCheckoutAction" />
<ref bean="authorizePaymentsCheckoutAction" />
<ref bean="updateLimitedUsageNumbersCheckoutAction" />
<ref bean="subscriptionCreditCheckCheckoutAction" />
<ref bean="updateOrderCheckoutAction" />
<ref bean="commitOrderTaxCheckoutAction" />
<ref bean="processCouponCustomerAssignmentsCheckoutAction" />
<ref bean="createNewOrderEventCheckoutAction" />
<ref bean="initiateFulfilmentCheckoutAction" />
<ref bean="capturePaymentsCheckoutAction" />
</util:list>
<util:list id="finalizeActions" list-class="java.util.ArrayList">
<ref bean="clearShoppingCartCheckoutAction" />
<ref bean="postCheckoutCheckoutAction" />
</util:list>
These checkout action lists can be overridden in your extensions project to add custom checkout actions, or to remove or reorder existing actions.
Note:
preCheckoutCheckoutAction
andpostCheckoutCheckoutAction
do nothing in the out of the box source code.
The CheckoutAction
implementations of each step delegate to the following key classes and files:
Key classes and files
CheckoutService
- Service that executes a checkoutCheckoutEventHandler
- Interface implemented by a class wired via Spring to the CheckoutService so that it can be notified of events during the checkout execution. This is useful for extending or modifying checkout functionalityShoppingCart
- The shopping cart contains items to be purchased during a checkoutOrderPayment
- An order payment represents information about how the customer will pay for the orderPaymentGateway
- Interface implemented by payment gateways that handle financial transactions such as credit card or PayPal paymentsOrder
- Represents a completed order for productsOrderSku
- A snapshot of information about a purchased SKU at the time of checkoutOrderShipment
- Contains shipping information for an orderInventory
- Contains inventory information for a particular product SKUOrderAddress
- A snapshot of the customer’s address (billing and shipping) at the time of checkout
Implementing Checkout Event Handlers
One way to extend the checkout process is to create a class that implements the CheckoutEventHandler
interface, add it to the checkoutEventHandlers
list in checkout.xml
, and implement one or more of the following methods, :
preCheckout
- called before any other checkout action is executedpreCheckoutOrderPersist
- called just before the order is persistedpostCheckout
called after all other checkout actions have completed
For example, if you wanted to call a webservice every time a checkout was completed, you could write a new class that extends the CheckoutEventHandler
and implement the web service call in the postCheckout() method.
Important concepts
Transaction boundaries
The checkout service is configured in Spring so that checkout methods do not run as transactions. This is required to persist audit trails for checkout actions.
For example, if a credit card payment fails, we want to record this in the OrderPayment table. If the checkout method was transactional, the database operation would be rolled back automatically.
However, the service methods invoked by checkout actions need to execute within transactions if they update the database.
Creating an Order
The CheckoutService
creates an Order
object and passes it to the OrderService
to persist.
Every Order must have a unique OrderNumber, set at Order creation time (not at persist time). Unique order numbers are obtained from the OrderImpl.getOrderNumber()
method.
The OrderService
provides services to query on Orders and persist Orders, but not to generate Orders. Order generation is performed by the CheckoutService.
There are methods to search for orders by various basic criteria, or you can pass in an OrderSearchCriteria object. The search methods must translate the given criteria into a Criteria String for the Persistence engine, and they do so via an OrderCriterion factory class.
Data replication
At order time a snapshot of various objects must be created and stored in separate tables to insulate the Order details from any future changes in prices, inventory, addresses, etc. Objects are copied as follows:
ProductSku --> OrderSku BillingAddress --> OrderAddress (for the Order) ShippingAddress --> OrderAddress (for the OrderShipment)
Attributes such as shipping cost, quantity, taxes, etc. are calculated at Order time and frozen in the appropriate object (Order, OrderShipment, OrderSku, etc).
Failed Orders
Failed orders are saved to the database (TORDER
table) with the order’s STATUS
set to FAILED
. The Quartz job, cleanupFailedOrdersJob
, will eventually remove the failed order from your system.
Asynchronous Order Processing and States
The synchronous order processing phase generates asynchronous order events. These events trigger subsequent processing of the order by internal handlers (e.g. email) and external systems (e.g. fulfillment). See Asynchronous Event Messaging for more information on creating and consuming events.
The diagram below shows the order STATES
in the boxes and the [EVENTS]
that are sent when transitioning to a new state.
The order states and events are defined in the OrderStatus
and OrderEventType
extensible enums
. They are also document from a business user perspective in Appendix A of the Commerce Manager Users Guide.
Shipment state transitions and events can be triggered by order state transitions, or conversely can initiate order state transitions.
Shipment states are defined in the OrderShipmentStatus
extensible enum
. The values are:
AWAITING_INVENTORY
INVENTORY_ASSIGNED
RELEASED
SHIPPED
ONHOLD
CANCELLED
FAILED_ORDER
Shipment events are defined in the OrderEventType
extensible enum
. The values are:
ORDER_SHIPMENT_CREATED
ORDER_SHIPMENT_SHIPPED
ORDER_SHIPMENT_RELEASE_FAILED
A common project requirement is to add asynchronous order review processing after the order has been committed. The simplest way to do this is:
Override the
reversibleActions
Spring bean to removeinitiateFulfilmentCheckoutAction
andcapturePaymentCheckoutAction
from the list of synchronous checkout actions defned incheckout.xml
.Create a new Camel route in the Integration Server to consume
ORDER_CREATED
events and initiate order review processing.If the order passes review, release the order and capture payments.
This will generate an
[ORDER_RELEASED]
event and transition the order state toIN_PROGRESS
.If the order fails review, cancel the order.
This will generate an
[ORDER_CANCELLED]
event and transition the order state toCANCELLED
.
Shipping and Billing Addresses
The Customer object contains a collection of addresses entered by a particular user. These addresses can be added to the collection in the customer self-service area or during a checkout. The collection contains addresses used for shipping as well as for billing and does not distinguish between them. A customer can have a single preferred shipping address and a single preferred billing address. These preferences are stored as references to addresses in the customer object’s address collection.
During the checkout process, the front end is responsible for prompting the customer to create and/or select the address to use for the checkout in progress. The shopping cart maintains references to the selected shipping and billing addresses to be used in the order.
Key classes and files
Address
Interface implemented by all address classes.
Customer
Maintains a collection of a user’s addresses, including a reference to the preferred shipping and billing addresses.
ShoppingCart
Maintains a reference to the billing and shipping addresses a customer has selected for a particular checkout.
AbstractAddressImpl
Abstract implementation of the Address interface.
CustomerAddressImpl
Subclass of
AbstractAddressImpl
used to represent a customer’s addresses. There are no members in this class; it is required to enable the persistence engine to store these addresses in a separate table from order addresses.OrderAddressImpl
Subclass of
AbstractAddressImpl
used to represent addresses associated with orders. There are no members in this class; it is required to enable the persistence engine to store these addresses in a separate table from customer addresses
Implementation details
Although any address can be selected as a shipping or billing address, addresses used as shipping addresses during checkout are validated by checking whether a shipping service level covering that region exists.
During the final checkout, a copy of the CustomerAddress
is made and associated with the order. This prevents the loss of the address information if the customer modifies or deletes the address.
Shipping Options
During the checkout process, customers have the opportunity to select their Shipping Service level. After the shipping address has been selected, the shipping service levels available are determined by the region to which their products are being shipped; available shipping options for different regions are configured in the Commerce Manager. For more information on how that is done, see the Shipping Regions and Shipping Service Levels sections in the Commerce Manager User Guide.
Key classes and files
The relevant files and classes can be roughly subdivided into those that deal mostly with displaying the appropriate ShippingServiceLevels and those that deal with the costs associated with those service levels.
Shipping Service Levels:
CheckoutServiceImpl
retrieveShippingOption(shoppingCart)
- Sets the valid shipping levels in the ShoppingCart for display.ShippingServiceLevelServiceImpl
Retrieves valid shipping service levels for a region, gets shipping cost calculations, takes into account shipping-related promotions.
ShippingServiceLevel
A shipping option associated with a shipping region.
ShoppingCartImpl
Gets the shipping cost based on the selected shipping service level
Shipping Costs associated with different service levels:
EpRuleEngine
Applies promotion rules that may affect shipping costs.
ShippingCostCalculationMethod
Calculates shipping costs for a given Shopping Cart, depending on which shipping option was selected, which promotions are in effect, the customer’s shipping region, and the shipping cost calculation method selected for use in the Commerce Manager.
AbstractShippingCostCalculationMethodImpl
Abstract implementation of a shipping cost calculation.
The following classes extend
AbstractShippingCostCalculationImpl
to implement specific shipping cost calculationsCostPerUnitWeightMethodImpl
FixedBaseAndCostPerUnitWeightMethodImpl
FixedBaseAndOrderTotalPercentageMethodImpl
FixedPriceMethodImpl
OrderTotalPercentageMethodImpl
How it works
The CheckoutService
then calls ShippingServiceLevelService.retrieveShippingServiceLevel(shoppingCart)
to get a list of of valid shipping service levels for the shipping address.
It does this by asking the database for all the ShippingServiceLevels
in the tshippingservicelevel
table, then filtering out the levels that do not apply to the region in which the shipping address is located.
The CheckoutService
method then calls the ShoppingCart.setShippingServiceLevelList()
, which in turn calls calculateShippingCost(shoppingCart)
on each ServiceLevel
in the list. These calls initiate the shipping cost calculation based on the items in the shopping cart, and set the first ShippingLevel
as the default selected level in the ShoppingCart
.
At this point, it’s time to apply the promotion rules. The shopping cart calls EpRuleEngine.fireOrderPromotionrules(shoppingCart)
.
The Rule Engine loops through all the CartItems in the ShoppingCart, using the drools engine to apply promotions rules. See the Promotion rule engine for information on how these work.
The available service levels are then displayed through the front end.