Spree Commerce

Try It Now

New on Edge: Refactored Checkout (Again)

Posted on January 13, 2010 by Sean Schofield

For those of you following the "edge" version of Spree, you probably noticed a massive new commit that introduced a much anticipated refactoring of the checkout process. These changes are significant and will likely require some modifications to your custom site extension in order to upgrade to the latest version of Spree. The refactoring was the result of additional real world experience building Spree sites as well as extensive developer feedback.

Checkout Gets a State Machine

One of the first major changes was the inclusion of a new state machine for the Checkout model.


<p>state_machine :initial =&gt; &#8216;address&#8217; do<br />
  after_transition :to =&gt; &#8216;complete&#8217;, :do =&gt; :complete_order<br />
  before_transition :to =&gt; &#8216;complete&#8217;, :do =&gt; :process_payment<br />
  event :next do<br />
    transition :to =&gt; &#8216;delivery&#8217;, :from  =&gt; &#8216;address&#8217;<br />
    transition :to =&gt; &#8216;payment&#8217;, :from =&gt; &#8216;delivery&#8217;<br />
    transition :to =&gt; &#8216;complete&#8217;, :from =&gt; &#8216;payment&#8217;<br />
  end<br />
end</p>

The state machine offers a standard approach for customizing the checkout steps and their sequence.

picture-14
Each step is rendered with a partial of the same name and the steps themselves are automatically transformed into a "progress train" that can be used for navigation. The look and feel of the progress train can be customized (or omitted) by your site theme and the logic for constructing it can be tweaked in your site extension.

Simplified Custom Controller Logic

The CheckoutsController now provides its own “hook mechanism” (not to be confused with theme hooks) which allow for the developer to perform additional logic (or to change the default) logic that is applied during the edit and/or update operation for a particular step. The Spree::Checkout::Hooks module provides this additional functionality and makes use of methods provided by the resource_controller gem.

The CheckoutsController will automatically call an edit_hook (if one is defined) before moving on to the standard edit functionality for a particular step. There is also a corresponding update_hook that can be used to execute additional functionality during an update. Both hooks can accept either a symbol (which is the name of a method) or a block.

The CheckoutsController itself makes limited use of these hooks – its expected that developers will add their own through a site extension. Here’s an example of an edit_hook defined in checkouts_controller.rb:


<p>delivery.edit_hook :load_available_methods</p>

This tells the controller to call load_available_methods before presenting the standard edit form for the delivery step. The hook will be called only during the delivery step – it is ignored for all other steps. In this specific case, Spree uses the hook to load all available shipping methods.

Perhaps more interestingly, hooks can also be appended to. In other words, if you wanted to perform some additional logic before presenting the delivery form, you could add something like the following in your site extension.


<p>delivery.edit_hook &lt;&lt; :perform_additional_logic</p>

Please see the documentation for more details on hooks and the rest of the checkout architecture.

Back to Multi Step

The other major change is that Spree is moving back to a multi step checkout as the default. Previous versions of Spree have experimented with a single page checkout similar to the one offered by Magento. After building several sites with the single step we decided to abandon it for several reasons.

  1. Single page approach requires javascript in order to checkout. Our new checkout makes minimal use of javascript (ex. populating the list of states based on countries) but it degrades nicely and is not required to move between checkout states.
  2. Old approach was very difficult to customize. The old checkout.js file was 500+ lines of code and growing. We’re not interested in debugging javascript everytime we make a change to the checkout process.
  3. Some of the previous issues with multi step no longer apply. Thanks to the Rails inclusion of nested attributes and some interesting third party work done on partial model validation, we’re no longer facing many of the issues that initially motivated us to try the single page checkout.

Moving Forward

We’re hoping to eventually support an simplified single page checkout based on the new state machine approach. The idea would be to use AJAX to render the individual steps and to simulate the effects of the multi page process. We’re interested in offering our users the flexibility to use either the multi step or single step approach. Contributions are always welcome.

Even though this is a non trivial change to how checkout is handled we definitely feel its worth the potential disruption to allow future development (and upgrades) to be less painful. Enjoy!