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.0(2mo ago)11469↑390.9%3MITPHPPHP ^8.2CI failing

Since Apr 5Pushed 1w agoCompare

[ 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 4w ago

READMEChangelog (1)Dependencies (7)Versions (3)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

46

—

FairBetter than 92% of packages

Maintenance91

Actively maintained with recent releases

Popularity27

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~50 days

Total

2

Last Release

36d 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 (16 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)
