// retail and e-commerce

The customer was charged. The order was never created. We test what happens after the click.

Retail defects hide in the gaps between systems: payment and order, discount and delivery, old shop and new one. We break those seams before peak does, with QA tools we built ourselves. On a platform migration two hundred automated flows found the basket still on the old platform.

Start a one-month trial
ISO 27001:2022teams at Sephora, McDonald's and NestleReviewed on Clutch€20-45/hour
The checkout bug I still think about: the total showed zero when the customer picked a different shipping country. On a release day I run the edge cases first, at phone width, and give the green light only after a sanity check on production.
Cristian Dragota, QA engineer on e-commerce and online pharmacy platforms
// what keeps you up at night, and what you get
"Checkout breaks on our biggest sales day."
Checkout, promotions and refunds tested end to end before every release, including the payments that stop halfway.Flows
"We migrate platforms and orders, promotions or search rankings quietly break."
Migration testing that compares the old shop with the new one, page by page and order by order.BugBoard
"An accessibility complaint turns into a legal problem."
Whole journeys checked for accessibility, ready for the European Accessibility Act.Auditi
// four places retail systems break
01 · payment gap

Charged, but no order exists

With automatic capture on, the shop authorises the card, then fails to create the order because a stock lock times out. The customer sees an error and tries again. Minutes later the payment provider's capture webhook arrives for a reference the shop has no order for, and the handler drops it without a trace.

also: partial refund on a split-tender order (voucher + card)
browsershoppspordersPOST /checkoutauthorise 84.50authorisedcreate orderstock lock timeout500 please retrywebhook: captured 84.50no order for ref, ignored
FAILcustomer charged 84.50, no order, no email
how we test itFail the order write after a successful authorisation, then deliver the webhook. Assert the payment is voided or the order is created, never neither.
psp.authoriseOk(); orders.failNext('create', 'lock timeout')
const res = await checkout(basket, card)
await psp.deliverWebhook('captured', res.ref)
const captured = psp.captures(res.ref)
expect(captured.length === 0 || orders.exists(res.ref)).toBe(true)
02 · promotions

Two discounts, two totals for one basket

A 20 percent code and a 10.00 voucher give a different total depending on which is applied first, and the order comes from configuration insertion order, not from a rule. The free delivery threshold is then checked against the pre-discount total, so a basket that ends up below it still ships free.

promotion engines are combinatorial and change weekly
basket600020% then 10.00 off380010.00 then 20% off4000delta200free delivery from5000threshold checked on6000
rule: percentage first3800threshold on 3800delivery charged
FAILsame basket, two totals
FAILfree delivery below threshold
PASSafter the fix
how we test itGenerate the matrix from the live promotion set whenever the configuration changes. Assert the rules, not individual outcomes, and shuffle application order.
for (const combo of combos(promotions.live(), { maxSize: 3 })) {
  const totals = permutations(combo).map(p => price(basket, p))
  expect(new Set(totals).size).toBe(1)        // order must not matter
  expect(delivery(price(basket, combo))).toBe(
    subtotalAfterDiscount >= FREE_FROM ? 0 : fee)
}
03 · inventory

The last unit is sold twice

A unit is held for 10 minutes while the customer pays. A shopper stuck on the 3-D Secure page passes the 10 minute mark, the sweeper releases the unit, and a second shopper buys it. The first one returns from the bank page, the payment completes and the order is confirmed without checking stock again.

also: reservations released by the sweeper but not by the cart
A holds1 left
B views0 left
10:10A on 3DS
B pays1 left
A backpaid
ORDER OKstock -1, oversold
how we test itMove the clock past the hold while a payment is pending, let a second buyer through, then complete the first. Assert stock never goes below zero.
await reserve('SKU-1', buyerA)               // last unit
const pay = await startPayment(buyerA)        // parked on 3DS
await clock.advance({ minutes: 11 })
await buy('SKU-1', buyerB)
await pay.complete()
expect(stock('SKU-1')).toBeGreaterThanOrEqual(0)   // got -1
04 · migration

The new shop forgets the old URLs

After a replatform the redirect map covers each old product path exactly, but email links and ads carry tracking parameters. The rule matches the full string, so those URLs return 404 and every campaign click lands on an error page. Customer accounts and order history need the same comparison, not just the catalogue.

also: accounts without a migrated password hash, orders without invoices
old /p/dark-truffle-box-250g new /shop/truffles/dark-truffle-box 301 matched, kept old /p/dark-truffle-box-250g?ref=mail 301 not matched 404 customer lands on an error page
404every tracked old URL is dead, silently
how we test itCrawl the old shop's full URL list, including parameter variants, and compare response, target and content with the new shop. Compare orders and accounts the same way.
for (const url of oldShop.urls({ withParams: true })) {
  const res = await fetch(url, { redirect: 'manual' })
  expect(res.status).toBe(301)
  expect(await finalUrl(res)).toBe(redirectMap.expected(url))
}
expect(newShop.orderCount()).toBe(oldShop.orderCount())

Illustrative examples built from defect patterns we test for. Not client data.

// on every pull request

Your code is written in hours. Our pipeline keeps up.

Pull requestfrom your coding agent
Flowsjourneys and fault injection
Security Toolkit15 scanners + 20 own checks
QA engineerconfirms every finding
BugBoardrelease report, Jira sync
// the regulation, in testing terms
regimewhere it actually breakswhat we run
PCI DSSCard data in logs and fixtures, and scripts on the payment pagePAN scans on logs and exports, inventory of payment page scripts
European Accessibility ActApplies to e-commerce since 28 June 2025; one blocked checkout step blocks the saleWCAG AA journey audits against EN 301 549
GDPR consentTags and pixels firing before the visitor has chosennetwork capture before and after the consent choice
PSD2 SCAThe bank challenge page is where baskets time out and stock is lost3-D Secure paths, abandoned challenge, return after expiry
// proof

