Portreeve
explainer · 5 Sept 202612 min read

Ecommerce fraud detection: match the signal to the attack

Ecommerce fraud detection is six different attacks. Name yours from the first loss, learn which signal catches it, and decide whether to build or buy it.

There is no such thing as ecommerce fraud detection in general. There are six attacks with different economics, run by different people, and each is caught by a different signal. A store searching for "ecommerce fraud detection" the morning after its first loss usually has one of them, and the first useful step is to name it. The tool that stops a card tester does nothing to a repeat disputer, and the rule that catches a reshipper never fires on a coupon farm.

The first loss tells you which one you have.

A night of $1 or $0 authorizations with hundreds of declines is card testing. A single $600 order to an address that does not match the billing address, on a card issued in another country, is a stolen-card purchase. A dispute filed 60 days after a package was delivered and signed for is friendly fraud. A password reset, a new device, and a changed shipping address on an account with a saved card is account takeover. A launch coupon redeemed 400 times from a handful of devices is promo abuse. Three orders of the same laptop to a suite number in Medley, Florida is reshipping.

Read the symptom first. Then pick the signal. Then decide whether to write it or buy it.

Card testing and stolen-card purchases are different problems

Both involve stolen cards. The people are different and so are the tells.

A card tester does not want your product. They have a list of numbers and want a bank to say which are live. Stripe's card-testing documentation describes the preferred method: card setup rather than payment, because a SetupIntent validates the card through an authorization that typically never appears on the cardholder's statement, so nobody reports it. When they do use payments, they use small ones for the same reason. One Shopify merchant, rachaeljean, raised their digital product prices to $21 to escape the $2 and $3 orders, and the attacker bought the $21 item that morning.

So forget the amount. The tell for card testing is the count of distinct cards per device and per hour, plus the decline ratio. A real buyer retries the same card twice, then tries one other card. A tester runs 40 different cards through one browser session and most of them decline. The card testing post covers the probe shapes in detail, including the $0 authorization ladder.

A stolen-card buyer is the next person in the chain. They bought a validated number and want goods to resell. The amount matters now and the card count does not. One card, one order, no retries. What gives them away is mismatch: the billing address on the card versus the shipping address, the card's issuing country versus the IP country, and the funding type. Stripe Radar's rule reference uses :card_country: != :ip_country: as its worked example for a reason. A prepaid card from one country, shipping to a second, from an IP in a third, on a device that has never been seen on your store, is a stolen-card purchase until proven otherwise.

The two signal sets barely overlap. Distinct-cards-per-device says nothing about a single stolen-card order, and billing mismatch never fires on a tester who fills in nonsense addresses because nothing will ship.

Friendly fraud only shows up if you remember who disputed

Friendly fraud is the one checkout-time tools miss, because at checkout nothing is wrong. The buyer is real. The card is theirs. The goods arrive. Then, weeks later, they call their bank.

The timeline is set by the card networks. Stripe's dispute documentation says cardholders can typically initiate a dispute within 120 days of the payment, and that 80% of early fraud warnings turn into a fraud dispute if you do nothing. In the United States, Stripe charges $15 when the dispute arrives, which it never refunds, and since June 2025 another $15 when you counter, returned only if you win.

Stripe's own guidance on the "fraudulent" category is blunt: it is the most common dispute reason and a difficult one to win, "because in many cases the reason for the dispute is correct." The ones you can win are where the reason is not correct, and that evidence has to already exist when the dispute lands.

Visa's Compelling Evidence 3.0 rules spell out what "already exist" means. For a reason code 10.4 dispute, you need two prior undisputed transactions from the same cardholder, each 120 to 365 days older than the disputed one, with at least two data elements matching across all three transactions, and one of those two must be the IP address or the device fingerprint. The other options are the account login ID and the shipping address. Visa's own merchant readiness guide covers the same criteria.

Read that as a storage requirement. If you are not recording a device fingerprint and IP per order today, you cannot qualify for CE3.0 on any dispute that lands next spring. The tell for friendly fraud is the same person, card, or device disputing for the second or third time, and you only see it if dispute outcomes are written back against an identity rather than the order. It is invisible to any tool that only sees the checkout and never hears what happened 90 days later. The chargeback fraud post goes through the evidence side in more depth.

The most valuable query a small store can run, against whatever table holds dispute webhooks:

select card_fingerprint, count(*) as disputes
from disputes
where reason = 'fraudulent'
group by card_fingerprint
having count(*) >= 2;

If it returns rows, the next order from those cards should go to review before it ships.

Account takeover and promo abuse are identity problems

Neither of these involves a bad card. Both are about who is holding the account.

An account takeover on a store starts with credentials from somewhere else. The attacker logs in from a device your account has never seen, changes the shipping address, and orders against the saved card. The card is good, the billing address matches, and every payment-side check passes because the stored method is legitimate. The only thing wrong is the device, and the only baseline you can compare it to is that account's own history. An account that has logged in from two devices in three years, both in one city, and now appears on a third from a datacenter range with an immediate address change, is the signal. The comparison has to be made at login, before any payment step runs.

Promo abuse is the inverse: one person, many accounts. A $20 launch code is worth $20 times however many signups the abuser can create, and signups are cheap. The tell is many accounts resolving to one device fingerprint or one card fingerprint, usually with emails that follow a pattern, either disposable domains or a single Gmail address with dot and plus variations.

The standard advice here is a blocklist of disposable email domains. It helps for a week. New domains appear faster than any list updates, and the abusers who matter use real mailbox providers with plus-addressing. What holds is linking: if the fourth "new" account shares a card fingerprint with three others that redeemed the same code, the email is irrelevant.

