How to test discount code checkout: the promo rules, not the happy path
A discount code isn't a text box, it's a rules engine — stacking, minimum-order thresholds, expiry timezones, per-customer limits, and the 100%-off edge. Here's how to test discount code checkout end to end in plain English, no scripts.
How to test discount code checkout: the promo rules, not the happy path
Applying a valid discount code and watching the total drop is the demo. It's also the one path everyone clicks by hand before shipping, which is exactly why it's never where the money leaks. The bugs that cost you real revenue live in the rules around the code: the two codes that were never supposed to stack but do, the "20% off orders over $50" that fires on a $40 cart, the coupon that expired at midnight in a timezone that isn't your customer's, and the 100%-off code that sails past a payment step it should have skipped — or worse, charges shipping on a "free everything" order.
A discount code field looks like a text box. It's actually the front door to a small rules engine: eligibility, thresholds, limits, expiry, stacking, and the interaction between the discount and every other number on the invoice — tax, shipping, the payment total. Testing it means testing the rules, not the reduction. This is the playbook for doing that end to end without writing a line of Playwright, and it deliberately doesn't overlap with testing the payment step itself — that's the card and the charge; this is everything that decides what you charge.
What "testing a discount code" actually has to cover
The valid-code-drops-the-total case is one line. The rules engine underneath it is where the surface is:
- Invalid, expired, and typo'd codes. A wrong code should be refused with a clear, specific message — not silently ignored, not applied anyway, and not a generic 500. Test the expired code, the never-existed code, the right code with a trailing space, and the right code in the wrong case. Each should fail honestly.
- The minimum-order threshold, on the correct base. "Over $50" raises the question nobody wrote down: fifty dollars before tax, or after? Before shipping, or after? Test a cart one cent under the threshold (refused) and one cent over (accepted), and confirm the number the rule checks against is the number the business actually meant.
- Per-customer and global usage limits. A single-use code should work once and be refused on the second attempt by the same account. A "first order only" code should refuse a returning customer. A globally-capped code should stop accepting once the cap is hit. These are the codes that turn into a Twitter thread when they don't enforce.
- Stacking. Can a customer apply two codes? Should they be able to? Whatever the intended answer, test it directly — apply one, then a second — and confirm the app does what the business decided, not what the form happens to allow. Unintended stacking is a discount you didn't budget for, multiplied by everyone who finds it.
- Expiry, at the boundary and across timezones. A code that ends "August 31" ends when, exactly, and in whose clock? Test it just before and just after its stated cutoff, and be explicit about the timezone, because "expired for me but not for a customer three zones east" is a real and confusing support ticket.
- The math, all the way to the payment total. The discount has to survive being combined with tax and shipping and still produce a total that's internally consistent. Percentage-off should compute against the right subtotal; amount-off should never drive a line — or the order — negative. The number the customer approves must be the number they're charged.
- The 100%-off and free-shipping edges. A code that zeroes the order is the single nastiest case: does the flow correctly skip or short-circuit the payment step, or does it ask for a card to charge $0 and choke? And does "free shipping" actually zero the shipping line, or just the item subtotal while shipping quietly rides along?
Most teams test item 1's happy sibling — valid code, total drops — ship, and meet items 2 through 7 as revenue leakage or support tickets. "Someone stacked two codes and got the order for free" is not a bug you find by clicking the happy path once; it's a bug you find by trying to break the rules on purpose, every time, which is precisely what an agent does the same way on every run — the reason an agent catches what a script waved through.
Nothing below references a field ID, a selector, or a DOM path. A checkout gets redesigned constantly — the promo field moves, the cart summary gets rebuilt — and a test pinned to today's markup rots on the first redesign. Describing the rule instead of the element is why this approach outlives the checkout it tests.
Prompt 1: the invalid and expired codes, refused honestly
Case 3 is the quiet one. Trailing whitespace and casing are the two things a human tester never types but a real customer does constantly, pasting from an email. The intended behavior is usually "trim and normalize, then accept" — but plenty of checkouts refuse the code the customer clearly meant, and that's a conversion bug wearing an input-validation costume. The agent reads the actual rendered message, so "refused with a helpful message" and "refused with a red box that says Error" come back as different outcomes instead of one failed assertion.
Prompt 2: the minimum-order threshold, on both sides of the line
Step 3 is the finding that saves an argument later. "Orders over $50" is ambiguous by construction, and different parts of a codebase often disagree about the answer — the frontend gates on subtotal, the backend re-checks against post-tax total, and a cart sitting exactly on the seam gets accepted by one and rejected by the other. An agent that reports the actual base it observed turns "it feels off" into "the code applies at $50.00 subtotal but the business rule was post-tax, so a $48 cart with tax qualifies when it shouldn't." That's a spec bug you want surfaced in staging, not discovered by an accountant.
Prompt 3: usage limits and unintended stacking
Step 3 is where the money is. Most discount systems are tested for "does the code work," never for "can the customer apply two." The form usually lets you try, and whether the second code stacks, replaces, or bounces is a decision that frequently exists only as an accident of implementation. If the business meant "one code per order" and the form silently allows two, you've shipped a discount you didn't authorize — and the customers who find it will tell each other. Testing the stack directly, every run, is the same discipline that makes an agent-driven multi-step flow catch the leftover-state bugs a linear script never reaches.
Prompt 4: the math to the payment total, and the 100%-off edge
Step 3 is the edge that breaks in production because nobody builds a $0 cart by accident during manual testing. A fully-discounted order is a genuinely different code path: the payment step should be skipped, but a checkout that always assumes a charge will happily render a card form for a $0 total and then fail when the payment processor rejects a zero-amount charge — or, the other direction, complete the order but still bill shipping on something advertised as free. The agent reads the final total and the network activity, so "the confirmation screen looked free" and "the charge was actually zero" come back as separately-verified facts instead of one screenshot of a happy page.
Reading the results
Each run comes back as a Monito Session, not a pass/fail bit: a screenshot timeline of the cart at every step, the network log with the real totals and the final order request, console output, and the agent's reasoning wherever it made a judgment call about whether a total reconciled. For discount testing the two documents to read are the cart-summary screenshots — because the bug is usually a number that's individually plausible but doesn't add up with the others — and the final order request, which is the only place the "confirmation said free, invoice said shipping" gap is actually visible.
The prompts survive your redesigns because none of them names a field or a selector — only the rule being tested. Move the promo box, rebuild the cart summary, restyle the whole checkout: the rule didn't change, so the prompt didn't either. That durability is the whole argument for describing intent over recording clicks, and it's why a prompt is a better long-term artifact than a spec for anything with a UI that moves.
The one to run right now
If you run a single check today, run the stacking-and-total test — it catches the two most expensive discount bugs at once (an unintended stack and a total that doesn't reconcile to the charge):
Save it as a Test Scenario, pin it to your staging Project, and wire it into CI against every deploy that touches cart, pricing, or promotions — the surfaces where a discount rule breaks quietly and expensively. A full run is typically 8–13 credits — roughly $0.08–$0.13 — and your first run is free. Point it at staging, paste the prompt, and read the invoice. If two codes stack when they shouldn't, you'd rather find out from an agent than from your customers.