Skip to main content
This page covers checkout-only usage of the frontend template engine: order summary, totals, form fields, coupon, order bumps, and payment. For cart items and quantity controls, see Cart.
checkout exists only on checkout pages. Guard shared templates with <template-if data-condition="checkout"> when using checkout.*.

Order summary: single-product vs cart

Checkout can show either a single-product block (one product, hero image + title) or a cart block (list of items with thumbnails and qty). Use the scope flags to choose.
ConditionWhen it’s trueUse in template
is_single_productOne product in cartcheckout.product_image, checkout.product_title, checkout.product_price, checkout.retail_price
is_cartMultiple items<template-foreach data-each="item in cartItems"> with [[ item.image ]], [[ item.name ]], [[ item.total ]]
Do not use cartItems[0] in the single-product block. Use checkout.product_* keys only.
<template-if data-condition="is_single_product">
  <div class="order-summary-single">
    <img src="[[ checkout.product_image ]]" alt="[[ checkout.product_title ]]" />
    <h2>[[ checkout.product_title ]]</h2>
    <span>[[ checkout.product_price ]]</span>
    <span>[[ checkout.retail_price ]]</span>
  </div>
</template-if>
<template-else-if data-condition="is_cart">
  <template-foreach data-each="item in cartItems">
    <div class="order-summary-line">
      <img src="[[ item.image ]]" alt="[[ item.name ]]" />
      <span>[[ item.name ]]</span>
      <span>[[ item.total ]]</span>
    </div>
  </template-foreach>
</template-else-if>
Equivalent binding-style variant (often easier to preview/edit in the page builder):
<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>
    <span ef-text="checkout.retail_price">$0.00</span>
  </div>
</template-if>
You can add ef-* attributes from the Data Attributes section (right sidebar) when a component is selected.

Totals (display + raw)

Checkout exposes the same totals keys in both modes (is_single_product and is_cart) so one layout can be reused for either flow. Use formatted keys for display ([[ checkout.total ]]). Use *_raw only for conditions/math.
Formatted keyRaw keyMeaning
checkout.product_pricecheckout.product_price_rawMain products amount (excludes bumps).
checkout.retail_pricecheckout.retail_price_rawRetail/original amount for main products.
checkout.bump_totalcheckout.bump_total_rawSelected order bumps total.
checkout.subtotal_before_discountcheckout.subtotal_before_discount_rawProduct + bumps before coupon discount.
checkout.discount_totalcheckout.discount_total_rawCoupon discount amount.
checkout.subtotal_after_discountcheckout.subtotal_after_discount_rawSubtotal after coupon discount, before shipping/tax.
checkout.subtotalcheckout.subtotal_rawBackward-compatible alias of subtotal_after_discount.
checkout.shippingcheckout.shipping_rawShipping amount.
checkout.taxcheckout.tax_rawTax amount.
checkout.totalcheckout.total_rawFinal grand total.
Recommended template bindings:
  • Subtotal row: [[ checkout.subtotal_after_discount ]]
  • Total row: [[ checkout.total ]]
  • Conditions: checkout.bump_total_raw > 0, checkout.tax_raw > 0, checkout.shipping_raw > 0

Form fields and validation

Binding fields

Use data-template-value="checkout.customer.<field>" on each input/select. Paths follow the checkout field spec (e.g. checkout.customer.email, checkout.customer.shipping_first_name).
  • Wrap each field in a container (e.g. .floating-label-group).
  • Add <div class="form-error" data-checkout-error="<id>"></div> so the form errors adapter can show validation messages.
  • Use checkout.countries and checkout.usStates for select options (each { value, label }).

Shipping same as billing

  • Bind the checkbox with data-template-value="checkout.shipping_same_as_billing".
  • Wrap the shipping address block in <template-if data-condition="!checkout.shipping_same_as_billing">.
  • Use <template-else> to show a short message when shipping equals billing.

Billing section visibility

The entire billing info section can be wrapped in <template-if data-condition="checkout.shipping_same_as_billing == false"> so the billing block is hidden when “same as billing” is checked. Inside that section, use <template-if data-condition="!checkout.shipping_same_as_billing"> for the visible billing form and <template-else> for a short message plus hidden billing inputs so the backend still receives billing data.

Validation errors

  • errors is an object keyed by field id: errors['email'], errors['shipping_city'], etc.
  • The form errors adapter looks for [data-checkout-error="<id>"] and sets the message; it also adds .error to the input and .has-error to the wrapper.
  • You can show the message in the template: [[ errors[f.id] ]] or data-ef-text="errors['email']".

Coupon

ScopePurpose
query.couponURL has ?coupon=... — use for showing the “automatically applied” block.
checkout.couponApplied coupon: applied, code, name, value_formatted.
checkout.coupon_messageValidation error message (e.g. invalid code).
  • Apply button: data-apply-coupon (plugin binds to validate + refresh).
  • Clear button: data-clear-coupon; show when checkout.coupon.applied.
  • Error block: <template-if data-condition="checkout.coupon_message"> with [[ checkout.coupon_message ]].

Order bumps

ScopePurpose
checkout.bump_productsAll available bumps (each: code, name, description, price formatted, price_raw, image, added).
checkout.selectedBumpLinesSelected bumps (each: code, name, price, price_raw).
  • Display [[ bump.price ]] and [[ line.price ]] as-is (already formatted).
  • Checkbox pattern: Use data-bump-code, data-bump-checkbox, data-bump-toggle and name="bump", value="[[ bump.code ]]" so the bump plugin can toggle and style. See Cart for the full offer vs added pattern.
  • Button + @click pattern: Alternatively, use <button @click="bump.added = true">…</button> for “Add to order” and <button @click="bump.added = false">…</button> for “Remove”. Mutating bump.added triggers re-render. Both checkbox + data-bump-* and button + @click are valid.

Payment panel

Pattern A (generic): Replace the payment block with only:
<checkout-cc-panel></checkout-cc-panel>
The app injects the full payment UI (method choice, card inputs, etc.). Do not keep duplicate payment method pickers or card sections in the template. Pattern B (explicit gateway): When the source already has an explicit gateway structure (e.g. NMI), preserve it. Use a wrapper <div class="checkout-cc-panel" data-gateway="nmi"> (or other gateway). Inside: (1) optional cardholder name in <template-if data-condition="!(checkout && checkout.customer && checkout.customer.first_name)"> with data-template-value="checkout.customer.cardholder_name" and <div class="form-error" id="cc-name-error"></div>; (2) a secure-card-element div with data-brand-id, data-domain-id, data-merchant-code, data-accepted-cards, plus a sibling <div class="form-error" id="nmi-card-errors"></div> for card errors.

Summary

  • Use is_single_product / is_cart and checkout.product_* vs cartItems for order summary.
  • Prefer explicit subtotal keys: checkout.subtotal_before_discount / checkout.subtotal_after_discount.
  • checkout.subtotal remains as a backward-compatible alias of checkout.subtotal_after_discount.
  • Use _raw keys only for conditions/math or explicit formatting with formatPrice.
  • Bind fields with data-template-value, use data-checkout-error and errors for validation.
  • Coupon: query.coupon, checkout.coupon, checkout.coupon_message; data-apply-coupon, data-clear-coupon.
  • Bumps: checkout.bump_products, checkout.selectedBumpLines; data-bump-* attributes.
  • Payment: Pattern A<checkout-cc-panel></checkout-cc-panel> only; Pattern B — explicit gateway div with data-gateway, optional cardholder, secure-card-element.