WooCommerce B2B and Wholesale
B2B selling breaks one assumption WooCommerce is built on: that a product has a price. Everything else — tax display, minimum orders, payment terms, hidden catalogues — follows from fixing that.
Retail sells a product at a price; B2B sells a price to a customer. WooCommerce is built on the first model, and every wholesale requirement is a consequence of overturning it. Start there and the rest of the setup is orderly. Start by installing a plugin and you will be patching symptoms for months.
The five decisions to make first
- Who sees wholesale prices — approved accounts only, or anyone who registers.
- How prices are set — a percentage off retail, a fixed tier price, or per-customer lists.
- What the tax display is for trade buyers, which usually differs from retail.
- Whether the catalogue is public or hidden behind a login.
- How trade customers pay — card at checkout like anyone else, or on account with an invoice.
Every one of these is a different plugin feature, and no single plugin does all five equally well. Deciding first tells you which to shortlist.
Roles are the mechanism
WordPress roles are how a store distinguishes buyers, and understanding them makes B2B setup much less mysterious — user roles explained covers the model.
The usual arrangement is a wholesale_customer role created by the plugin, with pricing rules attached to it and an approval step before an account gets it. Two things to plan:
- Approval workflow. Self-registration that instantly grants trade pricing is an open discount to anyone who finds the form.
- What an unapproved account sees. Retail prices, a "pending approval" notice, or nothing at all — the choice is yours and should be deliberate.
Pricing models, and their maintenance cost
| Model | How it scales | Watch for |
|---|---|---|
| Percentage off retail by role | Easiest, one rule | Margins break on low-margin lines |
| Tier price per product per role | Precise | Every new product needs prices |
| Quantity breaks | Encourages larger orders | Interacts badly with coupons |
| Per-customer price lists | Matches real trade relationships | Genuinely heavy to maintain |
Most stores over-engineer this. Start with role-based percentages, add per-product overrides where margin demands it, and go to per-customer lists only when a customer's contract requires it. Dynamic pricing rules multiply quickly, and a store with sixty overlapping rules is a store where nobody can explain a given order's total.
Tax display, which causes the most disputes
Trade buyers expect prices excluding tax; consumers expect them included. WooCommerce's tax display setting is store-wide.
Making it per-role requires a plugin or code, and getting it wrong is expensive in support time: every order becomes a conversation about whether tax was included. Decide the behaviour for catalogue pages, cart and checkout separately, then test all three as both a retail and a trade account before launch.
Cross-border trade adds validation of a customer's tax number and possible zero-rating, which is a plugin feature to check for explicitly rather than assume.
Minimum orders, and where to enforce them
A minimum order value or quantity is a normal B2B condition, and the enforcement point matters. Blocking at checkout after a buyer has assembled a large order is a bad experience; showing the requirement on the cart and in the product area is better.
The same applies to case quantities — if a product only sells in twelves, the quantity field should step in twelves rather than rejecting eleven at the end.
Ordering interfaces built for repeat buyers
Trade buyers do not browse. They know the codes and reorder the same list monthly, and a consumer-grade catalogue is a poor fit for that.
What actually helps:
- A bulk order form by SKU, which is the single most requested B2B feature — product tables is the usual shape.
- Reorder from a previous order, in one action.
- Saved lists for recurring purchases.
- Fast search, because a trade buyer types a code rather than clicking categories — see WordPress search.
Payment on account
This is where B2B leaves ecommerce and becomes accounting. Offering invoice terms means:
- A payment method that completes the order without taking money — usually a configured offline gateway, per WooCommerce payments.
- A credit decision per customer, which is a business process rather than a plugin setting.
- Invoices, terms tracking and chasing, which belong in your accounting system rather than in WordPress.
- An export or integration so orders reach that system without retyping — a CRM is often already the pivot point.
If most trade customers pay by card anyway, skip all of this. It is the heaviest part of B2B and the least often genuinely required.
Common mistakes
- Automatic trade approval, discovered when retail customers register for the discount.
- Deciding tax display after launch, then arguing about every historic order.
- Sixty pricing rules nobody can reason about.
- Coupons stacking on wholesale prices, because nobody restricted coupons by role.
- Two separate stores for one catalogue, doubling stock reconciliation — see installing WooCommerce before splitting anything.
Verify
Create a test account in each role and walk the whole path: catalogue price, cart total, tax basis, minimum order enforcement, shipping cost per shipping setup, payment method availability, and the confirmation email. Then log out and confirm an anonymous visitor sees exactly what you intended — the most common wholesale bug is trade pricing leaking to the public catalogue.
Frequently asked
- Not properly. Core assumes one price per product, with sale price as the only variation. Per-customer or per-role pricing requires a plugin or custom code that filters the price at runtime, and doing it by hand is a maintenance burden most stores should not take on.
- One store is simpler when the catalogue and stock are shared, which is the usual case. Two stores make sense when the products, tax treatment, shipping and branding genuinely differ — at which point you are running two businesses, not two price lists.
- Tax display is a store-wide setting in core, so per-customer display needs a plugin or code that switches it by role. Get this right before launch: showing the wrong tax basis to trade buyers produces disputes on every order.