# Panduan Integrasi: Login Eksternal & Katalog API Marszone untuk HQ App PWA

Dokumen ini ditujukan untuk tim engineer **HQ App PWA** (aplikasi *Progressive Web App* eksternal yang berjalan terpisah di perangkat web/mobile, bukan office app yang ada di repository ini). Dokumen ini memuat panduan lengkap mengenai:
1. **Arsitektur & Konfigurasi Integrasi PWA (CORS, Base URL, Header)**
2. **Alur Autentikasi Login Eksternal (`POST /api/login`) & Manajemen Token JWT**
3. **Best Practices Pengelolaan Sesi & State di PWA (Axios/Fetch Interceptor, Token Expiry, Offline Cache)**
4. **Manajemen Akun, Profil, Ubah Sandi & Push Notification (FCM Token)**
5. **Katalog Lengkap API Marszone yang Siap Digunakan Saat Ini**
6. **Struktur Hierarki Akun (Mars HQ > Mars Kas > Mars Point > Mars Sub Point)**
7. **Standar Error Handling & Status Code**

---

## 1. Arsitektur & Informasi Koneksi

HQ App PWA berkomunikasi secara *decoupled* (terpisah) dengan backend Marszone melalui protokol HTTP/HTTPS RESTful JSON.

### Base URL
| Lingkungan (Environment) | Base URL API |
|---|---|
| **Production** | `https://marszone.id` |
| **Local Development / Staging** | `http://localhost:8080` |

### Dukungan CORS (Cross-Origin Resource Sharing)
Endpoint pada grup `/api` telah dikonfigurasi dengan filter CORS global:
- `Access-Control-Allow-Origin: *` (Mendukung origin PWA baik `http://localhost:3000`, `http://localhost:5173`, maupun domain produksi PWA).
- `Access-Control-Allow-Headers: Authorization, Content-Type, X-API-Key, Accept, X-Requested-With, Origin`
- `Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS`
- Permintaan preflight `OPTIONS` otomatis direspons dengan status `200 OK`.

### Standar Header Permintaan (Request Headers)
Semua permintaan JSON dari PWA wajib menyertakan:
```http
Content-Type: application/json
Accept: application/json
```
Untuk endpoint yang membutuhkan autentikasi pengguna:
```http
Authorization: Bearer <jwt_token_hasil_login>
```
Untuk endpoint integrasi backend-to-backend / MECS Finance (opsional):
```http
X-API-Key: mz_live_03d67a26b01782e15f45a71f216ed76fe94ac670a021a8914e3ce81b3ac19d9d
```

---

## 2. Autentikasi Login Eksternal PWA

Backend Marszone menggunakan autentikasi berbasis **JSON Web Token (JWT)** stateless. PWA tidak memerlukan session cookies PHP, sehingga bebas dari kendala proteksi `SameSite`/`cross-site cookie blocking` pada browser mobile.

```
+---------------------------------------------------------------------------------+
|                               ALUR LOGIN PWA                                    |
+---------------------------------------------------------------------------------+
[ Pengguna PWA ]                                                    [ Backend ]
       |                                                                 |
       |-- 1. Input Email/Username & Password -------------------------->|
       |   POST /api/login                                               |
       |   Payload: { "identifier": "...", "password": "..." }           |
       |                                                                 |
       |                                                   Verifikasi DB & Status:
       |                                                   - Cocokkan Hash Password
       |                                                   - Cek status blokir user
       |                                                   - Cek keaktifan tenant
       |                                                   - Ambil hierarki Tier
       |                                                   - Buat token JWT (24 jam)
       |                                                                 |
       |<-- 2. Response: 200 OK { token, data: { user, tier, ... } } ---|
       |                                                                 |
       |-- 3. PWA Simpan Token di localStorage / IndexedDB ------------->|
       |                                                                 |
       |-- 4. Panggil Endpoint Terproteksi ----------------------------->|
       |   GET /api/permissions                                          |
       |   Header -> "Authorization: Bearer <token>"                     |
       |                                                                 |
```

### Endpoint: `POST /api/login`
- **Akses:** Publik (tanpa token)
- **Rate Limit:** 5 request per menit per IP

#### Request Payload
Field `identifier`, `username`, atau `email` didukung secara fleksibel.
```json
{
  "identifier": "admin_hq",
  "password": "PasswordRahasia123!"
}
```
*(Atau gunakan key `"email"` atau `"username"`)*

