This is the documentation for LemonStand V1, which has been discontinued. You can learn more and upgrade your store here.

LemonStand Version 1 Has Been Discontinued

This documentation is for LemonStand Version 1. LemonStand is now offered as a cloud-based eCommerce platform.
You can try the new LemonStand and learn about upgrading here.

Implementing the Step-By-Step Checkout

This section describes a process of implementing the conventional step-by-step checkout process, where a single page corresponds to a single checkout step. Data in each step can be sent either by a regular POST method, or using the AJAX request. This implementation is based on the step-by-step checkout actions, described in the API section of the documentation. 

All pages in this tutorial use the AJAX method for sending data to the server. Please note that each page, except the Order Review page, have a hidden field with the name redirect, which contains the URL of a page, corresponding to a next page. You need to use the actual URLs of the pages in your online store. Note: if your copy of LemonStand is installed in a subdirectory, you need to use the root_url() function in the redirect field value to generate correct URLs referring to LemonStand pages.

Step 1: the billing address page

The billing address page contains a form for entering a customer's billing address and contact information. This page should be based on the shop:checkout_billing_info action. The following snippet contains code which you can use on the billing address page.

<h3>Billing Information</h3>

<?= open_form() ?>
  <?= flash_message() ?>

  <? if ($this->customer): ?>
    <p>Bill to: <strong><?= h($this->customer->name) ?>, <?= $this->customer->email ?></strong>.</p>
  <? endif ?>
  
  <? if (!$this->customer): ?>
    <label for="first_name">First Name</label>
    <input name="first_name" value="<?= h($billing_info->first_name) ?>" id="first_name" type="text"/><br/>
    
    <label for="last_name">Last Name</label>
    <input name="last_name" value="<?= h($billing_info->last_name) ?>" id="last_name" type="text"/><br/>
    
    <label for="email">Email</label>
    <input id="email" name="email" value="<?= h($billing_info->email) ?>" type="text"/><br/>
  <? endif ?>
  
  <label for="company">Company</label>
  <input id="company" type="text" value="<?= h(post('company', $billing_info->company)) ?>" name="company"/><br/>

  <label for="phone">Phone</label>
  <input id="phone" type="text" value="<?= h($billing_info->phone) ?>" name="phone"/><br/>

  <label for="street_address">Street Address</label>
  <input id="street_address" name="street_address" type="text" value="<?= h($billing_info->street_address) ?>"/><br/>

  <label for="city">City</label>
  <input id="city" type="text" name="city" value="<?= h($billing_info->city) ?>"/><br/>

  <label for="zip">Zip/Postal Code</label>
  <input id="zip" type="text" name="zip" value="<?= h($billing_info->zip) ?>"/><br/>

  <label for="country">Country</label>
  <select id="country" name="country" onchange="return this.getForm().sendRequest(
      'shop:on_updateStateList', {
        extraFields: {
          'country': $('#country').val(), 
          'control_name': 'state', 
          'control_id': 'state', 
          'current_state': '<?= $billing_info->state ?>'},
        update: {'billing_states': 'shop:state_selector'}
    })">
    <? foreach ($countries as $country): ?>
    <option <?= option_state($billing_info->country, $country->id) ?> value="<?= h($country->id) ?>"><?= h($country->name) ?></option>
    <? endforeach ?>
  </select><br/>

  <label for="state">State</label>
  <div id="billing_states">
    <?= $this->render_partial('shop:state_selector', array(
      'states'=>$states, 
      'control_id'=>'state', 
      'control_name'=>'state', 
      'current_state'=>$billing_info->state)) ?>
  </div>
  
  <input type="hidden" value="/checkout_shipping_information" name="redirect"/>
  
  <input 
    type="image"
    src="/resources/images/btn_next.gif" 
    alt="Next" 
    onclick="return $(this).getForm().sendRequest('shop:on_checkoutSetBillingInfo')"/>
</form>

The code of the billing and shipping information pages are similar to the checkout partials described in the AJAX-driven single-page checkout section. Please refer to the Creating the Billing Information partial for explanation how the state list update works.

Step 2: the shipping address page

The shipping address page contains a form for entering a customer's shipping address and contact information. This page should be based on the shop:checkout_shipping_info action. The shipping address page can contain a link or button, which copies the billing address into a shipping address form. When a customer clicks this button (or link), the shipping information form is updated using the AJAX request. It means that the whole shipping information form should be placed into a separate partial. 

The following snippet contains code which you can use for creating the shipping address page. 

<h3>Shipping Information</h3>

