Overview
Icon for Cart Rules

Cart Rules

Cart and per-product quantity limits

@xross-dev/medusa-cart-rules

Medusa v2 plugin. Blocks add-to-cart, quantity increase and checkout when a quantity rule is violated.

Rules

Copy to clipboardrule_type Meaning Copy to clipboardmax_cart_quantity Total items ≤ limit. Scope Copy to clipboardglobal = whole cart; other scopes = combined items matching the scope (e.g. "max 4 items from collection X"). Copy to clipboardmax_product_quantity Per product ≤ limit (per variant when scope = Copy to clipboardvariant). Scope Copy to clipboardglobal = every product; Copy to clipboardproduct/Copy to clipboardcollection/Copy to clipboardcategory = matching products.

Audience: everyone (+ optional guests), or one customer group. Also: sales channel, active window (Copy to clipboardstarts_at/Copy to clipboardends_at), priority, custom message (Copy to clipboard{limit} {current} {product}).

Resolution

  • Per-product: variant > product > collection/category > global (first tier with a matching rule wins).
  • Same scope target: a customer-group rule overrides the "everyone" rule (VIP 50 beats global 20).
  • Several matching group rules: Copy to clipboardrestrictive (lowest, default) or Copy to clipboardpermissive (highest).
  • Cart-level and product-level rules are checked independently; all must pass.
  • Decreasing a quantity is always allowed. Add/update only block for products they touch; Copy to clipboardcomplete checks the whole cart.

Install

# in the plugin
yarn && yarn build # migration for cart_rule + cart_rule_violation is already included
# (after changing models: yarn db:generate, needs a local Postgres + DB_USERNAME/DB_PASSWORD)
npx medusa plugin:publish # or: yalc / npm publish
// medusa-config.ts
plugins: [
{ resolve: "@xross-dev/medusa-cart-rules", options: { conflict_strategy: "restrictive" } },
],
npx medusa db:migrate # in the app

Dev loop: Copy to clipboardnpx medusa plugin:develop in the plugin, Copy to clipboardnpx medusa develop in the app.

API

Admin (auth required): Copy to clipboardGET|POST /admin/cart-rules, Copy to clipboardGET|POST|DELETE /admin/cart-rules/:id, Copy to clipboardGET /admin/cart-rules/violations, Copy to clipboardPOST /admin/cart-rules/simulate Copy to clipboard{ cart_id? , customer_id?, items?: [{variant_id, quantity}] }.

Store (publishable key): Copy to clipboardGET /store/cart-rules?cart_id=…[&variant_id=…] or Copy to clipboard?variant_id=…

{ "limits": [{ "rule_id": "crule_…", "rule_type": "max_cart_quantity", "scope_type": "global", "limit": 20, "current": 12, "remaining": 8 }],
"lines": { "item_…": { "remaining": 2, "max_quantity": 5 } },
"variant": { "variant_id": "variant_…", "remaining": 3 } }

Use Copy to clipboardlines[id].max_quantity to cap the quantity stepper.

Blocked requests return HTTP 400 Copy to clipboard{ "type": "invalid_data", "code": "CART_RULE_VIOLATION", "message": "You can add at most 5 of Phone." }.

Custom rule types

import { registerRuleType } from "@xross-dev/medusa-cart-rules/modules/cart-rules/engine"
registerRuleType("max_cart_total", { label: "...", defaultMessage: "...", measure: (cart, rules, ctx) => [/* Measurement[] */] })

Call it from a loader or any file loaded at boot. The engine (Copy to clipboardengine/) is pure (no I/O) and unit-tested: Copy to clipboardyarn test.

Notes

  • Workflow hooks used: Copy to clipboardaddToCartWorkflow, Copy to clipboardupdateLineItemInCartWorkflow, Copy to clipboardcompleteCartWorkflow → Copy to clipboardhooks.validate.
  • Scope/group/channel IDs are plain strings (no module links), validated on create/update.
  • Admin page: Cart Rules in the sidebar (rules CRUD + blocked-attempts log), built with Copy to clipboard@medusajs/ui.

You may also like

Browse all integrations

Build your own

Develop your own custom integration

Build your own integration with our API to speed up your processes. Make your integration available via npm for it to be shared in our Library with the broader Medusa community.

gift card interface

Ready to build your custom commerce setup?