API v1

Dokumentasi API Reseller

Endpoint ini tersedia untuk member Gold dan Diamond. Semua request harus disertai header autentikasi HMAC-SHA256. API Key tersedia di Member Area.

Base URL

https://isiin.id/api/v1

Autentikasi

Setiap request wajib menyertakan tiga header berikut:

X-Api-Key: {api_key}
X-Timestamp: {unix_timestamp_detik}
X-Signature: {hmac_sha256}

Formula Signature

payload = METHOD + "\n" + path + "\n" + timestamp + "\n" + SHA256(request_body)
X-Signature = HMAC-SHA256(payload, api_secret)

Timestamp harus dalam 5 menit dari waktu server. Body kosong = SHA256 dari string kosong.

Contoh (PHP)

$timestamp = time();
$body      = json_encode(['product_code' => 'TLP5000', 'target_number' => '08123456789']);
$bodyHash  = hash('sha256', $body);
$payload   = implode("\n", ['POST', '/api/v1/transaction', $timestamp, $bodyHash]);
$signature = hash_hmac('sha256', $payload, $apiSecret);

HTTP Status Codes

200 OK — pembayaran sync berhasil / status transaksi
202 Accepted — pembelian masuk antrian (async)
400 Bad Request — inquiry gagal (nomor tidak ditemukan, tagihan tidak ada)
401 Unauthorized — header tidak valid / timestamp kedaluwarsa
402 Payment Required — saldo tidak mencukupi
403 Forbidden — level bukan Gold/Diamond
404 Not Found
422 Unprocessable — field wajib tidak ada / tipe produk tidak sesuai / inquiry_id tidak valid atau expired / inquiry_total tidak cocok dengan data inquiry
429 Too Many Requests — melampaui batas 100 request/menit
500 Internal Server Error — gagal terbitkan tiket pesawat (saldo dikembalikan otomatis)
503 Service Unavailable — maintain mode aktif

Callback Notification

Saat transaksi berhasil (status=success), sistem akan mengirimkan HTTP POST ke Callback URL yang Anda daftarkan di Member Area → API Key.

Format Payload (JSON)

{
  "event": "transaction.success",
  "order_id": "TRX20260508143201A3F9C12B",
  "status": "success",
  "product_id": "uuid",
  "target_number": "08123456789",
  "amount": 6500,
  "provider_ref": "SN_OR_REF_ID",
  "processed_at": "2026-05-08 14:32:05"
}
  • Timeout pengiriman: 5 detik. Server Anda harus merespons dengan HTTP 2xx.
  • Kegagalan pengiriman dicatat di log sistem — transaksi tetap sukses.
  • Tidak ada retry otomatis. Gunakan GET /api/v1/transaction/{order_id} untuk memverifikasi status.
  • Callback URL dapat diatur di Member Area → API Key tanpa perlu regenerasi key.

Timestamp: Toleransi ±5 menit dari waktu server. Pastikan waktu sistem Anda sinkron (NTP). Kirim ulang request yang gagal karena 401 dengan timestamp baru.

Umum

GET
/api/v1/balance

Cek Saldo

Kembalikan saldo deposit dan level member.

Response

{
  "level": "gold",
  "balance": 500000,
  "currency": "IDR"
}
GET
/api/v1/products

Daftar Produk

Kembalikan semua produk aktif beserta harga untuk level member Anda. Field `code` digunakan sebagai `product_code` di endpoint transaksi. Filter opsional: `?category={slug}`.

Response

{
  "data": [
    {
      "code": "TLP5000",
      "name": "Telkomsel 5.000",
      "category": "Pulsa",
      "category_slug": "pulsa",
      "price": 6500,
      "is_active": true
    }
  ],
  "total": 1,
  "level": "gold"
}

Transaksi PPOB & Pulsa

POST
/api/v1/inquiry

Inquiry Tagihan (Pembayaran)

Cek data pelanggan sebelum melakukan transaksi pembayaran (PLN, BPJS, air, dll). Hanya tersedia untuk produk dengan tipe `pembayaran`. Tidak memotong saldo. Response menyertakan `inquiry_id` yang berlaku 15 menit — wajib disertakan saat memanggil POST /api/v1/transaction untuk produk pembayaran.

Request Body (JSON)

{
  "product_code": "PLN20",
  "target_number": "530000123456"
}

Response

{
  "inquiry_id": "550e8400-e29b-41d4-a716-446655440000",
  "product_code": "PLN20",
  "target_number": "530000123456",
  "customer_name": "BUDI SANTOSO",
  "bill_amount": 250000,
  "admin_fee": 2500,
  "total_amount": 252500,
  "period": "202504",
  "message": "Berhasil"
}
POST
/api/v1/transaction

