Card testing: how the attack runs and what stops it

In short: Card testing is the attack where someone runs a list of stolen card numbers through your payment form to find out which ones are still alive. Small amounts, fast, automated. The damage does not come from the successful charge but from what follows: fees on every declined attempt, a deteriorating authorisation rate, and finally chargebacks. The defence has three layers: rate limiting on your server, Radar rules in Stripe, and 3D Secure on risky requests.

What is card testing?

Stolen card data on its own is worth little, because most lists are full of already-cancelled cards. The attacker first has to find out which numbers are live. For that they need a payment form that accepts a transaction and tells them whether it worked.

Your payment page is exactly that. You are not picked because you are large — you are picked because your payment form is public and has no limits on it.

What does an attack look like?

A typical wave runs like this:

MinuteWhat happensWhat you see in Stripe
0First attempts arrive, every few secondsA few declined payments, “card_declined”
1–3It accelerates: 5–20 attempts per minute, each a different cardDeclines in sequence, always the same amount
3–10A few successful charges appear — these are the live cards1–3 successful payments from unknown buyers
10+The wave ends, or switches IP and continuesAuthorisation rate collapses
1–90 daysCardholders noticeChargebacks, all coded “fraudulent”

The whole thing is machine-driven: no browser, no cart, no customer journey. The request goes straight to the payment endpoint.

What does it cost you?

Not the handful of successful charges — those are typically small, and get charged back in the end.

The real cost is elsewhere:

  1. Every attempt costs money. Declined transactions are billed too, and at thousands of attempts it shows up on the invoice.
  2. Your authorisation rate deteriorates. Card issuers watch what kind of traffic comes from a merchant. After a flood of declines, your genuine customers' payments start failing more often too.
  3. Chargebacks follow. This is the expensive part: every dispute carries its own fee, and your dispute rate worsens.
  4. Your account risk profile suffers. With a persistently high dispute rate the payment provider can suspend the account. Funds are frozen and your customers cannot pay.

How do you recognise it?

  • A sudden spike in declined payments, usually with identical or very similar amounts.
  • Attempts from one IP or a handful of IPs, in quick succession.
  • Many different card numbers with the same customer e-mail — or the other way round.
  • Disposable e-mail addresses (mailinator, yopmail and friends).
  • Night hours, with no other traffic around.
  • The request does not go through your shop's cart, it hits the payment endpoint directly.

What stops it? Three layers

One layer is never enough: a server-side limit can be worked around by rotating IPs, and Radar rules only act once the payment request is in. Together they make the attempt cost more than it is worth.

Layer 1 — rate limiting on your server

The cheapest defence is the one that never reaches the payment provider. In your own application:

  • limit calls to the payment endpoint per IP (say, 5 attempts per 10 minutes),
  • do not allow a payment to start without a cart and a session,
  • put an invisible bot check in front of checkout (Turnstile, hCaptcha),
  • do not return a detailed error explaining exactly why it failed.

This layer absorbs the crudest waves, and it is the only one that does not cost you transaction fees.

Layer 2 — Radar rules

Stripe's built-in machine learning catches a lot, but the card testing pattern is specific enough to be worth an explicit rule. In Radar's rule language they look like this:

`` Block if :card_count_for_ip_address_hourly: > 3 Block if :declined_charges_per_ip_address_hourly: > 3 Block if :blocked_charges_per_ip_address_daily: > 5 Block if :declined_charges_per_card_number_hourly: > 2 Block if :is_anonymous_ip: Block if :is_disposable_email: Block if :cvc_check: = 'fail' Block if :address_zip_check: = 'fail' ``

What these mean, in order:

  • card_count_for_ip_address_hourly — how many distinct cards the same IP has presented in the last hour. This is the clearest fingerprint of card testing: a real customer does not try four cards in an hour.
  • declined_charges_per_ip_address_hourly — how many declines came from the same IP. An attacker necessarily generates a lot of them.
  • blocked_charges_per_ip_address_daily — once blocked, no further attempts for the rest of the day.
  • declined_charges_per_card_number_hourly — the same card number cannot be retried over and over.
  • is_anonymous_ip — a known proxy or Tor exit node. In some businesses this is too strict (privacy-conscious buyers), so introduce it as Review first.
  • is_disposable_email — a throwaway address.
  • cvc_check / address_zip_check — if the issuer says the CVC or postal code does not match, that is a serious signal at payment time.

Two details worth knowing:

Custom rules require the higher Radar tier (Radar for Fraud Teams) — the base tier only gives you the machine learning and the built-in rules. If you do not have that tier today, layers 1 and 3 are still fully available to you.

The counters do not include the payment being processed. On the first attempt in a given hour the counter is still zero, on the second it is one, and so on. That is why writing a rule on > 0 is a bad idea: it would block your own customer who mistyped their card number the first time.

Layer 3 — 3D Secure on risky requests

3D Secure pushes authentication to the issuing bank: the customer has to prove who they are. For an automated attack that ends the process, because there is nobody to approve it on the bank's side.

You do not want it on everyone, though — every extra step loses buyers. The workable middle ground is to request it on risky payments:

`` Request 3D Secure if :risk_level: = 'elevated' Request 3D Secure if :card_country: != :ip_country: ``

risk_level can be normal, elevated, highest or not_assessed. If European SCA rules already force authentication on most of your traffic, this layer is largely working for you already.

What does NOT help?

  • Filling a block list after the fact. While you add card numbers one by one, the attacker is already on the next thousand. A list has to be built on the pattern, not on individual entries.
  • A CAPTCHA on the login page. The attacker never logs in; they go straight to payment.
  • An amount threshold. Card testing deliberately uses small amounts so as not to stand out.
  • Country blocking on its own. A proxy brings traffic from another country in seconds. (In the EU, the geo-blocking regulation also limits excluding customers from member states.)

What to do right now if you are under attack

  1. Turn on rate limiting at the payment endpoint. It is the only step that takes effect immediately and costs no transaction fees.
  2. Put the blocking rules above in place, strictly at first; tuning can wait until the wave is over.
  3. Review the successful charges during the wave — those are likely live stolen cards. Refunding them pre-empts the chargeback.
  4. Do not delete logs. The IPs, timestamps and request headers are the only evidence you will have later.
  5. Check your dispute rate — if it is anywhere near the threshold, that needs handling on its own.

Frequently asked questions

How do they even find my payment page?

They are not targeting you: automated scanners sweep the public internet for traces of known payment integrations. Your site appears on their list like everyone else's.

If I block them, won't they just come from somewhere else?

They will — they rotate IPs. That is why a single rule is not enough. The IP-based limit, the card-number-based limit and the risk level together ruin the economics of the attack: it becomes more work than it is worth.

How many real customers do I lose by tightening?

With well-set rules, very few. The most common mistakes are a threshold set too low (> 0 or > 1) and demanding a strict pass on CVC — digital wallets (Apple Pay, Google Pay) do not send a CVC at all, so fail is not even meaningful for them. Run every new rule in Review mode first and switch to blocking based on real data.

Is there a point where it is too late?

No, but there is a cheaper and a more expensive moment. If your account has already been suspended, the job becomes documenting the clean-up, and that can take weeks. If you start now, it is an afternoon's work.


If you would rather not do it yourself, we will review your Stripe account settings — the report is free, and it is usable even if your own developer implements the fixes.

Updated: 20 August 2026

Request a free security audit

We review your website, your server and your e-mail authentication, and write up what we found. No obligation, within 24 hours.

Request an audit →