whilesmart/paywall

Feature gates, quota checks, and usage tracking for Laravel applications

Maintainers

Package info

github.com/whilesmartphp/paywall

pkg:composer/whilesmart/paywall

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-03-24 15:44 UTC

This package is auto-updated.

Last update: 2026-03-24 15:50:32 UTC


README

Feature gates, quota checks, and usage tracking for Laravel applications via the subscriptions microservice.

Latest Version on Packagist

Features

  • Feature Gates: Gate routes and actions behind subscription features
  • Quota Enforcement: Check usage limits before allowing access, return quota details on denial
  • Usage Tracking: Buffer usage locally, flush to the microservice in batches
  • Stripe Billing Proxy: Get checkout and portal URLs from the microservice without needing stripe/stripe-php
  • Multiple Providers: External (microservice API) or local (database) subscription providers
  • Grace Periods: Configurable grace period for expired subscriptions
  • Role Bypass: Admin roles can bypass feature gates
  • Laravel Auto-Discovery: Automatically registers service provider

Installation

composer require whilesmart/paywall

Publish the config and migrations:

php artisan vendor:publish --tag=paywall-config
php artisan vendor:publish --tag=paywall-migrations
php artisan migrate

Configuration

Set your environment variables:

PAYWALL_PROVIDER=external
PAYWALL_EXTERNAL_URL=http://localhost:3000
PAYWALL_EXTERNAL_API_KEY=sk_your_api_key
PAYWALL_SUBSCRIBER_TYPE=user

Define features in config/paywall.php:

'features' => [
    'project_creation' => [
        'name' => 'Project Creation',
        'description' => 'Create and manage projects',
        'default_access' => false,
        'bypass_roles' => ['admin'],
    ],
],

Usage

Middleware

// Boolean feature gate — returns 402 if denied
Route::middleware('paywall:project_creation')->group(function () {
    Route::post('/projects', [ProjectController::class, 'store']);
});

// Quota check — returns 402 with quota details if exhausted
Route::middleware('paywall.quota:projects')->group(function () {
    Route::post('/projects', [ProjectController::class, 'store']);
});

// Usage tracking — records usage after response is sent
Route::middleware('paywall.usage:api_calls')->group(function () {
    Route::get('/api/data', [DataController::class, 'index']);
});

Facade

use Whilesmart\Paywall\Facades\Paywall;

// Boolean check
if (Paywall::hasAccess($user, 'project_creation')) {
    // allowed
}

// Detailed check with limits and subscription info
$result = Paywall::checkAccess($user, 'projects');
$result->allowed;              // bool
$result->reason;               // "Quota exceeded" etc.
$result->limit->remaining;     // 3
$result->subscription->status; // "active"

// Record usage
Paywall::recordUsage($user, 'api_calls', 1, ['endpoint' => '/api/users']);

// Stripe checkout (proxied via microservice)
$url = Paywall::getCheckoutUrl($user, 'pro', $successUrl, $cancelUrl);
return redirect($url);

// Stripe billing portal
$url = Paywall::getPortalUrl($user, $returnUrl);
return redirect($url);

HasSubscription Trait

use Whilesmart\Paywall\Traits\HasSubscription;

class User extends Model
{
    use HasSubscription;
}
$user->subscribed();           // bool
$user->onTrial();              // bool
$user->canAccess('projects');  // bool
$user->checkAccess('projects'); // AccessResult
$user->featureUsage('projects'); // FeatureLimit
$user->subscription();         // array|null

Usage Buffer

Usage is buffered locally to avoid HTTP calls during requests. Flush via cron:

php artisan paywall:flush-usage

Add to your scheduler:

$schedule->command('paywall:flush-usage')->everyMinute();

Requirements

  • PHP ^8.2
  • Laravel ^11.0|^12.0

License

The MIT License (MIT).