Hide Cash on Delivery above a cart total
A Shopify payment customization Function that hides Cash on Delivery when the cart total is above a limit you set, with tested JavaScript.
- Function API
- Payment Customization
- Target
- cart.payment-methods.transform.run
- Plan
- Any plan in a public app; Shopify Plus in a custom app
- App scope
- write_payment_customizations
- API version
- 2026-10
- Last checked
In short
This payment customization Function hides Cash on Delivery at checkout when the cart total is above a limit, 5,000 by default. The limit and the payment method names are read from a JSON metafield, so you can change them without a deploy. It uses the cart.payment-methods.transform.run target of the Payment Customization API, version 2026-10.
How to use it
- Create an app with the Shopify CLI (shopify app init), then add a Function extension: shopify app generate extension, and pick Payment customization with JavaScript.
- Replace the generated shopify.extension.toml and the two files in src/ with the files below.
- Run the tests: shopify app function run --input tests/above-limit.input.json (and the other fixtures).
- Deploy with shopify app deploy and install the app on the store.
- Create the customization with the paymentCustomizationCreate mutation, and set the function-configuration metafield if you want a different limit or method name.
Code
shopify.extension.tomlapi_version = "2026-10"
[[extensions]]
name = "Hide Cash on Delivery above a cart total"
handle = "hide-cod-above-total"
type = "function"
[[extensions.targeting]]
target = "cart.payment-methods.transform.run"
input_query = "src/cart_payment_methods_transform_run.graphql"
export = "cart-payment-methods-transform-run"
[extensions.build]
command = ""
path = "dist/function.wasm"
src/cart_payment_methods_transform_run.graphqlquery CartPaymentMethodsTransformRunInput {
cart {
cost {
totalAmount {
amount
}
}
}
paymentMethods {
id
name
}
paymentCustomization {
metafield(namespace: "$app", key: "function-configuration") {
jsonValue
}
}
}
src/cart_payment_methods_transform_run.js// @ts-check
/**
* @typedef {import("../generated/api").CartPaymentMethodsTransformRunInput} CartPaymentMethodsTransformRunInput
* @typedef {import("../generated/api").CartPaymentMethodsTransformRunResult} CartPaymentMethodsTransformRunResult
*/
/**
* Settings stored as JSON in the customization's metafield, so the limit can
* change without a deploy. Used when the metafield is missing.
*/
const DEFAULT_CONFIG = {
// Cart total (in the checkout currency) above which the methods are hidden.
maxAmount: 5000,
// Matched case-insensitively against the start of the payment method name.
methodNames: ["Cash on Delivery", "Kapıda Ödeme"],
};
/**
* Lowercase for matching, with Turkish dotless/dotted i folded to "i", so
* "KAPIDA ÖDEME" and "Kapıda Ödeme" match the same configured name.
* @param {string} text
*/
const fold = (text) => text.toLowerCase().replace(/ı/g, "i").replace(/\u0307/g, "");
/** @type {CartPaymentMethodsTransformRunResult} */
const NO_CHANGES = { operations: [] };
/**
* @param {CartPaymentMethodsTransformRunInput} input
* @returns {CartPaymentMethodsTransformRunResult}
*/
export function cartPaymentMethodsTransformRun(input) {
const config = { ...DEFAULT_CONFIG, ...input.paymentCustomization?.metafield?.jsonValue };
const total = Number(input.cart.cost.totalAmount.amount);
if (!(total > config.maxAmount)) return NO_CHANGES;
const names = config.methodNames.map(fold);
const operations = input.paymentMethods
.filter((method) => names.some((name) => fold(method.name).startsWith(name)))
.map((method) => ({ paymentMethodHide: { paymentMethodId: method.id } }));
return { operations };
}
Test input and expected output
tests/above-limit.input.json{
"cart": { "cost": { "totalAmount": { "amount": "6250.0" } } },
"paymentMethods": [
{ "id": "gid://shopify/PaymentCustomizationPaymentMethod/1", "name": "Credit card" },
{ "id": "gid://shopify/PaymentCustomizationPaymentMethod/2", "name": "Cash on Delivery (COD)" }
],
"paymentCustomization": { "metafield": null }
}
tests/above-limit.output.json{
"operations": [
{ "paymentMethodHide": { "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/2" } }
]
}
tests/below-limit.input.json{
"cart": { "cost": { "totalAmount": { "amount": "4999.99" } } },
"paymentMethods": [
{ "id": "gid://shopify/PaymentCustomizationPaymentMethod/1", "name": "Credit card" },
{ "id": "gid://shopify/PaymentCustomizationPaymentMethod/2", "name": "Cash on Delivery (COD)" }
],
"paymentCustomization": { "metafield": null }
}
tests/below-limit.output.json{ "operations": [] }
tests/configured-limit.input.json{
"cart": { "cost": { "totalAmount": { "amount": "1500.0" } } },
"paymentMethods": [
{ "id": "gid://shopify/PaymentCustomizationPaymentMethod/1", "name": "Kredi kartı" },
{ "id": "gid://shopify/PaymentCustomizationPaymentMethod/2", "name": "Kapıda Ödeme" }
],
"paymentCustomization": {
"metafield": { "jsonValue": { "maxAmount": 1000, "methodNames": ["Kapıda Ödeme"] } }
}
}
tests/configured-limit.output.json{
"operations": [
{ "paymentMethodHide": { "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/2" } }
]
}
tests/uppercase-turkish-name.input.json{
"cart": {
"cost": {
"totalAmount": {
"amount": "6250.0"
}
}
},
"paymentMethods": [
{
"id": "gid://shopify/PaymentCustomizationPaymentMethod/1",
"name": "Credit card"
},
{
"id": "gid://shopify/PaymentCustomizationPaymentMethod/2",
"name": "KAPIDA ÖDEME"
}
],
"paymentCustomization": {
"metafield": null
}
}tests/uppercase-turkish-name.output.json{
"operations": [
{ "paymentMethodHide": { "paymentMethodId": "gid://shopify/PaymentCustomizationPaymentMethod/2" } }
]
}
Written by Lintel. MIT license: free to use in your own store and in client work.
Limits and edge cases
- The cart total is in the currency the customer pays in. With several currencies, set the limit for the lowest-value currency you sell in.
- Methods are matched by the start of their name, as the customer sees it at checkout. Check the exact name of your Cash on Delivery method under Settings › Payments.
- Hiding a method only for some placements (for example accelerated checkout) is Shopify Plus only; this example hides it everywhere.
- A store can have up to 25 payment customizations.
Questions
- Can I hide Cash on Delivery without an app?
- Not by cart total. Shopify’s payment settings have no amount rule, so a payment customization Function is the supported way. Apps in the Shopify App Store package the same kind of Function.
- Does this work on Basic or Shopify plans?
- Yes, when the Function is part of a public app. Functions in a custom app only run on Shopify Plus stores.
- How do I change the limit later?
- Write {"maxAmount": 3000} to the customization’s $app:function-configuration metafield with metafieldsSet. The Function reads it on the next checkout; no redeploy needed.
- Can I hide it for certain products instead?
- Yes. Add cart.lines with each line’s product tags to the input query and hide the method when a tagged product is in the cart. The operation is the same paymentMethodHide.
Shopify documentation
- 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.
