Table of Contents

The Most Efficient One-Page Checkout Available

What Is the Checkout?

When we refer to the “checkout” we refer to the page (or pages, in other systems) where the customer enters their billing and payment information, and where they submit their order for processing. In general, the cart comes before the checkout, and the receipt is displayed when a successful checkout is processed.

How and Why FoxyCart's Checkout Is Different

One of the major pieces we wanted to simplify and improve upon when we started FoxyCart was the checkout page. In addition to making it a very dynamic one-page checkout, we also revolutionized (big hypey word, we know, but we feel it's true) the way customers choose between a guest checkout or creating an account.

No longer will your customers be presented with inputs for their email and password, only to submit the form and be told that they don't have a user, and that if they'd like to register they can click the teeny tiny link in the bottom right. Odds are they don't care about registering; they just want to buy your wares as quickly as possible. Our unique approach automatically determines whether the customer has an account, and also presents easy options within the templates configuration settings to checkout as a guest user if desired (and configured for your store).

The checkout is also highly flexible, and can be modified in a variety of ways. HTML, CSS, and JavaScript are all available for you to perform advanced customizations both of the design and the functionality.

Checkout Template Customization

The checkout can be quickly, easily, and automatically styled to match your existing site's templates. This is care of “AutoMagiCache”. There are screencasts, examples, and lots of documentation about customizing your checkout template. And really, it's quite easy and automatic, so we strongly recommend reviewing the screencasts. A customized checkout can significantly boost conversion rates, so it's likely well worth your time to style your FoxyCart-powered checkout to match your site.

Errors On the Checkout

"Hard" Errors

Any “hard” errors will be displayed for your customers (and in your store's error log), and will generally be supplied by your payment gateway for things like a declined card, a mismatched billing address, or other issues that impact payment. See Errors and What To Do About Them for more info.

"Soft" Errors

FoxyCart's checkout also employs JavaScript validation to provide for quick feedback if a required field is left empty, if a shipping option is unselected, an email address malformed, etc. These errors will not show up in your store's error log.

Other Errors and Unexpected Behavior

Because the checkout is complex, there are a variety of things that can go wrong if you're customizing things. If things aren't happening as expected…

Adding Custom Checkout Fields

Overview

Sometimes it is necessary or desirable to include additional input fields on checkout, such as:

As of v2.0, many of the common options can be easily added in the “configuration” section of the templates in the FoxyCart admin by selecting the appropriate option. For example, a TOS checkbox can be added using the “Display a Terms of Service agreement to customers” option and a mailing list subscription checkbox using the “Allow users to sign up for your newsletter” option. There may be further specifications you need to enter for any of the options.

For other custom fields you can simply use the “Add custom form fields to your checkout” template config option, and insert the desired HTML in the “custom checkout fields” textarea. For more advanced custom fields, you can also edit the checkout template directly.

An Example

Let's try an example, and add two fields: a “Referred by…” text input, and an “Order Notes” textarea. Find the “custom checkout fields” textarea mentioned above, and paste this in:

<div class="fc-form-group ">
	<div class="col-sm-8 col-sm-offset-3" data-fc-error-for="referral_source" data-fc-error-class="fc-alert-container--error">
		<div class="fc-input-group-container fc-input-group-container--checkbox fc-input-group-container--active">
			<label class="fc-input-group-container__title fc-input-group-container__title--forced fc-form-label">
				Referred by… (required)
			</label>
			<div class="fc-form-group">
				<p>Please let us know who referred you.</p>
				<input type="text" id="referral_source" name="referral_source" placeholder="" autocomplete="off" class="fc-form-control" formnovalidate="" aria-required="true" value="{{ referral_source }}" data-fc-required>
			</div>
		</div>
	</div>
</div>
 
<div class="fc-form-group">
    <div class="col-sm-8 col-sm-offset-3">
        <div class="fc-input-group-container fc-input-group-container--textarea fc-input-group-container--active">
            <label class="fc-input-group-container__title fc-input-group-container__title--forced fc-form-label">
                Order Notes (optional)
            </label>
            <div class="fc-form-group">
                <textarea name="Order_Notes" id="Order_Notes" aria-required="false" autocomplete="off" class="fc-form-control" placeholder="Questions? Feedback? Let us know!" style="background:#fff; height:5em;">{{ Order_Notes }}</textarea>
            </div>
        </div>
    </div>
</div>

This code will then be inserted in your checkout page, right under the “almost done” heading on the default checkout template (above the password entry, terms of service, and the main checkout button). Go ahead and load up your checkout to see what you just added. It should look something like this: Thanks to Corganics.com for beta testing 2.0. That's why they're on our screenshots :)

Note that the HTML we've entered here can be anything at all, but this example is using the same structure as the default template. That way it automatically gets the appropriate styling.

Also note the use of data-fc-error-for on the container div and data-fc-required on the input which will automatically make that field required for checkout and color the container with an error class if the customer attempts to checkout without adding a value there.

Looking for custom fields for multiship? Head over here.

A Dropdown Example

This example uses the sample scenario above, but with a dropdown instead of the text input. Note that you'll see some twig code like this at the top of the example code:

{% set options = ["Social Media", "Advertising", "Online Search", "Word Of Mouth", "Other"] %}

This array sets out what options are present in the dropdown - so can be edited to add or remove options as required.

{% set options = ["Social Media", "Advertising", "Online Search", "Word Of Mouth", "Other"] %}
<div class="fc-form-group ">
    <div class="col-sm-8 col-sm-offset-3" data-fc-error-for="referral_source" data-fc-error-class="fc-alert-container--error">
        <div class="fc-input-group-container fc-input-group-container--checkbox fc-input-group-container--active">
            <label class="fc-input-group-container__title fc-input-group-container__title--forced fc-form-label">
                Referred by… (required)
            </label>
            <div class="fc-form-group">
                <p>How did you hear about us?</p>
                    <select name="referral_source" id="referral_source" class="fc-form-control" aria-required="true" data-fc-required>
                        <option value="">Please select</option>
                        {% for option in options %}
                            <option value="{{ option }}" {% if referral_source == option %}selected{% endif %}>{{ option }}</option>
                        {% endfor %}
                    </select>
            </div>
        </div>
    </div>
</div>

A Radio Inputs Example

This example uses a similar approach as above, using a line of twig code at the top to set the values of the radio buttons as an array:

{% set options = {"Monday": "Monday - First Delivery", "Wednesday": "Wednesday - Second Delivery", "Friday": "Friday - Third Delivery"} %}

This array sets out what options are present in the radio inputs - setting both the value for the input (“Monday”) and the label shown to the customer (“Monday - First Delivery”) - so can be edited to add or remove options as required.

{% set options = {"Monday": "Monday - First Delivery", "Wednesday": "Wednesday - Second Delivery", "Friday": "Friday - Third Delivery"} %}
<div class="fc-form-group ">
    <div class="col-sm-8 col-sm-offset-3" data-fc-error-for="{% for option in options %}Delivery_{{ loop.index }} {% endfor %}" data-fc-error-class="fc-alert-container--error">
        <div class="fc-input-group-container fc-input-group-container--checkbox fc-input-group-container--active">
            <label class="fc-input-group-container__title fc-input-group-container__title--forced fc-form-label">
                Delivery Day (required)
            </label>
            <div class="fc-form-group">
                <p>What day would you like to receive your delivery?</p>
                {% for option, label in options %}
                <div class="fc-input-group-container--radio">
                    <label class="fc-form-label" for="Delivery_{{ loop.index }}"><input class="fc-form-control" type="radio" name="Delivery" value="{{ option }}" id="Delivery_{{ loop.index }}" {% if Delivery == option %}checked{% endif %} aria-required="true" data-fc-required />
                    {{ label }}</label>
                </div>
                {% endfor %}
            </div>
        </div>
    </div>
</div>

A Checkbox Example

This is a quick example of a standalone optional checkbox field for the checkout.

Special note is that this also includes a hidden input with the same name as the checkbox included just above it in the HTML. This ensures that a value is always passed for the attribute with the transaction. A HTML form only accepts a single instance of a given input name, and a checkbox is only submitted if it's checked. With that in mind - if the checkbox isn't checked, then the hidden input is passed with the transaction. If the checkbox is checked though, then as it's the second input with the same name, it will be the value passed with the transaction.

<div class="fc-form-group">
    <div class="col-sm-8 col-sm-offset-3 fc-checkout__additional-field--custom-checkbox">
        <div class="fc-input-group-container fc-input-group-container--checkbox">
            <label class="fc-input-group-container__title fc-form-label fc-form-label--custom-checkbox">
                <input type="hidden" name="my_custom_checkbox" value="0" />
                <input type="checkbox"
                   id="my_custom_checkbox"
                   name="my_custom_checkbox"
                   value="1"
                   class="fc-form-control fc-form-control--custom-checkbox"
                   {{ checked(my_custom_checkbox == '1') }} />
                My Checkbox Label
            </label>
        </div>
    </div>
</div>

A Multilingual Example

If your store is making use of Template Sets to offer multilingual functionality on your store, you will also want to have your custom checkout fields show in the respective languages too. Here's a quick example of achieving that with the referral field from the first example above.

You would obviously need to expand this to match the different languages you offer on your store, as selected in the template sets language dropdown. This example assumes that the default template set uses English, but there are also template sets using German and French.

{% set referral_label = "Referred by... (required)" %}
{% set referral_description = "Please let us know who referred you." %}
 
{% if language == "german" %}
	{% set referral_label = "Empfohlen von... (erforderlich)" %}
	{% set referral_description = "Bitte teilen Sie uns mit, wer Sie empfohlen hat." %}
{% elseif language == "french" %}
	{% set referral_label = "Référencé par... (obligatoire)" %}
	{% set referral_description = "Veuillez nous indiquer qui vous a référé." %}
{% endif %}
 
<div class="fc-form-group ">
	<div class="col-sm-8 col-sm-offset-3" data-fc-error-for="referral_source" data-fc-error-class="fc-alert-container--error">
		<div class="fc-input-group-container fc-input-group-container--checkbox fc-input-group-container--active">
			<label class="fc-input-group-container__title fc-input-group-container__title--forced fc-form-label">
				{{ referral_label }}
			</label>
			<div class="fc-form-group">
				<p>{{ referral_description }}</p>
				<input type="text" id="referral_source" name="referral_source" placeholder="" autocomplete="off" class="fc-form-control" formnovalidate="" aria-required="true" value="{{ referral_source }}" data-fc-required>
			</div>
		</div>
	</div>
</div>

What to name your fields

You can name your fields anything you want, but we recommend using the underscore (_) for spaces, and capitalizing your input names as you'd like them displayed. For all receipts, we will convert underscores to spaces for you.

For example, if you have <input name=“Update_List” />, it will be displayed in your receipt (and email receipts) as Update List.

Maintaining custom field values

With 2.0, our Twig templating system re-renders sections of the page at different points of the checkout process as customers trigger changes on the page. If a custom field is re-rendered, that would mean that it would be removed and re-added to the page, and wouldn't contain any data the customer had entered into it.

To maintain the value, you need to add some Twig logic to put it's value back when it's rendered. You can do that like this:

<!-- Text input -->
<input type="text" name="Referral_Source" value="{{ Referral_Source }}" />
 
<!-- Textarea -->
<textarea name="Comments">{{ Comments }}</textarea>
 
<!-- Checkbox -->
<input type="checkbox" name="Age_Approval" value="1" {% if Age_Approval == "1" %}checked{% endif %} />
 
<!-- Radio -->
<input type="radio" name="Delivery" value="Monday"  {% if Delivery == "Monday" %}checked{% endif %} />
<input type="radio" name="Delivery" value="Tuesday"  {% if Delivery == "Tuesday" %}checked{% endif %} />
 
<!-- Select Dropdown -->
{% set options = ["Social Media", "Advertising", "Online Search", "Word Of Mouth", "Other"] %}
<select name="referral_source" id="referral_source" class="fc-form-control" aria-required="true" data-fc-required>
    <option value="">Please select</option>
    {% for option in options %}
       <option value="{{ option }}" {% if referral_source == option %}selected{% endif %}>{{ option }}</option>
    {% endfor %}
</select>

If your field name includes accented characters, or if you're setting it up as a hidden field (one prepended with h:), then you'll need to access it in a slightly different way using Twig's global _context object. This is because the colon or accented characters in the field name changes how it needs to be referenced, as a string rather than an object. If you had a field with a name of h:hidden_field, you would access it as _context['h:hidden_field'] in Twig.

What about Receipts and the Datafeeds?

The custom fields will be inserted at the bottom of your receipt (after checkout), as well as in any emails that include receipt placeholder.

Custom checkout fields are included in all API responses and the XML datafeed.

A Note about Checkboxes

Checkboxes do not post if they are unchecked. This is standard HTML form behavior, but it's worth noting, especially if you're using checkboxes for your XML datafeeds.

Required Fields

If your custom field needs to be required on the checkout, simply appending data-fc-required as an attribute to the element will facilitate that for you on the checkout. As an example, in an input that might look like this:

<input type="text" id="referred_by" name="referred_by" class="fc-form-control" aria-required="true" value="{{ referred_by }}" data-fc-required>

Here's some code to turn on phone requirement if there are shippable items in the cart. Place it in the footer section of the template configuration custom code option. If you need to set the phone field as always required - you can do that from the required fields template configuration option within your stores administration.

<script type="text/javascript" charset="utf-8">
function myCustomDisplayUpdate() {
	var require_phone = 0;
	for (p in FC.json.items) {
		if (FC.json.items[p].delivery_type == "shipped") {
			require_phone = 1;
		}
	}
	if (require_phone == 1) {
		FC.json.required_fields.push('shipping_phone');
		FC.json.config.template_config.custom_checkout_field_requirements['billing_phone'] = "required";
	}
}
if (FC.json.context == "checkout") {
	FC.client.on("ready", myCustomDisplayUpdate);
}
</script>

"Sensitive" Custom Checkout Fields

By default, all custom checkout fields will be emailed as part of the email receipt sent to both the customer and the store's email address. Emailing sensitive information is a Bad, Bad Idea. If you aren't familiar with the reasons why, please read our Security Primer for more info. If you do need to collect information that you do not want to be emailed, such as account ID numbers or other sensitive data, you can prefix your field names with h:, like this:

Please enter your Secret Account ID: <input type="text" name="h:secret_account_id" value="{{ _context['h:secret_account_id'] }}" />

“Sensitive” fields are displayed in the admin's transaction view, and are sent in the XML, but are not emailed.

You should not be collecting passwords using custom fields, as they will be stored in cleartext and not hashed in our system. Though we work hard to keep our systems secure, it's just not a good idea to store passwords without a strong hashing algorithm.

Prepopulating custom fields

While the default fields can be prepopulated, the checkout doesn't currently prepopulate custom fields that you create. Using Twig though, you can handle that prepopulation yourself. Taking the following input as an example:

<input type="text" id="Referred_By" name="Referred_By" class="fc-form-control" aria-required="true" value="{{ Referred_By }}" data-fc-required>

Adding the following Twig code before the input in the page would prepopulate the input with a value that was passed through the cart as a hidden session attribute by appending it with h:, like h:Referred_By:

{% if Referred_By is not defined %}
	{% set Referred_By = "" %}
	{% for key, custom_field in custom_fields %}
		{% if key == "Referred_By" %}
			{% set Referred_By = custom_field.value %}
		{% endif %}
	{% endfor %}
{% endif %}

Passing Custom Fields to the Custom Shipping Code

Include a data attribute of data-fc-shipping-custom-field to capture the information from a customer on the checkout related to the shipping. Review this link for complete information on custom fields (fx:custom_fields) in custom shipping code or endpoint using data-fc-shipping-custom-field.

Pre-Populating the Checkout with Customer Information

Though we generally recommend Single Sign-On and synching customers via the API, there are certain situations where you may want to pre-populate the customer's billing or shipping fields without using SSO or the API.

Security/Privacy Note: If you pre-populate personally identifiable information (like name and addresses) (aka PII), your site should force SSL throughout. Otherwise you'd run the risk of the FoxyCart session ID cookie being intercepted (if a customer is on an open wifi network), and an malicious person being able to view their pre-populated session information. Also note that you may want to warn your users not to use a shared computer, as otherwise the pre-populated info would remain on the session until the session expires, and a subsequent user of the same physical computer who happens across your site could see the pre-populated info.

Pre-populating is done by passing in specific customer fields to the cart. Once the customer fields have been passed into the cart, they will remain there just like session variables, and when the checkout is loaded the values will be inserted into the appropriate fields on the checkout. Note that this does not impact the user's ability to change the data, nor does it impact the customer's ability to checkout as a guest or to create an account.

FoxyComplete: The checkout's country/state autocompleter

Multiple alternative spellings for each location

One main benefit of the new autocomplete is the ability for countries and states to have numerous different alternate spellings to allow customers to find locations in the way they're familiar. So for example, this means that a customer looking for “United Kingdom” could start typing “UK”, “GB”, “Great Britain”, “England” or “Britain” - with all of those resulting in the “United Kingdom” being a result in the autocomplete to select from. If the customer has typed in a name that is an alternate spelling for a location (eg: One that isn't the normal English spelling of the location), the alternate spelling they are matching against will also be displayed with the result (see the screenshot to the right for an example of that). This also means the autocomplete can match against different languages as well, for example 'Spain' can be matched against 'España', 'Russia' to 'Россия' and Japan to '日本'.

