Stop Card Testing

Stop Card Testing Bots, Auth Floods & Chargeback Fallout

Bots use checkout and donation forms to validate batches of stolen cards, and you can pay twice: authorization fees now, disputes weeks later. Maskbreak checks each checkout for the automation and proxies behind the run in one server-side call, before the payment intent exists. A blocked attempt never reaches your processor.

1 callServer-side decision; timing on /status
FreePlan: 10,000 visitor checks a month, no card
3Answers: allow, review or block, with reasons
WatchFirst integration logs the decision and always continues

What you're up against

Small-amount authorization floods

Bots cycle stolen card batches through your checkout with tiny authorizations to find which cards are live. On many processor plans every authorization attempt carries a fee, approved or declined, and a wall of declines drags down the approval rate your acquirer sees.

Donation forms as free card validators

Any form that accepts an arbitrary amount is a perfect card tester: no inventory, no shipping address, instant confirmation that a stolen card works. Nonprofits and tipping flows get hit hardest precisely because they keep friction low.

Chargebacks land weeks after the test

Cards validated on your site get drained elsewhere, and the test charges that went through come back as disputes. Enough of them push your chargeback ratio toward the card networks' monitoring programs, which carry fines and can end with the processor closing the account.

Velocity rules that proxies walk around

Per-IP rate limits and per-card counters assume the attacker keeps one address. Testing runs rotate residential proxies so every attempt arrives from a fresh "clean" IP, and your velocity rules never fire.

What Maskbreak does for you

  • Check every checkout before the payment intent is created, so once you enforce, a blocked attempt never reaches your processor
  • Flag scripted and headless browsers (automation_detected) and fake browsers (antidetect_browser) on payment pages; both block by default
  • Block proxies and Tor exits by default, residential proxies included; a VPN on its own is flagged for review, and a cloud-server address alone stays allow with datacenter_asn listed
  • Return device.visitor_id whenever the browser check completes, so you can count card attempts per browser instead of per IP address
  • No CAPTCHA or challenge from Maskbreak: the answer is an API response your server acts on
1 machine

running hundreds of small authorisations is the tell. A worked model of a store's checkout under a card-testing run, and where the check goes. Illustrative scenario, not a customer result.

Read the Playbook

Where the check goes

One call site covers this vector: the server route that creates the payment intent — your checkout submit and any donation or tipping endpoint that charges a card. Run the check after the form posts but before you talk to your processor; once you enforce, a block means the stolen card is never authorized, so there's no auth fee and nothing to charge back. The client snippet rides along in your existing payment page and adds the two tokens your server sends to Maskbreak. The example starts in watch mode: it logs each verdict, lets every request through and fails open if Maskbreak is unreachable. After a few days, compare the log with the Events in your dashboard, then uncomment the enforce line; the integration guide has the full enforce checks.

// Score the checkout before creating a payment intent
// Watch mode first: log the verdict, always continue, fail open
const body = req.body || {};  // sentinel.js adds monocle + sentinel_fp to your form
let risk = null;
try {
  const verdictRes = await fetch('https://maskbreak.com/v1/evaluate', {
    method: 'POST',
    signal: AbortSignal.timeout(5000),
    headers: {
      'Authorization': 'Bearer ' + process.env.MASKBREAK_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      token: body.token || body.monocle,  // network evidence
      fingerprintEventId: body.fingerprintEventId || body.sentinel_fp,  // device evidence
      tz: body.tz || body.sentinel_tz
    })
  });
  if (verdictRes.ok) risk = await verdictRes.json();
} catch {}  // unreachable or timed out: your handler still runs
console.log('[maskbreak]', JSON.stringify({
  decision: risk?.decision ?? null, reasons: risk?.reasons ?? null,
  risk_score: risk?.risk_score ?? null, degraded: risk ? !!risk.degraded : null
}));
// Enforce later: if (risk?.decision === 'block') return res.status(403).json({ error: 'Request rejected' });

What a card-testing script behind a residential proxy gets back. Example response, selected fields:

{
  "decision": "block",
  "risk_score": 90,
  "reasons": ["proxy_detected", "automation_detected"],
  "network": { "vpn": false, "proxy": true, "datacenter": false,
               "tor": false, "residential": false,
               "service": "<proxy network, when known>" },
  "device": { "automation": true, "antidetect": false,
              "visitor_id": "<opaque visitor id>", "tampering_score": 0 }
}

Proxy and automation are both hard signals, so the default is block. network.residential is false because the address is a proxy, even when it sits on a home connection. Route on decision, not on a risk_score cut-off of your own (why). Every response also reports its own processing time in evaluated_in_ms; /status publishes measured figures.

