Understand the b2c checkout flow in Hybris
1. Overview
In an eCommerce website, the Checkout is the set of steps showing to a customer before placing an order.
This is a simplified overview of the default checkout flow for the b2c accelerator in Hybris.
As a clean code and design patterns fan, the checkout is one of my favorite piece of code in Hybris.
In this article, we will try to dissect the checkout steps in details.
2. Checkout Step
A checkout step is defined by 3 main components :
- Checkout Step object: an instance of the
CheckoutStep
. - Checkout Step controller: a Spring MVC controller.
- Checkout Step view page: a JSP page.
2.1. Checkout Step Object
A checkout step object is an instance of the CheckoutStep
class, it’s declared in Spring by the bean id checkoutStep
.
A CheckoutStep
composed of 4 elements :
- CheckoutGroup : it’s the container of all the checkout steps (see next chapter).
- CheckoutStepValidator : every checkout step has its own validator, it’s used to check whether we are eligible to access to a checkout step or not (for example you can’t access to the payment method step without passing by the delivery address and the delivery method steps).
- Transitions : it holds the redirection URLs to the previous, the next and the current checkout steps controllers.
- progressBarId : it’s is a unique id of the checkout step, used for breadcrumb.
E.g. This is the definition of the delivery address step, it is located in the multi-step-checkout-config.xml
.
<!-- checkout step definintion -->
<alias name="defaultResponsiveDeliveryAddressCheckoutStep" alias="responsiveDeliveryAddressCheckoutStep" />
<bean id="defaultResponsiveDeliveryAddressCheckoutStep" parent="checkoutStep">
<property name="checkoutGroup" ref="responsiveCheckoutGroup"/>
<property name="checkoutStepValidator" ref="defaultResponsiveDeliveryAddressCheckoutValidator"/>
<property name="transitions">
<map merge="true">
<entry key="previous" value-ref="REDIRECT_TO_CART"/>
<entry key="current" value-ref="REDIRECT_TO_DELIVERY_ADDRESS"/>
<entry key="next" value-ref="REDIRECT_TO_DELIVERY_METHOD"/>
</map>
</property>
<property name="progressBarId" value="deliveryAddress"/>
</bean>
<!-- Redirects -->
<bean id="REDIRECT_TO_CART" class="java.lang.String">
<constructor-arg value="redirect:/cart"/>
</bean>
<bean id="REDIRECT_TO_DELIVERY_ADDRESS" class="java.lang.String">
<constructor-arg value="redirect:/checkout/multi/delivery-address/add"/>
</bean>
<bean id="REDIRECT_TO_DELIVERY_METHOD" class="java.lang.String">
<constructor-arg value="redirect:/checkout/multi/delivery-method/choose"/>
</bean>
2.2. Checkout Step Controller
The checkout step controller should extend the AbstractCheckoutStepController.java
and implement the following methods:enterStep()
, next()
, back()
and getCheckoutStep()
.
enterStep()
: it’s the first entry point to the checkout step, it should return back the checkout step view page (see next section).back()
: called whenever we want to go back to the previous checkout step.next()
: called whenever we want to go to the next checkout step.getCheckoutStep()
: should be implemented for the purpose of retrieving the currentCheckoutStep
.
E.g. This is a lite version of the checkout step controller of the delivery address step.
@Controller
@RequestMapping(value = "/checkout/multi/delivery-address")
public class DeliveryAddressCheckoutStepController extends AbstractCheckoutStepController
{
private static final String DELIVERY_ADDRESS = "delivery-address";
// implementation of the enterStep() method
@Override
@RequestMapping(value = "/add", method = RequestMethod.GET)
@RequireHardLogIn
@PreValidateQuoteCheckoutStep
@PreValidateCheckoutStep(checkoutStep = DELIVERY_ADDRESS)
public String enterStep(final Model model, final RedirectAttributes redirectAttributes) throws CMSItemNotFoundException
{
// ...
return ControllerConstants.Views.Pages.MultiStepCheckout.AddEditDeliveryAddressPage;
}
// implementation of back() method : go to previous checkout step
@RequestMapping(value = "/back", method = RequestMethod.GET)
@RequireHardLogIn
@Override
public String back(final RedirectAttributes redirectAttributes)
{
return getCheckoutStep().previousStep();
}
// implementation of next() method : go to next checkout step
@RequestMapping(value = "/next", method = RequestMethod.GET)
@RequireHardLogIn
@Override
public String next(final RedirectAttributes redirectAttributes)
{
return getCheckoutStep().nextStep();
}
// implementation of the getCheckoutStep() method : get current CheckoutStep
protected CheckoutStep getCheckoutStep()
{
return getCheckoutStep(DELIVERY_ADDRESS);
}
}
The
@PreValidateCheckoutStep(checkoutStep = DELIVERY_ADDRESS)
calls the validator of the delivery address step defined previously in the xml.
2.3. Checkout Step View
The checkout step view page is a basic JSP page, it’s the same JSP page returned by the enterStep()
method of the checkout step controller.
The checkout step view page should have a link to the next()
and back()
method of the checkout step controller.
E.g. The checkout JSP page for delivery address step is.
${HYBRIS_BIN_DIR}/custom/training/trainingstorefront/web/webroot/WEBINF/views/responsive/pages/checkout/multi/addEditDeliveryAddressPage.jsp
3. Checkout Group
The checkout group or checkout flow group is a container of the checkout steps, technically it’s an instance of the CheckoutGroup
.
The default checkout group used for a given CMS site (e.g b2c electronics) is defined in the BaseStoreModel
.
E.g. The default checkout group of the electronics b2c accelerator is responsiveCheckoutGroup
.
The responsiveCheckoutGroup
is defined as :
<alias name="defaultResponsiveMultiStepCheckoutGroup" alias="responsiveCheckoutGroup" />
<bean id="defaultResponsiveMultiStepCheckoutGroup" class="de.hybris.platform.acceleratorstorefrontcommons.checkout.steps.CheckoutGroup">
<property name="groupId" value="responsiveCheckoutGroup"/>
<property name="checkoutStepMap">
<map merge="true">
<entry key="multi" value-ref="responsiveMultiStepCheckout"/>
<entry key="delivery-address" value-ref="responsiveDeliveryAddressCheckoutStep"/>
<entry key="delivery-method" value-ref="responsiveDeliveryMethodCheckoutStep"/>
<entry key="payment-method" value-ref="responsivePaymentMethodCheckoutStep"/>
<entry key="summary" value-ref="responsiveSummaryCheckoutStep"/>
</map>
</property>
<property name="validationResultsMap">
<map merge="true">
<entry key="FAILED" value-ref="REDIRECT_TO_CART"/>
<entry key="REDIRECT_TO_DELIVERY_ADDRESS" value-ref="REDIRECT_TO_DELIVERY_ADDRESS"/>
<entry key="REDIRECT_TO_CART" value-ref="REDIRECT_TO_CART"/>
<entry key="REDIRECT_TO_PAYMENT_METHOD" value-ref="REDIRECT_TO_PAYMENT_METHOD"/>
<entry key="REDIRECT_TO_DELIVERY_METHOD" value-ref="REDIRECT_TO_DELIVERY_METHOD"/>
<entry key="REDIRECT_TO_SUMMARY" value-ref="REDIRECT_TO_SUMMARY"/>
</map>
</property>
<property name="checkoutProgressBar">
<map merge="true">
<entry key="1" value-ref="responsiveDeliveryAddressCheckoutStep"/>
<entry key="2" value-ref="responsiveDeliveryMethodCheckoutStep"/>
<entry key="3" value-ref="responsivePaymentMethodCheckoutStep"/>
<entry key="4" value-ref="responsiveSummaryCheckoutStep"/>
</map>
</property>
</bean>
- checkoutStepMap : a map contains the reference to the
CheckoutSteps
. - validationResultsMap : redirection URLs as constants used when it’s needed.
- checkoutProgressBar : it’s is a unique id of the checkout steps, used for breadcrumb.
4. Checkout Group Map
The checkout map group is a Map
that contains all the checkout flow group, we can retrieve a specific checkout flow group from the map using its id (key).
<!--Checkout GroupMap-->
<util:map id="checkoutFlowGroupMap" >
<entry key="apparelCheckoutGroup" value-ref="apparelCheckoutGroup"/>
<entry key="defaultCheckoutGroup" value-ref="defaultCheckoutGroup"/>
<entry key="responsiveCheckoutGroup" value-ref="responsiveCheckoutGroup"/>
</util:map>
<!--Checkout GroupMap-->
5. CheckoutFacade
The CheckoutFacade
and its implementation DefaultCheckoutFacade
is crucial for a proper functioning of the checkout flow.
It’s injected by default into the AbstractCheckoutController.java
, hence it could be used within the checkout step controller.
Some of the useful methods from the CheckoutFacade.java
.
boolean hasCheckoutCart()
: to check if checkout cart exist.CartData getCheckoutCart()
: to retrieve the current checkout cart if it exists.void prepareCartForCheckout()
: to prepare cart for the first time for the checkout.OrderData placeOrder()
: to place an order.boolean setDeliveryAddress(AddressData)
: to set a delivery address for the checkout cart.List<DeliveryModeData> getSupportedDeliveryModes()
: to get supported delivery modes for the checkout cart based on the delivery address.
6. Checkout in action
6.1. Checkout preparation
The checkout flow started when your click on the CHECK OUT button on the cart page.
The checkout()
method of the CheckoutController
is the first thing getting invoked via the request URL /checkout.
@Controller
@RequestMapping(value = "/checkout")
public class CheckoutController extends AbstractCheckoutController {
//...
@RequestMapping(method = RequestMethod.GET)
public String checkout(final RedirectAttributes redirectModel) {
if (getCheckoutFlowFacade().hasValidCart()) {
// if cart not valid go to Cart page
if (validateCart(redirectModel)) {
return REDIRECT_PREFIX + "/cart";
}
// else prepare the current Cart for the checkout and redirect to /checkout/multi
else {
checkoutFacade.prepareCartForCheckout();
return getCheckoutRedirectUrl();
}
}
// No session cart or empty session cart. Bounce back to the cart page.
return REDIRECT_PREFIX + "/cart";
}
//...
}
This is not a step of the checkout flow group, its main role is the validation and the preparation of the checkout cart then redirect to the /checkout/multi URL, which is the first step of checkout flow :
responsiveMultiStepCheckout
.
6.2. Multi Checkout Step
This is the first checkout step of the checkout flow group, defined with :
- Checkout Step object id : responsiveMultiStepCheckout
- Checkout Step Controller : MultiStepCheckoutController
- URL Mapping : /checkout/multi
- Checkout Step View : NONE
This is just an additional verification step, then redirect the customer to the second checkout step: responsiveDeliveryAddressCheckoutStep
.
6.3. Delivery Address Checkout Step
Is the second checkout step, in this step, the customer is invited to provide its shipping/delivery address.
Technically this checkout step is defined with :
- Checkout Step object id : responsiveDeliveryAddressCheckoutStep
- Checkout Step Controller : DeliveryAddressCheckoutStepController
- URL Mapping : /checkout/multi/delivery-address
- Checkout Step View : pages/checkout/multi/addEditDeliveryAddressPage.jsp
6.4. Delivery Method Checkout Step
In this step, the customer is invited to choose one of the supported delivery/shipping methods depending on its delivery address.
Technically the delivery method step is defined with :
- Checkout Step object id : responsiveDeliveryMethodCheckoutStep
- Checkout Step Controller : DeliveryMethodCheckoutStepController
- URL Mapping : /checkout/multi/delivery-method
- Checkout Step View : pages/checkout/multi/chooseDeliveryMethodPage.jsp
6.5. Payment Method Checkout Step
In the payment checkout step, the customer is asked to choose from one of the supported payment methods and enter it’s payment credentials.
Technically the payment method checkout step is defined with :
- Checkout Step object id : responsivePaymentMethodCheckoutStep
- Checkout Step Controller : PaymentMethodCheckoutStepController
- URL Mapping : /checkout/multi/payment-method
- Checkout Step View : pages/checkout/multi/addPaymentMethodPage.jsp
In case of the HOP payment module, the customer will be redirected to an externally hosted page.
For the sake of simplicity, I prefer to not mention some more details about this step as I will shed lights on it later on in a separated post 🙂
6.6. Summary Checkout Step
This is the last checkout step, in this step, a detailed summary of the order is displayed to the customer, so the customer can confirm/place the order or edit it.
Technically this checkout step is defined with :
- Checkout Step object id : responsiveSummaryCheckoutStep
- Checkout Step Controller : SummaryCheckoutStepController
- URL Mapping : /checkout/multi/summary
- Checkout Step View : pages/checkout/multi/checkoutSummaryPage.jsp
Software Craftsmanship, Stackextend author and Full Stack developer with 6+ years of experience in Java/Kotlin, Java EE, Angular and Hybris…
I’m Passionate about Microservice architectures, Hexagonal architecture, Event Driven architecture, Event Sourcing and Domain Driven design (DDD)…
Huge fan of Clean Code school, SOLID, GRASP principles, Design Patterns, TDD and BDD.
Greatwork moulad
Hi Teja, thanks for getting in touch.
thanks for sharing (y)
Thanks, Ayoub!
good tutorial i am new to hybris could you please explain starting with ant modulegen
we will get 7 extension ex: initialdata, storefront ,core,cockpits….etc
initialdata ::storefront :: core:: what we are going to write hear
Start the hybris server and what url to use in localhost to see site
Hi, I think you will find answer to your questions here : https://www.stackextend.com/hybris/install-hybris-b2c-accelerator-step-by-step/
Good Article. Nicely Explained.
Glad that you found it helpful, thanks!
Nice explanation. Can you please share a new article with an example if possible with complete flow i.e starting from adding product to cart to delivering product to customer. It would be really helpful for people who are new to hybris.
Thanks bro…..wiki is shit compared to your website
Thanks John, I appreciate it 🙂
could u explain the payment module integration in hybris of paypal or any other intergartion
I’m planning to write a post about it !
t
Thanks for the kudos! I feel great!
Great explanation Mouad !!! Can you please try to add more posts on Cart, Payment Integration, Data Hub Integration
How can I customize/ overwrite the payment page in hybris 6.7.
Very nice article, can you please add the order confirmation process that will help us in end to end flow of checkout.
one of the best checkout customization tutorial.
great work man.. !!
I’m going to integrate custom payment gateway in Hybris 1905 version. Need your guidance as you skipped the part I need to know in your steps mentioned above : “In case of the HOP payment module, the customer will be redirected to an externally hosted page. For the sake of simplicity, I prefer to not mention some more details about this step as I will shed lights on it later on in a separated post ” How I can update and go to next step when I get success payment redirection from payment provider page. Also need to know how… Read more »
You need to implement a new url (MVC Controller) to handle the callback responses from the payment provider, e.g /hop/callback?response=success and /hop/callback?response=failure, in case of response=success redirect to the next step.
Session/cart should not be an issue if you are using a StickySession strategy for load balancing.
No issue on success / fail callback. But I need your valuable guidance how I can update payment in to order when get response from provider in my ipn_url which hits my server as and when payment success. Session not available only I get guid which I sent during payment request.
Please help me to get cart data, user data and shipping data on with only carddata.getGuid() , so that I can place order
Hello, you should check why the session is not available.
Basically you should have access to your current session, the same session that been used before redirection to payment provider.
If you fixed that you will be able to get cart from session then create order and update it.
Other thing for non received callback due to client browser issues, you have to implement a new controller to handle notification from payment provide. and in this case you have to use order_id provided during payment to get your cart and create order then update its payment info.
In checkout flow for multistep ( Hybris version : 1905 ) after payment success final review comes and final order placed. I’m going to implement where qr code is getting generated in provider site ( redirected to provider site from base site) . After making payment client returned to success page as per setting during qr code generation. Secondly, as soon as payment is made via app from scanning qr code provider hits my ipn_url silently as per setting during QR code generation. I like to update payment for this order /cartData when ipn_url hits by providers serve on receipt… Read more »
Yes you are right if providers service hits your server there is no session available, so in this case you should work like your received a notification form provider, you have to find a way to make provider send you an id and use this id to get your cart or whatever you need to get. on most of case the provider send a notification with payment info as parameters of its request so you can use them to update you cart/order.
Yes, I’m sending guid of cart to provider which I get return on notification. My question is how I’ll get customer model and other required model to place order. Is it feasible to get all those model only by cart guid, my provider only accept and return in notify url one id that is only 32 character long. Otherwise I can sent all model id in coded form. I need help from an expert SAP developer like you.
Great work, what i was looking for since last months, you did it exactly , i appreciated Mouad EL Fakir
Good article… Explained well
I like the way all relevant details are provided, like sectionalized content, code references when needed, etc.
Helps understand overall flow.
Thanks.
thanks for sharing
Nicely explained, Thank you. Can you tell how to create a new checkout step. I tried to create one but was not able to render the template.
great work