Price governance in ecommerce: how a promotion died silently on four country shops for a month
A home and living brand advertised a fifty-euro saving while checkout charged full price on four markets for a month. The cause was the price guard itself - and the fix was declaring the price once and auditing the whole chain daily.
A customer wrote in about one page. It was a home and living brand I look after, four country shops on WooCommerce, and the product page advertised a bundle with the old price struck through and a fifty-euro saving called out underneath. The cart charged the full price. She noticed, she asked, and she was right.
The complaint was about one page on one market. By the time I had finished looking, the same thing was true on all four country shops, and it had been true since the first of September. A month of advertising a discount that checkout did not grant.
The promotion was not ended. It was gated.
The promotion had been rolled out open-ended. No end dates anywhere, verified in the cart at the time. What changed later was not the pages and not a decision anyone recorded — it was a date.
Two mechanisms carried that date, in two different places. The products themselves had been given a sale end date, and WooCommerce does what WooCommerce does: when the end date passes, the scheduled-sale handling takes the product off sale. The leftover sale metadata that stays behind in the database afterwards is enough of a known annoyance that it has its own feature request.
The second mechanism is the interesting one, and it is the reason this is a post rather than a ticket.
The guard was the thing that broke it
These shops sit downstream of an ERP that pushes prices into the webshop. That push has overwritten promotional prices before, so there is a protective snippet in front of it — a price lock that force-reverts every cart to the regular price unless the product is on a small allow-list. The allow-list is the only way a sale legitimately reaches the front end.
Somebody had wrapped that allow-list in a date condition. On the first of September the condition went false, the allow-list emptied, and the guard did exactly what it is built to do: it reverted every cart to the regular price. Correctly, from its own point of view. The code comment above it still said "open-ended".
Nothing alerted. The pages kept advertising the sale, because pages do not know what the cart thinks. The storefront and the cart disagreed for thirty days and the only monitoring in the chain was a customer with good attention.
A protection nobody monitors is just a single point of failure with good intentions.
I have written before about expired promotions that stay on the page and about fixing checkout in production. This is the same family of failure with the arrow reversed. The page was right and the cart was wrong, which is the more expensive direction, because in the EU an announced reduction is a legal claim about a price. The CJEU confirmed in 2024 that a price-reduction announcement has to be calculated from the lowest price charged in the previous thirty days, and the Commission's guidance on Article 6a is specific about what a strikethrough is allowed to mean. A shop that advertises a saving and charges full price is not in a grey area.
Declare the price once, then check the whole chain
Restoring the prices took an afternoon. Removing the date gate, putting the sale prices back open-ended on each market, and then proving it with a real guest add-to-cart on all four shops, because the only check that matters is the one the customer performs.
The part worth keeping is what went in afterwards. There is now a single file that states the communicated price for every promotional product in every market — the declared intent, in one place, in version control. And a read-only script that walks the entire chain against it: database prices, no stray sale windows, the storefront, a real guest cart total, the price as it appears in the page copy, any product the ERP has quietly put on sale, and the guard itself — is it active, is the allow-list populated, has anyone added a date to it again.
It runs daily, early, before anyone is awake. It is silent when everything matches and loud when it does not, and it never fixes anything on its own. Eighty-eight assertions, green at install. Google's SRE book makes the case better than I can: spend far more effort catching symptoms than causes, and keep the rules that catch real incidents as simple and predictable as you can. A guest cart total is a symptom. It is also the only number the customer sees.
The working rule is now the boring one: any price change updates the file first, then the shop, then the script runs until it is green. I have built enough of these scheduled audits to know they have a failure mode of their own — I once scheduled a weekly check that could never run, and a silent check is indistinguishable from a passing one until you make it shout.
The real question is who has write access
Two days later, a marketplace channel turned out to be carrying a different base price for the same product. Nobody set that either, as far as anyone knows. Two cases in two days of a channel quoting a price that no human claims to have chosen.
At that point it stops being a bug and starts being governance. Which system is the source of truth for price? Who is allowed to write to it? And when a protective hack exists only because the real answer is unresolved — the ERP push that should have been made read-only months ago — somebody has to carry that as an open item instead of letting the hack become the architecture.
This is the part that twenty years of operating teaches you and no tool gives you for free. AI wrote most of those eighty-eight assertions in an afternoon; I would not have had the patience otherwise. Knowing which eighty-eight things to assert, and that the guest cart is the one that counts, is the part that does not come out of a model.
Your shop has a price. Something is deciding it. Find out what.
Sources & further reading
External
CJEU confirms price reduction claims must be based on the lowest price in the last 30 days (Herbert Smith Freehills Kramer, on C-330/23)
European Commission guidance on Article 6a of the Price Indication Directive
Expired sale prices and dates remain after a scheduled sale ends (WooCommerce feature request)
Monitoring Distributed Systems (Google SRE Book, chapter 6)
Related posts
Expired promotions, price mismatches and Google Ads promotion assets
We fixed checkout in production
My scheduled consent audit found a missing cookie banner
I scheduled a weekly check that could never run
The death of generic AI: why deep domain expertise is the only real leverage left