<?= open_form() ?>
  <?= flash_message() ?>
  <p>Copy <a href="#" onclick="return $(this).getForm().sendRequest('shop:on_copyBillingInfo', {update:{'shipping_info_form': 'shop:shipping_info_checkout_step'}})">billing information</a>.<p>

  <div id="shipping_info_form">
    <? $this->render_partial('shop:shipping_info_checkout_step') ?>
  </div>

  <input type="hidden" name="redirect" value="/checkout_shipping_method"/>
  <input 
    type="image"
    src="/resources/images/btn_next.gif"
    alt="Next" 
    onclick="return $(this).getForm().sendRequest('shop:on_checkoutSetShippingInfo')"/>
</form>  

The partial shop:shipping_info_checkout_step, referred in the code snippet, contains a shipping information form. This partial can contain the following code:

<label for="first_name">First Name</label>
<input id="first_name" name="first_name" type="text" value="<?= h($shipping_info->first_name) ?>"/><br/>

<label for="last_name">Last Name</label>
<input id="last_name" name="last_name" type="text" value="<?= h($shipping_info->last_name) ?>"/><br/>

<label for="company">Company</label>
<input id="company" type="text" value="<?= h($shipping_info->company) ?>" name="company"/><br/>

<label for="phone">Phone</label>
<input id="phone" type="text" value="<?= h($shipping_info->phone) ?>" name="phone"/><br/>

<label for="street_address">Street Address</label>
<input id="street_address" name="street_address" type="text" value="<?= h($shipping_info->street_address) ?>"/><br/>

<label for="city">City</label>
<input id="city" type="text" name="city" value="<?= h($shipping_info->city) ?>"/><br/>

<label for="zip">Zip/Postal Code</label>
<input id="zip" type="text" name="zip" value="<?= h($shipping_info->zip) ?>"/><br/>

<label for="country">Country</label>
<select id="country" name="country" onchange="return $('#country').getForm().sendRequest(
  'shop:on_updateStateList', {
    extraFields: {
      'country': $('#country').val(), 
      'control_name': 'state', 
      'control_id': 'state', 
      'current_state': '<?= $shipping_info->state ?>'},
    update: {'shipping_states': 'shop:state_selector'}
  })">
  <? foreach ($countries as $country): ?>
  <option <?= option_state($shipping_info->country, $country->id) ?> value="<?= h($country->id) ?>"><?= h($country->name) ?></option>
  <? endforeach ?>
</select><br/>

<label for="state">State</label>
<div id="shipping_states">
<?= $this->render_partial('shop:state_selector', array(
  'states'=>$states, 
  'control_id'=>'state', 
  'control_name'=>'state', 
  'current_state'=>$shipping_info->state)) ?>
</div>

Step 3: the shipping method page

The shipping method page contains a form which allows a customer to select a shipping method from a list of available shipping methods. This page should be based on the shop:checkout_shipping_method action. Example of the shipping method page code:

<h3>Shipping Method</h3>

<?= open_form() ?>
  <?= flash_message() ?>
  <? if (count($shipping_options)): ?>
    <p>Please select shipping option.</p>
  
    <? foreach ($shipping_options as $option): ?>
      <? if ($option->multi_option): ?>
        <h3>
          <?= h($option->name) ?>
          <? if ($option->description): ?>
            <br/><?= h($option->description) ?>
          <? endif ?>
        </h3>
      
        <? foreach ($option->sub_options as $sub_option): ?>
        <input <?= radio_state($option->id == $shipping_method->id && $sub_option->id == $shipping_method->sub_option_id) ?> id="<?= 'option'.$sub_option->id ?>" type="radio" name="shipping_option" value="<?= $sub_option->id ?>"/>
          <label for="<?= 'option'.$sub_option->id ?>">
            <?= h($sub_option->name) ?> -
            <? if (!$option->is_free): ?>
              <strong><?= format_currency($sub_option->quote) ?></strong>
            <? else: ?>
              Free!
            <? endif ?>
          </label><br/>
        <? endforeach ?>
      <? else: ?>
        <input <?= radio_state($option->id == $shipping_method->id) ?> id="<?= 'option'.$option->id ?>" type="radio" name="shipping_option" value="<?= $option->id ?>"/>
        <label for="<?= 'option'.$option->id ?>">
          <strong><?= h($option->name) ?></strong> - 
          <? if (!$option->is_free): ?>
            <strong><?= format_currency($option->quote) ?></strong>
          <? else: ?>
            Free!
          <? endif ?> 
        
          <? if ($option->description): ?>
            <br/><?= h($option->description) ?>
          <? endif ?>
        </label><br/>
      <? endif ?>
    <? endforeach ?>
    <input type="hidden" value="/checkout_payment_method" name="redirect"/>
    <input 
      type="image" 
      src="/resources/images/btn_next.gif"
      alt="Next"
      onclick="return $(this).getForm().sendRequest('shop:on_checkoutSetShippingMethod')"/>
  <? else: ?>
    <p>There are no shipping options available for your location. Please contact our sales department: <a href="mailto:sales@mystoredotcom">sales@mystoredotcom</a>.</p>
  <? endif ?>
