Skip to main content
Test Design5 min readQAEverest Team

What happens to your data when payment fails halfway?

The card was declined, but the order was saved and the stock went down. Here is what a transaction is in plain words, three situations worth testing on purpose, and how to check the data was really rolled back.

A customer adds a pair of shoes to the cart and taps Pay. Their bank declines the card. The app shows a friendly message: "Payment failed. Please try again."

Everything looks fine.

But behind the screen, three things already happened before the card was declined. The order was saved. The shoes were taken out of stock. A shipping slot was reserved.

Nobody paid. The shoes are now "sold". And the warehouse has a ticket for an order that should not exist.

This is what a payment that fails halfway can leave behind. The app is happy. The data is not.

First, what is a transaction?

A transaction is a group of database steps that must succeed together or not at all.

Think of moving money between two bank accounts. Take 100 out of one account. Put 100 into the other. If the second step fails, the first one must be undone. Otherwise 100 simply disappears.

A transaction gives you that promise. Either every step is saved, or none of them is. Going back to the starting point is called a rollback.

When the rollback works, a failed payment leaves no trace. When it doesn't, you get a half-saved order.

Why this bug hides so well

A failed payment is a normal event. Cards are declined every day. So most tests check the message the user sees, and that part works.

The leftover data doesn't show on any screen. It shows up later, in other places:

  • Stock numbers that don't match the shelf.
  • Orders that never ship, because nobody paid.
  • Reports that count sales that never happened.

By then it is hard to trace back to one failed payment. So the best time to catch it is in a test, on purpose.

Three situations worth testing on purpose

1. A failure halfway through

This is the shoes story. Make the payment fail on purpose, in a test environment, then check the database.

What to checkWhat you expect
Number of orders for that customerUnchanged, or one order clearly marked as failed
Stock for that productSame as before
Payment records for the orderNone
Order items without an orderNone

2. A double click

The customer taps Pay, nothing seems to happen, so they tap again. Now the app gets the same request twice.

You expect one order and one charge. Check the database:

  • SELECT COUNT(*) FROM orders WHERE cart_id = the cart you used should return 1.
  • There should be one payment, not two.

3. Two buyers at once

Only one pair of shoes is left. Two customers tap Pay at the same moment.

One should succeed. The other should be told the item is gone. Afterwards the stock must be 0, never minus 1, and there must be exactly one order for that item.

This one is harder to test, because timing matters. It is also the one that shows up only on busy days, which is why it is worth the effort.

A simple recipe for any of the three

Every one of these tests has the same shape:

  1. Note the starting point. Read the number of orders and the stock level before you start.
  2. Trigger the situation. Send the failing payment, the repeated request, or the two requests together.
  3. Read the database. Look at the same numbers again, plus the related tables.
  4. Compare. Did the data end up where it should?

The important part is step 3. The screen can't tell you the answer. Only the data can.

A quick checklist for any payment flow

  1. After a failed payment, is there any order, payment or reserved stock left behind?
  2. After a repeated request, is there still only one order and one charge?
  3. After two buyers at once, is the stock still 0 or higher?
  4. After each test, is the test data cleaned up?

Where QAEverest helps

QAEverest can help with the database half of these tests.

In an API suite, you send the calls yourself: the order, then the failing payment. Then add a Database step that reads the tables and checks the result. Save the order id the API returned, and query the row it should or should not have created.

If you don't want to write the query from scratch, click Verify in DB on an API step. The AI drafts the query, and your QA team reviews it before it goes into the suite.

A few things to know:

  • Every query is read-only. A check can look at your data but cannot change it.
  • Each environment uses its own connection, so test checks stay on the test database.
  • Nothing the AI drafts goes into a suite until your QA team confirms it.

You can also choose Database as the type of testing and pick a saved connection. The test cases use your real tables and columns, and one of the six areas is transactions and concurrency. Those cases are written for your QA team to review and run by hand.

The short version

A failed payment is not an edge case. It happens every day.

What your app does with the data in that moment decides whether the numbers can be trusted tomorrow.

Pick one payment flow this week. Make it fail on purpose. Then look in the database, not on the screen.

See it on your own stories

1000+ free credits on signup, no card required — roughly 100 test-case generations before you pay for anything.

Start free