Fraud costs e-commerce businesses millions every year, and the damage extends beyond chargebacks—it erodes customer trust and strains operations. Rather than build custom fraud detection from scratch, teams at scale rely on machine learning models that understand payment risk in real time. This guide walks you through fraud prevention with Stripe Radar: a step-by-step guide to blocking bad payments while keeping your conversion rate healthy.
Machine learning by default — Radar scores every payment
Stripe Radar is built on years of fraud data from billions of transactions. Every payment that flows through Stripe is automatically scored by its machine learning model, which assigns a risk level from 0 to 100. A score of 0 means low risk; 100 means high risk. This happens transparently—you don't need to enable anything special to get these scores.
The model considers signals like:
- Card velocity (how many transactions in a short time)
- Geographic mismatch (billing address vs. card issuer location)
- Device fingerprinting (browser, OS, device ID patterns)
- Historical patterns (this card's past behavior)
- Network intelligence (IP reputation, VPN detection)
- Amount anomalies (order size vs. typical customer spend)
When you create a charge or payment intent, Stripe includes a fraud_details object in the response. Here's what that looks like in practice:
const charge = await stripe.charges.create({
amount: 5000,
currency: 'usd',
source: 'tok_visa',
metadata: {
order_id: 'order_12345',
customer_email: 'user@example.com'
}
});
console.log(charge.fraud_details);
// {
// user_report: null,
// stripe_report: 'fraudulent' | 'safe' | null
// }
The key insight: Radar's default scoring is free and always running. You're not paying extra for this layer of protection. However, you need to actually use those scores to make decisions. That's where rules come in.
Custom rules — Block or review based on risk signals
Default Radar blocks some payments automatically (the highest-risk tier), but most businesses need to tune this. Radar Rules let you define thresholds and actions based on the risk score and custom conditions.
In the Stripe Dashboard, you can set up rules like:
- Block automatically: Risk score > 75
- Require 3D Secure: Risk score between 50–75
- Block specific card types: Prepaid cards + high risk score
- Block by geography: Card issued in high-fraud regions + international billing
- Block velocity: More than 3 failed attempts in 10 minutes
These rules are configured via the Dashboard, but you can also manage them programmatically with the Radar API. Here's an example of creating a rule that flags high-risk charges for manual review:
const radarValueList = await stripe.radarValueLists.create({
name: 'Blocked IP ranges',
type: 'ip_address'
});
// Add IPs to the list
await stripe.radarValueListItems.create(radarValueList.id, {
value: '192.168.1.1'
});
// Now create a rule that references this list
const rule = await stripe.radarRules.create({
'grouping[and][0]': 'charge_dispute_count > 2',
'grouping[and][1]': 'card_velocity > 5',
action: 'block'
});
A more practical workflow uses payment intents and conditional logic in your application. When you create a payment intent, Stripe evaluates your rules before the charge is captured. You handle the outcome like this:
const paymentIntent = await stripe.paymentIntents.create({
amount: 9999,
currency: 'usd',
payment_method: paymentMethodId,
confirm: true,
metadata: {
order_id: 'order_abc123',
customer_id: 'cus_xyz'
}
});
if (paymentIntent.status === 'requires_action') {
// Radar blocked it or 3D Secure is required
console.log('Payment requires further action');
return { requiresAction: true, clientSecret: paymentIntent.client_secret };
} else if (paymentIntent.status === 'succeeded') {
console.log('Payment succeeded');
return { success: true };
} else if (paymentIntent.status === 'processing') {
// Charge is being processed
console.log('Payment is processing');
}
The balance here matters. Too aggressive, and you'll reject legitimate customers (and lose revenue). Too permissive, and you'll eat the cost of chargebacks and fraud losses. Most teams start by setting rules to flag charges for review rather than outright blocking them, especially for first-time customers or large orders.
Manual review — Queue risky orders for humans
Not every risky payment is fraud. A legitimate customer might be traveling, buying something expensive for the first time, or using a new device. Forcing a block is the wrong call—you lose the sale. Instead, queue the order for manual review.
The typical workflow is:
- Stripe charges the card and marks the payment as
succeededbut flags it internally. - Your system stores the order in a review queue (database table or external service).
- A team member checks the order against business rules: matches known fraud patterns, customer reputation, email domain quality, shipping address, etc.
- If approved, fulfill the order. If denied, refund and lock the customer.
Here's a schema and insertion logic:
interface ReviewQueue {
id: string;
order_id: string;
charge_id: string;
risk_score: number;
risk_reasons: string[];
customer_email: string;
amount: number;
billing_country: string;
shipping_country: string;
created_at: Date;
reviewed_at?: Date;
reviewed_by?: string;
approved: boolean;
notes?: string;
}
async function queueForReview(charge: any, order: any) {
const riskScore = charge.fraud_details?.stripe_report === 'fraudulent' ? 90 : 45;
const item: ReviewQueue = {
id: `review_${Date.now()}`,
order_id: order.id,
charge_id: charge.id,
risk_score: riskScore,
risk_reasons: [
charge.billing_details?.address?.country !== order.shipping_country
? 'Country mismatch'
: null,
order.amount > 50000 ? 'High value order' : null
].filter(Boolean) as string[],
customer_email: charge.billing_details?.email,
amount: charge.amount,
billing_country: charge.billing_details?.address?.country,
shipping_country: order.shipping_country,
created_at: new Date(),
approved: false
};
await db.reviewQueue.insert(item);
// Notify your support team
await notifySlack(`Order ${order.id} queued for fraud review`);
}
Many teams integrate this with a simple admin dashboard or spreadsheet. The key is speed—review should happen within minutes, not days. Slow review kills conversion and ties up cash flow.
Balance — Too strict rules lose real customers
This is where most teams stumble. The impulse is to be aggressive: block anything that looks slightly off. But your fraud prevention strategy should protect profit, not just prevent fraud.
Consider the math:
- A legitimate customer gets blocked: you lose the sale and damage trust. They might never come back.
- A fraudster gets through: you lose the goods and the chargeback fee (typically $15–$100).
- A risky customer gets flagged for review: small operational cost, but you capture most legitimate sales and catch fraud too.
The false positive rate (blocking legitimate customers) directly impacts your bottom line. Industry benchmarks suggest that 1–2% of legitimate transactions get flagged as suspicious. If your rule set pushes that to 5–10%, you're losing money faster than fraud costs you.
A pragmatic approach:
- Tier your customers: New customers or high-value orders get stricter scrutiny. Returning customers with a clean history get looser rules.
- Use soft declines: 3D Secure, additional verification, or a callback, rather than outright blocks.
- Monitor your metrics: Track the chargeback rate, false positive rate, and conversion impact. Adjust rules quarterly.
- Combine signals: Don't block on a single factor. High risk score alone isn't enough—also check customer history, device reputation, and email domain quality.
Here's a scoring function that balances multiple signals:
function calculateFraudScore(charge: any, customer: any): number {
let score = charge.fraud_details?.stripe_report === 'fraudulent' ? 50 : 0;
// Customer history
if (customer.orders_count === 0) score += 15; // New customer
if (customer.total_spent > 10000) score -= 20; // High-value history
if (customer.chargeback_count > 0) score += 30; // Past chargebacks
// Geographic signals
const countryMismatch = charge.billing_details?.address?.country
!== customer.shipping_country;
if (countryMismatch) score += 10;
// Email quality
if (customer.email?.endsWith('@tempmail.com')) score += 15;
// Order amount
const avgOrderSize = customer.total_spent / Math.max(customer.orders_count, 1);
if (charge.amount > avgOrderSize * 5) score += 10;
return Math.min(score, 100);
}
// Usage
const finalScore = calculateFraudScore(charge, customer);
if (finalScore > 70) {
return queueForReview(charge, order);
} else if (finalScore > 40) {
return requireStrongAuth(charge); // 3D Secure
} else {
return processNormally(charge);
}
This approach gives you granularity. You're not just looking at Radar's score; you're layering your own business logic on top.
Common mistakes
- Ignoring Radar scores entirely: Teams often set Radar and forget it. Audit your rules quarterly and measure their impact on conversion and fraud.
- Blocking instead of reviewing: Automatic blocks feel safer, but they hurt revenue. Use review queues for the gray zone.
- No coordination with payment methods: Radar works best when paired with strong authentication. Don't skip 3D Secure just because Radar is enabled.
- Treating all regions the same: Fraud patterns vary by geography. A transaction from Brazil and one from Germany have different risk profiles.
- Not tracking outcomes: Without measuring chargeback rates, false positives, and conversion impact, you can't optimize rules. Set up monitoring from day one.
Key takeaways
- Stripe Radar scores every payment automatically; use those scores to inform your decisions, not automate them blindly.
- Custom rules and value lists let you block, require authentication, or flag for review based on your risk tolerance and customer profile.
- Manual review queues are a powerful middle ground—capture fraud without rejecting legitimate customers.
- Balance is critical: aggressive rules hurt conversion and revenue. Measure false positives and chargeback rates to optimize over time.
- Layer Radar scores with your own business logic (customer history, email quality, order size) for better precision.
- Integrate 3D Secure and strong authentication alongside Radar for defense in depth.






Comments
Loading comments…