A PHP SDK for integrating with the LicensesOS license management API. Provides license validation, activation, and deactivation with built-in caching and WordPress integration.
- PHP 8.1 or higher
- Guzzle HTTP client 7.0+
composer require licenseos/php-sdkuse LicensesOS\LicensesOsClient;
$client = new LicensesOsClient('your_api_key');
// Validate a license
$result = $client->validate('LIC-XXXX-XXXX-XXXX-XXXX', 'example.com');
if ($result->isValid()) {
echo "License is valid!";
echo "Status: " . $result->status;
echo "Expires: " . $result->expiresAt;
}Validate a license key for a specific domain or device:
$result = $client->validate(
licenseKey: 'LIC-XXXX-XXXX-XXXX-XXXX',
identifier: 'example.com',
metadata: [
'app_version' => '1.2.3',
'php_version' => PHP_VERSION,
]
);
// Check validation status
if ($result->isValid()) {
// License is valid for this identifier
}
if ($result->isActive()) {
// License status is "active"
}
if ($result->isExpired()) {
// License has expired
}
if ($result->isRevoked()) {
// License was revoked
}
// Access entitlements
if ($result->hasEntitlement('plan')) {
$plan = $result->getEntitlement('plan');
}
// Get remaining activations
$remaining = $result->getRemainingActivations();Activate a license for a domain or device:
$result = $client->activate(
licenseKey: 'LIC-XXXX-XXXX-XXXX-XXXX',
identifier: 'example.com'
);
if ($result->isActivated()) {
echo "Successfully activated!";
echo "Remaining activations: " . $result->getRemainingActivations();
} else {
echo "Activation failed: " . $result->getErrorMessage();
// Error codes: ACTIVATION_LIMIT_REACHED, LICENSE_EXPIRED, etc.
}Deactivate a license from a domain or device:
$result = $client->deactivate(
licenseKey: 'LIC-XXXX-XXXX-XXXX-XXXX',
identifier: 'example.com'
);
if ($result->isDeactivated()) {
echo "License deactivated. Remaining: " . $result->getRemainingActivations();
}Get all activations for a license:
$result = $client->listActivations('LIC-XXXX-XXXX-XXXX-XXXX');
echo "Total activations: " . $result->total;
echo "Limit: " . $result->limit;
echo "Remaining: " . $result->remaining;
foreach ($result->getActiveActivations() as $activation) {
echo $activation['identifier'] . " - " . $activation['activated_at'];
}
// Check if a specific identifier is activated
if ($result->hasActivation('example.com')) {
// This domain is activated
}The SDK automatically normalizes domain identifiers:
// All of these become "example.com"
$client->validate($key, 'https://www.example.com/');
$client->validate($key, 'HTTP://EXAMPLE.COM:8080/path');
$client->validate($key, 'www.example.com');
$client->validate($key, 'example.com');
// Subdomain is preserved
$client->validate($key, 'app.example.com'); // becomes "app.example.com"
// IDN domains are converted to punycode
$client->validate($key, 'münchen.de'); // becomes "xn--mnchen-3ya.de"You can also normalize domains manually:
$normalized = LicensesOsClient::normalizeDomain('https://www.Example.COM/page');
// Returns: "example.com"The SDK includes a caching layer for reduced API calls and offline tolerance.
use LicensesOS\LicensesOsClient;
use LicensesOS\Cache\LicenseCache;
use LicensesOS\Cache\FileCache;
$client = new LicensesOsClient('your_api_key');
$cache = new FileCache('/path/to/cache/dir');
$licenseCache = new LicenseCache($client, $cache);
// Validate with caching (12-hour TTL)
$result = $licenseCache->validate($licenseKey, $domain);
// Force refresh from API
$result = $licenseCache->validate($licenseKey, $domain, [], true);
// Check if premium features should be allowed
// (uses cache with 48-hour grace period for offline tolerance)
if ($licenseCache->shouldAllowPremium($licenseKey, $domain)) {
// Enable premium features
}$licenseCache = new LicenseCache(
client: $client,
cache: $cache,
cacheTtl: 43200, // 12 hours (default)
gracePeriod: 172800 // 48 hours (default)
);FileCache - Stores cache in JSON files:
use LicensesOS\Cache\FileCache;
$cache = new FileCache('/var/cache/myapp', 'myapp');WordPressCache - Uses WordPress transients:
use LicensesOS\Cache\WordPressCache;
$cache = new WordPressCache('my_plugin');Custom Cache - Implement CacheInterface:
use LicensesOS\Cache\CacheInterface;
class RedisCache implements CacheInterface
{
public function get(string $key): mixed { /* ... */ }
public function set(string $key, mixed $value, int $ttl): bool { /* ... */ }
public function delete(string $key): bool { /* ... */ }
}The SDK includes ready-to-use WordPress integration classes.
use LicensesOS\WordPress\LicenseManager;
class MyPluginLicense extends LicenseManager
{
private static ?self $instance = null;
public static function getInstance(): self
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
protected function getApiKey(): string
{
return 'your_api_key_here';
}
protected function getOptionPrefix(): string
{
return 'my_plugin';
}
protected function getPluginVersion(): string
{
return MY_PLUGIN_VERSION;
}
}use LicensesOS\WordPress\SettingsPage;
class MyPluginSettings extends SettingsPage
{
protected function getLicenseManager(): LicenseManager
{
return MyPluginLicense::getInstance();
}
protected function getPageTitle(): string
{
return 'My Plugin License';
}
protected function getMenuTitle(): string
{
return 'License';
}
protected function getMenuSlug(): string
{
return 'my-plugin-license';
}
protected function getPurchaseUrl(): string
{
return 'https://yoursite.com/pricing';
}
}
// Register the settings page
add_action('admin_menu', function() {
$settings = new MyPluginSettings();
$settings->addMenuPage();
});$license = MyPluginLicense::getInstance();
// Simple premium check
if ($license->isPremium()) {
// Enable all premium features
}
// Check specific entitlements
if ($license->hasEntitlement('advanced_export')) {
// Enable advanced export feature
}
// Get entitlement values
$maxItems = $license->getEntitlement('max_items', 10);Refresh license status periodically via cron:
// Register cron hook
add_action('my_plugin_license_check', function() {
$license = MyPluginLicense::getInstance();
if ($license->needsRefresh()) {
$license->validate(true);
}
});
// Schedule daily check on activation
register_activation_hook(__FILE__, function() {
if (!wp_next_scheduled('my_plugin_license_check')) {
wp_schedule_event(time(), 'daily', 'my_plugin_license_check');
}
});
// Clear on deactivation
register_deactivation_hook(__FILE__, function() {
wp_clear_scheduled_hook('my_plugin_license_check');
});The SDK throws two types of exceptions:
Thrown when the API returns an error response:
use LicensesOS\Exceptions\ApiException;
try {
$result = $client->validate($licenseKey, $domain);
} catch (ApiException $e) {
echo "API Error: " . $e->getMessage();
echo "Error Code: " . $e->getErrorCode();
echo "HTTP Status: " . $e->getStatusCode();
}Common error codes:
LICENSE_NOT_FOUND- License key doesn't existLICENSE_EXPIRED- License has expiredLICENSE_REVOKED- License was revokedACTIVATION_LIMIT_REACHED- No more activations availableRATE_LIMITED- Too many requestsAUTH_INVALID_API_KEY- Invalid API key
Thrown when unable to connect to the API:
use LicensesOS\Exceptions\NetworkException;
try {
$result = $client->validate($licenseKey, $domain);
} catch (NetworkException $e) {
// Network error - use cached data if available
echo "Network Error: " . $e->getMessage();
}use LicensesOS\Exceptions\ApiException;
use LicensesOS\Exceptions\NetworkException;
function checkLicense(LicenseCache $cache, string $key, string $domain): bool
{
try {
$result = $cache->validate($key, $domain);
return $result->isValid();
} catch (NetworkException $e) {
// Network error - fall back to grace period
return $cache->shouldAllowPremium($key, $domain);
} catch (ApiException $e) {
// API error - license is invalid
return false;
}
}Always use the LicenseCache class to avoid excessive API calls:
// Good
$cache = new LicenseCache($client, new FileCache('/tmp/cache'));
$result = $cache->validate($key, $domain);
// Bad - calls API every time
$result = $client->validate($key, $domain);Don't break your app when the API is unreachable:
// Good - uses grace period
if ($licenseCache->shouldAllowPremium($key, $domain)) {
enablePremiumFeatures();
}
// Bad - might throw on network error
$result = $client->validate($key, $domain);
if ($result->isValid()) {
enablePremiumFeatures();
}For WordPress, use options (they're in the database):
// Good
update_option('my_plugin_license_key', $licenseKey);
// Bad - don't store in plain files
file_put_contents('license.txt', $licenseKey);Don't validate on every page load:
// Good - validate if cache is stale
if ($licenseCache->needsRefresh($key, $domain)) {
$licenseCache->validate($key, $domain, [], true);
}
// Bad - validates every time
$client->validate($key, $domain);Disable UI/features rather than breaking functionality:
// Good - soft gating
function renderExportButton() {
$license = MyPluginLicense::getInstance();
if ($license->isPremium()) {
echo '<button>Export</button>';
} else {
echo '<button disabled>Export (Pro)</button>';
echo '<a href="...">Upgrade to Pro</a>';
}
}
// Bad - hard gating that breaks UX
function exportData() {
if (!MyPluginLicense::getInstance()->isPremium()) {
die('License required');
}
}| Method | Description |
|---|---|
validate(string $licenseKey, string $identifier, array $metadata = []) |
Validate a license |
activate(string $licenseKey, string $identifier, array $metadata = []) |
Activate a license |
deactivate(string $licenseKey, string $identifier) |
Deactivate a license |
listActivations(string $licenseKey) |
List all activations |
normalizeDomain(string $input) |
Normalize a domain (static) |
shouldAllowPremium(array $cachedState, int $graceTtl = 172800) |
Check cached state (static) |
| Method | Description |
|---|---|
validate(...) |
Validate with caching |
shouldAllowPremium(string $licenseKey, ?string $identifier = null) |
Check premium status |
getCachedState(string $licenseKey, ?string $identifier = null) |
Get cached state |
clearCache(string $licenseKey, ?string $identifier = null) |
Clear cached data |
needsRefresh(string $licenseKey, ?string $identifier = null) |
Check if refresh needed |
| Method | Description |
|---|---|
getLicenseKey() |
Get stored license key |
setLicenseKey(string $key) |
Store license key |
clearLicenseKey() |
Clear license key and cache |
getStatus() |
Get cached status |
getLicenseData() |
Get cached license data |
isPremium() |
Check if premium features allowed |
hasEntitlement(string $key) |
Check for entitlement |
getEntitlement(string $key, mixed $default = null) |
Get entitlement value |
validate(bool $forceRefresh = false) |
Validate license |
activate(string $licenseKey) |
Activate license |
deactivate() |
Deactivate license |
needsRefresh() |
Check if refresh needed |
MIT