Download the PHP package vnuswilliams/laravel-subscription without Composer
On this page you can find all versions of the php package vnuswilliams/laravel-subscription. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download vnuswilliams/laravel-subscription
More information about vnuswilliams/laravel-subscription
Files in vnuswilliams/laravel-subscription
Package laravel-subscription
Short Description A robust, fluent and payment-agnostic subscription management package for Laravel. Handles plans, lifecycle (trial, grace period, cancellation) and consumable feature quotas.
License MIT
Homepage https://github.com/vnuswilliams/laravel-subscription
Informations about the package laravel-subscription
Laravel Subscription
A robust, fluent, and fully payment-agnostic Laravel package for managing subscription plans, lifecycle states (trial, grace period, cancellation), and consumable feature quotas.
This package does not handle any payments. It exclusively manages subscription business logic: who has access to what, for how long, and how much they have left. You plug in the payment provider of your choice (Stripe, Paystack, Flutterwave, PayPal…) around it.
This documentation is always release in french version.
Table of Contents
- Architecture
- Installation
- Configuring Plans in the Database
- Preparing the Subscriber Model
- Entry Points: Three Ways to Use the Package
- Managing Subscriptions
- Features & Quotas
- Lifecycle & Grace Period
- Route Protection Middleware
- Laravel Events
- Artisan Command
- Full Recipe: Application Service
- API Reference
- Tips & Best Practices
Architecture
The package is built on a strict separation of concerns:
Golden rule: the HasSubscriptions trait contains no business logic. It delegates everything to the SubscriptionManager. This keeps the logic testable, injectable, and independent of the Eloquent model.
Installation
1. Install via Composer
The ServiceProvider and Facade are auto-discovered by Laravel. No manual registration needed.
2. Publish the configuration and migrations
Use the package install command to publish both the configuration file and the migrations in one step:
If config/subscriptions.php or any package migration already exists in your application, subscription:install deletes the existing file first and regenerates a fresh copy from the package. By default, the subscriptions.subscriber_key_type config value is id, which creates the classic integer subscriber_id column. Set it to uuid or ulid before running the package migrations when the model using HasSubscriptions has a UUID/ULID primary key. The migrations also use subscriptions.price.precision and subscriptions.price.scale for plan and subscription prices, defaulting to a decimal(12, 2) column so values like 100 and 19.99 are both supported.
3. (Optional) Generate the application service
The package provides a generator command that scaffolds a ready-to-use SubscriptionService tailored to the model that carries the HasSubscriptions trait in your application.
Run it and pass your model name via the --model option:
Omit the option to be prompted interactively:
This generates app/Services/SubscriptionService.php pre-filled with your model. If the file already exists, the command asks for confirmation before overwriting it. See the Full Recipe section for the generated content and usage examples.
Tip: the model name is case-insensitive —
user,User, andUSERall produceUserin the generated file.
Configuring Plans in the Database
The package does not create your plans automatically. You insert them via a seeder, a migration, or your application's admin interface.
Here is the expected structure for a monthly plan with features:
Tip: centralise all your feature slugs in a
FeatureEnumin your application. This prevents typos and gives you IDE autocompletion.
Preparing the Subscriber Model
Add the HasSubscriptions trait to any Eloquent model that needs to subscribe to a plan: User, Company, Team, Organization…
That's it. The trait automatically exposes the subscription() relationship and all of the package's fluent methods directly on your model.
Entry Points: Three Ways to Use the Package
The package exposes three interfaces depending on your context. Choose the one that fits your situation.
1. Via the Trait (on the model)
The most fluent syntax for one-off calls directly on the Eloquent instance:
Ideal in Observers, Policies, or quick checks inside a Controller.
2. Via the Facade (anywhere in the app)
The Laravel-style static syntax, accessible everywhere without injection:
Ideal in Controllers, Actions, Jobs, or Listeners.
3. Via SubscriptionManager injection (in your services)
The recommended approach for complex business logic. Fully testable, with no static dependency:
Tip: in a dedicated subscription service, always prefer direct injection. The Facade is handy for isolated calls, but makes unit testing harder.
Managing Subscriptions
Subscribing to a plan
Pass the plan slug (string) or a Plan instance directly:
The created subscription stores its own price. If you do not pass a custom price, it copies the plan price at subscription time. You can override it for negotiated prices, add-ons, discounts, or prorated amounts:
If the plan has trial_days > 0, the status will automatically be set to on_trial and trial_ends_at will be calculated. No additional action required.
Subscribing with a custom expiration
Useful for free plans or fixed-duration promotional offers:
Switching plans (upgrade / downgrade)
Cancelling a subscription
Cancellation does not cut access immediately. The user retains access until ends_at, then the grace period activates if configured. This is the expected behaviour for an end-of-period cancellation.
To check whether a subscription is cancelled but still running:
Revoking access immediately
To cut access without waiting for the end of the period (suspension for non-payment, terms of service violation, etc.):
Renewing a subscription
Starts a full new cycle from now. Useful after a successful payment:
Checking subscription status
Features & Quotas
Boolean features (yes/no access)
A boolean feature is simply attached to a plan or not. If it is not in the plan's feature list, access is denied.
For a boolean feature, the
$amountparameter is ignored.canConsume('feature', 0)andcanConsume('feature', 1)return the same result.
Consumable features (quotas)
The standard flow for a quota feature: check → act → consume.
Important: always call
canConsume()beforeconsume(). The package does not throw an exception if you consume beyond the quota — that guard is your responsibility.
Releasing a slot (decrementing consumption)
When you delete a resource, release the corresponding slot:
release() decrements used safely (never below 0). This is more reliable than deleting the last consumption record.
Inspecting quotas (for dashboards)
Example usage in a Blade view for a progress bar:
Lifecycle & Grace Period
The full lifecycle of a subscription:
The hasAccess() method is your single source of truth. It returns true for the active, on_trial, on_grace_period, and canceled (if ends_at is in the future) states. It returns false for expired and suppressed subscriptions.
Configuring the grace period per plan
The grace period is configured in the plan data (grace_days column). No global configuration is required. Each plan can have its own duration:
Route Protection Middleware
The package automatically registers the subscribed middleware. Use it in your route files:
When access is denied, the middleware returns:
- A JSON 403 if the request expects JSON (
Accept: application/json) - A redirect to
homewith anerrorflash message otherwise
To customise this behaviour, extend CheckSubscription and rebind it in your AppServiceProvider.
Laravel Events
The package dispatches native Laravel events on every lifecycle transition. Register your listeners in EventServiceProvider or using Laravel 11+ #[AsEventListener] attributes.
| Event | Triggered when | Typical use case |
|---|---|---|
SubscriptionCreated |
A new subscription is created | Welcome email, access activation |
SubscriptionCanceled |
Subscription cancelled (end of period) | Retention email, exit survey |
SubscriptionEnteredGracePeriod |
Expiration reached, grace activated | Urgent payment reminder email |
SubscriptionExpired |
Grace period over, access cut | Suspension, notification, archiving |
FeatureQuotaReached |
A feature quota is exhausted | Upsell notification, admin alert |
Artisan Command
The subscription:check-lifecycle command iterates over all subscriptions in the database and performs any missing status transitions (active → on_grace_period → expired).
It is useful for users who do not log in often: their subscription will move to grace or expire even without an incoming request, and the relevant events will be dispatched correctly.
Schedule it to run daily in routes/console.php (Laravel 11+):
Or in app/Console/Kernel.php (Laravel 10 and earlier):
Full Recipe: Application Service
Generate a ready-to-use SubscriptionService by running the generator command with the model that carries the HasSubscriptions trait:
This creates app/Services/SubscriptionService.php pre-wired to your model. Here is what it contains and how to use it:
Usage in a Controller:
API Reference
HasSubscriptions Trait
| Method | Return | Description |
|---|---|---|
subscription() |
MorphOne |
Eloquent relationship to the latest subscription |
subscribeTo($plan, $expiration, $immediately, $price) |
Subscription |
Subscribes or switches if an active subscription exists |
switchTo($plan, $immediately, $price) |
Subscription |
Switches plan |
renewSubscription() |
Subscription |
Renews from now |
hasActiveSubscription() |
bool |
Is the subscription valid? (active, trial, grace) |
currentPlan() |
Plan\|null |
Current plan |
subscriptionExpiresAt() |
Carbon\|null |
Expiration date |
canConsume($slug, $amount) |
bool |
Quota or boolean access available? |
consume($slug, $amount) |
SubscriptionUsage |
Consumes $amount units |
release($slug, $amount) |
SubscriptionUsage |
Releases $amount units |
balance($slug) |
int |
Remaining balance (PHP_INT_MAX if unlimited) |
totalCharges($slug) |
int |
Total allocated by the plan |
usedCharges($slug) |
int |
Amount consumed |
Subscription Model
| Method | Return | Description |
|---|---|---|
isActive() |
bool |
Status is active AND ends_at is in the future |
isOnTrial() |
bool |
trial_ends_at is in the future |
isOnGracePeriod() |
bool |
Within the grace window |
isCanceled() |
bool |
Cancelled (access may still be available) |
isSuppressed() |
bool |
Immediately revoked |
isExpired() |
bool |
No access remaining |
hasAccess() |
bool |
Global source of truth |
cancel() |
static |
End-of-period cancellation |
suppress() |
static |
Immediate access revocation |
renew() |
static |
Renewal from now |
Available Enums
Tips & Best Practices
Centralise your feature slugs in an enum. A typo like 'max-employes' instead of 'max-employees' silently returns false. FeatureEnum::MAX_EMPLOYEES->value never makes that mistake.
Always check before consuming. The package does not throw an exception if you call consume() when the quota is exhausted. The canConsume() guard is your responsibility.
Use release() when deleting resources. If a user deletes an employee, release the slot. Otherwise the counter stays inflated and the user loses capacity they should get back.
Do not confuse cancel() and suppress(). cancel() is a normal cancellation — access is maintained until the end of the paid period. suppress() is an administrative or punitive suspension — access is cut immediately.
Inject SubscriptionManager in your services, use the Facade in your controllers. Services need to be unit-testable — avoid the Facade in classes you test with pest or phpunit. In a Controller or a Livewire component, the Facade is perfectly appropriate.
Handle exceptions. The package throws typed exceptions for error cases:
Schedule the subscription:check-lifecycle command without fail. Without it, an inactive user who makes no requests will never see their subscription transition to expired in the database — and SubscriptionExpired events will never be dispatched.
License
This package is open-sourced software licensed under the MIT license.
All versions of laravel-subscription with dependencies
illuminate/contracts Version ^10.0|^11.0|^12.0|^13.0
illuminate/database Version ^10.0|^11.0|^12.0|^13.0
illuminate/events Version ^10.0|^11.0|^12.0|^13.0
illuminate/http Version ^10.0|^11.0|^12.0|^13.0
illuminate/routing Version ^10.0|^11.0|^12.0|^13.0
illuminate/support Version ^10.0|^11.0|^12.0|^13.0
illuminate/console Version ^10.0|^11.0|^12.0|^13.0