Base URL:
http://localhost:5000Version: 2.0.0
Author: @farishhz
- Informasi Umum
- Authentication
- Products
- Product Reviews
- Cart (Keranjang Belanja)
- Wishlist
- Orders
- Payments
- User Profile & Addresses
- Shipping
- Categories
- Chat (Real-time)
- Vouchers & Flash Sale
- Notifications
- Banners
- Admin Panel
- Database Schema
- Error Handling
| Komponen | Teknologi |
|---|---|
| Runtime | Node.js |
| Framework | Express.js v5 |
| Database | SQLite |
| ORM | Prisma v6 |
| Authentication | JWT (jsonwebtoken) |
| Password Hash | bcryptjs |
| Payment Gateway | Midtrans (Snap API) |
| File Upload | Multer |
| Real-time Chat | Socket.io |
Semua endpoint mengembalikan response dalam format JSON yang konsisten:
{
"success": true | false,
"message": "Pesan deskriptif",
"data": { ... }
}Untuk endpoint yang mendukung pagination:
{
"success": true,
"message": "...",
"data": [ ... ],
"pagination": {
"total": 50,
"page": 1,
"limit": 10,
"totalPages": 5
}
}Endpoint dibagi menjadi 3 level akses:
| Level | Deskripsi | Header |
|---|---|---|
| 🟢 Public | Tidak perlu login | - |
| 🔒 Protected | Perlu Bearer Token | Authorization: Bearer <access_token> |
| 🔴 Admin | Perlu Bearer Token + role admin |
Authorization: Bearer <access_token> |
Token didapatkan dari response endpoint Login.
GET /health
Response:
{
"status": "OK",
"timestamp": "Senin, 26 Mei 2026 - 12:00:00"
}Base path: /api/auth
⚠️ Semua endpoint Auth bersifat 🟢 Public (tidak memerlukan Bearer Token).
Mendaftarkan user baru ke dalam sistem.
POST /api/auth/register
Content-Type: multipart/form-data atau application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| string | ✅ | Email user (harus unik) | |
| name | string | ✅ | Nama lengkap user |
| password | string | ✅ | Password (min. 6 karakter) |
| passwordConfirm | string | ✅ | Konfirmasi password |
Request Body Example (JSON):
{
"email": "user@example.com",
"name": "John Doe",
"password": "password123",
"passwordConfirm": "password123"
}Success Response (201):
{
"success": true,
"message": "Registrasi berhasil",
"data": {
"id": 1,
"email": "user@example.com",
"name": "John Doe",
"role": "customer",
"is_active": true,
"created_at": "2026-05-26T05:00:00.000Z"
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Password dan konfirmasi password tidak cocok |
| 409 | Email sudah terdaftar |
| 500 | Internal server error |
Login dan mendapatkan Access Token serta Refresh Token.
POST /api/auth/login
Content-Type: multipart/form-data atau application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| string | ✅ | Email user | |
| password | string | ✅ | Password user |
Request Body Example (JSON):
{
"email": "user@example.com",
"password": "password123"
}Success Response (200):
{
"success": true,
"message": "Login berhasil",
"data": {
"id": 1,
"email": "user@example.com",
"name": "John Doe",
"role": "customer",
"created_at": "Senin, 26 Mei 2026 - 12:00:00",
"updated_at": "Senin, 26 Mei 2026 - 12:00:00",
"token": "eyJhbGciOiJIUzI1NiIs...",
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}
}Token Details:
| Token | Masa Berlaku | Kegunaan |
|---|---|---|
| token | 7 hari (configurable) | Access token untuk mengakses semua endpoint protected |
| refresh_token | 30 hari (configurable) | Untuk mendapatkan access token baru tanpa login ulang |
Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Email atau password kosong |
| 401 | Password salah |
| 404 | User tidak ditemukan |
| 500 | Internal server error |
Mendapatkan Access Token baru menggunakan Refresh Token. Digunakan ketika Access Token sudah expired agar user tidak perlu login ulang.
POST /api/auth/refresh-token
Content-Type: multipart/form-data atau application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| refresh_token | string | ✅ | Refresh token dari response login |
Request Body Example (JSON):
{
"refresh_token": "eyJhbGciOiJIUzI1NiIs..."
}Success Response (200):
{
"success": true,
"message": "Token berhasil diperbarui",
"data": {
"token": "eyJhbGciOiJIUzI1NiIs..."
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Refresh token tidak disertakan |
| 401 | Refresh token tidak valid / expired / user tidak aktif |
Memverifikasi apakah Access Token masih valid.
GET /api/auth/verify-token
Headers:
Authorization: Bearer <access_token>
Success Response (200):
{
"success": true,
"message": "Token valid",
"data": {
"id": 1,
"email": "user@example.com",
"role": "customer",
"iat": 1716700800,
"exp": 1717305600
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 401 | Token tidak ditemukan / tidak valid |
Base path: /api/products
🔒 Semua endpoint Product membutuhkan Bearer Token di header
Authorization.
Mengambil daftar semua produk dengan dukungan pagination dan filter.
GET /api/products
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 10 | Jumlah produk per halaman |
| category | string | - | Filter berdasarkan kategori (exact match) |
| seller_id | number | - | Filter berdasarkan ID penjual |
| search | string | - | Pencarian berdasarkan nama produk |
| minPrice | number | - | Filter harga minimum |
| maxPrice | number | - | Filter harga maksimum |
| is_active | boolean | - | Filter berdasarkan status aktif (true/false) |
Contoh Request:
GET /api/products?page=1&limit=5&category=Electronic&minPrice=50000&maxPrice=500000&search=laptop
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data produk",
"data": [
{
"id": 1,
"name": "Laptop ASUS",
"description": "Laptop gaming terbaru",
"price": "Rp 15.000.000",
"stock": 10,
"category": "Electronic",
"image_url": "http://localhost:5000/public/products/image_url-1716700800.png",
"seller_id": 1,
"is_active": true,
"created_at": "Senin, 26 Mei 2026 - 12:00:00",
"updated_at": "Senin, 26 Mei 2026 - 12:00:00",
"seller": {
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
}
],
"pagination": {
"total": 50,
"page": 1,
"limit": 5,
"totalPages": 10
}
}Mengambil detail satu produk berdasarkan ID.
GET /api/products/:id
Path Parameters:
| Parameter | Type | Deskripsi |
|---|---|---|
| id | number | ID produk |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data produk",
"data": {
"id": 1,
"name": "Laptop ASUS",
"description": "Laptop gaming terbaru",
"price": "Rp 15.000.000",
"stock": 10,
"category": "Electronic",
"image_url": "http://localhost:5000/public/products/image_url-1716700800.png",
"seller_id": 1,
"is_active": true,
"created_at": "Senin, 26 Mei 2026 - 12:00:00",
"updated_at": "Senin, 26 Mei 2026 - 12:00:00",
"seller": {
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | ID produk tidak valid |
| 404 | Produk tidak ditemukan |
Membuat produk baru. Field seller_id otomatis diambil dari user yang sedang login.
POST /api/products/create
Content-Type: multipart/form-data
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| name | string | ✅ | Nama produk |
| description | string | ❌ | Deskripsi produk |
| price | number | ✅ | Harga produk (dalam Rupiah) |
| stock | number | ❌ | Jumlah stok (default: 0) |
| category | string | ❌ | Kategori produk (default: uncategorized) |
| image_url | file | ✅ | Gambar produk (max 5MB, format: jpg/png/jpeg) |
Success Response (201):
{
"success": true,
"message": "Produk berhasil dibuat",
"data": {
"id": 1,
"name": "Laptop ASUS",
"description": "Laptop gaming terbaru",
"price": "Rp 15.000.000",
"stock": 10,
"category": "Electronic",
"image_url": "http://localhost:5000/public/products/image_url-1716700800.png",
"seller_id": 1,
"is_active": true,
"created_at": "Senin, 26 Mei 2026 - 12:00:00",
"updated_at": "Senin, 26 Mei 2026 - 12:00:00"
}
}Memperbarui data produk. Hanya pemilik produk (seller) yang bisa mengubah.
PUT /api/products/update/:id
Content-Type: multipart/form-data
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| name | string | ❌ | Nama produk baru |
| description | string | ❌ | Deskripsi produk baru |
| price | number | ❌ | Harga produk baru |
| stock | number | ❌ | Jumlah stok baru |
| category | string | ❌ | Kategori baru |
| image_url | file | ❌ | Gambar produk baru |
| is_active | boolean | ❌ | Status aktif (true / false) |
💡 Hanya field yang dikirim saja yang akan di-update.
Success Response (200):
{
"success": true,
"message": "Produk berhasil diperbarui",
"data": { ... }
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | ID produk tidak valid |
| 403 | Bukan pemilik produk (akses ditolak) |
| 404 | Produk tidak ditemukan |
Menghapus produk secara permanen. Hanya pemilik produk (seller) yang bisa menghapus.
DELETE /api/products/delete/:id
Success Response (200):
{
"success": true,
"message": "Produk berhasil dihapus"
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | ID produk tidak valid |
| 403 | Bukan pemilik produk (akses ditolak) |
| 404 | Produk tidak ditemukan |
Pencarian produk lanjutan dengan filter harga, kategori, dan penjual. Hanya menampilkan produk aktif.
GET /api/products/search
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| q | string | - | Kata kunci pencarian (nama produk) |
| category | string | - | Filter berdasarkan kategori |
| minPrice | number | - | Filter harga minimum |
| maxPrice | number | - | Filter harga maksimum |
| seller_id | number | - | Filter berdasarkan ID penjual |
| page | number | 1 | Nomor halaman |
| limit | number | 10 | Jumlah per halaman |
Contoh Request:
GET /api/products/search?q=laptop&category=Electronic&minPrice=5000000&maxPrice=20000000
Success Response (200):
{
"success": true,
"message": "Berhasil mencari produk",
"data": [
{
"id": 1,
"name": "Laptop ASUS",
"price_formatted": "Rp 15.000.000",
"category": "Electronic",
...
}
],
"pagination": {
"total": 5,
"page": 1,
"limit": 10,
"totalPages": 1
}
}Base path: /api/products/:id/reviews
🔒 Membutuhkan Bearer Token.
Mengambil daftar ulasan untuk produk tertentu, termasuk rata-rata rating.
GET /api/products/:id/reviews
Path Parameters:
| Parameter | Type | Deskripsi |
|---|---|---|
| id | number | ID produk |
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 10 | Jumlah ulasan per halaman |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data ulasan",
"data": [
{
"id": 1,
"user_id": 2,
"product_id": 1,
"rating": 5,
"comment": "Produk bagus sekali, pengiriman cepat!",
"image_url": "http://localhost:5000/public/reviews/review-1716700800.jpg",
"created_at": "2026-05-27 10:30:00",
"updated_at": "2026-05-27 10:30:00",
"user": {
"id": 2,
"name": "Jane Doe",
"avatar_url": null
}
}
],
"summary": {
"average_rating": 4.5,
"total_reviews": 12
},
"pagination": {
"total": 12,
"page": 1,
"limit": 10,
"totalPages": 2
}
}Menambahkan ulasan dan rating untuk produk yang sudah dibeli dan diterima (delivered).
POST /api/products/:id/reviews
Content-Type: multipart/form-data
⚠️ User hanya bisa memberikan 1 ulasan per produk, dan produk harus sudah berstatusdelivered.
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| rating | number | ✅ | Rating 1-5 bintang |
| comment | string | ❌ | Teks ulasan |
| image | file | ❌ | Foto ulasan (max 5MB, format: jpg/png) |
Success Response (201):
{
"success": true,
"message": "Ulasan berhasil ditambahkan",
"data": {
"id": 1,
"user_id": 2,
"product_id": 1,
"rating": 5,
"comment": "Produk bagus sekali!",
"image_url": "http://localhost:5000/public/reviews/review-1716700800.jpg",
"created_at": "2026-05-27 10:30:00",
"updated_at": "2026-05-27 10:30:00",
"user": {
"id": 2,
"name": "Jane Doe",
"avatar_url": null
}
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Rating tidak diisi / rating bukan 1-5 |
| 400 | Sudah pernah memberikan ulasan untuk produk ini |
| 400 | Produk belum diterima (status bukan delivered) |
Base path: /api/cart
🔒 Semua endpoint Cart membutuhkan Bearer Token.
Mengambil seluruh isi keranjang user yang sedang login, termasuk subtotal.
GET /api/cart
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data keranjang",
"data": {
"items": [
{
"id": 1,
"user_id": 1,
"product_id": 3,
"quantity": 2,
"item_total": 30000000,
"item_total_formatted": "Rp 30.000.000",
"product": {
"id": 3,
"name": "Laptop ASUS",
"price": 15000000,
"price_formatted": "Rp 15.000.000",
"stock": 10,
"image_url": "http://localhost:5000/public/products/image.png",
"is_active": true,
"seller": { "id": 1, "name": "Toko Jaya" }
},
"created_at": "2026-05-27 10:30:00",
"updated_at": "2026-05-27 10:30:00"
}
],
"total_items": 1,
"subtotal": 30000000,
"subtotal_formatted": "Rp 30.000.000"
}
}Menambahkan produk ke keranjang. Jika produk sudah ada di keranjang, quantity akan ditambahkan.
POST /api/cart
Content-Type: application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| product_id | number | ✅ | ID produk |
| quantity | number | ❌ | Jumlah (default: 1) |
Request Body Example:
{
"product_id": 3,
"quantity": 2
}Success Response (201):
{
"success": true,
"message": "Produk berhasil ditambahkan ke keranjang",
"data": {
"id": 1,
"user_id": 1,
"product_id": 3,
"quantity": 2,
"product": { "id": 3, "name": "Laptop ASUS", ... }
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Product ID kosong / stok tidak cukup |
| 404 | Produk tidak ditemukan / tidak aktif |
Mengubah jumlah (quantity) produk di keranjang.
PUT /api/cart/:cart_item_id
Path Parameters:
| Parameter | Type | Deskripsi |
|---|---|---|
| cart_item_id | number | ID cart item |
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| quantity | number | ✅ | Jumlah baru (> 0) |
Success Response (200):
{
"success": true,
"message": "Keranjang berhasil diperbarui",
"data": { ... }
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Quantity ≤ 0 / stok tidak mencukupi |
| 403 | Bukan pemilik cart item |
| 404 | Cart item tidak ditemukan |
Menghapus produk dari keranjang.
DELETE /api/cart/:cart_item_id
Success Response (200):
{
"success": true,
"message": "Item berhasil dihapus dari keranjang"
}Error Responses:
| Status | Kondisi |
|---|---|
| 403 | Bukan pemilik cart item |
| 404 | Cart item tidak ditemukan |
Base path: /api/wishlist
🔒 Semua endpoint Wishlist membutuhkan Bearer Token.
Mengambil daftar produk favorit user.
GET /api/wishlist
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 20 | Jumlah per halaman |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data wishlist",
"data": [
{
"id": 1,
"user_id": 1,
"product_id": 5,
"product": {
"id": 5,
"name": "Headphone Sony",
"price": 1500000,
"price_formatted": "Rp 1.500.000",
"image_url": "...",
"is_active": true,
"stock": 25,
"seller": { "id": 2, "name": "Audio Store" }
},
"created_at": "2026-05-27 10:30:00"
}
],
"pagination": { "total": 3, "page": 1, "limit": 20, "totalPages": 1 }
}Menambahkan produk ke wishlist/favorit.
POST /api/wishlist
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| product_id | number | ✅ | ID produk |
Success Response (201):
{
"success": true,
"message": "Produk berhasil ditambahkan ke wishlist",
"data": { ... }
}Error Responses:
| Status | Kondisi |
|---|---|
| 404 | Produk tidak ditemukan |
| 409 | Produk sudah ada di wishlist |
Menghapus produk dari wishlist.
DELETE /api/wishlist/:id
Success Response (200):
{
"success": true,
"message": "Produk berhasil dihapus dari wishlist"
}Base path: /api/orders
🔒 Semua endpoint Order membutuhkan Bearer Token di header
Authorization.
Membuat pesanan baru. Field user_id otomatis diambil dari user yang sedang login.
POST /api/orders
Content-Type: application/json (RAW JSON)
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| items | array | ✅ | Daftar barang yang dipesan |
| items[].product_id | number | ✅ | ID produk |
| items[].quantity | number | ✅ | Jumlah barang |
| items[].price | number | ✅ | Harga satuan barang |
| total_amount | number | ✅ | Total harga keseluruhan pesanan |
| shipping_address | object | ❌ | Alamat pengiriman (format bebas) |
| notes | string | ❌ | Catatan tambahan untuk pesanan |
Request Body Example:
{
"total_amount": 250000,
"shipping_address": {
"nama_penerima": "Budi Santoso",
"nomor_hp": "081234567890",
"jalan": "Jl. Kemerdekaan No. 45",
"kota": "Jakarta Selatan",
"provinsi": "DKI Jakarta",
"kode_pos": "12345"
},
"notes": "Tolong dipacking bubble wrap",
"items": [
{ "product_id": 1, "quantity": 2, "price": 100000 },
{ "product_id": 2, "quantity": 1, "price": 50000 }
]
}Success Response (201):
{
"success": true,
"message": "Pesanan berhasil dibuat",
"data": {
"id": 1,
"user_id": 1,
"status": "pending",
"payment_status": "unpaid",
"total_amount": "Rp 250.000",
"shipping_address": "{...}",
"notes": "Tolong dipacking bubble wrap",
"created_at": "Senin, 26 Mei 2026 - 12:00:00",
"updated_at": "Senin, 26 Mei 2026 - 12:00:00",
"order_items": [
{
"id": 1,
"order_id": 1,
"product_id": 1,
"quantity": 2,
"price": "Rp 100.000",
"created_at": "Senin, 26 Mei 2026 - 12:00:00"
}
]
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Items kosong / total_amount kosong |
| 500 | Internal server error |
Mengambil daftar semua pesanan milik user yang sedang login.
GET /api/orders
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 10 | Jumlah pesanan per halaman |
| status | string | - | Filter status pesanan (pending, processing, shipped, delivered, cancelled) |
| payment_status | string | - | Filter status pembayaran (unpaid, pending, paid, failed, cancelled) |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data pesanan",
"data": [ ... ],
"pagination": { "total": 10, "page": 1, "limit": 5, "totalPages": 2 }
}Mengambil detail satu pesanan berdasarkan ID. User hanya bisa melihat pesanannya sendiri (kecuali admin).
GET /api/orders/:id
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data pesanan",
"data": {
"id": 1,
"user_id": 1,
"status": "pending",
"payment_status": "unpaid",
"total_amount": "Rp 250.000",
"tracking_number": null,
"courier": null,
"discount_amount": 0,
"shipping_address": { ... },
"order_items": [ ... ],
"user": { "id": 1, "name": "John Doe", "email": "john@example.com" }
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | ID pesanan tidak valid |
| 403 | Bukan pemilik pesanan (akses ditolak) |
| 404 | Pesanan tidak ditemukan |
Mengecek dan memvalidasi voucher sebelum checkout. Mengembalikan detail diskon dan total bayar setelah potongan.
POST /api/orders/apply-voucher
Content-Type: application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| code | string | ✅ | Kode voucher |
| total_amount | number | ✅ | Total belanja sebelum diskon |
Request Body Example:
{
"code": "DISKON50",
"total_amount": 500000
}Success Response (200):
{
"success": true,
"message": "Voucher valid",
"data": {
"voucher_id": 1,
"code": "DISKON50",
"type": "percentage",
"discount": 50000,
"discount_formatted": "Rp 50.000",
"original_amount": 500000,
"original_amount_formatted": "Rp 500.000",
"final_amount": 450000,
"final_amount_formatted": "Rp 450.000"
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Kode kosong / total amount tidak valid |
| 400 | Voucher tidak ditemukan / expired / limit tercapai |
| 400 | Minimum pembelian tidak terpenuhi |
Mengajukan komplain/komplain untuk pesanan (unggah bukti foto/video).
POST /api/orders/:id/dispute
Content-Type: multipart/form-data
Path Parameters:
| Parameter | Type | Deskripsi |
|---|---|---|
| id | number | ID pesanan |
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| reason | string | ✅ | Alasan komplain |
| description | string | ❌ | Deskripsi detail masalah |
| evidence | file | ❌ | Bukti foto/video (max 10MB, gambar/video) |
Success Response (201):
{
"success": true,
"message": "Komplain berhasil diajukan",
"data": {
"id": 1,
"order_id": 5,
"user_id": 1,
"reason": "Barang rusak",
"description": "LCD retak saat sampai",
"evidence_url": "http://localhost:5000/public/disputes/evidence-1716700800.jpg",
"status": "open",
"resolution": null,
"created_at": "2026-05-27 10:30:00",
"updated_at": "2026-05-27 10:30:00",
"order": { "id": 5, "status": "delivered", "total_amount": 15000000 },
"user": { "id": 1, "name": "John Doe", "email": "john@example.com" }
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Alasan kosong / pesanan tidak ditemukan |
| 400 | Bukan pemilik pesanan |
| 400 | Sudah ada komplain yang sedang diproses untuk pesanan ini |
Base path: /api/payments
Membuat transaksi pembayaran melalui Midtrans berdasarkan Order ID.
POST /api/payments/create
🔒 Membutuhkan Bearer Token
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| order_id | number | ✅ | ID pesanan yang ingin dibayar |
Success Response (200):
{
"success": true,
"message": "Berhasil mendapatkan token pembayaran",
"data": {
"token": "66e4fa55-fdac-4ef9-91b5-733b97d1b862",
"redirect_url": "https://app.sandbox.midtrans.com/snap/v4/redirection/66e4fa55..."
}
}Cara Penggunaan di Frontend:
<script src="https://app.sandbox.midtrans.com/snap/snap.js"
data-client-key="SB-Mid-client-xxxxx"></script>
<script>
snap.pay('66e4fa55-fdac-4ef9-91b5-733b97d1b862', {
onSuccess: function(result) { console.log('Pembayaran berhasil', result); },
onPending: function(result) { console.log('Menunggu pembayaran', result); },
onError: function(result) { console.log('Pembayaran gagal', result); },
onClose: function() { console.log('Pop-up ditutup tanpa bayar'); }
});
</script>Error Responses:
| Status | Kondisi |
|---|---|
| 400 | Order ID tidak disertakan / status sudah dibayar |
| 403 | Bukan pemilik pesanan |
| 404 | Pesanan tidak ditemukan |
| 500 | Gagal koneksi ke Midtrans / Server Key tidak valid |
Endpoint ini dipanggil secara otomatis oleh server Midtrans setiap kali terjadi perubahan status pembayaran. JANGAN dipanggil manual dari Frontend atau Postman.
POST /api/payments/webhook
⚠️ 🟢 Public endpoint — Tidak memerlukan Bearer Token karena dipanggil langsung oleh server Midtrans.
Mapping Status dari Midtrans:
| Midtrans Status | Payment Status (DB) | Order Payment Status (DB) | Order Status (DB) |
|---|---|---|---|
capture + accept |
completed |
paid |
processing |
settlement |
completed |
paid |
processing |
pending |
pending |
pending |
pending |
cancel / deny / expire |
failed |
failed |
pending |
capture + challenge |
pending |
pending |
pending |
Response ke Midtrans (200):
{
"success": true,
"message": "Webhook processed"
}Base path: /api/users
🔒 Semua endpoint membutuhkan Bearer Token.
Mengambil data detail profil user yang sedang login.
GET /api/users/profile
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data profil",
"data": {
"id": 1,
"email": "user@example.com",
"name": "John Doe",
"role": "customer",
"phone": "081234567890",
"avatar_url": "http://localhost:5000/public/avatars/avatar-1-1716700800.jpg",
"is_active": true,
"created_at": "2026-05-26 12:00:00",
"updated_at": "2026-05-27 10:30:00"
}
}Update profil dan foto avatar.
PUT /api/users/profile
Content-Type: multipart/form-data
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| name | string | ❌ | Nama baru |
| phone | string | ❌ | Nomor HP baru |
| avatar | file | ❌ | Foto profil baru (max 2MB, gambar) |
Success Response (200):
{
"success": true,
"message": "Profil berhasil diperbarui",
"data": {
"id": 1,
"email": "user@example.com",
"name": "John Doe Updated",
"phone": "081234567890",
"avatar_url": "http://localhost:5000/public/avatars/avatar-1-1716700800.jpg",
...
}
}Mengambil daftar alamat yang disimpan user. Diurutkan dengan alamat default di paling atas.
GET /api/users/addresses
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data alamat",
"data": [
{
"id": 1,
"user_id": 1,
"label": "Rumah",
"recipient": "John Doe",
"phone": "081234567890",
"address": "Jl. Kemerdekaan No. 45",
"city": "Jakarta Selatan",
"province": "DKI Jakarta",
"postal_code": "12345",
"is_default": true,
"created_at": "2026-05-26 12:00:00",
"updated_at": "2026-05-26 12:00:00"
}
]
}Menambahkan alamat pengiriman baru. Alamat pertama otomatis menjadi default.
POST /api/users/addresses
Content-Type: application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| label | string | ❌ | Label alamat (default: "Rumah") |
| recipient | string | ✅ | Nama penerima |
| phone | string | ✅ | Nomor HP penerima |
| address | string | ✅ | Alamat lengkap |
| city | string | ✅ | Kota |
| province | string | ✅ | Provinsi |
| postal_code | string | ✅ | Kode pos |
| is_default | boolean | ❌ | Jadikan alamat utama |
Request Body Example:
{
"label": "Kantor",
"recipient": "John Doe",
"phone": "081234567890",
"address": "Jl. Sudirman No. 123, Gedung ABC Lt. 5",
"city": "Jakarta Pusat",
"province": "DKI Jakarta",
"postal_code": "10110",
"is_default": false
}Success Response (201):
{
"success": true,
"message": "Alamat berhasil ditambahkan",
"data": { ... }
}Mengatur satu alamat sebagai alamat utama. Alamat default sebelumnya akan diubah.
PUT /api/users/addresses/:id/set-default
Path Parameters:
| Parameter | Type | Deskripsi |
|---|---|---|
| id | number | ID alamat |
Success Response (200):
{
"success": true,
"message": "Alamat utama berhasil diubah",
"data": { "id": 2, "is_default": true, ... }
}Error Responses:
| Status | Kondisi |
|---|---|
| 403 | Bukan pemilik alamat |
| 404 | Alamat tidak ditemukan |
Base path: /api/shipping
🔒 Membutuhkan Bearer Token.
Menghitung ongkos kirim berdasarkan tujuan, berat, dan pilihan kurir.
💡 Saat ini menggunakan mock calculator. Bisa diganti dengan integrasi RajaOngkir API.
POST /api/shipping/calculate
Content-Type: application/json
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| origin | string | ❌ | Kota asal (default: "Jakarta") |
| destination | string | ✅ | Kota tujuan |
| weight | number | ✅ | Berat paket dalam kg |
| courier | string | ❌ | Kurir spesifik (jne, sicepat, jnt, pos) |
Request Body Example:
{
"origin": "Jakarta",
"destination": "Surabaya",
"weight": 1.5,
"courier": "jne"
}Success Response (200):
{
"success": true,
"message": "Berhasil menghitung ongkos kirim",
"data": {
"origin": "Jakarta",
"destination": "Surabaya",
"weight": 1.5,
"rates": [
{
"courier": "JNE",
"service": "REG",
"description": "JNE Reguler (2-3 hari)",
"cost": 18000,
"cost_formatted": "Rp 18.000",
"etd": "2-3 hari"
},
{
"courier": "JNE",
"service": "YES",
"description": "JNE YES (1 hari)",
"cost": 36000,
"cost_formatted": "Rp 36.000",
"etd": "1 hari"
}
]
}
}Available Couriers:
| Kode | Nama Kurir | Services |
|---|---|---|
jne |
JNE | REG, YES, OKE |
sicepat |
SiCepat | REG, BEST |
jnt |
J&T | EZ, EXPRESS |
pos |
Pos Indonesia | KILAT, EXPRESS |
Base path: /api/categories
Mengambil daftar semua kategori aktif beserta jumlah produk di tiap kategori.
🟢 Public — tidak memerlukan Bearer Token.
GET /api/categories
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data kategori",
"data": [
{
"id": 1,
"name": "Electronic",
"icon_url": "http://localhost:5000/public/categories/category-1716700800.png",
"image_url": "http://localhost:5000/public/categories/category-1716700800.png",
"is_active": true,
"created_at": "2026-05-26 12:00:00",
"updated_at": "2026-05-26 12:00:00",
"_count": { "products": 15 }
}
]
}Base path: /api/chats
🔒 Semua endpoint Chat membutuhkan Bearer Token.
💡 WebSocket (Socket.io) tersedia untuk real-time messaging. Gunakan HTTP endpoint di bawah sebagai fallback.
| Event | Direction | Deskripsi |
|---|---|---|
join_room |
Client → Server | Bergabung ke room: socket.emit('join_room', roomId) |
leave_room |
Client → Server | Keluar dari room |
send_message |
Client → Server | Kirim pesan: { room_id, content, ... } |
new_message |
Server → Client | Pesan baru masuk dari room |
typing |
Client → Server | Mengetik: { room_id, user_id, name } |
user_typing |
Server → Client | User lain sedang mengetik |
stop_typing |
Client → Server | Berhenti mengetik |
user_stop_typing |
Server → Client | User lain berhenti mengetik |
Mengambil daftar riwayat chat/room milik user. Termasuk pesan terakhir dan jumlah belum dibaca.
GET /api/chats
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil daftar chat",
"data": [
{
"id": 1,
"created_at": "2026-05-26 12:00:00",
"updated_at": "2026-05-27 10:30:00",
"participants": [
{
"id": 1,
"user": { "id": 1, "name": "John Doe", "avatar_url": null }
},
{
"id": 2,
"user": { "id": 3, "name": "Toko Jaya", "avatar_url": "..." }
}
],
"last_message": {
"id": 15,
"content": "Barangnya ready gan?",
"created_at": "2026-05-27 10:30:00",
"sender_id": 1,
"is_read": false
},
"unread_count": 2
}
]
}Membuat atau menemukan chat room yang sudah ada antara 2 user. Digunakan untuk memulai percakapan baru (misal klik "Chat Penjual" di halaman produk).
POST /api/chats/start
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| target_user_id | number | ✅ | ID user yang ingin di-chat |
Request Body Example:
{
"target_user_id": 3
}Success Response (200):
{
"success": true,
"message": "Chat room berhasil dibuat/ditemukan",
"data": {
"id": 1,
"participants": [
{ "user": { "id": 1, "name": "John Doe" } },
{ "user": { "id": 3, "name": "Toko Jaya" } }
]
}
}Mengambil riwayat pesan dalam satu obrolan. Otomatis menandai pesan dari lawan bicara sebagai sudah dibaca.
GET /api/chats/:room_id/messages
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 50 | Jumlah pesan per halaman |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil riwayat pesan",
"data": [
{
"id": 1,
"room_id": 1,
"sender_id": 1,
"content": "Barangnya ready gan?",
"image_url": null,
"is_read": true,
"created_at": "2026-05-27 10:30:00",
"is_mine": true,
"sender": { "id": 1, "name": "John Doe", "avatar_url": null }
},
{
"id": 2,
"room_id": 1,
"sender_id": 3,
"content": "Ready kak, silakan order ya",
"image_url": null,
"is_read": true,
"created_at": "2026-05-27 10:31:00",
"is_mine": false,
"sender": { "id": 3, "name": "Toko Jaya", "avatar_url": "..." }
}
],
"pagination": { "total": 2, "page": 1, "limit": 50, "totalPages": 1 }
}Error Responses:
| Status | Kondisi |
|---|---|
| 403 | Bukan anggota chat room ini |
Mengirim pesan (teks atau lampiran gambar) dalam chat room.
POST /api/chats/:room_id/messages
Content-Type: multipart/form-data
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| content | string | ❌* | Isi pesan teks |
| image | file | ❌* | Lampiran gambar (max 5MB) |
* Minimal satu dari
contentatauimageharus diisi.
Success Response (201):
{
"success": true,
"message": "Pesan berhasil dikirim",
"data": {
"id": 3,
"room_id": 1,
"sender_id": 1,
"content": "Terima kasih!",
"image_url": null,
"is_mine": true,
"created_at": "2026-05-27 10:35:00",
"sender": { "id": 1, "name": "John Doe", "avatar_url": null }
}
}Mengambil daftar voucher/diskon yang sedang aktif dan belum kadaluarsa.
🔒 Membutuhkan Bearer Token.
GET /api/vouchers
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil daftar voucher",
"data": [
{
"id": 1,
"code": "DISKON50",
"type": "percentage",
"value": 50,
"value_formatted": "50%",
"min_purchase": 100000,
"min_purchase_formatted": "Rp 100.000",
"max_discount": 50000,
"max_discount_formatted": "Rp 50.000",
"usage_limit": 100,
"used_count": 23,
"is_active": true,
"expires_at": "2026-06-30 23:59:59",
"created_at": "2026-05-01 00:00:00",
"updated_at": "2026-05-27 10:30:00"
},
{
"id": 2,
"code": "FREEONGKIR",
"type": "free_shipping",
"value": 15000,
"value_formatted": "Rp 15.000",
"min_purchase": 50000,
"min_purchase_formatted": "Rp 50.000",
"max_discount": null,
"max_discount_formatted": null,
...
}
]
}Voucher Types:
| Type | Deskripsi |
|---|---|
percentage |
Diskon persentase (misal 10% dari total, max discount) |
fixed |
Diskon nominal tetap (misal Rp 25.000) |
free_shipping |
Gratis ongkir (nominal ongkir di-cover) |
Mengambil daftar produk yang sedang dalam flash sale (diskon terbatas waktu).
🔒 Membutuhkan Bearer Token.
GET /api/flash-sales
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data flash sale",
"data": [
{
"id": 1,
"product_id": 5,
"discount_price": 7500000,
"discount_price_formatted": "Rp 7.500.000",
"stock": 50,
"sold_count": 32,
"remaining_stock": 18,
"discount_percentage": 50,
"starts_at": "2026-05-28 00:00:00",
"ends_at": "2026-05-28 23:59:59",
"is_active": true,
"original_price_formatted": "Rp 15.000.000",
"product": {
"id": 5,
"name": "Laptop ASUS",
"price": 15000000,
"image_url": "...",
"seller": { "id": 1, "name": "Toko Jaya" }
}
}
]
}Base path: /api/notifications
🔒 Semua endpoint Notifikasi membutuhkan Bearer Token.
Notifikasi dibuat otomatis oleh sistem saat:
- Status pesanan berubah (dikirim, sampai, dibatalkan)
- Nomor resi ditambahkan
- Komplain diselesaikan
Mengambil daftar notifikasi milik user, termasuk jumlah yang belum dibaca.
GET /api/notifications
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 20 | Jumlah per halaman |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil notifikasi",
"data": [
{
"id": 1,
"user_id": 1,
"title": "Pesanan Dikirim",
"message": "Pesanan #5 sudah dikirim via JNE dengan nomor resi: JNE123456",
"type": "order",
"is_read": false,
"metadata": { "order_id": 5, "tracking_number": "JNE123456", "courier": "JNE" },
"created_at": "2026-05-27 10:30:00"
}
],
"unread_count": 3,
"pagination": { "total": 15, "page": 1, "limit": 20, "totalPages": 1 }
}Notification Types:
| Type | Deskripsi |
|---|---|
info |
Informasi umum |
order |
Update status pesanan |
dispute |
Update komplain |
Menandai semua notifikasi sebagai sudah dibaca (tanda lonceng merah hilang).
PUT /api/notifications/read-all
Success Response (200):
{
"success": true,
"message": "Semua notifikasi berhasil ditandai sudah dibaca",
"data": { "updated": 3 }
}Base path: /api/banners
Mengambil daftar gambar banner promosi untuk ditampilkan di halaman utama (carousel/slider).
🟢 Public — tidak memerlukan Bearer Token.
GET /api/banners
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data banner",
"data": [
{
"id": 1,
"title": "Promo Lebaran 2026",
"image_url": "http://localhost:5000/public/banners/banner-1716700800.jpg",
"link_url": "/promo/lebaran",
"is_active": true,
"sort_order": 1,
"created_at": "2026-05-26 12:00:00",
"updated_at": "2026-05-26 12:00:00"
}
]
}Base path: /api/admin
🔴 Semua endpoint Admin membutuhkan Bearer Token + role
admin.User dengan role selain
adminakan mendapat response403 Forbidden.
Mengambil semua pesanan dari semua user. Bisa difilter berdasarkan status.
GET /api/admin/orders
Query Parameters:
| Parameter | Type | Default | Deskripsi |
|---|---|---|---|
| page | number | 1 | Nomor halaman |
| limit | number | 10 | Jumlah pesanan per halaman |
| status | string | - | Filter: pending, processing, shipped, delivered, cancelled |
| payment_status | string | - | Filter: unpaid, pending, paid, failed, cancelled |
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil semua pesanan",
"data": [
{
"id": 1,
"user_id": 2,
"status": "processing",
"payment_status": "paid",
"total_amount": 250000,
"total_amount_formatted": "Rp 250.000",
"tracking_number": null,
"courier": null,
"user": { "id": 2, "name": "Budi", "email": "budi@example.com" },
...
}
],
"pagination": { ... }
}Mengubah status pesanan (misal: pending → processing → shipped → delivered).
📬 Otomatis membuat notifikasi ke pembeli.
PUT /api/admin/orders/:id/status
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| status | string | ✅ | Status baru: pending, processing, shipped, delivered, cancelled |
Request Body Example:
{
"status": "shipped"
}Success Response (200):
{
"success": true,
"message": "Status pesanan berhasil diubah menjadi 'shipped'",
"data": { "id": 1, "status": "shipped", ... }
}Menambahkan nomor resi dan nama kurir ke pesanan. Otomatis mengubah status menjadi shipped.
📬 Otomatis membuat notifikasi ke pembeli dengan info nomor resi.
PUT /api/admin/orders/:id/tracking
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| tracking_number | string | ✅ | Nomor resi |
| courier | string | ❌ | Nama kurir |
Request Body Example:
{
"tracking_number": "JNE123456789",
"courier": "JNE"
}Success Response (200):
{
"success": true,
"message": "Nomor resi berhasil ditambahkan",
"data": {
"id": 1,
"status": "shipped",
"tracking_number": "JNE123456789",
"courier": "JNE",
...
}
}Mengembalikan rekap data statistik untuk dashboard admin: Total Pendapatan, Pesanan, Produk Aktif, Produk Terlaris, dll.
GET /api/admin/dashboard/stats
Success Response (200):
{
"success": true,
"message": "Berhasil mengambil data dashboard",
"data": {
"total_revenue": 150000000,
"total_revenue_formatted": "Rp 150.000.000",
"monthly_revenue": 25000000,
"monthly_revenue_formatted": "Rp 25.000.000",
"orders_this_month": 42,
"active_products": 128,
"total_users": 350,
"orders_by_status": {
"pending": 5,
"processing": 10,
"shipped": 8,
"delivered": 120,
"cancelled": 3
},
"top_selling_products": [
{
"id": 3,
"name": "Laptop ASUS",
"image_url": "...",
"total_quantity_sold": 45
}
]
}
}Admin menengahi dan menyelesaikan komplain dari pembeli. Bisa menerima (refund) atau menolak.
📬 Otomatis membuat notifikasi ke pembeli tentang hasil resolusi.
PUT /api/admin/disputes/:id/resolve
Request Body:
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| resolution | string | ✅ | Penjelasan resolusi |
| status | string | ❌ | resolved (default) atau rejected |
Request Body Example:
{
"resolution": "Refund penuh telah diproses ke rekening pembeli",
"status": "resolved"
}Success Response (200):
{
"success": true,
"message": "Komplain berhasil diselesaikan",
"data": {
"id": 1,
"order_id": 5,
"status": "resolved",
"resolution": "Refund penuh telah diproses ke rekening pembeli",
"resolved_at": "2026-05-28T10:00:00.000Z",
...
}
}Admin menambahkan banner promo baru untuk ditampilkan di halaman utama.
POST /api/admin/banners
Content-Type: multipart/form-data
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| title | string | ✅ | Judul banner |
| image | file | ✅ | Gambar banner (max 5MB, format gambar) |
| link_url | string | ❌ | URL tujuan saat banner diklik |
| sort_order | number | ❌ | Urutan tampil (default: 0) |
Success Response (201):
{
"success": true,
"message": "Banner berhasil ditambahkan",
"data": {
"id": 1,
"title": "Promo Lebaran 2026",
"image_url": "http://localhost:5000/public/banners/banner-1716700800.jpg",
"link_url": "/promo/lebaran",
"sort_order": 1,
...
}
}Admin menambahkan kategori produk baru.
POST /api/admin/categories
Content-Type: multipart/form-data
Request Body (form-data):
| Field | Type | Required | Deskripsi |
|---|---|---|---|
| name | string | ✅ | Nama kategori (harus unik) |
| icon | file | ❌ | Icon/gambar kategori (max 2MB, gambar) |
Success Response (201):
{
"success": true,
"message": "Kategori berhasil ditambahkan",
"data": {
"id": 1,
"name": "Electronic",
"icon_url": "http://localhost:5000/public/categories/category-1716700800.png",
"image_url": "http://localhost:5000/public/categories/category-1716700800.png",
"is_active": true,
...
}
}Error Responses:
| Status | Kondisi |
|---|---|
| 409 | Kategori dengan nama tersebut sudah ada |
erDiagram
User ||--o{ Product : "sells"
User ||--o{ Order : "places"
User ||--o{ CartItem : "has"
User ||--o{ Wishlist : "favorites"
User ||--o{ Address : "owns"
User ||--o{ Review : "writes"
User ||--o{ Notification : "receives"
User ||--o{ Dispute : "files"
User ||--o{ ChatParticipant : "joins"
User ||--o{ ChatMessage : "sends"
Order ||--o{ OrderItem : "contains"
Order ||--o{ Dispute : "has"
Order ||--o| Payment : "has"
Product ||--o{ OrderItem : "included_in"
Product ||--o{ CartItem : "in_cart"
Product ||--o{ Wishlist : "favorited"
Product ||--o{ Review : "reviewed"
Product ||--o{ FlashSale : "on_sale"
Category ||--o{ Product : "categorizes"
ChatRoom ||--o{ ChatParticipant : "has"
ChatRoom ||--o{ ChatMessage : "contains"
User {
int id PK
string email UK
string name
string password_hash
string role
string phone
string avatar_url
boolean is_active
datetime created_at
datetime updated_at
}
Product {
int id PK
string name
string description
float price
int stock
string category
int category_id FK
string image_url
int seller_id FK
boolean is_active
datetime created_at
datetime updated_at
}
Order {
int id PK
int user_id FK
string status
string payment_status
float total_amount
string shipping_address
string notes
string tracking_number
string courier
int voucher_id
float discount_amount
datetime created_at
datetime updated_at
}
OrderItem {
int id PK
int order_id FK
int product_id FK
int quantity
float price
datetime created_at
}
Payment {
int id PK
int order_id FK_UK
float amount
string payment_method
string transaction_id UK
string status
string metadata
datetime created_at
datetime updated_at
}
CartItem {
int id PK
int user_id FK
int product_id FK
int quantity
datetime created_at
datetime updated_at
}
Wishlist {
int id PK
int user_id FK
int product_id FK
datetime created_at
}
Address {
int id PK
int user_id FK
string label
string recipient
string phone
string address
string city
string province
string postal_code
boolean is_default
}
Review {
int id PK
int user_id FK
int product_id FK
int rating
string comment
string image_url
}
Category {
int id PK
string name UK
string icon_url
string image_url
boolean is_active
}
ChatRoom {
int id PK
datetime created_at
datetime updated_at
}
ChatParticipant {
int id PK
int room_id FK
int user_id FK
}
ChatMessage {
int id PK
int room_id FK
int sender_id FK
string content
string image_url
boolean is_read
}
Voucher {
int id PK
string code UK
string type
float value
float min_purchase
float max_discount
int usage_limit
int used_count
boolean is_active
datetime expires_at
}
FlashSale {
int id PK
int product_id FK
float discount_price
int stock
int sold_count
datetime starts_at
datetime ends_at
boolean is_active
}
Notification {
int id PK
int user_id FK
string title
string message
string type
boolean is_read
string metadata
}
Dispute {
int id PK
int order_id FK
int user_id FK
string reason
string description
string evidence_url
string status
string resolution
datetime resolved_at
}
Banner {
int id PK
string title
string image_url
string link_url
boolean is_active
int sort_order
}
Order Status:
| Status | Deskripsi |
|---|---|
pending |
Pesanan baru dibuat, belum diproses |
processing |
Pesanan sedang diproses setelah bayar |
shipped |
Pesanan sedang dikirim |
delivered |
Pesanan sudah sampai ke pembeli |
cancelled |
Pesanan dibatalkan |
Order Payment Status:
| Status | Deskripsi |
|---|---|
unpaid |
Belum melakukan pembayaran |
pending |
Menunggu konfirmasi pembayaran |
paid |
Pembayaran berhasil/lunas |
failed |
Pembayaran gagal |
cancelled |
Pembayaran dibatalkan |
Payment Status (tabel payments):
| Status | Deskripsi |
|---|---|
pending |
Menunggu pembayaran |
completed |
Pembayaran berhasil |
failed |
Pembayaran gagal |
cancelled |
Pembayaran dibatalkan |
expired |
Pembayaran kadaluarsa |
Dispute Status:
| Status | Deskripsi |
|---|---|
open |
Komplain baru diajukan |
in_review |
Sedang ditinjau admin |
resolved |
Komplain diselesaikan (refund/dll) |
rejected |
Komplain ditolak |
Semua error response mengikuti format konsisten:
{
"success": false,
"message": "Deskripsi error"
}| Code | Deskripsi |
|---|---|
| 200 | Berhasil (OK) |
| 201 | Berhasil membuat data baru (Created) |
| 400 | Request tidak valid / data kurang (Bad Request) |
| 401 | Token tidak ada / expired / tidak valid (Unauthorized) |
| 403 | Tidak memiliki akses / bukan admin (Forbidden) |
| 404 | Data tidak ditemukan (Not Found) |
| 409 | Data duplikat / konflik (Conflict) |
| 500 | Kesalahan server internal (Internal Server Error) |
| Kondisi | Status | Message |
|---|---|---|
| Token tidak dikirim | 401 | Akses ditolak. Token tidak ditemukan |
| Token expired | 401 | Token sudah kadaluarsa. Silakan login kembali |
| Token tidak valid | 401 | Token tidak valid |
| Akun dinonaktifkan | 403 | Akun telah dinonaktifkan |
| Bukan admin | 403 | Akses ditolak. Hanya admin yang bisa mengakses endpoint ini |
| Variable | Deskripsi | Contoh |
|---|---|---|
PORT |
Port server | 5000 |
BASE_URL |
Base URL server | http://localhost:5000 |
DATABASE_URL |
Koneksi database Prisma (SQLite) | file:./dev.db?connection_limit=1 |
JWT_SECRET |
Secret key untuk access token JWT | your_secret_key |
JWT_EXPIRATION |
Masa berlaku access token | 7d |
JWT_REFRESH_SECRET |
Secret key untuk refresh token | your_refresh_secret |
JWT_REFRESH_EXPIRATION |
Masa berlaku refresh token | 30d |
MIDTRANS_SERVER_KEY |
Server Key Midtrans | SB-Mid-server-xxxxx |
MIDTRANS_CLIENT_KEY |
Client Key Midtrans | SB-Mid-client-xxxxx |
MIDTRANS_IS_PRODUCTION |
Mode Midtrans (true = production) |
false |
| Method | Endpoint | Deskripsi |
|---|---|---|
| POST | /api/auth/register |
Register user baru |
| POST | /api/auth/login |
Login |
| POST | /api/auth/refresh-token |
Refresh access token |
| GET | /api/auth/verify-token |
Verify token |
| GET | /api/categories |
Daftar kategori |
| GET | /api/banners |
Daftar banner promo |
| POST | /api/payments/webhook |
Midtrans webhook |
| Method | Endpoint | Deskripsi |
|---|---|---|
| GET | /api/products |
Daftar produk |
| GET | /api/products/:id |
Detail produk |
| GET | /api/products/search |
Cari produk |
| POST | /api/products/create |
Buat produk |
| PUT | /api/products/update/:id |
Update produk |
| DELETE | /api/products/delete/:id |
Hapus produk |
| GET | /api/products/:id/reviews |
Ulasan produk |
| POST | /api/products/:id/reviews |
Tambah ulasan |
| GET | /api/cart |
Lihat keranjang |
| POST | /api/cart |
Tambah ke keranjang |
| PUT | /api/cart/:cart_item_id |
Update quantity |
| DELETE | /api/cart/:cart_item_id |
Hapus dari keranjang |
| GET | /api/wishlist |
Lihat wishlist |
| POST | /api/wishlist |
Tambah ke wishlist |
| DELETE | /api/wishlist/:id |
Hapus dari wishlist |
| POST | /api/orders |
Buat pesanan |
| GET | /api/orders |
Daftar pesanan |
| GET | /api/orders/:id |
Detail pesanan |
| POST | /api/orders/apply-voucher |
Validasi voucher |
| POST | /api/orders/:id/dispute |
Ajukan komplain |
| POST | /api/payments/create |
Buat pembayaran |
| GET | /api/users/profile |
Lihat profil |
| PUT | /api/users/profile |
Update profil |
| GET | /api/users/addresses |
Daftar alamat |
| POST | /api/users/addresses |
Tambah alamat |
| PUT | /api/users/addresses/:id/set-default |
Set alamat utama |
| POST | /api/shipping/calculate |
Hitung ongkir |
| GET | /api/chats |
Daftar chat room |
| POST | /api/chats/start |
Mulai chat |
| GET | /api/chats/:room_id/messages |
Riwayat pesan |
| POST | /api/chats/:room_id/messages |
Kirim pesan |
| GET | /api/vouchers |
Daftar voucher |
| GET | /api/flash-sales |
Daftar flash sale |
| GET | /api/notifications |
Daftar notifikasi |
| PUT | /api/notifications/read-all |
Tandai semua dibaca |
| Method | Endpoint | Deskripsi |
|---|---|---|
| GET | /api/admin/orders |
Semua pesanan |
| PUT | /api/admin/orders/:id/status |
Ubah status pesanan |
| PUT | /api/admin/orders/:id/tracking |
Tambah nomor resi |
| GET | /api/admin/dashboard/stats |
Dashboard analytics |
| PUT | /api/admin/disputes/:id/resolve |
Selesaikan komplain |
| POST | /api/admin/banners |
Tambah banner |
| POST | /api/admin/categories |
Tambah kategori |