Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

License Server Laravel Package

laravel-ready/license-server

Self-hosted licensing for Laravel apps: manage products, generate and validate license keys, track activations and expirations, and secure software distribution through a ready-to-use API with an admin panel.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and publish

    composer require laravel-ready/license-server
    php artisan vendor:publish --provider="LaravelReady\LicenseServer\LicenseServerServiceProvider"
    php artisan migrate
    

    The publish step should give you config/license-server.php, migrations for the license/activation tables, and (depending on version) route and model stubs. Read the config first — it is the source of truth for route prefix, middleware, model bindings, and table names.

  2. Inspect the registered routes before writing any client code

    php artisan route:list --path=license
    

    The package exposes API endpoints (activation/validation/deactivation style) that your distributed product will call. Confirm the prefix, verbs, and payload shape here rather than guessing from the README. This is also where you decide what middleware protects them.

  3. Seed one license manually and hit the endpoint

    // tinker
    \LaravelReady\LicenseServer\Models\License::create([
        'license_key' => 'TEST-1234-ABCD',
        'product'     => 'my-app',
        'status'      => 'active',
        'expires_at'  => now()->addYear(),
    ]);
    

    Then exercise the activation route with curl or an HTTP test. Getting one end-to-end activation working against a hand-created row is the fastest way to understand the package's contract; everything else (admin UI, issuance flow, product integration) is built around that loop.

First real use case: a desktop/self-hosted product that phones home on boot to activate a key, stores the returned token locally, and re-validates periodically.

Implementation Patterns

Issuance (server side). Treat the package models as your license ledger and wrap them in your own service rather than calling License::create() from controllers everywhere:

class LicenseIssuer
{
    public function issue(Order $order, string $product): License
    {
        return DB::transaction(fn () => License::create([
            'license_key' => $this->generateKey(),   // your format, not the package's
            'product'     => $product,
            'status'      => 'active',
            'customer_id' => $order->user_id,
            'order_id'    => $order->id,
            'expires_at'  => $order->subscription_ends_at,
        ]));
    }
}

Hook this into order-paid / subscription-renewed events so licenses are a byproduct of billing, not a manual step.

Activation flow (client side). Keep the client thin — one LicenseClient class with activate(), validate(), deactivate() methods that post to the configured endpoints and persist the returned token/instance id. Everything else in your app should ask that client isLicensed() instead of touching HTTP.

$response = Http::post(config('license-server.url').'/activate', [
    'license_key' => $key,
    'instance'    => $machineFingerprint,
    'product'     => 'my-app',
    'version'     => config('app.version'),
]);

Seat/domain limits. The package tracks activations; enforce the policy in your own layer (a max_activations column checked inside a transaction) so you control the error semantics — "seat limit reached" needs a distinct client message from "license expired" or "not found."

Periodic revalidation. For offline-tolerant apps, cache a signed local license blob with a grace period (e.g., 7–30 days) and only hard-fail after repeated failed revalidations. This avoids making your product unusable when your license server is down — a common support burden with homegrown licensing.

Entitlements/feature gating. Store a features JSON column on the license and return it at activation/validation time. The client caches it and toggles features. Keeps plan logic on the server without shipping new client code for every tier change.

Admin surface. The package gives you models and endpoints, not necessarily a UI. Wire up a simple Filament/Nova resource or a few CRUD controllers over the license model — filtering by customer, product, and status is what support actually needs day-to-day.

Testing. Use Http::fake() in the client app and RefreshDatabase plus direct model factories in the server app. Write a feature test that walks activate → validate → deactivate → validate-fails, because that state machine is where regressions hide.

Gotchas and Tips

  • Maintenance risk is the headline issue. Last release was August 2022 and the opportunity score is low. Pin it, verify it installs cleanly on your Laravel/PHP version, and be prepared to fork. Check composer.json constraints before committing; if it caps at Laravel 9 you'll need to widen them and smoke-test the migrations and routes. Budget for owning this code.

  • Route exposure is the first security decision. The package's routes may be registered without authentication. Immediately wrap them in the config's middleware group or override the route registration. Activation endpoints need rate limiting (throttle:) — otherwise license keys are brute-forceable, and keys are usually short and human-transcribable.

  • License keys in client binaries are extractable. No design fixes this completely. Mitigate by requiring a per-instance fingerprint on activation, capping seats, logging IPs/instances per key, and offering a "revoke instance" path. Treat the client key as an identifier, not a secret.

  • Activation counters race. Two simultaneous activations can both pass a count() < max check. Do the check and insert inside a transaction with a row lock (lockForUpdate() on the license row), or use a unique constraint on (license_id, instance) and handle the duplicate-key exception.

  • Config caching. After editing config/license-server.php, run php artisan config:cache (and config:clear when debugging). Model-class overrides in a cached config that point at the wrong namespace produce confusing "class not found" errors at runtime.

  • Model swapping. If the config lets you point at custom License/Activation models, your subclasses must preserve the package's table names, casts, and relationships — the internal package code will call methods on whatever class it resolves from config. Missing a relationship method breaks endpoints silently.

  • Expiry and timezone semantics. Decide decisively whether expires_at is end-of-day inclusive and store UTC. Expiry bugs surface as "license stopped working at midnight" tickets, and comparisons against now() in the app's timezone versus the DB are a frequent source.

  • Migration collisions. If you already have licenses or activations tables, publishing migrations will conflict. Rename tables via the config before migrating, especially on an existing production database.

  • Extension points worth using: the published config (route prefix, middleware, model bindings), the package events for activation/deactivation (listen and log to your own audit table — the built-in records won't be enough for support investigations), and route/migration publishing to copy-and-modify behavior you need to change. Don't edit files in vendor/; fork the repo instead so upgrades and diffs stay sane.

  • Debugging failed activations: log the raw request and response on both sides in staging. Most "it doesn't activate" reports come down to key format/trimming, a mismatched route prefix, a product slug mismatch, or a 419/CSRF/401 from middleware — all invisible if you only log "activation failed."

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core