Relevancy sorting

Locations are also given weighting to help return the most relevant result based on a few different criteria, including where the search query appears in the matching location name (at the start of the name, start of a word in the string or elsewhere), and if they are matching against the main location spelling, or an alternate spelling. Locations are also given a relevancy boosting value, which is currently based off of global sales per country across all FoxyCart stores. For example, if you type in “United”, the United States and the United Kingdom would appear above other options that may be just as relevant and should appear first from an alphabetical ordering as they are a more common selection. All of this combined will help customers find the country and state they're looking for quicker and easier than ever.

Localised state labels and requirement status

The new autocompleter also takes into account if FoxyCart is aware of any states for the selected country or not. If a country has states present in the system, such as the United States, Australia and Germany, then the states inputs will also have the autocomplete functionality enabled and a selection would be required to checkout. If however a country is selected that doesn't have known states in the system (generally meaning that states may not be required for shipping), such as France, New Zealand, the Netherlands and Ireland, the state field is just a straight text input that can be left blank without error. The states field label is also customised to match the local way that states are referenced, with labels like 'State', 'Canton', 'County' and 'Province'. These labels are customisable from the checkout section of the 'language' page in the administration for your store.

Is there an alternate spelling that you think should be there, or an incorrect state label for a location that you've found? Get in contact with us and let us know and we'll get it added!

