Fraud playbook

How to stop card testing

Alex MugoFounder, Kaidn
3 min readRevised

Also called: card cracking · carding · BIN attack · auth testing · card validation attack

Why card testing costs you more in processor standing than in stolen goods, how the attack is structured, and the signals that catch it in the first minutes.

What it costs you

  • trending_downYour authorisation rate, which processors and issuers watch closely. A flood of declines marks you as a risky merchant and quietly lowers approvals for genuine customers.
  • trending_downPer-attempt fees, since many processors charge for declined authorisations too. Ten thousand failed attempts is a real invoice for nothing.
  • trending_downChargebacks on the cards that did work, plus the chargeback ratio that follows you into every future processor negotiation.
  • trending_downProcessor standing itself, the worst case being reserve requirements, higher rates, or termination. Replacing a payment processor under duress is far more expensive than the fraud was.
  • trending_downEngineering time at an unplanned moment, because this arrives suddenly and demands attention the same day.

How the attack runs

  1. 01

    Acquire a list of card numbers

    Bought in bulk from a breach or generated against a known BIN using the Luhn check. Neither the attacker nor anybody else yet knows which entries are live.

  2. 02

    Find a cheap, fast authorisation endpoint

    The target is not your product. It is any path that will attempt an authorisation with minimal friction: a donation form, a small top-up, adding a card to an account, a one-dollar trial. Forms with no cart and no shipping are ideal.

  3. 03

    Test in volume, at speed

    Attempts run in parallel across many sessions. The point is throughput, because the output is a filtered list rather than any single purchase. Most attempts are expected to fail.

  4. 04

    Keep the hits, discard the rest

    A card that authorises is now verified as live and worth far more than an untested number. It is usually sold on rather than used, and often not on your site at all.

  5. 05

    Monetise elsewhere

    This is why your fraud team may see very little theft from you directly. You were the free validation service; the loss lands on a different merchant later, and on the cardholder.

What does not work

These are the defences most teams try first. They are listed here because trying them and watching them fail is expensive.

Requiring CVV and AVS

Do it, and do not rely on it. Breach data frequently includes CVV and billing address, and where it does not, the attacker simply reads the response codes to learn which fields failed. You have turned your checkout into a more informative oracle.

Blocking the IPs you saw attacking

By the time the block is in place the attack has moved. It arrives from thousands of addresses and rotates continuously, so an IP blocklist is always describing the previous minute.

A CAPTCHA at checkout

Solving services cost a fraction of a cent, and this attack has a budget. Meanwhile you have added friction to the exact step where abandonment is most expensive, so it is paid for by your real customers.

Raising the minimum transaction amount

It slightly raises the attacker's cost and does not stop them, because they are not paying, the cardholder is. What it does reliably is remove a legitimate product tier.

Reviewing fraud reports weekly

This attack does its damage in hours. A weekly cadence means the first you hear of it is the processor asking about your decline rate.

The signals that do

These are the signals that carry weight on card testing, and why each one is the signal rather than the obvious alternative. Not all of them are ours: the ones marked you build this genuinely work and Kaidn does not check them, so you would be wiring them up yourself. Listing those unlabelled would read as a claim we cannot support.

Decline rate as a live alarm, not a monthly report

you build this

The single most reliable indicator, and the one most often reviewed too late. A sharp rise in the decline ratio over minutes is card testing until proven otherwise. Alert on the ratio, not the count, so it works at every traffic level.

BIN concentration

you build this

Generated lists cluster on a small number of issuer prefixes. Genuine traffic spreads across many. Concentration on one or two BINs inside a short window is close to definitive.

Velocity per identity, not just per card

Kaidn checks this

Attackers rotate card numbers, so counting attempts per card sees one attempt each and nothing looks wrong. Counting per device, per session, per IP, per email and per ASN is what makes the volume visible.

Amount uniformity

you build this

Testing uses the smallest amount that authorises, repeatedly. A burst of identical small amounts, particularly at odd hours, does not look like commerce because it is not commerce.

Automation traits on the payment page

Kaidn checks this

This is scripted almost without exception. Headless or driver-controlled sessions, and form completion faster than a person can type a card number, are strong signals precisely where the stakes are highest.

Card testing is unusual among the abuse patterns on this site, because the money mostly does not leave through you. Your site is being used as a validation oracle: a fast, cheap way to sort a list of stolen card numbers into live and dead.

That changes what you are defending. You are not primarily protecting inventory. You are protecting your standing with your payment processor, and that is a much less forgiving thing to lose.

The real bill#

A few thousand declined authorisations produce three separate problems, and only one of them looks like fraud on a report.

Per-attempt fees are the visible one, and usually the smallest. The authorisation rate is the serious one: processors and issuers treat a poor approval ratio as a signal about the merchant, and the resulting drag applies to your genuine customers, permanently and invisibly. Then there is the relationship itself, where a sustained pattern can mean reserves, worse rates, or being asked to leave.

