Solving Magento

Solutions for Magento E-Commerce Platform

by Oleg Ishenko

OneStep Checkout – A Magento Tutorial, Part 1 (Steps 1 – 4 of 12)

In this tutorial, I will demonstrate an extension that adds a OneStep checkout to Magento. In my earlier post Magento OnePage Checkout, I’ve explained the ideas behind different checkout types. In short, the checkout process is usually broken into steps, and in each step customers must provide a certain part of the checkout data. Despite the wide use of the step-by-step checkout, many online retailers find it too cumbersome for customers – especially the part where the browser has to load a new URL for each step. A different approach is the OnePage checkout such as the one implemented in Magento where all steps a brought under a single URL. The checkout shows only one step at a time and uses Ajax requests to switch between steps and refresh the checkout state without browser reloading the page. The process, however, retains its step character: customers must fill out all steps separately and in a specific order.

The OneStep approach is a further simplification of the checkout process: all step forms are shown at once and customers no longer need to submit each step individually. But, there is a catch: the customer must fill out the entire checkout form in one go. If you take all the fields of the Magento OnePage checkout and display them all in one step, they will make a long and intimidating form. Not only that might scare some buyers off, but implementing the dependencies between fields (e.g., shipping methods depending on the delivery destination) would be even more challenging. This is the exact opposite of a simple and transparent process that the OneStep checkout aims to achieve. For these reasons the use of the OneStep checkout is confined to special cases where the checkout process doesn’t need much information from customers. One such example could be a shop selling downloads which need no physical shipping and no fields for a shipping address and shipping methods are necessary. In this case, the checkout form is short: customer billing address, payment method, order overview – and ideal premise for a OneStep checkout.

In my extension I will not restrict myself to any specific scenario; instead, I will attempt to build a OneStep checkout as a replacement to the fully fledged Magento OnePage checkout. The necessary compromises may result in a checkout that is in some parameters less satisfactory than a production use would require – but, this I leave to you to judge.

This is a very long tutorial that spans several posts and consists of 12 steps. If you are impatient, you can download the finished extension from GitHub Solvingmagento_OneStepCheckout now. A demo shop with a working OneStep checkout can be found here.

Other parts of this tutorial are: Part 2: Starting with the OneStep Checkout JavaScript, Part3: Deeper into the Checkout Sections, Part4: Order Review.


Building a checkout is one of the most daunting tasks a Magento developer can face. As I’ve said before, the checkout is the most sensitive, make-or-break part of an online shop. Fortunately, there is a solid base to build on – Magento OnePage checkout that is designed for nearly every imaginable situation and is in use by thousands of shops. But still, extending this checkout and converting it into a OneStep version has two big challenges:

  • Creating a sensible checkout layout by accommodating input fields in one page in a clear and user-friendly fashion
  • Implementing dependencies between what used to be individual checkout steps

Checkout layout

This challenge is not only about finding the best way to display every address, shipping and payment method input field together in one place. In addition to that, I must figure out how to deal with different scenarios that a checkout can have:

  • A non-logged in customer opens the checkout page. The checkout must offer him to log in or register, or to proceed as a guest (submitting an order without creating a shop account – if the shop has the guest checkout enabled)
  • A logged-in customer opens the checkout page – the choices from the previous case are redundant and must be hidden
  • The customer has saved addresses in his shop account – the checkout must offer to use one of them as a billing or shipping address
  • The shopping cart contains virtual items only – the checkout must hide the fields for the shipping address and shipping methods

All these cases must be dealt in such a way that in each one the checkout layout remains clear and logical.

Field dependencies

This challenge arises from the step-less nature of the OneStep checkout. In the OnePage checkout, data entered in the earlier steps may influence the choices available in later stages:

  • Selecting “Use Billing address for shipping” makes the checkout skip the shipping address step
  • A selected shipping address may restrict available shipping methods
  • A selected shipping method may restrict available payment methods

In Magento’s OnePage checkout these dependencies are not a problem – to see the next step the customer must complete the step before, which updates the quote, which in turn allows the system to render the next step with all possible dependencies accounted for. In the OneStep checkout I must devise a way to update the quote in a similar fashion whenever the inter-step dependencies play a role.

Step 1: Directory structure and configuration files

Before starting the coding, I must set up a directory structure for my extension. As you can see in Figure 1, I place the extension in the local code pool under the folder containing my package “Solvingmagento”.

Figure 1. The directory structure of the OneStepCheckout extension.

My extension will also need a set of templates, whose directory structure I place under the default theme forlder of the base design package (Figure 2).

