Pricing & promotions
Quantity Discounts & Tiered Volume Pricing
Generated from docs/plan/core/quantity-discounts-tiered-volume-pricing/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Core free module that lets merchants create tiered pricing: ‘buy 1 = €10, buy 3+ = €9 each, buy 10+ = €7 each’. Storefront calculates discounted price dynamically as customer changes quantity in cart. Supports quantity tiers per product, supports currency-specific tiers, and integrates with order calculation for accurate final pricing.
The problem
A wholesale distributor cannot show ‘buy 3 and save 10%’ without manually managing 20+ SKUs for each quantity tier. Customers want to see ‘how much will 5 units cost?’ without going to checkout. Currently, merchants lose sales because pricing transparency is poor.
What it does
- Create tier pricing rules per product: { quantity_min, quantity_max, price_per_unit_minor_units }
- Display tiers on product page: ‘1 = €10.00, 3+ = €9.00, 10+ = €7.00’ as a table
- Cart calculation: if customer selects qty=5, system applies tier where qty_min <= 5 <= qty_max, calculates price = qty × tier_price
- Dynamic price update in cart: when customer changes quantity, price recalculates instantly (no page reload)
- Multi-currency support: define tiers per currency if using multi-currency-pricing-engine
- Storefront SDK: expose tier data so headless frontend can calculate price independently
- Admin dashboard: show tier popularity (which tier is most-ordered at scale) and margin impact
- Validation: prevent overlapping tiers (e.g., qty 3-5 and qty 4-7 are ambiguous; system rejects)
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Customer-specific pricing (see b2b-wholesale-pricing-custom-price-lists for wholesale account pricing)
- Time-based tier changes (dynamic pricing by season owns this)
- Automatic tier calculation based on profit margin targets (too prescriptive)
Data model
Add product_tier_pricing table: { id, product_id, quantity_min, quantity_max, price_per_unit_minor_units, currency }. Allow multiple tiers per product, sorted by quantity_min. Schema migration: new table.
API
- POST /admin/products/:id/tier-pricing — create tier rule
- GET /admin/products/:id/tier-pricing — list all tiers for product
- PUT /admin/products/:id/tier-pricing/:tierId — edit tier
- DELETE /admin/products/:id/tier-pricing/:tierId — delete tier
- POST /cart/calculate-tier-price — { product_id, quantity } → returns { applicable_tier, price_per_unit, total_price }
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
In product editor, add ‘Quantity Discounts’ section. Table: quantity range (min–max), price per unit. ‘Add tier’ button creates new row. Validation shows if tiers overlap. ‘Preview’: input qty and see calculated price and discount % vs base price.
The seam — why this is core
Core owns tier pricing structure and cart calculation. Merchants define tiers manually per product.
Core owns tiered pricing rule engine and product variant pricing model; paid module owns AI dynamic pricing optimization
Dependencies
- Product system (tiers are per-product data)
- Cart system (to calculate and apply tier prices)
- Multi-currency-pricing-engine (if merchant uses multi-currency, tiers must support per-currency pricing)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Product with tiers: qty 1–2 = €10, qty 3–9 = €9, qty 10+ = €8. Adding qty=5 to cart shows €45 (5 × €9)
- Changing cart qty from 3 to 10 recalculates price to €80 (10 × €8) and updates cart total
- Overlapping tiers (qty 3–5 and qty 4–8) are rejected by system with error message
- Tier pricing respects currency: EUR tiers show prices in EUR, GBP tiers in GBP
- Admin dashboard shows ‘Tier 3–9 accounts for 60% of orders, margin 40%’
Risks
If tiers are sorted by quantity_max instead of quantity_min, lookup fails (always sort by quantity_min ascending). If tier price is added to base price instead of replacing it, calculation is wrong (tiers replace, not modify). If cart does not recalculate when customer manually edits quantity in cart input, displayed price and checkout price mismatch.
Commercial context
| Suggested price | free (core) |
| Rival anchor | AOV.ai, Bundler (freemium, $15-30/mo for advanced rules) |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.