npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@webbycrown/medusa-quantity-pricing-rules

v1.0.1

Published

Medusa v2 plugin: quantity range pricing engine with admin UI, store API, and PostgreSQL-backed tiers

Downloads

147

Readme

Medusa Quantity Pricing Rules

npm version

Medusa v2 plugin for quantity range pricing (volume / tiered / wholesale). Set unit prices by quantity with fixed prices, percentage discounts, or fixed amount-off tiers.

Watch the Medusa Quantity Pricing Rules demo on YouTube

Watch demo: https://www.youtube.com/watch?v=CjBjc7j3H6g

Features

  • PostgreSQL quantity_prices table (module + migrations)
  • Pricing types: fixed, percentage, fixed_discount
  • Admin API (CRUD + Zod validation) and Store API (list tiers, calculate price)
  • Admin UI: Settings → Extensions → Quantity Pricing, per-product widget, store summary widget
  • Workflows with compensation (create / update / delete)
  • Auto cleanup on product.deleted
  • Optional variant, region, and customer-group scoping
  • Exported helpers: resolveQuantityUnitPrice, catalog utilities, workflows

Requirements

| Requirement | Version | |-------------|---------| | Node.js | 20+ | | Medusa | v2.13+ (@medusajs/framework ^2.13) |

Quantity pricing admin — product tiers with fixed price or discounts

Installation

npm install @webbycrown/medusa-quantity-pricing-rules

Register in medusa-config.ts:

import { defineConfig } from "@medusajs/framework/utils"

export default defineConfig({
  plugins: [
    {
      resolve: "@webbycrown/medusa-quantity-pricing-rules",
      options: {},
    },
  ],
})

Registers the pricingEngine module automatically. Do not add a duplicate entry under modules.

Run migrations and start Medusa:

npx medusa db:migrate
npm run dev

Open Admin → Settings → Extensions → Quantity Pricing to create tiers.

Admin UI

| Location | Purpose | |----------|---------| | Settings → Extensions → Quantity Pricing | List, create, edit, delete rules | | Products → [product] | Tiers for that product (product ID pre-filled) | | Settings → Store | Rule count + link to settings |

Tier fields

| Field | Description | |-------|-------------| | Product ID | Required (auto-filled on product page) | | Variant | Optional — empty = all variants | | Min qty | Minimum quantity (e.g. 1) | | Max qty | Upper bound; empty = open-ended (e.g. 51+) | | Pricing type | fixed, percentage, or fixed_discount | | Discount / unit price | See Pricing types | | Currency | ISO 4217, e.g. inr, usd | | List price (reference) | For discount tiers when catalog price is missing |

Non-overlapping ranges per product + currency are recommended. When ranges overlap, the tier with the highest min_qty that still fits the quantity wins.

Pricing types

| Type | price means | Resolved unit price | |------|----------------|---------------------| | fixed | Tier unit price | price | | percentage | Percent off catalog (e.g. 10 = 10% off) | base × (1 − price/100) | | fixed_discount | Amount off per unit | max(base − price, 0) |

base is resolved in order:

  1. base_unit_price on Store API / resolveQuantityUnitPrice
  2. Medusa variant catalog price for currency_code
  3. reference_unit_price on the rule

Store API

GET /store/quantity-pricing — requires a publishable API key.

| Param | Required | Description | |-------|----------|-------------| | product_id | Yes | Product to load tiers for | | variant_id | No | Variant-specific rules + product-level fallback | | currency_code | No | Filter / calculation currency | | customer_group_id | No | B2B segment | | region_id | No | Regional override | | quantity | No | When set, includes calculated_price | | base_unit_price | No | Catalog price for discount tiers |

GET /store/quantity-pricing?product_id=prod_01XXXX&currency_code=inr&quantity=25
{
  "quantity_prices": [],
  "calculated_price": {
    "unit_price": 90,
    "quantity": 25,
    "currency_code": "inr",
    "rule_id": "qprice_02...",
    "pricing_type": "fixed",
    "line_total": 2250
  }
}

Without variant_id, only product-level tiers (variant_id = null) are returned.

Admin API

Authenticated admin session or token.

| Method | Path | Description | |--------|------|-------------| | GET | /admin/quantity-pricing | List rules | | POST | /admin/quantity-pricing | Create rule | | GET | /admin/quantity-pricing/:id | Get rule | | PUT | /admin/quantity-pricing/:id | Update rule | | DELETE | /admin/quantity-pricing/:id | Delete rule | | GET | /admin/quantity-pricing/catalog-prices | Variant catalog prices |

Create fixed tier:

{
  "product_id": "prod_01XXXXXXXX",
  "min_qty": 1,
  "max_qty": 10,
  "pricing_type": "fixed",
  "price": 100,
  "currency_code": "inr"
}

Open-ended tier (51+): set "max_qty": null.

Package exports

import {
  PRICING_ENGINE_MODULE,
  resolveQuantityUnitPrice,
  fetchCatalogPricesForProducts,
} from "@webbycrown/medusa-quantity-pricing-rules"

const unitPrice = await resolveQuantityUnitPrice(container, {
  product_id: "prod_01...",
  variant_id: "variant_01...",
  quantity: 25,
  currency_code: "inr",
  rule_id: "qprice_02...", // optional — pin selected tier
})

const pricingEngine = container.resolve(PRICING_ENGINE_MODULE)
const result = await pricingEngine.calculatePrice({
  product_id: "prod_01...",
  quantity: 25,
  currency_code: "inr",
})
import {
  createQuantityPricingRuleWorkflow,
  updateQuantityPricingRuleWorkflow,
  deleteQuantityPricingRuleWorkflow,
} from "@webbycrown/medusa-quantity-pricing-rules/workflows"

| Export | Purpose | |--------|---------| | PRICING_ENGINE_MODULE | Container key ("pricingEngine") | | pricingEngineModule | Module definition | | PricingEngineService | Service class | | resolveQuantityUnitPrice | Resolve tier unit price (cart / checkout) | | fetchCatalogPricesForProducts | Batch catalog prices | | getBaseUnitPriceForRule | Base price for a rule | | Types | QuantityPriceRuleDTO, CalculatePriceInput, PricingType, … | | ./workflows | Create / update / delete workflows |

The plugin does not modify cart line items automatically. Use resolveQuantityUnitPrice in your add-to-cart flow when a tier applies.

Tier matching

  1. Load rules for product_id (+ optional filters).
  2. Prefer variant-specific rules when variant_id is set.
  3. Match min_qty ≤ quantity and (max_qty is null or quantity ≤ max_qty).
  4. Highest matching min_qty wins.
  5. Resolve unit price from pricing_type and base price sources.

Database

Table quantity_prices: product_id, variant_id, min_qty, max_qty, pricing_type, price, currency_code, reference_unit_price, customer_group_id, region_id.

After model changes in a fork:

npx medusa db:generate pricingEngine
npx medusa db:migrate

Troubleshooting

| Issue | Check | |-------|--------| | Admin page not found | Rebuild plugin (npm run build in package) and restart Medusa | | Empty quantity_prices on store | Rules exist for product_id + currency_code; pass variant_id if needed | | Wrong discount price | Variant catalog price or reference_unit_price; pass base_unit_price | | Wrong tier | Overlapping ranges — highest min_qty wins | | Module not found | Plugin under plugins in medusa-config.ts, not duplicated under modules |

Changelog

See CHANGELOG.md.

Author

WebbyCrown[email protected] · webbycrown.com