#### Response Sukses (`200 OK`)
```json
{
  "status": true,
  "message": "Login successful.",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYXJzZXhwcmVzcy1vZmZpY2UiLCJzdWIiOiJhdXRob3JpemF0aW9uIiwiaWF0IjoxNzI5OTQ1NDAwLCJleHAiOjE3Mjk5ODE4MDAsInVzZXJfaWQiOjEsImxvZ2luX2lkZW50aWZpZXIiOiJhZG1pbl9ocSIsImVtYWlsIjoiYWRtaW5AbWFyc3pvbmUuaWQifQ...",
  "data": {
    "id": 1,
    "name": "Super Administrator HQ",
    "email": "admin@marszone.id",
    "username": "admin_hq",
    "employee_number": "EMP-001",
    "department_id": 1,
    "department_name": null,
    "entity_id": 1,
    "entity_type": "internal",
    "entity_name": "Mars Express Indonesia HQ",
    "account_tier": "hq",
    "user_type": "internal",
    "permissions": [
      "tickets.can-list",
      "tickets.can-view",
      "tickets.can-create",
      "tickets.can-process",
      "users.can-register-user"
    ],
    "must_change_password": false
  }
}
```

#### Field Kunci pada Response:
- `token`: String JWT Bearer yang wajib disimpan oleh PWA dan dilampirkan pada header `Authorization: Bearer <token>`.
- `data.account_tier`: Tingkatan hierarki akun dalam ekosistem Mars:
  - `"hq"`: Mars HQ (Super Admin / Manajemen Pusat)
  - `"kas"`: Mars Kas (Akun Utama / Seller / Agen Induk)
  - `"point"`: Mars Point (Sub-Account 1 tingkat di bawah Mars Kas)
  - `"sub_point"`: Mars Sub Point / Mobile (Sub-Account terbawah)
- `data.permissions`: Array berisi hak akses pengguna untuk kontrol fitur di sisi UI frontend.
- `data.must_change_password`: Jika `true`, arahkan pengguna langsung ke layar ganti password sebelum menggunakan fitur lain.

#### Response Gagal:
- **`400 Bad Request`**: Identifier/email atau password kosong.
  ```json
  {
    "status": false,
    "message": "Email/username and password are required."
  }
  ```
- **`401 Unauthorized`**: Kredensial salah, akun diblokir, atau entity non-aktif.
  ```json
  {
    "status": false,
    "message": "Email/username atau password salah"
  }
  ```

---

## 3. Best Practices Integrasi di Frontend PWA

### Contoh Integrasi dengan Axios (Dengan Auto-Bearer Interceptor & Auto-Logout)

```javascript
// src/services/api.js
import axios from 'axios';

const api = axios.create({
  baseURL: process.env.VITE_API_BASE_URL || 'https://marszone.id',
  timeout: 15000,
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
});

// 1. Request Interceptor: Otomatis lampirkan Token JWT jika ada
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('mars_auth_token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 2. Response Interceptor: Tangani Token Expired / 401 Unauthorized
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response && error.response.status === 401) {
      // Token expired atau dicabut
      console.warn('Session expired. Redirecting to login...');
      localStorage.removeItem('mars_auth_token');
      localStorage.removeItem('mars_user_data');
      if (window.location.pathname !== '/login') {
        window.location.href = '/login?expired=1';
      }
    }
    return Promise.reject(error);
  }
);

export default api;
```

### Contoh Fungsi Login di PWA:
```javascript
// src/services/auth.js
import api from './api';

export async function loginToMars(identifier, password) {
  try {
    const response = await api.post('/api/login', {
      identifier: identifier.trim(),
      password: password,
    });

    if (response.data.status && response.data.token) {
      // Simpan kredensial di local storage
      localStorage.setItem('mars_auth_token', response.data.token);
      localStorage.setItem('mars_user_data', JSON.stringify(response.data.data));
      return { success: true, user: response.data.data };
    }
    return { success: false, message: response.data.message || 'Login gagal' };
  } catch (err) {
    const message = err.response?.data?.message || 'Gagal terhubung ke server Marszone';
    return { success: false, message };
  }
}
```

### Strategi PWA Service Worker Caching:
1. **Cache First / Stale-While-Revalidate**:
   - Master data provinsi, kota, kecamatan, negara (`/api/master-locations/*`)
   - Daftar vendor ekspedisi (`/api/portal-agent/vendors`)
2. **Network Only (Wajib Selalu Online & Tidak Boleh Dicache)**:
   - `/api/login`
   - `/api/portal-agent/orders/check-price` (Harga & tarif ongkir dinamis)
   - `/api/portal-agent/orders/create` (Pemotongan saldo & generate AWB)
   - `/api/v1/mecs/topups/*` (Approval keuangan & saldo)

