Hi everyone, I am using the latest Discount Function API for my app. I went through the documentation of combining discounts here. It says that multiple discounts can combine based on the combinesWith settings. Here all the discounts they have mentioned and provided examples, have only one discount class. But for my case, each and every discount may offer at most all the three (product, order, shipping) discount classes. So in that case, how the combination is handled between the eligible discounts in cart/checkout? I am required to replicate the combination behavior of Shopify’s discount engine.
I wanted to see the behavior myself between multiple discounts so created three discounts with the following configurations where:
Discount A provides:
- 50% product discount on product A
- 10% shipping discount
Discount B provides:
Discount C provides:
- 20tk product discount on product A and B
- 50% shipping discount
Each of them individually applies to cart/checkout as expected when other two discounts are deactivated. Then activated all the three, added the same two products to cart, collected the result of applied discounts in checkout based on different possible combinesWith settings and noted here. Where every discount has three columns for combinesWith properties (product, order, shipping) that can be true (checked) or false (unchecked) and Applied Discounts column shows which discount has been applied where (product, order, shipping). Product column inside Applied Discounts has two characters indicating which discount has been applied to which cart line. But unfortunately can’t find any specific pattern of the combination behavior from this analytics.
So my question is, is it possible to replicate the exact behavior of combination based on different combinesWith settings that Shopify applies in cart/checkout or am I missing something? Thanks in advance.
Hi @Rafi_Sakib
I used the Dev MCP to see what recommendations it would provide for this, and this is what it generated. Make sure to test everything before adding to prod:
Replicating discount combination in Shopify’s Discount Function API
Summary
- You can’t fully replicate the exact combination behavior outside Shopify. The checkout engine applies internal optimization (eligibility, per-class strategies, rounding, shipping-option effects) that isn’t fully documented and may evolve. You can, however, match the public rules and get equivalent outcomes by letting Shopify compute combinations at checkout.
What combinesWith actually controls
combinesWith is a compatibility switch between discount classes. A discount can opt-in to combine with:
- productDiscounts
- orderDiscounts
- shippingDiscounts
- Two discounts combine for a given class only if both sides “mutually opt in” for that class pairing:
- Product ↔ Product: both have productDiscounts: true
- Order ↔ Order: both have orderDiscounts: true
- Shipping ↔ Shipping: both have shippingDiscounts: true
- Cross-class at the same time (e.g., a product discount coexisting with an order discount): each must allow the other’s class (productDiscounts on the order discount, and orderDiscounts on the product discount).
How Shopify applies discounts (high level)
- Classes are independent surfaces:
- Product discounts act on individual cart lines.
- Order discounts act on the order subtotal after applicable product discounts.
- Shipping discounts act on delivery options.
- Checkout considers all eligible discounts, applies only combinations allowed by combinesWith, and chooses the outcome that maximizes buyer savings while respecting constraints (can’t discount below zero, usage limits, minimums, etc.).
- Within a single Function’s output, you control “selection strategy” via:
- ProductDiscountSelectionStrategy (e.g., FIRST, MAXIMUM, ALL for product candidates)
- OrderDiscountSelectionStrategy
- DeliveryDiscountSelectionStrategy
These strategies decide how candidates from your function compete with one another. They don’t override global engine decisions across multiple distinct discounts.
Important implications
- There isn’t a simple “pattern” that derives only from combinesWith. Outcomes depend on:
- Which lines are eligible and how strong each candidate is
- Whether multiple discounts are mutually combinable for the same class
- Selection strategies inside each discount result
- Rounding and currency
- Buyer’s chosen shipping option (affects which shipping candidates are relevant)
- Product + Order can both apply if both allow combining; order discounts are computed after product lines are discounted.
- Shipping discounts are computed against shipping options and can coexist with product/order savings if mutually allowed.
About stacking on the same product line
- Where multiple product discounts could affect the same line and all parties allow productDiscounts, Shopify chooses the best permitted outcome. Exact tie-breakers and stacking nuances are engine-defined and may change. Don’t assume a fixed “fixed-before-percent” or always-stack rule; rely on the engine’s optimization.
Practical guidance to “replicate”
- Best path: create discounts with intended combinesWith and let Shopify compute the combination in cart/checkout. This produces the same result you see in admin and storefront.
- If you must simulate combinations outside Shopify, approximate:
- Partition candidates by class (product/order/shipping).
- Build a compatibility matrix (mutual combinesWith + eligibility). For product lines, treat overlapping applications on the same line as competing unless you explicitly model stacking and its limits.
- For each class, pick the combination that yields the lowest payable amount subject to constraints (global maximum-savings subject to compatibility). Then apply order to the post-product subtotal, and shipping to delivery options.
- Use the store’s money rounding rules.
- Expect discrepancies: Shopify’s internal solver and rounding can produce results your external approximation won’t exactly match.
Why your A/B/C experiment feels “patternless”
- Your results vary because the engine optimizes across:
- Line-level (A and C product amounts)
- Order-level (B)
- Shipping-level (A and C shipping), which also depends on the selected delivery option
Even with the same items, small changes in eligibility, minimums, or shipping rate selection can flip the optimal combination. combinesWith gates what’s allowed; it doesn’t dictate which specific set is chosen.
References
Key takeaway
- You can control whether discounts are allowed to combine via combinesWith and you can control selection within your own Function outputs. The exact cross-discount combination chosen in checkout is computed by Shopify’s engine to maximize savings; it isn’t intended to be re-implemented externally one-to-one.
Info on how to use the Dev MCP yourself for more personalised responses: Meet Shopify Dev Assistant: Build Shopify Apps Faster Than Ever (2025) - Shopify Ireland