prepare("SELECT trial_started_at FROM system_license LIMIT 1"); $stmt->execute(); $res = $stmt->fetch(); if (!$res || !$res['trial_started_at']) { self::startTrial(); return 15; } $started = strtotime($res['trial_started_at']); $days_elapsed = floor((time() - $started) / (24 * 60 * 60)); return (int) max(0, 15 - $days_elapsed); } /** * Initializes the trial period. */ private static function startTrial() { require_once __DIR__ . '/../db/config.php'; $stmt = db()->prepare("SELECT COUNT(*) FROM system_license"); $stmt->execute(); if ($stmt->fetchColumn() == 0) { db()->exec("INSERT INTO system_license (status, trial_started_at) VALUES ('trial', NOW())"); } else { db()->exec("UPDATE system_license SET trial_started_at = NOW() WHERE trial_started_at IS NULL"); } } /** * Generates a unique fingerprint for this server environment. */ public static function getFingerprint() { $data = [ php_uname('n'), // Nodename (hostname) php_uname('m'), // Machine type $_SERVER['SERVER_ADDR'] ?? '127.0.0.1', PHP_OS ]; return hash('sha256', implode('|', $data)); } /** * Checks if the system is currently activated or within trial period. */ public static function canAccess() { if (self::isActivated()) return true; $daysLeft = self::getTrialRemainingDays(); return $daysLeft > 0; } /** * Checks if the system is currently activated with a valid license key. */ public static function isActivated() { require_once __DIR__ . '/../db/config.php'; $stmt = db()->prepare("SELECT * FROM system_license WHERE status = 'active' LIMIT 1"); $stmt->execute(); $license = $stmt->fetch(); if (!$license) return false; // 1. Verify fingerprint matches (Physical Protection) if ($license['fingerprint'] !== self::getFingerprint()) { return false; } // 2. Periodic Remote Validation (e.g., every 7 days) // This ensures if you disable a key on your server, the client will stop working. $last_check = strtotime($license['activated_at']); // In real use, store 'last_verified_at' if (time() - $last_check > (7 * 24 * 60 * 60)) { // It's been more than 7 days, let's re-verify $res = self::callRemoteApi('/verify', [ 'license_key' => $license['license_key'], 'fingerprint' => $license['fingerprint'], 'token' => $license['activation_token'] ]); if (!$res['success']) { db()->exec("UPDATE system_license SET status = 'suspended'"); return false; } // Update last verified timestamp db()->exec("UPDATE system_license SET activated_at = NOW()"); } return true; } /** * Attempts to activate the product online. */ public static function activate($license_key) { $license_key = trim($license_key); $fingerprint = self::getFingerprint(); // Call remote API for real validation $response = self::callRemoteApi('/activate', [ 'license_key' => $license_key, 'fingerprint' => $fingerprint, 'domain' => $_SERVER['HTTP_HOST'] ?? 'unknown', 'product' => 'Flatlogic Admin Panel' ]); if (!$response['success']) { return ['success' => false, 'error' => $response['error'] ?? 'Remote activation failed.']; } require_once __DIR__ . '/../db/config.php'; // Clear previous pending/failed attempts db()->exec("DELETE FROM system_license"); $stmt = db()->prepare("INSERT INTO system_license (license_key, fingerprint, status, activated_at, activation_token) VALUES (?, ?, 'active', NOW(), ?)"); $token = $response['activation_token'] ?? bin2hex(random_bytes(32)); $stmt->execute([$license_key, $fingerprint, $token]); return ['success' => true]; } /** * Fetches all licenses from the remote server. */ public static function listLicenses() { return self::callRemoteApi('/list', []); } /** * Updates an existing license. */ public static function updateLicense($id, $data) { $params = array_merge(['id' => $id, 'secret' => '1485-5215-2578'], $data); return self::callRemoteApi('/update', $params); } /** * Issues a new license. */ public static function issueLicense($max_activations, $prefix = 'FLAT', $owner = null, $address = null) { return self::callRemoteApi('/issue', [ 'secret' => '1485-5215-2578', 'max_activations' => $max_activations, 'prefix' => $prefix, 'owner' => $owner, 'address' => $address ]); } /** * Remote API Caller */ private static function callRemoteApi($endpoint, $params) { $action = ltrim($endpoint, '/'); $url = self::getApiUrl() . '/index.php?action=' . $action; // Check if we are in local development / simulation mode if (strpos($url, 'your-domain.com') !== false) { return self::simulateApi($endpoint, $params); } $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($params)); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $resp = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($http_code !== 200) { return ['success' => false, 'error' => "Remote server returned error code $http_code."]; } $data = json_decode($resp, true); if (!$data) { return ['success' => false, 'error' => "Invalid response from remote server."]; } return $data; } /** * Local Simulation for development purposes */ private static function simulateApi($endpoint, $params) { $clean_key = strtoupper(trim($params['license_key'] ?? '')); // Strict format check: FLAT-XXXX-XXXX-XXXX (where X is hex) $pattern = '/^FLAT-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}$/'; if (!preg_match($pattern, $clean_key)) { return [ 'success' => false, 'error' => 'Invalid License Format. Expected FLAT-XXXX-XXXX-XXXX (Simulation Mode).' ]; } // In a real server, you would check if this key is already used by a DIFFERENT fingerprint. // To simulate a "Max Activations Reached" error, you can use a specific key: if ($clean_key === 'FLAT-0000-0000-0000') { return [ 'success' => false, 'error' => 'Activation Failed: This license key is already active on another machine.' ]; } if ($endpoint === '/verify') return ['success' => true]; return [ 'success' => true, 'activation_token' => hash('sha256', $params['license_key'] . $params['fingerprint'] . 'DEBUG_SALT') ]; } }