A test transaction card is a card number that lives only inside a payment processor sandbox. You run a charge, the gateway answers with a success or a decline, and no bank, cardholder, or settlement network is involved. Once you switch to live API keys, those numbers stop working. That one rule, test numbers only in test mode, explains most of the confusion people hit on day one.

Where test card numbers come from

Every processor publishes its own set. Stripe's classic is 4242 4242 4242 4242 with any future expiry and any three digit CVC. Adyen, Braintree, Checkout.com, and Worldpay each ship a different list, and none of them are interchangeable. A Visa test number from one gateway means nothing at another, because the sandbox matches against its own BIN table, not against real issuer ranges.

That is also why a Luhn check is not enough. A number can pass the checksum and still get rejected, because routing happens on the first six to eight digits. Use the published list. Do not invent a number and expect it to behave.

The fields worth testing

  • Card number: one success number plus at least three decline numbers.
  • Expiry: any future date, but pin it in your fixtures so tests do not rot.
  • CVC: usually any three digits, four for Amex test cards.
  • Postal code and street: some sandboxes return AVS mismatch results on purpose when you send a specific ZIP.
  • 3D Secure: processors publish separate cards that force a challenge, a pass, or a failure. That is the only honest way to test the redirect and return path.

Test numbers that trigger specific outcomes

Stripe, for example, uses 4000 0000 0000 0002 for a generic decline, 4000 0000 0000 9995 for insufficient funds, and 4000 0000 0000 3220 to force 3DS authentication. Others follow the same idea with different digits. Build a small table of outcomes in your test suite instead of hardcoding one card and hoping.

Why a test transaction fails when it should not

  1. Live keys paired with a test number. The charge gets declined for an obvious reason.
  2. An expiry date that passed since you wrote the fixture two years ago.
  3. Currency or country mismatch. Some test cards only behave in USD.
  4. Reused idempotency keys returning a cached response from an earlier run.
  5. Fraud rules still switched on in the sandbox, blocking the very card you need.

Never test with a card you do not own

It is tempting to grab a real number to see how the live flow behaves. Do not. Running a charge on someone else's card, even for a cent, is card fraud in most jurisdictions, and PCI DSS rules forbid live card numbers in test systems at all. If you need production verification, use your own card for a small amount and refund it, or use the processor's live verification tools.

Before you ship

Run the happy path, each decline type, a 3DS challenge, a timeout, and a duplicate submit. Log the processor response codes, not just pass or fail. When those five cases behave the same way in the sandbox, you are ready for real traffic.