Final order processing
Final order processing
The final order processing during the checkout process is implemented by the CheckoutService. To perform a checkout, 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.
The final order processing can be broken down into three subgroups of actions which occur after the customer confirms his/her purchase in the storefront checkout (see the abstractCheckoutServiceDelegate bean in the core project's service.xml file):
- Setup actions (setupActionList).
- Reversible actions (reversibleActionList).
- Finalize actions (finalizeActionList).
<html><body> <pre class="j-path">core<span class="j-pathsep">/</span>ep-core<span class="j-pathsep">/</span>src<span class="j-pathsep">/</span>main<span class="j-pathsep">/</span>resources<span class="j-pathsep">/</span>spring<span class="j-pathsep">/</span>service<span class="j-pathsep">/</span>service.xml</pre> <pre class="j-text"><code><bean id="abstractCheckoutServiceDelegate" abstract="true" class="com.elasticpath.service.shoppingcart.impl.CheckoutServiceImpl"> <property name="shippingServiceLevelService" ref="shippingServiceLevelService"/commerce-legacy/> <property name="setupActionList"> <list> <ref bean="preCheckoutCheckoutAction" /> <ref bean="shippingInformationCheckoutAction" /> <ref bean="giftCertificateBalanceCheckerCheckoutAction" /> <ref bean="stockCheckerCheckoutAction" /> <ref bean="updateCustomerCheckoutAction" /> </list> </property> <property name="reversibleActionList" ref="reversibleActions"/commerce-legacy/> <property name="finalizeActionList"> <list> <ref bean="clearShoppingCartCheckoutAction" /> <ref bean="postCheckoutCheckoutAction" /> <ref bean="sendNotificationEmailCheckoutAction" /> </list> </property> </bean> <util:list id="reversibleActions" list-class="java.util.ArrayList"> <ref bean="createNewOrderCheckoutAction" /> <ref bean="populateOrderDataCheckoutAction" /> <ref bean="createGiftCertificatesCheckoutAction" /> <ref bean="populateTemplateOrderPaymentCheckoutAction" /> <ref bean="processOrderPaymentsCheckoutAction" /> <ref bean="updateLimitedUsageNumbersCheckoutAction" /> <ref bean="subscriptionCreditCheckCheckoutAction" /> <ref bean="updateOrderCheckoutAction" /> <ref bean="processCouponCustomerAssignmentsCheckoutAction" /> </util:list></code></pre> </body></html>
This service.xml file can be modified to refer to your custom implementations of the CheckoutAction interface.
Each step corresponds to one of the beans listed above.
- Verify inventory (stockCheckerCheckoutAction).
- Update customer (updateCustomerCheckoutAction).
- Create new order (createNewOrderCheckoutAction).
- Create purchased gift certificates (createGiftCertificatesCheckoutAction).
- Populate OrderPayment template object (populateTemplateOrderPaymentCheckoutAction).
- Process payments (processOrderPaymentsCheckoutAction).
- Update usage counts on limited-use coupons and promotions (updateLimitedUsageNumbersCheckoutAction).
- Conduct credit check for recurring charge items (subscriptionCreditCheckCheckoutAction).
- Persist order to database (updateOrderCheckoutAction).
- Assign promotion coupons to customer (processCouponCustomerAssignmentsCheckoutAction).
- Clear shopping cart (clearShoppingCartCheckoutAction).
- Send order confirmation e-mail (sendNotificationEmailCheckoutAction).
preCheckoutCheckoutAction and postCheckoutCheckoutAction do nothing in the OOTB source code.
The CheckoutAction implementations of each step delegate to the following key classes and files:
Key classes and files
- BillingAndReviewControllerImpl - Spring MVC controller for the final checkout step.
- CheckoutService - Service that executes a checkout when requested by the BillingAndReviewControllerImpl.
- CheckoutEventHandler - 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 functionality.
- ShoppingCart - The shopping cart contains items to be purchased during a checkout.
- OrderPayment - An order payment represents information about how the customer will pay for the order.
- PaymentGateway - Interface implemented by payment gateways that handle financial transactions such as credit card or PayPal payments.
- Order - Represents a completed order for products.
- OrderSku - A snapshot of information about a purchased SKU at the time of checkout.
- OrderShipment - Contains shipping information for an order.
- Inventory - Contains inventory information for a particular product SKU.
- OrderAddress - A snapshot of the customer's address (billing and shipping) at the time of checkout.
Click the thumbnail below to view the relationships between key classes involved in the checkout and order creation process.
The CheckoutService Spring configuration injects a CheckoutEventHandler instance into the Service, which provides hooks into the checkout process at certain points. By creating a new class implementing the CheckoutEventHandler interface and wiring in the new class via the Spring configuration, one can easily extend the Checkout Process at certain steps to call custom methods. Three extensible points exist: preCheckout, postCheckout, and preCheckoutOrderPersist (called just before the newly-created Order is persisted).
For example, if you wanted to have some code that called a webservice in some other application every time a checkout was completed, you could write a new class extending the CheckoutEventHandler and put the code in that class' postCheckout() method. Then you would edit the Spring bean definition for the CheckoutService to inject your new class into the Service, and your code would automatically be called at the right time.
The checkout service is configured in Spring so that the checkout method does NOT run as a transaction. This is because we don't want to trigger transaction overhead while processing credit card payments. If the credit card payment is successful, checkout calls processOrder(), which does run as a transaction and handles the creation and storage of the order information. Transactions are configured on a per-method basis in the service.xml Spring configuration file.
Orders, OrderPayments, and OrderShipments all have Status fields: OrderStatus, OrderPaymentStatus, and OrderShipmentStatus respectively.
The current OrderStatus values are:
The current OrderPaymentStatus values are:
The current OrderShipmentStatus values are:
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 OrderService.getNextOrderNumber() 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.
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 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.