isLocalhost()) { return true; } // Check cache first $cached = get_transient(self::CACHE_KEY); if ($cached !== false) { return (bool) $cached; } // Validate against server return $this->validateLicense(); } /** * Validate license against the server * * @param bool $forceRefresh Force refresh even if cached * @return bool True if license is valid */ public function validateLicense(bool $forceRefresh = false): bool { // Always valid on localhost if ($this->isLocalhost()) { return true; } // Check settings are configured $serverUrl = $this->getLicenseServerUrl(); $licenseKey = $this->getLicenseKey(); if (empty($serverUrl) || empty($licenseKey)) { set_transient( self::ERROR_CACHE_KEY, __('License settings not configured.', 'wc-licensed-product'), self::ERROR_CACHE_TTL ); return false; } // Check cache unless force refresh if (!$forceRefresh) { $cached = get_transient(self::CACHE_KEY); if ($cached !== false) { return (bool) $cached; } } try { $client = $this->createLicenseClient(); $domain = $this->getCurrentDomain(); // Validate the license $client->validate($licenseKey, $domain); // Valid license - cache success set_transient(self::CACHE_KEY, 1, self::CACHE_TTL); delete_transient(self::ERROR_CACHE_KEY); return true; } catch (LicenseException $e) { // License-specific error (invalid, expired, revoked, etc.) set_transient(self::CACHE_KEY, 0, self::CACHE_TTL); set_transient(self::ERROR_CACHE_KEY, $e->getMessage(), self::ERROR_CACHE_TTL); return false; } catch (\Throwable $e) { // Network/server error - use shorter cache to allow retry set_transient( self::ERROR_CACHE_KEY, __('Could not connect to license server.', 'wc-licensed-product') . ' ' . $e->getMessage(), self::ERROR_CACHE_TTL ); // Don't cache validation failure on network errors - allow retry on next page load return false; } } /** * Get the last error message */ public function getLastError(): ?string { $error = get_transient(self::ERROR_CACHE_KEY); return $error !== false ? (string) $error : null; } /** * Clear the validation cache */ public function clearCache(): void { delete_transient(self::CACHE_KEY); delete_transient(self::ERROR_CACHE_KEY); $this->isLocalhostCached = null; } /** * Check if running on localhost * * Matches localhost, 127.0.0.1, ::1, and any port number. */ public function isLocalhost(): bool { if ($this->isLocalhostCached !== null) { return $this->isLocalhostCached; } $domain = $this->getCurrentDomain(); // Remove port number if present $domainWithoutPort = preg_replace('/:[\d]+$/', '', $domain); // Check for localhost variants $localhostNames = ['localhost', '127.0.0.1', '::1']; if (in_array($domainWithoutPort, $localhostNames, true)) { $this->isLocalhostCached = true; return true; } // Check for .localhost and .local subdomains if ( str_ends_with($domainWithoutPort, '.localhost') || str_ends_with($domainWithoutPort, '.local') ) { $this->isLocalhostCached = true; return true; } $this->isLocalhostCached = false; return false; } /** * Get the current domain from the site URL */ private function getCurrentDomain(): string { $siteUrl = get_site_url(); $parsed = parse_url($siteUrl); $host = $parsed['host'] ?? 'localhost'; // Include port if non-standard if (isset($parsed['port'])) { $host .= ':' . $parsed['port']; } return strtolower($host); } /** * Get the license server URL from settings */ private function getLicenseServerUrl(): string { return (string) get_option('wc_licensed_product_plugin_license_server_url', ''); } /** * Get the license key from settings */ private function getLicenseKey(): string { return (string) get_option('wc_licensed_product_plugin_license_key', ''); } /** * Get the server secret from settings (optional) */ private function getServerSecret(): ?string { $secret = get_option('wc_licensed_product_plugin_license_server_secret', ''); return !empty($secret) ? (string) $secret : null; } /** * Create the license client instance */ private function createLicenseClient(): LicenseClientInterface { $httpClient = HttpClient::create([ 'timeout' => 10, 'verify_peer' => true, ]); $serverUrl = $this->getLicenseServerUrl(); $serverSecret = $this->getServerSecret(); // Use secure client if server secret is configured if ($serverSecret !== null) { return new SecureLicenseClient( httpClient: $httpClient, baseUrl: $serverUrl, serverSecret: $serverSecret, ); } return new LicenseClient( httpClient: $httpClient, baseUrl: $serverUrl, ); } }