Figure 2. The directory structure of the OneStep checkout templates.

The OneStep checkout requires a set of JavaScript files that will control the user interface interactions. For these I create a directory structure under the skin folder (Figure 3).

Figure 3. The directory structure for the JavaScript layer fo the OneStep checkout.

By placing the checkout JavaScript files I follow the same pattern used by Magento’s Onepage checkout, which will allow customizing the JavaScript behavior of the checkout on the skin level.

The next addition is the OneStep checkout styles file, which I also place under the skin folder (Figure 4).

Figure 4. The styles file location.

Every Magento extension requires a module registration file, which I create under /app/etc/modules. In this file I declare a dependency on the Mage_Checkout module, whose classes I will extend in this tutorial (Listing 1).

<?xml version="1.0"?>

Listing 1. The module registration file, Solvingmagento_OneStepCheckout.xml.

My next action is to add the configuration files to etc directory of my extension. The config.xml file extends the blocks, models, and helpers configuration nodes and declares a front-end layout updates file.

                <slvmto_onestepc module="Solvingmagento_OneStepCheckout">

Listing 2. The config.xml file.

Listing 2 shows the module configuration file at the initial stage of the tutorial. It will be extended as I go on.

Next, I create a system.xml file which will declare shop configuration nodes specific to my extension. My goal is to have a back-end setting that allows me to enable and disable the OneStep checkout. This setting will be available under System > Configuration > Sales > Checkout > Enable OneStep Checkout as shown in Figure 5.


Figure 5. Enabling and disabling the OneStep checkout in the back-end

Listing 3 shows the code required to configure this setting.

                        <onestep_checkout_enabled translate="label">
                            <label>Enable OneStep Checkout</label>

Listing 3. Adding a setting to the shop configuration, etc/system.xml.

I want the OneStep checkout to be disabled by default, so I add a default node to the configuration in the config.xml file (Listing 4).

    <!--code omitted for brevity -->

Listing 4. Extending the config.xml file with a <default> node, etc/config,xml, lines 57 – 65.

The helper class of my extension uses this setting to tell the system whether the OneStep checkout is enabled or not (Listing 5).

class Solvingmagento_OneStepCheckout_Helper_Data extends Mage_Core_Helper_Abstract
    public function oneStepCheckoutEnabled()
        return (bool)Mage::getStoreConfig('checkout/options/onestep_checkout_enabled');

Listing 5. Checking the enabled status of the OneStep checkout, Helper/Data.php, line 34.

Step 2. Checkout model

Similar to the standard Magento OnePage checkout, my new checkout extension will consist of a model, controller, and a set of views (blocks and templates). The model will contain the business logic, the controller will be responsible for receiving HTTP requests and returning responses, blocks and templates will be used to render the checkout front-end output.

I base my extension of the Mage_Checkout module – hence the dependency I’ve declared in the registration file. My checkout model class will extend the model class of the OnePage checkout – Mage_Checkout_Model_Type_Onepage – and use most of its business logic unchanged. There are, however, a few methods that I have to modify.

First, in the __construct method, I set a helper instance pointing to the class I’ve set up for my extension (Listing 6).

public function __construct()
    //instead of
    //$this->_helper = Mage::helper('checkout');

    $this->_helper = Mage::helper('slvmto_onestepc');
     * code omitted for brevity

Listing 6. Setting the helper instance, Model/Type/Onestep.php, line 42.

Second, in the initCheckout method, I remove the code that checks and sets the step_data property of the checkout model. This property is used to manage the step navigation within the OnePage checkout. The OneStep checkout needs no such functionality (Listing 7).