Being asked to leave is the outcome to design against. Finding a new processor while wearing a bad decline history is slow, expensive and sometimes not possible on your original terms.

Speed is the whole problem#

Most fraud on this site is a slow leak you can afford to study for a week. This one is not. A card testing run can put tens of thousands of attempts through a form in an afternoon, and the damage is already recorded by the time a daily report is read.

So the design constraint is different: whatever you build has to act inside the attack, not after it. That means alerting on rates rather than totals, and it means the response has to be automatic, because a human being paged at 3am is not a control.

Count the right thing#

The mistake that lets this run is counting attempts per card.

Rotate the card number on every request and every counter reads one. Nothing trips, because the thing you made unique is the thing they were always going to vary. It is the same error as one-account-per-email in multi-accounting, in a more expensive setting.

Count per session, per device, per IP, per ASN, per email, and per BIN. The attacker has to reuse something, and it is never the card.

Where we sit, and where we do not#

We score the payment attempt as an event: automation traits, velocity across every identity except the card, IP and ASN reputation, email identity, and device signals. Verdicts come back with the checks that fired and their weights, in milliseconds, so a block can happen inline.

We are not a payment processor and we do not see your authorisation responses unless you send them. The strongest version of this defence combines both: your processor's decline data, which only they have, and behavioural scoring on the attempt, which they largely do not do. If your processor offers velocity controls, turn them on as well. This is a layer, not a replacement.

Score the attempt, not the card#

The call goes before the authorisation, and the identity fields are everything except the card:

app/api/pay/route.ts
const r = await kaidn.score({
  event: "payment_attempt",
  user_id: session.userId ?? undefined,   // guest checkout often has none
  ip: req.ip,
  email: body.email,
  device_id: body.kaidn_device_id,
});

// this attack is high-volume and automated, so velocity and automation dominate
const testing =
  r.reasons.includes("device_velocity") ||
  r.reasons.includes("ip_velocity") ||
  r.reasons.includes("headless_browser");

if (testing || r.verdict === "block") {
  await recordSuppressedAttempt(r.event_id);
  return Response.json({ error: "please try again later" }, { status: 429 });
}

return authorise(body);   // only now does the card reach your processor

The point of the ordering is that a suppressed attempt never becomes a declined authorisation, which is the number your processor is actually reading.

The alarm you should build first, with no vendor at all#

This is the highest-value hour on the page and it costs nothing. Alert on the decline ratio inside a short window rather than on a count in a daily report:

decline ratio, last 10 minutes
SELECT
  count(*) FILTER (WHERE status = 'declined')::float / nullif(count(*), 0) AS decline_ratio,
  count(*) AS attempts
FROM payment_attempts
WHERE created_at > now() - interval '10 minutes'
HAVING count(*) > 20;

Page on decline_ratio > 0.5. A count threshold misses a slow run and fires falsely on a busy hour; a ratio does neither. Wire it to whatever already wakes somebody up, and make the automatic response a throttle rather than a notification.

What to do this week, before buying anything#

Put a live alert on your decline ratio, thresholded on the ratio rather than a count, firing within minutes. It costs an afternoon, it needs no vendor, and it converts this from an attack you discover in a processor email into one you discover while it is happening.

Frequently asked questions

What is card testing?

Card testing is the use of your payment form as a validation oracle: an attacker runs a list of stolen card numbers through it to sort live from dead. The money mostly does not leave through you, which is what makes it easy to under-rate. What you are defending is your standing with your payment processor, and that is a much less forgiving thing to lose than inventory.

What does card testing actually cost me?

Three things, and only one looks like fraud on a report. Per-attempt fees are visible and usually smallest. A damaged authorisation rate is the serious one, because processors and issuers read a poor approval ratio as a signal about the merchant and the resulting drag applies permanently to your genuine customers. The third is the relationship itself: reserves, worse rates, or being asked to leave.

Why does rate limiting per card not work?

Because the card number is the one thing the attacker varies on every single request. Counting attempts per card means every counter reads one and nothing ever trips. Count per session, per device, per IP, per ASN, per email and per BIN instead: the attacker has to reuse something, and it is never the card.

How fast does a defence have to be?

Fast enough to act inside the attack. A run can put tens of thousands of attempts through a form in an afternoon, so a daily report is a record of damage rather than a control. Alert on rates rather than totals, fire within minutes, and make the response automatic: a human paged at 3am is not a control.

Does Kaidn see my declines?

No, not unless you send them. Kaidn is not a payment processor and does not see your authorisation responses. It scores the payment attempt: automation traits, velocity across every identity except the card, IP and ASN reputation, email identity and device signals. The strongest version of this defence combines your processor's decline data, which only they have, with behavioural scoring on the attempt, which they largely do not do.

Score your own traffic for this

10,000 events a month free, no card. Every verdict comes back with the checks that fired and their weights, so you can see which signal caught it rather than trusting a number.

Other fraud types