CartThrob 9 documentation for ExpressionEngine commerce teams Visit CartThrob.com
CartThrob.com

Checkout Price Change (tag pair)

Use {exp:cartthrob:checkout_price_change} only as the target of a checkout_form configured with price_change_handling="template" and a price_change_template in group/template form.

The tag reads the opaque state identifier CartThrob places in the target URL. Do not create, alter, or expose that value yourself. It outputs no results when the state is missing, expired, consumed, from another session or cart, or used on a template other than the configured target.

Parameters

state

Optional. When omitted, the tag reads the state from the request URL. If supplied, it must be the same opaque token generated by CartThrob. It is not a price, quote ID, payment reference, or card value.

{exp:cartthrob:checkout_price_change}
    ...
{/exp:cartthrob:checkout_price_change}

Variables

The following single variables are available:

  • {checkout_price_changed} (1)
  • {checkout_price_change_handling} (template)
  • {checkout_price_change_currency}
  • {checkout_price_change_precision}
  • {checkout_price_change_expires_at}
  • {checkout_changed_components_list} (pipe-delimited names)
  • {checkout_price_change_title}
  • {checkout_price_change_description}
  • {checkout_price_change_continue_label}
  • {checkout_price_change_cancel_label}
  • {checkout_price_change_continue_url}
  • {checkout_price_change_cancel_url}
  • {checkout_previous_subtotal}, {checkout_previous_discount}, {checkout_previous_shipping}, {checkout_previous_tax}, {checkout_previous_total_adjustment}, and {checkout_previous_total}
  • {checkout_previous_subtotal_numeric}, {checkout_previous_discount_numeric}, {checkout_previous_shipping_numeric}, {checkout_previous_tax_numeric}, {checkout_previous_total_adjustment_numeric}, and {checkout_previous_total_numeric}
  • {checkout_current_subtotal}, {checkout_current_discount}, {checkout_current_shipping}, {checkout_current_tax}, {checkout_current_total_adjustment}, and {checkout_current_total}
  • {checkout_current_subtotal_numeric}, {checkout_current_discount_numeric}, {checkout_current_shipping_numeric}, {checkout_current_tax_numeric}, {checkout_current_total_adjustment_numeric}, and {checkout_current_total_numeric}
  • {checkout_previous_item_tax}, {checkout_previous_shipping_tax}, {checkout_previous_discount_tax}, {checkout_previous_discount_total} and their _numeric variants
  • {checkout_current_item_tax}, {checkout_current_shipping_tax}, {checkout_current_discount_tax}, {checkout_current_discount_total} and their _numeric variants
  • {checkout_previous_has_tax_breakdown} and {checkout_previous_tax_allocation}
  • {checkout_current_has_tax_breakdown} and {checkout_current_tax_allocation}
  • {checkout_previous_subtotal_source}, {checkout_previous_shipping_source}, {checkout_previous_tax_source}, and {checkout_previous_total_source}
  • {checkout_current_subtotal_source}, {checkout_current_shipping_source}, {checkout_current_tax_source}, and {checkout_current_total_source}
  • {checkout_subtotal_changed}, {checkout_discount_changed}, {checkout_shipping_changed}, {checkout_tax_changed}, and {checkout_total_changed} (1 or 0)

Test the matching has_tax_breakdown variable before displaying item, shipping, or discount-tax details. Those values are blank and allocation is unallocated when the finalized aggregate tax cannot be split exactly; the aggregate previous/current tax remains available. Sources are calculated, fixed, submitted, or extension.

The adjustment is derived from the other primary amounts. It is display-only, not an additional charge or an independently changed component.

{checkout_changed_components} is a tag pair. It provides one row for each changed component with {component}, {label}, {previous}, {previous_numeric}, {current}, and {current_numeric}.

Example

<section
    data-ct-checkout-price-change-modal
    aria-labelledby="price-change-title"
>
    {exp:cartthrob:checkout_price_change}
        <h1 id="price-change-title">
            {checkout_price_change_title}
        </h1>
        <p>{checkout_price_change_description}</p>
        <p>Adjustment: {checkout_previous_total_adjustment} → {checkout_current_total_adjustment}</p>
        <p>Previous total: {checkout_previous_total}</p>
        <p>Current total: {checkout_current_total}</p>
        {checkout_changed_components}
            <p>{label}: {previous} → {current}</p>
        {/checkout_changed_components}
        <p>
            <button
                type="button"
                data-ct-checkout-price-change-confirm
            >
                {checkout_price_change_continue_label}
            </button>
            <button
                type="button"
                data-ct-checkout-price-change-cancel
            >
                {checkout_price_change_cancel_label}
            </button>
        </p>
    {/exp:cartthrob:checkout_price_change}
</section>

CartThrob fetches the target template, extracts [data-ct-checkout-price-change-modal], and presents it over the existing checkout. The target controls the content and styling. CartThrob intercepts the two marked buttons to continue or cancel the original checkout submission. Because the page does not change, hosted payment fields such as Stripe Elements and PayPal Hosted Fields remain mounted.

Put any CSS needed by the modal inside the marked section or in a stylesheet already loaded by the checkout page. Scripts returned in the fetched template are not executed; use the documented data attributes for the two actions.

For backward compatibility, a target without data-ct-checkout-price-change-modal uses the older full-page transition. The Continue URL returns to the original checkout form with one-time acceptance state, while Cancel returns without accepting the updated total. Hosted payment fields cannot be serialized or restored after that navigation, so payment details must be entered again.

AJAX can also update coupons, addresses, shipping selections, and the displayed total before the final checkout submission.

The tag intentionally returns no_results for invalid state. The surrounding ExpressionEngine template owns any empty-state or HTTP status behavior.