Subscriptionify: Feature-Based Subscription Management for Laravel
Laravel Composer Pacakge #Laravel #Subscriptions #Package #SaaS #Feature Flags

Subscriptionify: Feature-Based Subscription Management for Laravel

4 min read Mohamed Said Mohamed Said

What Is Subscriptionify?

Subscriptionify is a Laravel package by Rasel Islam Rafi that handles subscription plan modelling, feature quota enforcement, and usage tracking entirely within your own database. Unlike Laravel Cashier, it is completely payment-gateway-agnostic — it does not wrap any billing API. That makes it a practical choice when you bill through a provider Cashier does not support, charge against a prepaid balance, or need to gate features without charging at all.

It requires PHP 8.2 and supports Laravel 11, 12, and 13.

Installation

composer require revoltify/subscriptionify
php artisan vendor:publish --tag=subscriptionify-config
php artisan vendor:publish --tag=subscriptionify-migrations
php artisan migrate

The migrations create six tables covering plans, features, the plan/feature pivot, subscriptions, usage records, and direct feature grants.

Making Any Model Subscribable

Implement the Subscribable contract and add the InteractsWithSubscriptions trait to any Eloquent model — User, Organisation, Workspace, or whatever fits your domain:

use Revoltify\Subscriptionify\Concerns\InteractsWithSubscriptions;
use Revoltify\Subscriptionify\Contracts\Subscribable;

class Workspace extends Model implements Subscribable
{
    use InteractsWithSubscriptions;
}

Four Feature Types for Different Quota Patterns

Subscriptionify distinguishes four feature types, each with its own consumption semantics:

  • Toggle — a plain on/off capability gate.
  • Consumable — a depletable quota that resets on a schedule (e.g. monthly API calls).
  • Limit — a hard cap on a running total that can be freed again (e.g. active seats or projects).
  • Metered — pay-per-use tracking with no cap, charging per unit.

Features are created once and attached to plans through a pivot that carries the allocation:

$plan->features()->attach($reports, [
    'value'          => 500,          // 0 means unlimited
    'unit_price'     => '0.02000000',
    'reset_period'   => 1,
    'reset_interval' => 'month',
]);

Usage Tracking on the Subscribable Model

All consumption methods live directly on the subscribable model:

$workspace->subscribe($plan);
$workspace->hasFeature('reports');       // feature available?
$workspace->canConsume('reports', 10);   // 10 units available?
$workspace->consume('reports', 10);      // record usage, throws on quota breach
$workspace->tryConsume('reports', 10);   // returns false instead of throwing
$workspace->remainingUsage('reports');   // units left this period

For Limit features, release() hands units back — essential for slot-based resources like projects or seats:

$workspace->consume('projects', 1);  // create a project
$workspace->release('projects', 1);  // delete it, freeing the slot

Direct Grants Independent of the Plan

grantFeature() assigns quota directly to a subscribable on top of whatever the plan already provides. A plan with 500 reports plus a 1,000-unit grant yields 1,500 available units — useful for one-off top-ups or promotional bonuses without creating bespoke plans:

$workspace->grantFeature('reports', value: 1_000);
$workspace->revokeFeature('reports'); // plan allocation still applies

Optional Overage and Metered Billing

Billing is opt-in. Implement the HasFunds contract and the package charges overage against a balance you control. Amounts are passed as strings and compared with bccomp, so all arithmetic is arbitrary-precision rather than floating-point. Without HasFunds, features fall back to hard limits that throw on breach — meaning you can enforce quotas first and add billing later without rewriting consumption code.

Route Middleware and Blade Directives

Three middleware aliases protect routes and return 403 on failure:

Route::middleware('subscribed')->group(...);
Route::middleware('plan:pro')->group(...);
Route::middleware('feature:reports')->group(...);

Matching Blade directives gate view content, including @onTrial and @feature blocks.

Lifecycle, Events, and Scheduled Expiry

Subscriptions support changePlan(), renew(), cancel(), cancelNow(), and resume(). Each transition dispatches an event (SubscriptionCreated, SubscriptionCancelled, FeatureConsumed, etc.). An Artisan command handles expiry:

Schedule::command('subscriptionify:expire-overdue')->hourly();

Key Takeaways

  • Gateway-agnostic: works with any payment provider or no provider at all.
  • Four distinct feature types cover toggle, consumable, limit, and metered patterns.
  • Direct grants let you top up individual subscribers without creating new plans.
  • Billing via HasFunds is opt-in; quota enforcement works without it.
  • Arbitrary-precision math (bcmath) prevents floating-point billing errors.
  • Middleware and Blade directives provide first-class access gating.

Source: Laravel News — Subscriptionify: Feature-Based Subscription Management for Laravel

Found this useful?

Frequently Asked Questions

3 questions
Q01 How is Subscriptionify different from Laravel Cashier?
Laravel Cashier wraps a specific payment provider's billing API (such as Stripe or Paddle). Subscriptionify is gateway-agnostic — it tracks plans, feature quotas, and usage in your own database and does not integrate with any payment provider directly, leaving billing to you.
Q02 Can I enforce feature quotas without setting up billing?
Yes. Billing via the HasFunds contract is entirely opt-in. Without it, Subscriptionify enforces hard quota limits that throw an exception when exceeded. You can add billing later without rewriting any of your consumption code.
Q03 What is the difference between a Consumable and a Limit feature type?
A Consumable is a depletable quota that resets on a schedule, such as a monthly allowance of API calls. A Limit is a hard cap on a running total that can be freed again — for example, active project slots where deleting a project calls release() to free the slot for reuse.

Continue reading

More Articles

View all