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.
Headline Numbers
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
First click: · Last click:
Post-Signup Engagement — do referees actually use the platform?
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)
| Hour Window (IST) | Signups | Unique IPs | IP Diversity | Read |
= 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');
?>
|
|
|
% |
|
IP Repeats — addresses appearing in multiple referrals
| IP Address | Total Hits | Signups | Verified |
|
|
|
|
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 Local | Domain | Variants | Emails |
|
|
|
|
Geographic Spread — by pincode region
Top Districts
| Pincode Prefix | Members | Sample Pincodes |
|
|
|
Recent Referees — most recent 30 click events
| Email | IP | C→S | S→V |
Verified | Mob | Prof | Postcode | Clicked (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.