Buat Transaksi (Live)

Buat transaksi dan potong saldo langsung. Perilaku berbeda berdasarkan tipe produk: **pembelian** (pulsa, paket data) → tidak memerlukan inquiry, status awal `pending`, diproses async, response `202`; **pembayaran** (PLN, BPJS) → wajib sertakan `inquiry_id` dari hasil POST /api/v1/inquiry (berlaku 15 menit), diproses synchronous, response `200` dengan status final dan SN. Field `inquiry_total` bersifat opsional — jika dikirim harus cocok persis dengan `total_amount` dari inquiry (bill_amount + admin_fee).

Request Body (JSON)

// Pembelian (pulsa, paket data)
{
  "product_code": "TLP5000",
  "target_number": "08123456789"
}

// Pembayaran (PLN, BPJS, dll) — inquiry_id wajib
{
  "product_code": "PLN20",
  "target_number": "530000123456",
  "inquiry_id": "550e8400-e29b-41d4-a716-446655440000",
  "inquiry_total": 252500
}

Response

// Pembelian (async) — HTTP 202
{
  "order_id": "TRX20260508143201A3F9C12B",
  "status": "pending",
  "product_code": "TLP5000",
  "target_number": "08123456789",
  "amount": 6500,
  "message": "Transaction queued. Poll GET /api/v1/transaction/{order_id} for status."
}

// Pembayaran (sync) — HTTP 200
{
  "order_id": "TRX20260508143201A3F9C12B",
  "status": "success",
  "product_code": "PLN20",
  "target_number": "530000123456",
  "amount": 252500,
  "sn": "TOKEN:1234-5678-9012-3456",
  "message": "Transaksi berhasil"
}
GET
/api/v1/transaction/{order_id}

Status Transaksi

Ambil status transaksi live berdasarkan `order_id`. Gunakan untuk polling transaksi pembelian async.

Response

{
  "order_id": "TRX20260508143201A3F9C12B",
  "status": "success",
  "product_code": "TLP5000",
  "target_number": "08123456789",
  "amount": 6500,
  "sn": null,
  "created_at": "2026-05-08 14:32:01"
}
POST
/api/v1/demo/transaction?simulate=success

Demo — Buat Transaksi

Simulasi transaksi tanpa memotong saldo dan tanpa hit provider. Parameter `simulate`: `success`, `pending`, atau `failed`.

Request Body (JSON)

{
  "product_code": "TLP5000",
  "target_number": "08123456789"
}

Response

{
  "order_id": "DEMO20260508143201A3F9",
  "status": "success",
  "product_code": "TLP5000",
  "target_number": "08123456789",
  "amount": 6500,
  "demo": true,
  "message": "Demo mode: status simulated as 'success'. No balance deducted."
}
GET
/api/v1/demo/transaction/{order_id}

Demo — Status Transaksi

Ambil status transaksi demo berdasarkan `order_id` (prefix `DEMO`).

Response

{
  "order_id": "DEMO20260508143201A3F9",
  "status": "success",
  "product_code": "TLP5000",
  "demo": true,
  "created_at": "2026-05-08 14:32:01"
}

Tiket Pesawat

POST
/api/v1/flight/search

Cari Jadwal Penerbangan

Cari jadwal penerbangan berdasarkan rute, tanggal, dan jumlah penumpang. Response berisi array penerbangan berangkat (`depart`) dan pulang (`return`, hanya untuk `roundtrip`). Harga sudah termasuk markup sesuai level member. Gunakan `search_key` dari response saat melakukan booking.

Request Body (JSON)

{
  "origin": "CGK",
  "destination": "SUB",
  "depart_date": "2026-06-10",
  "trip_type": "oneway",
  "pax_adult": 1,
  "pax_child": 0,
  "pax_infant": 0
}

Response

{
  "depart": [
    {
      "airline_code": "GA",
      "airline_name": "Garuda Indonesia",
      "flight_number": "GA-401",
      "depart_time": "06:00",
      "arrive_time": "08:10",
      "duration": "2j 10m",
      "origin": "CGK",
      "destination": "SUB",
      "depart_date": "2026-06-10",
      "cabin_class": "Ekonomi",
      "available": 9,
      "transit": 0,
      "base_price": 850000,
      "final_price": 885000,
      "final_price_per_pax": 885000,
      "search_key": "MOCK-SK-GA401-..."
    }
  ],
  "return": []
}
POST
/api/v1/flight/book

Pesan & Terbitkan Tiket

Buat booking dan langsung terbitkan tiket dalam satu request (synchronous). Saldo dipotong otomatis. Jika penerbitan tiket gagal, saldo dikembalikan. Field `flight` berisi data penerbangan yang dipilih dari `/api/v1/flight/search`, termasuk `search_key`. Field `passengers` adalah array data penumpang sesuai jumlah `pax_adult` + `pax_child` + `pax_infant`. Gunakan `order_id` di response untuk cek status via GET `/api/v1/flight/{order_id}`.

