masterix21/laravel-licensing-client
Composer 安装命令:
composer require masterix21/laravel-licensing-client
包简介
This is my package laravel-licensing-client
README 文档
README
A Laravel package for integrating license validation in your applications. Works with the Laravel Licensing server to provide secure, offline-capable license management using PASETO v4 tokens with Ed25519 signatures.
Related Packages
- Laravel Licensing Server - Server-side license management
- Laravel Licensing Filament Manager - Admin panel built with Filament
Requirements
- PHP 8.3+
- Laravel 12 or 13
Installation
composer require masterix21/laravel-licensing-client
Publish the configuration:
php artisan vendor:publish --tag="licensing-client-config"
Run the migrations:
php artisan migrate
Configuration
Add these variables to your .env:
LICENSING_SERVER_URL=https://your-licensing-server.com LICENSING_PUBLIC_KEY=your-base64-encoded-ed25519-public-key LICENSING_KEY=LIC-XXXX-XXXX-XXXX-XXXX
The full configuration is in config/licensing-client.php:
return [ 'server_url' => env('LICENSING_SERVER_URL', 'https://licensing.example.com'), 'api_version' => env('LICENSING_API_VERSION', 'v1'), 'license_key' => env('LICENSING_KEY'), 'public_key' => env('LICENSING_PUBLIC_KEY'), 'issuer' => env('LICENSING_ISSUER', 'laravel-licensing'), 'cache' => [ 'enabled' => env('LICENSING_CACHE_ENABLED', true), 'store' => env('LICENSING_CACHE_STORE', 'file'), 'ttl' => env('LICENSING_CACHE_TTL', 3600), ], 'heartbeat' => [ 'enabled' => env('LICENSING_HEARTBEAT_ENABLED', true), 'interval' => env('LICENSING_HEARTBEAT_INTERVAL', 3600), ], 'grace_period_days' => env('LICENSING_GRACE_PERIOD_DAYS', 7), 'timeout' => env('LICENSING_TIMEOUT', 30), 'debug' => env('LICENSING_DEBUG', false), 'storage_path' => storage_path('app/licensing'), 'excluded_routes' => [ 'login', 'register', 'password/*', 'licensing/*', ], ];
Usage
Facade
use LucaLongo\LaravelLicensingClient\Facades\LaravelLicensingClient; // Activate LaravelLicensingClient::activate('LIC-XXXX-XXXX-XXXX-XXXX'); // Check validity (offline, from stored PASETO token) LaravelLicensingClient::isValid(); // Validate with exception on failure $claims = LaravelLicensingClient::validate(); // Get license info from token claims $info = LaravelLicensingClient::getLicenseInfo(); // Returns: [ // 'license_id' => 1, // 'license_key_hash' => 'sha256...', // 'status' => 'active', // 'max_usages' => 5, // 'expires_at' => '2027-01-07T00:00:00+00:00', // 'issued_at' => '2027-01-01T00:00:00+00:00', // 'license_expires_at' => '2027-12-31T23:59:59+00:00', // 'force_online_after' => '2027-01-14T00:00:00+00:00', // 'grace_until' => null, // 'usage_fingerprint' => 'sha256...', // ] // Check expiration warnings LaravelLicensingClient::isExpiringSoon(7); // Check if online refresh is required (force_online_after exceeded) LaravelLicensingClient::requiresOnlineRefresh(); // Proactive refresh check (based on refresh_after from server) LaravelLicensingClient::shouldRefreshProactively(); // Refresh the token LaravelLicensingClient::refresh(); // Deactivate (with optional reason) LaravelLicensingClient::deactivate('LIC-XXXX-XXXX-XXXX-XXXX', 'switching device'); // Server health LaravelLicensingClient::isServerHealthy();
Dependency Injection
use LucaLongo\LaravelLicensingClient\LaravelLicensingClient; class LicenseController extends Controller { public function status(LaravelLicensingClient $licensing) { return response()->json([ 'valid' => $licensing->isValid(), 'info' => $licensing->getLicenseInfo(), 'requires_refresh' => $licensing->requiresOnlineRefresh(), ]); } }
Middleware
Protect routes with the license middleware:
// Single route Route::get('/dashboard', DashboardController::class)->middleware('license'); // Route group Route::middleware('license')->group(function () { Route::get('/reports', ReportsController::class); Route::get('/analytics', AnalyticsController::class); });
Middleware Behavior
The middleware follows this flow:
- Check if the route is excluded
- Validate the stored token offline
- If valid, check
force_online_after— refresh if past date - If token invalid, attempt refresh from server
- If refresh fails, check client-side grace period
- If not in grace period, check server health
- If server unreachable, start grace period and allow access
- If server healthy and no valid license, block with 403
On valid requests, the middleware also:
- Sends heartbeat if interval has elapsed
- Sets
license_expiring_soonandlicense_expires_atas request attributes if expiration is near
Accessing Expiration Warnings
public function dashboard(Request $request) { if ($request->attributes->get('license_expiring_soon')) { $expiresAt = $request->attributes->get('license_expires_at'); // Show renewal warning } }
Excluding Routes
Configure in config/licensing-client.php:
'excluded_routes' => [ 'login', 'register', 'password/*', 'api/health', ],
Grace Period
The client manages a local grace period when the licensing server is unreachable:
// Check if in grace period LaravelLicensingClient::isInGracePeriod(); // Manually start (useful for testing) LaravelLicensingClient::startGracePeriod();
The default grace period is 7 days, configurable via LICENSING_GRACE_PERIOD_DAYS.
The middleware automatically enters grace period when the server is unreachable, allowing the application to continue working.
Artisan Commands
# Activate a license (interactive if no key provided) php artisan license:activate LIC-XXXX-XXXX-XXXX-XXXX # Validate current license php artisan license:validate # Display license details php artisan license:info # Refresh token from server php artisan license:refresh # Deactivate license (with confirmation prompt) php artisan license:deactivate
Heartbeat
When enabled, the package automatically sends heartbeats to the licensing server at the configured interval. The heartbeat reports:
- Laravel version
- Application environment
Configure in .env:
LICENSING_HEARTBEAT_ENABLED=true LICENSING_HEARTBEAT_INTERVAL=3600 # seconds
The heartbeat is registered as a scheduled task in the service provider and runs via Laravel's scheduler.
Token Validation
The client validates PASETO v4 tokens offline using the Ed25519 public key. The following claims are validated:
| Claim | Validation |
|---|---|
usage_fingerprint |
Must match the current device fingerprint |
exp |
Token must not be expired |
status |
Must be active or grace |
force_online_after |
If past, an online refresh is required |
The client also stores the public_key_bundle received from the server during activation and refresh, enabling future key rotation support.
Device Fingerprinting
The client generates a stable SHA-256 fingerprint from:
- Hostname
- Machine ID (platform-specific:
/etc/machine-id,IOPlatformUUID, WMI UUID) - PHP version
- Laravel version
- Application key
This fingerprint is sent to the server during activation to bind the license to the device.
Custom Fingerprint Generator
use LucaLongo\LaravelLicensingClient\Services\FingerprintGenerator; class CustomFingerprintGenerator extends FingerprintGenerator { public function generate(): string { $components = [ $this->getHostname(), $this->getMachineId(), config('app.deployment_id'), ]; return hash('sha256', implode('|', array_filter($components))); } } // Register in a service provider $this->app->bind(FingerprintGenerator::class, CustomFingerprintGenerator::class);
Error Handling
The package throws LicensingException with specific factory methods:
use LucaLongo\LaravelLicensingClient\Exceptions\LicensingException; try { LaravelLicensingClient::validate(); } catch (LicensingException $e) { // Possible messages: // - "The provided license key is invalid." // - "The license has expired." // - "The license has not been activated." // - "The license has been suspended." // - "The license has been cancelled." // - "Device fingerprint does not match the licensed device." // - "The fingerprint is already in use by another device." // - "License usage limit has been exceeded." // - "Offline tokens are not enabled for this license." // - "Too many requests to the licensing server. Please try again later." // - "Online verification is required. Please connect to the internet." // - "Unable to reach the licensing server." // - "The license token is invalid or corrupted." // - "Public key for token verification is not configured." }
API Communication
The client communicates with the server at /api/licensing/v1/ using these endpoints:
| Method | Endpoint | Description |
|---|---|---|
| POST | /activate |
Activate a license with fingerprint |
| POST | /deactivate |
Deactivate a license |
| POST | /refresh |
Refresh the PASETO token |
| POST | /heartbeat |
Send heartbeat with usage data |
| POST | /validate |
Validate license server-side |
| POST | /licenses/show |
Get license information |
| GET | /health |
Check server health |
All responses follow the format:
{
"success": true,
"data": { ... }
}
Error responses:
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "Human-readable message"
}
}
The client handles these HTTP error codes: 404 (invalid key), 403 (fingerprint mismatch / not active), 409 (usage limit / fingerprint conflict / offline disabled), 410 (expired), 422 (validation failed), 423 (suspended / cancelled), 429 (rate limited).
Testing
In Your Application Tests
use Illuminate\Support\Facades\Http; use LucaLongo\LaravelLicensingClient\Facades\LaravelLicensingClient; public function test_protected_route(): void { Http::fake([ '*/api/licensing/v1/activate' => Http::response([ 'success' => true, 'data' => [ 'token' => 'v4.public.test-token...', 'license' => ['id' => 'ulid', 'status' => 'active'], ], ]), ]); LaravelLicensingClient::activate('TEST-KEY'); $this->get('/protected-route')->assertStatus(200); }
Mocking the Client
use LucaLongo\LaravelLicensingClient\LaravelLicensingClient; $mock = Mockery::mock(LaravelLicensingClient::class); $mock->shouldReceive('isValid')->andReturn(true); $mock->shouldReceive('getLicenseInfo')->andReturn([ 'status' => 'active', 'max_usages' => 5, ]); $this->app->instance(LaravelLicensingClient::class, $mock);
Running Package Tests
composer test # Run all tests composer test-coverage # Run with coverage composer analyse # PHPStan static analysis composer format # Laravel Pint formatting
Development Roadmap
The following features are planned for future releases:
Phase 1 — Token Security
iss(issuer) claim validationnbf(not before) claim validation- Clock skew tolerance (configurable, default ±60s)
Phase 2 — Features & Entitlements
hasFeature(string $feature): boolandgetFeatures(): arraygetEntitlement(string $key, mixed $default = null): mixed- Feature-gating middleware:
Route::middleware('license:premium_export') - Features and entitlements are stored from the API response (not in the token)
Phase 3 — Network Resilience
- Automatic retry with exponential backoff via
Http::retry()
Phase 4 — License Information
- Seat info:
getSeatsInfo()(active/available/max usages) - License vs token expiry distinction:
isLicenseExpiringSoon() - Server-side grace period awareness from
grace_untiltoken claim
Phase 5 — Key Rotation
- Public key selection via
kidfrom token footer - Proactive scheduled token refresh based on
refresh_after
Contributing
Contributions are welcome! Please submit a Pull Request.
License
MIT License. See LICENSE.md.
Credits
masterix21/laravel-licensing-client 适用场景与选型建议
masterix21/laravel-licensing-client 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 187 次下载、GitHub Stars 达 45, 最近一次更新时间为 2025 年 09 月 16 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「Luca Longo」 「laravel-licensing-client」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 masterix21/laravel-licensing-client 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 masterix21/laravel-licensing-client 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 masterix21/laravel-licensing-client 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Laravel licensing package with polymorphic assignment to any model, activation keys, expirations/renewals, and seat control via LicenseUsage. Supports offline verification with public-key–signed tokens, a CLI to generate/rotate/revoke keys, and an extensible architecture via config and contracts.
Simple and flexible entitlement management for Laravel applications, with support for plans, features, limits, and usage tracking.
This is my package laravel-licensing-filament-manager
Alfabank REST API integration
Integrate Helpdesk features to any Laravel application
Laravel package for Accurate Online API integration.
统计信息
- 总下载量: 187
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 45
- 点击次数: 22
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-09-16