Blog

How We Built a Mix ‘n Match Packet Discount with Shopify Functions

Shopify Functions Mix 'n Match discount case study featuring a real cart with 20 seed packets and $14.80 saved.

Seeds ‘n Such has a simple offer: buy enough seed packets and every packet gets cheaper. Any mix counts. Ten different tomatoes, or two each of five vegetables, or one packet each of twenty herbs. That’s the “Mix ‘n Match” part.

There’s one rule that makes it harder than it sounds: bulk seed never counts. A pound of beans shouldn’t push a customer into a packet tier, and it shouldn’t get the packet discount either.

For years that rule was easy to enforce because packets and bulk were separate products. When we rebuilt the Seeds ‘n Such storefront with Fencepost, we merged them into variants of one product. Shopping got simpler. The discount got harder. This post is about how we solved it with a custom Shopify Function.

Why merging packet and bulk changed the problem

Before the redesign, packet and bulk sizes of the same variety were separate products. Three automatic packet discounts supported the Mix ‘n Match offer.

That structure was awkward for customers and for the team. A shopper looking at one variety saw it split across several products, and every variety meant more products to administer.

Putting “Packet” and the bulk sizes on the same product as variants fixed that. But it moved the eligibility question from the product to the variant. “This product qualifies” was no longer true. Only some of its variants did.

Combined Listings or one product with variants

We looked at two ways to present packet and bulk together.

Shopify Combined Listings group separate products under one parent listing. That would have kept packet and bulk as separate products, so product-level discounts would have kept working. In return, we’d have had availability and presentation trade-offs on the storefront, and the team would still be administering multiple products per variety.

One product with packet and bulk variants was simpler for shoppers and staff, and it’s the standard Shopify model. The cost was the discount: it now had to work at the variant level.

We chose variants and took on the discount problem directly.

Why not a native discount or a volume-discount app

Shopify’s built-in automatic discounts can target specific variants. On a catalog this size, that would mean hand-picking hundreds of packet variants, keeping three tier discounts in sync, and remembering to add every new variety. That’s the kind of manual list that quietly falls out of date.

The volume-discount apps we reviewed didn’t fit this merchant’s rule. They either couldn’t target the right variants cleanly, or eligibility depended on lists someone would have to maintain as varieties were added. That doesn’t mean no app can do cross-product tiers. Plenty can. None we looked at matched this combination: variant-level eligibility from catalog data, counted across the whole cart, with explicit exclusions.

Why a custom app with Shopify Functions

Shopify Functions let a custom app replace part of Shopify’s discount logic. The code runs on Shopify’s infrastructure during cart and checkout, not in the theme, so customers can’t bypass it by editing the page.

Seeds ‘n Such is on Shopify Plus, which matters here. Custom apps that contain Functions can only be used on Plus stores. Public apps from the Shopify App Store can use Functions on any plan. We cover the trade-offs in more detail in Shopify Functions vs. public apps.

We built the app in our Shopify Partner account and installed it directly on the store. The merchant manages it from a small settings screen in the Shopify admin.

Eligibility and discount configuration

The core of the Function is one question per cart line: does this variant count?

A variant counts only when both of these are true:

  • mix_match_eligible is true
  • package_type is packet

PakPlus and ProPak variants are excluded even though they’re sold alongside packets. Bulk sizes never match package_type = packet, so they’re out by definition.

Eligibility is explicit catalog data on each variant, not something the Function guesses from a product title or tag. New varieties qualify as soon as their variants are set up correctly. Nobody edits a list.

Custom Shopify discount app showing packet eligibility requirements and configurable quantity tiers.
Custom app settings: only explicitly eligible packet variants count; bulk, PakPlus and ProPak are excluded.

Getting that data right was its own job. Before writing the discount, we audited the catalog across all 1,363 products. The store’s SKU convention helped: packet variants follow a consistent pattern, ending in 00, which made it quick to spot variants that were missing data or labeled inconsistently. The SKU pattern was an audit tool. The Function’s decision rests on the eligibility fields shown above.

