Fraud Detection Signals with Machine Learning on Payment Data
Key fraud signals computable from Paystack data: 1) Velocity — more than N successful transactions from the same card or email in 1 hour. 2) BIN mismatch — card BIN (first 6 digits) country differs from customer's stated country (Paystack returns authorization.country_code). 3) Amount anomaly — large round amounts from first-time customers. 4) Email pattern — disposable email domains. 5) Time-of-day spike — unusual volume at 2-4am local time. These signals feed a risk score. Flag for human review at a threshold; do not auto-block. False positives cost real customers real money.
Computing Fraud Signals from Paystack Data
Paystack transaction data fields useful for fraud detection:
| Signal | Paystack Field | What to Compute | Risk Indicator |
|---|---|---|---|
| Velocity | customer.email, paid_at | Count transactions from same email in last 1 hour | > 3 attempts in 1 hour = high risk |
| BIN mismatch | authorization.bin, authorization.country_code, customer metadata | Compare card-issuing country to customer country | Mismatch on first-time customer = medium risk |
| Amount anomaly | amount, customer history | Compare amount to customer's average transaction | > 5× average from first-time customer = medium risk |
| Round amount | amount | Is amount divisible by 10,000 (kobo)? | Large round amounts from new customers = low-medium risk |
| Email pattern | customer.email | Check domain against disposable email list | Disposable domain = medium risk |
| Card reuse | authorization.signature | Count distinct customers using same card signature | > 2 customers on same card = high risk |
// Risk scorer — computes a score from Paystack transaction data
async function scoreTransaction(txn) {
var signals = [];
var score = 0;
// Velocity: count same email in last hour
var recentCount = await db.transactions.countByEmailInHour(txn.customer.email);
if (recentCount > 3) { signals.push('high_velocity'); score += 40; }
else if (recentCount > 1) { signals.push('moderate_velocity'); score += 15; }
// BIN mismatch: card country vs customer country
var cardCountry = txn.authorization?.country_code;
var customerCountry = txn.metadata?.customer_country;
if (cardCountry && customerCountry && cardCountry !== customerCountry) {
signals.push('bin_country_mismatch'); score += 20;
}
// Card reuse across multiple customers
var cardCustomers = await db.transactions.countCustomersByCardSignature(txn.authorization?.signature);
if (cardCustomers > 2) { signals.push('card_shared_across_customers'); score += 50; }
// Disposable email
var disposableDomains = ['mailinator.com', 'guerrillamail.com', 'temp-mail.org'];
if (disposableDomains.some(d => txn.customer.email.endsWith('@' + d))) {
signals.push('disposable_email'); score += 25;
}
return { score, signals, action: score >= 60 ? 'review' : score >= 30 ? 'monitor' : 'allow' };
}
Learn More
See AI-assisted reconciliation for the broader AI in financial operations context.
Key Takeaways
- ✓Fraud signals are inputs to a risk score — not triggers for automatic blocks.
- ✓Velocity, BIN mismatch, amount anomaly, email pattern, and time-of-day spike are the core signals from Paystack data.
- ✓Paystack returns authorization.country_code and authorization.bin — use these for BIN-country mismatch.
- ✓False positives in fraud detection block legitimate customers — tune thresholds conservatively.
- ✓All fraud flags should route to human review before action, not auto-block.
Frequently Asked Questions
- Does Paystack have its own fraud detection?
- Yes — Paystack has built-in fraud screening that declines high-risk transactions before they reach you. Your application-level fraud detection is an additional layer for business-specific patterns that Paystack's general system may not catch: your specific customer base behavior, known bad actors from your own data, or industry-specific fraud patterns. The two layers are complementary, not alternatives.
- What should I do when a transaction is flagged as high risk?
- Route it to a human fraud analyst for review before taking action. Possible outcomes: allow (false positive), request additional verification from the customer (ID or OTP), or refund and blacklist. Never auto-block based on a risk score alone — false positive rates of even 1% mean blocking legitimate customers regularly. Build a review queue with clear SLAs so fraud decisions are made promptly.
- Can an LLM help with fraud detection beyond rule-based scoring?
- Yes — an LLM can analyze a cluster of flagged transactions and identify patterns your rule-based system missed. Feed it the flagged transaction data (not raw customer PII) and ask: "Are there common patterns across these flagged transactions?" It can surface non-obvious correlations. But keep the LLM in an analysis role — the actual block/allow decision should be made by a human fraud analyst, not the LLM.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon