PHP code example of revoltify / subscriptionify

1. Go to this page and download the library: Download revoltify/subscriptionify library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

revoltify / subscriptionify example snippets


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

class Team extends Model implements Subscribable
{
    use InteractsWithSubscriptions;
}

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

// 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->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)

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->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

$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
]);

$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)

$team->subscribe($plan);

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

$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

$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

$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();

// 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);

// 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');

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]);
    }
}

// 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)

$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

$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<int, FeatureInfo>
$info->isActive();         // true

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

$features = $team->allFeatures(); // Collection<int, FeatureInfo>

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
});

// 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();

use Illuminate\Support\Facades\Schedule;

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

// 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
    }
}

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
}

// 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',
    ],
];

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,
],

use Revoltify\Subscriptionify\Subscriptionify;

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

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 
bash
php artisan vendor:publish --tag=subscriptionify-config
php artisan vendor:publish --tag=subscriptionify-migrations
php artisan migrate
bash
php artisan subscriptionify:expire-overdue
bash
php artisan vendor:publish --tag=subscriptionify-config