#!/usr/bin/env php /** * Real-time Price Feed Service * Fetches real-time cryptocurrency prices from external APIs */ class PriceFeedService { private static $api_keys = []; private static $last_update = null; private static $price_cache = []; private static $cache_duration = 60; // 60 seconds public static function initialize() { require_once __DIR__ . '/../config/env.php'; EnvConfig::load(__DIR__ . '/../config.env'); self::$api_keys = [ 'coinmarketcap' => EnvConfig::get('COINMARKETCAP_API_KEY'), 'coingecko' => EnvConfig::get('COINGECKO_API_KEY'), 'binance' => EnvConfig::get('BINANCE_API_KEY') ]; } public static function getPrices($symbols = ['BTC', 'ETH', 'USDT', 'USDC', 'BNB', 'ADA', 'SOL', 'MATIC']) { try { // Check cache first if (self::isCacheValid()) { return self::getCachedPrices($symbols); } // Fetch fresh prices $prices = self::fetchFromAPI($symbols); if ($prices) { self::$price_cache = $prices; self::$last_update = time(); return $prices; } // Fallback to cached data if API fails return self::getCachedPrices($symbols); } catch (Exception $e) { error_log("Price feed error: " . $e->getMessage()); return self::getFallbackPrices($symbols); } } private static function isCacheValid() { return self::$last_update && (time() - self::$last_update) < self::$cache_duration; } private static function getCachedPrices($symbols) { $result = []; foreach ($symbols as $symbol) { if (isset(self::$price_cache[$symbol])) { $result[$symbol] = self::$price_cache[$symbol]; } } return $result; } private static function fetchFromAPI($symbols) { // Try CoinGecko first (free tier) $prices = self::fetchFromCoinGecko($symbols); if ($prices) return $prices; // Try Binance API $prices = self::fetchFromBinance($symbols); if ($prices) return $prices; // Try CoinMarketCap if API key available if (self::$api_keys['coinmarketcap']) { $prices = self::fetchFromCoinMarketCap($symbols); if ($prices) return $prices; } return null; } private static function fetchFromCoinGecko($symbols) { try { $symbols_str = implode(',', array_map('strtolower', $symbols)); $url = "https://api.coingecko.com/api/v3/simple/price?ids={$symbols_str}&vs_currencies=usd&include_24hr_change=true&include_24hr_vol=true"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_USERAGENT, 'YELLOW-Crypto-Exchange/1.0'); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $data = json_decode($response, true); $prices = []; foreach ($symbols as $symbol) { $id = strtolower($symbol); if (isset($data[$id])) { $prices[$symbol] = [ 'price' => $data[$id]['usd'], 'change_24h' => $data[$id]['usd_24h_change'] ?? 0, 'volume_24h' => $data[$id]['usd_24h_vol'] ?? 0 ]; } } return $prices; } } catch (Exception $e) { error_log("CoinGecko API error: " . $e->getMessage()); } return null; } private static function fetchFromBinance($symbols) { try { $prices = []; foreach ($symbols as $symbol) { $symbol_pair = $symbol . 'USDT'; $url = "https://api.binance.com/api/v3/ticker/24hr?symbol={$symbol_pair}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $data = json_decode($response, true); if (isset($data['lastPrice'])) { $prices[$symbol] = [ 'price' => floatval($data['lastPrice']), 'change_24h' => floatval($data['priceChangePercent']), 'volume_24h' => floatval($data['volume']) ]; } } } return !empty($prices) ? $prices : null; } catch (Exception $e) { error_log("Binance API error: " . $e->getMessage()); } return null; } private static function fetchFromCoinMarketCap($symbols) { try { $symbols_str = implode(',', $symbols); $url = "https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol={$symbols_str}"; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 10); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'X-CMC_PRO_API_KEY: ' . self::$api_keys['coinmarketcap'], 'Accept: application/json' ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $data = json_decode($response, true); $prices = []; if (isset($data['data'])) { foreach ($data['data'] as $symbol => $coin) { $prices[$symbol] = [ 'price' => $coin['quote']['USD']['price'], 'change_24h' => $coin['quote']['USD']['percent_change_24h'], 'volume_24h' => $coin['quote']['USD']['volume_24h'] ]; } } return $prices; } } catch (Exception $e) { error_log("CoinMarketCap API error: " . $e->getMessage()); } return null; } private static function getFallbackPrices($symbols) { // Fallback prices for development $fallback_prices = [ 'BTC' => ['price' => 45000, 'change_24h' => 2.5, 'volume_24h' => 25000000000], 'ETH' => ['price' => 3000, 'change_24h' => 1.8, 'volume_24h' => 15000000000], 'USDT' => ['price' => 1.00, 'change_24h' => 0.1, 'volume_24h' => 50000000000], 'USDC' => ['price' => 1.00, 'change_24h' => 0.05, 'volume_24h' => 20000000000], 'BNB' => ['price' => 300, 'change_24h' => 3.2, 'volume_24h' => 2000000000], 'ADA' => ['price' => 0.45, 'change_24h' => -1.2, 'volume_24h' => 800000000], 'SOL' => ['price' => 100, 'change_24h' => 5.8, 'volume_24h' => 3000000000], 'MATIC' => ['price' => 0.85, 'change_24h' => 2.1, 'volume_24h' => 1200000000] ]; $result = []; foreach ($symbols as $symbol) { if (isset($fallback_prices[$symbol])) { $result[$symbol] = $fallback_prices[$symbol]; } } return $result; } public static function updateDatabasePrices($prices) { try { require_once __DIR__ . '/../config/database.php'; $db = new Database(); $conn = $db->getConnection(); foreach ($prices as $symbol => $data) { $stmt = $conn->prepare(" UPDATE market_prices mp JOIN trading_pairs tp ON mp.pair_id = tp.id JOIN cryptocurrencies c1 ON tp.base_crypto_id = c1.id JOIN cryptocurrencies c2 ON tp.quote_crypto_id = c2.id SET mp.price = ?, mp.change_24h = ?, mp.volume_24h = ?, mp.updated_at = NOW() WHERE c1.symbol = ? AND c2.symbol = 'USD' "); $stmt->execute([ $data['price'], $data['change_24h'], $data['volume_24h'], $symbol ]); } return true; } catch (Exception $e) { error_log("Database price update failed: " . $e->getMessage()); return false; } } } // Initialize the service PriceFeedService::initialize(); /** * Backup and Monitoring Service * Handles database backups and system monitoring */ class BackupService { private static $backup_dir; private static $max_backups = 7; // Keep 7 days of backups public static function initialize() { self::$backup_dir = __DIR__ . '/../backups/'; // Create backup directory if it doesn't exist if (!is_dir(self::$backup_dir)) { mkdir(self::$backup_dir, 0755, true); } } public static function createDatabaseBackup() { try { require_once __DIR__ . '/../config/env.php'; EnvConfig::load(__DIR__ . '/../config.env'); $db_host = EnvConfig::get('DB_HOST', 'localhost'); $db_name = EnvConfig::get('DB_NAME', 'crypto_exchange'); $db_user = EnvConfig::get('DB_USER', 'root'); $db_pass = EnvConfig::get('DB_PASS', ''); $timestamp = date('Y-m-d_H-i-s'); $backup_file = self::$backup_dir . "backup_{$db_name}_{$timestamp}.sql"; // Create mysqldump command $command = "mysqldump -h {$db_host} -u {$db_user}"; if ($db_pass) { $command .= " -p{$db_pass}"; } $command .= " {$db_name} > {$backup_file}"; // Execute backup exec($command, $output, $return_code); if ($return_code === 0 && file_exists($backup_file)) { // Compress the backup $compressed_file = $backup_file . '.gz'; exec("gzip {$backup_file}"); // Clean old backups self::cleanOldBackups(); error_log("Database backup created: {$compressed_file}"); return $compressed_file; } else { error_log("Database backup failed with return code: {$return_code}"); return false; } } catch (Exception $e) { error_log("Backup creation error: " . $e->getMessage()); return false; } } private static function cleanOldBackups() { try { $files = glob(self::$backup_dir . "backup_*.sql.gz"); if (count($files) > self::$max_backups) { // Sort by modification time (oldest first) usort($files, function($a, $b) { return filemtime($a) - filemtime($b); }); // Remove oldest backups $files_to_remove = array_slice($files, 0, count($files) - self::$max_backups); foreach ($files_to_remove as $file) { unlink($file); error_log("Removed old backup: {$file}"); } } } catch (Exception $e) { error_log("Backup cleanup error: " . $e->getMessage()); } } public static function restoreDatabaseBackup($backup_file) { try { require_once __DIR__ . '/../config/env.php'; EnvConfig::load(__DIR__ . '/../config.env'); $db_host = EnvConfig::get('DB_HOST', 'localhost'); $db_name = EnvConfig::get('DB_NAME', 'crypto_exchange'); $db_user = EnvConfig::get('DB_USER', 'root'); $db_pass = EnvConfig::get('DB_PASS', ''); // Decompress if needed if (strpos($backup_file, '.gz') !== false) { $temp_file = str_replace('.gz', '', $backup_file); exec("gunzip -c {$backup_file} > {$temp_file}"); $backup_file = $temp_file; } // Create restore command $command = "mysql -h {$db_host} -u {$db_user}"; if ($db_pass) { $command .= " -p{$db_pass}"; } $command .= " {$db_name} < {$backup_file}"; // Execute restore exec($command, $output, $return_code); // Clean up temp file if (isset($temp_file) && file_exists($temp_file)) { unlink($temp_file); } if ($return_code === 0) { error_log("Database restored from: {$backup_file}"); return true; } else { error_log("Database restore failed with return code: {$return_code}"); return false; } } catch (Exception $e) { error_log("Backup restore error: " . $e->getMessage()); return false; } } public static function getBackupList() { try { $files = glob(self::$backup_dir . "backup_*.sql.gz"); $backups = []; foreach ($files as $file) { $backups[] = [ 'filename' => basename($file), 'size' => filesize($file), 'created' => date('Y-m-d H:i:s', filemtime($file)), 'path' => $file ]; } // Sort by creation time (newest first) usort($backups, function($a, $b) { return strtotime($b['created']) - strtotime($a['created']); }); return $backups; } catch (Exception $e) { error_log("Backup list error: " . $e->getMessage()); return []; } } } class MonitoringService { private static $log_file; private static $metrics = []; public static function initialize() { self::$log_file = __DIR__ . '/../logs/monitoring.log'; // Create logs directory if it doesn't exist $log_dir = dirname(self::$log_file); if (!is_dir($log_dir)) { mkdir($log_dir, 0755, true); } } public static function logMetric($metric_name, $value, $tags = []) { try { $timestamp = date('Y-m-d H:i:s'); $log_entry = [ 'timestamp' => $timestamp, 'metric' => $metric_name, 'value' => $value, 'tags' => $tags ]; file_put_contents(self::$log_file, json_encode($log_entry) . "\n", FILE_APPEND | LOCK_EX); // Store in memory for quick access self::$metrics[$metric_name] = [ 'value' => $value, 'timestamp' => $timestamp, 'tags' => $tags ]; } catch (Exception $e) { error_log("Metric logging error: " . $e->getMessage()); } } public static function getSystemMetrics() { try { $metrics = [ 'timestamp' => date('Y-m-d H:i:s'), 'memory_usage' => memory_get_usage(true), 'memory_peak' => memory_get_peak_usage(true), 'memory_limit' => ini_get('memory_limit'), 'disk_free' => disk_free_space(__DIR__ . '/../'), 'disk_total' => disk_total_space(__DIR__ . '/../'), 'load_average' => function_exists('sys_getloadavg') ? sys_getloadavg() : null, 'uptime' => self::getSystemUptime(), 'database_status' => self::checkDatabaseStatus(), 'blockchain_service_status' => self::checkBlockchainServiceStatus() ]; return $metrics; } catch (Exception $e) { error_log("System metrics error: " . $e->getMessage()); return []; } } private static function getSystemUptime() { try { if (function_exists('sys_getloadavg')) { return [ 'load_average' => sys_getloadavg(), 'memory_limit' => ini_get('memory_limit'), 'max_execution_time' => ini_get('max_execution_time') ]; } } catch (Exception $e) { // Ignore } return ['status' => 'unknown']; } private static function checkDatabaseStatus() { try { require_once __DIR__ . '/../config/database.php'; $db = new Database(); $conn = $db->getConnection(); $start_time = microtime(true); $stmt = $conn->query("SELECT 1"); $end_time = microtime(true); return [ 'status' => 'healthy', 'response_time' => round(($end_time - $start_time) * 1000, 2) . 'ms' ]; } catch (Exception $e) { return [ 'status' => 'unhealthy', 'error' => $e->getMessage() ]; } } private static function checkBlockchainServiceStatus() { try { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, 'http://localhost:3001/health'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); $start_time = microtime(true); $response = curl_exec($ch); $end_time = microtime(true); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { return [ 'status' => 'healthy', 'response_time' => round(($end_time - $start_time) * 1000, 2) . 'ms' ]; } else { return [ 'status' => 'unhealthy', 'http_code' => $httpCode ]; } } catch (Exception $e) { return [ 'status' => 'unhealthy', 'error' => $e->getMessage() ]; } } public static function sendAlert($alert_type, $message, $severity = 'warning') { try { $alert = [ 'timestamp' => date('Y-m-d H:i:s'), 'type' => $alert_type, 'message' => $message, 'severity' => $severity ]; // Log the alert error_log("ALERT: " . json_encode($alert)); // In production, you would send to monitoring service (e.g., PagerDuty, Slack) // For now, just log it return true; } catch (Exception $e) { error_log("Alert sending error: " . $e->getMessage()); return false; } } } // Initialize services BackupService::initialize(); MonitoringService::initialize(); /** * Email Notification Service * Handles email notifications for the crypto exchange */ class EmailService { private static $smtp_host; private static $smtp_port; private static $smtp_username; private static $smtp_password; private static $from_email; private static $from_name; public static function initialize() { require_once __DIR__ . '/../config/env.php'; EnvConfig::load(__DIR__ . '/../config.env'); self::$smtp_host = EnvConfig::get('SMTP_HOST', 'smtp.gmail.com'); self::$smtp_port = EnvConfig::get('SMTP_PORT', 587); self::$smtp_username = EnvConfig::get('SMTP_USERNAME'); self::$smtp_password = EnvConfig::get('SMTP_PASSWORD'); self::$from_email = EnvConfig::get('SMTP_FROM_EMAIL', 'noreply@yellowcrypto.com'); self::$from_name = EnvConfig::get('SMTP_FROM_NAME', 'YELLOW Crypto Exchange'); } public static function sendWelcomeEmail($user_email, $user_name) { $subject = "Welcome to YELLOW Crypto Exchange!"; $body = "