---

## 4. Manajemen Akun, Profil & Push Notification

### 4.1. Ambil Permissions User Terkini
- **Endpoint:** `GET /api/permissions`
- **Auth:** Wajib `Authorization: Bearer <token>`
- **Response `200 OK`:**
  ```json
  {
    "status": true,
    "permissions": ["tickets.can-list", "tickets.can-view"],
    "user": {
      "id": 1,
      "name": "Super Administrator HQ",
      "email": "admin@marszone.id",
      "employee_number": "EMP-001"
    }
  }
  ```

### 4.2. Ganti Password Pengguna
- **Endpoint:** `POST /api/users/change-password`
- **Auth:** Wajib `Authorization: Bearer <token>`
- **Payload:**
  ```json
  {
    "password": "PasswordBaruMin8Karakter",
    "confirm_password": "PasswordBaruMin8Karakter"
  }
  ```
- **Response `200 OK`:**
  ```json
  {
    "status": true,
    "message": "User data has been successfully updated"
  }
  ```

### 4.3. Registrasi Token Push Notification PWA (Firebase Cloud Messaging)
PWA dapat mendaftarkan token push notifikasi perangkat agar server Marszone dapat mengirimkan update status pengiriman, approval top-up, dsb.

- **Endpoint:** `POST /api/users/save-fcm-token`
- **Auth:** Wajib `Authorization: Bearer <token>`
- **Payload:**
  ```json
  {
    "token": "eK3xJ19_fLq...fcm_device_token_from_browser..."
  }
  ```
- **Response `200 OK`:**
  ```json
  {
    "status": true,
    "message": "FCM token saved"
  }
  ```

- **Ambil Token Tersimpan:** `GET /api/users/get-fcm-token`

### 4.4. Pemulihan Sandi (Forgot Password via WhatsApp/Email OTP)
Untuk layar *Forgot Password* di PWA:
1. **Kirim OTP:** `POST /api/forgot-password/send-otp`  
   Payload: `{"email": "user@example.com"}`
2. **Verifikasi OTP:** `POST /api/forgot-password/verify-otp`  
   Payload: `{"email": "user@example.com", "otp": "123456"}`
3. **Reset Password:** `POST /api/forgot-password/reset-password`  
   Payload: `{"email": "user@example.com", "otp": "123456", "password": "NewPassword123!"}`

---

## 5. Katalog Lengkap API Marszone yang Tersedia Saat Ini

Berikut pemetaan seluruh endpoint aktif di Marszone yang siap dihubungkan ke HQ App PWA:

### Kategori A: Master Data Lokasi & Wilayah

| Method | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| `GET` | `/api/master-locations/provinces` | Publik | Daftar seluruh provinsi di Indonesia |
| `GET` | `/api/master-locations/cities/{province_id}` | Publik | Daftar kota/kabupaten berdasarkan ID provinsi |
| `GET` | `/api/master-locations/districts/{city_id}` | Publik | Daftar kecamatan berdasarkan ID kota |
| `GET` | `/api/master-locations/subdistricts/{district_id}` | Publik | Daftar kelurahan + kode pos (`postal_code`) |
| `GET` | `/api/master-locations/countries` | Publik | Daftar negara tujuan internasional (ISO code & name) |
| `GET` | `/api/international/countries` | Publik | Alias daftar negara tujuan pengiriman internasional |
| `GET` | `/api/portal-agent/locations/sap-express/districts/search?q={query}` | Publik | Pencarian lokasi spesifik coverage SAP Express |

---

### Kategori B: Ekspedisi, Cek Ongkir & Pengiriman Domestik (Portal Agent API)

| Method | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| `GET` | `/api/portal-agent/vendors` | Publik | List kurir aktif (`JNTC`: J&T Cargo, `IDEX`: ID Express, `SAPX`: SAP Express) |
| `GET` | `/api/portal-agent/vendors/{vendor_code}` | Publik | Detail info kurir tertentu |
| `POST` | `/api/portal-agent/orders/check-price` | Publik / Bearer | Cek kalkulasi ongkos kirim real-time multi-kurir |
| `POST` | `/api/portal-agent/orders/create` | Bearer / Public | Pembuatan order kirim, booking kurir, pemotongan deposit, generate resi/AWB |
| `GET` | `/api/portal-agent/orders` | Bearer | Mengambil daftar riwayat pesanan (paginated, filter status & vendor) |
| `GET` | `/api/portal-agent/orders/{id}` | Bearer | Mengambil detail lengkap satu pesanan pengiriman |
| `GET` | `/api/portal-agent/orders/{id}/tracking` | Bearer | Pelacakan status pergerakan paket real-time dari kurir |
| `POST` | `/api/portal-agent/orders/{id}/cancel` | Bearer | Membatalkan pesanan pengiriman ke sistem kurir |

