
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.
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.
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.