# Billing & Langganan


## Ringkasan

Halaman **Billing** (`/billing`) adalah pusat pengelolaan langganan OmniStream untuk admin tenant. Di sini admin dapat melihat paket yang sedang aktif, memantau pemakaian (agen, kontak, pesan), mengisi saldo prepaid untuk AI agent, melihat daftar invoice, dan mengelola metode pembayaran.

Billing dikelola oleh layanan terpisah yang terintegrasi langsung dengan OmniStream.

:::info
**Hak akses:** Halaman Billing hanya dapat diakses oleh **admin** tenant. Agent dan supervisor tidak memiliki akses ke rute `/billing`.
:::

## Paket & Langganan

### Melihat paket aktif

1. Buka `/billing`. Halaman akan memuat dua data secara paralel: paket aktif (`GET /api/billing/plan`) dan pemakaian bulan ini (`GET /api/billing/usage`).
2. Kartu **Current Plan** menampilkan:
   - **Nama paket** dan **status langganan** (badge berwarna — lihat tabel di bawah)
   - **Harga** per bulan (format Rp untuk IDR, $ untuk USD)
   - **Banner peringatan trial** jika status `trialing` dan masa trial < habis — menunjukkan sisa hari

| Status | Warna | Arti |
|---|---|---|
| `active` | Hijau | Langganan aktif dan berbayar |
| `trialing` | Biru | Masa trial 14 hari berjalan |
| `past_due` | Kuning | Invoice jatuh tempo belum dibayar |
| `cancelled` | Merah | Langganan dibatalkan |
| `suspended` | — | Akses diblokir (trial habis atau past_due >7 hari) |

:::warning
Jika status menjadi `suspended`, seluruh pengguna tenant akan mendapat HTTP 403 saat memanggil API. Admin harus segera memperbarui pembayaran untuk memulihkan akses.
:::

### Upgrade atau ganti paket

1. Klik **Upgrade Plan** di kartu Current Plan.
2. Modal **Choose a Plan** menampilkan semua paket aktif dari `GET /api/billing/plans`, diurutkan berdasarkan `sort_order`.
3. Gunakan toggle **Monthly / Annual** untuk melihat harga masing-masing periode. Pilihan Annual memberikan diskon ~17%.
4. Setiap kartu paket menampilkan batas: jumlah agen, kontak, dan pesan per bulan. Nilai `0` atau negatif berarti **Unlimited**.
5. Klik **Select** untuk mengkonfirmasi. Sistem akan menghitung **proration** secara otomatis:
   - Credit untuk sisa hari paket lama
   - Charge untuk sisa hari paket baru
   - Untuk mata uang IDR, **PPN 11%** ditambahkan ke net positif
   - Invoice proration dibuat langsung (status `open`) dan dapat dilihat di halaman Invoices

:::note
Jika upgrade dilakukan di pertengahan siklus, invoice proration akan muncul di `/billing/invoices` dengan deskripsi line item yang menjelaskan rincian credit dan charge.
:::

### Membatalkan langganan

1. Klik **Cancel** (hanya muncul jika status `active` atau `trialing`).
2. Konfirmasi di dialog. Sistem akan menandai `cancel_at_period_end = true` — langganan **tidak langsung berhenti**, tetapi akan dinonaktifkan di akhir periode penagihan berjalan.

---

## Kuota & Batas Penggunaan

Setiap paket memiliki batas sumber daya yang disimpan di kolom `limits` (JSON) pada tabel `billing_plans`:

| Field | Arti |
|---|---|
| `max_agents` | Jumlah maksimum agent aktif |
| `max_contacts` | Jumlah maksimum kontak tersimpan |
| `max_messages_month` | Jumlah pesan keluar+masuk per bulan kalender |
| `max_channels` | Jumlah integrasi kanal (WhatsApp/Instagram/Email) |
| `max_ai_agents` | Jumlah AI agent yang dapat diaktifkan |

Nilai `-1` atau `0` berarti **tidak terbatas** untuk field tersebut.

### Penegakan kuota (enforcement)

