
Fraud Detection
- 96 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Protect a store from fraudulent orders using risk scoring, 3D Secure challenges, velocity checks, and manual review queues.
About
Layers platform-native risk scoring, 3DS authentication, velocity checks, and manual review to reduce chargebacks. A developer uses it when chargeback rates climb, entering new markets, or selling high-resale-value goods.
- Per-platform built-in fraud analysis vs recommended fraud-service table
- Velocity checks, 3DS, and manual review queues for suspicious orders
Fraud Detection by the numbers
- 96 all-time installs (skills.sh)
- Ranked #1,031 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill fraud-detectionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 96 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Protect a store from fraudulent orders using risk scoring, 3D Secure challenges, velocity checks, and manual review queues.
Files
Fraud Detection
Overview
Payment fraud costs e-commerce merchants 2–3% of revenue through chargebacks, lost goods, and dispute fees. Effective fraud detection layers platform-native risk scoring, 3D Secure authentication, velocity checks, and manual review queues for suspicious orders. The right approach depends on your platform — Shopify includes a built-in fraud analysis tool, while WooCommerce and BigCommerce require a dedicated fraud prevention service or payment processor's fraud tools.
When to Use This Skill
- When chargeback rates exceed 0.5% of transaction volume (Visa's threshold for "excessive" disputes is 0.9%)
- When launching in a new market with unfamiliar fraud patterns
- When selling high-value, easily resold goods (electronics, gift cards, luxury items)
- When you observe account takeover patterns, card testing, or bulk bot purchases
- When building or auditing a checkout flow that processes card-not-present transactions
Core Instructions
Step 1: Determine the merchant's platform and choose the right fraud tools
| Platform | Built-in Fraud Analysis | Recommended Fraud Service |
|---|---|---|
| Shopify | Shopify Fraud Analysis (included free); basic risk scoring on orders | Enable Stripe Radar or Signifyd (Shopify App Store) for advanced ML scoring |
| WooCommerce | None built in | Use Stripe (with Radar) or Braintree as payment processor; or install Kount or NoFraud plugin |
| BigCommerce | Payment processor fraud tools (varies by processor) | Signifyd integrates natively with BigCommerce; NoFraud also supports BigCommerce |
| All platforms | — | Stripe Radar (if using Stripe) provides ML-based fraud scoring on every charge at no extra cost |
Step 2: Enable and configure platform-native fraud tools
---
Shopify
Shopify includes a Fraud analysis indicator on every order based on signals like IP/billing address mismatch, card verification failure, and known fraud patterns.
Reviewing fraud indicators: 1. Open any order in Shopify admin 2. Click Fraud analysis in the order details panel 3. Shopify shows a risk level (High / Medium / Low) with specific reasons (e.g., "Card verification value failed", "IP and billing address country differ")
Configuring fraud response rules: 1. Go to Settings → Payments → Fraud prevention (if using Shopify Payments) 2. Enable Automatic review for orders flagged as high risk — Shopify will hold these orders and send you an email 3. Set Automatically cancel for orders Shopify deems highest risk
Signifyd (Shopify App Store — Guaranteed Fraud Protection): Signifyd provides chargeback guarantees — if they approve an order and it results in a chargeback, they reimburse you. This is the most comprehensive solution for Shopify. 1. Install Signifyd from the Shopify App Store 2. Signifyd automatically reviews every order using ML scoring 3. Orders Signifyd flags go into a review queue in the Signifyd console 4. Set up the Signifyd Shopify integration to automatically hold or cancel high-risk orders
---
WooCommerce
WooCommerce does not include fraud detection. You need either a payment processor with built-in fraud tools or a dedicated plugin.
Option A: Stripe Radar (recommended if using Stripe for WooCommerce)
If using the WooCommerce Stripe Payment Gateway: 1. Stripe Radar is automatically enabled — it scores every charge on your Stripe account 2. In the Stripe Dashboard, go to Radar → Rules to add custom blocking/review rules:
# Block orders over $500 from high-fraud-rate IP countries
Block if :order_amount: > 50000 and :ip_country: in ('NG', 'RO')
# Review first-time customers placing large orders
Review if :order_amount: > 20000 and :customer_account_age: < 7
# Block cards used more than 3 times in the last hour
Block if :card_velocity_hour: > 33. Orders flagged for review appear in Stripe Dashboard → Radar → Reviews
Option B: WooCommerce Anti-Fraud plugin (free) 1. Install WooCommerce Anti-Fraud from the plugin directory 2. Configure risk scoring rules based on:
- Order amount thresholds
- New customer + high value combination
- Proxy/VPN IP detection
- Billing/shipping country mismatch
3. High-risk orders are placed in "On Hold" status for manual review
Option C: Kount or NoFraud (enterprise) For high-volume WooCommerce stores, enterprise fraud prevention platforms offer:
- Kount: full fraud management platform with ML scoring, manual review tools, and chargeback management
- NoFraud: provides a fraud protection guarantee similar to Signifyd; integrates via WooCommerce plugin
---
BigCommerce
Signifyd for BigCommerce: 1. Install the Signifyd app from the BigCommerce App Marketplace 2. Configure automatic hold or cancellation of high-risk orders 3. Signifyd's guarantee covers chargebacks on approved orders
Payment processor fraud tools:
- Stripe (via BigCommerce Stripe integration): Radar is included; configure rules in the Stripe Dashboard
- PayPal: PayPal's fraud management filters are available in your PayPal business account settings
- Braintree: Advanced fraud protection via Kount is available as an add-on
---
Custom / Headless
For custom storefronts using Stripe, leverage Stripe Radar for ML scoring and add application-layer velocity checks for business-specific patterns.
Retrieve Stripe's fraud score after payment attempt:
const paymentIntent = await stripe.paymentIntents.retrieve(paymentIntentId, {
expand: ['latest_charge'],
});
const riskScore = paymentIntent.latest_charge.outcome?.risk_score; // 0–100
const riskLevel = paymentIntent.latest_charge.outcome?.risk_level; // 'normal', 'elevated', 'highest'Request 3D Secure for high-risk transactions (shifts chargeback liability to card issuer):
const paymentIntent = await stripe.paymentIntents.create({
amount: order.totalCents,
currency: 'usd',
payment_method_options: {
card: {
// 'automatic' = Stripe decides; 'challenge' = always require 3DS for high-risk
request_three_d_secure: riskScore > 70 ? 'challenge' : 'automatic',
},
},
});Application-layer velocity checks:
import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL!);
async function checkVelocity(params: { email: string; ip: string; cardFingerprint: string; amountCents: number }) {
const { email, ip, cardFingerprint, amountCents } = params;
// IP: max 10 orders per hour
const ipCount = await redis.incr(`vel:ip:${ip}`);
if (ipCount === 1) await redis.expire(`vel:ip:${ip}`, 3600);
if (ipCount > 10) return { allowed: false, reason: 'ip_velocity' };
// Email: max 5 orders per 24 hours
const emailCount = await redis.incr(`vel:email:${email.toLowerCase()}`);
if (emailCount === 1) await redis.expire(`vel:email:${email.toLowerCase()}`, 86400);
if (emailCount > 5) return { allowed: false, reason: 'email_velocity' };
// Card: max $500 per day
const spendKey = `vel:spend:${cardFingerprint}`;
const currentSpend = parseInt(await redis.get(spendKey) ?? '0');
if (currentSpend + amountCents > 50000) return { allowed: false, reason: 'daily_spend_limit' };
return { allowed: true };
}Manual review queue:
async function flagForManualReview(orderId: string, riskScore: number, signals: Record<string, unknown>) {
// Hold the order — do NOT fulfill; do NOT capture payment (authorize only)
await db.orders.update(orderId, {
status: 'pending_fraud_review',
fraud_risk_score: riskScore,
fraud_signals: signals,
review_requested_at: new Date(),
});
// Notify fraud review team
await sendSlackAlert('#fraud-review', {
text: `Order ${orderId} flagged for review. Risk score: ${riskScore}/100`,
actions: [
{ text: 'Approve', url: `${ADMIN_URL}/fraud-review/${orderId}/approve` },
{ text: Reject', url: `${ADMIN_URL}/fraud-review/${orderId}/reject` },
],
});
}
// Auto-cancel unreviewed orders after 48 hours
async function expireUnreviewedOrders() {
const expired = await db.orders.findExpiredReviews(48);
for (const order of expired) {
await stripe.paymentIntents.cancel(order.payment_intent_id);
await db.orders.update(order.id, { status: 'fraud_review_expired' });
await sendOrderCancellationEmail(order);
}
}Best Practices
- Layer defenses — no single signal reliably stops all fraud; combine Stripe Radar, velocity checks, IP reputation, and device fingerprinting
- Use authorize-then-capture for high-risk orders — authorize at checkout to hold funds, then capture only after fraud review passes; releasing an authorization is less costly than issuing a refund
- Track false positive rate as a KPI — if more than 1% of legitimate orders are blocked or held, your rules are too aggressive; measure both fraud losses and revenue lost to false positives
- Rotate and obfuscate fraud rules — sophisticated fraudsters probe checkout flows to find rule thresholds; never expose block reasons in API error messages
- Keep a deny-list of fraudulent emails, cards, and devices — once fraud is confirmed via chargeback, add the identifiers to a blocklist for future orders
- Review your chargeback rate monthly — if it climbs above 0.5% for Visa/Mastercard, review your fraud rules; exceeding 0.9% triggers Visa's dispute monitoring program
Common Pitfalls
| Problem | Solution |
|---|---|
| 3DS causing checkout abandonment | Use automatic 3DS mode — Stripe decides when a challenge is needed; this frictionlessly authenticates low-risk transactions |
| Velocity rules blocking legitimate bulk buyers | Whitelist B2B customers or high-LTV customer segments from velocity rules; use tiered limits based on account history |
| Manual review queue growing unboundedly | Set SLA targets (4-hour review window); implement auto-cancellation for orders not reviewed within 48 hours |
| Chargeback filed despite 3DS authentication | Verify your processor submits 3DS authentication data (eci, cavv, xid) correctly; without these fields the liability shift does not apply |
| Redis velocity keys never expiring | Always call EXPIRE when setting a new key; use SET key value EX seconds NX for atomic set-if-not-exists with expiry |
Related Skills
- @secure-checkout
- @account-security
- @stripe-integration
- @bot-protection
- @gdpr-ecommerce
{
"context": "Tests whether the agent implements the composite fraud scoring function with the correct signal weights, decision thresholds, and risk-score-based 3DS enforcement using Stripe PaymentIntent options.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Stripe score at 40%",
"max_score": 10,
"description": "The Stripe Radar risk score is multiplied by 0.4 (weighted at 40%) as the base of the composite score"
},
{
"name": "Velocity violation weight",
"max_score": 8,
"description": "Each velocity violation adds 15 points to the score (velocityViolations * 15)"
},
{
"name": "Address mismatch weight",
"max_score": 8,
"description": "Address mismatch (billing ≠ shipping country) adds exactly 10 points"
},
{
"name": "Proxy/VPN weight",
"max_score": 8,
"description": "Proxy or VPN detection adds exactly 20 points"
},
{
"name": "Email age weight",
"max_score": 8,
"description": "Email age less than 24 hours adds exactly 15 points"
},
{
"name": "First order + high amount weight",
"max_score": 8,
"description": "First order combined with order amount over 30000 cents adds exactly 10 points"
},
{
"name": "Unknown device weight",
"max_score": 6,
"description": "Device fingerprint not previously seen adds exactly 5 points"
},
{
"name": "Score capped at 100",
"max_score": 4,
"description": "The returned score is capped at a maximum of 100 (Math.min(100, ...) or equivalent)"
},
{
"name": "Block threshold",
"max_score": 8,
"description": "getFraudDecision returns 'block' for scores >= 80"
},
{
"name": "Review threshold",
"max_score": 8,
"description": "getFraudDecision returns 'review' for scores >= 50 and < 80"
},
{
"name": "3DS challenge for high risk",
"max_score": 12,
"description": "Sets request_three_d_secure to 'challenge' when risk score is above 70"
},
{
"name": "3DS automatic for normal risk",
"max_score": 12,
"description": "Sets request_three_d_secure to 'automatic' for risk scores at or below 70 (not omitting the field, and not always using 'challenge')"
}
]
}
Payment Risk Scoring Engine
Problem/Feature Description
A subscription box company has grown rapidly into international markets and is now seeing a significant uptick in fraudulent orders originating from VPN-masked IPs, newly registered email addresses, and cards that have never been used on the platform before. The fraud team has assembled a list of signals they want combined into a single numeric risk score that can drive automated decisions — without requiring manual review of every single transaction.
They need a TypeScript module that accepts multiple fraud signals as inputs and produces both a numeric risk score and a clear approve/review/block decision. The module must also integrate with Stripe PaymentIntents so that high-risk orders require stronger authentication from the card issuer, while low-risk transactions stay as frictionless as possible.
The company's fraud analyst has specifically requested that the Stripe Radar machine-learning score be the dominant input, but that local signals supplement it with meaningful weight. They also want the decision logic to be clean and auditable.
Output Specification
Produce a single TypeScript file named fraud-score.ts that exports:
- A
calculateRiskScore(signals: FraudSignals): numberfunction - A
getFraudDecision(score: number): 'approve' | 'review' | 'block'function - A
createPaymentIntentWithFraudCheck(order: Order, customer: Customer): Promise<Stripe.PaymentIntent>function (types can be stubbed/simplified)
Include a FraudSignals interface in the file.
Also write a test-cases.md file with at least 3 worked examples showing input signals → expected score → expected decision, calculated by hand, to demonstrate that the scoring logic is correct.
Do not install or run anything; source code and the worked examples are the deliverables.
{
"context": "Tests whether the agent correctly implements the manual fraud review queue: setting the right order status, using authorize-only (no capture), sending Slack alerts to the correct channel, auto-expiring after 48 hours by cancelling the PaymentIntent, and avoiding exposure of fraud rule details in client-facing error responses.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Order status set correctly",
"max_score": 8,
"description": "Sets the order status to 'pending_fraud_review' (exact string) when flagging an order"
},
{
"name": "No fulfillment on flag",
"max_score": 8,
"description": "The flagForManualReview function does NOT trigger order fulfillment — a comment or code structure explicitly shows fulfillment is withheld"
},
{
"name": "Authorize-only (no capture)",
"max_score": 10,
"description": "The design-notes.md or code comments explicitly state that payment is authorized but NOT captured until review passes (capture_method: 'manual' or equivalent discussion)"
},
{
"name": "Slack channel name",
"max_score": 8,
"description": "Sends Slack notification to the channel '#fraud-review' (exact channel name)"
},
{
"name": "Slack message includes risk score",
"max_score": 6,
"description": "The Slack alert includes the order ID and the numeric risk score out of 100"
},
{
"name": "48-hour expiry window",
"max_score": 10,
"description": "expireUnreviewedOrders uses a 48-hour (not 24-hour or 72-hour) window to identify stale review orders"
},
{
"name": "PaymentIntent cancelled on expiry",
"max_score": 10,
"description": "Calls stripe.paymentIntents.cancel() for each expired order (not refund, not void — cancel)"
},
{
"name": "Expiry status set",
"max_score": 8,
"description": "Sets order status to 'fraud_review_expired' after cancellation"
},
{
"name": "No fraud reason in client error",
"max_score": 12,
"description": "The checkout-error-handler.ts does NOT include internal fraud signal details, rule thresholds, or block reasons in the HTTP response body sent to the client"
},
{
"name": "fraudRiskScore stored",
"max_score": 6,
"description": "Stores the numeric risk score on the order record when flagging (e.g., fraudRiskScore field)"
},
{
"name": "reviewRequestedAt timestamp",
"max_score": 6,
"description": "Stores a timestamp (reviewRequestedAt or equivalent) on the order when it is flagged for review"
},
{
"name": "Cancellation email sent",
"max_score": 8,
"description": "Sends an order cancellation email to the customer when an unreviewed order expires"
}
]
}
Fraud Review Workflow Implementation
Problem/Feature Description
A luxury goods retailer processes high-value orders online and needs a robust workflow to handle transactions that a risk-scoring system has flagged as suspicious. In the current system, flagged orders are sometimes accidentally fulfilled before a fraud analyst can inspect them, and on at least two occasions, a payment was captured on an order that later turned out to be fraudulent — resulting in a costly chargeback.
The operations team wants a well-defined server-side workflow for what happens when an order is flagged: the order should be held, the payment should be secured but not settled, the fraud review team should be immediately notified through their existing communication channel, and there should be a safety valve that automatically cleans up orders that nobody reviewed within a reasonable window. The team is also concerned about leaking internal fraud logic to bad actors — they've heard that sophisticated fraudsters will deliberately trigger errors to probe detection thresholds.
Output Specification
Produce a TypeScript file named fraud-review.ts that exports:
- A
flagForManualReview(orderId: string, riskScore: number, signals: object, stripe: any, db: any): Promise<void>function - A
expireUnreviewedOrders(stripe: any, db: any): Promise<void>function (intended to be called by a cron job)
Also produce a checkout-error-handler.ts file that demonstrates how the checkout API should respond to the client when an order is blocked or held for review (i.e., what the HTTP error response looks like).
Write a design-notes.md explaining the rationale for the payment handling strategy chosen for flagged orders.
{
"context": "Tests whether the agent implements Redis velocity checks using ioredis with the correct per-dimension thresholds, time windows, Redis key naming conventions, and proper TTL/EXPIRE handling to avoid keys that never expire.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Uses ioredis",
"max_score": 8,
"description": "Imports and uses 'ioredis' (not 'redis', 'node-redis', or another package) for Redis access"
},
{
"name": "IP key naming",
"max_score": 7,
"description": "Uses the key pattern 'velocity:ip:<ip>' (colon-separated, with 'velocity:ip:' prefix) for IP tracking"
},
{
"name": "Email key naming",
"max_score": 7,
"description": "Uses the key pattern 'velocity:email:<email>' for email tracking"
},
{
"name": "Card key naming",
"max_score": 7,
"description": "Uses the key pattern 'velocity:card:<fingerprint>' for card fingerprint tracking"
},
{
"name": "Spend key naming",
"max_score": 7,
"description": "Uses the key pattern 'velocity:spend:<fingerprint>' for daily spend tracking"
},
{
"name": "IP threshold and window",
"max_score": 8,
"description": "Blocks when IP order count exceeds 10 within a 1-hour window (3600 seconds)"
},
{
"name": "Email threshold and window",
"max_score": 8,
"description": "Blocks when email order count exceeds 5 within a 24-hour window (86400 seconds)"
},
{
"name": "Card threshold and window",
"max_score": 8,
"description": "Blocks when card fingerprint usage exceeds 3 within a 24-hour window"
},
{
"name": "Daily spend limit",
"max_score": 8,
"description": "Blocks when cumulative spend for a card fingerprint exceeds 50000 cents ($500) within a day"
},
{
"name": "EXPIRE on new keys",
"max_score": 12,
"description": "Calls EXPIRE (or equivalent TTL method) on a Redis key immediately after it is first created (i.e., when the counter was previously zero or absent), not just when it is incremented"
},
{
"name": "Reason codes returned",
"max_score": 10,
"description": "Returns distinct reason strings for each type of violation: 'ip_velocity_exceeded', 'email_velocity_exceeded', 'card_velocity_exceeded', 'daily_spend_exceeded'"
},
{
"name": "recordSuccessfulTransaction updates spend",
"max_score": 10,
"description": "The recordSuccessfulTransaction function increments the spend key and sets a TTL if the key is new (ttl < 0 check or equivalent)"
}
]
}
Checkout Velocity Guard Module
Problem/Feature Description
An online marketplace selling consumer electronics has been experiencing a wave of card-testing attacks and coordinated fraud attempts during flash sales. The engineering team has noticed that the existing payment processor's built-in fraud tools are not enough — attackers are rotating IP addresses but reusing the same card numbers across many freshly created accounts, and some are also spending heavily on a single card before it gets reported.
The team wants a standalone TypeScript module that can be dropped into the checkout API to perform velocity checks before any payment is attempted. The module should track order attempts per IP address, per email address, per card fingerprint (counting distinct account usage), and total spend per card — all backed by Redis so it works across multiple API server instances. It must also record successful transactions so that the running spend total stays accurate.
Output Specification
Produce a single TypeScript file named velocity.ts that exports:
- A
checkVelocityfunction accepting{ email, ip, cardFingerprint, amount }and returning{ allowed: boolean; reason?: string } - A
recordSuccessfulTransactionfunction accepting(cardFingerprint: string, amount: number)
Also produce a short notes.md explaining the key design decisions — in particular, any limits used, how Redis keys are structured, and how expiry is handled.
Do not install or run anything; the deliverable is source code only.
{
"name": "finsi/fraud-detection",
"version": "0.1.0",
"summary": "Rule-based and ML fraud scoring with 3DS, velocity checks, and manual review",
"skills": {
"fraud-detection": {
"path": "SKILL.md"
}
}
}