prepare(" SELECT id, email, first_name, last_name, gender, date_of_birth, postcode, status, created_at, email_verified, onboarding_completed FROM users WHERE email = ? LIMIT 1 "); $stmt->execute([$searchEmail]); $user = $stmt->fetch(PDO::FETCH_ASSOC); if (!$user) $searchError = 'No member found with that email.'; } catch (Exception $e) { $searchError = 'Database error: ' . $e->getMessage(); } } // ─── If member found, run the full audit ─── if ($user) { $userId = (int)$user['id']; $report = []; try { $panelPdo = getPanelDBConnection(); // Tier configuration $report['tier_cfg'] = null; try { $st = $panelPdo->prepare("SELECT * FROM member_referral_tiers WHERE user_id = ?"); $st->execute([$userId]); $report['tier_cfg'] = $st->fetch(PDO::FETCH_ASSOC) ?: null; } catch (Exception $e) { /* table may not exist */ } // Aggregate referral stats $st = $panelPdo->prepare(" SELECT COUNT(*) AS total_clicks, COALESCE(SUM(signup_completed), 0) AS signups, COALESCE(SUM(email_verified), 0) AS verified, COUNT(DISTINCT ip_address) AS unique_ips, COUNT(DISTINCT referee_user_id) AS unique_referees, COUNT(DISTINCT user_agent) AS unique_uas, MIN(clicked_at) AS first_click, MAX(clicked_at) AS last_click FROM member_referral_clicks WHERE referrer_user_id = ? "); $st->execute([$userId]); $report['aggregate'] = $st->fetch(PDO::FETCH_ASSOC) ?: []; // Engagement (mobile + profilers) $st = $panelPdo->prepare(" SELECT COUNT(*) AS verified_referees, SUM(CASE WHEN EXISTS(SELECT 1 FROM mobile_verifications mv WHERE mv.user_id = u.id AND mv.is_verified = 1) THEN 1 ELSE 0 END) AS mobile_verified, SUM(CASE WHEN (SELECT COUNT(*) FROM profiler_completion WHERE user_id = u.id AND is_completed = 1) > 0 THEN 1 ELSE 0 END) AS profilers_started FROM users u JOIN member_referral_clicks mrc ON mrc.referee_user_id = u.id WHERE mrc.referrer_user_id = ? AND mrc.email_verified = 1 "); $st->execute([$userId]); $report['engagement'] = $st->fetch(PDO::FETCH_ASSOC) ?: []; // Timing analysis $st = $panelPdo->prepare(" SELECT COUNT(*) AS verified_count, MIN(TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at)) AS click_to_signup_min_s, ROUND(AVG(TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at))) AS click_to_signup_avg_s, MAX(TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at)) AS click_to_signup_max_s, MIN(TIMESTAMPDIFF(SECOND, signed_up_at, verified_at)) AS signup_to_verify_min_s, ROUND(AVG(TIMESTAMPDIFF(SECOND, signed_up_at, verified_at))) AS signup_to_verify_avg_s, MAX(TIMESTAMPDIFF(SECOND, signed_up_at, verified_at)) AS signup_to_verify_max_s, SUM(CASE WHEN TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at) < 15 THEN 1 ELSE 0 END) AS fast_signups_under_15s, SUM(CASE WHEN TIMESTAMPDIFF(SECOND, signed_up_at, verified_at) < 30 THEN 1 ELSE 0 END) AS fast_verifies_under_30s FROM member_referral_clicks WHERE referrer_user_id = ? AND email_verified = 1 AND clicked_at IS NOT NULL AND signed_up_at IS NOT NULL AND verified_at IS NOT NULL "); $st->execute([$userId]); $report['timing'] = $st->fetch(PDO::FETCH_ASSOC) ?: []; // Hour-of-day distribution (IST) $st = $panelPdo->prepare(" SELECT HOUR(CONVERT_TZ(signed_up_at, '+00:00', '+05:30')) AS ist_hour, COUNT(*) AS signups FROM member_referral_clicks WHERE referrer_user_id = ? AND email_verified = 1 GROUP BY ist_hour ORDER BY ist_hour "); $st->execute([$userId]); $report['hours'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; // Burst detection (with IP diversity) $st = $panelPdo->prepare(" SELECT DATE_FORMAT(CONVERT_TZ(signed_up_at, '+00:00', '+05:30'), '%Y-%m-%d %H:00') AS hour_window_ist, COUNT(*) AS signups, COUNT(DISTINCT ip_address) AS unique_ips, ROUND(COUNT(DISTINCT ip_address) / COUNT(*), 2) AS ip_diversity FROM member_referral_clicks WHERE referrer_user_id = ? AND email_verified = 1 AND signed_up_at IS NOT NULL GROUP BY hour_window_ist HAVING signups >= 3 ORDER BY signups DESC LIMIT 20 "); $st->execute([$userId]); $report['bursts'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; // Top IPs (any IP feeding multiple signups) $st = $panelPdo->prepare(" SELECT ip_address, COUNT(*) AS hits, SUM(signup_completed) AS signups, SUM(email_verified) AS verified FROM member_referral_clicks WHERE referrer_user_id = ? GROUP BY ip_address HAVING hits > 1 ORDER BY hits DESC LIMIT 15 "); $st->execute([$userId]); $report['top_ips'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; // Email pattern duplicates (Gmail dot/plus tricks) $st = $panelPdo->prepare(" SELECT REPLACE(SUBSTRING_INDEX(SUBSTRING_INDEX(u.email, '@', 1), '+', 1), '.', '') AS norm_local, SUBSTRING_INDEX(u.email, '@', -1) AS domain, COUNT(*) AS dupes, GROUP_CONCAT(u.email ORDER BY u.email SEPARATOR ' | ') AS emails FROM member_referral_clicks mrc JOIN users u ON u.id = mrc.referee_user_id WHERE mrc.referrer_user_id = ? GROUP BY norm_local, domain HAVING dupes > 1 ORDER BY dupes DESC LIMIT 10 "); $st->execute([$userId]); $report['email_dupes'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; // Geographic — region (first digit) $st = $panelPdo->prepare(" SELECT LEFT(u.postcode, 1) AS region_code, CASE LEFT(u.postcode, 1) WHEN '1' THEN 'Delhi / Haryana / Punjab / HP / J&K' WHEN '2' THEN 'UP / Uttarakhand' WHEN '3' THEN 'Rajasthan / Gujarat' WHEN '4' THEN 'Maharashtra / MP / Goa / Chhattisgarh' WHEN '5' THEN 'Telangana / AP / Karnataka' WHEN '6' THEN 'Tamil Nadu / Kerala / Puducherry' WHEN '7' THEN 'West Bengal / Odisha / NE States' WHEN '8' THEN 'Bihar / Jharkhand' ELSE 'Other / Unknown' END AS region, COUNT(*) AS members FROM users u JOIN member_referral_clicks mrc ON mrc.referee_user_id = u.id WHERE mrc.referrer_user_id = ? AND mrc.email_verified = 1 AND u.postcode IS NOT NULL AND u.postcode != '' GROUP BY region_code, region ORDER BY members DESC "); $st->execute([$userId]); $report['regions'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; // Geographic — district (first 3 digits) $st = $panelPdo->prepare(" SELECT LEFT(u.postcode, 3) AS prefix, COUNT(*) AS members, GROUP_CONCAT(DISTINCT LEFT(u.postcode, 6) ORDER BY u.postcode SEPARATOR ', ') AS sample_pins FROM users u JOIN member_referral_clicks mrc ON mrc.referee_user_id = u.id WHERE mrc.referrer_user_id = ? AND mrc.email_verified = 1 AND u.postcode IS NOT NULL AND u.postcode != '' GROUP BY prefix ORDER BY members DESC LIMIT 25 "); $st->execute([$userId]); $report['districts'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; // Get referee user IDs for cross-DB survey lookup $st = $panelPdo->prepare(" SELECT referee_user_id FROM member_referral_clicks WHERE referrer_user_id = ? AND referee_user_id IS NOT NULL AND email_verified = 1 "); $st->execute([$userId]); $refIds = array_map('intval', array_column($st->fetchAll(PDO::FETCH_ASSOC), 'referee_user_id')); // Survey activity (cross-DB → rrshop) $report['surveys'] = ['referees_with_invites' => 0, 'total_invites' => 0, 'total_completes' => 0, 'unique_with_completes' => 0, 'bad_quality' => 0]; if (!empty($refIds)) { try { $shopPdo = getDBConnection(); $placeholders = implode(',', array_fill(0, count($refIds), '?')); $st = $shopPdo->prepare(" SELECT COUNT(DISTINCT sent_to_user_id) AS referees_with_invites, COUNT(*) AS total_invites, SUM(CASE WHEN status = 'complete' THEN 1 ELSE 0 END) AS total_completes, COUNT(DISTINCT CASE WHEN status = 'complete' THEN sent_to_user_id END) AS unique_with_completes, SUM(CASE WHEN quality_flag IN ('speedster','ip_duplicate','client_flagged') THEN 1 ELSE 0 END) AS bad_quality FROM survey_urls WHERE sent_to_user_id IN ($placeholders) "); $st->execute($refIds); $report['surveys'] = $st->fetch(PDO::FETCH_ASSOC) ?: $report['surveys']; } catch (Exception $e) { error_log("Audit survey lookup error: " . $e->getMessage()); } } // Recent referees detail (top 30) $st = $panelPdo->prepare(" SELECT mrc.id, mrc.ip_address, mrc.email AS masked_email, mrc.signup_completed, mrc.email_verified, mrc.signup_reward_paid, mrc.clicked_at, mrc.signed_up_at, mrc.verified_at, u.email AS full_email, u.gender, u.postcode, u.status, EXISTS(SELECT 1 FROM mobile_verifications mv WHERE mv.user_id = u.id AND mv.is_verified = 1) AS mob_v, (SELECT COUNT(*) FROM profiler_completion WHERE user_id = u.id AND is_completed = 1) AS prof_c FROM member_referral_clicks mrc LEFT JOIN users u ON u.id = mrc.referee_user_id WHERE mrc.referrer_user_id = ? ORDER BY mrc.clicked_at DESC LIMIT 30 "); $st->execute([$userId]); $report['recent'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: []; } catch (Exception $e) { error_log("Referral audit error: " . $e->getMessage()); } // ─── Compute fraud verdict score (0=clean, higher=worse) ─── $score = 0; $signals = []; $agg = $report['aggregate'] ?? []; $eng = $report['engagement'] ?? []; $tim = $report['timing'] ?? []; $bursts = $report['bursts'] ?? []; $verifiedN = max((int)($agg['verified'] ?? 0), 1); // 1) Aggregate IP diversity $aggIpRatio = (int)($agg['total_clicks'] ?? 0) > 0 ? (int)($agg['unique_ips'] ?? 0) / (int)$agg['total_clicks'] : 0; if ($aggIpRatio >= 0.5) { $signals[] = ['ok', 'IP diversity (overall): ' . round($aggIpRatio*100) . '% — healthy']; } elseif ($aggIpRatio >= 0.3) { $score += 1; $signals[] = ['warn', 'IP diversity (overall): ' . round($aggIpRatio*100) . '% — borderline']; } else { $score += 2; $signals[] = ['bad', 'IP diversity (overall): ' . round($aggIpRatio*100) . '% — too low']; } // 2) Worst-burst IP diversity $worstBurst = 1.0; foreach ($bursts as $b) { if ((float)$b['ip_diversity'] < $worstBurst) $worstBurst = (float)$b['ip_diversity']; } if (!empty($bursts)) { if ($worstBurst >= 0.7) { $signals[] = ['ok', 'Worst-burst IP diversity: ' . round($worstBurst*100) . '% — healthy']; } elseif ($worstBurst >= 0.3){ $score += 1; $signals[] = ['warn', 'Worst-burst IP diversity: ' . round($worstBurst*100) . '%']; } else { $score += 3; $signals[] = ['bad', 'Worst-burst IP diversity: ' . round($worstBurst*100) . '% — concentrated']; } } // 3) Mobile verified rate $mobRate = $verifiedN > 0 ? (int)($eng['mobile_verified'] ?? 0) / $verifiedN : 0; if ($mobRate >= 0.30) { $signals[] = ['ok', 'Mobile-verified rate: ' . round($mobRate*100) . '%']; } elseif ($mobRate >= 0.10) { $score += 1; $signals[] = ['warn', 'Mobile-verified rate: ' . round($mobRate*100) . '% — low']; } else { $score += 2; $signals[] = ['bad', 'Mobile-verified rate: ' . round($mobRate*100) . '% — almost no engagement']; } // 4) Profilers started rate $profRate = $verifiedN > 0 ? (int)($eng['profilers_started'] ?? 0) / $verifiedN : 0; if ($profRate >= 0.30) { $signals[] = ['ok', 'Profiler-started rate: ' . round($profRate*100) . '%']; } elseif ($profRate >= 0.10) { $score += 1; $signals[] = ['warn', 'Profiler-started rate: ' . round($profRate*100) . '% — low']; } else { $score += 2; $signals[] = ['bad', 'Profiler-started rate: ' . round($profRate*100) . '% — empty profiles']; } // 5) Fast verifies (<30s) rate $vCount = (int)($tim['verified_count'] ?? 0); $fast30 = (int)($tim['fast_verifies_under_30s'] ?? 0); $fastRate = $vCount > 0 ? $fast30 / $vCount : 0; if ($fastRate < 0.20) { $signals[] = ['ok', 'Fast-verify rate (<30s): ' . round($fastRate*100) . '%']; } elseif ($fastRate < 0.50) { $signals[] = ['info', 'Fast-verify rate (<30s): ' . round($fastRate*100) . '% — note']; } else { $score += 1; $signals[] = ['warn', 'Fast-verify rate (<30s): ' . round($fastRate*100) . '% — high (event style or automation)']; } // 6) Scripted-fast signups (<15s) $fast15 = (int)($tim['fast_signups_under_15s'] ?? 0); if ($fast15 == 0) { $signals[] = ['ok', 'Scripted-fast signups (<15s): 0']; } elseif ($fast15 < 5) { $score += 1; $signals[] = ['warn', "Scripted-fast signups (<15s): $fast15"]; } else { $score += 3; $signals[] = ['bad', "Scripted-fast signups (<15s): $fast15 — likely automation"]; } // 7) Email pattern duplicates $emailDupCount = count($report['email_dupes'] ?? []); if ($emailDupCount == 0) { $signals[] = ['ok', 'Email pattern duplicates: none']; } elseif ($emailDupCount <= 3) { $score += 1; $signals[] = ['warn', "Email pattern duplicates: $emailDupCount (possible Gmail tricks)"]; } else { $score += 3; $signals[] = ['bad', "Email pattern duplicates: $emailDupCount — same person, multiple aliases"]; } // 8) Surveys completed by referees $sv = $report['surveys'] ?? []; $totalRefs = max(count($refIds), 1); $surveyEngagement = (int)($sv['unique_with_completes'] ?? 0) / $totalRefs; if ($verifiedN >= 20) { // only meaningful at scale if ($surveyEngagement >= 0.10) { $signals[] = ['ok', 'Survey engagement: ' . round($surveyEngagement*100) . '% of referees took surveys']; } elseif ($surveyEngagement >= 0.03) { $score += 1; $signals[] = ['warn', 'Survey engagement: ' . round($surveyEngagement*100) . '% — low']; } else { $score += 2; $signals[] = ['bad', 'Survey engagement: ' . round($surveyEngagement*100) . '% — referees never take surveys']; } } // Verdict from total score if ($score <= 3) { $verdictLevel = 'clean'; $verdictText = 'CLEAN'; $verdictColor = '#059669'; $verdictBg = '#d1fae5'; $verdictIcon = '✓'; } elseif ($score <= 7) { $verdictLevel = 'review'; $verdictText = 'REQUIRES REVIEW'; $verdictColor = '#d97706'; $verdictBg = '#fef3c7'; $verdictIcon = '⚠'; } else { $verdictLevel = 'risky'; $verdictText = 'HIGH FRAUD RISK'; $verdictColor = '#dc2626'; $verdictBg = '#fee2e2'; $verdictIcon = '✗'; } $report['verdict'] = [ 'score' => $score, 'level' => $verdictLevel, 'text' => $verdictText, 'color' => $verdictColor, 'bg' => $verdictBg, 'icon' => $verdictIcon, 'signals' => $signals ]; } // Helpers if (!function_exists('fmtNum')) { function fmtNum($v) { return number_format((int)$v); } } if (!function_exists('fmtIST')) { function fmtIST($dt, $fmt = 'd M Y h:i A') { if (!$dt || $dt === '0000-00-00 00:00:00') return '—'; try { $d = new DateTime($dt, new DateTimeZone('Asia/Kolkata')); return $d->format($fmt); } catch (Exception $e) { return date($fmt, strtotime($dt)); } } } function fmtDuration($s) { if ($s === null || $s === '') return '—'; $s = (int)$s; if ($s < 60) return $s . 's'; if ($s < 3600) return floor($s/60) . 'm ' . ($s%60) . 's'; if ($s < 86400) return floor($s/3600) . 'h ' . floor(($s%3600)/60) . 'm'; return floor($s/86400) . 'd ' . floor(($s%86400)/3600) . 'h'; } include 'includes/header.php'; ?>

