Skip to content

Phase 06: License Service

Implement the central LicenseService that creates licenses, validates them via a 7-step algorithm, and provides status-transition helpers.

  • src/Models/License.php
  • src/Services/LicenseService.php

License::from_row(array $row): self maps a raw $wpdb row to a typed object.

$license->is_active() // status === 1
$license->is_expired() // status === 3 or expires_at in past
$license->is_revoked() // status === 5
$license->is_terminated() // status === 6 (irreversible)
$license->is_suspended() // status === 4
$license->is_within_expiry() // active AND not past expiry
$license->status_label() // "Active", "Expired", etc.
$license->to_array(false) // safe array excluding plaintext key

Status codes:

CodeMeaningTerminal?
0PendingNo
1ActiveNo
2InactiveNo
3ExpiredNo (can be renewed)
4SuspendedNo
5RevokedNo
6TerminatedYes — cannot be reversed

Constructor takes 6 dependencies via DI:

public function __construct(
LicenseRepository $license_repo,
Signer $signer,
KeyVault $vault,
ActivationLogRepository $log_repo,
BlacklistRepository $blacklist_repo,
MachineRepository $machine_repo,
)
  1. Generate raw key string (delegates to GeneratorService or accepts a pre-generated key).
  2. Compute sha256($raw_key) — used as the lookup hash stored in license_key_hash.
  3. Encrypt the raw key with KeyVault::encrypt().
  4. Sign a claims payload with Signer::sign() — token stored in signed_token.
  5. Insert the record via LicenseRepository::insert() with status = 2 (Inactive) — keys are inactive until explicitly activated.
  6. Fire do_action('wplm_license_created', $license).
1. Parse and base64url-decode the token.
2. Verify Ed25519 signature — fail → INVALID_SIGNATURE.
3. Verify `exp` claim — fail → TOKEN_EXPIRED.
4. Look up the license record by key hash.
5. Check license status — suspended/revoked/terminated → SUSPENDED / REVOKED / TERMINATED.
6. Check blacklist (key hash and any device fingerprint provided) — hit → BLACKLISTED.
7. Optionally verify machine fingerprint if device_id supplied.
Return: ['valid' => bool, 'code' => string, 'license' => License|null]

The code string is one of: VALID, INVALID_SIGNATURE, TOKEN_EXPIRED, NOT_FOUND, SUSPENDED, REVOKED, TERMINATED, BLACKLISTED, INACTIVE.

$c->bind(LicenseService::class, fn($c) => new Services\LicenseService(
$c->make(Repositories\LicenseRepository::class),
$c->make(Crypto\Signer::class),
$c->make(Crypto\KeyVault::class),
$c->make(Repositories\ActivationLogRepository::class),
$c->make(Repositories\BlacklistRepository::class),
$c->make(Repositories\MachineRepository::class),
));