Request Body (JSON)

{
  "flight": {
    "airline_code": "GA",
    "airline_name": "Garuda Indonesia",
    "flight_number": "GA-401",
    "origin": "CGK",
    "destination": "SUB",
    "depart_date": "2026-06-10",
    "depart_time": "06:00",
    "arrive_time": "08:10",
    "cabin_class": "Ekonomi",
    "ticket_price": 850000,
    "sales_price": 850000,
    "search_key": "MOCK-SK-GA401-...",
    "trip_type": "oneway",
    "pax_adult": 1,
    "pax_child": 0,
    "pax_infant": 0
  },
  "passengers": [
    {
      "type": "adult",
      "title": "Tn.",
      "first_name": "Budi",
      "last_name": "Santoso",
      "nationality": "ID",
      "id_number": "3201234567890001"
    }
  ],
  "contact_email": "budi@email.com",
  "contact_phone": "08123456789"
}

Response

// Sukses — HTTP 200
{
  "order_id": "FLT-XXXX260610-260602",
  "status": "issued",
  "booking_code": "DW-MOCK-XXXXXX",
  "booking_code_airline": "MOCKABCD",
  "origin": "CGK",
  "destination": "SUB",
  "depart_date": "2026-06-10",
  "flight_number": "GA-401",
  "amount": 885000,
  "message": "Tiket berhasil diterbitkan"
}

// Saldo kurang — HTTP 402
{
  "error": "Insufficient balance.",
  "balance": 100000,
  "required": 885000
}
GET
/api/v1/flight/{order_id}

Status / Detail Tiket

Ambil detail booking tiket pesawat berdasarkan `order_id` (prefix `FLT-`). Hanya mengembalikan booking milik member yang terautentikasi.

Response

{
  "order_id": "FLT-XXXX260610-260602",
  "status": "issued",
  "booking_code": "DW-MOCK-XXXXXX",
  "booking_code_airline": "MOCKABCD",
  "origin": "CGK",
  "destination": "SUB",
  "trip_type": "oneway",
  "depart_date": "2026-06-10",
  "return_date": null,
  "airline": "GA",
  "flight_number": "GA-401",
  "cabin_class": "Ekonomi",
  "pax_adult": 1,
  "pax_child": 0,
  "pax_infant": 0,
  "amount": 885000,
  "issued_at": "2026-06-02 10:00:00",
  "created_at": "2026-06-02 10:00:00",
  "passengers": [
    {
      "type": "adult",
      "name": "TN. BUDI SANTOSO",
      "id_number": "3201234567890001",
      "seat": null,
      "baggage_kg": 0
    }
  ]
}

Hotel

POST
/api/v1/hotel/search

Cari Hotel

Cari hotel berdasarkan kota tujuan, tanggal check-in/out, jumlah kamar, dan tamu. Harga sudah termasuk markup sesuai level member. Gunakan `search_key` dari response saat memilih kamar.

Request Body (JSON)

{
  "city_code": "DPS",
  "check_in": "2026-07-10",
  "check_out": "2026-07-12",
  "rooms": 1,
  "adult_count": 2,
  "child_count": 0
}

Response

{
  "hotels": [
    {
      "hotel_code": "HTL-DPS-001",
      "hotel_name": "Grand Hyatt Bali",
      "hotel_stars": 5,
      "city_code": "DPS",
      "city_name": "Bali",
      "address": "Jl. Utama No. 1, Bali",
      "facilities": ["Pool", "Gym", "Spa", "Restaurant", "WiFi"],
      "rating": 9.2,
      "available": true,
      "price_per_night": 1800000,
      "display_price_per_night": 1854000,
      "search_key": "MOCK-SK-HTL001-..."
    }
  ]
}
POST
/api/v1/hotel/rooms

Kamar Tersedia

Ambil daftar tipe kamar tersedia untuk hotel dan tanggal yang dipilih. Gunakan `search_key` dari response saat melakukan booking.

Request Body (JSON)

{
  "hotel_code": "HTL-DPS-001",
  "check_in": "2026-07-10",
  "check_out": "2026-07-12",
  "rooms": 1,
  "adult_count": 2,
  "child_count": 0
}

Response

