Files
wc-licensed-product-client/src/LicenseClient.php

191 lines
5.7 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Magdev\WcLicensedProductClient;
use Magdev\WcLicensedProductClient\Dto\ActivationResult;
use Magdev\WcLicensedProductClient\Dto\LicenseInfo;
use Magdev\WcLicensedProductClient\Dto\LicenseStatus;
use Magdev\WcLicensedProductClient\Exception\LicenseException;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface;
use Psr\Log\NullLogger;
use Symfony\Contracts\HttpClient\HttpClientInterface;
final class LicenseClient implements LicenseClientInterface
{
private const API_PATH = '/wp-json/wc-licensed-product/v1';
private const CACHE_TTL = 300; // 5 minutes
private readonly LoggerInterface $logger;
public function __construct(
private readonly HttpClientInterface $httpClient,
private readonly string $baseUrl,
?LoggerInterface $logger = null,
private readonly ?CacheItemPoolInterface $cache = null,
private readonly int $cacheTtl = self::CACHE_TTL,
) {
$this->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;
}
}