Overview
Spree’s pricing system supports both simple single-currency pricing and advanced multi-currency, rule-based pricing through Price Lists. Every Variant can have multiple prices — a base price per currency, plus additional prices from Price Lists that apply conditionally based on rules like geography, customer segment, or quantity.Prices
Price objects track a price for a particular currency and variant combination. For instance, a Variant may be available for $15 (15 USD) and €7 (7 Euro).
Price object contains 3 important attributes:
| Attribute | Description | Example Value |
|---|---|---|
amount | The current selling price of the variant in the specified currency. | 99.90 |
compare_at_amount | The recommended retail price of the variant in the specified currency. This can be used to display crossed out prices in the storefront. | 129.90 |
currency | The ISO code for the currency in which the amount and compare_at_amount are denominated. | USD |
prices to get a list of related Price objects:
prices to get a list of related Price objects:
If there’s no price set for requested currency this will return a new Price object with the currency set to the requested currency. It will not be a persisted object.
Getting amount (number)
Getting amount (number)
Getting amount (string)
Getting amount (string)
Displaying price with currency symbol
Displaying price with currency symbol
Spree behind the scenes uses Ruby Money gem with some additional tweaks.
Price Lists
Price Lists are available as of Spree 5.2.
- Market-based pricing - Different prices for different Markets (e.g., North America vs Europe)
- Regional pricing - Different prices for different geographic zones
- Wholesale/B2B pricing - Special prices for business customers
- Volume discounts - Tiered pricing based on quantity purchased
- Promotional pricing - Time-limited special offers
- VIP customer pricing - Exclusive prices for specific users
How Price Lists Work
Each Price List contains a collection of prices for specific variants and currencies. When a customer views a product or adds it to their cart, Spree’s pricing resolver determines which price to use based on:- Price List priority - Price Lists are ordered by position; higher priority lists are checked first
- Status - Only
activeorscheduledPrice Lists are considered - Date range - The current time must fall within
starts_atandends_at(if set) - Price Rules - All configured rules must match (or any, depending on
match_policy)
Price List Attributes
| Attribute | Description |
|---|---|
name | Human-readable name for the Price List |
status | Current status: draft, active, scheduled, or inactive |
starts_at | Optional start date/time when the Price List becomes applicable |
ends_at | Optional end date/time when the Price List stops being applicable |
match_policy | How rules are evaluated: all (every rule must match) or any (at least one rule must match) |
position | Priority order for evaluation (lower numbers = higher priority) |
Price Rules
Price Rules define conditions that must be met for a Price List to apply. Spree includes five built-in rule types:Market Rule
Market Rule
Applies the Price List based on the current Market. This is the recommended approach for regional pricing when using Markets.If
market_ids is empty, the rule matches any market. If the pricing context has no market, the rule does not match.Zone Rule
Zone Rule
Applies the Price List based on the customer’s tax/shipping zone. Useful for regional or country-specific pricing when not using Markets.
User Rule
User Rule
Applies the Price List to specific users. Useful for VIP customers, wholesale accounts, or B2B pricing.
Customer Group Rule
Customer Group Rule
Applies the Price List to members of specific customer groups. Useful for wholesale tiers, loyalty programs, or membership-based pricing.
Volume Rule
Volume Rule
Applies the Price List based on quantity purchased. Useful for bulk discounts or tiered pricing.
Creating Custom Price Rules
You can create custom Price Rules by subclassingSpree::PriceRule:
Pricing Context
TheSpree::Pricing::Context object carries all the information needed to resolve the correct price. It encapsulates the current pricing scenario and is used by the Spree::Pricing::Resolver to find the best applicable price.
Context Attributes
| Attribute | Type | Required | Default | Description |
|---|---|---|---|---|
variant | Spree::Variant | Yes | - | The variant for which to resolve the price |
currency | String | Yes | - | ISO 4217 currency code (e.g., 'USD', 'EUR') |
store | Spree::Store | No | Spree::Current.store | The store context; Price Lists are scoped to stores |
zone | Spree::Zone | No | Spree::Current.zone | The tax/shipping zone for zone-based pricing rules. Falls back to Spree::Zone.default_tax if not set. |
market | Spree::Market | No | Spree::Current.market | The current Market for market-based pricing rules. Falls back to the store’s default market. |
user | User class | No | nil | The current user; used by User Rules for customer-specific pricing |
quantity | Integer | No | nil | The quantity being purchased; used by Volume Rules for tiered pricing |
date | Time | No | Time.current | The date/time for price resolution; used to check Price List date ranges |
order | Spree::Order | No | nil | The current order; provides additional context for price resolution |
Understanding the Market and Zone Attributes
Themarket attribute is the primary way to scope pricing geographically. Markets bundle a set of countries with a currency and locale, making them the natural unit for regional pricing.
How market is resolved:
Spree::Current.marketis set per-request, typically based on the customer’s country- Falls back to the store’s default market if not explicitly set
- The Market Rule on Price Lists uses this attribute to match market-specific prices
The
zone attribute is still available for zone-based pricing via the Zone Rule. Zones are primarily used for tax and shipping calculations, while Markets are the recommended approach for regional pricing. Both can be used together — for example, a Market Rule for currency-based pricing and a Zone Rule for state-level tax-inclusive pricing.Creating a Context
There are several ways to create a pricing context:From an Order (recommended)
From an Order (recommended)
The most common approach during checkout. Automatically extracts all relevant information from the order:
From Currency (simple)
From Currency (simple)
For basic price lookups when you only need currency-based pricing:
Manual Construction
Manual Construction
For full control over all pricing parameters:
Resolving the Price
Once you have a context, use theSpree::Pricing::Resolver to find the best price:
Price resolution results are cached for 15 minutes using the context’s cache key. The cache key includes all context attributes (including market), so different scenarios are cached separately.
Getting Prices: price_for vs price_in
Spree provides two methods for fetching variant prices. Understanding when to use each is important for correct pricing behavior.
price_for (Recommended)
The price_for method resolves prices using the full Price List system. It considers all applicable Price Lists, rules, and returns the best matching price. This is the recommended method for most use cases.
price_for when:
- Displaying prices to customers in the storefront
- Calculating line item prices during checkout
- You need market-based, zone-based, user-based, or volume-based pricing
- You want prices from active Price Lists to apply
price_in (Base Prices Only)
The price_in method returns only the base price (prices without a Price List) for a given currency. It bypasses the Price List system entirely.
price_in when:
- You specifically need the base price, ignoring all Price Lists
- Managing prices in the admin interface
- Importing/exporting base pricing data
- Backward compatibility with pre-5.2 code
Comparison
| Method | Price Lists | Rules Applied | Use Case |
|---|---|---|---|
price_for | Yes | Yes | Storefront, checkout, customer-facing |
price_in | No | No | Admin, base price management |
Managing Price List Products
Products can be added to a Price List, which creates placeholder prices for all variants:Time-Based Pricing
Price Lists support scheduling throughstarts_at and ends_at attributes:
Scheduled Price Lists automatically become applicable when the current time falls within their date range. They don’t need to be manually activated.
Related Documentation
- Products — Products, Variants, and base prices
- Markets — Geographic regions with currency and locale
- Taxes — Tax categories, tax rates, and zones
- Promotions — Discount-based pricing via promotion rules

