koel/app/Services/LicenseService.php

153 lines
4.8 KiB
PHP
Raw Normal View History

2024-01-03 17:02:18 +00:00
<?php
namespace App\Services;
2024-01-03 17:02:18 +00:00
2024-01-05 16:42:50 +00:00
use App\Exceptions\FailedToActivateLicenseException;
use App\Models\License;
use App\Services\ApiClients\ApiClient;
use App\Services\License\LicenseServiceInterface;
2024-01-05 16:42:50 +00:00
use App\Values\LicenseInstance;
use App\Values\LicenseMeta;
use App\Values\LicenseStatus;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Http\Response;
2024-01-05 16:42:50 +00:00
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Throwable;
2024-01-09 18:34:40 +00:00
class LicenseService implements LicenseServiceInterface
2024-01-03 17:02:18 +00:00
{
2024-01-05 16:42:50 +00:00
public function __construct(private ApiClient $client, private string $hashSalt)
{
}
2024-01-08 13:02:26 +00:00
public function activate(string $key): License
2024-01-05 16:42:50 +00:00
{
try {
$response = $this->client->post('licenses/activate', [
'license_key' => $key,
'instance_name' => 'Koel Plus',
]);
2024-01-07 19:02:05 +00:00
if ($response->meta->store_id !== config('lemonsqueezy.store_id')) {
throw new FailedToActivateLicenseException('This license key is not from Koels official store.');
}
$license = $this->updateOrCreateLicenseFromApiResponse($response);
$this->cacheStatus(LicenseStatus::valid($license));
return $license;
2024-01-05 16:42:50 +00:00
} catch (ClientException $e) {
throw FailedToActivateLicenseException::fromClientException($e);
2024-01-05 16:42:50 +00:00
} catch (Throwable $e) {
2024-01-08 13:02:26 +00:00
Log::error($e);
throw FailedToActivateLicenseException::fromThrowable($e);
2024-01-05 16:42:50 +00:00
}
}
2024-01-09 18:34:40 +00:00
public function deactivate(License $license): void
2024-01-08 13:02:26 +00:00
{
try {
$response = $this->client->post('licenses/deactivate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
]);
if ($response->deactivated) {
self::deleteLicense($license);
}
} catch (ClientException $e) {
if ($e->getResponse()->getStatusCode() === Response::HTTP_NOT_FOUND) {
// The instance ID was not found. The license record must be a leftover from an erroneous attempt.
self::deleteLicense($license);
return;
2024-01-08 13:02:26 +00:00
}
throw FailedToActivateLicenseException::fromClientException($e);
2024-01-08 13:02:26 +00:00
} catch (Throwable $e) {
Log::error($e);
throw $e;
}
}
public function getStatus(bool $checkCache = true): LicenseStatus
2024-01-05 16:42:50 +00:00
{
if ($checkCache && Cache::has('license_status')) {
return Cache::get('license_status');
}
2024-01-07 12:43:10 +00:00
/** @var ?License $license */
2024-01-05 16:42:50 +00:00
$license = License::query()->latest()->first();
if (!$license) {
return LicenseStatus::noLicense();
}
try {
$response = $this->client->post('licenses/validate', [
'license_key' => $license->key,
'instance_id' => $license->instance->id,
]);
$updatedLicense = $this->updateOrCreateLicenseFromApiResponse($response);
return self::cacheStatus(LicenseStatus::valid($updatedLicense));
} catch (ClientException $e) {
Log::error($e);
$statusCode = $e->getResponse()->getStatusCode();
2024-01-05 16:42:50 +00:00
if ($statusCode === Response::HTTP_BAD_REQUEST || $statusCode === Response::HTTP_NOT_FOUND) {
2024-01-05 16:42:50 +00:00
return self::cacheStatus(LicenseStatus::invalid($license));
}
throw $e;
} catch (DecryptException) {
// the license key has been tampered with somehow
return self::cacheStatus(LicenseStatus::invalid($license));
} catch (Throwable $e) {
Log::error($e);
return LicenseStatus::unknown($license);
}
}
/** @noinspection PhpIncompatibleReturnTypeInspection */
private function updateOrCreateLicenseFromApiResponse(object $response): License
{
return License::query()->updateOrCreate([
'hash' => sha1($response->license_key->key . $this->hashSalt),
], [
'key' => $response->license_key->key,
'instance' => LicenseInstance::fromJsonObject($response->instance),
'meta' => LicenseMeta::fromJsonObject($response->meta),
'created_at' => $response->license_key->created_at,
'expires_at' => $response->license_key->expires_at,
2024-01-05 16:42:50 +00:00
]);
}
private static function deleteLicense(License $license): void
{
$license->delete();
Cache::delete('license_status');
}
2024-01-05 16:42:50 +00:00
private static function cacheStatus(LicenseStatus $status): LicenseStatus
{
Cache::put('license_status', $status, now()->addWeek());
return $status;
}
2024-01-03 17:02:18 +00:00
public function isPlus(): bool
{
2024-01-08 13:02:26 +00:00
return $this->getStatus()->isValid();
2024-01-03 17:02:18 +00:00
}
public function isCommunity(): bool
{
return !$this->isPlus();
}
}