McTaba Labs logo
Bonaventure OgetoBy Bonaventure Ogeto|

Fraud Signals Available in Paystack Transaction Data

Key fraud signals in Paystack transaction data: (1) risk_action — Paystack's own risk score: "allow", "deny", or "review"; (2) ip_address — the customer's IP at charge time; (3) gateway_response — card network response messages like "Do Not Honor" or "Stolen Card"; (4) channel — card charges carry more fraud risk than bank transfers; (5) authorization.reusable — a card that suddenly becomes reusable without prior subscription setup is suspicious; (6) attempts — multiple failed attempts before success on the same reference indicate probing.

Key Fraud Signal Fields in Paystack Transaction Data

When you call GET /transaction/verify/:reference or receive a charge.success webhook, the transaction object contains several fraud-relevant fields:

FieldLocationWhat It Signals
risk_action Top-level Paystack's internal risk score: "allow", "deny", or "review". "review" means proceed with caution.
ip_address Top-level Customer's IP at time of charge. Velocity-check this against recent failed attempts.
gateway_response Top-level Raw card network response: "Approved", "Do Not Honor", "Stolen Card", "Lost Card", "Restricted Card".
channel Top-level Payment method: "card", "bank", "ussd", "mobile_money". Card has highest fraud surface.
authorization.bin Nested First 6 digits of card. Cross-reference against known fraud BINs from your history.
authorization.country_code Nested Card-issuing country. Mismatch with customer's IP country is a weak fraud signal.
authorization.reusable Nested If true and the customer has no subscription, the card may have been saved without consent.

Building a Fraud Score From Paystack Data

// fraud-score.js
var blockedIPs = new Set(); // Load from Redis or DB

function scoreFraud(transaction) {
  var score = 0;
  var flags = [];

  // Hard signals from Paystack risk engine
  if (transaction.risk_action === 'deny') {
    score += 100;
    flags.push('paystack_risk_deny');
  }
  if (transaction.risk_action === 'review') {
    score += 20;
    flags.push('paystack_risk_review');
  }

  // Gateway response signals
  var badResponses = ['Do Not Honor', 'Stolen Card', 'Lost Card', 'Restricted Card', 'Pick Up Card'];
  if (badResponses.some(r => transaction.gateway_response?.includes(r))) {
    score += 25;
    flags.push('bad_gateway_response:' + transaction.gateway_response);
  }

  // IP velocity signals
  if (blockedIPs.has(transaction.ip_address)) {
    score += 30;
    flags.push('blocked_ip');
  }

  // Channel signals — card on large amount
  if (transaction.channel === 'card' && transaction.amount > 5000000) { // NGN 50,000
    score += 10;
    flags.push('large_card_charge');
  }

  // Country mismatch
  var cardCountry = transaction.authorization?.country_code;
  var ipCountry = resolveIPCountry(transaction.ip_address); // your GeoIP lookup
  if (cardCountry && ipCountry && cardCountry !== ipCountry) {
    score += 15;
    flags.push('country_mismatch:' + cardCountry + '_vs_' + ipCountry);
  }

  return { score, flags, action: score >= 50 ? 'hold' : score >= 20 ? 'log' : 'pass' };
}

// Use in webhook handler
if (event.event === 'charge.success') {
  var fraud = scoreFraud(event.data);
  if (fraud.action === 'hold') {
    await db.orders.update({ id: orderId, status: 'fraud_hold', fraud_flags: fraud.flags });
    await alertTeam('Fraud hold on order ' + orderId + ' score ' + fraud.score);
    // Do NOT fulfill the order yet
  } else {
    await fulfillOrder(orderId);
  }
}

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Learn More

Key Takeaways

  • Paystack includes a risk_action field on transaction data: "allow", "deny", or "review". Log this for every transaction.
  • The ip_address field identifies the customer's IP. Cross-reference it against blocklists and velocity checks.
  • gateway_response strings like "Do Not Honor", "Stolen Card", and "Lost Card" are hard fraud signals — escalate these immediately.
  • Card channel transactions carry higher fraud risk than bank transfers. Apply extra scrutiny to card charges for high-value orders.
  • A transaction with many prior failed attempts before succeeding suggests probing — flag these for manual review.
  • Build a simple fraud score by summing risk points: blocked IP (+30), bad gateway_response (+25), risk_action=review (+20), card channel on large amount (+10).

Frequently Asked Questions

What does risk_action: "review" mean in practice?
It means Paystack's fraud engine flagged the transaction as potentially suspicious but did not block it outright. You should log these transactions, consider adding a manual review step before fulfilling high-value orders, and monitor whether "review" transactions have a higher chargeback rate in your data.
Can I access these fraud fields in the Paystack dashboard?
Some fields like gateway_response and channel are visible in the transaction detail view. risk_action and ip_address are in the API response. For systematic fraud analysis, pull transaction data via the API and store these fields in your own database for querying.
Should I block transactions with risk_action: "deny"?
Paystack already blocks deny-rated transactions before they succeed — a charge.success webhook will not fire for them. If you see risk_action: "deny" in a failed transaction notification, record it. If you proactively check via the API, treat deny as a hard stop.
What is the most reliable fraud signal in Paystack data?
gateway_response values like "Stolen Card" and "Lost Card" are the most reliable — these come directly from the card-issuing bank and are almost never false positives. risk_action from Paystack's engine is also reliable. IP velocity (same IP, many different emails, rapid succession) is highly predictive for card testing attacks.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account