{
  "hotel_code": "HTL-DPS-001",
  "check_in": "2026-07-10",
  "check_out": "2026-07-12",
  "nights": 2,
  "rooms": 1,
  "room_types": [
    {
      "room_type_code": "HTL-DPS-001-DLX",
      "room_type_name": "Deluxe Room",
      "bed_type": "King",
      "max_occupancy": 2,
      "breakfast": true,
      "refundable": true,
      "available": 4,
      "facilities": ["AC", "TV", "WiFi", "Bathtub", "Sarapan"],
      "price_per_night": 2400000,
      "final_price_per_night": 2472000,
      "final_total": 4994000,
      "search_key": "MOCK-ROOM-DLX-..."
    }
  ]
}
POST
/api/v1/hotel/book

Pesan & Terbitkan Voucher Hotel

Buat booking hotel dan langsung terbitkan voucher (synchronous). Saldo dipotong otomatis. Jika penerbitan gagal, saldo dikembalikan. Field `room` berisi data kamar yang dipilih dari `/api/v1/hotel/rooms`, termasuk `search_key`. Field `guests` adalah array data tamu (minimal 1 tamu utama).

Request Body (JSON)

{
  "room": {
    "hotel_code": "HTL-DPS-001",
    "hotel_name": "Grand Hyatt Bali",
    "hotel_stars": 5,
    "city_code": "DPS",
    "city_name": "Bali",
    "room_type_code": "HTL-DPS-001-DLX",
    "room_type_name": "Deluxe Room",
    "check_in": "2026-07-10",
    "check_out": "2026-07-12",
    "nights": 2,
    "rooms": 1,
    "adult_count": 2,
    "child_count": 0,
    "price_per_night": 2400000,
    "search_key": "MOCK-ROOM-DLX-..."
  },
  "guests": [
    {
      "title": "Tn.",
      "first_name": "Budi",
      "last_name": "Santoso",
      "nationality": "ID",
      "id_number": "3201234567890001"
    }
  ],
  "contact_email": "budi@email.com",
  "contact_phone": "08123456789"
}

Response

// Sukses — HTTP 200
{
  "order_id": "HTL-DPS1N2-260710-A3F9",
  "status": "issued",
  "booking_code": "DW-HTL-XXXXXXXX",
  "hotel_name": "Grand Hyatt Bali",
  "check_in": "2026-07-10",
  "check_out": "2026-07-12",
  "amount": 4994000,
  "message": "Voucher hotel berhasil diterbitkan"
}

// Saldo kurang — HTTP 402
{
  "error": "Insufficient balance.",
  "balance": 1000000,
  "required": 4994000
}
GET
/api/v1/hotel/{order_id}

Status / Detail Voucher Hotel

Ambil detail booking hotel berdasarkan `order_id` (prefix `HTL-`). Hanya mengembalikan booking milik member yang terautentikasi.

Response

{
  "order_id": "HTL-DPS1N2-260710-A3F9",
  "status": "issued",
  "booking_code": "DW-HTL-XXXXXXXX",
  "hotel_name": "Grand Hyatt Bali",
  "hotel_stars": 5,
  "city": "Bali",
  "room_type": "Deluxe Room",
  "check_in": "2026-07-10",
  "check_out": "2026-07-12",
  "nights": 2,
  "rooms": 1,
  "guests_adult": 2,
  "guests_child": 0,
  "amount": 4994000,
  "issued_at": "2026-07-08 10:00:00",
  "guests": [
    {
      "name": "TN. BUDI SANTOSO",
      "nationality": "ID",
      "id_number": "3201...",
      "is_primary": true
    }
  ]
}

Tiket Kereta Api

POST
/api/v1/train/search

1. Cari Jadwal Kereta

Cari jadwal kereta berdasarkan stasiun asal, tujuan, tanggal keberangkatan, dan jumlah penumpang. Response berisi array kereta beserta kelas yang tersedia. Harga sudah termasuk markup sesuai level member. Gunakan `search_key` dari response saat melakukan booking.

Request Body (JSON)

{
  "origin_code": "GMR",
  "destination_code": "SGU",
  "depart_date": "2026-07-15",
  "pax_adult": 2,
  "pax_child": 0,
  "pax_infant": 0
}

Response

{
  "trains": [
    {
      "train_number": "KA-001",
      "train_name": "Argo Bromo Anggrek",
      "origin_code": "GMR",
      "origin_name": "Gambir",
      "destination_code": "SGU",
      "destination_name": "Surabaya Gubeng",
      "depart_time": "08:00",
      "arrive_time": "14:30",
      "class_code": "EKS",
      "class_name": "Eksekutif",
      "available": 12,
      "search_key": "MOCK-TRN001-EKS-...",
      "base_price": 475000,
      "markup_percent": 3.0,
      "markup_rupiah": 14250,
      "admin_fee": 15000,
      "price_per_pax": 489250,
      "total_price": 993500
    }
  ],
  "count": 5,
  "origin": "GMR",
  "destination": "SGU",
  "date": "2026-07-15"
}
GET
/api/v1/train/seatmap