Configuration Options

FoxyComplete can be customised in a number of ways by utilising the configuration options present in the administration, allowing you to turn off the autocomplete entirely, and removing or customising certain aspects of its functionality. Look for the “Enable FoxyComplete location autocompleter” option on the “configuration” section of the FoxyCart administration to alter your store's FoxyComplete instance.

Customising the styling

There are some default styles applied to the autocomplete aspects to make it display nicely. If you'd like to customise this past that point, simply overwrite the styles as you would for any other aspect of the checkout.

One specific thing to note is the way that the comboBox and flag icon are placed in the template. These items are placed absolutely, with its position based on the styling and positioning of the related location text input. For example, the width and height of the comboBox is set to the height of the text input (including its padding), and placed on its right hand side. When the comboBox is present, the text inputs width is reduced by the width of the comboBox, and added back in as padding - which means that the text input still remains the same width, but the actual space that text can be entered doesn't continue underneath the comboBox button. The flag icon is placed to the right of the text input, and to the left of the comboBox if it's present.

For the most part, this should work for just about any custom template you have for the checkout. If your design requires some customisation to the dimensions of the comboBox, simply overwrite its styling - noting that for its width and height styles you will need to set the rule to important by doing width: 30px !important;.

Adding custom validation to your Checkout

Often when adding custom fields or doing some advanced customisations, you need to prevent the checkout unless a specific situation has occurred. For example, you may need to validate an age of a customer, or prevent the checkout if they haven't filled out a specific text box after selecting a checkbox.

