PHPackages                             revoltify/subscriptionify - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Payment Processing](/categories/payments)
4. /
5. revoltify/subscriptionify

ActiveLibrary[Payment Processing](/categories/payments)

revoltify/subscriptionify
=========================

Feature-based subscription management for Laravel

1.0.1(1mo ago)1675.3k↑4130%7MITPHPPHP ^8.2CI passing

Since Apr 5Pushed 2w ago2 watchersCompare

[ Source](https://github.com/revoltify/subscriptionify)[ Packagist](https://packagist.org/packages/revoltify/subscriptionify)[ Docs](https://github.com/revoltify/subscriptionify)[ RSS](/packages/revoltify-subscriptionify/feed)WikiDiscussions 1.x Synced 1w ago

READMEChangelog (2)Dependencies (15)Versions (5)Used By (0)

Subscriptionify
===============

[](#subscriptionify)

[![Tests](https://github.com/revoltify/subscriptionify/actions/workflows/run-tests.yml/badge.svg)](https://github.com/revoltify/subscriptionify/actions/workflows/run-tests.yml)[![Laravel](https://camo.githubusercontent.com/5c5576721d3a9233c218b8d6db0f2ea626aee0ab821ffd60b59a9e1d0e942daa/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d31312532422d7265642e737667)](https://laravel.com/)[![PHP](https://camo.githubusercontent.com/0f16581d1180dbfd4c0e13166ec1267d4ad2f2fab8281ea6d6b284cf5c65d921/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c75652e737667)](https://www.php.net/)[![License: MIT](https://camo.githubusercontent.com/784362b26e4b3546254f1893e778ba64616e362bd6ac791991d2c9e880a3a64e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e737667)](LICENSE.md)

Feature-based subscription management for Laravel. Gateway-agnostic plans, features, usage tracking, and optional overage billing.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Plans](#plans)
- [Features](#features)
- [Subscriptions](#subscriptions)
- [Feature Usage](#feature-usage)
- [Direct Feature Grants](#direct-feature-grants)
- [Metered Billing &amp; Overage](#metered-billing--overage)
- [DTOs](#dtos)
- [Middleware](#middleware)
- [Blade Directives](#blade-directives)
- [Query Scopes](#query-scopes)
- [Scheduled Commands](#scheduled-commands)
- [Events](#events)
- [Exceptions](#exceptions)
- [Configuration](#configuration)
- [Customization](#customization)
- [Testing](#testing)

---

Requirements
------------

[](#requirements)

- PHP 8.2+
- Laravel 11, 12, or 13

Installation
------------

[](#installation)

```
composer require revoltify/subscriptionify
```

Publish the config and migrations, then run them:

```
php artisan vendor:publish --tag=subscriptionify-config
php artisan vendor:publish --tag=subscriptionify-migrations
php artisan migrate
```

This creates six tables: `plans`, `features`, `feature_plan`, `subscriptions`, `feature_usages`, and `feature_subscribable`.

---

Quick Start
-----------

[](#quick-start)

Add the trait and contract to your subscribable model (e.g. `Team`, `User`, or `Organization`):

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

class Team extends Model implements Subscribable
{
    use InteractsWithSubscriptions;
}
```

Create a plan, add features, and subscribe:

```
use Revoltify\Subscriptionify\Models\Plan;
use Revoltify\Subscriptionify\Models\Feature;
use Revoltify\Subscriptionify\Enums\FeatureType;
use Revoltify\Subscriptionify\Enums\Interval;

// Create a plan
$plan = Plan::create([
    'name'             => 'Pro',
    'slug'             => 'pro',
    'billing_period'   => 1,
    'billing_interval' => Interval::Month,
    'trial_days'       => 14,
]);

// Create features and attach to plan
$apiCalls = Feature::create(['name' => 'API Calls', 'slug' => 'api-calls', 'type' => FeatureType::Consumable]);

$plan->features()->attach($apiCalls, [
    'value'      => 10_000,
    'unit_price' => '0.00100000',
]);

// Subscribe
$team->subscribe($plan);

// Use features
$team->consume('api-calls', 100);
$team->remainingUsage('api-calls');   // 9900
$team->remainingOverage('api-calls'); // extra units affordable from balance
```

---

Plans
-----

[](#plans)

Plans define billing cycles, trial periods, and grace periods.

```
// Free plan — never expires
Plan::create(['name' => 'Free', 'slug' => 'free', 'is_free' => true]);

// Monthly plan with trial
Plan::create([
    'name'             => 'Pro',
    'slug'             => 'pro',
    'billing_period'   => 1,
    'billing_interval' => Interval::Month,
    'trial_days'       => 14,
    'grace_days'       => 3,
]);

// Quarterly plan
Plan::create([
    'name'             => 'Business',
    'slug'             => 'business',
    'billing_period'   => 3,
    'billing_interval' => Interval::Month,
]);

// Yearly plan
Plan::create([
    'name'             => 'Enterprise',
    'slug'             => 'enterprise',
    'billing_period'   => 1,
    'billing_interval' => Interval::Year,
    'grace_days'       => 7,
]);
```

### Plan columns

[](#plan-columns)

ColumnTypeDefaultDescription`name``string`—Display name`slug``string`—Unique identifier`description``string|null``null`Optional description`is_free``bool``false`Free plans never expire (`ends_at` is null)`is_active``bool``true`Whether the plan accepts new subscriptions`billing_period``int``1`Number of intervals per billing cycle`billing_interval``Interval``Month``Day`, `Week`, `Month`, or `Year``trial_days``int``0`Trial length in days (0 = no trial)`grace_days``int``0`Days of access after cancellation`sort_order``int``0`Display ordering### Plan methods

[](#plan-methods)

```
$plan->getName();              // 'Pro'
$plan->getSlug();              // 'pro'
$plan->getDescription();       // 'Professional plan'
$plan->isFree();               // false
$plan->isActive();             // true
$plan->getTrialDays();         // 14
$plan->hasTrialDays();         // true
$plan->getBillingPeriod();     // 1
$plan->getBillingInterval();   // Interval::Month
$plan->getGraceDays();         // 3
$plan->hasGraceDays();         // true
$plan->getSortOrder();         // 0
$plan->calculateEndsAt(now()); // Carbon (null for free plans)
```

---

Features
--------

[](#features)

Four feature types model different SaaS quota patterns:

TypeBehaviourResetsReleasesCharges**Toggle**On/off access gate———**Consumable**Depletable quotaPeriodicallyNoOn overage**Limit**Hard cap with releaseNoYesOn overage**Metered**Pay-per-use, no cap—NoPer unit```
use Revoltify\Subscriptionify\Models\Feature;
use Revoltify\Subscriptionify\Enums\FeatureType;

Feature::create(['name' => 'Custom Branding', 'slug' => 'branding',  'type' => FeatureType::Toggle]);
Feature::create(['name' => 'API Calls',       'slug' => 'api-calls', 'type' => FeatureType::Consumable]);
Feature::create(['name' => 'Projects',        'slug' => 'projects',  'type' => FeatureType::Limit]);
Feature::create(['name' => 'Compute Hours',   'slug' => 'compute',   'type' => FeatureType::Metered]);
```

### Feature methods

[](#feature-methods)

```
$feature->getName();           // 'API Calls'
$feature->getSlug();           // 'api-calls'
$feature->getDescription();    // 'Monthly API call quota'
$feature->getType();           // FeatureType::Consumable
$feature->hasQuota();          // true (consumable & limit)
$feature->isToggle();          // false
$feature->isConsumable();      // true
$feature->isLimit();           // false
$feature->isMetered();         // false
```

### Attaching features to plans

[](#attaching-features-to-plans)

Features are attached to plans via a pivot table with allocation data:

```
$plan->features()->attach($feature, [
    'value'          => 10_000,        // quota limit (0 = unlimited)
    'unit_price'     => '0.00100000',  // overage/metered price per unit
    'reset_period'   => 1,             // reset cycle length
    'reset_interval' => 'month',       // day, week, month, or year
]);
```

> **Unlimited**: Setting `value` to `0` grants unlimited usage for that feature.

### Pivot data access

[](#pivot-data-access)

Pivot allocation data is accessed through the `HasFeaturePivot` contract on the pivot models (`FeaturePlan`, `FeatureSubscribable`):

```
$feature = $plan->features()->first();

$feature->pivot->getValue();        // 10000
$feature->pivot->getUnitPrice();    // '0.00100000'
$feature->pivot->getResetPeriod();  // 1
$feature->pivot->getResetInterval();// Interval::Month
$feature->pivot->getResetDate();    // Carbon (next reset date)
```

---

Subscriptions
-------------

[](#subscriptions)

### Creating a subscription

[](#creating-a-subscription)

```
$team->subscribe($plan);

// With custom end date
$team->subscribe($plan, endsAt: now()->addMonths(6));
```

If a plan has `trial_days > 0`, the subscription starts in `Trialing` status automatically. Free plans create subscriptions with `ends_at` set to `null` (never expires).

### Subscription statuses

[](#subscription-statuses)

StatusDescription`Active`Normal active subscription`Trialing`In trial period`PastDue`Payment overdue`Cancelled`Cancelled by user`Expired`Billing period ended### Checking status on the subscribable

[](#checking-status-on-the-subscribable)

```
$team->subscribed();               // has active/trialing subscription
$team->onPlan($plan);             // on a specific plan
$team->onTrial();                 // currently in trial
$team->onFreePlan();              // on a free plan
$team->canChangePlan($otherPlan); // not already on that plan
```

### Checking status on the subscription

[](#checking-status-on-the-subscription)

```
$subscription = $team->subscription();

$subscription->active();              // active or trialing
$subscription->onTrial();             // in trial period
$subscription->recurring();           // active, not trialing
$subscription->canceled();            // has been cancelled
$subscription->onGracePeriod();       // cancelled but still within grace period
$subscription->ended();               // cancelled and past grace period
$subscription->pastDue();             // marked as past due
$subscription->expired();             // status is Expired, or Active with past ends_at
$subscription->valid();               // active || trialing || on grace period
$subscription->hasPlan('pro');        // on a specific plan by slug
$subscription->daysRemaining();       // days until ends_at
$subscription->trialDaysRemaining();  // days remaining in trial
```

### Managing subscriptions

[](#managing-subscriptions)

```
$subscription = $team->subscription();

// Change plans
$subscription->changePlan($newPlan);
$subscription->changePlan($newPlan, endsAt: now()->addYear());
$subscription->changePlan($newPlan, resetUsages: true); // resets consumable usages

// Renew
$subscription->renew();
$subscription->renew(endsAt: now()->addYear());

// Cancel
$subscription->cancel();       // at end of billing period (grace period applies)
$subscription->cancelNow();    // immediately

// Resume (only during grace period)
$subscription->resume();

// Lifecycle
$subscription->expire();
$subscription->markPastDue();
```

### `subscriptions()` vs `subscription()`

[](#subscriptions-vs-subscription)

- `$team->subscriptions()` — raw `MorphMany` relationship (all records, any status)
- `$team->subscription()` — resolves the current active/trialing subscription, **cached per request**

---

Feature Usage
-------------

[](#feature-usage)

All feature operations are available directly on the subscribable model:

```
// Check access (does the subscribable have this feature?)
$team->hasFeature('api-calls');

// Check if specific units can be consumed
$team->canConsume('api-calls', 100);

// Consume units (throws FeatureException if quota exceeded)
$team->consume('api-calls', 100);

// Try to consume (returns false instead of throwing)
$team->tryConsume('api-calls', 100);

// Check remaining plan quota
$team->remainingUsage('api-calls');

// Check remaining overage capacity (balance / unit_price)
// Requires HasFunds + unit_price configured, returns '0' otherwise
$team->remainingOverage('api-calls');

// Check if feature has unlimited quota
$team->isUnlimitedUsage('api-calls'); // true if unlimited

// Release units (Limit type only — frees up slots)
$team->release('projects', 1);
```

### How consumption works per type

[](#how-consumption-works-per-type)

Type`consume()` behaviour**Toggle**No-op (access is checked via `hasFeature`)**Consumable**Increments usage, resets when `valid_until` expires, charges overage if `HasFunds`**Limit**Increments usage (use `release()` to free slots), charges overage if `HasFunds`**Metered**Increments usage and charges per unit if `HasFunds`---

Direct Feature Grants
---------------------

[](#direct-feature-grants)

Grant features directly to a subscribable, independent of their plan. Grants are **additive** — if a plan provides 10,000 API calls and a direct grant adds 50,000, the total quota is 60,000.

```
// Grant with quota
$team->grantFeature('api-calls', value: 50_000);

// Grant with custom unit price for overage
$team->grantFeature('api-calls', value: 50_000, unitPrice: '0.00050000');

// Grant with auto-reset
$team->grantFeature('reports', value: 100, resetPeriod: 1, resetInterval: Interval::Month);

// Grant unlimited (value: 0)
$team->grantFeature('api-calls', value: 0);

// Revoke direct grant (plan quota still applies)
$team->revokeFeature('api-calls');
```

---

Metered Billing &amp; Overage
-----------------------------

[](#metered-billing--overage)

Implement `HasFunds` alongside `Subscribable` to enable pay-per-use and overage billing:

```
use Revoltify\Subscriptionify\Contracts\HasFunds;

class Team extends Model implements Subscribable, HasFunds
{
    use InteractsWithSubscriptions;

    /** @return numeric-string */
    public function getBalance(): string
    {
        return $this->balance;
    }

    public function hasSufficientFunds(string $amount): bool
    {
        return bccomp($this->balance, $amount, 8) >= 0;
    }

    public function deductFunds(string $amount, string $description): void
    {
        /** @var numeric-string $newBalance */
        $newBalance = bcsub($this->balance, $amount, 8);

        $this->update(['balance' => $newBalance]);
    }
}
```

### Billing behaviour per feature type

[](#billing-behaviour-per-feature-type)

Feature TypeWithout `HasFunds`With `HasFunds`**Toggle**Access check onlyAccess check only**Consumable**Hard quota limit — exceeding throwsQuota + automatic overage charging when exceeded**Limit**Hard cap — exceeding throwsHard cap + automatic overage charging when exceeded**Metered**Free unlimited usage trackingCharged per unit consumed, deducted from balance**Overage** kicks in when a consumable or limit feature exceeds its quota and the subscribable has both:

1. A `unit_price` configured on the feature
2. `HasFunds` implemented with sufficient balance

### Checking remaining overage capacity

[](#checking-remaining-overage-capacity)

Use `remainingOverage()` to check how many additional overage units a subscribable can afford based on their current balance:

```
// Plan quota: 10,000 | Unit price: $0.001 | Balance: $50.00
$team->remainingUsage('api-calls');   // '10000' — plan quota remaining
$team->remainingOverage('api-calls'); // '50000' — extra units affordable (50 / 0.001)
```

Returns `'0'` when:

- The subscribable does not implement `HasFunds`
- The feature has no `unit_price` configured
- The balance is zero or negative

> **Note:** Since the balance is shared across all features, consuming overage on one feature reduces the overage capacity for all others. The value represents a point-in-time snapshot.

---

DTOs
----

[](#dtos)

### `FeatureInfo`

[](#featureinfo)

Rich snapshot of a feature's current state for a subscribable:

```
$feature = $team->featureInfo('api-calls');

$feature->name;              // 'API Calls'
$feature->slug;              // 'api-calls'
$feature->type;              // FeatureType::Consumable
$feature->limit;             // 10000
$feature->used;              // 3500
$feature->remaining;         // 6500
$feature->percentage;        // '35.00%'
$feature->unlimited;         // false
$feature->applicable;        // true (false for toggle features)
$feature->validUntil;        // '2026-05-01 00:00:00'
$feature->overageAvailable;  // true (HasFunds + unit price configured)
$feature->unitPrice;         // '0.00100000'
$feature->resetPeriod;       // 1
$feature->resetInterval;     // Interval::Month
```

### `SubscriptionInfo`

[](#subscriptioninfo)

Complete subscription snapshot for building UI:

```
$info = $team->subscriptionInfo();

$info->planName;           // 'Pro'
$info->planSlug;           // 'pro'
$info->isFree;             // false
$info->status;             // SubscriptionStatus::Active
$info->billingInterval;    // Interval::Month
$info->billingPeriod;      // 1
$info->startsAt;           // '2026-04-01 00:00:00'
$info->endsAt;             // '2026-05-01 00:00:00'
$info->trialEndsAt;        // null
$info->onTrial;            // false
$info->onGracePeriod;      // false
$info->features;           // Collection
$info->isActive();         // true
```

### `ConsumptionResult`

[](#consumptionresult)

Returned internally after consuming units:

```
$result->remaining;   // 6500
$result->cost;        // '0.00000000' or '0.50000000' (if overage)
$result->usedOverage; // false or true
```

### All features

[](#all-features)

```
$features = $team->allFeatures(); // Collection
```

---

Middleware
----------

[](#middleware)

Three middleware are registered automatically via the config. They throw `403` responses on failure.

MiddlewarePurposeUsage`subscribed`Requires active subscription`Route::middleware('subscribed')``plan:{slug}`Requires specific plan`Route::middleware('plan:pro')``feature:{slug}`Requires specific feature`Route::middleware('feature:api-calls')````
Route::middleware('subscribed')->group(function () {
    // Only accessible with an active subscription
});

Route::middleware('plan:pro')->group(function () {
    // Only accessible on the Pro plan
});

Route::middleware('feature:api-calls')->group(function () {
    // Only accessible if the subscribable has the api-calls feature
});
```

The subscribable is resolved via `Subscriptionify::resolveSubscribable()`, which defaults to `auth()->user()`. See [Subscribable Resolver](#subscribable-resolver) to customize.

---

Blade Directives
----------------

[](#blade-directives)

```
@subscribed
    {{-- Active subscription content --}}
@endsubscribed

@plan('pro')
    {{-- Pro plan only content --}}
@endplan

@feature('custom-branding')
    {{-- Custom branding enabled --}}
@endfeature

@onTrial
    {{-- Trial period notice --}}
@endonTrial

@onFreePlan
    {{-- Upgrade prompt --}}
@endonFreePlan

@onGracePeriod
    {{-- Grace period warning --}}
@endonGracePeriod
```

---

Query Scopes
------------

[](#query-scopes)

Query scopes are available on models that use the `InteractsWithSubscriptions` trait:

```
// All teams with active/trialing subscriptions
Team::whereSubscribed()->get();

// All teams on a specific plan
Team::whereOnPlan($proPlan)->get();

// All teams currently in trial
Team::whereOnTrial()->get();

// All teams with expired subscriptions
Team::whereExpired()->get();
```

---

Scheduled Commands
------------------

[](#scheduled-commands)

Subscriptionify ships with an artisan command to automatically expire overdue subscriptions:

```
php artisan subscriptionify:expire-overdue
```

This finds all `Active` subscriptions whose `ends_at` date has passed and transitions them to `Expired` status, firing a `SubscriptionExpired` event for each.

### Scheduling

[](#scheduling)

Add to your `routes/console.php`

```
use Illuminate\Support\Facades\Schedule;

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

> **Tip:** The `expired()` method on a subscription also returns `true` for active subscriptions with a past `ends_at` — providing a real-time safety net between scheduler runs.

---

Events
------

[](#events)

All lifecycle events are dispatched automatically:

EventDispatched when`SubscriptionCreated`A new subscription is created`SubscriptionRenewed`A subscription is renewed`SubscriptionCancelled`A subscription is cancelled`SubscriptionResumed`A cancelled subscription is resumed`SubscriptionPlanChanged`The subscription's plan is changed`SubscriptionExpired`A subscription is expired`SubscriptionExpiring`A subscription is about to expire`SubscriptionMarkedPastDue`A subscription is marked as past due`FeatureConsumed`Feature units are consumed`FeatureReleased`Feature units are released (limit type)```
// Example: Listen for feature consumption
use Revoltify\Subscriptionify\Events\FeatureConsumed;

class TrackApiUsage
{
    public function handle(FeatureConsumed $event): void
    {
        // $event->subscribable
        // $event->feature
        // $event->units
        // $event->remaining
        // $event->cost
        // $event->usedOverage
    }
}
```

---

Exceptions
----------

[](#exceptions)

ExceptionWhen`SubscriptionException`Already subscribed, cannot resume ended subscription`FeatureException`Feature not found, quota exceeded, non-limit release`InsufficientFundsException`Balance too low for metered charge or overage```
use Revoltify\Subscriptionify\Exceptions\SubscriptionException;
use Revoltify\Subscriptionify\Exceptions\FeatureException;
use Revoltify\Subscriptionify\Exceptions\InsufficientFundsException;

try {
    $team->consume('api-calls', 100);
} catch (FeatureException $e) {
    // Quota exceeded
} catch (InsufficientFundsException $e) {
    // Insufficient balance for overage
}
```

---

Configuration
-------------

[](#configuration)

Publish the config file:

```
php artisan vendor:publish --tag=subscriptionify-config
```

```
// config/subscriptionify.php

return [
    // Override with your own models (must extend base or implement contracts)
    'models' => [
        'plan'         => Plan::class,
        'feature'      => Feature::class,
        'subscription' => Subscription::class,
    ],

    // Rename tables if they conflict (e.g. with Cashier)
    'tables' => [
        'plans'                => 'plans',
        'features'             => 'features',
        'feature_plan'         => 'feature_plan',
        'subscriptions'        => 'subscriptions',
        'feature_usages'       => 'feature_usages',
        'feature_subscribable' => 'feature_subscribable',
    ],

    // Rename middleware aliases if they conflict
    'middleware' => [
        'subscribed' => 'subscribed',
        'plan'       => 'plan',
        'feature'    => 'feature',
    ],
];
```

---

Customization
-------------

[](#customization)

### Custom models

[](#custom-models)

Extend the base models and register them in the config. All internal relationships resolve from config automatically.

```
use Revoltify\Subscriptionify\Models\Plan as BasePlan;

class Plan extends BasePlan
{
    // Add your own columns, relationships, or methods
}
```

```
// config/subscriptionify.php
'models' => [
    'plan' => \App\Models\Plan::class,
],
```

### Subscribable resolver

[](#subscribable-resolver)

By default, `auth()->user()` is used as the subscribable for middleware and Blade directives. Override this in your `AppServiceProvider`:

```
use Revoltify\Subscriptionify\Subscriptionify;

public function boot(): void
{
    Subscriptionify::resolveSubscribableUsing(fn () => Team::current());
}
```

### Custom subscription resolution

[](#custom-subscription-resolution)

Override `resolveSubscription()` in your model to change which subscription is resolved. The default resolves the latest `Active` or `Trialing` subscription:

```
use Revoltify\Subscriptionify\Enums\SubscriptionStatus;
use Revoltify\Subscriptionify\Models\Contracts\HasSubscription;

class Team extends Model implements Subscribable
{
    use InteractsWithSubscriptions;

    protected function resolveSubscription(): ?HasSubscription
    {
        return $this->subscriptions()
            ->whereIn('status', [
                SubscriptionStatus::Active,
                SubscriptionStatus::Trialing,
                SubscriptionStatus::PastDue, // also include past-due
            ])
            ->with('plan')
            ->latest()
            ->first();
    }
}
```

> `subscription()` is `final` — override `resolveSubscription()` instead. The caching layer stays intact.

---

Testing
-------

[](#testing)

```
./vendor/bin/pest
```

###  Health Score

53

—

FairBetter than 96% of packages

Maintenance93

Actively maintained with recent releases

Popularity42

Moderate usage in the ecosystem

Community13

Small or concentrated contributor base

Maturity50

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 83.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~54 days

Total

3

Last Release

23d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/1a6975c50488b3eeb7cb613bf0b280342ce3912dc4ffaa1aa14b75add8fe070a?d=identicon)[rtraselbd](/maintainers/rtraselbd)

---

Top Contributors

[![rtraselbd](https://avatars.githubusercontent.com/u/31556372?v=4)](https://github.com/rtraselbd "rtraselbd (20 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (4 commits)")

---

Tags

laravelbillingsubscriptionsaasusagefeaturesplansmetered

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/revoltify-subscriptionify/health.svg)

```
[![Health](https://phpackages.com/badges/revoltify-subscriptionify/health.svg)](https://phpackages.com/packages/revoltify-subscriptionify)
```

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