2. Peta Kursi (Opsional)

Ambil peta kursi kereta berdasarkan nomor kereta, kode kelas, dan tanggal. Response berisi layout gerbong: jumlah baris, kolom (A/B/C/D atau A/B/C/D/E untuk EKO), dan array kursi yang sudah terpesan. Gunakan nomor kursi dari response ini di field `seat_number` pada request `/book`. **Pilih kursi bersifat opsional** — jika tidak disertakan, sistem akan assign otomatis.

Request Body (JSON)

// Query params:
GET /api/v1/train/seatmap?train_number=KA-001&class_code=EKS&depart_date=2026-07-15

Response

{
  "train_number": "KA-001",
  "class_code": "EKS",
  "depart_date": "2026-07-15",
  "rows": 20,
  "columns": ["A", "B", "C", "D"],
  "occupied": ["1A", "2C", "5B", "7D", "10A"],
  "available": 75
}

// EKO — layout 2-3 (5 kolom):
{
  "rows": 25,
  "columns": ["A", "B", "C", "D", "E"],
  "occupied": [...],
  "available": 110
}
POST
/api/v1/train/book

3. Pesan & Terbitkan Tiket Kereta

Buat booking dan langsung terbitkan tiket kereta dalam satu request. Saldo dipotong otomatis. Jika penerbitan gagal, saldo dikembalikan. Field `train` berisi data kereta dari `/api/v1/train/search`, termasuk `search_key`. Field `passengers` adalah array data penumpang. Field `seat_number` opsional — isi dari response `/seatmap` untuk pilih kursi spesifik. Gunakan `order_id` di response untuk cek status.

Request Body (JSON)

{
  "train": {
    "train_number": "KA-001",
    "train_name": "Argo Bromo Anggrek",
    "origin_code": "GMR",
    "origin_name": "Gambir",
    "destination_code": "SGU",
    "destination_name": "Surabaya Gubeng",
    "depart_date": "2026-07-15",
    "depart_time": "08:00",
    "arrive_time": "14:30",
    "class_code": "EKS",
    "class_name": "Eksekutif",
    "base_price": 475000,
    "pax_adult": 2,
    "pax_child": 0,
    "pax_infant": 0,
    "search_key": "MOCK-TRN001-EKS-..."
  },
  "passengers": [
    {
      "type": "adult",
      "title": "Tn.",
      "first_name": "Budi",
      "last_name": "Santoso",
      "gender": "M",
      "birth_date": "1990-05-15",
      "nationality": "ID",
      "id_number": "320112345678",
      "seat_number": "3A"
    },
    {
      "type": "adult",
      "title": "Ny.",
      "first_name": "Sari",
      "last_name": "Dewi",
      "gender": "F",
      "birth_date": "1992-08-20",
      "nationality": "ID",
      "id_number": "320198765432",
      "seat_number": "3B"
    }
  ],
  "contact_email": "budi@email.com",
  "contact_phone": "081234567890"
}

Response

{
  "order_id": "TRN-GMRSGУ-260715-X7K2",
  "booking_code": "DW-TRN-ABCD1234",
  "status": "issued",
  "train_name": "Argo Bromo Anggrek",
  "depart_date": "2026-07-15",
  "depart_time": "08:00",
  "final_price": 993500
}

// Saldo kurang — HTTP 402
{
  "error": "Saldo tidak mencukupi. Saldo Anda: Rp 500.000"
}
GET
/api/v1/train/{order_id}

4. Status / E-Tiket Kereta

Ambil detail booking kereta berdasarkan `order_id` (prefix `TRN-`). Hanya mengembalikan booking milik member yang terautentikasi.

Response

{
  "order_id": "TRN-GMRSGУ-260715-X7K2",
  "booking_code": "DW-TRN-ABCD1234",
  "booking_code_train": "KAI-XYZ123",
  "train_number": "KA-001",
  "train_name": "Argo Bromo Anggrek",
  "class_code": "EKS",
  "class_name": "Eksekutif",
  "origin_code": "GMR",
  "origin_name": "Gambir",
  "destination_code": "SGU",
  "destination_name": "Surabaya Gubeng",
  "depart_date": "2026-07-15",
  "depart_time": "08:00",
  "arrive_time": "14:30",
  "pax_adult": 2,
  "pax_child": 0,
  "pax_infant": 0,
  "final_price": 993500,
  "status": "issued",
  "issued_at": "2026-07-13 10:00:00",
  "passengers": [
    {
      "type": "adult",
      "title": "Tn.",
      "first_name": "Budi",
      "last_name": "Santoso",
      "nationality": "ID",
      "seat_number": "3A"
    }
  ]
}

