Every online store eventually reaches a point where a flat ten percent off banner is not enough. A B2B buyer wants a volume discount, a marketing team wants a flash sale that stacks with a loyalty tier, and a regional manager wants a coupon that only works in one currency zone. On a rigid platform, each of these requests turns into a support ticket for the development team. On Medusa.js, promotions are treated as a first class module with its own rule engine, which means most of these scenarios can be configured rather than coded from scratch.
This guide walks through how the Medusa.js promotions module is structured, the pricing rule types it supports, and the patterns that keep a growing discount catalog from turning into an unmanageable pile of exceptions.
Why Promotion Logic Breaks in Rigid Platforms
Traditional monolithic commerce platforms tend to treat discounts as an afterthought bolted onto the checkout step. The moment a business needs a rule like "15 percent off for returning customers, but not on items already on clearance, and only if the cart total exceeds a threshold", the platform's native discount engine usually cannot express it, and the team ends up hardcoding conditions into the checkout controller. That approach works for a while, then becomes fragile every time marketing asks for a new campaign.
Medusa.js separates the promotion engine from the checkout flow entirely. Promotions are modeled as data, evaluated by a dedicated module, and applied to a cart or order through explicit rule matching. That separation is what allows a store to run dozens of overlapping campaigns without the checkout code knowing anything about the specifics of each one.
Talk To Our Medusa Team
Lets TalkAt its core, a promotion in Medusa.js is made up of an application method, a set of rules, and optional campaign metadata. The application method decides whether the discount is a percentage, a fixed amount, or applies to shipping. The rules decide who qualifies and under what cart conditions. Campaigns group multiple promotions together so a business can track a single initiative, such as a festival sale, across several discount codes at once.This structure means a developer working with Medusa.js's ecommerce framework does not need to write custom logic for each new campaign type. Instead, the work is in configuring the right combination of rules, which is far faster to test and far easier to hand off to a non-technical operations team once it is wired into an admin screen.
Building Percentage, Fixed, and Buy X Get Y Rules
Percentage and fixed amount promotions are the simplest to configure. A percentage rule takes a value between 1 and 100 and applies it either to the entire cart or to specific line items that match a target rule, such as a product collection or a tag. Fixed amount rules work the same way but subtract a set currency value instead of a proportion, which matters for stores running in multiple regions since a flat 500 rupee discount and a flat 5 dollar discount need to be configured as separate currency-aware amounts rather than a single converted number.
Buy X Get Y promotions are more involved because they require two rule sets: one that defines the qualifying purchase and one that defines the reward. A common pattern is buy two of a specific collection and get the third at no cost, which Medusa.js handles by matching the qualifying items first, then applying the reward to the cheapest eligible item in the cart. Getting this order of evaluation right avoids the classic bug where a promotion accidentally discounts the most expensive item instead of the intended one.
Stacking Rules and Campaign Conditions
Once a store has more than a handful of active promotions, the real complexity shows up in how they interact. Medusa.js allows promotions to be marked as stackable or exclusive, and campaign-level budgets can cap how many times a promotion is used in total or per customer. This is the layer that prevents a well-meaning flash sale from accidentally combining with a loyalty discount to produce a negative margin order.
Get A Promotions Audit
Lets TalkCoupon Codes and Automatic Promotions
Medusa.js supports both code based promotions, where the shopper has to enter a value at checkout, and automatic promotions, which apply themselves the moment the cart meets the qualifying conditions. Automatic promotions are useful for cart abandonment recovery, since a discount that appears without the customer needing to hunt for a code tends to convert better on the second or third visit. Code based promotions remain the right choice for influencer campaigns and email marketing, where tracking a specific code back to a channel is part of the point.
A detail that is easy to miss during setup is code collision. Two campaigns created by two different marketing team members can accidentally reuse the same code string, and Medusa.js will reject the second one silently unless the admin interface surfaces the conflict clearly. Building a simple naming convention for codes, such as a campaign prefix followed by a sequence number, avoids this entirely.
Displaying Promotions Clearly on the Storefront
A promotion that calculates correctly on the backend still needs to be communicated clearly at the front end, or customers end up confused about why a price changed between the product page and the cart. Storefronts built on Medusa.js typically surface the applied promotion name directly on the cart line item, show a strikethrough of the original price next to the discounted one, and display a short message when a customer is close to unlocking a threshold based offer, such as free shipping at a certain cart value. This last pattern, sometimes called a progress nudge, tends to lift average order value because it gives the shopper a concrete reason to add one more item before checking out.
It is worth deciding early whether the storefront should show every stacked discount individually or roll them into a single combined line. Showing each one separately builds trust for shoppers who want to see exactly what they are getting, while a combined line keeps the cart summary shorter for stores running many small automatic promotions at once. Neither approach is wrong, but switching between them after launch tends to confuse returning customers, so it is a decision worth locking in during the initial storefront build rather than revisiting every quarter.
Handling Refunds and Returns With Promotions Applied
Discounts complicate returns more than most teams expect during initial planning. If a customer bought two items under a Buy X Get Y promotion and returns one of them, the refund logic needs to know whether the returned item was the qualifying purchase or the free reward, since refunding the wrong amount either overpays the customer or breaks the original discount math. Medusa.js keeps the applied promotion data attached to each line item at the order level, which means the return workflow can look up exactly how much discount applied to the specific unit being returned instead of recalculating from scratch.
The same logic extends to partial refunds and store credit. A customer service team issuing a partial refund on a discounted order needs visibility into the original promotion terms, not just the final paid price, so building an admin view that shows both the list price and the applied discount side by side saves a considerable amount of back and forth during dispute resolution.
Testing Promotion Rules Before Launch
Because promotions in Medusa.js are evaluated server side against live cart data, the safest way to validate a new rule set is to build a handful of representative test carts, covering the qualifying case, the edge case just below the threshold, and a cart that combines the new promotion with an existing one. Teams that work with an experienced Medusa.js development partner typically wire this into their staging environment so every campaign goes through the same three checks before it reaches production, which catches the majority of pricing mistakes before a customer ever sees them.
A useful habit for larger catalogs is to keep a spreadsheet or admin note of every active promotion's rule set alongside its expiry date, since campaigns that quietly outlive their intended window are one of the most common sources of margin leakage in stores running frequent sales. Reviewing that list monthly, alongside actual redemption counts, tends to surface both dead campaigns worth retiring and popular ones worth extending.
Getting the promotions module right early pays off well beyond the first campaign. A rule structure that accounts for stacking, currency scope, and usage limits from the start means marketing can launch new offers on their own schedule instead of waiting on a developer for every discount tweak, and the storefront keeps calculating totals correctly no matter how many campaigns are running at once.
