Install your license
Connect a PHP website to LicenseHub, bind the license to its current hostname, and move it later with self-service reissue.
How licensing works
Your PHP script sends the license key, product slug, a stable installation ID and the website hostname to the validation API. The first valid request creates the website binding. Later requests must match that binding.
License plans and expiry
Your license may be issued on a Free, Monthly, Yearly, Lifetime or custom administrator-defined plan. The selected plan determines the license expiry date and maximum activation count at the moment the license is issued. A Lifetime plan has no automatic expiry.
Plan names, durations, activation limits and product availability are controlled by the LicenseHub administrator.
PHP installation
- Copy your license key from the client dashboard.
- Find your product slug in the install guide link for that license.
- Create a server-side file such as
license-check.php. - Paste the example below and replace
YOUR-LICENSE-KEYandYOUR_API_SIGNING_SECRET. - Require the checker early in the protected script, before protected functionality is executed.
<?php
// license-check.php
// Store these values in a config file outside your public web root when possible.
$licenseKey = 'YOUR-LICENSE-KEY';
$productSlug = 'your-product-slug';
$licensingUrl = 'https://license.fibrenode.co.uk/api/validate.php';
$apiSigningSecret = 'YOUR_API_SIGNING_SECRET';
// Use the website hostname as the installation identity.
$host = strtolower($_SERVER['HTTP_HOST'] ?? '');
$host = preg_replace('/:\d+$/', '', $host);
if ($host === '') {
exit('Unable to determine licensed hostname.');
}
$instanceId = hash('sha256', $productSlug . '|' . $host);
$request = json_encode([
'license_key' => $licenseKey,
'product' => $productSlug,
'instance_id' => $instanceId,
'domain' => $host,
], JSON_UNESCAPED_SLASHES);
$ch = curl_init($licensingUrl);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => $request,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_TIMEOUT => 10,
]);
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($response === false || $error !== '') {
exit('Unable to contact the licensing server.');
}
$data = json_decode($response, true);
if (!is_array($data)) {
exit('Invalid licensing response.');
}
// Verify the response signature before trusting the result.
$signature = (string)($data['signature'] ?? '');
unset($data['signature']);
$canonical = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$expected = hash_hmac('sha256', $canonical, $apiSigningSecret);
if ($signature === '' || !hash_equals($expected, $signature)) {
exit('Licensing response signature failed.');
}
if ($httpCode !== 200 || empty($data['valid'])) {
$reason = (string)($data['reason'] ?? 'unknown');
exit('License validation failed: ' . htmlspecialchars($reason, ENT_QUOTES, 'UTF-8'));
}Then load it from the protected application:
<?php
require __DIR__ . '/license-check.php';
// Your licensed application continues here.Validation API
Endpoint: https://license.fibrenode.co.uk/api/validate.php
{
"license_key": "LIC-...",
"product": "your-product-slug",
"instance_id": "sha256-product-and-host",
"domain": "customer.example"
}A valid response returns HTTP 200 with valid: true. Revoked, suspended, expired, disabled-product, activation-limit and domain-mismatch responses return valid: false with a reason.
Reissuing to another website URL
After a license has been used on a website, the dashboard shows its current domain and a Reissue license button. Reissuing deletes the current activation binding but keeps the same license key.
The administrator can disable reissues or configure a cooldown between reissues. If the button is unavailable, contact the licensing administrator.
Offline validation and signed certificates
Valid API responses now include an Ed25519-signed certificate when the PHP Sodium extension is available. The certificate contains the product, domain, instance, expiry, refresh time and grace-period deadline. Your application can cache the last successful certificate and continue during a temporary licensing-server outage until grace_until.
Do not extend the grace period locally. Refresh at the product's configured validation interval and reject cached certificates after their signed grace deadline.
Security notes
Keep license validation server-side and always use HTTPS for LicenseHub. The example verifies the online HMAC signature with hash_equals(). For offline operation, verify the Ed25519 certificate using sodium_crypto_sign_verify_detached() and the public key returned by LicenseHub. A licensing check controls authorization, but it does not encrypt or encode PHP source code; source protection is a separate layer.
Software updates & protected downloads
Use /api/update.php with license_key, product, current_version and optional channel. The response reports whether a newer published release exists and includes the release SHA-256 hash. Customer package downloads are served through /download.php only after LicenseHub confirms an active, unexpired license for the product.
Webhook verification
Each webhook request includes X-LicenseHub-Signature: sha256=.... Compute HMAC-SHA256 over the raw request body with that webhook's secret and compare it using hash_equals().