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

Laravel Licensing Laravel Package

masterix21/laravel-licensing

A Laravel package for issuing and validating license keys with full lifecycle support. Create, activate, renew, and revoke licenses tied to your Eloquent models, with flexible usage limits, expiration policies, and cryptographic verification built in.

Deep Wiki
Context7

Getting Started

  1. Install the package:

    composer require masterix21/laravel-licensing
    
  2. Publish config and migrations, then migrate:

    php artisan vendor:publish --provider="Masterix21\LaravelLicensing\LicensingServiceProvider"
    php artisan migrate
    
  3. Open config/licensing.php first. This is where you define products, key pairs, signing settings, seat limits, and offline-verification behavior.

  4. Inspect the published migrations before running them in production. You will typically see tables for licenses, activations/seats, and signing keys. Confirm column names and indexes match how you plan to query licenses.

  5. Look at the package models and the signing/verification service next. The service is the core you will call from both the issuing application (server) and the licensed application (client). Exact class and method names should be confirmed in src/ or the README, but the flow is consistent:

    • Create a license for a product and customer.
    • Generate or assign a license key.
    • Activate the license on a machine/instance using a fingerprint.
    • Verify the license offline using a signed payload and public key.
  6. First useful use case: issue a license in tinker or a test, then verify it from a second Laravel app using only the public key and the stored license payload. This exercises the full happy path: issuance, activation, signing, and offline verification.

Implementation Patterns

Product-scoped licensing. Treat each product as a first-class record or config entry. Issue licenses against a product_id rather than a free-form string. This makes entitlement checks explicit and prevents a key for product A from unlocking product B. Scope queries by product early, because retrofitting product scoping later touches every license lookup.

Issuance through your billing flow. Do not expose raw license creation in a public endpoint. Create licenses from a trusted server context: after a successful checkout, subscription webhook, or admin action. Wrap issuance in a transaction that creates the license, generates the key, and records the initial activation if needed. Dispatch a domain event after commit so email delivery or provisioning does not block the database transaction.

Activation and seat management. Model activation as a separate record containing the license ID, a stable fingerprint, metadata (hostname, OS, app version), and timestamps. On activation:

  • Lock the license row or use a transaction to avoid seat-limit races.
  • Count active activations inside the transaction.
  • Reject or queue when the seat limit is reached.
  • Make deactivation idempotent so support can free a seat safely.

For seat transfers, prefer deactivate-then-activate rather than mutating an existing activation. Keep an audit trail of activation changes; customers will ask why a seat disappeared.

Offline verification. The client application should embed only the public key. The signed license payload should include at minimum: license ID, product, customer identifier, issued-at, expiry, seat limit, and a key ID. Verify the signature locally, then enforce expiry and product checks in your application code. Do not rely on parsing the key format itself for authorization; verify the signed payload.

A practical pattern is to cache the verified payload in the application and re-verify on startup, on a schedule, or when config changes. Offline verification is only as strong as your public-key delivery, so ship the public key with the app and pin it in config.

Key rotation. Design verification around a keyring, not a single key. Each signed payload should carry a key identifier. The verifier looks up the public key by ID and accepts any non-retired key. Rotate by:

  1. Generating a new key pair.
  2. Signing new licenses with the new private key.
  3. Shipping the new public key alongside old public keys.
  4. Retiring old keys only after the maximum license lifetime plus a grace period.

Never remove an old public key while licenses signed by it can still be valid offline.

Revocation with grace periods. Offline clients cannot observe revocation immediately. Implement a grace window: allow the app to run offline for N days since last successful online check, then require revalidation. On the server, mark licenses revoked and expose a lightweight validation endpoint. On the client, treat network failure differently from an explicit revocation response. Never lock users out solely because your licensing server is down.

Integration points. Common integration surfaces:

  • Billing webhooks create, renew, or revoke licenses.
  • A middleware or gate checks license entitlement for protected features.
  • Scheduled commands expire licenses, send renewal reminders, and prune stale activations.
  • Admin panels expose seat counts, activation history, and manual revocation.
  • API endpoints handle activation, deactivation, and heartbeat checks.

Keep the licensing service behind an interface in your app. That makes it replaceable in tests and isolates the package's API from your domain code.

Testing. Use an in-memory or test key pair and a fake licensing client. Test these cases explicitly: valid license, expired license, wrong product, revoked license, seat limit exceeded, duplicate activation, key rotation with an old key, and offline verification with a tampered payload. Freeze time in expiry tests.

Gotchas and Tips

Config caching will bite you. If you add key pairs or products to config/licensing.php, run php artisan config:cache in deployment. Stale config is the most common reason a newly rotated key "does not work" in production while it works locally.

Private keys do not belong in the client. It is easy to publish a config file and accidentally ship the signing private key. Keep private keys server-side only, load them from environment or a secrets manager, and verify that your client build contains only public keys. Add a test that asserts the private key is absent from the client config.

Seat limits need database-level protection. Counting activations and then inserting is racy under concurrent requests. Use a transaction with row locking, a unique constraint on (license_id, fingerprint) for active activations, or both. Without this, two simultaneous activations can exceed the seat limit.

Fingerprints drift. Hardware changes, VM cloning, container restarts, and OS updates change machine identity. Decide what stability you need and document it. Provide a support path to deactivate a stale seat. Avoid fingerprinting on volatile values like MAC address alone if you support containers or VMs.

Offline expiry needs clock-skew handling. Users can change system clocks. Store the last-seen trusted time and reject large backwards jumps, but allow a reasonable tolerance. If the clock is far in the future, fail closed with a clear error rather than silently granting access.

Revocation is eventually consistent offline. Do not promise instant revocation to customers. Communicate the grace period. On the server, make revocation idempotent and record who revoked and why.

Key rotation requires versioning. Include a key ID in every signed payload. If the package defaults to a single key, wrap its signing and verification calls so you can introduce a keyring without changing every call site. Keep retired public keys available for verification longer than you think necessary.

Exception handling carries meaning. Distinguish between "invalid signature", "expired", "revoked", "seat limit reached", and "server unreachable". Treating all failures as a generic "license invalid" makes support impossible. Log the specific failure category without logging the full license key.

Do not log license keys or signed payloads at info level. They are credentials. Redact them in logs, exception messages, and error trackers. If you must correlate, log a hash or the license ID instead.

Multi-tenancy needs explicit scoping. If your application is multi-tenant, scope license queries by tenant and product. A global scope on the license model is tempting but can hide bugs in admin and background jobs. Prefer explicit scoping in repositories or query objects.

Migration ordering and UUIDs. Check whether the package uses UUIDs or auto-incrementing IDs and match your foreign keys. If you already have a customers table, confirm the polymorphic or foreign-key relationship before writing your own migrations against these tables.

Extension points to know. Typical extension points include custom key generators, custom fingerprint resolvers, model replacement via config, signing algorithm selection, and events around activation/revocation. Use model replacement sparingly; it couples you to the package's schema. Prefer wrapping the package service in your own domain service for business logic.

Debugging checklist. When verification fails: confirm the public key matches the signing key ID, confirm the payload was not modified in transit or storage, check expiry against the client clock, confirm the product identifier matches exactly, and check whether the license is revoked server-side. Reproduce with a known-good fixture before changing keys.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource