Documentation Index
Fetch the complete documentation index at: https://docs.elasticfunnels.io/llms.txt
Use this file to discover all available pages before exploring further.
This guide walks through building checkout pages with the page builder or code editor. For exact semantics of checkout_settings, scope keys, and security options, see Checkout functionality reference. For template syntax details, see Frontend Template Engine: Checkout.
Prerequisites
- The page must have “Is checkout page” enabled (Page List → Edit Page → toggle on).
- A product must be linked via a buy link or cart.
- The domain must have a merchant assigned (Settings → Domains → Edit → Merchant).
Quiz pages that process payments must also have “Is checkout page” enabled. Quiz functionality and checkout are independent page flags — enabling both is valid and expected for quiz-to-purchase flows.
Example 1: Standard checkout
The most common layout: shipping address at the top, billing (same as shipping by default), payment panel with card form and wallet buttons, then a pay button.
Backend script
<script scope="backend">
var checkout_settings = checkout_settings || {};
checkout_settings.countries = ['US', 'CA', 'GB', 'AU'];
</script>
Template
<form id="checkout-form" method="POST">
<!-- Contact -->
<section class="shipping-info-card">
<div class="form-grid">
<div class="floating-label-group">
<label class="form-label">Phone Number *</label>
<input type="text" name="phone" data-template-value="checkout.customer.phone" class="form-input" />
<div class="form-error" data-checkout-error="phone"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Email Address *</label>
<input type="email" name="email" data-template-value="checkout.customer.email" class="form-input" />
<div class="form-error" data-checkout-error="email"></div>
</div>
</div>
</section>
<!-- Shipping address -->
<section class="shipping-info-card">
<h2 class="section-title">Shipping Information</h2>
<div class="form-grid">
<div class="floating-label-group">
<label class="form-label">Street Address *</label>
<input type="text" name="shipping_address"
data-template-value="checkout.customer.shipping_address"
autocomplete="shipping street-address" class="form-input" />
<div class="form-error" data-checkout-error="shipping_address"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Apt/Suite (Optional)</label>
<input type="text" name="shipping_address2"
data-template-value="checkout.customer.shipping_address2"
autocomplete="shipping address-line2" class="form-input" />
</div>
<div class="floating-label-group">
<label class="form-label">City *</label>
<input type="text" name="shipping_city"
data-template-value="checkout.customer.shipping_city"
autocomplete="shipping address-level2" class="form-input" />
<div class="form-error" data-checkout-error="shipping_city"></div>
</div>
<div class="floating-label-group">
<label class="form-label">State/Province *</label>
<select name="shipping_state"
data-template-value="checkout.customer.shipping_state"
autocomplete="shipping address-level1" class="form-input">
<option value="">Select State</option>
<template-foreach data-each="opt in checkout.usStates">
<option value="[[ opt.value ]]">[[ opt.label ]]</option>
</template-foreach>
</select>
<div class="form-error" data-checkout-error="shipping_state"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Postal Code *</label>
<input type="text" name="shipping_zip"
data-template-value="checkout.customer.shipping_zip"
autocomplete="shipping postal-code" class="form-input" />
<div class="form-error" data-checkout-error="shipping_zip"></div>
</div>
<input type="hidden" name="shipping_country"
data-template-value="checkout.customer.shipping_country" />
</div>
<!-- Same as billing toggle -->
<label class="checkbox-label">
<input type="checkbox" checked
data-template-value="checkout.shipping_same_as_billing"
data-set-onload class="checkbox-input" />
<span class="checkbox-text">Shipping details same as billing</span>
</label>
</section>
<!-- Conditional billing (shown when unchecked) -->
<template-if data-condition="checkout.shipping_same_as_billing == false">
<section class="billing-info-card">
<h2 class="section-subtitle">Billing Information</h2>
<template-if data-condition="!checkout.shipping_same_as_billing">
<div class="form-grid">
<div class="floating-label-group">
<label class="form-label">Full Name *</label>
<input type="text" name="billing_first_name"
data-template-value="checkout.customer.billing_first_name" class="form-input" />
<div class="form-error" data-checkout-error="billing_first_name"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Street Address *</label>
<input type="text" name="billing_address"
data-template-value="checkout.customer.billing_address" class="form-input" />
<div class="form-error" data-checkout-error="billing_address"></div>
</div>
<div class="floating-label-group">
<label class="form-label">City *</label>
<input type="text" name="billing_city"
data-template-value="checkout.customer.billing_city" class="form-input" />
<div class="form-error" data-checkout-error="billing_city"></div>
</div>
<div class="floating-label-group">
<label class="form-label">State/Province *</label>
<select name="billing_state"
data-template-value="checkout.customer.billing_state" class="form-input">
<option value="">Select State</option>
<template-foreach data-each="opt in checkout.usStates">
<option value="[[ opt.value ]]">[[ opt.label ]]</option>
</template-foreach>
</select>
<div class="form-error" data-checkout-error="billing_state"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Postal Code *</label>
<input type="text" name="billing_zip"
data-template-value="checkout.customer.billing_zip" class="form-input" />
<div class="form-error" data-checkout-error="billing_zip"></div>
</div>
</div>
</template-if>
<template-else>
<p class="billing-info-message">Billing address same as shipping.</p>
<input type="hidden" name="billing_first_name" data-template-value="checkout.customer.billing_first_name" />
<input type="hidden" name="billing_address" data-template-value="checkout.customer.billing_address" />
<input type="hidden" name="billing_city" data-template-value="checkout.customer.billing_city" />
<input type="hidden" name="billing_state" data-template-value="checkout.customer.billing_state" />
<input type="hidden" name="billing_zip" data-template-value="checkout.customer.billing_zip" />
</template-else>
<input type="hidden" name="billing_country" data-template-value="checkout.customer.billing_country" />
</section>
</template-if>
<!-- Order summary -->
<template-if data-condition="is_single_product">
<div class="order-summary-single">
<img ef-src="checkout.product_image" ef-alt="checkout.product_title" />
<h2><span ef-text="checkout.product_title">Product Title</span></h2>
<span ef-text="checkout.product_price">$0.00</span>
</div>
</template-if>
<!-- Totals -->
<div class="price-summary-section">
<div class="price-breakdown-row">
<span>Subtotal</span>
<span ef-text="checkout.subtotal_after_discount">$0.00</span>
</div>
<template-if data-condition="checkout.shipping_raw > 0">
<div class="price-breakdown-row">
<span>Shipping</span>
<span ef-text="checkout.shipping">FREE</span>
</div>
</template-if>
<template-if data-condition="checkout.tax_raw > 0">
<div class="price-breakdown-row">
<span>Tax</span>
<span ef-text="checkout.tax">$0.00</span>
</div>
</template-if>
<div class="total-price-row">
<span class="total-label">Total</span>
<span ef-text="checkout.total">$0.00</span>
</div>
</div>
<!-- Payment panel (card + wallet buttons injected automatically) -->
<checkout-cc-panel></checkout-cc-panel>
<div data-checkout-message class="checkout-message"></div>
<!-- Submit -->
<button type="submit" data-checkout-submit form="checkout-form"
class="pay-now-button">
<span class="pay-now-text">PAY NOW</span>
</button>
</form>
Apple Pay and Google Pay buttons appear automatically in the payment panel when available. When a customer pays with a wallet, the correct billing address from the wallet is used for payment verification — no special handling needed in the template.
Example 2: Wallet-first checkout
For quiz funnels or lightweight pages where you want wallet buttons prominently at the top. This uses the same blocks and backend processing as the standard checkout — the only difference is layout order.
Backend script
<script scope="backend">
var checkout_settings = checkout_settings || {};
checkout_settings.countries = ['US', 'CA'];
// Optional: subscription wallet labels
checkout_settings.wallet = {
recurring_description: 'Monthly wellness subscription',
management_url: 'https://members.yourdomain.com/subscriptions'
};
</script>
Template
<form id="checkout-form" method="POST">
<!-- Contact (minimal: email + phone first) -->
<section class="shipping-info-card">
<div class="form-grid">
<div class="floating-label-group">
<label class="form-label">Email Address *</label>
<input type="email" name="email" data-template-value="checkout.customer.email"
autocomplete="email" class="form-input" />
<div class="form-error" data-checkout-error="email"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Phone Number *</label>
<input type="text" name="phone" data-template-value="checkout.customer.phone"
class="form-input" />
<div class="form-error" data-checkout-error="phone"></div>
</div>
</div>
</section>
<!-- Payment panel EARLY (wallet buttons appear here, above shipping) -->
<checkout-cc-panel></checkout-cc-panel>
<div data-checkout-message class="checkout-message"></div>
<!-- Shipping address (below payment) -->
<section class="shipping-info-card">
<h2 class="section-title">Shipping Information</h2>
<div class="form-grid">
<div class="floating-label-group">
<label class="form-label">Street Address *</label>
<input type="text" name="shipping_address"
data-template-value="checkout.customer.shipping_address"
autocomplete="shipping street-address" class="form-input" />
<div class="form-error" data-checkout-error="shipping_address"></div>
</div>
<div class="floating-label-group">
<label class="form-label">City *</label>
<input type="text" name="shipping_city"
data-template-value="checkout.customer.shipping_city"
autocomplete="shipping address-level2" class="form-input" />
<div class="form-error" data-checkout-error="shipping_city"></div>
</div>
<div class="floating-label-group">
<label class="form-label">State/Province *</label>
<select name="shipping_state"
data-template-value="checkout.customer.shipping_state"
autocomplete="shipping address-level1" class="form-input">
<option value="">Select State</option>
<template-foreach data-each="opt in checkout.usStates">
<option value="[[ opt.value ]]">[[ opt.label ]]</option>
</template-foreach>
</select>
<div class="form-error" data-checkout-error="shipping_state"></div>
</div>
<div class="floating-label-group">
<label class="form-label">Postal Code *</label>
<input type="text" name="shipping_zip"
data-template-value="checkout.customer.shipping_zip"
autocomplete="shipping postal-code" class="form-input" />
<div class="form-error" data-checkout-error="shipping_zip"></div>
</div>
<input type="hidden" name="shipping_country"
data-template-value="checkout.customer.shipping_country" />
</div>
<label class="checkbox-label">
<input type="checkbox" checked
data-template-value="checkout.shipping_same_as_billing"
data-set-onload class="checkbox-input" />
<span class="checkbox-text">Billing same as shipping</span>
</label>
</section>
<!-- Totals -->
<div class="price-summary-section">
<div class="total-price-row">
<span class="total-label">Total</span>
<span ef-text="checkout.total">$0.00</span>
</div>
</div>
<!-- Submit -->
<button type="submit" data-checkout-submit form="checkout-form"
class="pay-now-button">
<span class="pay-now-text">COMPLETE ORDER</span>
</button>
</form>
The key difference is <checkout-cc-panel> is placed above the shipping address section. Customers with Apple Pay or Google Pay see wallet buttons immediately after entering email/phone. Customers without wallet support see the card form there instead and fill in shipping below.
Both examples use the same checkout-details, checkout-cc-panel, and checkout-button patterns. The backend processes them identically. The only difference is where the blocks appear in the template.
Example 3: Minimal digital product checkout
For digital products where you only need email and payment — no name or address fields. The card panel automatically adds a cardholder name field and billing details since they’re missing from the page.
Template
<form id="checkout-form" method="POST">
<section class="shipping-info-card">
<div class="floating-label-group">
<label class="form-label">Email Address *</label>
<input type="email" name="email" data-template-value="checkout.customer.email"
autocomplete="email" class="form-input" />
<div class="form-error" data-checkout-error="email"></div>
</div>
</section>
<checkout-cc-panel data-accepted-cards="visa,mastercard,amex,discover"
data-applepay-layout="separated"></checkout-cc-panel>
<div data-checkout-message class="checkout-message"></div>
<button type="submit" data-checkout-submit class="checkout-button">Purchase</button>
</form>
What happens automatically:
- Apple Pay appears above the card panel with an “OR PAY ANOTHER WAY” divider. Since only email is on the page, the wallet only requests email — no address sheet.
- Card panel shows cardholder name (since
first_name/last_name aren’t on the page) and billing details (name, country, address).
- Billing values are automatically copied to shipping during submission.
Configuration options
These are set via checkout_settings in a <script scope="backend"> block. See Checkout functionality reference for full details.
| Setting | Purpose |
|---|
checkout_settings.countries | Restrict country dropdown (e.g. ['US', 'CA']) |
checkout_settings.usStates | Restrict US state dropdown (e.g. ['CA', 'TX']) |
checkout_settings.abuse_protection | Card-testing / velocity protection |
checkout_settings.wallet.recurring_description | Label on Apple Pay sheet for subscriptions |
checkout_settings.wallet.management_url | Subscription management URL for Apple Pay |
Page builder vs code editor
Both examples above show raw HTML for the code editor. In the page builder, use the Checkout block category:
- Checkout Details — contact + shipping + billing (same as Example 1 contact/shipping sections)
- Credit Card Form — equivalent to
<checkout-cc-panel>
- Checkout Button — equivalent to
<button data-checkout-submit>
- Price Summary — totals section
- Checkout Coupon — coupon input with apply/clear
- Checkout Bumps — order bump checkboxes
- Single Product Summary — product image/title for single-product checkout
- Cart Products — product list for multi-item checkout
Drag these blocks in your preferred order. For a wallet-first layout, place Credit Card Form above Checkout Details.