HOW TO TEST
How to Test a Checkout Flow.
E-Commerce A complete testing guide for e-commerce checkout: cart validation, discounts, address handling, shipping methods, tax, payment success and failure, order confirmation, inventory updates, refunds, guest vs authenticated checkout, and API-layer validation.
Checkout is the highest-revenue-risk surface in any e-commerce application. A bug that prevents payment, double-charges a customer, or fails to decrement inventory can cost thousands of dollars per hour. This guide covers the complete checkout surface: cart state and quantity validation, coupon and discount application, shipping address and method selection, tax calculation, payment processing (success, decline, network failure, cancellation), order confirmation emails, inventory decrements, guest and authenticated checkout, refunds, and API-level idempotency. All test cases are written for a Cypress/Playwright engineer with access to a sandbox payment gateway.
Risks
Double-charge on payment retry
If the payment POST is not idempotent and the user retries after a network timeout, two charges can be created for the same order. Without server-side deduplication, the customer is charged twice and inventory decremented twice.
Inventory not decremented — overselling
If inventory is decremented only after payment confirmation (not atomically), a race condition between two concurrent checkouts for the last item in stock can sell the item to both customers.
Price tampering via API
If the client sends the item price in the checkout POST body and the server uses it without re-verifying against the product catalogue, an attacker can set the price to $0.00 or $0.01.
Coupon codes stackable or reusable beyond their limit
Discount codes that can be applied multiple times in the same checkout (stacking beyond intent) or reused after their single-use limit is exhausted result in unintended revenue loss.
Tax calculation incorrect for cross-border or exempted customers
Tax is jurisdiction-specific. Incorrect tax for international orders or for tax-exempt customers (B2B, non-profit) can create legal liability and customer disputes.
Order created without payment confirmation
An asynchronous webhook from the payment provider may arrive before the synchronous response in some failure modes, or a race condition can mark an order 'paid' before the payment is actually confirmed, shipping goods for free.
Guest checkout data persisted insecurely
Guest checkout collects sensitive PII (name, address, card details passed through the UI). If this data is logged, stored in plaintext, or linked to a weak session, it can be accessed by subsequent users on shared devices.
Test Scenarios
Successful authenticated checkout with credit card creates an order
CriticalfunctionalFully automatedComplete end-to-end checkout as a logged-in user with a test card. Assert order is created, confirmation email sent, inventory decremented, and order appears in the user's order history.
Guest checkout completes without an account
CriticalfunctionalFully automatedComplete checkout without logging in. Assert order is created, confirmation email sent to the provided email, and no user account is created unless explicitly offered.
Declined card shows a clear error and preserves cart state
CriticalnegativeFully automatedUse a test card that triggers a decline. Assert an error message is shown (not a 500), the cart is preserved, and no order or charge is created.
Network error during payment does not double-charge
CriticalnegativeFully automatedSimulate a network timeout after the payment POST is sent (drop the response). Re-submit the same checkout. Assert only one charge is created (idempotency key enforced).
Valid coupon code applies correct discount
HighfunctionalFully automatedApply a valid coupon at checkout. Assert the discount is reflected in the order total before payment, the correct amount is charged, and the coupon is marked used (if single-use).
Invalid or expired coupon code is rejected
HighnegativeFully automatedApply an expired coupon, a non-existent code, and a used single-use code. Assert each is rejected with a clear error and the order total is unchanged.
Inventory is decremented after successful payment
CriticalfunctionalFully automatedBefore checkout, record stock quantity for the item. After successful payment, assert the stock is reduced by the purchased quantity. Verify via the admin API or database.
Server ignores client-submitted price and uses catalogue price
CriticalsecurityFully automatedIntercept the checkout POST request and modify the item price to $0.01. Assert the server charges the correct catalogue price and returns an error or corrects the amount.
Order confirmation email contains correct order details
HighfunctionalFully automatedAfter successful checkout, assert a confirmation email arrives with correct: item names, quantities, prices, shipping address, and order reference number.
Cart quantity updates reflect in checkout total
HighfunctionalFully automatedUpdate item quantity in the cart (increase, decrease, remove). Assert the checkout summary and the payment amount update correctly before and after proceeding to payment.
Missing required address fields block checkout
HighnegativeFully automatedAttempt checkout with each required address field omitted. Assert the form blocks progress and shows a field-level error message.
Refund reverses payment and restores inventory
HighfunctionalFully automatedTrigger a full refund via the admin panel or refund API. Assert the payment is reversed in the payment gateway, the order status changes to 'refunded', and inventory is restored.
Checkout form is keyboard-operable and screen-reader accessible
HighaccessibilityManual onlyTab through the entire checkout flow. Assert all form fields have visible labels, error messages are associated via aria-describedby, and the payment button is reachable and operable via keyboard.
Detailed Test Cases
Preconditions
- At least one in-stock product in the catalogue
- Authenticated test user with a saved address
- Stripe test card: 4242 4242 4242 4242 (or equivalent sandbox card)
- Test inbox accessible via API
Steps
- 1.Add one unit of an in-stock product to the cart via API (POST /api/cart)
- 2.Navigate to /checkout
- 3.Assert order summary shows correct item name, quantity, and price
- 4.Select or confirm shipping address
- 5.Select a shipping method — assert total updates with shipping cost
- 6.Assert tax line is displayed (if applicable to the jurisdiction)
- 7.Enter test card details: 4242 4242 4242 4242, exp 12/26, CVV 123
- 8.Click 'Place order'
- 9.Assert redirect to /order-confirmation/{orderId}
- 10.Assert confirmation page shows order reference, items, and total
- 11.Poll test inbox — assert confirmation email arrives within 60 s
- 12.Assert email contains: order reference, item list, shipping address, total charged
- 13.Assert inventory for the purchased item decremented by 1 via GET /api/products/{id}
Expected result
Order created, charge processed, confirmation email received, inventory decremented.
Test data
- Card: 4242 4242 4242 4242 / 12/26 / 123
- Product: first in-stock item from API
- Quantity: 1
Edge Cases
Item goes out of stock between cart add and checkout
A user adds an item to their cart, but it sells out while they are filling in payment details. The checkout POST should return a clear 'item no longer available' error, not a silent failure or a generic 500.
Price changes between cart add and checkout
If the price of an item increases after a user adds it to their cart, the checkout should use the current price (not the cart-cached price), and the user should be notified if the price changed.
Cart contains a mix of in-stock and out-of-stock items
The checkout flow should clearly communicate which items cannot be fulfilled and offer options: remove out-of-stock items and continue, or wait for restocking.
Applying a coupon that brings the total below zero
A coupon for $20 off applied to a $15 order. The final amount must be $0 (not a negative charge or a credit).
Back button during 3DS authentication cancels the payment
If a user presses Back during a 3D Secure authentication popup, the payment is cancelled. The application should detect the cancellation and return the user to the payment form cleanly.
Browser tab closed mid-payment after the charge was captured
If the browser is closed after the payment gateway confirms the charge but before the success redirect completes, the order exists in the payment system but not in the application. The webhook must create the order record.
Checkout with maximum cart quantity
Adding 999 of an item that has a max-quantity restriction of 5 per order. The server should enforce the limit, not the UI alone.
Checkout with a PO box address rejected by some shipping carriers
Some shipping methods (e.g. UPS, FedEx) cannot deliver to PO boxes. The address validation should reject these combinations with a clear carrier-specific message.
Payment for exactly $0.00 (free order via 100% coupon)
A 100% discount coupon makes the order free. The payment step should be skipped or handled as a $0 charge. Attempting to process a $0 payment with some gateways returns an error.
Two separate sessions with the same last-in-stock item in cart
Two users have the same last-in-stock item in their cart. The item display in the cart may show 'in stock' for both, but only one checkout should succeed.
Automation Ideas
Payment sandbox E2E matrix
Drive a table of Stripe test cards (success, decline, insufficient-funds, 3DS required) through the checkout flow, asserting the correct outcome for each. Run in CI against a staging environment with a real Stripe test-mode account.
Tools: playwright, cypress
Idempotency key assertion via network interception
Intercept the checkout POST, extract the idempotency key, send a duplicate POST with the same key, and assert only one charge appears in the gateway. Uses Playwright's route interception.
Tools: playwright
Price integrity sweep via request tampering
Use Playwright to intercept checkout requests and replace price values with $0.01. Assert the server rejects or corrects the price. Run as a security regression suite on every release.
Tools: playwright, burp-suite
Concurrent checkout race condition test
Fire two simultaneous checkout API calls for the last-in-stock item using parallel Promise.all() in Playwright. Assert inventory never goes below 0 and exactly one order succeeds.
Tools: playwright, k6
Inventory snapshot before/after checkout
Before each checkout test, snapshot inventory levels via GET /api/products. After checkout, assert each purchased item's stock decreased by exactly the purchased quantity.
Tools: playwright, cypress
Checkout accessibility audit
Run axe-core against each checkout step (cart, address, payment, confirmation). Assert zero WCAG 2.1 AA violations. Combine with a tab-order assertion.
Tools: axe-core, playwright, pa11y
Coupon expiry and reuse edge case matrix
Create a test matrix of {couponType, couponState, expectedOutcome}: valid/single-use/multi-use/expired/non-existent. Drive it through the apply-coupon endpoint and assert correct responses.
Tools: postman, playwright
Common Bugs
Order created before payment confirmed — free order exploit
When an order record is written synchronously with the checkout POST but payment confirmation arrives asynchronously (webhook), a delayed or missing webhook can leave an order in 'payment_pending' that the fulfilment system treats as paid.
Impact: Goods shipped or digital products delivered without actual payment.
Coupon discount not reflected in the charged amount
The UI shows the discounted total, but the payment request is sent with the original pre-discount amount. The gateway charges full price; the coupon is cosmetic only.
Tax recalculated after payment — total mismatch
Tax is recalculated server-side at order confirmation using a different rate than was shown at checkout (e.g. rate updated between the two steps), causing the charged amount to differ from what the customer approved.
Inventory decremented on payment failure
A failed payment still triggers inventory decrement, gradually reducing stock for orders that never completed. Hard to detect without monitoring inventory vs. order totals.
Back button after payment re-submits the form
The checkout confirmation page is not a redirect (POST response, not POST-redirect-GET), so the browser's back-then-forward navigation re-submits the payment form, triggering a duplicate charge attempt.
Guest email used to create a user account silently
Guest checkout silently creates a user account with a random password, sending the customer an unexpected account-creation email. They cannot log in but their data is now in a user record.
Shipping cost not included in payment charge
The order total displayed includes shipping, but the payment API call uses only the subtotal. The merchant absorbs shipping cost without knowing it.
Order confirmation email sent for declined payments
An async email job fires on order creation (before payment confirmation), so customers receive a 'your order is confirmed' email even for declined cards.
Currency rounding errors cause $0.01 discrepancy
Float arithmetic on subtotal + tax + shipping produces a total that is $0.01 off the expected value. Some payment gateways reject amounts that don't match their records to the cent.
Useful Tools
End-to-end checkout flows, network interception for price tampering and idempotency tests.
Checkout E2E with cy.intercept for network stubbing and cy.request for API payment assertions.
Concurrent checkout load tests — race conditions, inventory limits under simultaneous purchases.
API-layer checkout tests: price tamper, coupon abuse, refund flows, and webhook simulation.
Intercept and modify checkout requests for price tampering, coupon stacking, and parameter injection.
WCAG 2.1 AA audit on each checkout step — labels, focus management, error announcement.
Generate realistic shipping addresses, names, and emails for checkout test data.
Stub payment gateway responses to simulate declines, timeouts, and 3DS flows in isolation.
// Related resources