Reshipping and the address that gives it away

Reshipping is a stolen-card purchase with a logistics layer. The buyer cannot receive goods in their own country on a card issued in yours, so the order ships to a freight forwarder or a recruited mule who reboxes and forwards it. The goods are laptops, phones, consoles, anything with a liquid resale market.

One merchant on the Shopify forums, SKTEDS, found multiple orders going to one unit in Medley, Florida, an address linked to a forwarding company. The orders had been flagged as high risk but went through on Amazon Pay and PayPal anyway, and only after they contacted the customer directly did they learn the account had been compromised.

The signals are a shipping address in a known forwarder cluster, billing and shipping mismatch on a high-resale SKU, and multiple accounts converging on one shipping address. None are strong enough to block on. Plenty of real customers abroad use forwarders. This is the one attack where the right answer is mostly a manual review rule: hold anything over a dollar threshold going to a forwarder address, and confirm by a channel the attacker does not control. It costs a day of shipping delay and it is the cheapest fraud control on this list.

Why counting by IP fails and what to count instead

Almost every first attempt starts with the IP address, because it arrives for free. Stripe's card-testing page suggests limiting new customers created per IP per day as an example, a few paragraphs after saying that "simple firewall rules or filters based on a single heuristic such as IP addresses are usually not sufficient."

It is leaky in one direction and harmful in the other.

Leaky because an attacker running card tests has a residential proxy pool. The merchant trish11679 on that same Shopify thread described exactly that: a bot placing orders of $5 or less, "all with different IP addresses, names, and billing addresses." A per-IP counter never reaches two.

Harmful because of carrier-grade NAT. Mobile networks and some home ISPs put many end users behind a small pool of shared public addresses. Your rule that blocks after five orders from one IP in an hour will fire on a university, a hospital, or every customer on one mobile carrier in a city, on the afternoon a promotion goes out.

So count on identifiers that belong to one actor. A card fingerprint is one card. A device fingerprint, collected in the browser, is one browser on one machine, and a proxy rotation does not change it. A hashed email is one mailbox. Velocity counted on those three means something: five distinct card fingerprints on one device in an hour is a tester, and shared egress never produces that pattern by accident. IP still has a use as a soft signal, especially the anonymizing-proxy flag that Radar exposes as :is_anonymous_ip:, but it should raise suspicion rather than link accounts or block on its own.

The advice that is wrong

Three pieces of advice show up in every thread, and each is wrong for most stores under a few thousand orders a month.

"Turn on AVS and CVV and require 3DS on everything." AVS and CVV checks are worth having. Requiring a strict pass is a conversion tax. Stripe's rule reference warns that requiring a strict pass can be overly restrictive because wallets like Apple Pay do not provide a CVC at all. Blanket 3DS adds a bank challenge to every domestic debit card purchase, and the liability shift it buys you covers stolen-card disputes only. It does nothing for friendly fraud, promo abuse, or an order from a taken-over account whose saved card passes 3DS. Request 3DS on the mismatch signals from the stolen-card section. Leave everyone else alone.

"Block by IP." Covered above. Leaky and harmful at once.

"Buy an enterprise fraud platform." The enterprise vendors are built around annual minimums, a sales cycle, and an integration project. At a few hundred orders a month, the minimum alone exceeds your fraud losses, and you will spend the first quarter mapping your order fields to their schema.

Build or buy at a few hundred orders a month

At a few hundred orders a month you can, and probably should, write the first pass yourself in an afternoon.

The velocity counters are a table keyed on card fingerprint, device fingerprint, and email hash, with a timestamp, and three queries that count rows in the last hour and last day. The stolen-card checks are string comparisons you already have the inputs for: billing country against shipping country, card country against IP country, funding type. The reshipping rule is a list of forwarder addresses and a threshold. The repeat-disputer query is the one above.

The review step costs nothing and it is the piece most stores skip. A review outcome lets the order go through, shows the customer a normal confirmation, and puts a human in front of it before the label prints. For a physical-goods store that is a natural hold: you were not going to ship for hours anyway. For digital goods it means provisioning immediately and revoking the few you later confirm as bad. The false-positive cost of review is close to zero, which is why you can set its thresholds aggressively and reserve outright declines for the signals you are nearly certain about.

Where building stops paying is identity linking across accounts. The moment promo abuse or account takeover is your problem, you need the fourth signup on a device to know about the first three, the card fingerprint on an order to know it was disputed under a different account last quarter, and the confirmed-abuse mark on one account to propagate to everything linked to it. That is a graph. It needs a review queue with state, a way to record outcomes, and hashing so you are not holding raw identifiers. Maintaining it is a part-time job, and the point at which paying someone becomes cheaper than the engineering hours.

This is the part I built Portreeve for. It is an abuse firewall: one API call at checkout_attempt, signup, login, trial_start, or trial_convert returns allow, review, or block with reason codes in under 100 ms. review never blocks the buyer; the flow proceeds, the event lands in a dashboard queue, and a later deny reaches your server as a signed webhook so you can revoke. Accounts are linked on hashed email, device fingerprint, card fingerprint, and phone, while IP stays a soft signal that never links accounts on its own. The card-probe rules read a device-keyed counter, so the browser snippet (@portreeve/browser) is required for card testing to be blockable at ordinary amounts. The free tier is 1,000 screened events a month with no card, then $19 for 10,000. It is in open beta.

If you want the identity linking and the review queue without maintaining them, create a free Portreeve account and screen your first 1,000 events a month with no card required.

← Back to all posts