Configure pricebooks, tiered pricing, promotions, coupon codes, and cart discounts for B2B or D2C Commerce stores. Trigger keywords: WebStorePricebook, BuyerGroupPricebook, PriceAdjustmentSchedule, PromotionsCartCalculator, commerce coupon, cart-level discount, tiered price, Commerce promotion. NOT for Sales Cloud pricebooks and PricebookEntry - use admin/products-and-pricebooks. NOT for CPQ quote pricing and discount schedules - use admin/cpq-pricing-rules.
Installs into .claude/skills of the current project.
Are you the author of Commerce Pricing And Promotions?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/pranavnagrecha-commerce-pricing-and-promotions)
---
name: commerce-pricing-and-promotions
description: "Configure pricebooks, tiered pricing, promotions, coupon codes, and cart discounts for B2B or D2C Commerce stores. Trigger keywords: WebStorePricebook, BuyerGroupPricebook, PriceAdjustmentSchedule, PromotionsCartCalculator, commerce coupon, cart-level discount, tiered price, Commerce promotion. NOT for Sales Cloud pricebooks and PricebookEntry - use admin/products-and-pricebooks. NOT for CPQ quote pricing and discount schedules - use admin/cpq-pricing-rules."
category: admin
salesforce-version: "Spring '25+"
well-architected-pillars:
- Reliability
- Performance
- Scalability
triggers:
- "pricebook is not showing up for buyers in the commerce store"
- "commerce promotion is active but discount is not applying at checkout"
- "how do I set up tiered pricing based on quantity in B2B Commerce"
- "coupon code is being rejected even though it is valid in the Promotions object"
- "buyer group pricebook assignment is not resolving the right price"
- "how many pricebooks can a B2B store support and what happens when you exceed the limit"
tags:
- commerce
- pricing
- promotions
- pricebook
- b2b-commerce
- d2c-commerce
- buyer-group
- tiered-pricing
- coupons
- cart-discounts
inputs:
- "WebStore Id and store type (B2B or D2C)"
- "Pricebook2 records and their intended buyer audience (BuyerGroup or guest)"
- "Promotion scope: store-wide, segment, BuyerGroup, or coupon-gated"
- "Tiered pricing requirements: quantity breaks and adjustment amounts"
- "Checkout flow name (to confirm PromotionsCartCalculator subflow inclusion)"
outputs:
- "WebStorePricebook junction records wiring pricebooks to the store"
- "BuyerGroupPricebook junction records assigning pricebooks to buyer groups"
- "PriceAdjustmentSchedule and PriceAdjustmentTier records for tiered pricing"
- "Promotion, PromotionSegmentSalesStore, and PromotionSegmentBuyerGroup records"
- "Checkout flow verified to include the Promotions subflow"
- "SOQL validation queries confirming record wiring"
dependencies: []
version: 1.0.0
author: Pranav Nagrecha
updated: 2026-04-10
---
# Commerce Pricing and Promotions
This skill activates when a practitioner needs to configure pricebook assignment, tiered pricing, or promotions (including coupons and cart-level discounts) for a Salesforce B2B or D2C Commerce store. It covers the full pricing resolution stack — from pricebook attachment through discount application at checkout — and explains all silent-failure limits that cause real production bugs.
---
## Before Starting
Gather this context before working on anything in this domain:
- **Store type and Id:** B2B and D2C stores share the same pricing objects but differ in guest-buyer access patterns. Retrieve the `WebStore.Id` first.
- **Pricebook resolution limit:** The price engine evaluates at most 25 pricebooks per API call. Pricebooks ranked beyond 25 are silently excluded — no error is raised. Count assigned pricebooks before adding new ones.
- **Promotions do not self-apply:** Activating a Promotion record does not automatically apply discounts at checkout. The checkout flow must include the `PromotionsCartCalculator` subflow. Confirm this before debugging "promotion not working" reports.
- **API version floor:** `PriceAdjustmentSchedule` (tiered pricing parent) requires API v60.0+. `PriceAdjustmentTier` requires API v47.0+. Verify the org's API version before deploying tiered pricing metadata.
---
## Core Concepts
### 1. Pricebook Attachment via Junction Objects
Commerce stores do not use the standard Sales Cloud `Pricebook2Id` field on Account or Opportunity. Two dedicated junction objects control pricebook visibility:
- **`WebStorePricebook`** — links a `Pricebook2` to a `WebStore`. Maximum **5 pricebooks per store**. This is the platform hard limit; the 6th record raises a validation error.
- **`BuyerGroupPricebook`** — links a `Pricebook2` to a `BuyerGroup`. Maximum **50 pricebooks per BuyerGroup** and **100 BuyerGroups per pricebook**.
During price resolution, the engine collects all pricebooks visible to the buyer (via their BuyerGroup memberships and the store's WebStorePricebook assignments), ranks them by the `Priority` field on the junction record, then evaluates at most **25 pricebooks**. Pricebooks ranked 26th or higher are silently skipped — no error, no log entry.
### 2. Tiered Pricing with PriceAdjustmentSchedule
Tiered pricing (quantity-based price breaks) uses two objects linked to a `PricebookEntry`:
- **`PriceAdjustmentSchedule`** (API v60.0+) — the parent record that defines the adjustment method (`Percent`, `Amount`, or `Price`) and the associated `PricebookEntry`.
- **`PriceAdjustmentTier`** (API v47.0+) — child records on the schedule, each defining a `LowerBound`, optional `UpperBound`, and the adjustment value.
The engine applies the matching tier at add-to-cart time. If no tier matches (e.g., quantity falls outside all defined bounds), the base `PricebookEntry.UnitPrice` is used without adjustment.
### 3. Promotions, Segments, and the Checkout Flow Requirement
A `Promotion` record activates a discount but does nothing by itself. The full promotion execution stack is:
1. **`Promotion`** — defines the discount type, priority, start/end dates, and whether it is automatic or coupon-gated.
2. **`PromotionSegmentSalesStore`** — scopes the promotion to a specific `WebStore`.
3. **`PromotionSegmentBuyerGroup`** — further scopes the promotion to a specific `BuyerGroup` (optional; omit for store-wide promotions).
4. **`PromotionCode`** — links a coupon code string to the promotion (only for coupon-gated promotions).
5. **Checkout flow with `PromotionsCartCalculator` subflow** — at checkout, the platform's evaluate API runs all applicable automatic promotions (up to 50) and all applied manual/coupon promotions (up to 50) ranked by `Priority`. Promotions beyond rank 50 in either category are silently excluded.
Hard cart limits enforced at checkout:
- Maximum **5 coupon codes per cart** — adding a 6th raises a user-visible error.
- Maximum **24 item-level adjustments** per cart.
- Maximum **6 order-level adjustments** per cart.
---
## Common Patterns
### Pattern A: Store-Wide Automatic Promotion
**When to use:** A discount applies to all buyers in the store with no coupon required — e.g., a site-wide 10% off sale.
**How it works:**
1. Create a `Promotion` record with `IsActive = true`, `Priority` set relative to other promotions, and `StartDate`/`EndDate` defined.
2. Create a `PromotionSegmentSalesStore` record linking the promotion to the target `WebStore`.
3. Leave `PromotionSegmentBuyerGroup` absent (store-wide scope).
4. Confirm the checkout flow includes the `PromotionsCartCalculator` subflow — without it, the promotion never fires.
5. Test by adding qualifying products to a cart and completing checkout.
**Why not the alternative:** Simply activating the `Promotion` record and assuming it applies is the most common production bug. The checkout flow integration is non-optional.
### Pattern B: BuyerGroup-Scoped Pricebook with Tiered Pricing
**When to use:** Different buyer tiers (e.g., Distributors vs. Retailers) need different base prices, with further quantity-based discounts layered on top.
**How it works:**
1. Create one `Pricebook2` per buyer tier with appropriate `PricebookEntry` records and `UnitPrice` values.
2. Create a `BuyerGroupPricebook` junction record for each `Pricebook2` → `BuyerGroup` pairing. Set `Priority` to control resolution order within the group (lower number = higher priority).
3. Create a `WebStorePricebook` junction record for each pricebook that should also be discoverable at the store level (keep total under 5).
4. For products with quantity breaks, create a `PriceAdjustmentSchedule` linked to the relevant `PricebookEntry`, then add `PriceAdjustmentTier` child records for each quantity band.
5. Verify tier bounds are contiguous and non-overlapping — gaps cause the base price to apply silently.
**Why not the alternative:** Using a single pricebook for all buyers and applying manual price overrides at order entry bypasses the automated resolution stack and cannot scale across thousands of products or buyer groups.
### Pattern C: Coupon-Gated Promotion
**When to use:** A discount applies only when a buyer enters a valid coupon code at checkout.
**How it works:**
1. Create a `Promotion` record with the discount definition. Set it as manual (not automatic) so it does not fire without a code.
2. Create a `PromotionSegmentSalesStore` junction to scope it to the correct store.
3. Create one or more `PromotionCode` records linked to the promotion, each with a unique `Code` string.
4. Buyers enter the code at checkout. The `PromotionsCartCalculator` subflow validates the code and applies the discount.
5. Enforce the 5-coupon-per-cart limit in UX design — surface a clear error message if a buyer attempts to add a 6th code.
---
## Decision Guidance
| Situation | Recommended Approach | Reason |
|---|---|---|
| All buyers in the store get the same price | One `Pricebook2` attached via `WebStorePricebook` | Simplest resolution path; use `BuyerGroupPricebook` only when differentiation is needed |
| Different buyer groups need different prices | Separate `Pricebook2` per tier, attached via `BuyerGroupPricebook` | The resolution engine picks the highest-priority matching pricebook per buyer |
| Quantity breaks on specific products | `PriceAdjustmentSchedule` + `PriceAdjustmentTier` on `PricebookEntry` | Declarative tiered pricing; no custom Apex required |
| Automatic discount for all buyers | `Promotion` + `PromotionSegmentSalesStore`, no coupon | Simplest promotion scope; still requires the checkout flow subflow |
| Discount only for a specific buyer group | `Promotion` + `PromotionSegmentSalesStore` + `PromotionSegmentBuyerGroup` | Scoping to BuyerGroup prevents the discount from leaking to other segments |
| Discount requires a coupon code | `Promotion` (manual) + `PromotionCode` records | Manual promotions are only evaluated when a buyer supplies the code |
| Store is close to the 5-pricebook limit | Consolidate buyer tiers into fewer `Pricebook2` records | The 5-per-store limit is hard; consolidation or BuyerGroupPricebook is the only path |
---
## Recommended Workflow
Step-by-step instructions for an AI agent or practitioner working on this task:
1. **Audit current pricebook assignment.** Query `WebStorePricebook` for the target `WebStore.Id` and count existing records. Confirm the count is under 5 before creating new ones. Query `BuyerGroupPricebook` for each relevant `BuyerGroup` and confirm counts are under 50.
2. **Design the buyer-tier-to-pricebook mapping.** Document which `BuyerGroup` maps to which `Pricebook2` and what `Priority` value each junction record should carry. Confirm total unique pricebooks evaluated per buyer does not exceed 25.
3. **Create or update pricebook junction records.** Insert `WebStorePricebook` records for store-level visibility and `BuyerGroupPricebook` records for segment-level assignment. Set `Priority` fields deliberately — the evaluation order determines which price wins when multiple pricebooks contain the same product.
4. **Configure tiered pricing if required.** Create `PriceAdjustmentSchedule` linked to the target `PricebookEntry`, then add `PriceAdjustmentTier` child records with contiguous quantity bounds. Verify no gaps exist between tier upper and lower bounds.
5. **Set up promotions and scope them correctly.** Create `Promotion` records, then wire `PromotionSegmentSalesStore` (required) and optionally `PromotionSegmentBuyerGroup`. For coupon-gated promotions, create `PromotionCode` records. Confirm `IsActive = true` and date ranges are correct.
6. **Verify the checkout flow includes the Promotions subflow.** Open the active checkout flow in Flow Builder and confirm the `PromotionsCartCalculator` element is present in the cart calculation sequence. Without it, no promotions fire.
7. **Run SOQL validation and end-to-end checkout test.** Execute the validation queries in the template to confirm all junction records are wired correctly. Place a test order as a buyer in the affected `BuyerGroup` and confirm prices and discounts resolve as expected.
---
## Review Checklist
Run through these before marking work in this area complete:
- [ ] `WebStorePricebook` count for the store is 5 or fewer
- [ ] `BuyerGroupPricebook` count per BuyerGroup is 50 or fewer; pricebook has 100 or fewer BuyerGroups
- [ ] Total pricebooks visible to any single buyer does not exceed 25
- [ ] `PriceAdjustmentTier` bounds are contiguous (no gaps, no overlaps) for every tiered product
- [ ] Each active `Promotion` has a `PromotionSegmentSalesStore` record for the correct store
- [ ] Checkout flow contains the `PromotionsCartCalculator` subflow element
- [ ] Cart-level adjustments stay within limits: 24 item-level, 6 order-level, 5 coupon codes
- [ ] Promotions ranked beyond 50 (automatic) or 50 (manual) have been removed or re-prioritized
- [ ] End-to-end checkout test performed as a buyer in each affected BuyerGroup
---
## Salesforce-Specific Gotchas
Non-obvious platform behaviors that cause real production problems:
1. **Silent pricebook exclusion at rank 26+** — The price engine evaluates at most 25 pricebooks per call. If a buyer belongs to multiple BuyerGroups with many pricebook assignments and the total visible pricebooks exceeds 25, the lowest-priority ones are silently skipped. No error is logged. Buyers may see wrong prices with no obvious cause.
2. **Promotions require the checkout flow subflow** — Activating a `Promotion` record and making it active does not apply any discount. The `PromotionsCartCalculator` must be present in the active checkout flow. Removing or bypassing the flow (e.g., during testing with a custom flow) silently disables all promotions.
3. **5 coupon codes per cart is a hard limit** — Attempting to apply a 6th coupon code raises a user-visible error. Design the UX to surface a clear message before this limit is hit, not after.
4. **Silent promotion exclusion at rank 51+** — The evaluate API processes at most 50 automatic and 50 manual promotions per checkout, ranked by `Priority`. Promotions outside that window are silently ignored. Regularly audit promotion counts and retire expired records.
5. **`Pricebook2Id` on Account does not work for Commerce** — The standard Sales Cloud field `Account.Pricebook2Id` is ignored by the Commerce pricing engine. Pricebook assignment for Commerce buyers must go through `WebStorePricebook` and `BuyerGroupPricebook`.
---
## Output Artifacts
| Artifact | Description |
|---|---|
| `WebStorePricebook` records | Junction records linking each `Pricebook2` to the `WebStore` with a `Priority` value |
| `BuyerGroupPricebook` records | Junction records assigning pricebooks to `BuyerGroup` records for segment-specific pricing |
| `PriceAdjustmentSchedule` records | Parent records enabling tiered pricing on a `PricebookEntry` (requires API v60.0+) |
| `PriceAdjustmentTier` records | Child records defining quantity bounds and adjustment values for tiered pricing |
| `Promotion` + segment junction records | `Promotion`, `PromotionSegmentSalesStore`, `PromotionSegmentBuyerGroup` records defining the discount and its scope |
| `PromotionCode` records | Coupon code strings linked to manual promotions |
| SOQL validation queries | Queries verifying junction record wiring before buyer testing |
---
## Related Skills
- `admin/b2b-commerce-store-setup` — covers `WebStore`, `BuyerGroup`, `EntitlementPolicy`, and catalog access wiring that must be in place before pricing configuration