Referral Audit

Comprehensive fraud/legitimacy assessment for any referring panel member. Enter their email to run a full multi-signal check across IPs, timing, engagement, geographic spread, and survey activity.

Tip: Use the exact email registered to the panel-member account. The lookup is case-sensitive.
↻ New Audit 👤 View Member ✏️ Edit Member
Member Audited

Member IDRR
Status
Joined
Email Verified
Postcode
Referral TierEnabled · T2:₹ T3:₹ T4:₹ T5:₹
Referral TierDefault flat ₹5
Risk score: / 16

Headline Numbers

Total Clicks
Signups
0 ? round((int)$report['aggregate']['signups']/(int)$report['aggregate']['total_clicks']*100) : 0; ?>% conv.
Verified
0 ? round((int)$report['aggregate']['verified']/(int)$report['aggregate']['signups']*100) : 0; ?>% of signups
Unique IPs
0 ? round((int)$report['aggregate']['unique_ips']/(int)$report['aggregate']['total_clicks']*100) : 0; ?>% diversity
Unique Referees
Unique User-Agents
First click: · Last click:

Post-Signup Engagement — do referees actually use the platform?

Verified Referees
Mobile Verified
0 ? round((int)$report['engagement']['mobile_verified']/$verifiedN*100) : 0; ?>%
Started Profilers
0 ? round((int)$report['engagement']['profilers_started']/$verifiedN*100) : 0; ?>%
Survey Invites
referees got invites
Surveys Done
took at least 1 0): ?> · bad-quality

