Kembali ke panduan Bot Telegram

Lanjutan — coding bot jualan

Bikin bot Telegram jualan produk (pakai menu tree)

Di sini kamu belajar membuat bot toko dari nol: pelanggan menekan tombol untuk pindah dari menu utama → kategori → produk → keranjang → checkout. Rahasianya cuma satu konsep: pohon menu (tree). Kalau paham ini, kamu bisa custom bot jadi apa saja. Ikuti dan cocokkan dengan gambar di tiap langkah.

Konsep inti

Menu = pohon: akar, cabang, daun.

Tombol (inline)

Pelanggan klik, bukan ketik perintah.

Hasil akhir

Katalog, keranjang, sampai checkout.

1
Konsep dulu

Apa itu "tree" (pohon) dalam bot?

Bayangkan menu bot seperti pohon. Pelanggan mulai dari akar (menu utama), lalu menekan tombol untuk turun ke cabang (kategori), lalu ke daun (produk tertentu). Tiap tombol memindahkan pelanggan ke cabang yang lebih dalam — dan selalu ada tombol ⬅️ Kembali untuk naik lagi.

Peta pohon menu
🏠 Menu Utama  ← akar (/start)
├─ 🛒 Katalog
│  ├─ 🥤 Minuman        ← cabang (kategori)
│  │  ├─ Kopi Susu      ← daun (produk)
│  │  └─ Teh Lemon
│  └─ 🍔 Makanan
│     ├─ Burger Sapi
│     └─ Kentang Goreng
├─ 🧺 Keranjang Saya
│  └─ 💳 Checkout
└─ ℹ️ Bantuan
Inti dari coding bot jualan hanyalah: setiap tombol membawa pelanggan ke satu node lain di pohon ini. Sisanya cuma mengisi teks dan data.
2

Gambar dulu peta menunya

Sebelum menulis kode, tulis dulu pohon menu kamu di kertas atau notes. Ini langkah paling penting yang sering dilewati pemula. Untuk tiap tombol, tentukan: tulisannya apa dan membawa ke mana. Kita beri tiap aksi sebuah kode bernama callback_data, formatnya aksi:nilai — contoh kategori:minuman.

Tombolcallback_dataMembawa ke
🛒 Lihat Katalogmenu:katalogDaftar kategori
🥤 Minumankategori:minumanProduk di kategori minuman
Kopi Susuproduk:kopiDetail produk Kopi Susu
➕ Tambah ke keranjangtambah:kopiMasukkan ke keranjang
💳 Checkoutcheckout:nowSelesaikan pesanan
Pola aksi:nilaiini kunci yang membuat bot rapi. Satu fungsi "otak" nanti membaca kode ini dan memutuskan harus pindah ke cabang mana.
3

Siapkan project & token bot

Pertama, buat bot di @BotFather dan ambil tokennya (caranya sama persis seperti di panduan Bot Telegram, langkah 1). Lalu buat project dan pasang satu library yang memudahkan kita:

# 1. buat folder project
mkdir bot-toko && cd bot-toko
 
# 2. siapkan package.json
npm init -y
 
# 3. pasang satu-satunya library yang kita butuh
npm install node-telegram-bot-api

Simpan token di file .env supaya tidak tercampur ke dalam kode:

GNU nano 7.2bot-toko/.env
1TELEGRAM_TOKEN=123456789:AAE-ContohTokenKamu_RAHASIA
^O Write Out^X Exit^W Where Is^K Cut
Jangan pernah menulis token langsung di bot.js dan jangan upload .env ke GitHub. Tambahkan .env ke file .gitignore.
4

Buat kerangka bot + tombol pertama

Buat file bot.js. Inilah pondasi bot — menyambung ke Telegram dan menyiapkan tempat menyimpan keranjang:

// bot.js — kerangka utama bot
const TelegramBot = require('node-telegram-bot-api')
const { katalog } = require('./data/produk')
 
const token = process.env.TELEGRAM_TOKEN
const bot = new TelegramBot(token, { polling: true })
 
// "keranjang" sederhana di memori: { chatId: [ {id, nama, harga} ] }
const keranjang = {}
 
// helper kirim/ubah pesan
const rupiah = (n) => 'Rp' + n.toLocaleString('id-ID')
 
console.log('Bot toko berjalan...')

Library node-telegram-bot-api menangani hal rumit untuk kita. Kita cukup berpikir soal pesan dan tombol.

5

Siapkan data produk (katalog)

Pisahkan data dari kode. Buat file data/produk.js berisi kategori dan produk. Nanti kalau mau tambah produk, kamu cukup edit file ini — tidak perlu mengubah logika bot.

// data/produk.js — anggap ini "gudang" data toko kamu
// Nanti bisa diganti dengan database, tapi mulai sederhana dulu.
 
