kazistm/subscriptions
Flexible Laravel subscription and plan management for SaaS apps. Create plans with features and usage limits, manage subscription lifecycles, track consumption via subscription usage, and extend/override models as needed. Built on laravelcm/laravel-subscriptions.
Installation
composer require kazistm/subscriptions
php artisan vendor:publish --provider="KaziSTM\Subscriptions\SubscriptionsServiceProvider" --tag="migrations"
php artisan migrate
Publish Config
php artisan vendor:publish --provider="KaziSTM\Subscriptions\SubscriptionsServiceProvider" --tag="config"
config/subscriptions.php to define your payment gateways (Stripe, PayPal, etc.) and default settings.First Use Case: Create a Plan
use KaziSTM\Subscriptions\Models\Plan;
$plan = Plan::create([
'name' => 'Premium',
'description' => 'Access to all features',
'price' => 29.99,
'interval' => 'month',
'interval_count' => 1,
'currency' => 'USD',
'trial_period_days' => 7,
]);
Make a Model Subscribable
use KaziSTM\Subscriptions\Traits\Subscribable;
class User extends Authenticatable
{
use Subscribable;
}
Subscribe a User
$user->subscribe($plan, $gateway = 'stripe', $card = null);
Create/Update Plans
Use the Plan model to define subscription tiers. Leverage the hasMany relationship for limitations (e.g., API calls, storage).
$plan->limitations()->create([
'name' => 'api_calls',
'max' => 1000,
'period' => 'month',
]);
Dynamic Pricing
Use the price field for static pricing or extend the Plan model to support dynamic calculations (e.g., tiered pricing).
// Example: Override price logic in a custom Plan model
public function getPriceAttribute()
{
return $this->base_price + ($this->base_price * $this->user_count_discount / 100);
}
Webhook Handling
Configure your payment gateway (e.g., Stripe) to send webhooks to POST /subscriptions/webhook. The package includes a WebhookController to handle events like invoice.paid or customer.subscription.deleted.
// Example: Extend webhook logic in a service
public function handleWebhook($payload)
{
if ($payload->type === 'invoice.paid') {
$subscription = Subscription::find($payload->data->object->subscription);
$subscription->markAsActive();
}
}
Cancellation & Renewals
Use the cancel() and resume() methods on the Subscription model.
$user->subscription()->cancel(); // Soft cancel
$user->subscription()->cancelNow(); // Immediate cancellation
Usage model to log consumption (e.g., API calls).
$user->trackUsage('api_calls', 1);
if ($user->hasExceededLimit('api_calls')) {
abort(429, 'Limit exceeded');
}
public function handle($request, Closure $next)
{
if (!$request->user()->isSubscribed()) {
abort(403, 'Subscription required');
}
return $next($request);
}
@subscribed
<p>You are subscribed!</p>
@else
<p>Please subscribe to access this feature.</p>
@endsubscribed
Extend the Gateway class to support non-Stripe/PayPal providers.
namespace App\Subscriptions\Gateways;
use KaziSTM\Subscriptions\Contracts\Gateway as GatewayContract;
class CustomGateway implements GatewayContract
{
public function createSubscription($user, $plan, $token)
{
// Custom logic
}
}
Register in config/subscriptions.php:
'gateways' => [
'custom' => \App\Subscriptions\Gateways\CustomGateway::class,
],
Use the tenant() method on the Subscription model to scope subscriptions to tenants.
$tenantSubscriptions = Subscription::where('tenant_id', $tenant->id)->get();
Use query builders to manage subscriptions at scale.
// Cancel all subscriptions for a plan
Subscription::where('plan_id', $plan->id)->update(['ends_at' => now()]);
Use the SubscriptionTestCase trait for seamless testing.
use KaziSTM\Subscriptions\Tests\SubscriptionTestCase;
class SubscriptionTest extends SubscriptionTestCase
{
public function test_subscription_creation()
{
$user = User::factory()->create();
$plan = Plan::factory()->create();
$this->assertTrue($user->subscribe($plan));
}
}
Webhook Verification
use KaziSTM\Subscriptions\Http\Middleware\VerifyStripeWebhook;
Route::post('/subscriptions/webhook', [WebhookController::class, 'handle'])
->middleware(VerifyStripeWebhook::class);
Database Transactions
DB::transaction(function () use ($user, $plan) {
$user->subscribe($plan);
// Additional logic (e.g., send welcome email)
});
Plan Updates
proration_behavior field in the Plan model to control proration (e.g., none, create_prorations, none).Time Zones
starts_at, ends_at) are stored in UTC. Convert to user time zones when displaying to users.
$user->subscription->ends_at->tz($user->timezone)->format('F j, Y');
Soft Deletes
Subscription model uses soft deletes. Use withTrashed() if querying for canceled subscriptions.
$canceled = Subscription::withTrashed()->whereNotNull('canceled_at')->get();
Log Webhook Payloads
Add logging to the WebhookController to debug payloads:
\Log::debug('Webhook payload', ['payload' => $payload]);
Check Gateway Responses
Inspect the gateway_response field on the Subscription model for errors from payment providers.
Use Tinker for Quick Checks
php artisan tinker
$user = App\User::find(1);
$user->subscription; // Inspect current subscription
$user->subscription->plan; // Inspect plan details
Custom Events
Publish and listen to subscription events (e.g., SubscriptionCreated, SubscriptionCanceled).
// In a service provider
event(new SubscriptionCreated($subscription));
// In an event listener
public function handle(SubscriptionCreated $event)
{
// Send notification, update analytics, etc.
}
Override Models
Extend the core models (Plan, Subscription, Usage) to add custom fields or logic.
namespace App\Models;
use KaziSTM\Subscriptions\Models\Plan as BasePlan;
class Plan extends BasePlan
{
protected $casts = [
'is_active' => 'boolean',
];
}
Custom Validation
Override the validateForSubscription method in your subscribable model to add custom rules.
public function validateForSubscription(array $attributes)
{
$validator = Validator::make($attributes, [
'plan_id'
How can I help you explore Laravel packages today?