public function initCheckout()
    $customerSession = $this->getCustomerSession();

    //$checkout = $this->getCheckout();
    //$customerSession = $this->getCustomerSession();
    //if (is_array($checkout->getStepData())) {
    //    foreach ($checkout->getStepData() as $step=>$data) {1
    //      if (!($step==='login' || $customerSession->isLoggedIn() && $step==='billing')) {
    //         $checkout->setStepData($step, 'allow', false);
    //      }
    //    }
    // }

    if ($this->getQuote()->getIsMultiShipping()) {

    $customer = $customerSession->getCustomer();
    if ($customer) {
    return $this;

Listing 7. Removing the code not used by the OneStep checkout, /Model/Type/Onestep.php, line 57.

My final two modifications are in methods saveBilling and saveShipping where I add an additional validation of the $customerAddressId parameter which these methods receive to load an existing customer address (Listing 8).

public function saveShipping($data, $customerAddressId)
     * code omitted for brevity
    if (!empty($customerAddressId)) {
        $customerAddress = Mage::getModel('customer/address')->load($customerAddressId);

        //extra address check added
        if (!$customerAddress->getId()) {
            return array('error' => 1,
                'message' => Mage::helper('checkout')->__('Customer Address is not valid.')
     * code omitted for brevity

Listing 8. Checking if the customer address is valid, Model/Type/Onestep.php, line 105.

Step 3. Controller basics

My controller class also extends its OnePage counterpart, Mage_Checkout_OnepageController. The modifications which I must make to the controller logic mostly deal with the necessity to implement the dependencies between checkout steps. Besides this, I have to take into account some differences in the user interaction that are imposed by the one-step nature of the new checkout.

The first of these peculiarities is the way the new checkout interprets the settings Allow Guest Checkout and Require Customer To Be Logged In To Checkout. The OnePage checkout checks this settings in its _predispatch method (Listing 9).

public function preDispatch()
     * code omitted for brevity

    return $this;

Listing 9. Checking if a guest customer is allowed to check out, /app/code/core/Mage/Checkout/controllers/OnepageController.php, line 42.

In the _preDispatch method, the controller calls is protected function _canShowForUnregisteredUsers, which checks whether the both settings allow the unregistered customers to proceed to the checkout; if not, the controller redirects the customer to the 404 page. The only checkout step that an unregistered customer can see regardless of these settings is the “Login Information” (i.e. the index action of the checkout). In this step, the customer must decide between logging into his existing account or creating a new one. But the OneStep checkout is literally one step – I can’t use a step-minded logic there.

My solution to this problem is as follows:

  • If the guest checkout is allowed, simply show the checkout
  • If the guest checkout or checkout without logging in are not allowed, redirect the customer to the log-in page (instead of the 404 page as in the OnePage case).

Listing 10 shows the modified _canShowForUnregisteredUsers method.

protected function _canShowForUnregisteredUsers()
    $guestAllowed = Mage::getSingleton('customer/session')->isLoggedIn()
        || Mage::helper('checkout')->isAllowedGuestCheckout($this->getOnestep()->getQuote())
        || !Mage::helper('checkout')->isCustomerMustBeLogged();

    if (!$guestAllowed) {
            Mage::helper('checkout')->__('Please login or register to continue to the checkout')
        $this->setFlag('', self::FLAG_NO_DISPATCH, true);
        //return true to the caller method _preDispatch so that it doesn't redirect to the 404 page
        return true;
    } else {
        return true;

Listing 10. Checking if the customer must be redirected to the log-in page, controllers/OnestepController.php, line 36.

Note that in Listing 10 my method always returns true. If a redirect to the log-in page is needed, the controller sets the redirect response in the _canShowForUnregisteredUsers method itself. Had my method returned false, the preDispatch method would have reacted by resetting the redirect to a 404 page. Returning true tricks the controller to think that everything is normal and the redirect happens as I need it to.

This trick is necessary because I can’t rewrite the preDispatch method without messing up the inheritance chain. The OnePage controller’s _preDispatch already contains a call to the parent Mage_Core_Controller_Front_Action::preDispatch function, and I know no way of calling “grandparent” methods from my classes.

The original OnePage controller class has a method called getOnepage that returns a singleton instance of the OnePage checkout model. This method is widely used in the class’ other functions, and I need a similar one for my OneStep controller (Listing 11).

public function getOnestep()
    return Mage::getSingleton('slvmto_onestepc/type_onestep');

Listing 11. Getting an instance of the checkout model in the OneStep controller, controller/OnestepController.php, line 86.

The getOnestep method in Listing 11 returns a singleton instance of my checkout model class Solvingmagento_OneStepCheckout_Model_Type_Onestep using the reference path, which I’ve set up earlier in the module configuration file.

In the OneStep controller class, the first “action” method, i.e. a method that the front controller dispatches an HTTP request to, is indexAction. In this method, I use the code of the parent controller class with only little changes: the calls to the getOnepage method are replaced with the getOnestep calls, and the error message, which is displayed when the OneStep checkout is disabled, now reads: ‘’The onestep checkout is disabled’ instead of “onepage”.

public function indexAction()
    if (!Mage::helper('slvmto_onestepc')->oneStepCheckoutEnabled()) {
            ->addError($this->__('The onestep checkout is disabled.'));
    $quote = $this->getOnestep()->getQuote();
    if (!$quote->hasItems() || $quote->getHasError()) {
    if (!$quote->validateMinimumAmount()) {
        $error = Mage::getStoreConfig('sales/minimum_order/error_message') ?
            Mage::getStoreConfig('sales/minimum_order/error_message') :
            Mage::helper('checkout')->__('Subtotal must exceed minimum order amount');

        ->setBeforeAuthUrl(Mage::getUrl('*/*/*', array('_secure'=>true)));

Listing 12. the indexAction method for the OneStep controller, controllers/OnestepController.php, line 246.

Listing 12 shows the indexAction method, which Magento invokes when a customer opens the OneStep checkout page. The process here is as follows:

  1. The system checks if the OneStep checkout is enabled and, if not, redirects the customer to the shopping cart page where an error message is shown.
  2. The method fetches the current quote from the session using a call to the checkout model instance. If the quote has no items or has an error property, the controller redirects the customer to the shopping cart page.
  3. The method checks if the quote’s amount is below the minimum, which is set in the shop configuration. If this is the case, the customer is redirected to the shopping cart page.
  4. If the checks above are passed, the method initializes the checkout: strips the “Multishipping” marking off the quote, fetches a current customer object from the session and assigns it to the quote. The “Multishipping” marking can be set to a quote, if the customer first attempted to use the Multishipping checkout but didn’t complete it and switched to the OneStep checkout instead.
  5. The last actions in this method are the loading of the layout and the rendering of the output, which Magento then returns to the browser.

The next methods in the controller deal with the Ajax requests that the front-end user interface generates when interacting with the customer. Before I can proceed with the discussion of the implementation of these interactions in the back-end code, I have to make decisions on how the overall process flow is organized. To do that, I must visualize the front-end part of the OneStep checkout first.

Step 4. Designing the layout

In this step, I will face the first challenge: finding a way to pool all input fields of the checkout forms into one step. To do so, I will divide the input fields into groups which correspond to the steps of the OnePage checkout:

  • Checkout Method
  • Billing Information
  • Shipping Information
  • Shipping Method
  • Payment Information
  • Order Review

I will accommodate these groups into a grid layout consisting of three columns and three rows. The first and the third rows will span the three columns in one as shown in Figure 6.


Figure 6. The layout grid of the OneStep checkout.

The first row – “columnUp” – will house the Checkout Method input group. The checkout will show this area only to non-authenticated customers. If a logged-in customer opens the checkout page, Magento will hide “columnUp” and the Checkout Method group.

The second row will house the next four input groups:

  • columnLeft: Billing Information
  • columnCenter: Shipping Information
  • columnRight: Shipping Method and Payment Information, one under another.

In a case of a virtual quote, the checkout hides the Shipping Information and Shipping Method groups and moves the Payment Information group to the columnCenter leaving the columnRight empty.

The third row – columnBottom – is reserved for the Order Review group.

Figure 7 displays the OneStep checkout page as it looks with all groups shown.


Figure 7. OneStep checkout.

Figure 8 shows a case of a logged-in customer buying a download product. The checkout hides the columnUp area and moves the Payment Information group to the center in place of the hidden shipping groups.


Figure 8. A virtual quote in the OneStep checkout.

To implement this layout I use a layout updates file, which I’ve created under /app/design/frontend/base/default/layout/solvingmagento/onestepcheckout.xml. In this file I define a handle for the index action of my checkout – checkout_onestep_index. To this handle I set a one-column template and instruct Magento to load the OneStep checkout styles file (Listing 13).

<checkout_onestep_index translate="label">
    <label>One Step Checkout</label>
    <!-- Mage_Checkout -->
    <reference name="root">
        <action method="setTemplate"><template>page/1column.phtml</template></action>
    <reference name="head">
        <action method="addItem">
    <!-- code omitted for brevity -->

Listing 13. Defining the checkout_onestep_index in the layout updates file, /app/design/frontend/base/default/layout/solvingmagento/onestepcheckout.xml, line 27.

Next, in Listing 14, I add a reference node for the “content” block where I add a wrapper “onestep” block that will house the checkout section blocks.

<reference name="content">
    <!-- onestep wrapper block -->
        <!-- here go the section blocks -->

Listing 14. OneStep wrapper block, /app/design/frontend/base/default/layout/solvingmagento/onestepcheckout.xml, line 40.

The section blocks are these:

  • onestep_login for Checkout Method
  • onestep_billing for Billing Information
  • onestep_shipping for Shipping Information
  • onestep_shipping_method for Shipping Method
  • onestep_payment for Payment Information
  • onestep_review for Order Review

The first three section blocks are simple. They have no child of their own and need only definitions of the block type and template (Listing 15).

    name="checkout.onestep.login" as="login"
    name="checkout.onestep.billing" as="billing"
    name="checkout.onestep.shipping" as="shipping"

Listing 15. The first three children of the OneStep block, /app/design/frontend/base/default/layout/solvingmagento/onestepcheckout.xml, line 45.

The Shipping Method and Payment Information sections are a bit more complex. They are responsible for loading the radio button lists of the available shipping and payment options. My extension doesn’t change the logic that compiles these lists, and I can use the children block configuration from the Shipping Method and Payment Information steps of the OnePage checkout module for the respective sections in OneStep checkout (Listing 16).

<!-- shipping_method blocks and its children -->
<!-- payment information block and its children -->
        <action method="setMethodFormTemplate">

Listing 16. Block configuration of the Shipping Method and Payment Information checkout sections, /app/design/frontend/base/default/layout/solvingmagento/onestepcheckout.xml, line 58.

The final section – Order Review – also has two child blocks:

  • button – Represents the order submit button. I use a custom template for this block because I will have to include some OneStep-specific logic there (more details on that later)
  • agreements – Outputs a set of conditions which a customer must agree to before placing an order. These conditions can be configured in the back-end.

Listing 17. Block configuration of the Order Review section, /app/design/frontend/base/default/layout/solvingmagento/onestepcheckout.xml, line 90.

Note that in the blocks above, even those that still refer to the OnePage checkout classes, I use custom templates which I put under app/design/frontend/base/default/template/solvingmagento/onestepcheckout/. I will talk more about the contents of these templates when discussing the respective checkout sections. But before diving into the details of the checkout sections, I must lay the foundation for the JavaScript component of the new checkout.

This tutorial will continue in the next post, Part 2: Starting with the OneStep Checkout JavaScript.

14 thoughts on “OneStep Checkout – A Magento Tutorial, Part 1 (Steps 1 – 4 of 12)

  1. Wow, awesome!!!

    I need to try this when I get some time, but I feel a stepped checkout is better. (Personal opinion though)

    Cheers, this must have a took some time to write up.

    • Thanks, Philip! True, it did took a while.

      I did say that OneStep checkout works best under special conditions, i.e. a low number of required fields in the checkout. The OneStep checkout I did in this tutorial is a conversion of every possible field of the standard OnePage checkout into one step – hence the awkward result.

  2. Oleg, this is absolutely brilliant. Thank you for offering this as it is a great way to learn more advanced Magento development.

    I have one question, if I may. If I wanted to re-arrange the shipping and billing fields, where shipping is default, would it be best for me to focus on the js and .xml files, or would that require more ‘core file’ hacking of the module itself?

    Thanks for your time.

    • Hi Bernard,

      If I understand correctly, you intend to make customers fill out the shipping address first then the billing one if it is different. In this case, the main work has to be done in the JavaScript, template and stylesheet files. You will also have to make some changes in the OneStep checkout controller class: several of its methods use the request parameter use_for_shipping when processing address information:

      • updateShippingMethodsAction
      • updatePaymentMethodsAction
      • updateOrderReviewAction
      • submitOrderAction

      Besides these functions, you will have to modify the checkout model’s methods saveBillng and saveShipping to use the new logic (i.e. “use shipping for billing”)

  3. Hello,

    It is very useful tutorial for me.
    can you guide me that how to add new step with a select dropdown field in one step checkout extension?


  4. Hi Oleg,

    Great reading. I have a question.

    Normally you have shipping information and the shipping method. This basically assume the web-shop delivers (can deliver) any place.

    Magento has not really considered that web-shop may target specific delivery places only (or…?)

    We are in Africa. We therefore like to only deliver to specific countries and cities. This mean the user cannot just insert any address (city+country) as shipping info.

    Therefore we like that user choose country, then city, and then shipping method (pick-up point or door delivery).
    And then the rest of the shipping info, name, street, postal etc.
    If user chose pick-up-point then street + postal is filled into the form automatically .

    So in a sense we have shipping method before some part of the shipping info.

    Magento default require sipping info before shipping method to decide on / present shipping methods.

    Is there a way to by-pass this default steps? Any input would be appreciated and any links.


  5. Is this compatible with ?

    in back-end it shows (Enable OneStep Checkout) option

    but nothing happens in front-end!!!!!!!!!!

  6. I want some customization in this module. Just I remove payment method section and place order status as quote in magento 1.8 version .What I do? Please reply any one that is good help for me.

  7. hi

    I am using magento version admin working fine but front end showing error when i am clicking on onestep checkout then i am getting 404 not found what is the problem there
    please let me know



Leave a Reply

Your email address will not be published. Required fields are marked *

Theme: Esquire by Matthew Buchanan.