const katalog = {
  minuman: {
    label: '🥤 Minuman',
    produk: [
      { id: 'kopi', nama: 'Kopi Susu', harga: 18000, stok: 12 },
      { id: 'teh', nama: 'Teh Lemon', harga: 12000, stok: 30 },
    ],
  },
  makanan: {
    label: '🍔 Makanan',
    produk: [
      { id: 'burger', nama: 'Burger Sapi', harga: 25000, stok: 8 },
      { id: 'kentang', nama: 'Kentang Goreng', harga: 15000, stok: 20 },
    ],
  },
}
 
module.exports = { katalog }
Saat tokomu besar, isi file ini bisa diganti dengan database (mis. Neon/Supabase). Tapi untuk belajar, objek sederhana seperti ini sudah cukup dan mudah dipahami.
6

Menu utama — akar pohon

Saat pelanggan mengetik /start, tampilkan menu utama dengan tombol. Tombol di Telegram disebut inline keyboard: tiap tombol punya text (yang dilihat pelanggan) dan callback_data (kode tersembunyi yang kita baca nanti).

// ===== AKAR POHON: /start menampilkan menu utama =====
function menuUtama(chatId) {
  bot.sendMessage(chatId, 'Selamat datang di *Toko Saya* 🛍️\nMau lihat apa?', {
    parse_mode: 'Markdown',
    reply_markup: {
      inline_keyboard: [
        [{ text: '🛒 Lihat Katalog', callback_data: 'menu:katalog' }],
        [{ text: '🧺 Keranjang Saya', callback_data: 'menu:keranjang' }],
        [{ text: 'ℹ️ Bantuan', callback_data: 'menu:bantuan' }],
      ],
    },
  })
}
 
bot.onText(/\/start/, (msg) => menuUtama(msg.chat.id))

Di HP pelanggan, tampilannya seperti ini:

Toko Saya

bot

/start09:01
Selamat datang di Toko Saya 🛍️ Mau lihat apa?09:01
🛒 Lihat Katalog
🧺 Keranjang Saya
ℹ️ Bantuan
7

Cabang — tampilkan kategori & produk

Sekarang buat dua fungsi: satu menampilkan daftar kategori (cabang), satu lagi menampilkan produk di dalam sebuah kategori (ranting). Perhatikan kita membuat tombol secara otomatis dari data katalog — jadi kalau nambah produk, tombolnya muncul sendiri.

// ===== CABANG: daftar kategori =====
function kirimKategori(chatId) {
  // ubah objek katalog jadi tombol, satu kategori = satu baris
  const tombol = Object.entries(katalog).map(([key, kat]) => [
    { text: kat.label, callback_data: 'kategori:' + key },
  ])
  tombol.push([{ text: '⬅️ Kembali', callback_data: 'menu:utama' }])
 
  bot.sendMessage(chatId, 'Pilih kategori:', {
    reply_markup: { inline_keyboard: tombol },
  })
}
 
// ===== RANTING: daftar produk dalam 1 kategori =====
function kirimProduk(chatId, kategoriKey) {
  const kat = katalog[kategoriKey]
  const tombol = kat.produk.map((p) => [
    { text: `${p.nama} — ${rupiah(p.harga)}`, callback_data: 'produk:' + p.id },
  ])
  tombol.push([{ text: '⬅️ Kembali', callback_data: 'menu:katalog' }])
 
  bot.sendMessage(chatId, `${kat.label}\nPilih produk:`, {
    reply_markup: { inline_keyboard: tombol },
  })
}

Toko Saya

bot

Pilih kategori:
🥤 Minuman
🍔 Makanan
⬅️ Kembali
🥤 Minuman Pilih produk:
Kopi Susu — Rp18.000
Teh Lemon — Rp12.000
⬅️ Kembali
8

Daun — detail produk & tombol beli

Ini ujung pohon: halaman satu produk dengan tombol ➕ Tambah ke keranjang. Fungsi cariProduk mencari produk berdasarkan id-nya di semua kategori.

// ===== DAUN: detail satu produk + tombol "Tambah ke keranjang" =====
function cariProduk(id) {
  for (const kat of Object.values(katalog)) {
    const p = kat.produk.find((x) => x.id === id)
    if (p) return p
  }
  return null
}
 
function kirimDetail(chatId, produkId) {
  const p = cariProduk(produkId)
  if (!p) return
  const teks =
    `*${p.nama}*\n` +
    `Harga: ${rupiah(p.harga)}\n` +
    `Stok: ${p.stok}`
 
  bot.sendMessage(chatId, teks, {
    parse_mode: 'Markdown',
    reply_markup: {
      inline_keyboard: [
        [{ text: '➕ Tambah ke keranjang', callback_data: 'tambah:' + p.id }],
        [{ text: '⬅️ Kembali', callback_data: 'menu:katalog' }],
      ],
    },
  })
}

Toko Saya

bot

Kopi Susu Harga: Rp18.000 Stok: 12
➕ Tambah ke keranjang
⬅️ Kembali
9
Bagian terpenting

Otak percabangan, keranjang & checkout

Semua tombol tadi belum berfungsi sampai kita buat "otak"-nya: satu fungsi yang dipanggil tiap kali tombol ditekan, membaca callback_data, lalu memanggil fungsi cabang yang tepat. Inilah yang membuat pohon menu benar-benar bisa dijelajahi.

