Laravel Packages

Manage Subscription Plans and Entitlements in Laravel with Laravel Entitlements

Published
Manage Subscription Plans and Entitlements in Laravel with Laravel Entitlements image

Laravel Entitlements tackles one of the more involved problems in SaaS development: modeling what a subscriber is allowed to do, how much of it they can use, and what happens when they hit a limit or switch plans.

Rather than rolling your own entitlement logic, this package gives you a structured approach built around plans, licenses, and consumption tracking. Here's a quick overview of what the package covers:

  • Slot-based and pool-based consumption — slot strategies track one usage per subject (e.g., a specific device or seat), while pool strategies drain a shared counter (e.g., AI tokens or API credits)
  • Polymorphic ownership — any Eloquent model can act as a subscriber by adding the HasEntitlements trait, making it straightforward to scope entitlements to teams, workspaces, or individual users
  • Plan catalog with billing options — plans support monthly/yearly billing cycles and can be configured as recurring or fixed-term, with items that allow per-assignment quantity overrides
  • Domain events — the package dispatches events like LicenseConsumed, ReleaseRequested, and LicenseReconciled, giving you integration points for billing webhooks, background jobs, or audit logging
  • Optional Filament v5 admin panel — a ready-made management interface for plans, categories, and licenses that can be dropped into an existing Filament project

Defining Entitlement Types

The package uses a PHP backed enum to define what kinds of entitlements exist in your app and which strategy each one uses. This means the consumption logic is tied to your domain vocabulary rather than generic configuration:

enum LicenseType: string implements EntitlementType
{
case Device = 'device';
case AiTokens = 'ai_tokens';
case Seat = 'seat';
 
public function strategy(): EntitlementStrategy
{
return match ($this) {
self::Device => new SlotStrategy(twoPhase: true),
self::AiTokens => new PoolStrategy(),
self::Seat => new SlotStrategy(),
};
}
}

The twoPhase: true option on the Device case is worth noting — it splits release into two steps (requestRelease() then confirmRelease()), which is useful when you need an external process (like revoking a device token) to complete before the slot is freed.

Consuming and Checking Entitlements

Once a plan is assigned to a subscriber, consuming an entitlement is a single method call:

// Slot-based: tie usage to a specific subject
Entitlements::consume($workspace, LicenseType::Seat, $user);
 
// Pool-based: drain a set amount from the shared counter
Entitlements::consume($workspace, LicenseType::AiTokens, $usage, amount: 1500);

Checking availability before consuming is just as direct:

Entitlements::can($workspace, LicenseType::AiTokens, 1500); // bool
Entitlements::available($workspace, LicenseType::AiTokens); // remaining capacity

When capacity runs out, the package throws a NoEntitlementAvailableException, which you can catch and handle at the controller or middleware level.

Plan Transitions

Switching a subscriber between plans is handled through changePlan(), which supports both immediate switches and end-of-period transitions. The package validates the transition before persisting it, so subscribers won't lose capacity for usages already in flight when a downgrade is applied.

Installation

Install via Composer:

composer require masterix21/laravel-entitlements

Then publish and run the migrations:

php artisan vendor:publish --tag="laravel-entitlements-config"
php artisan vendor:publish --tag="laravel-entitlements-migrations"
php artisan migrate

The package requires PHP 8.2+ and Laravel 11 or higher. The optional Filament integration requires Filament v5.

You can find the full documentation and source code on GitHub.

Paul Redmond photo

Staff writer at Laravel News. Full stack web developer and author.

Sponsored

serpapi logo
SerpApi

The Web Search API for Your LLM and AI Applications

Visit SerpApi

The latest

View all →
WhenMounted and BigInt Props in Inertia.js v3.8 image

WhenMounted and BigInt Props in Inertia.js v3.8

Read article
Postcodes for Laravel: GB Postcode Lookup and Geography Data image

Postcodes for Laravel: GB Postcode Lookup and Geography Data

Read article
Laravel 14 Adds a defaults() Method to Eloquent Models image

Laravel 14 Adds a defaults() Method to Eloquent Models

Read article
Laravel AI SDK and Laravel MCP Security Fixes: Update Now image

Laravel AI SDK and Laravel MCP Security Fixes: Update Now

Read article
Mailbox for Laravel: Preview and Test Rendered Email image

Mailbox for Laravel: Preview and Test Rendered Email

Read article
Count Worker Crashes as Job Exceptions in Laravel 13.34 image

Count Worker Crashes as Job Exceptions in Laravel 13.34

Read article