Backend (Laravel)

The backend is a stateless REST API built with Laravel. All business logic, authentication, billing, and data access lives here.

Key Directories

Controllers

app/Http/Controllers/
├── Admin/              # 14 controllers — super admin panel
│   ├── AdminDashboardController
│   ├── AdminUserController
│   ├── AdminOrganizationController
│   ├── AdminPlanController
│   ├── AdminSettingsController
│   ├── AdminEmailTemplateController
│   ├── AdminFeatureFlagController
│   ├── AdminAuditLogController
│   ├── AdminQueueController
│   ├── AdminHealthController
│   ├── AdminRoleController
│   ├── AdminPermissionController
│   ├── AdminOrgRoleController
│   └── AdminUsageController
├── Auth/               # 11 controllers — all auth methods
│   ├── LoginController
│   ├── RegisterController
│   ├── MagicLinkController
│   ├── OAuthController
│   ├── MfaController
│   ├── SamlController
│   ├── PasswordResetController
│   ├── EmailVerificationController
│   ├── SessionController
│   ├── PersonalTokenController
│   └── LogoutController
├── Organization/       # 6 controllers — multi-tenancy
│   ├── OrganizationController
│   ├── MemberController
│   ├── InvitationController
│   ├── WebhookController
│   ├── SamlConfigController
│   └── OrgSessionController
├── Custom/             # Your controllers go here
├── BillingController
├── BillingWebhookController
├── ApiKeyController
├── ProfileController
├── FileController
├── NotificationController
├── EntitlementController
├── FeatureFlagController
└── UsageController

Models

app/Models/
├── User                # Auth, MFA, roles, orgs
├── Organization        # Multi-tenant workspace
├── OrganizationUser    # Pivot with role
├── Plan                # Subscription plans
├── ApiKey              # Hashed API keys
├── Invitation          # Email invitations
├── Webhook             # Webhook endpoints
├── WebhookDelivery     # Delivery tracking
├── AuditLog            # Activity tracking
├── AppSetting          # Key-value settings
├── EmailTemplate       # Editable email templates
├── FeatureFlag         # Feature flag definitions
├── MagicLink           # Passwordless tokens
├── OrgRoleTemplate     # Custom org roles
├── UsageRecord         # Usage metrics
├── SamlConfiguration   # SSO config per org
├── OrgDomain           # Verified domains for SSO
└── Custom/             # Your models go here

Billing Drivers

app/Billing/
├── StripeCashierDriver     # Stripe via Laravel Cashier
├── PaddleDriver            # Paddle
├── MercadoPagoDriver       # MercadoPago
├── LemonSqueezyDriver      # Lemon Squeezy
├── PayPalDriver            # PayPal
└── NullBillingDriver       # No-op (free plans)

All implement the BillingGateway interface. Switch gateways from admin settings without code changes.

Middleware

app/Http/Middleware/
├── Authenticate             # Sanctum auth
├── AuthenticateApiKey       # API key auth
├── SetActiveOrganization    # Resolves current org
├── CheckEntitlement         # Feature gating (entitled:feature)
├── CheckOrgPermission       # Org-level RBAC (org.can:perm)
├── CheckApiKeyScope         # API key scope enforcement
├── ApiKeyRateLimiter        # Per-key rate limiting
├── TrackApiUsage            # Usage metering
├── AuditRequest             # Audit logging
├── EnsureMfaVerified        # MFA challenge gate
├── EnforceOrgMfa            # Org-wide MFA requirement
└── SecurityHeaders          # CSP, X-Frame-Options, etc.

Routes

All routes are in two files:

  • routes/api.php — core routes (auth, orgs, billing, admin)
  • routes/custom.php — your feature routes (auto-loaded inside the auth middleware group)

Config

config/saas.php

Tenant mode, feature definitions, and default plan entitlements.

return [
    'tenant_mode' => env('SAAS_TENANT_MODE', 'organization'), // or 'personal'

    'features' => [
        'api_keys'    => ['label' => 'API Keys',        'description' => 'Programmatic API access'],
        'webhooks'    => ['label' => 'Webhooks',         'description' => 'HTTP POST notifications'],
        'billing'     => ['label' => 'Billing',          'description' => 'Subscription management'],
        'invitations' => ['label' => 'Team Invitations', 'description' => 'Invite members to organizations'],
        'usage'       => ['label' => 'Usage',            'description' => 'Usage metrics and analytics'],
        'projects'    => ['label' => 'Projects',         'description' => 'Project management module'],
    ],

    'plans' => [
        'free' => [
            'features' => ['invitations', 'projects'],
            'limits'   => ['api_keys' => 0, 'members' => 3, 'storage_mb' => 100],
        ],
        'pro' => [
            'features' => ['api_keys', 'webhooks', 'invitations', 'billing', 'usage', 'projects'],
            'limits'   => ['api_keys' => 25, 'members' => 50, 'storage_mb' => 10000],
        ],
    ],
];

config/custom.php

Register your own features, webhook events, permissions, and resource limits. Everything added here appears automatically in the admin panel.

return [
    'features' => [
        'projects' => [
            'label'       => 'Projects',
            'description' => 'Project management with statuses and comments',
        ],
    ],

    'webhook_events' => [
        'project.created' => [
            'group'       => 'Projects',
            'description' => 'A new project was created',
        ],
    ],

    'limits' => [
        'projects' => 'Projects',
    ],

    'permissions' => [
        'view_projects',
        'create_projects',
        'update_projects',
        'delete_projects',
    ],
];

routes/custom.php

Your feature routes. Auto-loaded inside the authenticated middleware group — all routes require auth and have access to the active organization.

use App\Http\Controllers\Custom\ProjectController;

Route::prefix('projects')
    ->middleware('entitled:projects')
    ->group(function () {
        Route::get('/',             [ProjectController::class, 'index'])
            ->middleware('org.can:view_projects');
        Route::post('/',            [ProjectController::class, 'store'])
            ->middleware('org.can:create_projects');
        Route::get('/{project}',    [ProjectController::class, 'show'])
            ->middleware('org.can:view_projects');
        Route::patch('/{project}',  [ProjectController::class, 'update'])
            ->middleware('org.can:update_projects');
        Route::delete('/{project}', [ProjectController::class, 'destroy'])
            ->middleware('org.can:delete_projects');
    });