Getting started
PriceVeil removes prices from your WooCommerce store — not just the visible label, but the numbers behind it — and puts a quote request in their place. It is built for shops that publish a catalogue but price each customer individually: B2B, wholesale, made-to-order, dealer and tender-driven stores.
Requirements
| Requirement | Version |
|---|---|
| WordPress | 6.4 or newer |
| WooCommerce | 8.0 or newer (HPOS-ready, tested to 11.0) |
| PHP | 8.1 or newer |
Install
- Install the plugin. Search for PriceVeil under
Plugins › Add New, or upload the zip from WordPress.org. - Activate it. Prices are hidden immediately — the defaults are hiding on, for everyone, with catalog mode and the quote form enabled.
- Purge your caches once. Any page cached before activation still contains the old prices. See Troubleshooting.
- Open
PriceVeil › Settingsand set your wording, audience and notification address.
PriceVeil never edits your product prices. It hides them at output time, so turning the master switch off restores the normal store instantly, with every price intact.
Settings
Everything lives on one screen: PriceVeil › Settings. The header carries two live badges — Hiding ON/OFF and Catalog mode / Buying enabled — so the store's real state is always visible at a glance.
Price hiding
| Setting | Default | What it does |
|---|---|---|
| Hide prices | On | The master switch. Turning it off restores the normal store instantly. |
| Audience | Everyone | Who sees the hidden-price experience: Everyone, or Guests only so logged-in customers see prices and buy normally. |
| Hidden price label | Price on request | The text shown wherever a price used to be. |
Catalog mode & quote requests
| Setting | Default | What it does |
|---|---|---|
| Catalog mode | On | Removes add-to-cart everywhere and rejects Store API cart writes. |
| Quote request form | On | Shows the button and form on product pages. Requests become non-revenue Quote orders. |
| Quote button label | Request a quote | The wording on the button that replaces add-to-cart. |
| Notification email | Your admin e-mail | Where new requests land. Replies go straight to the customer. |
Every save clears WooCommerce's product and page caches and signals LiteSpeed, SG Optimizer and Cloudflare where those are present — so a settings change shows up immediately rather than at the next cache expiry.
What gets hidden
The visible label is the easy part. "Hide price" plugins leak because modern WooCommerce ships the same number through half a dozen other channels — JSON for block themes, schema for Google, feeds, APIs. PriceVeil closes each one server-side, before the response leaves your site.
| Surface | What PriceVeil does |
|---|---|
| Storefront | Single product, shop loop, related and grouped products, variable price ranges and the inline variation JSON — numeric fields included, not just the label. |
| Store API (block themes) | Product prices, price_html, the collection price-range aggregation and cart totals are blanked server-side, so a client-rendered theme has nothing to display. |
| Structured data | WooCommerce core JSON-LD offers, plus Rank Math, Yoast SEO, All in One SEO and Schema & Structured Data for WP product schema. |
| WooCommerce REST API | wc/v3 and wc/v2 product and variation price fields — a read-scoped key handed to a third party reveals nothing. |
| GraphQL | WPGraphQL / WooGraphQL product price fields, blanked at field resolution. |
| Cart, checkout, e-mails | Classic PHP-rendered price output, quote-order customer e-mails and My Account views. |
| Catalog mode | Add-to-cart disappears from classic and block templates, and the Store API rejects cart writes — the API cannot be used as a side door around the missing button. |
What is not covered automatically
The settings screen prints this list too, because pretending the coverage is total is how stores get caught out:
- Product feed plugins (CTX Feed, Google Product Feed, Merchant integrations) export prices on purpose — disable your feeds while you are in quote mode.
- Theme or custom code that calls
$product->get_price()directly bypasses display filters. Check custom templates once. - Page-builder, object and edge caches can re-serve markup rendered before activation — purge once after enabling.
Who sees prices
Set Audience to Guests only and the store splits in two: anonymous visitors get "Price on request" and the quote flow, while logged-in customers see real prices and check out normally. That is the usual shape for a wholesale store with an approved-buyer list.
If a full-page cache serves anonymous visitors, make sure it is not also handing a logged-in customer's copy to a guest. WooCommerce's cookie rules cover this on most hosts; verify once if you run Cloudflare "Cache Everything" or Varnish.
Catalog mode
Catalog mode removes buying, not just the button:
- Add-to-cart is removed from classic templates and block templates alike, including the variations form on variable products.
- The Store API rejects cart writes, so the missing button cannot be worked around with a direct API call.
- Products stay fully browsable, searchable and indexable — you keep the catalogue, you lose the checkout.
Leave the quote form on and the button that replaces add-to-cart becomes your enquiry funnel. Turn the form off and you have a pure look-book catalogue.
Verify with WP-CLI
Price hiding is only worth as much as its weakest surface, so PriceVeil ships a self-test you can run any time — especially after a WooCommerce update, because API shapes change.
| Command | What it does |
|---|---|
wp priceveil selftest | Walks every product and asserts that price HTML, structured data, the Store API product response and the collection price-range aggregation are all blank. Exits non-zero on any leak, so it works in CI. |
wp priceveil scan | Reports per-surface exposure for the first 20 products (or the IDs you pass) as JSON, plus the list of things it cannot inspect. |
wp priceveil quote <product_id> | Creates a test quote request against a product, so you can exercise the flow without filling in the form. |
PriceVeil self-test PASSED — 150 products, 0 leaks across all server-reachable surfaces.
The quote request form
When the quote form is on, the price area of a product page carries a button — Request a quote by default — that opens a modal with the form. It works with the keyboard (focus moves into the form, Esc closes it, focus returns to the button) and it works without JavaScript, where it falls back to an ordinary in-page form.
Fields
| Field | Required | Notes |
|---|---|---|
| Your name | Yes | Stored as the order's billing name. |
| Email address | Yes | Where your reply goes. |
| Quantity | No | Defaults to 1; becomes the quote line quantity. |
| Message | No | Saved as the order's customer note. |
| Phone, company, VAT | Optional (Pro) | Switch them on individually, and mark each one required or not. |
| Custom fields | Optional (Pro) | Your own label, type and required flag. |
Spam protection
The public endpoint is protected three ways, with no third-party service and no CAPTCHA for the customer to solve:
- a nonce, so the request has to come from a real page view;
- a honeypot field that humans never see and bots usually fill;
- a per-IP rate limit, so one source cannot flood your orders list.
Quote orders
A request is not a separate table or a custom post type. It is a native WooCommerce order with its own status, so everything you already know still applies — search, filters, notes, e-mails, HPOS.
| Status | Meaning |
|---|---|
| Quote Request | A new enquiry from the form. This is where every request starts. |
| Quote Sent Pro | You priced the lines and sent the customer an accept/decline link. |
| Declined Pro | The customer declined. The order keeps its own status instead of pretending to be cancelled. |
Find them under WooCommerce › Orders, where Quote Request appears as its own filter link next to Pending payment and Completed.
The quote statuses are excluded from WooCommerce's paid statuses and from reports, so an enquiry never inflates your sales figures. They only start counting once a quote is accepted and actually paid.
Notification
Each new request e-mails the address in Notification email. The message is branded with your WooCommerce e-mail template and the reply-to is the customer, so hitting reply in your mail client answers them directly.
Shortcode & blocks
Use [priceveil_form] to place the request form anywhere — a landing page, a contact page, a category description. On a product page it picks up that product automatically.
There is also a Request form block for the block editor, and the plugin works with block themes: because hiding happens in the Store API response, a block-rendered product grid receives no price to show.
What Pro adds
The free plugin hides prices and collects enquiries. Pro closes the loop: price the enquiry, send it, let the customer accept and pay — without leaving WooCommerce.
| Capability | What it gives you |
|---|---|
| Requests console | A dedicated screen to price lines, add products, set validity and send the quote. |
| Customer quote page | A branded document with accept & pay / decline, optional add-ons and a paid receipt. |
| Quote drawer | A multi-product quote list, so one enquiry can cover a whole basket. |
| Custom form fields | Phone, company and VAT toggles plus your own fields. |
| Customer groups | Named groups with percentage pricing; members see prices and buy at their rate. |
| Targeting rules | Hide only certain categories or products, and let chosen roles see prices. |
| Automation | Answer instantly with an auto-quote at catalog or group prices. |
Requests console
The console is where an enquiry becomes an offer. It lists only quote orders, in tabs — New, Quote sent, Accepted, Declined and All — each with a live count.
- Open a request. You see the customer card, their message and the products they asked about.
- Price the lines. Type a unit price per line; subtotals and the running total update as you type. Add more products with the search box — they are added without reloading the page.
- Choose the line type. Each line can be Required, Optional (the customer may add it) or Alternative (one of a group). Added products default to Optional.
- Set a validity. 7, 14 or 30 days, or a date of your own. The customer sees the countdown.
- Send. A note under the button names the exact address the quote goes to, and the confirmation repeats it after sending.
You can also send the quote over WhatsApp: PriceVeil builds a click-to-chat message from your own editable template — no API, no integration, and you can save your wording as the default for future quotes.
The customer quote page
The customer opens one link and lands on a branded quote document — no account, no login. The link is authenticated by the order key.
- The quote. Letterhead with the quote number and date, your message, the required scope, and tappable cards for optional add-ons and alternatives. The total updates live as they choose.
- Accept & pay. The order is rebuilt from exactly what they ticked and sent to normal WooCommerce checkout — your existing gateways, taxes and e-mails.
- Decline. An inline confirmation, then a page that says plainly that they declined, with a way to re-open the conversation. You get an e-mail either way.
- Paid receipt. Once the order is paid, the same link shows a green Paid stamp above the usual WooCommerce order details.
- Their language. The quote page detects the visitor's language and offers a compact switcher on the document itself.
Customers with an account also find their quotes under My Account › Quotes.
Customer groups
Create named groups — Wholesale, Dealer, Key account — each with a percentage adjustment, and assign customers by e-mail, login or ID with a type-ahead search.
A logged-in member bypasses hiding entirely: they see prices and can buy, and every price they see is already adjusted by their group percentage. Because the adjustment happens in the price getters rather than per surface, the same number appears on the storefront, in the Store API, in REST and in GraphQL.
Targeting rules
Hiding does not have to be all-or-nothing:
- Include or exclude categories and products — hide prices on the made-to-order range while the accessories stay shoppable.
- Let chosen roles see prices — for example a wholesale customer role, or your own shop managers.
Languages
PriceVeil ships in eight languages — English, Turkish, Spanish, German, French, Italian, Brazilian Portuguese and Arabic, with right-to-left layout. It follows the site language automatically and falls back to English for anything else.
Admins can switch the plugin's own language from the control in its header without changing the rest of wp-admin. In Pro, the customer's quote page follows the visitor's own language and offers a switcher on the quote.
Licence & billing
Pro is licensed per site and sold through Freemius, our merchant of record. After checkout you get a licence key and the Pro package.
- Install the Pro plugin in place of the free one — your settings, groups and quote orders are untouched.
- Activate your licence from the plugin's own screen.
- Manage billing — invoices, seats and cancellation — from the same account screen.
Every purchase carries a 14-day money-back guarantee. If the licence lapses, the Pro modules simply stop loading: the plugin degrades to the free behaviour, and nothing is deleted.
Data, privacy & uninstall
- Where data lives. Settings sit in one option; quote requests are ordinary WooCommerce orders; group membership is one user-meta value. No custom tables.
- What leaves your site. The free build contains no licensing or telemetry code at all — no SDK, nothing to opt into. The Pro build talks to Freemius only for licence checks and updates.
- Uninstall. Deleting the plugin removes its options and its user-meta. Your products, prices and orders stay exactly as they are — quote orders included, because they are real orders.
Troubleshooting
Run wp priceveil selftest first — it tells you whether the leak is server-side (a real bug, please report it) or not. If the self-test passes but a page still shows a price, it is almost always a cache serving markup rendered before activation, or a feed/theme that reads get_price() directly. Purge every layer once: your caching plugin, the host cache, and the CDN.
Run the self-test. Some price surfaces are only reachable through filters WooCommerce marks as internal; if an update changes one, the self-test fails loudly and names the surface. Send us that output and it gets fixed.
Make sure catalog mode is on. Some themes render their own variations form; if a custom template calls the add-to-cart template directly, it bypasses the standard hooks — tell us the theme and we will check it.
Check that your security plugin or host firewall is not blocking admin-ajax.php for logged-out visitors, and that a required custom field is not empty. The response body in the browser's network tab names the actual reason.
That is WooCommerce's own guest protection on order pages. PriceVeil exempts quote links so they keep working; if you see it, tell us the order status involved.
Structured data is blanked from the moment you enable hiding, but Google re-crawls on its own schedule. Request re-indexing in Search Console for the affected URLs, and make sure any product feed is disabled — feeds publish prices on purpose.
Get support
Free plugin questions belong on the WordPress.org support forum, where the answers help the next person too. Pro customers get direct support from the plugin's own contact screen, or by e-mail at support@erenlabs.net.
When something is wrong, the single most useful thing you can send is the output of wp priceveil selftest together with your WordPress, WooCommerce and theme versions.