[ 'name' => 'Bronze Bean', 'min_points' => 0, 'multiplier' => 1.0, 'benefits' => [ 'Earn 1 point per $1 spent', 'Birthday reward', 'Member-only offers' ], 'color' => '#CD7F32', 'icon' => 'fa-coffee' ], 'silver' => [ 'name' => 'Silver Roast', 'min_points' => 500, 'multiplier' => 1.25, 'benefits' => [ 'Earn 1.25 points per $1 spent', 'Free shipping on orders $25+', 'Early access to new products', 'Double points weekends' ], 'color' => '#C0C0C0', 'icon' => 'fa-mug-hot' ], 'gold' => [ 'name' => 'Gold Blend', 'min_points' => 1500, 'multiplier' => 1.5, 'benefits' => [ 'Earn 1.5 points per $1 spent', 'Free shipping on all orders', 'Exclusive Gold-only products', 'Priority customer support', 'Quarterly free coffee sample' ], 'color' => '#FFD700', 'icon' => 'fa-crown' ], 'platinum' => [ 'name' => 'Platinum Reserve', 'min_points' => 5000, 'multiplier' => 2.0, 'benefits' => [ 'Earn 2 points per $1 spent', 'Free express shipping', 'VIP early access to everything', 'Annual free bag of premium coffee', 'Dedicated account manager', 'Exclusive tasting events' ], 'color' => '#E5E4E2', 'icon' => 'fa-gem' ] ]; // Points redemption rates private float $pointsToValue = 0.01; // 1 point = $0.01 (100 points = $1) /** * Get all tier definitions */ public function getTiers(): array { return $this->tiers; } /** * Get a specific tier */ public function getTier(string $tierKey): ?array { return $this->tiers[$tierKey] ?? null; } /** * Determine customer's tier based on total points earned (lifetime) */ public function calculateTier(int $lifetimePoints): string { $currentTier = 'bronze'; foreach ($this->tiers as $key => $tier) { if ($lifetimePoints >= $tier['min_points']) { $currentTier = $key; } } return $currentTier; } /** * Get customer's current tier info */ public function getCustomerTier(string $customerId): array { $customer = db()->fetch( "SELECT reward_points, lifetime_points, loyalty_tier FROM customers WHERE customer_id = :id", ['id' => $customerId] ); if (!$customer) { return ['tier' => 'bronze', 'info' => $this->tiers['bronze'], 'points' => 0]; } $lifetimePoints = $customer['lifetime_points'] ?? $customer['reward_points'] ?? 0; $tierKey = $this->calculateTier($lifetimePoints); $tier = $this->tiers[$tierKey]; // Calculate progress to next tier $nextTierKey = $this->getNextTier($tierKey); $nextTier = $nextTierKey ? $this->tiers[$nextTierKey] : null; $progress = 100; $pointsToNext = 0; if ($nextTier) { $currentMin = $tier['min_points']; $nextMin = $nextTier['min_points']; $pointsToNext = $nextMin - $lifetimePoints; $progress = min(100, (($lifetimePoints - $currentMin) / ($nextMin - $currentMin)) * 100); } return [ 'tier' => $tierKey, 'info' => $tier, 'points' => $customer['reward_points'] ?? 0, 'lifetime_points' => $lifetimePoints, 'next_tier' => $nextTierKey, 'next_tier_info' => $nextTier, 'points_to_next' => $pointsToNext, 'progress_percent' => round($progress, 1) ]; } /** * Get next tier key */ private function getNextTier(string $currentTier): ?string { $keys = array_keys($this->tiers); $index = array_search($currentTier, $keys); return isset($keys[$index + 1]) ? $keys[$index + 1] : null; } /** * Award points for a purchase */ public function awardPoints(string $customerId, float $amount, string $description = 'Purchase'): array { $customerTier = $this->getCustomerTier($customerId); $multiplier = $customerTier['info']['multiplier']; // Calculate points (base: 1 point per dollar) $basePoints = floor($amount); $bonusPoints = floor($basePoints * ($multiplier - 1)); $totalPoints = $basePoints + $bonusPoints; // Update customer points db()->query( "UPDATE customers SET reward_points = reward_points + :points, lifetime_points = COALESCE(lifetime_points, 0) + :points, updated_at = NOW() WHERE customer_id = :id", ['points' => $totalPoints, 'id' => $customerId] ); // Log the transaction db()->insert('loyalty_transactions', [ 'transaction_id' => generateId('lt_'), 'customer_id' => $customerId, 'points' => $totalPoints, 'type' => 'earn', 'description' => $description . ($bonusPoints > 0 ? " (+{$bonusPoints} bonus)" : ''), 'reference_amount' => $amount ]); // Check for tier upgrade $newTier = $this->checkTierUpgrade($customerId, $customerTier['tier']); return [ 'points_earned' => $totalPoints, 'base_points' => $basePoints, 'bonus_points' => $bonusPoints, 'multiplier' => $multiplier, 'tier_upgraded' => $newTier !== null, 'new_tier' => $newTier ]; } /** * Redeem points for credit */ public function redeemPoints(string $customerId, int $points): array { $customer = db()->fetch( "SELECT reward_points FROM customers WHERE customer_id = :id", ['id' => $customerId] ); if (!$customer || $customer['reward_points'] < $points) { return ['success' => false, 'error' => 'Insufficient points']; } $creditValue = $points * $this->pointsToValue; // Deduct points db()->query( "UPDATE customers SET reward_points = reward_points - :points, updated_at = NOW() WHERE customer_id = :id", ['points' => $points, 'id' => $customerId] ); // Log the redemption db()->insert('loyalty_transactions', [ 'transaction_id' => generateId('lt_'), 'customer_id' => $customerId, 'points' => -$points, 'type' => 'redeem', 'description' => "Redeemed for " . formatCurrency($creditValue) . " credit", 'reference_amount' => $creditValue ]); // Add to wallet $newBalance = db()->fetch( "SELECT wallet_balance FROM customers WHERE customer_id = :id", ['id' => $customerId] )['wallet_balance'] ?? 0; $newBalance += $creditValue; db()->query( "UPDATE customers SET wallet_balance = :balance WHERE customer_id = :id", ['balance' => $newBalance, 'id' => $customerId] ); // Log wallet transaction db()->insert('wallet_transactions', [ 'transaction_id' => generateId('wt_'), 'customer_id' => $customerId, 'amount' => $creditValue, 'balance_after' => $newBalance, 'type' => 'loyalty_redemption', 'description' => "Redeemed {$points} loyalty points" ]); return [ 'success' => true, 'points_redeemed' => $points, 'credit_value' => $creditValue, 'new_points_balance' => $customer['reward_points'] - $points, 'new_wallet_balance' => $newBalance ]; } /** * Check and process tier upgrade */ public function checkTierUpgrade(string $customerId, string $currentTier): ?string { $customer = db()->fetch( "SELECT lifetime_points, loyalty_tier FROM customers WHERE customer_id = :id", ['id' => $customerId] ); if (!$customer) { return null; } $calculatedTier = $this->calculateTier($customer['lifetime_points'] ?? 0); $storedTier = $customer['loyalty_tier'] ?? 'bronze'; // Compare tier levels $tierOrder = ['bronze', 'silver', 'gold', 'platinum']; $calculatedIndex = array_search($calculatedTier, $tierOrder); $storedIndex = array_search($storedTier, $tierOrder); if ($calculatedIndex > $storedIndex) { // Upgrade! db()->query( "UPDATE customers SET loyalty_tier = :tier, updated_at = NOW() WHERE customer_id = :id", ['tier' => $calculatedTier, 'id' => $customerId] ); // Log the upgrade db()->insert('loyalty_transactions', [ 'transaction_id' => generateId('lt_'), 'customer_id' => $customerId, 'points' => 0, 'type' => 'tier_upgrade', 'description' => "Upgraded from {$this->tiers[$storedTier]['name']} to {$this->tiers[$calculatedTier]['name']}" ]); // Send notifications $this->sendTierUpgradeNotifications($customerId, $calculatedTier); return $calculatedTier; } return null; } /** * Send tier upgrade notifications */ private function sendTierUpgradeNotifications(string $customerId, string $newTier): void { $customer = db()->fetch( "SELECT email, phone, name FROM customers WHERE customer_id = :id", ['id' => $customerId] ); if (!$customer) return; $tierInfo = $this->tiers[$newTier]; // Send email notification if (!empty($customer['email'])) { require_once __DIR__ . '/email.php'; // Custom email for tier upgrade would go here } // Send SMS notification if (!empty($customer['phone'])) { require_once __DIR__ . '/sms.php'; sendSMS()->sendTierUpgrade($customer['phone'], $tierInfo['name']); } // Send push notification require_once __DIR__ . '/push.php'; pushNotify()->sendTierNotification($customerId, $tierInfo['name'], $tierInfo['benefits']); } /** * Get points conversion info */ public function getConversionInfo(): array { return [ 'points_per_dollar' => 1, 'points_value' => $this->pointsToValue, 'points_for_one_dollar' => intval(1 / $this->pointsToValue), 'description' => 'Earn 1 point for every $1 spent. Redeem 100 points for $1 credit.' ]; } /** * Get customer's loyalty history */ public function getHistory(string $customerId, int $limit = 20): array { return db()->fetchAll( "SELECT * FROM loyalty_transactions WHERE customer_id = :id ORDER BY created_at DESC LIMIT :limit", ['id' => $customerId, 'limit' => $limit] ); } /** * Award birthday bonus */ public function awardBirthdayBonus(string $customerId): array { $customerTier = $this->getCustomerTier($customerId); // Bonus points based on tier $bonusPoints = match($customerTier['tier']) { 'platinum' => 500, 'gold' => 300, 'silver' => 200, default => 100 }; db()->query( "UPDATE customers SET reward_points = reward_points + :points WHERE customer_id = :id", ['points' => $bonusPoints, 'id' => $customerId] ); db()->insert('loyalty_transactions', [ 'transaction_id' => generateId('lt_'), 'customer_id' => $customerId, 'points' => $bonusPoints, 'type' => 'birthday_bonus', 'description' => "Birthday reward - Happy Birthday!" ]); return ['success' => true, 'points' => $bonusPoints]; } /** * Award referral bonus */ public function awardReferralBonus(string $referrerId, string $newCustomerId): array { $referrerBonus = 100; $newCustomerBonus = 50; // Award to referrer db()->query( "UPDATE customers SET reward_points = reward_points + :points WHERE customer_id = :id", ['points' => $referrerBonus, 'id' => $referrerId] ); db()->insert('loyalty_transactions', [ 'transaction_id' => generateId('lt_'), 'customer_id' => $referrerId, 'points' => $referrerBonus, 'type' => 'referral_bonus', 'description' => "Referral bonus - Thank you for spreading the word!" ]); // Award to new customer db()->query( "UPDATE customers SET reward_points = reward_points + :points WHERE customer_id = :id", ['points' => $newCustomerBonus, 'id' => $newCustomerId] ); db()->insert('loyalty_transactions', [ 'transaction_id' => generateId('lt_'), 'customer_id' => $newCustomerId, 'points' => $newCustomerBonus, 'type' => 'referral_welcome', 'description' => "Welcome bonus from referral" ]); return [ 'referrer_bonus' => $referrerBonus, 'new_customer_bonus' => $newCustomerBonus ]; } } // Helper function function loyalty(): LoyaltyProgram { static $instance = null; if ($instance === null) { $instance = new LoyaltyProgram(); } return $instance; }