Tiket Kapal Laut

POST
/api/v1/ship/search

1. Cari Jadwal Kapal

Cari jadwal kapal Pelni berdasarkan pelabuhan asal, tujuan, tanggal keberangkatan, dan jumlah penumpang. Response berisi array jadwal kapal beserta kelas yang tersedia. Harga sudah termasuk markup sesuai level member. Simpan seluruh object ship dari response untuk digunakan di `/book`.

Request Body (JSON)

{
  "origin_port": "TPRIOK",
  "destination_port": "MKSSAR",
  "depart_date": "2026-07-20",
  "pax_adult": 2,
  "pax_child": 0,
  "pax_infant": 0
}

Response

{
  "ships": [
    {
      "ship_number": "KM-001",
      "ship_name": "KM Umsini",
      "origin_port": "TPRIOK",
      "origin_name": "Tanjung Priok (Jakarta)",
      "destination_port": "MKSSAR",
      "destination_name": "Makassar",
      "class_code": "EKO",
      "class_name": "Ekonomi",
      "sub_class": "Ekonomi",
      "depart_datetime": "2026-07-20 08:00:00",
      "arrival_datetime": "2026-07-23 14:00:00",
      "adult_fare": 350000,
      "child_fare": 280000,
      "infant_fare": 50000,
      "hpp_total": 700000,
      "markup_percent": 3,
      "markup_rupiah": 21000,
      "admin_fee": 50000,
      "total_price": 771000
    }
  ],
  "count": 5,
  "origin_port": "TPRIOK",
  "origin_name": "Tanjung Priok (Jakarta)",
  "destination_port": "MKSSAR",
  "destination_name": "Makassar",
  "depart_date": "2026-07-20"
}
POST
/api/v1/ship/book

2. Pesan & Terbitkan Tiket Kapal

Buat booking dan langsung terbitkan tiket kapal dalam satu request. Sistem otomatis melakukan GetRoom (reservasi kamar), Booking, dan Issue ke Darmawisata. Saldo dipotong otomatis. Field `ship` berisi seluruh object jadwal dari `/search`. Field `passengers` adalah array data penumpang. Response mencakup `order_id` untuk cek status.

Request Body (JSON)

{
  "ship": {
    "ship_number": "KM-001",
    "ship_name": "KM Umsini",
    "origin_port": "TPRIOK",
    "origin_call": 101,
    "origin_name": "Tanjung Priok (Jakarta)",
    "destination_port": "MKSSAR",
    "destination_call": 201,
    "destination_name": "Makassar",
    "class_code": "EKO",
    "class_name": "Ekonomi",
    "sub_class": "Ekonomi",
    "depart_datetime": "2026-07-20 08:00:00",
    "arrival_datetime": "2026-07-23 14:00:00",
    "adult_fare": 350000,
    "child_fare": 280000,
    "infant_fare": 50000,
    "hpp_total": 700000,
    "total_price": 771000,
    "admin_fee": 50000
  },
  "passengers": [
    {
      "type": "adult",
      "title": "Tn.",
      "first_name": "Budi",
      "last_name": "Santoso",
      "birth_date": "1990-05-15",
      "id_number": "3174xxxxxxxx0001",
      "phone": "08123456789",
      "nationality": "ID"
    }
  ],
  "contact_email": "budi@email.com",
  "contact_phone": "08123456789"
}

Response

{
  "order_id": "SHP-TPRIMKSS-260715-A3B2",
  "booking_number": "DW-SHP-ABCD1234",
  "status": "issued",
  "ship_name": "KM Umsini",
  "depart_datetime": "2026-07-20 08:00:00",
  "final_price": 771000
}
GET
/api/v1/ship/{order_id}

3. Status / E-Tiket Kapal

Ambil detail booking kapal berdasarkan `order_id` (prefix `SHP-`). Hanya mengembalikan booking milik member yang terautentikasi. Response mencakup detail penumpang beserta nomor tiket, nomor kamar (deck/cabin/bed), dan QR code tiket individual.

Response

{
  "order_id": "SHP-TPRIMKSS-260715-A3B2",
  "booking_number": "DW-SHP-ABCD1234",
  "ship_number": "KM-001",
  "ship_name": "KM Umsini",
  "class_code": "EKO",
  "class_name": "Ekonomi",
  "origin_port": "TPRIOK",
  "origin_name": "Tanjung Priok (Jakarta)",
  "destination_port": "MKSSAR",
  "destination_name": "Makassar",
  "depart_datetime": "2026-07-20 08:00:00",
  "arrival_datetime": "2026-07-23 14:00:00",
  "pax_adult": 1,
  "pax_child": 0,
  "pax_infant": 0,
  "final_price": 771000,
  "status": "issued",
  "issued_at": "2026-07-15 10:00:00",
  "passengers": [
    {
      "type": "adult",
      "first_name": "Budi",
      "last_name": "Santoso",
      "deck": "Dek 5",
      "cabin": "Kabin 01",
      "bed": "Kasur 1",
      "ticket_number": "TKT-ABCDEF1234",
      "ticket_qr_code": "QR-MOCK-ABCDEF123456789"
    }
  ]
}

