Quality Mart — Storefront Playground
Quality Mart — Online Store
A fictional retail storefront. Browse the catalog, add items to your cart, apply a coupon, and check out with a real state-transitioning order flow. Every planted defect is a retail-realistic business-logic bug — pricing, inventory, coupons, checkout retries, and cart/order ownership — not a cosmetic one.
Products
Loading products…
Cart
Loading cart…
Checkout
Investigate the API
Hit the Quality Mart REST API directly — same requests the storefront above makes, plus anything else in its own catalog. Runs from your browser using your own session; nothing here can reach another app or reveal the answer key.
API Console
Same-origin only · uses your own sessionHeaders & body (optional)
Known Intentional Issues (12)
Checkout total is computed in floating-point dollars instead of integer cents, producing sub-cent rounding drift
POST /api/ecommerce/checkout with a cart containing qm-1002 x3 and qm-1005 x7 (odd-cent unit prices). Compute the expected total by summing priceCents*quantity in integer cents by hand. Compare to the returned order.totalCents. Actual: off by 1 cent.
Sales tax is calculated on the pre-discount subtotal instead of the post-discount subtotal
POST /api/ecommerce/checkout with a cart and coupon code WELCOME10 (10% off). Compute expected tax as 8% of (subtotal - discount). Compare to the returned order.taxCents. Actual: tax is 8% of the undiscounted subtotal, so taxCents is higher than expected.
Two concurrent add-to-cart requests for the same product on the same cart can both read the same pre-add item and one overwrites the other's quantity instead of summing
Fire two concurrent POST /api/ecommerce/cart/items requests for the same cart + same productId (qty 1 each). Expect the line item's quantity to be 2. Actual: quantity is 1 — one add was silently lost.
Checkout allows an order quantity that exceeds the product's current inventory (oversell)
Checkout a cart containing qm-1004 (inventory 1) with quantity 5. Expect 409 Insufficient inventory. Actual: 201 Created, and GET /api/ecommerce/products/qm-1004 shows inventory: -4.
Two active coupons can both be applied to the same order, stacking discounts beyond what either coupon alone allows
POST /api/ecommerce/checkout with couponCodes: ["WELCOME10", "WELCOME10", "SAVE5"]. Expect 400 (one coupon per order). Actual: 201, and the order total reflects the 10% discount applied twice plus the flat $5 off.
Checkout endpoint accepts an Idempotency-Key header but never enforces it, so a retried request creates a duplicate order and double-decrements inventory
POST /api/ecommerce/checkout twice with the same body and the same Idempotency-Key header. Expect one order to be created. Actual: two separate orders are created and inventory is decremented twice.
GET /api/ecommerce/cart returns another customer's cart when given that cart's id
As qmcust-001 (owns qmcart-001), GET /api/ecommerce/cart?cartId=qmcart-002 (owned by qmcust-002). Expect 403/404. Actual: 200 with qmcart-002's real contents.
GET /api/ecommerce/orders/:id returns order details for an order the session's customer does not own
As qmcust-001, place an order (note its id). As qmcust-002 (a separate session), GET /api/ecommerce/orders/<that id>. Expect 403/404. Actual: 200 with the other customer's real order.
Cart line-item quantity endpoint accepts a negative quantity, which subtracts from the line total instead of being rejected
PATCH /api/ecommerce/cart/items/<line-id> with { "quantity": -3 }. Expect 400. Actual: 200, and the cart subtotal decreases.
Checkout succeeds with an empty cart, creating a $0 order with zero line items
POST /api/ecommerce/checkout for a cart with zero items. Expect 400 Cart is empty. Actual: 201 Created, order.totalCents is 0 and order.items is an empty array.
Product catalog pagination skips a page of results
GET /api/ecommerce/products?page=1&pageSize=3 then page=2&pageSize=3. Expect page 2's first product to be the 4th product overall. Actual: page 2 starts at the 7th product — 3 products (the 4th-6th) are never returned by any page.
Sorting the product catalog by price sorts lexicographically (as strings) instead of numerically
GET /api/ecommerce/products?sort=price. Assert each product's priceCents is >= the previous one's. Actual: the sequence is not monotonically non-decreasing — a $18.99 item appears before a $7.99 item.