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.
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.
🏠 Menu Utama ← akar (/start) ├─ 🛒 Katalog │ ├─ 🥤 Minuman ← cabang (kategori) │ │ ├─ Kopi Susu ← daun (produk) │ │ └─ Teh Lemon │ └─ 🍔 Makanan │ ├─ Burger Sapi │ └─ Kentang Goreng ├─ 🧺 Keranjang Saya │ └─ 💳 Checkout └─ ℹ️ Bantuan
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.
| Tombol | callback_data | Membawa ke |
|---|---|---|
| 🛒 Lihat Katalog | menu:katalog | Daftar kategori |
| 🥤 Minuman | kategori:minuman | Produk di kategori minuman |
| Kopi Susu | produk:kopi | Detail produk Kopi Susu |
| ➕ Tambah ke keranjang | tambah:kopi | Masukkan ke keranjang |
| 💳 Checkout | checkout:now | Selesaikan pesanan |
aksi:nilaiini kunci yang membuat bot rapi. Satu fungsi "otak" nanti membaca kode ini dan memutuskan harus pindah ke cabang mana.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-apiSimpan token di file .env supaya tidak tercampur ke dalam kode:
1TELEGRAM_TOKEN=123456789:AAE-ContohTokenKamu_RAHASIAbot.js dan jangan upload .env ke GitHub. Tambahkan .env ke file .gitignore.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.
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 }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
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
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
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
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 statusdeploy@vps:~$ pm2 status┌─────┬───────────┬─────────┬────────┐│ id │ name │ status │ uptime ││ 0 │ bot-toko │ online │ 5s │└─────┴───────────┴─────────┴────────┘menu:promo, menu:lacak, atau kategori:snack.Ide untuk kustomisasi
| Mau menambah apa | Caranya |
|---|---|
| Jumlah/qty per produk | Tambah tombol ➕ / ➖ dengan callback_data qty:tambah:kopi |
| Simpan pesanan permanen | Ganti objek keranjang dengan database (Neon/Supabase) |
| Pembayaran otomatis | Sambungkan ke payment gateway (mis. Midtrans/Stripe) |
| Notifikasi ke admin | Saat checkout, kirim pesan juga ke chat ID admin |
| Foto produk | Pakai bot.sendPhoto(chatId, urlFoto, { caption, reply_markup }) |