Tiket Kapal DLU

POST
/api/v1/ship-dlu/search

1. Cari Jadwal Kapal DLU

Cari jadwal kapal DLU berdasarkan pelabuhan asal, tujuan, dan tanggal. Response berisi array jadwal beserta pilihan fare (kelas/tipe tiket: penumpang atau kendaraan). Harga sudah termasuk markup sesuai level member. Simpan seluruh object fare yang dipilih untuk digunakan di `/book`.

Request Body (JSON)

{
  "origin_port": "TPRIOK",
  "destination_port": "BALI",
  "depart_date": "2026-07-20"
}

Response

{
  "schedules": [
    {
      "ship_number": "DLU-001",
      "ship_name": "KMP Leuser",
      "ship_id": "DLU-S001",
      "depart_datetime": "2026-07-20 07:00:00",
      "arrival_datetime": "2026-07-22 15:00:00",
      "origin_port": "TPRIOK",
      "origin_name": "Tanjung Priok (Jakarta)",
      "destination_port": "BALI",
      "destination_name": "Benoa (Bali)",
      "fares": [
        {
          "data": "EKO|PAX",
          "ticketType": "Penumpang",
          "class": "Ekonomi",
          "hpp_price": 280000,
          "markup_rupiah": 11200,
          "admin_fee": 50000,
          "total_price": 341200
        }
      ]
    }
  ],
  "count": 1
}
POST
/api/v1/ship-dlu/book

2. Pesan & Terbitkan Tiket Kapal DLU

Buat booking dan langsung terbitkan tiket kapal DLU dalam satu request (tidak ada step GetRoom/Booking terpisah). Saldo dipotong otomatis. Field `ship` berisi object jadwal dari `/search` ditambah info fare yang dipilih. Field `passengers` adalah array data penumpang. Field `vehicles` (opsional) untuk kendaraan yang diangkut.

Request Body (JSON)

{
  "ship": {
    "ship_number": "DLU-001",
    "ship_name": "KMP Leuser",
    "ship_id": "DLU-S001",
    "origin_port": "TPRIOK",
    "origin_name": "Tanjung Priok (Jakarta)",
    "destination_port": "BALI",
    "destination_name": "Benoa (Bali)",
    "depart_datetime": "2026-07-20 07:00:00",
    "arrival_datetime": "2026-07-22 15:00:00",
    "ticket_type": "Penumpang",
    "class_type": "Ekonomi",
    "pax_adult": 1,
    "pax_child": 0,
    "pax_infant": 0
  },
  "passengers": [
    {
      "type": "adult",
      "first_name": "Budi",
      "last_name": "Santoso",
      "gender": "M",
      "birth_date": "1990-01-15",
      "id_number": "3271xxxxxxxxxx",
      "nationality": "ID",
      "ticket_type": "Penumpang",
      "class_type": "Ekonomi",
      "fare": 280000
    }
  ],
  "vehicles": []
}

Response

{
  "order_id": "DLU-TPRIBALI-260715-A3B2",
  "booking_number": "DW-DLU-ABCD1234",
  "status": "issued",
  "ship_name": "KMP Leuser",
  "depart_datetime": "2026-07-20 07:00:00",
  "final_price": 341200
}
GET
/api/v1/ship-dlu/{order_id}

3. Status / E-Tiket Kapal DLU

Ambil detail booking kapal DLU berdasarkan `order_id` (prefix `DLU-`). Hanya mengembalikan booking milik member yang terautentikasi. Response mencakup detail penumpang beserta nomor tiket dan QR code, serta daftar kendaraan jika ada.

Response

{
  "order_id": "DLU-TPRIBALI-260715-A3B2",
  "booking_number": "DW-DLU-ABCD1234",
  "ship_number": "DLU-001",
  "ship_name": "KMP Leuser",
  "ticket_type": "Penumpang",
  "class_type": "Ekonomi",
  "origin_port": "TPRIOK",
  "origin_name": "Tanjung Priok (Jakarta)",
  "destination_port": "BALI",
  "destination_name": "Benoa (Bali)",
  "depart_datetime": "2026-07-20 07:00:00",
  "arrival_datetime": "2026-07-22 15:00:00",
  "pax_adult": 1,
  "pax_vehicle": 0,
  "final_price": 341200,
  "status": "issued",
  "issued_at": "2026-07-15 10:00:00",
  "passengers": [
    {
      "type": "adult",
      "first_name": "Budi",
      "last_name": "Santoso",
      "ticket_type": "Penumpang",
      "class_type": "Ekonomi",
      "ticket_number": "DLU-TKT-ABCDEF1234",
      "ticket_qr_code": "DLU-QR-ABCDEF123456789"
    }
  ],
  "vehicles": []
}