// ===== "OTAK" PERCABANGAN: tiap tombol ditangani di sini =====
// callback_data kita format jadi "aksi:nilai", lalu kita pecah.
bot.on('callback_query', (q) => {
  const chatId = q.message.chat.id
  const [aksi, nilai] = q.data.split(':')
 
  if (aksi === 'menu' && nilai === 'utama') menuUtama(chatId)
  else if (aksi === 'menu' && nilai === 'katalog') kirimKategori(chatId)
  else if (aksi === 'menu' && nilai === 'keranjang') kirimKeranjang(chatId)
  else if (aksi === 'kategori') kirimProduk(chatId, nilai)
  else if (aksi === 'produk') kirimDetail(chatId, nilai)
  else if (aksi === 'tambah') tambahKeranjang(chatId, nilai)
  else if (aksi === 'checkout') checkout(chatId)
 
  // wajib: hilangkan ikon "loading" di tombol
  bot.answerCallbackQuery(q.id)
})

Terakhir, fungsi keranjang dan checkout. Keranjang hanyalah daftar produk per pelanggan; checkout menjumlahkan total lalu mengosongkannya.

// ===== KERANJANG & CHECKOUT =====
function tambahKeranjang(chatId, produkId) {
  const p = cariProduk(produkId)
  if (!p) return
  if (!keranjang[chatId]) keranjang[chatId] = []
  keranjang[chatId].push(p)
  bot.sendMessage(chatId, `✅ ${p.nama} masuk keranjang.`)
}
 
function kirimKeranjang(chatId) {
  const isi = keranjang[chatId] || []
  if (isi.length === 0) {
    return bot.sendMessage(chatId, 'Keranjang kamu masih kosong 🧺')
  }
  const total = isi.reduce((sum, p) => sum + p.harga, 0)
  const daftar = isi.map((p, i) => `${i + 1}. ${p.nama} — ${rupiah(p.harga)}`).join('\n')
 
  bot.sendMessage(chatId, `🧺 *Keranjang kamu*\n${daftar}\n\nTotal: *${rupiah(total)}*`, {
    parse_mode: 'Markdown',
    reply_markup: {
      inline_keyboard: [
        [{ text: '💳 Checkout', callback_data: 'checkout:now' }],
        [{ text: '⬅️ Lanjut belanja', callback_data: 'menu:katalog' }],
      ],
    },
  })
}
 
function checkout(chatId) {
  const isi = keranjang[chatId] || []
  const total = isi.reduce((sum, p) => sum + p.harga, 0)
  bot.sendMessage(
    chatId,
    `Terima kasih! 🎉\nPesananmu (${isi.length} item, ${rupiah(total)}) sedang diproses.\nSilakan transfer ke BCA 123-456-7890.`,
  )
  keranjang[chatId] = [] // kosongkan keranjang
}

Toko Saya

bot

✅ Kopi Susu masuk keranjang.09:03
🧺 Keranjang kamu 1. Kopi Susu — Rp18.000 2. Burger Sapi — Rp25.000 Total: Rp43.00009:04
💳 Checkout
⬅️ Lanjut belanja
Terima kasih! 🎉 Pesananmu (2 item, Rp43.000) sedang diproses. Silakan transfer ke BCA 123-456-7890.09:04
Susunan kode yang rapi: data (katalog) terpisah, tampilan (fungsi kirim*) terpisah, dan otak (callback_query) di satu tempat. Pola ini membuat bot mudah dikembangkan.
10
Tayang!

Jalankan bot 24 jam di VPS

Di komputer/HP kamu bisa tes dulu dengan node --env-file=.env bot.js. Kalau sudah jalan, pindahkan ke VPS dan jaga dengan PM2 supaya tetap online walau kamu tutup terminal:

# jalankan bot terus-menerus di VPS
pm2 start "node --env-file=.env bot.js" --name bot-toko
pm2 save
pm2 status
deploy@vps: ~
deploy@vps:~$ pm2 status┌─────┬───────────┬─────────┬────────┐│ id  │ name      │ status  │ uptime ││ 0   │ bot-toko  │ online  │ 5s     │└─────┴───────────┴─────────┴────────┘
Selesai! Kamu sekarang punya bot toko yang bisa dijelajahi lewat tombol. Mau menambah fitur? Tinggal tambah node baru di pohon: misal menu:promo, menu:lacak, atau kategori:snack.

Ide untuk kustomisasi

Mau menambah apaCaranya
Jumlah/qty per produkTambah tombol ➕ / ➖ dengan callback_data qty:tambah:kopi
Simpan pesanan permanenGanti objek keranjang dengan database (Neon/Supabase)
Pembayaran otomatisSambungkan ke payment gateway (mis. Midtrans/Stripe)
Notifikasi ke adminSaat checkout, kirim pesan juga ke chat ID admin
Foto produkPakai bot.sendPhoto(chatId, urlFoto, { caption, reply_markup })

Lanjut ke mana?