Ga naar inhoud

Custom Discount

Deze inhoud is nog niet vertaald.

A Custom Discount holds product, order and shipping offers in one discount, and lets each offer read its value from a metafield you already own. Instead of typing a price for every product, you keep the numbers on your variants — where your ERP, your spreadsheet or your supplier feed already puts them — and the discount looks them up as the cart is priced.

Use it when the numbers belong to the products rather than to the promotion: wholesale price lists, per-item contract pricing, case prices that differ by SKU. If the same value applies to everything in the promotion, Multi-offer is simpler.

Three pages cover this discount type. This one is the reference. Set up the price metafield walks through creating the metafield in Shopify from nothing. Custom Discount examples has thirteen complete setups to copy.

  1. Open Discounts, click Create discount, and choose Custom Discount.

  2. Choose the discount method and name the discount.

    Automatic applies on its own. Discount code asks the customer to enter a code. The title is what the customer sees; tick Add internal title for reference if you want a private name as well.

  3. Choose where the discount applies.

    All markets, or only the markets you pick. Each offer below can narrow this further.

  4. Turn on Read prices from a product metafield, if your prices live in your own data.

    Enter the namespace and key of the metafield that holds them. This address applies to the whole discount — one metafield per discount, read from every variant in the cart.

    The Read prices from a product metafield card, switched on, with Namespace set to custom and Key set to volume_pricing, help text explaining the JSON shape, and the Check a product button
  5. Add cart groups to describe which items you mean.

    A cart group is a named selection of items — “Case packs”, “Spring range” — built from conditions. Groups are defined once and reused by any offer. All items is built in.

  6. Turn on the offers you need, and point each one at a group.

    Product, order and shipping offers each have their own value, their own items and their own requirements.

  7. Set requirements, overlap handling, combinations, markets and dates, then save.

The discount reads a variant metafield. Shopify shows product metafields and variant metafields on the same page, and only the variant one is read.

The Shopify product page showing a Product metafields card with an empty Volume pricing field above a Variant metafields card whose Volume pricing field holds a JSON value

The metafield is a JSON object of flat name-and-value pairs. You choose the names.

{ "v": 1, "qty6": 850, "qty12": 750, "floor": 700, "pack": 12, "season": "ss26" }
  • "v": 1 is required. It is the format version, and it lets the shape grow later without changing what today’s values mean. A metafield without it, or with any other value, is ignored.
  • Numbers are amounts, or values a condition can compare. 850 and 12 above.
  • Text is for selecting items, never for money. "ss26" above.
  • Anything else — true, a nested object, a list — is ignored.
  • Two names are reserved: v and unit. Every other name is yours.

Field names use letters, digits, _ and -, and start with a letter or _.

By default a number is a whole minor unit of your store currency, so 850 means 8.50 and 249 means 2.49. This is the format most ERP exports already use, and it cannot be misread.

If you would rather write prices the way you say them, add "unit": "major":

{ "v": 1, "unit": "major", "qty6": 8.50, "qty12": 7.50, "floor": 7 }

Both files above mean the same thing. Some rules to know:

  • The setting applies to the whole metafield, not to one field. Pick one style per variant.
  • Under "major", an amount with more decimals than your currency has is ignored — 1.999 in a two-decimal currency, 15.5 in yen. The app will not round it into money.
  • "unit" must be exactly minor or major. Any other word and the whole metafield is ignored, so a typo cannot quietly change what your prices mean.
  • A percentage is never affected by this setting. It is always read as written, so 10 is 10% in both styles, and 12.5 is 12.5%.

Set a product offer’s Discount value to Price from product data.

A product offer card with Discount value set to Price from product data, a Field box containing qty, What the number means set to Fixed price for the item, Tiered by line quantity switched on, and empty Floor field and Cap field boxes
SettingWhat it does
FieldThe name in the metafield that holds this offer’s number.
What the number meansFixed price for the item — the item is sold at that price. Amount off each item — that much comes off each unit. Percentage off — that percentage comes off.
Tiered by line quantityReads qty6, qty12 and so on, using the highest tier the line’s quantity reaches.
Floor fieldNames a field holding the lowest price the item may reach.
Cap fieldNames a field holding the most that may come off one unit.

Three things that are easy to miss:

  • Tiers are per line. Six of a variant in one line reaches tier 6. Three plus three in two separate lines does not.
  • A field name used for tiers must not end in a digit, because the tier number is added to the end of it. price_1 with tiers on would read price_12 as tier 2. The app refuses that combination when you save.
  • A floor or cap you name must be readable on the variant. If you point an offer at a cap field and a variant has no usable cap, that item gets no discount — the limit is never dropped silently.

