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 orderPaymentProvider
- Interface implemented by payment providers 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
.
Error Handling
When a problem occurs while executing a method on a PaymentGateway
, an exception is thrown to inform the client. All vendor-specific problems should be consumed and reported as one of the following standard exception types:
PaymentGatewayException
Indicates that the payment processor has failed to process a request. For example, an internal error or communication failure has occurred.
CardErrorException
An unspecified error has occurred in processing the card information. This exception is thrown when there is a problem with the card data and not with the payment processor itself.
CardExpiredException
The card being processed has expired.
CardDeclinedException
The card being processed was declined. For example, the card has insufficient credit for the purchase.
InsufficientFundException
The payment method was declined due to insufficient funds
More exceptions can be found in the com.elasticpath.plugin.payment.exceptions
package in the payment-gateway-extension-point
project.
Configuring the Payment Gateway Factory
To use your new payment gateway, you need to make the payment gateway factory aware of it. In your Core extension, you will need to override the paymentGatewayFactory
bean definition in the service.xml
file in your Core extension project. Add an entry to the gatewayClasses
set property with the fully qualified class name of your payment gateway plugin implementation:
<bean id="paymentHandlerFactory" class="com.elasticpath.domain.payment.impl.PaymentHandlerFactoryImpl">
<property name="elasticPath" ref="elasticPath"/>
</bean>
<bean id="paymentGatewayFactory" class="com.elasticpath.domain.payment.impl.PaymentGatewayFactoryImpl">
<property name="settingsReader" ref="settingsService"/>
<property name="giftCertificateTransactionService" ref="giftCertificateTransactionService"/>
<property name="gatewayClasses">
<set>
<value>com.elasticpath.service.payment.gateway.impl.NullPaymentGatewayPluginImpl</value>
<value>com.elasticpath.service.payment.gateway.impl.ExchangePaymentGatewayPluginImpl</value>
<value>com.elasticpath.service.payment.gateway.impl.GiftCertificatePaymentGatewayPluginImpl</value>
<value>com.elasticpath.service.payment.gateway.impl.MyPaymentGatewayImpl</value>
</set>
</property>
</bean>
A maven dependency also needs to be added to the extension-core
pom.xml
file for the new payment gateway:
<!-- Payment Gateways START -->
<dependency>
<groupId>com.extension.mypaymentgateway</groupId>
<artifactId>my-payment-gateway</artifactId>
<version>0-SNAPSHOT</version>
<scope>runtime</scope>
</dependency>
.....
Using the Payment Gateway
To use your new credit card gateway implementation, you will first need to create a new payment gateway configuration in the CM, and then configure your store to use it.
- In the CM, click on Configuration icon.
- Under Payment Methods, click Payment Gateways.
- Click Create Gateway.
- Enter the payment gateway name.
- Select the gateway implementation the Type list.
- Set any required properties.
- Click Save.
To use the new payment gateway in your store:
- Click on Configuration icon.
- Click Stores.
- Select the store in the list and click Edit Store.
- Click the Payments tab.
- Select the payment gateway in the Credit Card Payment Gateway list.
- Configure the options for enabling the CCV (Credit Card Verification) value, saving credit card numbers, and the supported card types.
- Click Save.
The Payment Gateway Certificate File
Many payment gateways issue a certificate file to the merchant and require them to use it in the client code. These gateways may have properties to denote the location of the certificate file(s) and the name of the certificate file.
There is a system wide setting that is used for the base directory of all the payment-related certificate key files. The setting resides in COMMERCE/SYSTEM/PAYMENTGATEWAY/certificatesDirectory
, and can be changed through Commerce Manager’s configuration perspective. When setting the payment gateway configurations in the Commerce Manager, the path to the certificate should be relative to the base directory mentioned by the above setting.
The payment gateway implementation class can access that base directory through a protected method on AbstractPaymentGatewayPluginSPI
:
/**
* Gets the path prefix for the payment gateway certificate files.
*
* @return the path prefix
*/
protected String getCertificatePathPrefix() {
return certificatePathPrefix;
}
Caveats
Many processors will provide a testing infrastructure. Occasionally, this means a test merchant account, test card numbers, and possibly a different URL from what should be used in production. See your processor’s development documentation to understand how to test it. When moving to production, the first thing you will want to do is to test it. This means using a live credit card. There aren’t many ways around this. However, you should have access to a console to manage your merchant account, and be able to cancel any transactions.
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.