Cover Image

Tutorial Membuat SaaS Multi-Tenant dengan PHP & MySQL: Sistem Subscription, Billing & Role Management (Production Ready 2026)

Pendahuluan

Software as a Service (SaaS) adalah model bisnis digital paling stabil di 2026. Dengan sistem subscription bulanan, Anda bisa membangun pendapatan recurring tanpa harus menjual ulang produk.

Harga Terus Naik, Kenapa Hidup Banyak Orang Terasa Semakin Berat?

Dalam tutorial lengkap ini, kita akan membangun SaaS Multi-Tenant menggunakan PHP & MySQL dengan fitur:

  • Multi-Tenant Architecture
  • Register & Login System
  • Role Management (Super Admin, Owner, User)
  • Subscription Plan
  • Billing Simulation
  • Tenant Isolation
  • Secure Authentication
  • Dashboard SaaS
  • Production Checklist

1. Konsep Multi-Tenant SaaS

Multi-tenant berarti satu aplikasi digunakan banyak perusahaan (tenant), namun data masing-masing terisolasi.

Contoh model SaaS populer seperti konsep yang digunakan oleh Slack dan Notion.

Harga Makanan Terus Naik di 2026, Apa Penyebabnya?

2. Struktur Folder Project


/saas-app
│
├── config/
│   └── database.php
│
├── auth/
│   ├── register.php
│   ├── login.php
│   └── logout.php
│
├── middleware/
│   └── auth.php
│
├── dashboard/
│   ├── tenant.php
│   └── superadmin.php
│
└── index.php

3. Database Structure


CREATE DATABASE saas_system;

CREATE TABLE tenants (
  id INT AUTO_INCREMENT PRIMARY KEY,
  company_name VARCHAR(150),
  subscription_plan VARCHAR(50),
  subscription_expired DATE,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  tenant_id INT,
  username VARCHAR(100),
  email VARCHAR(150),
  password VARCHAR(255),
  role ENUM('superadmin','owner','user') DEFAULT 'user',
  FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);

CREATE TABLE subscriptions (
  id INT AUTO_INCREMENT PRIMARY KEY,
  plan_name VARCHAR(50),
  price INT,
  duration_days INT
);

4. Koneksi Database


<?php
$conn = new mysqli("localhost","root","","saas_system");
if ($conn->connect_error) {
    die("Connection failed");
}
?>

5. Sistem Register Tenant


<?php
include "../config/database.php";

$company = $_POST['company'];
$email = $_POST['email'];
$password = password_hash($_POST['password'], PASSWORD_DEFAULT);

$conn->query("INSERT INTO tenants (company_name,subscription_plan) 
VALUES ('$company','basic')");

$tenant_id = $conn->insert_id;

$stmt = $conn->prepare("INSERT INTO users (tenant_id,username,email,password,role) VALUES (?,?,?,?,?)");
$role="owner";
$stmt->bind_param("issss",$tenant_id,$company,$email,$password,$role);
$stmt->execute();

echo "Tenant berhasil dibuat";
?>

6. Login System


session_start();
include "../config/database.php";

$email=$_POST['email'];
$password=$_POST['password'];

$stmt=$conn->prepare("SELECT * FROM users WHERE email=?");
$stmt->bind_param("s",$email);
$stmt->execute();
$result=$stmt->get_result();
$user=$result->fetch_assoc();

if($user && password_verify($password,$user['password'])){
    $_SESSION['user']=$user['username'];
    $_SESSION['tenant_id']=$user['tenant_id'];
    $_SESSION['role']=$user['role'];
    header("Location: ../dashboard/tenant.php");
}else{
    echo "Login gagal";
}

7. Tenant Data Isolation

Semua query wajib menggunakan tenant_id:


$tenant_id = $_SESSION['tenant_id'];

$stmt=$conn->prepare("SELECT * FROM projects WHERE tenant_id=?");
$stmt->bind_param("i",$tenant_id);
$stmt->execute();

8. Subscription Plan


INSERT INTO subscriptions (plan_name,price,duration_days) 
VALUES ('Basic',100000,30),
       ('Pro',250000,30),
       ('Enterprise',750000,30);

9. Billing Simulation


$plan_id = $_POST['plan_id'];

$stmt=$conn->prepare("SELECT * FROM subscriptions WHERE id=?");
$stmt->bind_param("i",$plan_id);
$stmt->execute();
$plan=$stmt->get_result()->fetch_assoc();

$expired=date('Y-m-d',strtotime("+".$plan['duration_days']." days"));

$conn->query("UPDATE tenants 
SET subscription_plan='".$plan['plan_name']."',
subscription_expired='$expired'
WHERE id=".$_SESSION['tenant_id']);

10. Middleware Subscription Check


$today=date('Y-m-d');
$tenant_id=$_SESSION['tenant_id'];

$result=$conn->query("SELECT subscription_expired FROM tenants WHERE id=$tenant_id");
$data=$result->fetch_assoc();

if($data['subscription_expired']<$today){
    die("Subscription expired. Silakan upgrade.");
}

11. Super Admin Panel

Super admin dapat melihat semua tenant:


if($_SESSION['role']=='superadmin'){
    $result=$conn->query("SELECT * FROM tenants");
    while($row=$result->fetch_assoc()){
        echo $row['company_name']." - ".$row['subscription_plan']."<br>";
    }
}

12. Production Security Checklist

  • ✔ Gunakan Prepared Statement
  • ✔ Gunakan HTTPS
  • ✔ Validasi Input
  • ✔ Rate Limit Login
  • ✔ Session Regenerate
  • ✔ Role-Based Access Control
  • ✔ Isolasi Tenant Ketat

Pengembangan Lanjutan

  • Integrasi Payment Gateway
  • Invoice Otomatis
  • Email Reminder Expired
  • Dashboard Statistik
  • API Version
  • Upgrade ke MVC Architecture

Dengan sistem ini, Anda sudah memiliki fondasi SaaS Multi-Tenant Production Ready.