Manipulating Checkout Flow

In this document I will walk you through a way to customize your checkout flow. View the code examples.

Modifying Checkout Steps

In this first code example I will go through the process on how to have a checkout that only uses Shipping Info or only uses Billing Info. These two cases are the exact same, they just mirror each other. A use case where this becomes helpful is that you are offering a free product so there is no need for billing info. However if your use case doesn't match this. This example will teach you how to write an ajax call to LemonStands checkout validator, how to copy field values, and how to skip steps during checkout. Before looking in this document I recommend looking here for some knowledge on LemonStands checkout process.


Step 1: The Skeleton

All LemonStand checkouts should have a skeleton, it is normally located in the shop-checkout partial. Here is what my example skeleton looks like:

{% if cart.getTotal() == 0 %}
    {% if step == 'billing_info' %}
      {{ partial('shop-checkout-shipping-address-free') }}
    {% elseif step == 'review' %}
      {{ partial('shop-checkout-review') }}  
    {% endif %}
{% else %}
    {% if step == 'billing_info' %}
      {{ partial('shop-checkout-billing-address') }}
    {% elseif step == 'shipping_info' %}
      {{ partial('shop-checkout-shipping-address') }}
    {% elseif step == 'shipping_method' %}
      {{ partial('shop-checkout-shippingmethod') }}
    {% elseif step == 'review' %}
      {{ partial('shop-checkout-review') }}  
    {% elseif step == 'pay' %}
      {{ partial('shop-checkout-pay') }} 
    {% endif %}
{% endif %}

The skeleton manages the number of steps in the checkout. The first if block handles a free checkout and the second block handles the normal checkout. I will focus on the first block in this example. 

Step 2: Building the Steps

The partials for the steps make up the majority of the checkout. Here my partial for the first step is:

partial('shop-checkout-shipping-address-free')

This partial will hold a form tag that contains all the data we will submit in this step. So for a shipping address form we will need some mandatory fields:

  1. <input type="text" value="" name="shippingInfo[firstName]" id="shipping_firstName" />
    <input type="text" value="" name="shippingInfo[lastName]" id="shipping_lastName" />
    <input type="text" value="" name="shippingInfo[postalCode]" id="shipping_postalCode" />
    <input type="text" value="" name="shippingInfo[countryId]" id="shipping_country" />
    <input type="text" value="" name="shippingInfo[stateId]" id="shipping_state" />
    <input type="text" value="" name="shippingInfo[city]" id="shipping_city" />
    <input type="text" value="" name="shippingInfo[streetAddressLine1]" id="shipping_address1" />
    

However, if we submit a form with just these fields it will fail because LemonStand will also ask for the billing_address as well. So we will add hidden fields for all the billing address fields as well like so:

  1. <input type="hidden" value="" name="billingInfo[firstName]" id="billing_firstName" />
    <input type="hidden" value="" name="billingInfo[lastName]" id="billing_lastName" />
    <input type="hidden" value="" name="billingInfo[postalCode]" id="billing_postalCode" />
    <input type="hidden" value="" name="billingInfo[countryId]" id="billing_country" />
    <input type="hidden" value="" name="billingInfo[stateId]" id="billing_state" />
    <input type="hidden" value="" name="billingInfo[city]" id="billing_city" />
    <input type="hidden" value="" name="billingInfo[streetAddressLine1]" id="billing_address1" />
    

However, the type=hidden fields will still fail validation because they will be empty on submission. So we will add a function that auto fills them on form submission with the info from the shippingInfo. Put this function on the bottom of your page in a script tag like so:


<script type="text/javascript">
    function processBlankFields () {
        $('#billing_firstName').val($('#shipping_firstName').val());
        $('#billing_lastName').val($('#shipping_lastName').val());
        $('#billing_postalCode').val($('#shipping_postalCode').val());
        $('#billing_country').val($('#shipping_country').val());
        $('#billing_state').val($('#shipping_state').val());
        $('#billing_city').val($('#shipping_city').val());
        $('#billing_address1').val($('#shipping_address1').val());
        $.ajax({
            data : $('#free-checkout').serialize(),
            type: 'post',
            url: window.location.href,
            headers: {
              'X-Event-Handler': 'shop:checkout',
              'X-Partials'     : 'shop-checkout,shop-checkout-progress,shop-minicart',
              'X-Requested-With': 'XMLHttpRequest'
            },
            
            success: function(data) {
                $('#checkout-page').html(data['shop-checkout']);
                $('#breadcrumbs-area').html(data['shop-checkout-progress']);
                $('#mini-cart').html(data['shop-minicart']);
            }     
        });
    };
        
