Require a minimum order value

A Shopify validation Function that blocks checkout below a minimum order value set in your store currency and converted for each market. Tested JavaScript.

Function API
Cart and Checkout Validation
Target
cart.validations.generate.run
Plan
Any plan in a public app; Shopify Plus in a custom app
App scope
write_validations
API version
2026-10
Last checked

In short

This validation Function blocks checkout when the cart subtotal is below a minimum, 500 in your store currency by default, converted to the customer’s currency. Customers can still build their cart freely; the rule applies from checkout on. It uses the cart.validations.generate.run target of the Cart and Checkout Validation API, version 2026-10, on any plan in a public app.

How to use it

  1. Create an app with the Shopify CLI and add the extension: shopify app generate extension --template cart_checkout_validation, with JavaScript.
  2. Replace the generated shopify.extension.toml and the two files in src/ with the files below.
  3. Run the fixtures with shopify app function run --input tests/below-minimum-at-checkout.input.json.
  4. Deploy with shopify app deploy and turn the validation on with validationCreate.
  5. Set the minimum and message in the validation’s $app:function-configuration metafield, for example {"minimumAmount": 250}.

Code

shopify.extension.toml
api_version = "2026-10"

[[extensions]]
name = "Minimum order value"
handle = "minimum-order-value"
type = "function"

  [[extensions.targeting]]
  target = "cart.validations.generate.run"
  input_query = "src/cart_validations_generate_run.graphql"
  export = "cart-validations-generate-run"

  [extensions.build]
  command = ""
  path = "dist/function.wasm"
src/cart_validations_generate_run.graphql
query CartValidationsGenerateRunInput {
  buyerJourney {
    step
  }
  cart {
    cost {
      subtotalAmount {
        amount
      }
    }
  }
  presentmentCurrencyRate
  validation {
    metafield(namespace: "$app", key: "function-configuration") {
      jsonValue
    }
  }
}
src/cart_validations_generate_run.js
// @ts-check

/**
 * @typedef {import("../generated/api").CartValidationsGenerateRunInput} CartValidationsGenerateRunInput
 * @typedef {import("../generated/api").CartValidationsGenerateRunResult} CartValidationsGenerateRunResult
 */

/** Settings stored as JSON in the validation's metafield. */
const DEFAULT_CONFIG = {
  // In the store's currency; converted to the customer's currency below.
  minimumAmount: 500,
  // {amount} is replaced with the converted minimum.
  message: "The minimum order value is {amount}. Add a few more items to check out.",
};

/** Errors about the cart as a whole show at the top of checkout and on the cart. */
const CART_TARGET = "$.cart";

/** @type {CartValidationsGenerateRunResult} */
const NO_ERRORS = { operations: [] };

/**
 * @param {CartValidationsGenerateRunInput} input
 * @returns {CartValidationsGenerateRunResult}
 */
export function cartValidationsGenerateRun(input) {
  // Let customers keep adding to the cart freely; enforce the rule from checkout on.
  if (input.buyerJourney.step === "CART_INTERACTION") return NO_ERRORS;

  const config = { ...DEFAULT_CONFIG, ...input.validation?.metafield?.jsonValue };
  // The cart is priced in the customer's currency; the minimum is set in the store's.
  const minimum = config.minimumAmount * Number(input.presentmentCurrencyRate);
  const subtotal = Number(input.cart.cost.subtotalAmount.amount);
  if (subtotal >= minimum) return NO_ERRORS;

  const message = config.message.replace("{amount}", minimum.toFixed(2));
  return { operations: [{ validationAdd: { errors: [{ message, target: CART_TARGET }] } }] };
}
Test input and expected output
tests/below-minimum-at-checkout.input.json
{
  "buyerJourney": { "step": "CHECKOUT_COMPLETION" },
  "cart": { "cost": { "subtotalAmount": { "amount": "420.0" } } },
  "presentmentCurrencyRate": "1.0",
  "validation": { "metafield": null }
}
tests/below-minimum-at-checkout.output.json
{
  "operations": [
    {
      "validationAdd": {
        "errors": [
          { "message": "The minimum order value is 500.00. Add a few more items to check out.", "target": "$.cart" }
        ]
      }
    }
  ]
}
tests/below-minimum-in-cart.input.json
{
  "buyerJourney": { "step": "CART_INTERACTION" },
  "cart": { "cost": { "subtotalAmount": { "amount": "420.0" } } },
  "presentmentCurrencyRate": "1.0",
  "validation": { "metafield": null }
}
tests/below-minimum-in-cart.output.json
{ "operations": [] }
tests/converted-minimum-met.input.json
{
  "buyerJourney": { "step": "CHECKOUT_INTERACTION" },
  "cart": { "cost": { "subtotalAmount": { "amount": "95.0" } } },
  "presentmentCurrencyRate": "0.031",
  "validation": { "metafield": { "jsonValue": { "minimumAmount": 3000 } } }
}
tests/converted-minimum-met.output.json
{ "operations": [] }

Written by Lintel. MIT license: free to use in your own store and in client work.

Limits and edge cases

  • The subtotal is before taxes and cart-level discounts, as Shopify defines cart.cost.subtotalAmount.
  • The minimum is converted with presentmentCurrencyRate; with fixed local prices per market, the converted figure may not be a round number.
  • Errors are skipped while the buyer journey step is CART_INTERACTION and returned at checkout interaction and completion.

Questions

How do I set a minimum order amount on Shopify?
Use a cart and checkout validation Function that compares the subtotal with your minimum and blocks checkout below it, as this example does.
Will customers see the message on the cart page?
Not with this code; it waits until checkout so the cart stays usable. Remove the CART_INTERACTION check if you want the message on the cart as well.
Can wholesale customers have a different minimum?
Yes. Add cart.buyerIdentity.purchasingCompany or a customer tag check to the input query and use a second amount for them.
  • Written for Function API version 2026-10 and checked against Shopify’s reference on the date shown. Shopify releases a new version every quarter.
  • Custom apps that contain Functions only run on Shopify Plus stores; a public app with the same Function runs on any plan unless the page says otherwise.
  • Every example ships with test fixtures, and we run them before publishing. Run them again after you change the code: shopify app function run.
  • MIT license. Use the code in your own store or in client work.

Need this built, tested and maintained for your store? See Shopify Functions development.

Have something to build? Let’s talk it through.

Taking a limited number of projects at a time. Every brief is read by an engineer, not a salesperson.

Gencer KarakayaShopify Engineer

An engineer reads your brief, and we reply by email.