getkeymanager/php-sdk
PHP SDK for integrating with GetKeyManager: manage API keys and access securely from your Laravel or PHP apps. Simple client setup, request helpers, and authentication support to streamline key creation, retrieval, rotation, and usage.
Version: 3.0.0 - Identifier-first architecture with offline-first validation!
getLicenseFile(), getProductMeta(), getProduct(), getProductChangelog(), getProductPublicKey()parseLicenseFile() (Base64 decode → 256-byte chunks → RSA PKCS1 decrypt → JSON)syncLicenseAndKey() with atomic file updates + telemetryisCheckIntervalPast() and isForceValidationPast() (fail-safe true on error)$identifier (domain or HWID)productPublicKey, licenseKey, licenseFilePath, defaultIdentifier once, reuse everywhereValidationType constantsValidationResultDto, ActivationResultDto, SyncResultDto with typed accessorsValidationType, IdentifierType, OptionKeys for predictable behaviorgenerateIdentifier(), getIdentifierOrGenerate(), resolvePublicKey(), canValidateOffline()use GetKeyManager\SDK\Validation\LicenseValidator;
// $validator is the LicenseValidator instance (used internally by LicenseClient)
$licenseData = $validator->parseLicenseFile($licFileContent, $publicKey);
if ($validator->isForceValidationPast($licPath, $keyPath)) {
showLicenseScreen();
die("License validation required");
}
30+ new methods added while maintaining full backward compatibility!
composer require getkeymanager/php-sdk
require_once __DIR__ . '/vendor/autoload.php';
use GetKeyManager\SDK\LicenseClient;
$client = new LicenseClient([
'apiKey' => 'your-api-key-here',
'publicKey' => file_get_contents('/path/to/public-key.pem'),
'baseUrl' => 'https://api.getkeymanager.com', // Optional
'verifySignatures' => true, // Optional, default: true
'cacheEnabled' => true, // Optional, default: true
'cacheTtl' => 300, // Optional, default: 300 seconds
]);
use GetKeyManager\SDK\Constants\ValidationType;
try {
// Basic validation with auto-generated identifier
$result = $client->validateLicense('XXXXX-XXXXX-XXXXX-XXXXX');
// Or specify identifier explicitly
$result = $client->validateLicense(
'XXXXX-XXXXX-XXXXX-XXXXX',
'example.com', // Domain identifier
null, // Use config's public key
ValidationType::OFFLINE_FIRST, // Try cache first
[] // Options
);
if ($result['success']) {
echo "License is valid!\n";
echo "Status: " . $result['license']['status'] . "\n";
echo "Expires: " . ($result['license']['expires_at'] ?? 'Never') . "\n";
} else {
echo "License is invalid: " . $result['message'] . "\n";
}
} catch (LicenseException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
try {
// Activation requires identifier (domain or hardware ID)
$result = $client->activateLicense(
'XXXXX-XXXXX-XXXXX-XXXXX',
'workstation-01', // Identifier: domain or HWID
null, // Use config's public key
[ // Additional options
'idempotency_key' => 'request-uuid-here',
'os' => 'Linux',
'product_version' => '1.0.0'
]
);
if ($result['success']) {
echo "License activated successfully!\n";
echo "Activation ID: " . $result['activation_id'] . "\n";
// .lic file generated on server
if (isset($result['lic_file_content'])) {
file_put_contents('/app/license.lic', $result['lic_file_content']);
}
}
} catch (LicenseException $e) {
echo "Activation failed: " . $e->getMessage() . "\n";
}
try {
// Identifier MUST match the activation being deactivated
$result = $client->deactivateLicense(
'XXXXX-XXXXX-XXXXX-XXXXX',
'workstation-01' // Must match activation identifier
);
if ($result['success']) {
echo "License deactivated successfully!\n";
echo "Activation ID: " . $result['activation_id'] . "\n";
} else {
echo "Deactivation failed: " . $result['message'] . "\n";
// Check if activation not found
if ($result['error'] === 'activation_not_found') {
echo "Tip: Ensure identifier matches the activation being deactivated\n";
}
}
} catch (LicenseException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
try {
$result = $client->checkFeature('XXXXX-XXXXX-XXXXX-XXXXX', 'premium_feature');
if ($result['enabled']) {
echo "Feature is enabled!\n";
if (isset($result['value'])) {
echo "Feature value: " . json_encode($result['value']) . "\n";
}
} else {
echo "Feature is not enabled\n";
}
} catch (LicenseException $e) {
echo "Error: " . $e->getMessage() . "\n";
}
use GetKeyManager\SDK\LicenseClient;
$client = new LicenseClient([
'apiKey' => 'your-api-key',
'publicKey' => file_get_contents('/path/to/public-key.pem')
]);
$offlineLicense = file_get_contents('/path/to/offline-license.json');
try {
$result = $client->validateOfflineLicense($offlineLicense, [
'hardwareId' => $client->generateHardwareId()
]);
if ($result['valid']) {
echo "Offline license is valid!\n";
print_r($result['license']);
} else {
echo "Offline license validation failed:\n";
foreach ($result['errors'] as $error) {
echo " - $error\n";
}
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
$result = $client->sendTelemetry(
'XXXXX-XXXXX-XXXXX-XXXXX',
'application.started',
[
'version' => '1.0.0',
'platform' => PHP_OS
],
[
'custom_field' => 'custom_value'
]
);
if ($result['success']) {
echo "Telemetry sent successfully\n";
}
$hardwareId = $client->generateHardwareId();
echo "Hardware ID: $hardwareId\n";
| Option | Type | Default | Description |
|---|---|---|---|
apiKey |
string | required | Your API key |
publicKey |
string | null | RSA public key for signature verification |
baseUrl |
string | https://api.getkeymanager.com |
API base URL |
timeout |
int | 30 | Request timeout in seconds |
verifySignatures |
bool | true | Verify response signatures |
environment |
string | null | Environment (production/staging/development) |
cacheEnabled |
bool | true | Enable response caching |
cacheTtl |
int | 300 | Cache TTL in seconds |
retryAttempts |
int | 3 | Number of retry attempts |
retryDelay |
int | 1000 | Retry delay in milliseconds |
The SDK uses a hierarchy of exceptions:
LicenseException (base)
├── ValidationException
├── NetworkException
├── SignatureException
├── RateLimitException
└── LicenseStatusException
├── ExpiredException
├── SuspendedException
└── RevokedException
use GetKeyManager\SDK\LicenseClient;
use GetKeyManager\SDK\ExpiredException;
use GetKeyManager\SDK\RateLimitException;
use GetKeyManager\SDK\NetworkException;
try {
$result = $client->validateLicense($licenseKey);
} catch (ExpiredException $e) {
echo "License has expired: " . $e->getMessage() . "\n";
} catch (RateLimitException $e) {
echo "Rate limit exceeded. Please try again later.\n";
} catch (NetworkException $e) {
echo "Network error: " . $e->getMessage() . "\n";
} catch (LicenseException $e) {
echo "License error: " . $e->getMessage() . "\n";
}
The SDK automatically caches:
// Clear all cache
$client->clearCache();
// Clear cache for specific license
$client->clearLicenseCache('XXXXX-XXXXX-XXXXX-XXXXX');
All API responses are cryptographically signed. The SDK automatically verifies signatures when verifySignatures is enabled.
use GetKeyManager\SDK\SignatureVerifier;
$verifier = new SignatureVerifier($publicKeyPem);
$data = '{"license":"XXXXX-XXXXX-XXXXX-XXXXX"}';
$signature = 'base64_encoded_signature';
if ($verifier->verify($data, $signature)) {
echo "Signature is valid!\n";
} else {
echo "Signature verification failed!\n";
}
$jsonResponse = '{"data":{},"signature":"..."}';
if ($verifier->verifyJsonResponse($jsonResponse)) {
echo "Response signature is valid!\n";
}
Activation and deactivation operations support idempotency:
$idempotencyKey = '550e8400-e29b-41d4-a716-446655440000';
$result = $client->activateLicense($licenseKey, [
'hardwareId' => $hardwareId,
'idempotencyKey' => $idempotencyKey
]);
// Repeat with same key will return same response
$result2 = $client->activateLicense($licenseKey, [
'hardwareId' => $hardwareId,
'idempotencyKey' => $idempotencyKey
]);
// ❌ Don't hardcode API keys
$client = new LicenseClient(['apiKey' => 'pk_live_...']);
// ✅ Use environment variables
$client = new LicenseClient(['apiKey' => getenv('LICENSE_API_KEY')]);
// Generate once and store
$hardwareId = $client->generateHardwareId();
file_put_contents('/var/app/hwid.txt', $hardwareId);
// Reuse stored value
$hardwareId = file_get_contents('/var/app/hwid.txt');
try {
$result = $client->validateLicense($licenseKey);
} catch (NetworkException $e) {
// Fall back to offline validation
$offlineLicense = file_get_contents('/var/app/offline-license.json');
$result = $client->validateOfflineLicense($offlineLicense);
}
// Check license every 24 hours
$lastCheck = (int) file_get_contents('/var/app/last-check.txt');
if (time() - $lastCheck > 86400) {
$result = $client->validateLicense($licenseKey);
file_put_contents('/var/app/last-check.txt', (string) time());
}
# Install dependencies
composer install
# Run tests
composer test
# Run with coverage
composer test -- --coverage-html coverage/
See the /examples directory for complete working examples:
validate.php - Basic license validationactivate.php - License activationfeatures.php - Feature flag checkingoffline.php - Offline license validationtelemetry.php - Sending telemetry dataMIT License - see LICENSE file for details
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
See CHANGELOG.md for version history.
How can I help you explore Laravel packages today?