</form>

Step 4: the payment method page

The payment method page contains a form which allows a customer to select a payment method from a list of available methods. This page should be based on the shop:checkout_payment_method action. Example of the payment method page code:

<h3>Payment Method</h3>

<?= open_form() ?>
  <?= flash_message() ?>
  <? if (count($payment_methods)): ?>
    <p>Please select payment method.</p>
  
    <? foreach ($payment_methods as $method): ?>
      <input <?= radio_state($method->id == $payment_method->id) ?> id="<?= 'method'.$method->id ?>" type="radio" name="payment_method" value="<?= $method->id ?>"/>
      <label for="<?= 'method'.$method->id ?>">
        <?= h($method->name) ?>
        <? if ($method->description): ?>
        <br><?= h($method->description) ?>
        <? endif ?>
      </label><br/>
    <? endforeach ?>
    
    <input type="hidden" name="redirect" value="/checkout_order_preview"/>
    <input
      type="image"
      src="/resources/images/btn_next.gif"
      alt="Next"
      onclick="return $(this).getForm().sendRequest('shop:on_checkoutSetPaymentMethod')"/>
  <? else: ?>
    <p>There are no payment methods available for your location. Please contact our sales department: <a href="mailto:sales@mystoredotcom">sales@mystoredotcom</a>.</p>
  <? endif ?>
</form>

Step 5: the order review page

The order review page displays the information gathered during the checkout process along with complete order information. Also this page usually contains the Place Order button. This page should be based on the shop:checkout_order_review action. Example of the order review page code:

<h3>Order Review</h3>

<?= open_form() ?>
  <?
    $bill_to_str = $billing_info->as_string();
    $ship_to_str = $shipping_info->as_string();
    $items = Shop_Cart::list_active_items();
  ?>
  
  <table>
    <thead>
      <tr>
        <th>Cart Items</th>
        <th>Quantity</th>
        <th>Price</th>
        <th>Discount</th>
        <th>Total</th>
      </tr>
    </thead>
    <tbody>
      <? 
        foreach ($items as $item): 
        $options_str = $item->options_str();
      ?>
      <tr>
        <td>
          <strong><?= h($item->product->name) ?></strong>
          <? if (strlen($options_str)): ?>
            <br/><?= h($options_str) ?>.
          <? endif ?>
          <? if ($item->extra_options): ?>
            <? foreach ($item->extra_options as $option): ?>
              <br/>
              + <?= h($option->description) ?>:
              <?= format_currency($option->price) ?>
            <? endforeach ?>
          <? endif ?>
        </td>
        <td><?= $item->quantity ?></td>
        <td><?= format_currency($item->single_price()) ?></td>
        <td><?= format_currency($item->discount()) ?></td>
        <th><?= format_currency($item->total_price()) ?></th>
      </tr>
      <? endforeach ?>
    </tbody>
  </table>
  
  <table>
    <tr>
      <td>Subtotal: </td>
      <th><?= format_currency($subtotal) ?></th>
    </tr>
    <tr>
      <td>Discount: </td>
      <th><?= format_currency($discount) ?></th>
    </tr>
    <? foreach ($product_taxes as $tax): ?>
      <tr>
        <td><?= ($tax->name) ?>: </td>
        <th><?= format_currency($tax->total) ?></th>
      </tr>
    <? endforeach ?>
    <tr>
      <td>Shipping: </td>
      <th><?= format_currency($shipping_quote) ?></th>
    </tr>
    <? foreach ($shipping_taxes as $tax): ?>
      <tr>
        <td>Shipping tax (<?= ($tax->name) ?>): </td>
        <th><?= format_currency($tax->rate) ?></th>
      </tr>
    <? endforeach ?>
  </table>
  
  <h3>Total</h3>
  <p><?= format_currency($total) ?></p>
  
  <input
    type="image"
    src="/resources/images/btn_place_order.gif"
    alt="Place Order and Pay"
    onclick="return $(this).getForm().sendRequest('shop:on_checkoutPlaceOrder')"/>
</form>

Posting forms without AJAX

If you don't want use AJAX for posting the checkout forms, please use a simple SUBMIT element with the "submit" name:

<input type="submit" name="submit" value="Next"/>

See also:

Next: Implementing a One-Step Checkout
Previous: Adding a Field for Customer Order Notes in Checkout
Return to Checkout Process