#### Contoh Payload Cek Ongkos Kirim (`POST /api/portal-agent/orders/check-price`):
```json
{
  "origin_district_id": 1420,
  "destination_district_id": 2105,
  "weight": 1200,
  "length": 20,
  "width": 15,
  "height": 10,
  "item_value": 150000,
  "vendors": ["IDEX", "JNTC", "SAPX"]
}
```
**Response Sukses:**
```json
{
  "success": true,
  "rates": [
    {
      "vendor_code": "IDEX",
      "vendor_name": "ID Express",
      "service_code": "STD",
      "service_name": "Standard Regular",
      "tariff": 18000,
      "etd": "1-2 days"
    },
    {
      "vendor_code": "SAPX",
      "vendor_name": "SAP Express",
      "service_code": "UDR",
      "service_name": "Reguler",
      "tariff": 19500,
      "etd": "2-3 days"
    }
  ]
}
```

---

### Kategori C: Keuangan & Verifikasi Top-Up Saldo (MECS / HQ Finance API)

Endpoint ini digunakan oleh modul Finance di HQ App PWA untuk mengawasi dan menyetujui mutasi deposit saldo.

| Method | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| `GET` | `/api/v1/mecs/topups` | API Key / Bearer | Tarik list data deposit masuk (filter `status=pending\|approved\|rejected`, search, pagination) |
| `GET` | `/api/v1/mecs/topups/{id}` | API Key / Bearer | Detail lengkap 1 deposit (bukti transfer URL, detail rekening tujuan & pengirim) |
| `POST` | `/api/v1/mecs/topups/update-status` | API Key / Bearer | Approval atau Rejection deposit (Saldo entitas dikreditkan otomatis secara atomik jika approved) |
| `POST` | `/api/v1/mecs/topups/{id}/status` | API Key / Bearer | Alias endpoint persetujuan dengan ID transaksi di path |

> **Header Token yang Didukung:**
> - `X-API-Key: mz_live_03d67a26b01782e15f45a71f216ed76fe94ac670a021a8914e3ce81b3ac19d9d`
> - Atau `Authorization: Bearer <jwt_or_api_key>`

#### Contoh Approval Top-Up (`POST /api/v1/mecs/topups/update-status`):
```json
{
  "transaction_number": "DEP-20261026-0001",
  "status": "approved",
  "notes": "Dana mutasi BCA Rp 500.000 terverifikasi valid oleh Finance HQ"
}
```
*(Untuk menolak, kirim `"status": "rejected"` dan wajib sertakan `"rejection_reason": "Mutasi bank belum masuk"`)*.

---

### Kategori D: Customer Service & Tiket Bantuan (Ticket API)

| Method | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| `GET` | `/api/v1/tickets/list` | Bearer (`tickets.can-list`) | Mengambil daftar tiket kendala |
| `GET` | `/api/v1/tickets/{id}` | Bearer (`tickets.can-view`) | Mengambil detail tiket |
| `GET` | `/api/v1/tickets/{id}/get-messages` | Bearer (`tickets.can-view`) | Riwayat pesan percakapan tiket |
| `POST` | `/api/v1/tickets` | Bearer (`tickets.can-create`) | Membuka tiket baru |
| `POST` | `/api/v1/tickets/send-message` | Bearer | Mengirim balasan pesan ke dalam tiket |
| `POST` | `/api/v1/tickets/{id}/process` | Bearer (`tickets.can-process`) | Update status tiket (`in_progress`, `resolved`, `closed`) |

---

### Kategori E: Pelacakan Resi Publik (Public Tracking)

Dapat diakses langsung oleh PWA (bahkan tanpa login) untuk fitur pelacakan resi instan di landing page:

| Method | Endpoint | Parameter | Deskripsi |
|---|---|---|---|
| `GET` | `/tracking?awb={nomor_resi}` | Query `awb` | Cek status perjalanan paket publik |
| `GET` | `/tracks.php?awb={nomor_resi}` | Query `awb` | Format alternatif legacy pelacakan resi |

---

### Kategori F: Sinkronisasi Pengiriman Internasional (HQ Order Ext API)

Untuk PWA atau sistem HQ yang mengelola pesanan luar negeri (International Shipping):