Checkout, migration and regression work

453 test cases moved into Flows3 checkout flows, weekly regression3-month migration pilot
  • A UK chocolate retailer: end-to-end testing of its move to Salesforce Commerce Cloud, across browsers and phones.
  • A Berlin food e-commerce brand: a three-month pilot through a platform migration.
  • An Italian online pharmacy: 453 test cases moved from a paid tool into Flows.
  • Weekly regression of three checkout flows for an online services retailer.
  • Teams at Sephora, McDonald's and Nestle, and Avilabs.
  • METRO: store systems, tested since 2025.
  • An e-commerce platform vendor: shop migrations and checkout, since 2024.

Client names withheld except where already public.

BUG-2204 · checkout / orders · examplehigh

Customer charged when order creation fails after authorisation

  1. Fill the basket with the last unit of a product
  2. Pay by card; make the stock lock time out during order creation
  3. Provider webhook reports the payment captured, no order exists

Fix: Void the authorisation when order creation fails, and reconcile provider captures against orders every hour.

REQ-CHK-12TC-2204 addedJira SHOP-1187

A one-month trial on your checkout and promotions. The first two weeks are complimentary if you're not happy.

Start a one-month trial
// in depth: the long version
// 01

Checkout, which is the only page with a cost per minute

A BetterQA engineer working on a MacBook by the window in the office

Every other defect is expensive in a way you argue about afterwards. A broken checkout has a measurable cost while it is broken.

It is also the most variable page you own. Card, wallet, buy now pay later, gift card, voucher, store credit and split tender across two of them. Guest against account. Address validation and the addresses it rejects that are real. Delivery slot and its expiry. Tax and duty for a cross-border order. A payment that is authorised and then fails to capture.

The test that is almost always missing is the partial failure: the payment succeeded, the order did not get created, and the customer has a charge and no confirmation email. That path exists in every implementation and is exercised in almost none of them.

// 02

Promotions, which is where retail bugs actually live

Stacking rules, exclusions, thresholds, category overlaps, a voucher that combines with itself, a percentage discount applied after a fixed one instead of before, and the free delivery threshold calculated on the pre-discount total.

Promotion engines are combinatorial and the combinations change weekly, so scripted regression falls behind by design. The approach that holds is a generated matrix over the live promotion set, run automatically whenever the promotion configuration changes, asserting the rules rather than the individual outcomes.

// 03

The order pipeline after the order

Inventory reservation and timeout, oversell and what happens when it occurs, click and collect, ship from store, split shipment across three fulfilment points, and the partial refund of an order that was split.

The refund and returns path carries the same money as the order path and gets tested roughly a tenth as often. Returning one item from a split-shipment order that used a voucher, a gift card and a card is a sentence that describes a real customer and an untested code path.

Omnichannel adds the seams: an online order returned in store, loyalty points earned on one channel and spent on another, and the customer who exists twice because they used a different email at the till.

// 04

Stores, and the offline case

A BetterQA engineer at a standing desk in the open office, seen through the glass wall

Point of sale is software with a queue of people standing in front of it. The defect that matters most is not a functional one, it is what the till does when the connection drops. Offline mode, queued transactions, the reconciliation when it comes back, and duplicate postings from transactions that went through twice.

It is almost never in a test plan, and it is the one that becomes a business continuity incident.

// 05

Scale, peak and the things that degrade

A catalogue of tens of thousands of pages means defects are statistical rather than individual. You sample, and then you report which URLs were affected rather than a percentage on its own. We hold the URL list behind every aggregate figure we give you, because the first question anyone sensible asks about "31 percent of sampled product pages" is which 31 percent, and re-sampling to answer it wastes a day.

For peak, the number is p95 at load rather than an average in a quiet window, and the usual cause of checkout degradation is not your application at all but third party tags loading in front of it. See performance testing and load and performance testing.

Across the whole estate: web testing, mobile app testing and localisation testing for currency, tax display, address formats and right to left layouts.

// 06

Accessibility, which became a deadline

The European Accessibility Act has applied to ecommerce since 28 June 2025, which moved accessibility out of the backlog and into the same category as tax and payments. Assessments are made against WCAG AA, reached through the harmonised standard EN 301 549.

A retail site is also unusually exposed here, because the journey is long and a single inaccessible step in checkout blocks the whole transaction rather than degrading it. We audit against the real journey rather than page by page, using Auditi, our own auditing tool, which is included in the engagement. See WCAG compliance audits.

// 07

How we start, and what it costs

A one-month trial. We embed, learn the estate, write tests and file real bugs against your actual software. The first two weeks are complimentary if you are not happy with what we deliver. You decide whether to carry on at the end of the first month.

After that, €20 to €45 an hour depending on seniority, commitment length and the mix of manual, automation and specialist work. Longer commitments move down that range.

Included at no licence cost: BugBoard for test management, Flows for automation where an engineer approves each selector fix when a template changes, Auditi for GDPR, WCAG and FDA auditing, BetterFlow so you can see where the hours went, and our AI Security Toolkit.

Our engineers are in Cluj-Napoca, Romania, on GMT+2 and GMT+3 in summer, covering the European trading day and the start of the US East Coast one.

// 08

Talk to us

Tell us what you sell, where peak falls for you, and what broke last time. We will tell you what is worth doing before the freeze and what should wait until after it.