Kuota ditegakkan secara real-time. Saat admin mencoba menambah agen, kontak, atau pesan melebihi batas:
- API mengembalikan **HTTP 402 Payment Required** dengan pesan yang menyebut limit saat ini dan maksimumnya
- Permintaan ditolak sampai tenant upgrade ke paket yang lebih tinggi

### Fitur paket

Selain kuota numerik, paket juga memiliki feature flag di kolom `features`:

| Flag | Arti |
|---|---|
| `ai_agent` | Akses ke fitur AI agent |
| `api_access` | Akses ke API publik OmniStream |
| `dedicated_support` | Dukungan teknis dedicated (opsional) |

---

## Pemakaian Bulan Ini

Kartu **Current Usage** di bawah kartu paket menampilkan pemakaian dari `GET /api/billing/usage`:

| Metrik | Sumber data |
|---|---|
| **Agents** | `agents_used` / `agents_limit` |
| **Contacts** | `contacts_used` / `contacts_limit` |
| **Messages (this month)** | `messages_used` / `messages_limit` |

Progress bar berubah warna sesuai persentase:
- **Biru** — di bawah 70%
- **Kuning** — 70–89%
- **Merah** — 90% ke atas

Jika limit adalah `0` (unlimited), bar tetap ditampilkan kosong.

:::tip
Data usage diambil dari tabel `tenant_usage` di control plane dan diperbarui secara berkala. Jika pemakaian baru saja meningkat signifikan, muat ulang halaman untuk mendapatkan data terkini.
:::

---

## Saldo AI (`/billing/ai-balance`)

Saldo AI adalah sistem **prepaid** dalam IDR, terpisah dari biaya langganan. Saldo ini digunakan untuk mengoperasikan fitur AI agent. Tersedia hanya jika paket memiliki feature flag `ai_agent: true`.

### Melihat saldo

Klik **Saldo AI** di header halaman Billing atau navigasi langsung ke `/billing/ai-balance`. Halaman menampilkan:

- **Saldo saat ini** (Rp) dengan progress bar
- **Badge status**: Aktif (hijau) / Saldo Rendah (kuning) / Habis (merah)
- **Threshold peringatan** — batas minimum sebelum status menjadi "Saldo Rendah"

### Top-up saldo

1. Pilih nominal dari preset: **Rp 50.000 / 100.000 / 200.000 / 500.000**, atau masukkan nominal kustom (minimum **Rp 10.000**).
2. Klik **Top Up**. Sistem membuat invoice Xendit dan langsung mengarahkan browser ke halaman pembayaran Xendit.
3. Setelah pembayaran dikonfirmasi oleh webhook Xendit, saldo otomatis bertambah dan transaksi tercatat.

### Riwayat transaksi

Tabel di bawah menampilkan 20 transaksi terakhir (dapat dipaginasi). Setiap baris menampilkan:

| Kolom | Arti |
|---|---|
| Deskripsi / Tipe | `topup`, `deduction`, `refund`, `adjustment` |
| Tanggal | Waktu transaksi (WIB) |
| Jumlah | Positif (hijau) = penambahan saldo; negatif (merah) = pemakaian |
| Saldo setelah | Saldo setelah transaksi ini |

:::note
**Catatan teknis:** Harga per token AI dihitung dalam USD berdasarkan `input_cost_usd_per_1m` dan `output_cost_usd_per_1m` di tabel `ai_pricing_config`, kemudian dikonversi ke IDR menggunakan `exchange_rate` dan markup persen yang dikonfigurasi oleh super-admin. Konfigurasi ini tidak dapat diubah dari antarmuka tenant.
:::

---

## Invoice (`/billing/invoices`)

Klik **Invoices** di header halaman Billing untuk membuka daftar invoice.

Tabel menampilkan semua invoice dari `GET /api/billing/invoices`:

| Kolom | Arti |
|---|---|
| **Invoice #** | Nomor invoice (format `INV-YYYY-MM-NNNN`) |
| **Date** | Tanggal invoice dibuat |
| **Amount** | Total tagihan (termasuk PPN 11% untuk IDR) |
| **Status** | `paid` / `open` / `overdue` / `void` |
| **PDF** | Tautan unduh PDF (jika tersedia) |

