Shopify Functions: custom discounts, delivery, payment and cart logic
Shopify Functions let you replace parts of Shopify's backend logic with your own code: discounts Shopify doesn't offer natively, bundles, rules for which delivery and payment options appear, and validation that stops an invalid order at checkout. Functions are compiled to WebAssembly, ship inside an app, and run on Shopify's infrastructure at the right moment in the cart and checkout. They are also the replacement for Shopify Scripts, which stopped running on June 30, 2026.
The Function APIs
| Function API | What it does | Typical uses |
|---|---|---|
| Discount | Adds product, order and shipping discounts | Tiered pricing, BOGO, bundles, customer-specific discounts, free shipping rules |
| Cart Transform | Expands, merges or updates cart lines | Bundles and kits, add-ons shown as one line |
| Delivery Customization | Hides, renames, reorders delivery options | Hide express for PO boxes, rename rates, sort local delivery first |
| Payment Customization | Hides, renames, reorders payment methods | Invoice only for B2B, hide COD above a value |
| Cart and Checkout Validation | Blocks progress with errors | Quantity limits, restricted items, required data, PO number rules |
| Fulfillment Constraints | Controls how items are fulfilled together | Ship certain items from one location |
| Order Routing Location Rule | Ranks locations for fulfillment | Custom location priority |
| Pickup point / Local pickup generators | Generate pickup options | Custom pickup networks |
Shopify allows up to 25 active functions per Function API for payment customizations, delivery customizations, validations and fulfillment constraints, so unrelated rules can live in separate, simpler functions.
Plan requirements
- Public apps from the App Store that contain Functions: usable on any plan.
- Custom apps that contain Functions: usable only on Shopify Plus.
- Some capabilities are Plus-only inside an API. For example, a payment customization on a non-Plus store hides the whole payment method rather than an individual placement.
- Network access from Functions is available primarily to Shopify for enterprises merchants, for specific Function APIs.
This is why the same requirement may be a custom app for a Plus store and a public app for a store on another plan.
How a Function is built
A Function is an extension inside an app, generated with Shopify CLI:
shopify app generate extension --template discount
It has three parts: configuration, an input query that declares the data it needs, and the run code that returns operations. Here is a tiered order discount that reads its tiers from a metafield, so merchants can change thresholds without a deploy.
Configuration
# extensions/tiered-discount/shopify.extension.toml
api_version = "2026-07"
[[extensions]]
name = "Tiered order discount"
handle = "tiered-discount"
type = "function"
[[extensions.targeting]]
target = "cart.lines.discounts.generate.run"
input_query = "src/cart_lines_discounts_generate_run.graphql"
export = "cart-lines-discounts-generate-run"
[extensions.build]
command = ""
path = "dist/function.wasm"
Input query
# src/cart_lines_discounts_generate_run.graphql
query CartInput {
cart {
cost {
subtotalAmount {
amount
}
}
}
discount {
discountClasses
metafield(namespace: "$app", key: "function-configuration") {
jsonValue
}
}
}
Run function
// src/cart_lines_discounts_generate_run.js
import {DiscountClass, OrderDiscountSelectionStrategy} from '../generated/api';
// Config example: {"tiers":[{"minSubtotal":100,"percentage":5},{"minSubtotal":250,"percentage":10}]}
export function cartLinesDiscountsGenerateRun(input) {
if (!input.discount.discountClasses.includes(DiscountClass.Order)) {
return {operations: []};
}
const tiers = input.discount.metafield?.jsonValue?.tiers ?? [];
const subtotal = Number(input.cart.cost.subtotalAmount.amount);
const tier = tiers
.filter((t) => subtotal >= t.minSubtotal)
.sort((a, b) => b.percentage - a.percentage)[0];
if (!tier) return {operations: []};
return {
operations: [
{
orderDiscountsAdd: {
candidates: [
{
message: `${tier.percentage}% off`,
targets: [{orderSubtotal: {excludedCartLineIds: []}}],
value: {percentage: {value: tier.percentage}},
},
],
selectionStrategy: OrderDiscountSelectionStrategy.First,
},
},
],
};
}
The function never fetches data and never touches the cart directly. Shopify gives it exactly what the input query asks for, and applies the operations it returns. That's what makes Functions fast and safe to run at checkout scale.
Validation, delivery and payment: the same shape
The other APIs follow the same pattern with different targets and operations:
- Validation (
cart.validations.generate.run) returnsvalidationAddoperations with error messages and targets such as$.cart. Its input includes the buyer journey step, so you can validate only at checkout. - Delivery customization (
cart.delivery-options.transform.run) returns operations such asdeliveryOptionHidefor a delivery option handle. - Payment customization (
cart.payment-methods.transform.run) returns operations such aspaymentMethodHidefor a payment method ID. - Cart transform (
cart.transform.run) returns expand and merge operations, reading bundle definitions from metafields.
Configuration: metafields, not code
Well-built Functions keep their rules in metafields on the discount, customization or shop: thresholds, tiers, excluded products, customer tags. An admin UI in your app, or an admin UI extension, lets the merchant edit them. The function code stays the same while the business changes its rules.
Limits to design around
- Functions have no network access by default. Put the data they need into metafields ahead of time.
- Execution has resource limits, so keep input queries small and logic simple. Request only the fields you use.
- Functions are deterministic: same input, same output. No clocks, randomness or state between runs. Use metafields updated by your app for time-based rules.
- Some behaviours are fixed by Shopify. For example, wallets can't be renamed by a payment customization, and payment customizations don't run in POS.
Testing and rollout
- Unit-test the run function with JSON input fixtures, which is easy because it's a pure function.
- Use
shopify app function runand the function logs in the dashboard to replay real inputs. - Deploy, then activate the discount or customization for a test segment first, such as a customer tag or a single market, before turning it on for everyone.
Decision checklist
- Which Function API matches the requirement? Could a native setting do it?
- Is the store on Plus (custom app possible) or not (public app needed)?
- What data does the function need, and which metafields will hold it?
- Who will change the rules, and through what UI?
- How will you test it with real carts before launch?
Frequently asked questions
What are Shopify Functions?
Shopify Functions are small programs, compiled to WebAssembly, that Shopify runs on its own infrastructure to customize backend logic: discounts, cart line transformations, delivery and payment options, validation and fulfillment. They ship inside an app, receive a GraphQL-shaped input and return a list of operations Shopify applies.
Do Shopify Functions require Shopify Plus?
It depends on how they're distributed. Stores on any plan can use public App Store apps that contain Functions. Custom apps that contain Functions can only be used by stores on Shopify Plus. Some individual capabilities, such as hiding specific payment method placements, are Plus-only as well.
What is a Shopify discount function?
A discount Function implements a discount type Shopify doesn't offer natively. The current Discount Function API can return product, order and shipping discounts from one function, using targets such as cart.lines.discounts.generate.run for cart lines and cart.delivery-options.discounts.generate.run for delivery. Merchants create discounts from your function in the admin like any other discount.
What is the Shopify cart transform function used for?
Cart Transform changes how lines appear in the cart and checkout, most often for bundles: expanding one bundle product into its component items, or merging several lines into one bundle line. It's how many bundle apps work.
What is Shopify delivery customization and payment customization?
They are two Function APIs that change the options a buyer sees at checkout. Delivery customization can hide, rename or reorder shipping and delivery options. Payment customization can hide, rename or reorder payment methods, with some limits, for example wallets such as Apple Pay can't be renamed.
Can a Shopify Function call an external API?
Normally no; Functions get their data from the input query, including metafields. Network access for Functions exists for some Function APIs but is available primarily to merchants on Shopify for enterprises and must be enabled by Shopify. The usual pattern is to write the data your function needs into metafields ahead of time.
Related
- Scripts to Functions migration
Shopify Scripts stopped running on June 30, 2026. How to migrate line item, shipping and payment scripts from Script Editor to Shopify Functions, with a pattern-by-pattern mapping, example code and a migration plan.
- Checkout UI extensions
How Shopify Checkout UI extensions work: targets, the Preact and Polaris web components API, settings, cart metafields, blocking progress, network access and plan limits, with a complete extension example.
- Shopify B2B
What Shopify B2B includes on each plan since April 2026, how companies, catalogs, volume pricing, payment terms and purchase orders work, and which B2B requirements need a custom Shopify B2B app or ERP integration.
- Hire a Shopify app developer
Freelance Shopify app developer for custom app development, ERP and accounting integrations, Checkout UI extensions and Shopify Functions. How an engagement works and what to prepare.