laravel-saas-kit maintained by mahbub
🚀 Laravel SaaS Kit
A complete, framework-agnostic SaaS boilerplate package for Laravel. Drop it into any Laravel app and get multi-tenancy, subscription billing, feature flags, and usage limits — all in one package.
Supports both international (Stripe) and Bangladeshi payment gateways (bKash, Nagad) out of the box.
✨ Features
- 🏢 Multi-tenancy — Single DB (column isolation) or Multi DB (per-tenant database)
- 💳 Payment Gateways — Stripe, bKash, Nagad with a unified interface
- 📦 Subscription Plans — Free/Pro/Enterprise with trial periods and grace periods
- 🚦 Feature Flags — Per-plan feature toggling
- 📊 Usage Limits — Track and enforce limits (users, projects, API calls, storage)
- 🔔 Events — PaymentSucceeded, PaymentFailed, UsageLimitReached
- 🔌 Extensible — Override any model or gateway with your own
📋 Requirements
- PHP 8.1+
- Laravel 10 or 11
📦 Installation
composer require mahbub/laravel-saas-kit
Publish config and migrations
php artisan vendor:publish --tag=saas-kit-config
php artisan vendor:publish --tag=saas-kit-migrations
php artisan migrate
⚙️ Configuration
Open config/saas-kit.php and configure your setup:
'tenancy' => [
'driver' => 'single', // 'single' or 'multi'
'identify_by' => 'subdomain', // 'subdomain', 'domain', 'header', 'path'
],
'billing' => [
'default_gateway' => 'stripe', // 'stripe', 'bkash', 'nagad'
],
Add to your .env:
# Tenancy
SAAS_TENANCY_DRIVER=single
SAAS_IDENTIFY_BY=subdomain
APP_DOMAIN=yourapp.com
# Stripe
STRIPE_KEY=pk_live_xxx
STRIPE_SECRET=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
# bKash
BKASH_APP_KEY=your_app_key
BKASH_APP_SECRET=your_app_secret
BKASH_USERNAME=your_username
BKASH_PASSWORD=your_password
BKASH_SANDBOX=false
# Nagad
NAGAD_MERCHANT_ID=your_merchant_id
NAGAD_MERCHANT_NUMBER=your_number
NAGAD_PUBLIC_KEY=your_public_key
NAGAD_PRIVATE_KEY=your_private_key
NAGAD_SANDBOX=false
🚀 Usage
1. Tenant-Scoped Models
Add the BelongsToTenant trait to any model. The tenant_id column will be auto-set and all queries will be automatically scoped:
use Mahbub\SaasKit\Concerns\BelongsToTenant;
class Project extends Model
{
use BelongsToTenant;
}
// Automatically filtered by current tenant
$projects = Project::all();
// Query without scope (admin use)
$allProjects = Project::withoutTenantScope()->get();
// Query for specific tenant
$projects = Project::forTenant($tenant)->get();
2. Identify Tenant in Routes
Add the middleware to your tenant routes:
Route::middleware('identify.tenant')->group(function () {
Route::get('/dashboard', DashboardController::class);
// ...
});
3. Subscriptions
use Mahbub\SaasKit\Facades\SaasKit;
// Subscribe tenant to a plan
SaasKit::subscribeTo(plan: 'pro', gateway: 'bkash');
// Check subscription
if ($tenant->subscribed('pro')) {
// tenant is on pro plan
}
// Cancel
SaasKit::cancelSubscription();
4. Feature Flags
// Check if tenant's plan includes a feature
if (SaasKit::hasFeature('advanced_reports')) {
return view('reports.advanced');
}
return view('reports.basic');
5. Usage Limits
// Check if within limit (doesn't consume)
if (SaasKit::withinLimit('projects', count: 1)) {
// can create a project
}
// Check and consume in one step (throws if exceeded)
try {
SaasKit::consume('projects');
Project::create([...]);
} catch (UsageLimitExceededException $e) {
return response()->json(['error' => 'Project limit reached. Please upgrade.'], 403);
}
6. Payments
// Initiate a Stripe payment
$result = SaasKit::billing('stripe')->initiate($tenant, 29.99);
// Returns: ['client_secret' => '...'] (use with Stripe.js)
// Initiate a bKash payment (redirect-based)
$result = SaasKit::billing('bkash')->initiate($tenant, 2500);
return redirect($result['redirect_url']); // redirect to bKash page
// Initiate a Nagad payment
$result = SaasKit::billing('nagad')->initiate($tenant, 2500);
return redirect($result['redirect_url']);
// Verify a payment
$confirmed = SaasKit::billing('bkash')->verify($paymentId);
7. Listen to Events
// In your EventServiceProvider:
use Mahbub\SaasKit\Events\PaymentSucceeded;
use Mahbub\SaasKit\Events\UsageLimitReached;
protected $listen = [
PaymentSucceeded::class => [
ActivateSubscriptionListener::class,
],
UsageLimitReached::class => [
NotifyTenantListener::class,
],
];
🔄 Multi-Database Tenancy
Switch to multi-DB mode in config:
SAAS_TENANCY_DRIVER=multi
Each tenant gets their own database (tenant_1, tenant_2, etc.). Run tenant migrations:
// Run migrations on a specific tenant's database
$tenantManager->runForTenant($tenant, function () {
Artisan::call('migrate', ['--path' => 'database/migrations/tenant']);
});
🔧 Override Models
You can use your own models:
// config/saas-kit.php
'models' => [
'tenant' => App\Models\Tenant::class,
'plan' => App\Models\Plan::class,
'subscription' => App\Models\Subscription::class,
],
📄 License
The MIT License (MIT). See LICENSE for details.
Made with ❤️ by Mahbub — Dhaka, Bangladesh