The signals that matter here

Automation, before the first authorization. Nobody types stolen card numbers by hand — card testing is scripted end to end, usually headless Chrome with stealth patches that pass simple bot checks. Maskbreak's device check looks for the traces an automated or tampered browser leaves. When it finds them, device.automation comes back true with automation_detected in the reasons; a fake-browser profile shows as device.antidetect and antidetect_browser. Either one is a block by default, before the first authorization is attempted.

One browser, many cards. A testing run rotates card numbers, emails and IP addresses, but often keeps the browser. Count attempts per device.visitor_id in your checkout handler (say, more than a few different cards from one visitor in an hour) instead of per IP. Do not use device.times_seen as that counter: it is how often Maskbreak has seen the device across every site that uses it, not on your checkout. high_activity_device is listed when a device is unusually busy; it is a soft signal that adds a little to risk_score and never changes the decision on its own.

Network context on the payment path. Most real buyers check out from home or mobile networks. Card-testing traffic often runs through proxies, including residential ones: network.proxy is true, proxy_detected is in the reasons and the default is block, as it is for a Tor exit. A VPN on its own can be privacy hygiene, so it is flagged for review, not blocked, and the operator is named in network.service when known. A cloud-server address alone stays allow, with datacenter_asn listed for your own policy.

A blocked attempt can't become a chargeback. An authorization that reaches your processor can cost a fee, approved or declined, and an approval on a live stolen card can come back as a dispute weeks later. Checking before the payment intent exists means a blocked attempt ends in your own backend: no authorization, nothing to dispute, and no new friction for real buyers, because the answer is an API response rather than a challenge. Allowed attempts still go through your processor's own checks.

Common questions

How do I score a visitor's risk before checkout?
Call Maskbreak before you create the payment intent. POST /v1/evaluate with the two browser tokens the snippet adds to your checkout form returns a decision (allow, review or block), a risk_score from 0 to 100 and the reasons, such as proxy_detected or automation_detected. Maskbreak never sees card numbers. Start in watch mode, which logs the decision and lets every buyer through, then block or step up verification once the Events in your dashboard look right. The Free plan includes 10,000 visitor checks a month with no card; paid plans start at €29 a month.
How does Maskbreak detect card testing?
The snippet on your payment page adds two browser tokens to the form, and your server sends them to POST /v1/evaluate before it creates the payment intent. Card-testing runs are scripted and usually routed through proxies, so they tend to return automation_detected, antidetect_browser or proxy_detected, each a block by default, even when every attempt comes from a different residential address. A VPN on its own is flagged for review, and a cloud-server address alone stays allow with datacenter_asn listed.
Does Maskbreak ever see card numbers?
No. Your server sends Maskbreak the two browser tokens the snippet adds to the form, not payment data; the card number goes only to your processor, as it does today. Maskbreak returns a decision, a risk_score and the reasons behind it, and what you do with a flagged attempt stays in your backend.
Can I step up verification instead of hard-blocking?
Yes. The decision field has three values, allow, review and block, and your server owns the policy. A common split: decline block, send review through 3-D Secure or another verification step, and let allow through. Route on decision rather than on a risk_score cut-off of your own. Rules-tab actions change the decision only for the signal they name, so softening one signal, say VPN to Allow, never lets through a proxy or a scripted browser on the same attempt.
Will real customers notice the check?
Not from Maskbreak. The snippet runs in the background on your payment page and your server makes one API call before the payment intent is created: no CAPTCHA, no challenge, no extra click. The only friction a real buyer can see is whatever your own policy adds on a review.
Should Maskbreak replace my processor's fraud tools?
No. Run it in front of them. Once you enforce, scripted and proxied attempts stop before they reach the processor, while your processor's own checks and 3-D Secure still screen whatever gets through. Maskbreak does not score the card or the order and offers no chargeback guarantee. Integrate fail-open: if the API is unreachable or your key is over its limit, checkout carries on as it does today.

Start protecting your checkout

Free plan: 10,000 visitor checks a month, every signal included, no card. A testing run costs one check per attempt: Free is capped at 1,000 checks an hour, and paid plans from €29 a month raise the cap (Starter 5,000, Growth 20,000, Scale 50,000 an hour). Start in watch mode and enforce when the log looks right.

Fraud BriefOnce a month · no spam · unsubscribe anytime
Get the new VPN, proxy & bot patterns we see each month
Short, technical breakdowns of what fraudsters changed last month — written for engineers, not marketers.
Stop fraud before it hides — try Maskbreak free. Free plan: 10,000 visitor checks a month, no card. Paid plans from €29 a month.