Installation
composer require bachisoft/thunder-pack
php artisan vendor:publish --provider="BachiSoft\ThunderPack\ThunderPackServiceProvider"
php artisan migrate
Configure Tenancy
Tenantable trait to your User model:
use BachiSoft\ThunderPack\Traits\Tenantable;
class User extends Authenticatable { use Tenantable; }
.env:
THUNDERPACK_DEFAULT_TENANT=1
First Use Case: Tenant Switching
use BachiSoft\ThunderPack\Facades\ThunderPack;
// Switch tenant via ID
ThunderPack::switchTenant(2);
// Or via user (automatically resolves tenant)
ThunderPack::switchTenant($user);
Verify Installation
tenants table exists$tenant1 = Tenant::find(1);
$tenant2 = Tenant::find(2);
$tenant1->users()->count(); // Should return only users for tenant 1
// In middleware or service provider
ThunderPack::resolveTenantFromSession();
if (auth()->user()->isSuperAdmin()) {
ThunderPack::disableTenantIsolation();
// Access all tenants' data
}
$subscription = ThunderPack::createSubscription($tenant, [
'plan' => 'pro',
'trial_days' => 14,
'billing_cycle' => 'monthly'
]);
// In webhook controller
ThunderPack::handlePaymentEvent($paymentData, $tenant);
if ($tenant->subscription->isActive()) {
// Grant access
}
if ($tenant->hasFeature('custom_branding')) {
// Enable custom branding
}
if ($tenant->checkLimit('max_projects', 5)) {
// Allow project creation
} else {
abort(403, 'Project limit exceeded');
}
ThunderPack::sendWhatsAppMessage($tenant, '1234567890', 'Hello from SaaS!');
ThunderPack::sendWhatsAppTemplate($tenant, 'welcome', ['name' => 'John']);
// app/Http/Kernel.php
protected $middlewareGroups = [
'web' => [
// ...
\BachiSoft\ThunderPack\Http\Middleware\ResolveTenant::class,
\BachiSoft\ThunderPack\Http\Middleware\CheckSubscription::class,
],
];
// In route middleware
ThunderPack::checkRateLimit('api_calls_per_month', 1000);
Extend the limits system via events:
// Event listener
ThunderPack::listen('tenant.limit.violation', function ($tenant, $limitName) {
// Log or notify admin
});
class Project extends Model {
use BelongsToTenant; // Automatically scopes to current tenant
public function tenant() {
return $this->belongsTo(Tenant::class);
}
}
Tenant Isolation Leaks
ThunderPack::resolveTenant() in API routes.Subscription State Confusion
isActive() returns true for trials but not paid subscriptions.isPaid() separately or use isValid() for both.WhatsApp Credentials
.env config for WhatsApp API.WHATSAPP_API_KEY=your_key
WHATSAPP_PHONE_NUMBER=+1234567890
Limit Caching
ThunderPack::clearTenantCache($tenant);
Super Admin Scope
disableTenantIsolation() is left enabled.if (auth()->user()->isSuperAdmin()) {
ThunderPack::disableTenantIsolation();
// Critical: Re-enable after access
ThunderPack::enableTenantIsolation();
}
Tenant Resolution
php artisan tinker
>>> \BachiSoft\ThunderPack\Facades\ThunderPack::getCurrentTenant();
Subscription Logs
$tenant->subscription->logs()->latest()->take(5)->get();
Limit Violations
event(new \BachiSoft\ThunderPack\Events\LimitViolation($tenant, 'max_projects'));
// Check logs for debug output
WhatsApp Debugging
WHATSAPP_DEBUG=true
storage/logs/thunderpack-whatsapp.log.Custom Tenant Fields
// Extend Tenant model
class Tenant extends \BachiSoft\ThunderPack\Models\Tenant {
protected $casts = [
'custom_field' => 'boolean',
];
}
Custom Subscription Plans
// Publish and extend config
php artisan vendor:publish --tag=thunderpack-config
// Add to config/thunderpack.php
'plans' => [
'custom_plan' => [
'price' => 99.99,
'features' => ['advanced_reports'],
],
],
Custom Limit Types
// Register new limit type
ThunderPack::extend('custom_limit', function ($tenant, $value) {
return $tenant->custom_metric >= $value;
});
Override Tenant Resolution
// In service provider
ThunderPack::macro('resolveTenant', function () {
return Tenant::where('domain', request()->getHost())->first();
});
Webhook Handling
// Extend payment event handling
ThunderPack::extendPaymentHandler('stripe', function ($data) {
// Custom Stripe logic
});
How can I help you explore Laravel packages today?