To prevent the checkout from validating (and therefore preventing the checkout from submitting), you simply add a function to the following event:

<script type="text/javascript">
  FC.client.on("checkout-submit", function() {
 
  });
</script>

Within that function, you can either return true or false, which will either allow the checkout or block it respectively.

So for example if you had an age input and needed to ensure that only someone aged 18 or over can checkout, that code might look like this:

<script type="text/javascript">
FC.client.on("checkout-submit", function() {
  var custAge = jQuery("#CustomerAge").val();
  if (!isNaN(parseFloat(custAge)) && isFinite(custAge) && parseFloat(custAge) >= 18) {
    return true;
  } else {
    alert("You must be over the age of 18 to purchase these products");
    return false;
  }
});
</script>

This type of setup can be expanded to show and hide error messages similar to how FoxyCart does with the default fields also.

Customising which Credit Card types are allowed on your Checkout

In some situations it is necessary to not allow certain credit cards to be used on a store, a common example being the need to prevent American Express cards due to a payment gateway restriction. You can customize that in the template configuration page in the admin.

Note that this doesn't impact your payment gateway at all. If your gateway isn't configured to accept AmEx, this setting won't do anything there. It just impacts the display of the AmEx logo, and also will error if an AmEx card type is entered (but note that this is just a javascript error, not a gateway error).

