getkeymanager / php-sdk
Official PHP SDK for License Management Platform API
3.3.0
2026-02-16 10:25 UTC
Requires
- php: ^7.4 || ^8.0
- ext-curl: *
- ext-json: *
- ext-openssl: *
Requires (Dev)
- php-parallel-lint/php-parallel-lint: ^1.3
- phpstan/phpstan: ^1.8
- phpunit/phpunit: ^9.5
- squizlabs/php_codesniffer: ^3.7
README
Version: 3.0.0 - Identifier-first architecture with offline-first validation!
Features
Core Features (v1.x)
- ✅ License validation (online and offline)
- ✅ License activation and deactivation
- ✅ Feature flag checking
- ✅ Hardware ID generation
- ✅ Telemetry submission
- ✅ RSA-4096-SHA256 signature verification
- ✅ Automatic retry with exponential backoff
- ✅ Built-in caching
- ✅ Idempotent operations
- ✅ PSR-12 compliant
New in v2.0.0
- ✅ Complete license management - Create, update, delete, list licenses
- ✅ License assignment - Assign licenses to customers (sync & async)
- ✅ Metadata management - Custom metadata for licenses and products
- ✅ Product management - Create and manage products via API
- ✅ Generator support - Generate licenses programmatically
- ✅ Contract management - Manage API contracts
- ✅ Downloadables access - Provide downloadable files to users
- ✅ Advanced telemetry - Retrieve telemetry data with filters
- ✅ Public changelog API - Access changelogs without authentication
New in v2.1.0
- ✅ New endpoints:
getLicenseFile(),getProductMeta(),getProduct(),getProductChangelog(),getProductPublicKey() - ✅ New response codes: LICENSE_FILE_RETRIEVED (502), LICENSE_FILE_GENERATION_FAILED (503), LICENSE_KEY_NOT_FOUND_DETAILS (501), PRODUCT_FOUND (631), PRODUCT_PUBLIC_KEY_FOUND (632), PRODUCT_PUBLIC_KEY_NOT_FOUND (633)
- ✅ Offline .lic parsing:
parseLicenseFile()(Base64 decode → 256-byte chunks → RSA PKCS1 decrypt → JSON) - ✅ License/key sync:
syncLicenseAndKey()with atomic file updates + telemetry - ✅ Validation timers:
isCheckIntervalPast()andisForceValidationPast()(fail-safe true on error)
New in v3.0.0 - BREAKING CHANGES
- ✅ Mandatory Identifier Parameter - All validation/activation methods now require
$identifier(domain or HWID) - ✅ Configuration Inheritance - Set
productPublicKey,licenseKey,licenseFilePath,defaultIdentifieronce, reuse everywhere - ✅ Offline-First Validation - Try cached .lic file first, fallback to API with
ValidationTypeconstants - ✅ Type-Safe DTOs -
ValidationResultDto,ActivationResultDto,SyncResultDtowith typed accessors - ✅ Constants & Enums -
ValidationType,IdentifierType,OptionKeysfor predictable behavior - ✅ Helper Methods -
generateIdentifier(),getIdentifierOrGenerate(),resolvePublicKey(),canValidateOffline() - ✅ Enhanced Error Messages - Actionable, context-aware errors with documentation links
- ⚠️ ⚠️ MIGRATION REQUIRED: See Migration Guide v2→v3 below
Offline .lic Parsing (LicenseValidator)
use GetKeyManager\SDK\Validation\LicenseValidator; // $validator is the LicenseValidator instance (used internally by LicenseClient) $licenseData = $validator->parseLicenseFile($licFileContent, $publicKey);
Force Validation Check
if ($validator->isForceValidationPast($licPath, $keyPath)) { showLicenseScreen(); die("License validation required"); }
30+ new methods added while maintaining full backward compatibility!
Requirements
- PHP 7.4 or higher
- ext-json
- ext-openssl
- ext-curl
Installation
Via Composer (Recommended)
composer require getkeymanager/php-sdk
Manual Installation
- Download the SDK files
- Include the autoloader:
require_once __DIR__ . '/vendor/autoload.php';
Quick Start
Initialize the Client
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 ]);
Validate a License (Online)
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"; }
Activate a License
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"; }
Deactivate a License
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"; }
Check Feature Flags
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"; }
Validate Offline License
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"; }
Send Telemetry
$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"; }
Generate Hardware ID
$hardwareId = $client->generateHardwareId(); echo "Hardware ID: $hardwareId\n";
Configuration Options
| 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 |
Error Handling
The SDK uses a hierarchy of exceptions:
LicenseException (base)
├── ValidationException
├── NetworkException
├── SignatureException
├── RateLimitException
└── LicenseStatusException
├── ExpiredException
├── SuspendedException
└── RevokedException
Example Error Handling
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"; }
Caching
The SDK automatically caches:
- License validation responses
- Feature flag checks
Clear Cache
// Clear all cache $client->clearCache(); // Clear cache for specific license $client->clearLicenseCache('XXXXX-XXXXX-XXXXX-XXXXX');
Signature Verification
All API responses are cryptographically signed. The SDK automatically verifies signatures when verifySignatures is enabled.
Using SignatureVerifier Directly
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"; }
Verify JSON Response
$jsonResponse = '{"data":{},"signature":"..."}'; if ($verifier->verifyJsonResponse($jsonResponse)) { echo "Response signature is valid!\n"; }
Idempotency
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 ]);
Best Practices
1. Store API Key Securely
// ❌ Don't hardcode API keys $client = new LicenseClient(['apiKey' => 'pk_live_...']); // ✅ Use environment variables $client = new LicenseClient(['apiKey' => getenv('LICENSE_API_KEY')]);
2. Cache Hardware ID
// 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');
3. Handle Network Failures Gracefully
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); }
4. Validate Licenses Periodically
// 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()); }
Testing
# Install dependencies composer install # Run tests composer test # Run with coverage composer test -- --coverage-html coverage/
Examples
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 data
Support
- Website: https://getkeymanager.com
- Documentation: https://docs.getkeymanager.com
- API Reference: https://dev.getkeymanager.com/api
- Issues: https://github.com/getkeymanager/php-sdk/issues
- Email: support@getkeymanager.com
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
Changelog
See CHANGELOG.md for version history.