</script>

The first part will copy the values in shipping address to billing address. The ajax call will submit the form. Notice my ajax call uses:

$('#free-checkout')

This is the ID of my form tag that holds my hidden and non hidden inputs. Normally the form would be submitted using something like this:

<a class="btn" href="#" data-ajax-handler="shop:checkout" data-ajax-update="#checkout-page=shop-checkout, #breadcrumbs-area=shop-checkout-progress, #mini-cart=shop-minicart">
Order review
</a>

However, with our function and custom ajax call we can do this:

<a class="btn" href="#" onclick="processBlankFields();">
Order review
</a>

The reason we need a custom ajax call is because we need the ajax call to happen after the values are copied from shipping to billing address. That is why we have the custom function.


One last thing we will need to be done to make sure we move past 2 steps and to our next step. We need 2 hidden inputs to pass this data.

<input type="hidden" name="step" value="billing_info,shipping_info,"/>
<input type="hidden" name="nextStep" value="review" />

The first hidden input tells the LemonStand checkout validator to check for billing_info and shipping_info and the second input tells LemonStand what to set the next step too. In this case we are giving a free product, so I just want their shipping info and then to review to the order. I don't need a payment method or shipping method.


Please note the non-hidden inputs in this example are bare minimum in styling. If you are using a pre existing LemonStand theme, the existing checkout will most likely have labels and styling classes associated to all the inputs to make it look better. 

Here is a bare minimum sample of what the whole thing would look like:


{{ open_form({'id': 'free-checkout','class': 'custom', 'data-validation-message' : ''}) }}
<input type="text" value="" name="shippingInfo[firstName]" id="shipping_firstName" />
<input type="text" value="" name="shippingInfo[lastName]" id="shipping_lastName" />
<input type="text" value="" name="shippingInfo[postalCode]" id="shipping_postalCode" />
<input type="text" value="" name="shippingInfo[countryId]" id="shipping_country" />
<input type="text" value="" name="shippingInfo[stateId]" id="shipping_state" />
<input type="text" value="" name="shippingInfo[city]" id="shipping_city" />
<input type="text" value="" name="shippingInfo[streetAddressLine1]" id="shipping_address1" />
<input type="hidden" value="" name="billingInfo[firstName]" id="billing_firstName" />
<input type="hidden" value="" name="billingInfo[lastName]" id="billing_lastName" />
<input type="hidden" value="" name="billingInfo[postalCode]" id="billing_postalCode" />
<input type="hidden" value="" name="billingInfo[countryId]" id="billing_country" />
<input type="hidden" value="" name="billingInfo[stateId]" id="billing_state" />
<input type="hidden" value="" name="billingInfo[city]" id="billing_city" />
<input type="hidden" value="" name="billingInfo[streetAddressLine1]" id="billing_address1" />
<a class="btn btn-primary" href="#" onclick="processBlankFields();" >
Order review
</a>
<input type="hidden" name="step" value="billing_info,shipping_info,"/>
<input type="hidden" name="nextStep" value="review" />
{{ close_form() }}
<script type="text/javascript">
    function processBlankFields () {
        $('#billing_firstName').val($('#shipping_firstName').val());
        $('#billing_lastName').val($('#shipping_lastName').val());
        $('#billing_postalCode').val($('#shipping_postalCode').val());
        $('#billing_country').val($('#shipping_country').val());
        $('#billing_state').val($('#shipping_state').val());
        $('#billing_city').val($('#shipping_city').val());
        $('#billing_address1').val($('#shipping_address1').val());
        $.ajax({
            data : $('#free-checkout').serialize(),
            type: 'post',
            url: window.location.href,
            headers: {
              'X-Event-Handler': 'shop:checkout',
              'X-Partials'     : 'shop-checkout,shop-checkout-progress,shop-minicart',
              'X-Requested-With': 'XMLHttpRequest'
            },
            
            success: function(data) {
                $('#checkout-page').html(data['shop-checkout']);
                $('#breadcrumbs-area').html(data['shop-checkout-progress']);
                $('#mini-cart').html(data['shop-minicart']);
            }     
        });
    };
        
</script>
Variable Usage