Feature gates, quota checks, and usage tracking for Laravel applications via the subscriptions microservice.
- 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
composer require whilesmart/paywallPublish the config and migrations:
php artisan vendor:publish --tag=paywall-config
php artisan vendor:publish --tag=paywall-migrations
php artisan migrateSet your environment variables:
PAYWALL_PROVIDER=external
PAYWALL_EXTERNAL_URL=http://localhost:3000
PAYWALL_EXTERNAL_API_KEY=sk_your_api_key
PAYWALL_SUBSCRIBER_TYPE=userDefine features in config/paywall.php:
'features' => [
'project_creation' => [
'name' => 'Project Creation',
'description' => 'Create and manage projects',
'default_access' => false,
'bypass_roles' => ['admin'],
],
],// 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']);
});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);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|nullUsage is buffered locally to avoid HTTP calls during requests. Flush via cron:
php artisan paywall:flush-usageAdd to your scheduler:
$schedule->command('paywall:flush-usage')->everyMinute();- PHP ^8.2
- Laravel ^11.0|^12.0
The MIT License (MIT).