# Price selection The single most common B2B implementation issue is not "the wrong price showed"; it is "no price showed at all," or "a price showed that nobody can explain." Both come from the same source: a Price exists, but it does not match the buyer's context, or a more specific Price won. The [Price and discount your products](/learning-price-and-discount-your-products) path introduced price selection and its fallback order; this page focuses on tracing it in a multi-buyer B2B project and on debugging it when a price goes missing. ## What price selection actually does When a Cart resolves a Line Item Price, the platform does not start by picking the cheapest or newest Price. It filters the candidate Prices for the SKU by context, then walks a fixed fallback order from most specific to least specific and uses the first fallback level that contains an eligible Price. Two of those context dimensions behave as **hard filters**, meaning that if they do not match, the Price is never a candidate: - **Currency** must match the Cart's currency exactly. - **Validity period**: a Price is only eligible when the request time falls within its `validFrom` and `validUntil` window. Prices with a current validity window are preferred over Prices with no dates. The remaining dimensions (Customer Group, Channel, and country) are scored by specificity. ## The fallback order For a given currency, commercetools applies the canonical [price-selection fallback logic](/api/pricing-and-discounts-overview.md#fallback-logic): Customer Group is evaluated before Channel, and Channel before country. Use that order when tracing the worked example below. The important B2B design point is not to memorize every fallback combination, but to know which pricing dimension wins when more than one matches. ```mermaid flowchart TD A[Candidate Prices for SKU] --> B{Currency matches Cart?} B -- No --> X[Not a candidate] B -- Yes --> C{Within validFrom / validUntil?} C -- No --> X C -- Yes --> D[Walk fallback: Customer Group then Channel then country] D --> E[Use first matching fallback level] E --> F{Any match found?} F -- No --> G[No selected price - 'price on request'] F -- Yes --> H[Selected Price applied to Line Item] ``` When the walk completes without finding any matching fallback level (most commonly because no Currency-matching Price exists, or because the buyer's Store has no distribution Channel attached), the product has no selected price. In storefronts this outcome is typically surfaced as **"price on request"**: the product is visible but cannot be added to a Cart at a resolved price, and the buyer must contact the seller directly. Treat this as a diagnostic signal: if a buyer sees "price on request" for a product that should be priced, price selection terminated without a match, and the debugging checklist below will identify why. The ordering tells you something important for B2B design: **Customer Group outranks Channel, which outranks country.** A Customer-Group-scoped Price will beat a Channel-scoped Price when both match at different fallback levels. If multiple eligible Prices match the same fallback level, the cheapest eligible Price is selected before Cart Discounts. This matters most when a Customer or Business Unit has multiple Customer Group assignments. For the Channel-on-Store pattern from the previous page, where buyers are not assigned Customer Groups for pricing, the Channel-specific fallback levels resolve the negotiated rate. ## Resolving prices for B2B catalogs When a buyer browses, you need prices resolved in the catalog response, not just on the Cart. Use the price-selection parameters on the [Product Projection](/api/projects/productProjections.md) and [Product Search](/api/projects/product-search.md) APIs (`priceCurrency`, `priceChannel`, `priceCustomerGroup`, and `priceCountry`) scoped to the buyer's context. Setting `priceChannel` to the buyer's distribution Channel returns the negotiated Price alongside each Product. When the buyer adds a Product to the Cart, pass the same Channel as the Line Item `distributionChannel` so Cart price selection uses the same commercial context. To fetch product data alongside search results, the recommended integration path is the GraphQL `productsSearch` query, which lets you request only the fields you need. The older `productProjectionParameters` data-integration field on Product Search was deprecated in December 2025. Search and faceting for B2B are covered in the previous module, [Product Search for B2B catalogs](/learning-model-b2b-commerce/discover-and-order-products-in-b2b/product-search-for-b2b-catalogs.md). ## Worked example: an Atlas Corporate APAC buyer Atlas Corporate Solutions has an APAC division buying in AUD through its `atlas-apac-pricing` distribution Channel. Trace the selection for SKU `ZET-MONITOR-27`, given these candidate Standalone Prices: | Price | Currency | Channel | Country | validFrom / validUntil | Value | | --- | --- | --- | --- | --- | --- | | P1 | AUD | `atlas-apac-pricing` | AU | current | 64900 | | P2 | AUD | `atlas-apac-pricing` | (any) | none | 67900 | | P3 | AUD | (any) | (any) | current | 79900 | | P4 | USD | `atlas-apac-pricing` | AU | current | 49900 | For a Cart in AUD with country AU and the `atlas-apac-pricing` Channel: - **P4 is filtered out** immediately: its currency is USD, which does not match the Cart. The lowest number on the list is never even a candidate. - **P1, P2, P3 remain.** The walk starts with Customer Group combinations. There is no Customer Group in context, so those levels find nothing. The first Channel-and-country level matches **P1** (`atlas-apac-pricing`, AU, currently valid). - The first matching fallback level contains P1, **AUD 649.00**. P2 and P3 are never reached, even though later fallback levels would have matched them. The lesson for tracing: read top-down, stop at the first matching fallback level, and remember that currency and validity gate the list before specificity is ever scored. ## The pricing debugging checklist When a buyer reports a missing or wrong price, work this checklist in order. It mirrors the fallback order, so the first failing check is usually the cause. Keep it to hand during implementation as a diagnostic for the most common B2B pricing failures. 1. **Currency**: does a Price exist for the exact Cart currency? A price in the "wrong" currency is invisible, not a fallback. 2. **Validity window**: is the request time inside the Price's `validFrom` / `validUntil`? An expired or not-yet-valid Price is filtered out. 3. **Channel ↔ Store link**: is the buyer's distribution Channel actually attached to the Store the Business Unit resolves to? A Channel-scoped Price with no Channel on the Store never matches. 4. **Customer Group**: if you scope prices by Customer Group, is the expected group resolved on the Cart or Customer? A Customer Group match also outranks a Channel match, so check it is not selecting an unexpected Price. 5. **Product Selection / availability**: is the SKU actually available to the buyer through an active Product Selection (from the previous module)? A buyer cannot be priced for a Product they cannot see. 6. **Precision and rounding**: are amounts the precision you expect, and is the Cart's rounding mode the one your invoicing requires? Covered in [high-precision pricing](/learning-model-b2b-commerce/configure-b2b-pricing/high-precision-pricing.md). 7. **API surface**: are you reading the Price from a price-aware response (a Cart Line Item with `distributionChannel` set, or a Product Search/Projection with price-selection parameters set) rather than a raw Product that performs no price selection? ## Key takeaways - Currency and validity period are hard filters; non-matching Prices are removed before specificity is scored. - The fallback order is Customer Group, then Channel, then country, using the first matching fallback level from most specific to least specific. - If multiple eligible Prices match the same fallback level, the cheapest eligible Price is selected before Cart Discounts. - A Customer-Group-scoped Price outranks a Channel-scoped Price when both match, which matters when mixing the two scopes. - For Channel-on-Store B2B pricing, the Channel-and-country or Channel-only fallback level resolves the negotiated rate. - A missing price is almost always one of seven things; work the debugging checklist in waterfall order to find the first failing check. ## Related pages - [Area overview page with navigation](/learning-model-b2b-commerce.md) - [Previous page: Company-specific pricing](/learning-model-b2b-commerce/configure-b2b-pricing/company-specific-pricing.md) - [Next page: Price versus discount: Channels and Customer Groups](/learning-model-b2b-commerce/configure-b2b-pricing/price-versus-discount.md)