20% off which price? The law says the lowest of the previous 30 days
The badge says "-20%". Behind it are three prices. One is the price the product had last week, one the number typed into the compare-at field this morning, and one the lowest price it carried at any point in the previous 30 days. For a shop selling goods to consumers in the European Union, the law names the third as the only reference, and the standard shop fields hold the other two.
The rule covers goods sold to EU consumers, from anywhere; services are outside it at EU level, and the UK and US have their own tests, covered below. Every document quoted was read on 12 September 2026. I am not a lawyer.
The reference price is the lowest of the previous 30 days
Article 6a of the price indication directive, added by the 2019 Omnibus Directive and in force for announcements since 28 May 2022, says it in two sentences:
"1. Any announcement of a price reduction shall indicate the prior price applied by the trader for a determined period of time prior to the application of the price reduction. 2. The prior price means the lowest price applied by the trader during a period of time not shorter than 30 days prior to the application of the price reduction."
Directive (EU) 2019/2161, Article 2, inserting Article 6a into Directive 98/6/EC
The Commission's guidance on the article fills in what the sentence leaves open. The lowest price "shall include any previous 'reduced' price during that period", so a product that was on offer three weeks ago carries that offer price as its reference. The guidance's own example: "where the price reduction announcement is '50 % off' and the lowest price in the 30 previous days was EUR 100, the seller will have to present EUR 100 as the 'prior' price from which the 50 % reduction is calculated, despite the fact that the last selling price of the good was EUR 160."
An announcement is any signal of a cut: a percentage, an amount, and also "'sales' price, 'special offers' or 'Black Friday offers'". Loyalty cards and personal vouchers are exempt; a "code XYZ" offered to everyone counts as an announcement. The article covers goods, wherever the seller sits: a shop in Miami shipping to Lyon is in scope.
The court closed the arithmetic
In September 2024 the Court of Justice answered the question a sale scheduler would ask: from which price is the percentage counted? The case came from an Aldi brochure. Bananas were offered at EUR 1.29, with EUR 1.69 struck through and a percentage badge; under the tile the line "Last selling price. Lowest price in the last 30 days" gave EUR 1.29. Pineapples were offered at EUR 1.49 against a struck-through EUR 1.69; their lowest 30-day price was EUR 1.39, below the "reduced" price. The Court's ruling, in the operative part that binds:
"a price reduction of a product announced by a trader in the form of a percentage, or in the form of a promotional statement intended to highlight the advantageous nature of the announced price, must be determined on the basis of the 'prior price'"
Court of Justice of the EU, Case C-330/23, Aldi Süd, 26 September 2024, operative part
Paragraph 27 draws the consequence: a price presented as reduced "cannot in fact be the same as that 'prior price', or be higher than it". Aldi had printed the accurate 30-day low under the tile; the ruling says the percentage had to be counted from that line, which for the bananas leaves a reduction of zero.
The fields your shop has keep today's numbers
I read the pricing documentation of four platforms on 12 September 2026: what the discount field is, and what it remembers.
| Platform | Discount field | What it stores |
|---|---|---|
| Shopify | "Compare-at price" next to "Price" | one number; Shopify's own EU guidance page: "only two fields for product price: the price, and compare-at price" |
| WooCommerce | "Regular price", "Sale price", optional from/to dates | two numbers and a schedule; no history |
| Stripe coupons | percent_off |
"Percent that will be taken off the subtotal of any invoices"; no prior price |
| Amazon | "Typical Price" | "the 90-day median price paid by customers", a different definition from the directive's |
Shopify's compliance page tells merchants to enter the 30-day low into the compare-at field themselves; the theme cannot look it up, because there is nothing to look it up in. A merchant on the Shopify forum put it in one line in July 2026: the compare-at price "doesn't store any history. It's just a number you type in and overwrite next time." Anything that keeps the history is built alongside the shop: a message the shop sends out on every price change, a database that stores it, and a scheduled task that reads it back.
Thirty days before the sale, frozen when it starts
The counting has two traps, and tools built for this rule get both of them wrong often enough that they are worth naming. The first is the window itself: the directive asks for the lowest price in the 30 days before the promotion, not the lowest price in the last 30 days. A window counted from today, during the sale, contains the sale price and returns it, which reads as no reduction at all. The second trap is what happens once the sale is running.
Once a reduction begins, the Commission's guidance says, the prior price "remains the lowest price applied during at least 30 days before the price reduction" for as long as it runs; for successive campaigns, "'Black Friday', 'Cyber Monday' or Christmas sales campaigns in November/December", each new reference includes the reduced price of the previous one. The period is at least thirty calendar days, and there is one history per sales channel.
Where the price history has to live
A sale scheduler that writes a compare-at price has to read a log first, one the platform does not keep: product, price, timestamp, channel, appended on every change. Your systems keep the sales history already; the orders your report does not know about belong to the same family, and price history is one more column of the same kind. Once two tools can write the price, which system owns the field decides whose number the log records. The automation that switches a sale on gets a second step, looking up the lowest price of the 30 days before the start date, and where that log should sit is the kind of map a process audit draws.
Outside the EU the tests differ
The UK guidance for traders, updated in April 2026, asks "whether the average consumer would think it is a fair comparison" and sets no fixed window. The US FTC guides accept a former price that was "the actual, bona fide price at which the article was offered to the public on a regular basis for a reasonably substantial period of time". Both catch an inflated "was" price, and the fixed 30-day window is the EU's own addition.
Where this leaves you
Before the next sale goes live, one check is worth making.
What is in the compare-at field, and who typed it? If it is last week's price, it may be higher than the reference the law names, and without a place to look up the lowest price of the 30 days before the sale started, nobody can check the percentage on the badge, you included.
If you would rather have someone map where your prices are written, by which tools, and where a 30-day low could be looked up before a sale starts: that is part of the process audit. $299, three business days, and a written map you keep whether or not you hire us.
Nothing here is legal advice.
Related: the register of what is automated is where the sale scheduler and the price log both belong, and who answers when your automation is wrong applies to a badge as much as to a message.