An item gets no discount, rather than a wrong one, whenever the number is missing, zero, negative, text, or not a usable amount. That is deliberate: a discount that cannot be computed exactly does not run.

Give the metafield a definition with a JSON schema and Shopify refuses a broken value at the moment someone saves it — in the admin, in the bulk editor and through the API. Create the definition under Settings → Metafields and metaobjects → Variants, choose the JSON type, and paste this as the schema:

{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["v"],
"minProperties": 2,
"maxProperties": 40,
"properties": {
"v": { "type": "integer", "enum": [1] },
"unit": { "type": "string", "enum": ["minor", "major"] },
"label": { "type": "string", "maxLength": 40 }
},
"patternProperties": {
"^(qty|price|wholesale|floor|cap|min|max)[0-9]*$": {
"type": "number", "minimum": 0, "maximum": 100000000
}
},
"additionalProperties": { "type": ["number", "string", "boolean"] }
}

That schema refuses a wrong version, a list instead of an object, a nested object, a negative amount, and a misspelled unit. Shopify does not enforce field-name patterns on a metafield schema, so field names are checked by the app instead, when you type one into an offer or a condition.

A cart group is a named selection of cart items. Offers refer to groups by name, so one group can target one offer and gate another.

Each group matches all, any or none of its conditions. “None” is the complement: the group holds every item that matches none of the conditions.

Conditions available on a Custom Discount:

GroupConditions
ItemSpecific products, specific variants, specific collections, line quantity, line subtotal, line attribute, compare-at price, has selling plan
Price dataPrice data field, Price data number — shown only while Read prices from a product metafield is on, and only on product offers
CartCart subtotal, cart quantity, cart attribute, retail location
CustomerCustomer tags, customer logged in, customer is B2B, company
ShippingShipping type, shipping method, country, province or state, city, postal code — shipping offers only

Product tags and customer order count are not available on this discount type.

Price data field selects items by a piece of text in the metafield — a season code, a supplier, a contract tier. Price data number selects them by a number in it.

A cart group named Group 1 matching all of one condition: Field set to Price data number, Operator set to is greater than or equal to, and a Value row with Field name in the metafield set to pack and Number set to 6

A number condition offers equals, is greater than or equal to, is less than or equal to, and is between. It reads the number exactly as written, so it is unaffected by the unit setting — pack of 12 is twelve whether your amounts are in minor or major units.

What happens when an item has no price data, or not that field:

  • A positive condition does not match it.
  • A negative text condition — is not one of, does not contain — does match it. The field genuinely is not there.
  • A number condition never matches it, whichever comparison you chose. “Less than or equal to 6” does not match an item with no number to compare.
  • A group set to none therefore includes items with no price data, which is usually what you want from “everything except the case packs”.

Requirements gate an offer on a whole group rather than on each item. Choose a group, a measure and a comparison.

MeasureWhat it counts
Subtotal, compare-at subtotalMoney in the group
QuantityUnits in the group
Lines, products, variantsHow many of each are in the group
Groups matchedHow many of several named groups the cart has items from

Comparisons are equals, is greater than or equal to, is less than or equal to and is divisible by. Divisible by works on the whole number measures — quantity, lines, products, variants, groups matched — and is how you express “every third item” or “sold in pairs”.

  • When offers overlap, choose to apply the first matching offer, the one that saves the most, or all of them. Applying all is unavailable while an offer reads prices from product data.
  • An overall item limit caps how many units the whole discount may touch, taking the highest-value, lowest-value or first items in the cart.
  • Combinations decide whether this discount runs alongside product, order and shipping discounts. See Combinations.
  • Discount tags let you label a discount for your own reporting, up to 20 tags. Stacking tags, which let two product discounts apply to the same item, require Shopify Plus.
  • Usage limits and scheduling work as they do everywhere else. See Usage limits and scheduling.

Amounts in the metafield are in your store currency. With Convert to customer’s currency on, they are converted at Shopify’s rate for the customer’s currency. With it off, an offer that reads prices from product data applies only to customers paying in your store currency — it will not treat your number as though it were the customer’s currency. Percentages are unaffected either way. See Markets and currency.

Limit the discount to signed-in customers, to customer tags, to B2B companies, or to a Shopify customer segment. See Customer eligibility.

