Bonaventure OgetoBy Bonaventure Ogeto|

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:

SignalPaystack FieldWhat to ComputeRisk Indicator
Velocitycustomer.email, paid_atCount transactions from same email in last 1 hour> 3 attempts in 1 hour = high risk
BIN mismatchauthorization.bin, authorization.country_code, customer metadataCompare card-issuing country to customer countryMismatch on first-time customer = medium risk
Amount anomalyamount, customer historyCompare amount to customer's average transaction> 5× average from first-time customer = medium risk
Round amountamountIs amount divisible by 10,000 (kobo)?Large round amounts from new customers = low-medium risk
Email patterncustomer.emailCheck domain against disposable email listDisposable domain = medium risk
Card reuseauthorization.signatureCount 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.

Sign up for the McTaba newsletter

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