# Commerce Pricing And Promotions

> 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.

- Skill: `pranavnagrecha/commerce-pricing-and-promotions` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds add pranavnagrecha/commerce-pricing-and-promotions`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pranavnagrecha/commerce-pricing-and-promotions/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: PranavNagrecha (https://skillmd.com/u/pranavnagrecha)
- Updated: 2026-09-08
- Page: https://skillmd.com/skills/pranavnagrecha/commerce-pricing-and-promotions

---


# 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

