Shopify discount stacking with Functions packs
Which Shopify automatic discounts, codes, Buy X Get Y rules, and free-shipping thresholds can safely combine with a live Functions pack — by allocation mode and line vs order application — plus a 10-minute checkout test to run before BFCM.
Jeshua Leger
Founder, Leger Studio ·
Your kit is live. The pack product shows $79 on its page; at checkout Shopify Functions expand it into three component lines that add up to $79. Then marketing schedules 20% off sitewide for the weekend, the welcome code is still running, free shipping kicks in over $75, and a Buy 2 Get 1 automatic discount on tees is left over from last month. Every one of those is a second discount trying to land on an order where the pack has already spent its savings. Some will compound on top of the pack price. Some will be silently swapped for it. One will make the “free” gift stop being free.
This post does one job: decide, per discount, which of those outcomes you accept for an already-published pack, and make Shopify enforce it before traffic arrives. It is not about how the pack’s own savings land on component lines (Functions allocation math), whether BOGO should be a pack or a cart rule (BOGO vs cart discounts), or how to price a kit (fixed-price packs). The pack is already right. The question is what else is allowed to touch that order.
The pack has already spent its discount
A pack has a natural sum (components at their own prices) and a pack price. The gap is the pack’s discount, and it is already placed the moment the pack is published — on specific component lines, or as one order-level discount on Growth+. Any second discount that reaches the same order does one of three things, and you should be able to name which one you want before the sale goes live:
- Compound — the second discount is calculated on top of the pack price. The order total drops below the number on the pack’s product page.
- Block — the two cannot combine and the pack’s savings are the better deal, so Shopify keeps the pack and the sale never touches it. The shopper who came for “20% off everything” sees no extra discount on the kit and writes to support.
- Replace — the two cannot combine and the sale is the better deal, so Shopify drops the pack’s pricing and applies the sale to component lines at natural prices. The pack page is now advertising a number the checkout never charges.
Which one you get is not random. Shopify puts every discount into one of three classes — product, order, shipping — and two discounts combine only when both of their Combinations settings opt in to the other’s class. When they cannot combine, Shopify applies whichever discount is best for the shopper; that is not configurable. When they can, product discounts are calculated first on individual lines, order discounts second on the reduced subtotal, shipping discounts last. Two product discounts can share one line item only on Shopify Plus; combining product with order discounts, or order with order, requires Checkout Extensibility with no checkout.liquid customizations. Reference: Combining discounts.
What Shopify evaluates: the pack line, not the components
The detail merchants miss: your automatic discounts and codes are evaluated against the pack product — the single line the shopper added — not against the components it expands into. Shopify’s guidance to bundle developers is that discounts apply to the bundle parent, never directly to components, and whatever the parent earns is allocated across the component lines in proportion to their prices. Treat that as the default and let the test at the end confirm it on your store. Three consequences:
- Scope decides everything. A sale collection that contains the pack product discounts the pack. A sale aimed at the hoodie product does nothing to the hoodie inside the Hoodie + Stand pack — that hoodie is a component, not the line Shopify is scoring.
- “All products” means packs too. A sitewide percentage with no scope hits every published pack.
- Allocation of the second discount is Shopify’s, not yours. Your free-item or spread choice governs the pack’s own savings; the sale’s savings are spread by price weight across the lines as they stand after the pack. That is why a $0.00 gift line changes where a sale lands.
So give pack products a product type or tag you can build collections around, point sale discounts at a collection instead of “all products” whenever a pack is live, and read the Combinations section of every active automatic discount and code. Shopify caps a store at 25 active automatic discounts, app-based ones included; a BFCM calendar full of forgotten rules can run out of slots.
Free-item packs plus a percentage off
Hoodie $60 + Stand $20 = $80 natural. The pack sells for $60 with the stand marked free, product-level application. Order lines: Hoodie $60, Stand $0.00 — that line is the whole offer. Now a 20% sitewide sale goes live with the pack product in scope.
Compound case: Shopify grants 20% of the pack’s $60 — $12 — and allocates it across the component lines by price weight. The stand is already $0, so all $12 lands on the hoodie. Order: Hoodie $48, Stand $0.00, total $48 against an $80 natural sum. Merchandising pitched a $20 gift; the shopper got the gift and 40% off. The gift stayed honest, the contribution math did not.
Replace case, on a store where the pack pricing and the sale are treated as two product-class discounts competing for the same lines: the shopper gets whichever is worth more. At 20% the pack’s $20 wins and the sale is blocked on the kit — a support ticket, not a margin problem. At 30% the sale is worth $24 and wins: both lines revert to natural prices and the order reads Hoodie $42, Stand $14. The shopper paid $56 for a “$60 pack with a free stand” and was invoiced $14 for the stand. No macro explains that.
Spread and fixed-price packs plus automatic discounts
Serum $50 + Cream $30 + Mini $20 = $100 natural. The kit is a fixed $80 with spread allocation, so lines show $40 / $24 / $16 — fair share. This is the pack type that can legitimately join a sale, because a shopper reading “kit $80, 20% off this weekend, $64” is reading exactly what happened.
Compound case: 20% of $80 is $16, allocated by weight → $8 / $4.80 / $3.20 on top of the pack’s own allocation. Lines: $32 / $19.20 / $12.80, total $64. Every line is discounted twice and it still sums cleanly. The check is not the math; it is the depth. You planned this kit at 20% off natural and it is now 36% off natural. Recompute contribution at the stacked price before the kit goes into the sale collection.
Replace case: a 25% sale is worth $25 on the $100 natural sum, which beats the pack’s $20. If the two cannot share lines on your store, Shopify drops the pack pricing and the order reads $37.50 / $22.50 / $15, total $75 — three products on sale that happen to ship together, with the kit’s savings gone as pack savings. Amount-off packs (“$15 off the set”) behave the same way. If the story you actually want is “this kit is $64 this weekend,” change the pack price for the dated window instead of stacking — the rule from the fixed-price packs guide — so page, cart, and order agree on one number.
Order-level application changes the pack’s class (Growth+)
On Growth and Pro you can apply the pack savings as an order-level discount: component lines stay at natural prices and the savings appear once against the order. Accounting teams and 3PLs like it. For combinations it is a category change — the pack moves from the product class to the order class and every rule above re-sorts:
- Your sitewide percentage (product class) and the pack (order class) no longer fight for the same lines. If both opt in and your store meets the product-plus-order eligibility, they combine deterministically: the sale reduces the component lines first, then the pack’s savings apply to that reduced subtotal. A fixed pack saving stays fixed, so the stack is deeper than in product-level mode.
- Your own order-class promotions now compete with the pack. “10% off orders over $150” and “$15 off your first order” are order discounts. Where order-with-order combining is not available, Shopify keeps whichever is better for the shopper, so either your promo or the pack’s savings vanish from that order. Where it is available, percentage order discounts are each calculated on the same subtotal (10% + 15% is 25%, not 23.5%), percentages before fixed amounts.
- Thresholds read a different number. With lines at natural prices, the subtotal after product discounts is higher than with product-level application of the same pack, so a free-shipping or spend-threshold offer may trigger in one mode and not the other for the identical cart.
- Free-item allocation loses its visible $0.00. The gift line shows its natural price and the savings roll up to the order — by design, per the application modes docs. If the gift being free on the line is the offer, stay on product-level application.
Decision rules: allow, restrict, or rebuild the offer
Allow the combination when:
- The second discount is a shipping discount. Free-shipping thresholds should stay reachable with a pack in the cart. Confirm the pack discount and the shipping discount both opt in to each other’s class — when they cannot combine, Shopify keeps the product or order discount and drops the shipping one, which is the “adding the bundle removed my free shipping” ticket.
- The pack is spread or fixed-price, the sale is explicitly marketed as including kits, and contribution clears at the stacked depth.
- The second discount is order-class, the pack is product-level, and your store is eligible for product-plus-order combining. Pack lines first, order discount after.
Restrict it — exclude pack products from the discount’s scope, or pause the pack for the window — when:
- The pack is free-item or BOGO-style and the second discount is any percentage or amount off. One of those promotions was already worth a whole SKU.
- A Buy X Get Y automatic discount overlaps the pack product or its components. Shopify makes products inside an active Buy X Get Y ineligible for further product discounts, and a code that hits them removes the Buy X Get Y and applies the code instead. One mechanism per SKU — the BOGO post is about choosing which.
- The pack is order-level and you also run order-class promotions, unless you have confirmed order-with-order combining is available on your store and priced the additive result.
- You cannot say, for a given cart, whether you expect compound, block, or replace.
Rebuild the offer instead of stacking when the stacked result is the offer you wanted:
- “Kit at $80, and 20% off this weekend” → edit the pack price, or publish a dated sale pack, for the window.
- “Buy 2 get 1 free, plus the sitewide sale” → make the BOGO a pack, exclude it from the sale, and let the sale run on solo SKUs.
- “The gift must read $0.00 even during the sale” → keep product-level free-item allocation and keep the pack out of the sale scope. Do not switch to order-level to “make it combine”; you would lose the $0.00 line and gain a deeper stack.
A 10-minute combination test before you publish
Run this on the real storefront checkout and stop at the payment step — the totals there are what shoppers will be charged, and you do not need to place the order. If the sale is not live yet, set its start time to now and its end time 30 minutes out during a quiet hour, or use a staging store if you have one.
- Pack alone. Component lines and total match the pack page. If not, stop and fix allocation first.
- Pack + the sale (or code). Lower total → compound. Same total and the sale shows nowhere on the pack → block. Lines back at natural prices with the sale applied → replace. Whichever you did not expect is the ticket you would have received on Friday.
- Free-item packs: look at the gift line. Anything other than $0.00 means the gift is being invoiced.
- Pack + one solo unit of a component. The sale should hit the solo unit; the pack should behave exactly as in step 2. If the solo unit changes the pack’s outcome, scope is leaking between component and pack product.
- Two of the pack. The second discount should scale with pack quantity, not cap at one.
- Threshold offers. Build a cart that clears the free-shipping threshold at natural prices but not at pack price, then one just above it at pack price. Now you know which subtotal the threshold reads on your store — and if you use order-level application, repeat, because the answer can differ.
- Buy X Get Y. Add a pack whose component is also a Buy X Get Y product, then the qualifying solo units. Confirm neither discount is silently removed and the pack’s units are not counted toward the other offer.
- Count active automatic discounts, app-created ones included. Under 25 with room for the sale calendar, or something will not fire.
Write the totals into the promo brief and hand them to support: “pack page shows $80, sale checkout shows $64, that is correct,” or “kits are excluded from the sale; the pack price is the deal.” Either sentence ends the ticket in one reply. For what happens to stacked lines when a shopper returns one component, see bundle returns and partial refunds.
Set the pack’s class before the sale calendar does
Everything above comes down to two settings on the pack: how its savings are allocated (free-item or spread) and where they are applied (component lines on every plan, or the order on Growth and Pro). Those two decide which Shopify discounts the pack can meet and what happens when it does. Better Bundles exposes both in the editor with a live preview of how the savings land on each component, and applies them through Shopify Functions with no codes and no theme scripts. Basic is free forever with up to 3 active bundles; Starter, Growth, and Pro are $17 / $37 / $77 a month with a 15-day trial. Install from apps.shopify.com/better-bundles, publish one pack with create your first bundle, then run the ten-minute test against your current sale calendar before you add a second offer.