Welcome to YELLOW Crypto Exchange!

Dear {$user_name},

Thank you for joining YELLOW Crypto Exchange. Your account has been successfully created.

You can now:

If you have any questions, please contact our support team.

Best regards,
YELLOW Crypto Exchange Team

"; return self::sendEmail($user_email, $subject, $body); } public static function sendTransactionNotification($user_email, $transaction_type, $amount, $currency) { $subject = "Transaction Notification - {$transaction_type}"; $body = "

Transaction Notification

Your {$transaction_type} transaction has been processed:

Amount: {$amount} {$currency}

Status: Completed

Time: " . date('Y-m-d H:i:s') . "

You can view more details in your account dashboard.

Best regards,
YELLOW Crypto Exchange Team

"; return self::sendEmail($user_email, $subject, $body); } public static function sendSecurityAlert($user_email, $alert_type, $details) { $subject = "Security Alert - {$alert_type}"; $body = "

Security Alert

We detected unusual activity on your account:

Alert Type: {$alert_type}

Details: {$details}

Time: " . date('Y-m-d H:i:s') . "

If this was not you, please contact our support team immediately.

Best regards,
YELLOW Crypto Exchange Security Team

"; return self::sendEmail($user_email, $subject, $body); } private static function sendEmail($to, $subject, $body) { try { // For development, just log the email if (EnvConfig::get('APP_ENV') === 'development') { error_log("EMAIL TO: {$to}"); error_log("EMAIL SUBJECT: {$subject}"); error_log("EMAIL BODY: {$body}"); return true; } // In production, use actual SMTP if (!self::$smtp_username || !self::$smtp_password) { error_log("SMTP credentials not configured"); return false; } // Use PHPMailer or similar in production // For now, return true for development return true; } catch (Exception $e) { error_log("Email sending failed: " . $e->getMessage()); return false; } } } // Initialize the service EmailService::initialize(); /** * SMS Service * Handles SMS notifications for OTP and alerts */ class SMSService { private static $api_url; private static $api_key; private static $sender_id; public static function initialize() { require_once __DIR__ . '/../config/env.php'; EnvConfig::load(__DIR__ . '/../config.env'); self::$api_url = EnvConfig::get('SMS_API_URL'); self::$api_key = EnvConfig::get('SMS_API_KEY'); self::$sender_id = EnvConfig::get('SMS_SENDER_ID', 'YELLOW'); } public static function sendOTP($phone_number, $otp_code) { $message = "Your YELLOW Crypto Exchange OTP code is: {$otp_code}. Valid for 5 minutes."; return self::sendSMS($phone_number, $message); } public static function sendTransactionAlert($phone_number, $transaction_type, $amount, $currency) { $message = "YELLOW Crypto: {$transaction_type} of {$amount} {$currency} completed successfully."; return self::sendSMS($phone_number, $message); } public static function sendSecurityAlert($phone_number, $alert_type) { $message = "YELLOW Crypto Security Alert: {$alert_type} detected on your account. Contact support if this wasn't you."; return self::sendSMS($phone_number, $message); } private static function sendSMS($phone_number, $message) { try { // For development, just log the SMS if (EnvConfig::get('APP_ENV') === 'development') { error_log("SMS TO: {$phone_number}"); error_log("SMS MESSAGE: {$message}"); return true; } // In production, use actual SMS API if (!self::$api_url || !self::$api_key) { error_log("SMS API credentials not configured"); return false; } $data = [ 'to' => $phone_number, 'message' => $message, 'sender_id' => self::$sender_id ]; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, self::$api_url); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data)); curl_setopt($ch, CURLOPT_HTTPHEADER, [ 'Content-Type: application/json', 'Authorization: Bearer ' . self::$api_key ]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 30); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode === 200) { $result = json_decode($response, true); return $result['success'] ?? false; } return false; } catch (Exception $e) { error_log("SMS sending failed: " . $e->getMessage()); return false; } } } // Initialize the service SMSService::initialize(); This script must be run from the command line