pdo = $db->getConnection(); } /** * Award points to a user */ public function awardPoints($userId, $points, $source, $description, $referenceId = null) { try { $this->pdo->beginTransaction(); // Update user points $stmt = $this->pdo->prepare("INSERT INTO user_points (user_id, points, total_earned) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE points = points + ?, total_earned = total_earned + ?"); $stmt->execute([$userId, $points, $points, $points, $points]); // Add transaction record $stmt = $this->pdo->prepare("INSERT INTO point_transactions (user_id, transaction_type, points, source, description, reference_id) VALUES (?, 'earned', ?, ?, ?, ?)"); $stmt->execute([$userId, $points, $source, $description, $referenceId]); $this->pdo->commit(); logError('Points awarded', [ 'user_id' => $userId, 'points' => $points, 'source' => $source, 'description' => $description ]); return true; } catch (Exception $e) { $this->pdo->rollback(); logError('Error awarding points', [ 'user_id' => $userId, 'points' => $points, 'source' => $source, 'error' => $e->getMessage() ]); return false; } } /** * Deduct points from a user (for redemptions) */ public function deductPoints($userId, $points, $source, $description, $referenceId = null) { try { $this->pdo->beginTransaction(); // Check if user has enough points $stmt = $this->pdo->prepare("SELECT points FROM user_points WHERE user_id = ?"); $stmt->execute([$userId]); $userPoints = $stmt->fetch(); if (!$userPoints || $userPoints['points'] < $points) { $this->pdo->rollback(); return false; } // Deduct points $stmt = $this->pdo->prepare("UPDATE user_points SET points = points - ?, total_redeemed = total_redeemed + ? WHERE user_id = ?"); $stmt->execute([$points, $points, $userId]); // Add transaction record $stmt = $this->pdo->prepare("INSERT INTO point_transactions (user_id, transaction_type, points, source, description, reference_id) VALUES (?, 'redeemed', ?, ?, ?, ?)"); $stmt->execute([$userId, $points, $source, $description, $referenceId]); $this->pdo->commit(); logError('Points deducted', [ 'user_id' => $userId, 'points' => $points, 'source' => $source, 'description' => $description ]); return true; } catch (Exception $e) { $this->pdo->rollback(); logError('Error deducting points', [ 'user_id' => $userId, 'points' => $points, 'source' => $source, 'error' => $e->getMessage() ]); return false; } } /** * Get user's point balance */ public function getUserPoints($userId) { try { $stmt = $this->pdo->prepare("SELECT points, total_earned, total_redeemed FROM user_points WHERE user_id = ?"); $stmt->execute([$userId]); $result = $stmt->fetch(); if (!$result) { return ['points' => 0, 'total_earned' => 0, 'total_redeemed' => 0]; } return $result; } catch (Exception $e) { logError('Error fetching user points', ['user_id' => $userId, 'error' => $e->getMessage()]); return ['points' => 0, 'total_earned' => 0, 'total_redeemed' => 0]; } } /** * Check and award onboarding points */ public function checkOnboardingPoints($userId) { try { $stmt = $this->pdo->prepare("SELECT email_verified, onboarding_completed, onboarding_points_awarded FROM users WHERE id = ?"); $stmt->execute([$userId]); $user = $stmt->fetch(); if ($user && $user['email_verified'] && !$user['onboarding_points_awarded']) { $this->pdo->beginTransaction(); // Award onboarding points $this->awardPoints($userId, 10, 'onboarding', 'Welcome bonus for email verification and first login'); // Mark onboarding as completed $stmt = $this->pdo->prepare("UPDATE users SET onboarding_completed = 1, onboarding_points_awarded = 1 WHERE id = ?"); $stmt->execute([$userId]); $this->pdo->commit(); return true; } return false; } catch (Exception $e) { $this->pdo->rollback(); logError('Error checking onboarding points', ['user_id' => $userId, 'error' => $e->getMessage()]); return false; } } /** * Check and award profiler section completion points */ public function checkProfilerCompletion($userId, $section) { try { // Get section questions count $profilerQuestionCounts = [ 'personal_background' => 6, 'household_family' => 7, 'shopping_lifestyle' => 7, 'technology_digital' => 7, 'travel_transportation' => 7, 'health_fitness' => 7, 'entertainment_media' => 8, 'food_dining' => 8, 'financial_services' => 8, 'communication_payments' => 8 ]; if (!isset($profilerQuestionCounts[$section])) { return false; } $totalQuestions = $profilerQuestionCounts[$section]; // Count answered questions $stmt = $this->pdo->prepare("SELECT COUNT(*) as answered FROM user_profiler WHERE user_id = ? AND section = ?"); $stmt->execute([$userId, $section]); $answered = $stmt->fetch()['answered']; $completionPercentage = ($answered / $totalQuestions) * 100; $isCompleted = $completionPercentage >= 100; // Update completion record $stmt = $this->pdo->prepare("INSERT INTO profiler_completion (user_id, section, total_questions, answered_questions, completion_percentage, is_completed, completed_at) VALUES (?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE answered_questions = ?, completion_percentage = ?, is_completed = ?, completed_at = CASE WHEN ? = 1 THEN NOW() ELSE completed_at END, updated_at = NOW()"); $completedAt = $isCompleted ? date('Y-m-d H:i:s') : null; $stmt->execute([$userId, $section, $totalQuestions, $answered, $completionPercentage, $isCompleted, $completedAt, $answered, $completionPercentage, $isCompleted, $isCompleted]); // Award points if completed and not already awarded if ($isCompleted) { $stmt = $this->pdo->prepare("SELECT points_awarded FROM profiler_completion WHERE user_id = ? AND section = ?"); $stmt->execute([$userId, $section]); $completion = $stmt->fetch(); if ($completion && !$completion['points_awarded']) { $pointsToAward = ($section === 'communication_payments') ? 10 : 5; $description = ($section === 'communication_payments') ? 'Mobile verification and UPI details completion' : 'Profiler section completion: ' . ucwords(str_replace('_', ' ', $section)); $this->awardPoints($userId, $pointsToAward, 'profiler_' . $section, $description); // Mark points as awarded $stmt = $this->pdo->prepare("UPDATE profiler_completion SET points_awarded = 1 WHERE user_id = ? AND section = ?"); $stmt->execute([$userId, $section]); return $pointsToAward; } } return false; } catch (Exception $e) { logError('Error checking profiler completion', ['user_id' => $userId, 'section' => $section, 'error' => $e->getMessage()]); return false; } } /** * Get user's transaction history */ public function getTransactionHistory($userId, $limit = 50) { try { $stmt = $this->pdo->prepare("SELECT * FROM point_transactions WHERE user_id = ? ORDER BY created_at DESC LIMIT ?"); $stmt->execute([$userId, $limit]); return $stmt->fetchAll(); } catch (Exception $e) { logError('Error fetching transaction history', ['user_id' => $userId, 'error' => $e->getMessage()]); return []; } } /** * Calculate points needed for next redemption tier */ public function getNextRedemptionTier($currentPoints) { $tiers = [200, 500, 1000, 2000, 5000, 10000]; foreach ($tiers as $tier) { if ($currentPoints < $tier) { return [ 'next_tier' => $tier, 'points_needed' => $tier - $currentPoints, 'amount_value' => $tier * 0.5 ]; } } // If user has more than highest tier return [ 'next_tier' => null, 'points_needed' => 0, 'amount_value' => $currentPoints * 0.5 ]; } /** * Get points earning summary for admin */ public function getEarningSummary($dateRange = 30) { try { $stmt = $this->pdo->prepare(" SELECT source, COUNT(*) as transaction_count, SUM(points) as total_points, COUNT(DISTINCT user_id) as unique_users FROM point_transactions WHERE transaction_type = 'earned' AND created_at >= DATE_SUB(NOW(), INTERVAL ? DAY) GROUP BY source ORDER BY total_points DESC "); $stmt->execute([$dateRange]); return $stmt->fetchAll(); } catch (Exception $e) { logError('Error fetching earning summary', ['error' => $e->getMessage()]); return []; } } /** * Validate redemption request */ public function validateRedemption($userId, $points, $upiId) { $errors = []; // Check minimum points if ($points < 200) { $errors[] = 'Minimum redemption amount is 200 points (₹100).'; } // Check if points are in multiples of 10 if ($points % 10 !== 0) { $errors[] = 'Points must be redeemed in multiples of 10.'; } // Check user balance $userPoints = $this->getUserPoints($userId); if ($points > $userPoints['points']) { $errors[] = 'You cannot redeem more points than your available balance.'; } // Validate UPI ID if (empty($upiId)) { $errors[] = 'Please enter your UPI ID.'; } elseif (!preg_match('/^[\w\.-]+@[\w\.-]+$/', $upiId)) { $errors[] = 'Please enter a valid UPI ID (e.g., yourname@paytm).'; } return $errors; } } // Convenience functions for backward compatibility function awardUserPoints($userId, $points, $source, $description, $referenceId = null) { $pm = new PointsManager(); return $pm->awardPoints($userId, $points, $source, $description, $referenceId); } function getUserPointsBalance($userId) { $pm = new PointsManager(); return $pm->getUserPoints($userId); } function checkUserOnboarding($userId) { $pm = new PointsManager(); return $pm->checkOnboardingPoints($userId); } ?>