Timing Patterns — scripted vs human

Click → Signup (avg)
min · max
Signup → Verify (avg)
min · max
Signups <15s
scripted threshold
Verifies <30s
% of verified

Hour Distribution (IST)

Burst Windows — concentrated signup activity (≥3 in same hour, IST)

= 0.7 ? 'ok' : ($div >= 0.3 ? 'warn' : 'bad'); $read = $div >= 0.7 ? 'Healthy — distinct devices' : ($div >= 0.3 ? 'Mixed — possibly shared WiFi' : 'Concentrated — same network, suspicious'); ?>
Hour Window (IST)SignupsUnique IPsIP DiversityRead
%

IP Repeats — addresses appearing in multiple referrals

IP AddressTotal HitsSignupsVerified
A small number of repeats with 2-3 hits each is normal (people use multiple devices). Many IPs each producing 5+ verified signups is a fraud signal.

IP Repeats

No IP appears in multiple referrals — every signup came from a distinct IP. ✓

Email Pattern Duplicates — Gmail dot/plus aliases pointing to same inbox

Normalized LocalDomainVariantsEmails

Geographic Spread — by pincode region

xxxxx —
· %

Top Districts

Pincode PrefixMembersSample Pincodes

Recent Referees — most recent 30 click events

EmailIPC→SS→V VerifiedMobProfPostcodeClicked (IST)
✓' : ''; ?> ✓' : ''; ?> 0 ? '' . (int)$r['prof_c'] . '' : ''; ?>
Report generated IST · Relevant Reflex Admin Audit · For internal review only
How this audit works: Enter the email of any panel member who has been referring others. The tool runs 8 independent checks across IP diversity, signup/verify timing patterns, post-signup engagement, geographic concentration, email-pattern tricks, burst-window analysis, and survey activity in real projects — then scores each signal and gives a verdict (CLEAN / REQUIRES REVIEW / HIGH FRAUD RISK). Click "Save as PDF" once results are shown to keep a record for fraud-related disputes or member suspensions.