| Method | Endpoint | Auth | Deskripsi |
|---|---|---|---|
| `GET` | `/ext/v1/international/orders?status=pending` | Bebas Akses / Office API | Menarik data order internasional pending untuk diproses HQ |
| `POST` | `/ext/v1/international/orders/claim` | Bebas Akses / Office API | Menandai order sudah ditarik oleh HQ (`mecs_claim = 1`) |
| `POST` | `/ext/v1/international/orders/update` | Bebas Akses / Office API | Mengirimkan update nomor resi vendor internasional & status pergerakan paket |

---

## 6. Hierarki Akun Mars & Aturan Sub-Account

Di dalam sistem Marszone, terdapat 4 tingkatan hierarki akun:

```
[ Level 1 ] Mars HQ (Super Admin / Kantor Pusat)
    │
    ▼ (Menyetujui pendaftaran Mars Kas)
[ Level 2 ] Mars Kas (Akun Utama / Seller / Agen Induk)
    │
    ▼ (Menyetujui pendaftaran Mars Point)
[ Level 3 ] Mars Point (Sub-Account 1 tingkat di bawah Mars Kas)
    │
    ▼ (Menyetujui pendaftaran Mars Sub Point)
[ Level 4 ] Mars Sub Point / Mobile (Tingkat Terbawah - Tidak bisa membuat sub account baru)
```

### Aturan Permintaan (Request) Akun & Pelanggan:
1. **Mars Kas:**
   - Dapat mengajukan pembuatan sub-account **Mars Point** (ditujukan ke Mars HQ untuk disetujui).
   - Dapat mendaftarkan akun **Customer** langsung.
2. **Mars Point:**
   - Hanya dapat mengajukan pembuatan sub-account **Mars Sub Point / Mobile** kepada **Mars Kas induknya** (tidak bisa lintas Mars Kas).
   - Dapat mendaftarkan akun **Customer** (dengan persetujuan Mars Kas induk).
3. **Mars Sub Point / Mobile:**
   - Level operasional terbawah (misalnya kurir / drop-off point keliling).
   - **TIDAK DAPAT** membuat sub-account baru lagi.

Saat login di PWA, periksa field `response.data.data.account_tier`. Gunakan nilai ini untuk mengatur visibilitas menu pembuatan sub-account di UI:
- Jika `account_tier === 'sub_point'`, sembunyikan menu *Buat Sub Akun*.
- Jika `account_tier === 'point'`, menu hanya mengizinkan request ke level *Sub Point / Mobile*.
- Jika `account_tier === 'kas'`, menu mengizinkan request ke level *Mars Point*.
- Jika `account_tier === 'hq'`, menu mengizinkan approval ke seluruh level.

---

## 7. Rujukan Kode Status HTTP & Error Format

Semua respon kesalahan dari API Marszone memiliki format standar:
```json
{
  "status": false,
  "message": "Pesan deskripsi kesalahan dalam bahasa yang informatif.",
  "errors": {
    "field_name": "Pesan validasi detail per field jika ada"
  }
}
```

| Kode HTTP | Arti | Tindakan yang Harus Dilakukan PWA |
|---|---|---|
| **`200 OK`** | Berhasil | Render data ke antarmuka pengguna |
| **`201 Created`** | Sumber daya baru berhasil dibuat | Tampilkan notifikasi sukses, refresh list |
| **`400 Bad Request`** | Parameter request tidak lengkap / tidak valid | Tampilkan pesan validasi input ke user |
| **`401 Unauthorized`** | Token hilang, tidak valid, atau telah kedaluwarsa | Hapus token lokal & arahkan ke `/login` |
| **`403 Forbidden`** | Pengguna tidak memiliki permission untuk aksi ini | Tampilkan pesan "Akses Ditolak" |
| **`404 Not Found`** | Data / endpoint tidak ditemukan | Tampilkan halaman/komponen not found |
| **`422 Unprocessable`** | Validasi data bisnis gagal (misal: saldo tidak cukup) | Tampilkan pesan spesifik dari field `message` |
| **`429 Too Many Requests`** | Terkena rate limit pengiriman (misal: spam login/OTP) | Minta pengguna menunggu 1 menit |
| **`500 Server Error`** | Kesalahan internal server | Tampilkan pesan "Terjadi kendala sistem, silakan coba lagi" |

---

## 8. Kontak & Dukungan Teknis
Jika ada kendala integrasi atau memerlukan tambahan field khusus pada endpoint tertentu untuk kebutuhan HQ App PWA, silakan berkoordinasi dengan tim backend Marszone.
