B2B Commerce: Engineering Price Complexity
DTC is easy. B2B is hard. A technical guide to Tiered Pricing, Customer-Specific Catalogs, and Headless B2B architecture on Shopify Plus.
In Direct-to-Consumer (DTC) commerce, the logic is simple: “Here is a shirt. It costs $50.” In Business-to-Business (B2B) commerce, the answer to “How much does this cost?” is always: “It depends.”
- Who are you? (Gold Partner vs Bronze Reseller).
- How many are you buying? (1 unit vs 1,000 units).
- Where are you shipping? (EU VAT vs US Sales Tax).
- When are you paying? (Credit Card vs Net 30 Invoice).
For existing legacy B2B portals, this logic is buried in 20-year-old SAP spaghetti code. Modern B2B buyers expect the “Amazon Experience”—slick UI, instant search, mobile compatibility—but with the complex pricing logic of an ERP.
At Maison Code Paris, we build Headless B2B Portals that marry consumer-grade UX with enterprise-grade logic.
Why Maison Code Discusses This
B2B commerce is 5x larger than DTC. Yet, B2B websites are often 5x worse. We believe B2B buyers are humans too. They hate clunky interfaces. They hate waiting for PDFs. We help Manufacturers and Wholesalers transform their “Order Taking” centers into “Self-Service Revenue” machines. We engineer systems that handle thousands of SKUs and millions of pricing permutations without blinking.
1. The Data Model: Companies and Locations
In Shopify’s new B2B Primitive, we move away from the simple Customer object.
erDiagram
Company ||--|{ CompanyLocation : has
Company ||--|{ CompanyContact : employs
CompanyLocation ||--|| Catalog : assigned_to
Catalog ||--|{ PriceList : contains
CompanyContact }|--|| Customer : links_to
- Company: The legal entity (e.g., “Sephora”).
- CompanyLocation: The physical store (e.g., “Sephora Paris Champs Elysees”).
- Catalog: The subset of products this location is allowed to buy.
- CompanyContact: The human buyer (Buying Manager).
This hierarchy allows a single buyer to purchase for multiple locations, with different tax rules for each properly enforced.
2. Pricing Logic: The Waterfall
The rigorous part of B2B is calculating the final price. The algorithm follows a specific waterfall:
- Base Price: $100 (MSRP).
- Catalog Price: $80 (Wholesale List).
- Fixed Price Override: $75 (Contracted rate for this SKU).
- Percentage Off: -10% (Gold Tier Discount).
- Quantity Break: -5% (For buying >100 units).
Implementing Dynamic Pricing (Shopify Functions)
We don’t sync 50 variants for 50 price tiers. We use Shopify Functions (WASM) to calculate logic at checkout.
// rust/discount-allocator/src/main.rs (Simplified)
fn run(input: Input) -> Output {
let company = input.cart.buyer_identity.company;
let tier = company.metafields.get("custom.tier").unwrap_or("BRONZE");
let discount = match tier {
"GOLD" => 0.25, // 25% Off
"SILVER" => 0.15, // 15% Off
_ => 0.0,
};
// Apply discount to eligible lines
// ...
}
This ensures that no matter how complex the contract, the calculation happens in <5ms on the edge.
3. Quantity Rules (The Pallet Problem)
You cannot ship 1 brick. You ship a pallet of 500 bricks. B2B requires Quantity Increments and Minimum Order Quantities (MOQ).
We enforce this on the PDP (Product Detail Page) using UI constraints, but also validate it server-side.
// components/QuantitySelector.tsx
export function QuantitySelector({ moq, increment }) {
const [val, setVal] = useState(moq);
const stepUp = () => setVal(v => v + increment); // 500 -> 1000
return <input type="number" min={moq} step={increment} value={val} />;
}
4. The Payment: Net Terms vs Credit Card
B2B checkouts rarely use Credit Cards for large orders. The fees (2.9%) are too high on a $50k order. We use Draft Orders and Payment Terms.
- Checkout: User selects “Pay by Invoice (Net 30)”.
- Risk Check: We call the ERP (NetSuite) API to check the Company’s Credit Limit.
- If
CurrentBalance + CartTotal > CreditLimit: Reject Order. - If
OK: Proceed.
- If
- Submission: Order is created in Shopify with status
Payment Pending. - Invoicing: Middleware syncs order to ERP. ERP emails PDF Invoice.
- Reconciliation: When wire transfer arrives, ERP updates Shopify Order to
Paid.
5. The “Gatekeeper” Pattern
A B2B store is often private. We implement a Gatekeeping Middleware at the edge (Cloudflare / Vercel Edge).
// middleware.ts
export default function middleware(request: Request) {
const token = request.cookies.get('b2b_session');
if (!token) {
return NextResponse.redirect(new URL('/login', request.url));
}
const user = await verifyToken(token);
if (!user.is_b2b_approved) {
return NextResponse.rewrite(new URL('/pending-approval', request.url));
}
}
This prevents Google from indexing wholesale prices (which ruins brand value) and keeps competencies out.
6. Matrix Ordering (High Velocity)
A buyer ordering the FW25 collection needs to order 50 SKUs across 4 sizes. They do not want to visit 50 product pages. We build Matrix Grids (Excel-like interfaces). (See our specific article on Matrix Ordering).
This requires heavy frontend optimization (Virtualization) to render 2,000 inputs without crashing the browser.
7. ERP Integration: The Source of Truth
In B2B, Shopify is Not the master of data. The ERP is.
- Inventory: We do not trust Shopify inventory. We fetch real-time ATP (Available to Promise) from the ERP.
- Customer Data: When a customer updates their address in Shopify, we must push it to the ERP.
We use Event-Driven Architecture (See AWS Infrastructure) to handle these syncs asynchronously via Webhooks and Queues (SQS).
8. Customer Specific Catalogs
Some brands have “Exclusive” products. Nike might sell “Air Jordans” only to “Tier 1” retailers. Standard retailers must not even see the product. We use Catalog Separation.
- Tag Products:
tag: "exclusive". - Tag Companies:
metafield: "access_level: 1". - Search Filtering: When querying Algolia, we inject the filter:
filters: '(tag:exclusive AND user.access_level >= 1) OR NOT tag:exclusive'. This ensures security through obscurity.
9. Headless B2B Architecture (Hydrogen)
We build B2B on Shopify Hydrogen (React). Why not Liquid? Because Liquid cannot handle the complexity of “Matrix Ordering” or “custom Price Lists” fast enough. With Remix/Hydrogen:
- We cache the “Base Product Data” at the Edge (CDN).
- We fetch “Personalized Price” via
defer(Streaming). - The page loads instantly. The price pops in 200ms later. This feels like a Consumer App, not an Enterprise Portal.
10. The Quote Request Workflow
For orders > $50k, the price is not fixed. It is negotiated.
- Add to Quote: User builds a cart. Instead of “Checkout”, they click “Request Quote”.
- Sales Agent: Sales rep in Salesforce receives the quote.
- Negotiation: Rep edits the prices (“I’ll give you 5% more off if you add 100 units”).
- Approval: User receives an email: “Quote Ready”.
- Conversion: User clicks link, sees the custom pricing, and pays via Net 30. This digitizes the negotiation without removing the human touch.
11. AI-Driven Pricing (Dynamic Yield)
Negotiated contracts are static. “You get 10% off.” But what if the customer is about to churn? We use AI (Dynamic Yield / Nosto) to inject Dynamic Promotions. “If Customer X visits the site 3 times and doesn’t buy -> Offer 5% extra discount for 24 hours.” This is strictly controlled by Margin Rules (set in the ERP). The AI is the “Digital Sales Rep” that knows exactly how much juice to squeeze to close the deal.
12. Conclusion
B2B is no longer the “Ugly Duckling” of e-commerce. The brands winning in wholesale are the ones offering a DTC-like experience. By abstracting the complexity of Pricing and Logistics behind a clean Headless UI, you respect the buyer’s time. And in B2B, time is the only currency that matters.
Scaling your Wholesale channel?
Is your B2B portal stuck in the 1990s?