Other Checkout Actions

The checkout is primarily used to pay for new purchases, but there are certain other situations that arise that are still handled through the checkout. It's important to understand when your checkout can be used for other purposes so you can design and style accordingly.

Subscription Cancellation

An example of a subscription cancellation checkout page If you're doing recurring billing you may likely be using sub_cancel links in your email receipts, which will allow your customers to follow a sub_token link with sub_cancel=true. This will immediately redirect the customer to your checkout, but it is a highly streamlined checkout experience geared only towards allowing your customer to cancel their subscription. As you can see in the example to the right, the checkout includes just three main pieces:

  1. The normal email + password authentication section.
  2. The cart.
  3. The button that is normally labelled something like “checkout” or “confirm your order”, but is now labelled “Cancel My Subscription”. (This text can be modified in your store's language section.)

It is important to note that the password is still required in order to confirm the cancellation. This is to prevent an unauthorized user from modifying a subscription they aren't supposed to be modifying.

"Update Info" Requests

If the cart parameter is set to updateinfo, the cart will immediately redirect to the checkout in “updateinfo” mode. Simply put, this will process an empty transaction, thus allowing the customer to update their password, address(es), or payment info. This updated information will be sent in a transaction datafeed, but the payment gateway will not be contacted during this checkout.

The updateinfo process can be useful in certain situations, but especially if you're handling subscriptions you'll likely want to use the sub_token rather than the updateinfo approach.

Pre-Payment Webhook

The pre-payment hook functionality sends a request to an endpoint of your choosing right before Foxy would otherwise submit the payment to the payment gateway, and allows for stores to apply custom validations for transactions prior to the request being set to the chosen payment method. A payload containing information about the current transaction is sent to the endpoint configured for this functionality, allowing the endpoint to respond to either approve or reject the transaction. If approved, the transaction will be sent on to the gateway to process, or if rejected the customer will be sent back to the checkout with a custom error message.

Common use cases for the pre-payment hook include:

  • Final inventory checks for low-stock or one-of-a-kind products.
  • Custom validations to ensure that certain product configurations are present.
  • Integrations with custom validation or fraud-check services (such as FraudLabsPro).

→ Read more...

Click here for details on how to use the Pre-Payment Webhook.