Tutorial langkah demi langkah dari setup awal sampai operasional harian. Pelajari semua fitur dan cara mengelola bisnis restoran Anda dengan POS Restoran.
Login untuk eksplorasi semua fitur
| Role | Password | Cakupan | |
|---|---|---|---|
| Superadmin | [email protected] |
password |
Akses penuh semua menu admin & SaaS panel |
Ikuti 5 fase tutorial ini untuk menguasai POS Restoran dari nol sampai mahir. Setiap fase mencakup langkah-langkah esensial sesuai alur bisnis restoran.
Registrasi akun, konfigurasi outlet, dan atur staff untuk pertama kali.
Buka halaman login, klik "Daftar gratis". Isi nama restoran, email, password. Akun langsung aktif.
Setelah login, buka menu Pengaturan > Outlet. Isi nama outlet, alamat, jam operasional, pajak restoran (PB1), dan service charge. Satu akun bisa punya banyak outlet.
Tambah staff (kasir, chef, manager) di menu Pengaturan > Staff. Setiap staff punya role dengan izin berbeda: kasir hanya bisa transaksi, manager bisa lihat laporan, chef akses antrian dapur.
Jika restoran Anda pakai sistem meja, buat denah meja di menu Pengaturan > Meja. Tentukan nomor meja, kapasitas, dan zona (indoor/outdoor).
Hubungkan printer thermal Bluetooth/USB untuk cetak struk. Bisa atur multiple printer: satu di kasir, satu di dapur.
Input menu, bahan baku, dan resep — fondasi data bisnis Anda.
Buat kategori menu: Makanan, Minuman, Snack, Dessert. Kategori membantu pengelompokan di POS dan laporan.
Tambah menu makanan & minuman lengkap: nama, foto, harga jual, kategori, dan varian (level pedas, ukuran, topping). Upload foto dari HP langsung.
Daftarkan semua bahan baku: beras, ayam, minyak, bumbu, dll. Isi satuan (kg/gram/liter/pcs), harga beli per satuan, dan stok awal.
Buat resep untuk setiap menu: tentukan bahan baku yang dipakai + takarannya. Sistem otomatis hitung HPP (Harga Pokok Penjualan) dan food cost percentage.
Setelah semua bahan baku masuk, lakukan stok opname awal untuk memastikan stok fisik = stok sistem. Ini penting untuk akurasi laporan ke depan.
Buka shift, transaksi POS, dan kelola antrian dapur setiap hari.
Setiap awal shift, kasir buka shift dengan input saldo awal kas. Sistem mencatat semua transaksi dalam shift tersebut.
Kasir input pesanan pelanggan: pilih menu, tentukan meja (jika ada), tambah catatan khusus, terapkan diskon/voucher. Sistem otomatis kurangi stok bahan baku.
Pilih metode bayar: tunai, QRIS, kartu debit/kredit, atau split bill. Cetak struk. Sistem catat semua pembayaran untuk rekonsiliasi.
Setelah pesanan dibayar, otomatis muncul di layar dapur (kitchen display). Chef lihat urutan pesanan, proses, lalu tandai "selesai" saat makanan siap saji.
Di akhir shift, kasir tutup shift. Sistem hitung total transaksi tunai, non-tunai, diskon, pajak. Bandingkan dengan fisik uang di laci.
Pantau stok, cek laporan penjualan, dan analisis performa bisnis.
Dashboard menampilkan stok menipis dengan warna merah. Filter per kategori bahan baku. Sistem kirim notifikasi saat stok di bawah batas minimum.
Lakukan stok opname mingguan/bulanan. Bandingkan stok fisik vs stok sistem. Catat selisih dan adjustment untuk menjaga akurasi data.
Lihat laporan penjualan harian/mingguan/bulanan. Filter per outlet, per kategori, per menu. Lihat tren, best seller, dan worst seller. Export Excel & PDF.
Sistem hitung otomatis: total penjualan - HPP = laba kotor. Kurangi biaya operasional untuk dapat laba bersih. Lihat margin per menu.
Pantau pergerakan stok: barang masuk, barang keluar (terpakai), stok akhir. Rekomendasi pembelian bahan baku berdasarkan usage history.
Untuk pemilik SaaS — kelola tenant, subscription, dan monetisasi.
Lihat semua restoran yang terdaftar. Filter by plan (free/basic/pro), status aktif/trial/expired. Bisa suspend atau aktifkan tenant kapan saja.
Buat plan subscription dinamis: tentukan harga, max menu, max outlet, max staff, fitur on/off (export, multi outlet, QR menu, white label). Ubah kapan saja tanpa coding.
Dashboard SaaS menampilkan MRR, ARR, churn rate, ARPU. Lihat history pembayaran per tenant, status paid/expired, integrasi Tripay.
Kirim pengumuman massal ke semua tenant via broadcast. Generate kupon diskon untuk akuisisi user baru atau retensi user existing.
Aktifkan/nonaktifkan fitur per tenant via Feature Flags (QR menu, white label, dll). Kelola support ticket dari tenant.
Semua fitur yang tersedia di POS Restoran, dikelompokkan berdasarkan kategori.
Kasir cepat dengan antarmuka intuitif. Dukungan meja, split bill, diskon, voucher, pajak otomatis, dan print Bluetooth/USB.
Pesanan otomatis muncul di layar dapur. Chef tahu apa yang harus dimasak, urutannya, dan berapa lama pesanan pending.
Buka/tutup shift kasir dengan saldo awal. Rekap kas per shift otomatis. Lacak siapa yang bertugas di setiap shift.
Tetap bisa transaksi meski internet mati. Data tersimpan lokal dan auto-sync ke server saat online kembali.
Atur akses staff: kasir, chef, manager — masing-masing dengan izin berbeda. Full RBAC (Role-Based Access Control).
Program member dengan poin reward. Setiap transaksi kumpulkan poin yang bisa ditukar diskon. Riwayat pembelian pelanggan tercatat lengkap.
Buat kupon diskon (persentase atau nominal). Batasi masa berlaku, minimal pembelian, maksimal pemakaian. Bagikan via broadcast atau manual.
Notifikasi real-time: stok menipis, order masuk, pembayaran sukses, plan expired. Via in-app, email, atau WhatsApp (opsional).
Laporan penjualan harian/mingguan/bulanan. Filter per outlet, kategori, menu. Tren penjualan, best seller & worst seller. Export Excel & PDF.
Hitung otomatis: total penjualan - HPP = laba kotor. Kurangi biaya = laba bersih. Margin per menu, food cost analysis.
Pergerakan stok: barang masuk, keluar (terpakai), stok akhir. Nilai inventori, rekomendasi pembelian, barang kadaluarsa.
Lihat semua tenant, status plan, history subscription. Suspend/aktifkan tenant. Reset password.
Pantau pembayaran via Tripay, status paid/expired, revenue per bulan. Reminder otomatis sebelum plan expired.
Buat & edit plan sesuka hati: harga, limit menu, staff, outlet. Fitur on/off per plan (export, multi outlet, QR menu, white label).
Dashboard SaaS dengan metrik: MRR, ARR, churn rate, ARPU, LTV, conversion rate, trial-to-paid rate.
Kirim pengumuman massal ke semua tenant atau filter per plan. Track delivery status.
Aktifkan/nonaktifkan fitur per tenant tanpa deploy: QR menu, white label, export, API access, custom domain.
Superadmin, admin, support — RBAC penuh. Audit log mencatat semua aksi admin: siapa melakukan apa, kapan.
Sistem ticket untuk tenant melapor bug atau minta bantuan. Admin reply, update status, resolusi.
Email otomatis: welcome email saat daftar, reminder sebelum expiry, notifikasi pembayaran sukses/gagal.
Panduan praktis menggunakan Laravel admin panel & Flutter POS app sehari-hari.
/login di browser, masuk dengan akun demo di atas.
Cara membuat project Supabase, menjalankan SQL schema, dan menghubungkan ke Flutter.
File SQL sudah disediakan di folder supabase/
menu-images untuk upload gambar menu & logo restoran.supabase/.
supabase/README.md untuk panduan lengkap.
supabase/full-schema.sql → Run.
supabase/storage-bucket.sql untuk setup bucket gambar.
Project URL dan anon public key.
https://domain-anda.com/**.
lib/main.dart: supabaseUrl + supabaseAnonKey.
lib/services/supabase_service.dart: laravelBaseUrl + apiSharedKey.
flutter build web → upload build/web/ ke server.
Copy-paste satu per satu di SQL Editor Supabase. Total 18 tabel + 1 RPC function.
profiles
CREATE TABLE profiles (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL UNIQUE REFERENCES auth.users(id) ON DELETE CASCADE,
restaurant_name TEXT NOT NULL DEFAULT 'Restoran Saya',
address TEXT,
phone TEXT,
receipt_note TEXT DEFAULT 'Terima kasih telah berkunjung!',
logo_url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY profiles_user_policy ON profiles
FOR ALL USING (auth.uid() = user_id);
promos
CREATE TABLE promos (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
type TEXT NOT NULL DEFAULT 'percentage' CHECK (type IN ('percentage','fixed')),
value NUMERIC(12,2) NOT NULL,
min_purchase NUMERIC(12,2) NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE promos ENABLE ROW LEVEL SECURITY;
CREATE POLICY promos_user_policy ON promos FOR ALL USING (auth.uid() = user_id);
tables
CREATE TABLE tables (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
capacity INTEGER NOT NULL DEFAULT 4,
status TEXT NOT NULL DEFAULT 'available' CHECK (status IN ('available','occupied','reserved')),
current_order_id UUID,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE tables ENABLE ROW LEVEL SECURITY;
CREATE POLICY tables_user_policy ON tables FOR ALL USING (auth.uid() = user_id);
ingredients
CREATE TABLE ingredients (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
unit TEXT NOT NULL DEFAULT 'gram',
stock NUMERIC(12,3) NOT NULL,
min_stock NUMERIC(12,3) NOT NULL DEFAULT 0,
cost_per_unit NUMERIC(12,2) NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE ingredients ENABLE ROW LEVEL SECURITY;
CREATE POLICY ingredients_user_policy ON ingredients FOR ALL USING (auth.uid() = user_id);
menus
CREATE TABLE menus (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
category TEXT NOT NULL DEFAULT 'Makanan',
price NUMERIC(12,2) NOT NULL,
image_url TEXT,
is_available BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE menus ENABLE ROW LEVEL SECURITY;
CREATE POLICY menus_user_policy ON menus FOR ALL USING (auth.uid() = user_id);
recipes
CREATE TABLE recipes (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
menu_id UUID NOT NULL REFERENCES menus(id) ON DELETE CASCADE,
ingredient_id UUID NOT NULL REFERENCES ingredients(id) ON DELETE CASCADE,
quantity NUMERIC(12,3) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE recipes ENABLE ROW LEVEL SECURITY;
CREATE POLICY recipes_user_policy ON recipes FOR ALL
USING (EXISTS (SELECT 1 FROM menus WHERE menus.id = recipes.menu_id AND menus.user_id = auth.uid()));
transactions
CREATE TABLE transactions (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
total_amount NUMERIC(12,2) NOT NULL,
payment_method TEXT NOT NULL DEFAULT 'cash',
amount_paid NUMERIC(12,2) NOT NULL,
change_amount NUMERIC(12,2) NOT NULL DEFAULT 0,
discount_amount NUMERIC(12,2) NOT NULL DEFAULT 0,
discount_type TEXT NOT NULL DEFAULT 'none',
discount_value NUMERIC(12,2) NOT NULL DEFAULT 0,
payment_details JSONB NOT NULL DEFAULT '[]'::jsonb,
notes TEXT,
table_id UUID REFERENCES tables(id) ON DELETE SET NULL,
member_id UUID REFERENCES members(id) ON DELETE SET NULL,
outlet_id UUID REFERENCES outlets(id) ON DELETE SET NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_transactions_user_date ON transactions(user_id, created_at DESC);
ALTER TABLE transactions ENABLE ROW LEVEL SECURITY;
CREATE POLICY transactions_user_policy ON transactions FOR ALL USING (auth.uid() = user_id);
transaction_items
CREATE TABLE transaction_items (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
transaction_id UUID NOT NULL REFERENCES transactions(id) ON DELETE CASCADE,
menu_id UUID NOT NULL REFERENCES menus(id) ON DELETE SET NULL,
menu_name TEXT NOT NULL,
quantity INTEGER NOT NULL,
unit_price NUMERIC(12,2) NOT NULL,
subtotal NUMERIC(12,2) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE transaction_items ENABLE ROW LEVEL SECURITY;
CREATE POLICY transaction_items_user_policy ON transaction_items FOR ALL
USING (EXISTS (SELECT 1 FROM transactions WHERE transactions.id = transaction_items.transaction_id AND transactions.user_id = auth.uid()));
returns
CREATE TABLE returns (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
transaction_id UUID NOT NULL REFERENCES transactions(id) ON DELETE CASCADE,
reason TEXT NOT NULL,
total_amount NUMERIC(12,2) NOT NULL,
status TEXT NOT NULL DEFAULT 'approved',
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE returns ENABLE ROW LEVEL SECURITY;
CREATE POLICY returns_user_policy ON returns FOR ALL USING (auth.uid() = user_id);
order_queues
CREATE TABLE order_queues (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
queue_number INTEGER NOT NULL,
table_name TEXT,
customer_notes TEXT,
items JSONB NOT NULL DEFAULT '[]'::jsonb,
status TEXT NOT NULL DEFAULT 'waiting' CHECK (status IN ('waiting','cooking','ready','done')),
updated_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_order_queues_active ON order_queues(user_id, status) WHERE status != 'done';
ALTER TABLE order_queues ENABLE ROW LEVEL SECURITY;
CREATE POLICY order_queues_user_policy ON order_queues FOR ALL USING (auth.uid() = user_id);
shifts
CREATE TABLE shifts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
opening_cash NUMERIC(12,2) NOT NULL,
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','closed')),
opened_at TIMESTAMPTZ NOT NULL DEFAULT now(),
closing_cash NUMERIC(12,2),
total_sales NUMERIC(12,2),
closed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE shifts ENABLE ROW LEVEL SECURITY;
CREATE POLICY shifts_user_policy ON shifts FOR ALL USING (auth.uid() = user_id);
vouchers
CREATE TABLE vouchers (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
code TEXT NOT NULL,
type TEXT NOT NULL CHECK (type IN ('percentage','fixed')),
value NUMERIC(12,2) NOT NULL,
min_purchase NUMERIC(12,2) NOT NULL DEFAULT 0,
max_usage INTEGER NOT NULL DEFAULT 1,
is_active BOOLEAN NOT NULL DEFAULT true,
expired_at TIMESTAMPTZ,
used_count INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE vouchers ENABLE ROW LEVEL SECURITY;
CREATE POLICY vouchers_user_policy ON vouchers FOR ALL USING (auth.uid() = user_id);
-- RPC untuk atomic increment used_count
CREATE OR REPLACE FUNCTION increment_voucher_usage(voucher_code TEXT, tenant_id UUID)
RETURNS void AS $$
BEGIN
UPDATE vouchers SET used_count = used_count + 1
WHERE code = voucher_code AND user_id = tenant_id AND is_active = true;
END;
$$ LANGUAGE plpgsql;
members
CREATE TABLE members (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
phone TEXT NOT NULL,
email TEXT,
points INTEGER NOT NULL DEFAULT 0,
total_spent NUMERIC(12,2) NOT NULL DEFAULT 0,
tier TEXT NOT NULL DEFAULT 'regular' CHECK (tier IN ('regular','silver','gold')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE members ENABLE ROW LEVEL SECURITY;
CREATE POLICY members_user_policy ON members FOR ALL USING (auth.uid() = user_id);
outlets
CREATE TABLE outlets (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
address TEXT,
phone TEXT,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE outlets ENABLE ROW LEVEL SECURITY;
CREATE POLICY outlets_user_policy ON outlets FOR ALL USING (auth.uid() = user_id);
staff
CREATE TABLE staff (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
owner_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
email TEXT,
pin TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'kasir' CHECK (role IN ('kasir','supervisor')),
outlet_id UUID REFERENCES outlets(id) ON DELETE SET NULL,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE staff ENABLE ROW LEVEL SECURITY;
CREATE POLICY staff_user_policy ON staff FOR ALL USING (auth.uid() = owner_id);
notifications
CREATE TABLE notifications (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
title TEXT NOT NULL,
message TEXT NOT NULL,
is_read BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ALTER TABLE notifications ENABLE ROW LEVEL SECURITY;
CREATE POLICY notifications_user_policy ON notifications FOR ALL USING (auth.uid() = user_id);
support_tickets
CREATE TABLE support_tickets (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
tenant_id INTEGER NOT NULL,
subject TEXT NOT NULL,
message TEXT NOT NULL,
priority TEXT NOT NULL DEFAULT 'medium' CHECK (priority IN ('low','medium','high','urgent')),
status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','in_progress','resolved','closed')),
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
ticket_replies
CREATE TABLE ticket_replies (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
ticket_id UUID NOT NULL REFERENCES support_tickets(id) ON DELETE CASCADE,
sender TEXT NOT NULL CHECK (sender IN ('admin','user')),
admin_email TEXT,
message TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
Konfigurasi Flutter
// File: lib/main.dart (baris ~12-13)
const String supabaseUrl = 'https://XXXX.supabase.co';
const String supabaseAnonKey = 'eyJhbGciOi...';
// File: lib/services/supabase_service.dart (baris ~15-20)
const String laravelBaseUrl = 'https://manage.DOMAIN-ANDA.com/api';
const String apiSharedKey = 'SHARED-KEY-DARI-ENV-LARAVEL';
const Map<String, String> apiHeaders = {
'Content-Type': 'application/json',
'X-API-Key': apiSharedKey,
};
Cara build & deploy Flutter web ke VPS, aaPanel, atau Firebase Hosting dengan domain kustom.
saas project/).
flutter build web — ini akan menghasilkan folder build/web/.
build/web/ — folder ini yang akan di-upload ke server.
flutter build apk --debug — output di build/app/outputs/flutter-apk/app-debug.apk.
flutter build apk --release — lebih kecil, optimized.
flutter build appbundle — output di build/app/outputs/bundle/release/app-release.aab.
keytool -genkey -v -keystore android/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload.
android/key.properties dengan password & alias yang dipakai saat generate keystore.
key.properties dan *.jks sudah di-exclude dari git — aman.
pos.restoran-anda.com).
build/web/ ke root folder website.
server {
listen 80;
server_name pos.restoran-anda.com;
root /www/wwwroot/pos.restoran-anda.com;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
https://pos.restoran-anda.com).
https://pos.restoran-anda.com/**.
lib/services/supabase_service.dart — baris 3:
// Ganti dari:
const String laravelBaseUrl = 'https://manage.balaspro.com/api';
// Menjadi:
const String laravelBaseUrl = 'https://DOMAIN-ANDA.com/api';
lib/services/supabase_service.dart — baris 4 (API Shared Key):
// Ganti dengan key dari .env ADMIN_API_SHARED_KEY
const String apiSharedKey = 'YOUR_SHARED_KEY';
flutter build web dan upload ulang build/web/.
npm install -g firebase-tools
firebase login
firebase init hosting → pilih project Firebase Anda → public directory: build/web → SPA: Yes.
flutter build web
firebase deploy --only hosting
https://PROJECT-ID.web.app dan https://PROJECT-ID.firebaseapp.com.
firebase.json dan .firebaserc sudah ada di project — tinggal sesuaikan project ID.
Daftar gratis, setup 2 menit, langsung bisa transaksi. Upgrade kapan saja sesuai kebutuhan bisnis Anda.