Counting across the cart, discounting only packets

The Function adds up the quantity of every eligible line in the cart, across all varieties. That total picks the tier. The discount is then applied only to the eligible lines. A bulk line in the same cart doesn’t add to the count and doesn’t get a discount.

Eligible packets in cart Discount per eligible packet
Fewer than 10 No Mix ‘n Match tier
10–15 $0.24 off each
16–19 $0.49 off each
20 or more $0.74 off each

The tiers are stored as settings in the app, not hard-coded, so the merchant can change thresholds and amounts without a deploy.

In outline, the logic looks like this:

eligible = cart lines where
  variant.mix_match_eligible == true
  and variant.package_type == "packet"
  and variant is not PakPlus or ProPak

count = sum of quantity across eligible

tier = highest configured tier where count >= tier.min
if no tier: return no operations

return operations:
  productDiscountsAdd
    candidates: one per eligible line
      target: that cart line
      value: fixedAmount(tier.amount, appliesToEachItem = true)

Illustrative pseudocode — not the production Shopify Function or a copy-paste-ready API payload. It follows the shape of the current Discount Function API, where the cart.lines.discounts.generate.run target returns operations containing productDiscountsAdd. Older examples that return discounts with a discountApplicationStrategy use the previous API and shouldn’t be copied into new work.

How it works in the cart

Here’s a live cart with 20 eligible packets. The cart counts toward the next tier as the customer adds packets, and once they reach 20, every eligible packet gets $0.74 off. That’s 20 × $0.74 = $14.80 in savings.

Seeds 'n Such cart with 20 eligible seed packets and $14.80 in Mix 'n Match savings.
Live cart: 20 eligible packets unlock the $0.74-per-packet tier, saving $14.80.

This cart only contains packets, so it shows the tier logic, not the bulk exclusion. Bulk exclusion is something you prove with test carts, not with one screenshot.

What to test before switching it on

A discount like this is mostly edge cases. The test plan we’d use for any Function of this shape covers:

  • Tier boundaries: 9, 10, 15, 16, 19 and 20 eligible packets. Off-by-one errors live here.
  • Mixed carts: packets and bulk of the same variety together. The bulk line must not change the count or receive a discount.
  • Excluded packages: PakPlus and ProPak lines, alone and mixed with packets.
  • Missing data: a packet variant without the eligibility fields shouldn’t qualify by accident.
  • Overlap: the old automatic packet discounts need to be switched off at cutover, or customers get both.

Usage after launch

In Shopify Analytics for October 11 – November 10, 2026, the discount named Seeds Mix ‘n Match appears on 328 discounted orders, with approximately $25.2K in total sales on those orders.

Shopify Analytics showing 328 discounted orders for Seeds Mix 'n Match, October 11 through November 10, 2026.
Shopify Analytics showing approximately $25.2K in total sales associated with Seeds Mix 'n Match, October 11 through November 10, 2026.

What the numbers do show is that the offer is in regular use after the variant merge, and that the discount applies on real orders, not only in test carts.

When a custom Function is worth building

Most stores don’t need one. If a native discount or a well-maintained app handles your rule, use it.

A custom Function starts to make sense when:

  • Eligibility depends on catalog data, such as variant fields, rather than on a product list.
  • The rule has to count across many products but discount only some lines.
  • The store is on Shopify Plus, or the logic is general enough to justify a public app.
  • The catalog changes often enough that hand-maintained lists will drift.

Seeds ‘n Such met all four. For the broader redesign, including crop, goal and zone navigation, see the Seeds ‘n Such case study. The project was delivered in partnership with Fencepost.

Have a discount rule your current setup can’t express? Tell us about it. We’ll say whether it needs a custom build or a setting you haven’t tried yet. More on our Shopify custom apps and Functions work and e-commerce development.