DEV Community

Building a Telegram Bot Payment System with Python (Complete Guide)

What We're Building

A fully functional e-commerce bot that:

  • Displays product catalog with categories
  • Accepts Telegram Stars as payment
  • Delivers digital files instantly
  • Tracks purchases in SQLite

Prerequisites

  • Python 3.9+
  • pyTelegramBotAPI library
  • Bot token from @botfather
pip install pyTelegramBotAPI
Enter fullscreen mode Exit fullscreen mode

Step 1: Bot Setup

import telebot
from telebot.types import (
    InlineKeyboardMarkup, InlineKeyboardButton,
    LabeledPrice
)
import sqlite3
import os

TOKEN = os.environ.get('BOT_TOKEN')
bot = telebot.TeleBot(TOKEN)
Enter fullscreen mode Exit fullscreen mode

Step 2: Product Catalog

CATEGORIES = {
    'templates': 'Code Templates',
    'notion': 'Notion Systems',
    'career': 'Career Guides',
    'ai': 'AI Toolkits'
}

PRODUCTS = {
    'starter_kit': {
        'name': 'Starter Kit Pro',
        'description': 'Complete project templates with MVVM, networking, and more',
        'stars': 75,
        'category': 'templates',
        'file': 'products/starter_kit.pdf'
    },
    'notion_brain': {
        'name': 'Second Brain System',
        'description': 'Notion-based knowledge management',
        'stars': 40,
        'category': 'notion',
        'file': 'products/notion_brain.pdf'
    },
    # Add more products...
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Main Menu

@bot.message_handler(commands=['start'])
def start(message):
    keyboard = InlineKeyboardMarkup(row_width=2)
    for cat_id, cat_name in CATEGORIES.items():
        keyboard.add(InlineKeyboardButton(
            cat_name,
            callback_data=f'category_{cat_id}'
        ))

    bot.send_message(
        message.chat.id,
        'Welcome to the Digital Shop!\n'
        'Browse our products by category:',
        reply_markup=keyboard
    )
Enter fullscreen mode Exit fullscreen mode

Step 4: Category Browsing

@bot.callback_query_handler(func=lambda c: c.data.startswith('category_'))
def show_category(call):
    category = call.data.replace('category_', '')
    keyboard = InlineKeyboardMarkup(row_width=1)

    for pid, product in PRODUCTS.items():
        if product['category'] == category:
            keyboard.add(InlineKeyboardButton(
                f"{product['name']}{product['stars']}",
                callback_data=f'product_{pid}'
            ))

    keyboard.add(InlineKeyboardButton(
        '← Back to Menu',
        callback_data='main_menu'
    ))

    bot.edit_message_text(
        f"📂 {CATEGORIES[category]}",
        call.message.chat.id,
        call.message.message_id,
        reply_markup=keyboard
    )
Enter fullscreen mode Exit fullscreen mode

Step 5: Product Details

@bot.callback_query_handler(func=lambda c: c.data.startswith('product_'))
def show_product(call):
    pid = call.data.replace('product_', '')
    product = PRODUCTS[pid]

    keyboard = InlineKeyboardMarkup()
    keyboard.add(InlineKeyboardButton(
        f"Buy for {product['stars']}",
        callback_data=f'buy_{pid}'
    ))
    keyboard.add(InlineKeyboardButton(
        '← Back',
        callback_data=f"category_{product['category']}"
    ))

    bot.edit_message_text(
        f"**{product['name']}**\n\n"
        f"{product['description']}\n\n"
        f"Price: {product['stars']}",
        call.message.chat.id,
        call.message.message_id,
        reply_markup=keyboard,
        parse_mode='Markdown'
    )
Enter fullscreen mode Exit fullscreen mode

Step 6: Payment (The Key Part)

@bot.callback_query_handler(func=lambda c: c.data.startswith('buy_'))
def buy_product(call):
    pid = call.data.replace('buy_', '')
    product = PRODUCTS[pid]

    bot.send_invoice(
        call.message.chat.id,
        title=product['name'],
        description=product['description'],
        invoice_payload=pid,
        provider_token='',      # Empty for Stars!
        currency='XTR',          # Telegram Stars
        prices=[LabeledPrice(
            label=product['name'],
            amount=product['stars']
        )]
    )

@bot.pre_checkout_query_handler(func=lambda q: True)
def pre_checkout(query):
    bot.answer_pre_checkout_query(query.id, ok=True)

@bot.message_handler(content_types=['successful_payment'])
def payment_success(message):
    pid = message.successful_payment.invoice_payload
    product = PRODUCTS[pid]

    # Deliver the product
    with open(product['file'], 'rb') as f:
        bot.send_document(
            message.chat.id, f,
            caption=f"Here's your {product['name']}!"
        )

    # Save purchase
    save_to_db(message.from_user.id, pid, product['stars'])
Enter fullscreen mode Exit fullscreen mode

Step 7: Database

def init_db():
    conn = sqlite3.connect('shop.db')
    conn.execute('''CREATE TABLE IF NOT EXISTS purchases (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        user_id INTEGER,
        product_id TEXT,
        amount INTEGER,
        created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    )''')
    conn.commit()
    conn.close()

def save_to_db(user_id, product_id, amount):
    conn = sqlite3.connect('shop.db')
    conn.execute(
        'INSERT INTO purchases (user_id, product_id, amount) VALUES (?, ?, ?)',
        (user_id, product_id, amount)
    )
    conn.commit()
    conn.close()
Enter fullscreen mode Exit fullscreen mode

Step 8: Run

if __name__ == '__main__':
    init_db()
    print('Bot is running...')
    bot.infinity_polling()
Enter fullscreen mode Exit fullscreen mode

Key Points

  • provider_token='' means Telegram Stars (no external payment provider)
  • currency='XTR' is the currency code for Stars
  • Always handle pre_checkout_query or payments will fail
  • successful_payment is where you deliver the product

Live Example

Try @SwiftUIDailyBot on Telegram to see this in action with 26 real products.

Channel: t.me/SwiftUIDaily


Found this helpful? Follow me for more Python and SwiftUI tutorials.

Top comments (0)