### Status invoice

| Status | Arti |
|---|---|
| `paid` | Sudah dibayar |
| `open` | Menunggu pembayaran |
| `overdue` | Melewati jatuh tempo |
| `void` | Dibatalkan/tidak berlaku |

### Penagihan otomatis (dunning)

Billing scheduler berjalan setiap 60 detik dan menangani:

1. **Invoice bulanan** — dibuat otomatis setiap tanggal 1 untuk semua langganan `active`
2. **Retry pembayaran gagal** — invoice `open` yang jatuh tempo dicoba ulang hingga 3 kali dengan interval eskalasi (1 hari, 3 hari, 7 hari)
3. Setelah 3 kali retry habis, status langganan berubah menjadi `past_due`
4. **Suspensi otomatis** — langganan `past_due` selama lebih dari 7 hari akan disuspend

Admin menerima email notifikasi pada setiap event: peringatan trial 3 hari sebelum habis, trial expired, invoice gagal bayar, dan invoice bulanan (dengan PDF terlampir).

---

## Metode Pembayaran (`/billing/payment`)

Klik **Payment Methods** di header halaman Billing untuk mengelola metode pembayaran.

### Menambah metode pembayaran

Klik **Add Method**, lalu pilih tipe:

**Virtual Account** (via Xendit):
- Pilih bank: BCA, BNI, Mandiri, BRI, atau Permata
- Nomor VA aktual dibuat oleh Xendit saat invoice diproses

**Kartu Kredit**:
- **Stripe** — untuk kartu internasional. Nomor kartu ditokenisasi langsung oleh Stripe Elements di browser; OmniStream tidak menyimpan nomor kartu
- **Xendit** — untuk kartu lokal Indonesia. Tokenisasi melalui Xendit.js

:::warning
Jika tombol Stripe atau Xendit menampilkan pesan "Not configured", berarti publishable key belum diset di environment variable frontend (`PUBLIC_STRIPE_PUBLISHABLE_KEY` atau `PUBLIC_XENDIT_PUBLISHABLE_KEY`). Hubungi administrator infrastruktur.
:::

### Menghapus metode pembayaran

Klik ikon tempat sampah di baris metode pembayaran, lalu konfirmasi. Penghapusan bersifat permanen.

---

## Endpoint API (tag **Billing**)

Semua endpoint tersedia di `/api/billing/*`.

| Aksi | Endpoint |
|---|---|
| Paket aktif | `GET /api/billing/plan` |
| Daftar paket tersedia | `GET /api/billing/plans` |
| Berlangganan baru | `POST /api/billing/subscribe` |
| Upgrade/ganti paket | `POST /api/billing/upgrade` |
| Batalkan langganan | `POST /api/billing/cancel` |
| Pemakaian bulan ini | `GET /api/billing/usage` |
| Daftar invoice | `GET /api/billing/invoices` |
| Unduh PDF invoice | `GET /api/billing/invoices/{id}/pdf` |
| Daftar metode pembayaran | `GET /api/billing/payment-methods` |
| Tambah metode pembayaran | `POST /api/billing/payment-methods` |
| Hapus metode pembayaran | `DELETE /api/billing/payment-methods/{id}` |
| Saldo AI | `GET /api/billing/ai-balance` |
| Riwayat transaksi AI | `GET /api/billing/ai-balance/transactions` |
| Top-up saldo AI | `POST /api/billing/ai-balance/topup` |

Skema lengkap tersedia di [API Reference — Billing](/api).

---

## Rute terkait

- [AI Agent](/panduan/admin/ai-agents) — konfigurasi agent AI yang mengkonsumsi saldo AI
- [Manajemen Pengguna](/panduan/admin/manajemen-pengguna) — menambah agent (dipengaruhi kuota `max_agents`)
- [Integrasi](/panduan/integrasi/integrations-overview) — menambah kanal (dipengaruhi kuota `max_channels`)
- [Activity Logs](/panduan/admin/activity-logs) — audit perubahan konfigurasi tenant