Test mode makes a saved discount apply only to a signed-in customer whose customer record carries the tag dos-test. Everyone else shops as if the discount did not exist. It is the safe way to try pricing on a live store before anyone else sees it: turn it on, add the tag to your own customer account, check the cart, then turn it off.

Check a product answers “what would this discount do to this item?” without touching a cart. Pick a product and a variant, and the app reads that variant’s real metafield and runs the same arithmetic the discount uses.

For an offer that reads prices, you get a row per quantity — quantity 1 and every tier present — with the discount per item, the resulting price, and a plain reason when nothing applies.

A Check a product result for Stoneware Mug No. 001 priced 10.00 USD, listing quantity 1 with no discount and the note No field qty with N at or below 1, quantity 6 at 1.50 USD off for 8.50 USD, and quantity 12 at 2.50 USD off for 7.50 USD

For an offer whose group uses Price data conditions, you get the conditions instead, each with what the engine decides for that variant, and a verdict.

A Check a product result showing Group 1 matches all of these for this variant, the line pack is greater than or equal to 6 marked matches, and the conclusion This variant is in the offer's group

Use it whenever a discount is not behaving: it tells you whether the data or the discount is at fault, and names the field when the data is.

You rarely want to edit these by hand. In rough order of how many variants you have:

A few variants — the product page. Open the product, find Variant metafields, edit the value. Good for a correction, painful past a handful.

Tens to hundreds — Shopify’s bulk editor. The bulk editor can show the variant metafield as a column, so you can fill or paste a whole column in one pass. Reach it from a product’s variant list or from the products list, and use Columns to add the field. Shopify’s bulk editing guide has the current navigation.

Shopify's bulk editor listing fifty variants with a Volume pricing column, each row holding a JSON value such as v 1, pack 12, qty6 800

Make sure you are editing variants, not products. The product-level column of the same name is a different field, and nothing reads it.

Hundreds to thousands — a spreadsheet. Build the JSON in a spreadsheet so every row has an identical shape, then paste the column into the bulk editor. A formula like ="{""v"":1,""qty6"":"&B2&",""qty12"":"&C2&"}" keeps the punctuation right. If your export tool supports metafield columns in a CSV, the same column works there — check Shopify’s import and export guide for what your plan supports.

On a schedule, or from another system — the API. The metafieldsSet mutation writes metafields in batches and is the right tool for a nightly sync from an ERP. If your source already emits JSON, write it straight through — the app reads 79, 79.0 and 7.9e1 as the same amount, so you do not need to reformat your exporter’s numbers.

Without code — Shopify Flow. A workflow can write a metafield when something changes, which covers rules like “copy the supplier price into the wholesale field” without a developer.

Thirteen complete setups live on their own page, from a plain 20% off a collection to per-account contract pricing read from your data: Custom Discount examples.

A few of them:

ExampleNeeds a metafield?
A plain percentage off a collectionNo
A product discount and free shipping in one discountNo
Wholesale case pricing, a different price per SKU at 6 and at 12Yes
A protected floor so a deep tier never sells below costYes
Contract pricing for tagged customers onlyYes
Case packs only, chosen by a number in your dataYes
Clearing a season by a text fieldYes
  • Configuration size. A discount’s configuration is capped at 10 kB, shown live as you edit. Long lists of specific products are what fill it; collections and conditions are compact.
  • Very large carts. Shopify gives a discount a fixed amount of work per cart. On our test store a discount with a Price data condition applied to carts of up to 172 different products and stopped applying beyond that; without such a condition the same discount reached 180. When the limit is passed the discount applies nothing — it never applies partially or wrongly.
  • Quantities are not a constraint in the same way. A single line of 3,000 units prices correctly.
  • Price data is for product offers only. Price data conditions and the Price from product data value are available on product offers; order and shipping offers do not read the metafield, and the app refuses that combination when you save.

Work through these in order.

  1. Check a product on a variant you expect to discount. It names the reason.
  2. Confirm the value is under Variant metafields, not product metafields.
  3. Confirm "v": 1 is present.
  4. Confirm the units. 8.5 without "unit": "major" is eight and a half minor units, not 8.50.
  5. Confirm the field name in the offer matches the field name in the data, exactly.
  6. For tiers, confirm the line’s quantity reaches a tier, and that the field name does not end in a digit.
  7. If you named a floor or cap field, confirm every variant has a usable value in it.

Still stuck? See Discount not applying.