Tiket Bus

POST
/api/v1/bus/search

1. Cari Jadwal Bus

Cari jadwal bus berdasarkan terminal asal, tujuan, tanggal, dan jumlah penumpang. Harga yang dikembalikan sudah termasuk markup sesuai level member. Gunakan object schedule (termasuk `directCode`, `locationID`, `depart_id`, `arrival_id`, `selectedClass`) yang dipilih untuk request `/book`.

Request Body (JSON)

{
  "origin_terminal": "Jakarta Pulo Gadung",
  "destination_terminal": "Surabaya Bungurasih",
  "depart_date": "2026-07-20",
  "pax_adult": 1,
  "pax_child": 0,
  "pax_infant": 0,
  "bus": ""
}

Response

{
  "data": {
    "schedules": [
      {
        "directCode": "RI-JKT-SBY-001",
        "locationID": "LOC001",
        "operatorName": "Rosalia Indah",
        "busType": "Big Bus",
        "isAllowChooseSeat": true,
        "depart_time": "2026-07-20 07:00:00",
        "arrival_time": "2026-07-20 16:00:00",
        "depart_id": 1,
        "arrival_id": 2,
        "classes": [
          {
            "subClassFare": "EKS",
            "className": "Eksekutif",
            "adultHpp": 320000,
            "adultFareTotal": 349600
          }
        ]
      }
    ],
    "origin_terminal": "Jakarta Pulo Gadung",
    "destination_terminal": "Surabaya Bungurasih"
  }
}
POST
/api/v1/bus/book

2. Pesan & Terbitkan Tiket Bus

Booking dan terbitkan tiket bus dalam satu request. Sistem memanggil `Bus/Booking` lalu `Bus/Issued` secara atomik. Saldo dipotong otomatis. Field `bus` berisi data jadwal dari `/search`. Field `seats` adalah array nomor kursi (kosong jika bus tidak support pilih kursi). Field `passengers` adalah array data penumpang.

Request Body (JSON)

{
  "bus": {
    "bus_name": "Rosalia Indah",
    "origin_terminal": "Jakarta Pulo Gadung",
    "destination_terminal": "Surabaya Bungurasih",
    "direct_code": "RI-JKT-SBY-001",
    "location_id": "LOC001",
    "sub_class_fare": "EKS",
    "depart_date": "2026-07-20",
    "depart_id": 1,
    "arrival_id": 2,
    "pax_adult": 1,
    "pax_child": 0,
    "pax_infant": 0
  },
  "seats": ["A1"],
  "passengers": [
    {
      "pax_type": "adult",
      "title": "Tn.",
      "first_name": "Budi",
      "last_name": "Santoso",
      "identity": "3271xxxxxxxxxx",
      "identity_type": "KTP",
      "phone": "08123456789",
      "email": "budi@example.com",
      "address": "Jl. Contoh No. 1",
      "birth_date": "1990-01-15",
      "fare": 320000
    }
  ]
}

Response

{
  "data": {
    "order_id": "BUS-JAKASURA-260720-A3B2",
    "booking_code": "BK-ABCD1234EF",
    "reff_number": "REF-XYZ123456789",
    "final_price": 349600,
    "status": "issued"
  }
}
GET
/api/v1/bus/{order_id}

3. Status / E-Tiket Bus

Ambil detail booking bus berdasarkan `order_id` (prefix `BUS-`). Hanya mengembalikan booking milik member yang terautentikasi. Field `reff_number` adalah nomor tiket yang ditunjukkan ke petugas bus.

Response

{
  "data": {
    "order_id": "BUS-JAKASURA-260720-A3B2",
    "booking_code": "BK-ABCD1234EF",
    "reff_number": "REF-XYZ123456789",
    "bus_name": "Rosalia Indah",
    "origin_terminal": "Jakarta Pulo Gadung",
    "destination_terminal": "Surabaya Bungurasih",
    "depart_time": "2026-07-20 07:00:00",
    "seat_numbers": "A1",
    "final_price": 349600,
    "status": "issued",
    "issued_at": "2026-07-15 10:23:45",
    "passengers": [
      {
        "pax_type": "adult",
        "title": "Tn.",
        "first_name": "Budi",
        "last_name": "Santoso",
        "fare": 320000
      }
    ]
  }
}