logger = $logger ?? new NullLogger(); } public function validate(string $licenseKey, string $domain): LicenseInfo { $cacheKey = $this->buildCacheKey('validate', $licenseKey, $domain); if ($this->cache !== null) { $item = $this->cache->getItem($cacheKey); if ($item->isHit()) { $this->logger->debug('License validation cache hit', [ 'domain' => $domain, ]); return $item->get(); } } $this->logger->info('Validating license', [ 'domain' => $domain, ]); $response = $this->request('validate', [ 'license_key' => $licenseKey, 'domain' => $domain, ]); $result = LicenseInfo::fromArray($response['license']); if ($this->cache !== null) { $item = $this->cache->getItem($cacheKey); $item->set($result); $item->expiresAfter($this->cacheTtl); $this->cache->save($item); } $this->logger->info('License validated successfully', [ 'domain' => $domain, 'product_id' => $result->productId, ]); return $result; } public function status(string $licenseKey): LicenseStatus { $cacheKey = $this->buildCacheKey('status', $licenseKey); if ($this->cache !== null) { $item = $this->cache->getItem($cacheKey); if ($item->isHit()) { $this->logger->debug('License status cache hit'); return $item->get(); } } $this->logger->info('Checking license status'); $response = $this->request('status', [ 'license_key' => $licenseKey, ]); $result = LicenseStatus::fromArray($response); if ($this->cache !== null) { $item = $this->cache->getItem($cacheKey); $item->set($result); $item->expiresAfter($this->cacheTtl); $this->cache->save($item); } $this->logger->info('License status retrieved', [ 'status' => $result->status->value, 'valid' => $result->valid, ]); return $result; } public function activate(string $licenseKey, string $domain): ActivationResult { $this->logger->info('Activating license', [ 'domain' => $domain, ]); $response = $this->request('activate', [ 'license_key' => $licenseKey, 'domain' => $domain, ]); $result = ActivationResult::fromArray($response); // Invalidate related cache entries after activation if ($this->cache !== null) { $this->cache->deleteItem($this->buildCacheKey('validate', $licenseKey, $domain)); $this->cache->deleteItem($this->buildCacheKey('status', $licenseKey)); } $this->logger->info('License activation completed', [ 'domain' => $domain, 'success' => $result->success, ]); return $result; } /** * @throws LicenseException */ private function request(string $endpoint, array $payload): array { $url = rtrim($this->baseUrl, '/') . self::API_PATH . '/' . $endpoint; try { $response = $this->httpClient->request('POST', $url, [ 'json' => $payload, 'headers' => [ 'Accept' => 'application/json', 'Content-Type' => 'application/json', ], ]); $statusCode = $response->getStatusCode(); $data = $response->toArray(false); if ($statusCode >= 400) { $this->logger->warning('License API error response', [ 'endpoint' => $endpoint, 'status_code' => $statusCode, 'error' => $data['error'] ?? 'unknown', ]); throw LicenseException::fromApiResponse($data, $statusCode); } return $data; } catch (LicenseException $e) { throw $e; } catch (\Throwable $e) { $this->logger->error('License API request failed', [ 'endpoint' => $endpoint, 'error' => $e->getMessage(), ]); throw new LicenseException( 'Failed to communicate with license server: ' . $e->getMessage(), null, 0, $e ); } } private function buildCacheKey(string $operation, string $licenseKey, ?string $domain = null): string { $key = 'wc_license_' . $operation . '_' . hash('sha256', $licenseKey); if ($domain !== null) { $key .= '_' . hash('sha256', $domain); } return $key; } }