Shopify Merchants on Standard Plans Are Losing Chargebacks Over Terms-of-Sale Proof — Here's the Evidence Trail You Can Build Without Plus

By Milan Dhameliya · · 8 min read

Banks are increasingly rejecting chargeback disputes unless merchants can show a timestamped record that the customer saw and agreed to their terms of sale before paying — evidence Shopify's hosted checkout doesn't generate by default, and that customizing the checkout page to fix directly requires Plus. Here's how to build a real consent trail on any plan, and where the gap still exists on accelerated checkout.

A thread on the Shopify Community forum lays out a problem that's easy to miss until it costs you real money: banks are now routinely rejecting chargeback disputes unless a merchant can produce specific proof that the customer saw and affirmatively agreed to the terms of sale before the card was charged — not just that a terms page exists somewhere on the site. The merchant who started the thread put it bluntly: standard-plan stores don't have the ability to customize Shopify's hosted checkout the way Plus stores can, so there's no built-in way to insert a mandatory, logged "I agree" step at the exact moment of payment. You lose the dispute not because you did anything wrong, but because you can't produce the specific artifact the bank's dispute team is asking for.

This isn't a hypothetical edge case. It's the same structural gap covered from a different angle in our checkout customization guide: modifying the checkout page itself — adding a custom validation, a mandatory checkbox, a blocking condition — has historically required Shopify Plus, because Shopify Functions and full checkout UI extension capacity are gated by plan. Terms-of-sale consent evidence just happens to be the specific place where that gap turns into lost revenue.

What the bank's dispute team is actually asking for

Replies in the thread narrow down what "proof" means in practice, and it's more specific than most merchants assume:

None of this is unreasonable from the bank's side. It is, however, a real gap on Shopify's standard plans, because the one native setting built for exactly this — a confirmation step with terms text before the charge — isn't visible or configurable on every account by default.

Step 1: turn on the native confirmation step first

Before reaching for an app or custom code, check Settings → Checkout → Order processing for Require a confirmation step. When enabled, it adds a review screen after payment details are entered and before the order is placed, and lets you fill in Terms of sale and Refund policy text in the Policies section, referenced with placeholders like:

By completing your order, you agree to our %{terms_of_sale} and %{refund_policy}.

Two things worth knowing before you go looking for it:

Step 2: for cart-specific consent, capture it before checkout with a timestamped cart attribute

Because the checkout page itself is off-limits for custom logic without Plus, the practical workaround merchants in the thread converge on is moving the checkbox one step earlier, onto the cart page, and writing the agreement into a cart attribute so it rides through checkout and lands on the order as a permanent, timestamped record.

Add a checkbox to your cart template (via the theme editor's Cart & Checkout section, or directly in cart.liquid/main-cart-items.liquid depending on your theme):

<label for="terms-consent">
  <input type="checkbox" id="terms-consent" required>
  I agree to the <a href="/policies/terms-of-service" target="_blank">Terms of Sale</a>
</label>

On check, write the agreement — plus a timestamp — into the cart's attributes via the Ajax Cart API, so it's stored server-side against the cart rather than only in the browser:

document.getElementById("terms-consent").addEventListener("change", async (event) => {
  if (!event.target.checked) return;

  await fetch("/cart/update.js", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      attributes: {
        terms_consent: "agreed",
        terms_consent_timestamp: new Date().toISOString(),
        terms_consent_version: "2026-07-01",
      },
    }),
  });
});

Cart attributes carry through to the order and show up in the admin order page under Additional details, and in Liquid via order.attributes:

{% for attribute in order.attributes %}
  
  • {{ attribute.first }}: {{ attribute.last }}
  • {% endfor %}

    Versioning the consent (terms_consent_version) matters more than it looks — if you update your terms of sale six months from now, you want every historical order to show which version of the terms the customer actually saw, not your current one.

    Step 3: know exactly where this workaround stops covering you

    Cart attributes only get written if the customer actually passes through your cart page. Dynamic checkout buttons — Buy it now, Shop Pay, Google Pay/Apple Pay buttons rendered directly on the product page — skip the cart entirely and take the customer straight into checkout. Any order placed through one of those buttons has no cart-attribute consent record, because the checkbox never rendered.

    You have two honest options here, not a clean fix:

    Don't present the cart-attribute checkbox to a client or your own team as "we now have full consent coverage" — it's real evidence, but only for the traffic that goes through the cart.

    Step 4: on Shopify Plus, close the gap with a checkout validation Function

    If you're on Plus, you can go further than a cart-page checkbox: block checkout completion entirely unless consent was captured, using a cart and checkout validation Function, the same replacement platform covered in our Shopify Scripts to Functions migration guide. The Function reads the cart attribute set by your checkbox and rejects checkout if it's missing, which is a materially stronger dispute artifact than a cart-page checkbox alone — the order literally could not have been placed without the flag being set:

    export function cartValidationsGenerateRun(input) {
      const hasConsent = (input.cart?.attribute?.value === "agreed");
    
      if (hasConsent) return { operations: [] };
    
      return {
        operations: [
          {
            validationAdd: {
              errors: [
                {
                  message: "Please confirm you agree to the Terms of Sale to continue.",
                  target: "$.cart",
                },
              ],
            },
          },
        ],
      };
    }

    This still depends on the checkbox rendering somewhere before the validation runs (cart page or a checkout UI extension block), so pair it with a Checkout UI Extension that renders the checkbox directly inside checkout for Plus stores, rather than relying solely on the cart-page version.

    What to actually submit when you dispute a chargeback

    Having the record is only half the job — submit it in a form the bank's dispute portal can actually use:

    Setting expectations honestly

    None of this is a guaranteed dispute win — banks still weigh evidence case by case, and the community thread is clear that merchants remain uncertain how consistently these records actually convert to wins. What this does do is close the gap between "we have a terms page" and "we have a timestamped record tied to this specific order," which is the actual difference between evidence a dispute team accepts and evidence it doesn't.

    Code Kaarigari builds cart-attribute consent capture, checkout validation Functions, and Checkout UI Extensions for stores on every plan tier, and can wire up an evidence trail that matches what your payment processor and bank actually ask for. Start with our Shopify development services, or contact Code Kaarigari to get a consent and evidence audit running against your own checkout flow.

    Sources reviewed