DEV Community

How I Sell 26 Digital Products With a 200-Line Python Bot

The Setup

I wanted to sell digital products without:

  • Building a website
  • Setting up payment processing
  • Paying monthly SaaS fees

So I built a Telegram bot. Here's the entire architecture.

Architecture Overview

User opens bot
    → /start command
    → Main menu (inline keyboard)
    → Browse categories
    → Select product
    → View details + price
    → Click "Buy"
    → Telegram Stars payment
    → pre_checkout_query → approve
    → successful_payment → deliver PDF
    → Save to SQLite
Enter fullscreen mode Exit fullscreen mode

The Product Catalog

I store everything in a Python dictionary:

PRODUCTS = {
    'swiftui_starter': {
        'name': 'SwiftUI Starter Kit Pro',
        'description': 'MVVM templates, networking, Core Data...',
        'stars': 75,
        'category': 'swiftui',
        'file': 'products/swiftui_starter.pdf'
    },
    # ... 25 more products
}
Enter fullscreen mode Exit fullscreen mode

No database needed for the catalog. Products rarely change, so a dict works fine.

Navigation System

Telegram inline keyboards make great UIs:

def category_keyboard(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", callback_data="main_menu"
    ))
    return keyboard
Enter fullscreen mode Exit fullscreen mode

Payment Flow

The payment code is surprisingly simple:

@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 on_payment(message):
    product_id = message.successful_payment.invoice_payload
    product = PRODUCTS[product_id]

    # Send the PDF
    with open(product['file'], 'rb') as f:
        bot.send_document(message.chat.id, f)

    # Save to database
    save_purchase(message.from_user.id, product_id)

    bot.send_message(
        message.chat.id,
        f"Thank you! Enjoy {product['name']}!"
    )
Enter fullscreen mode Exit fullscreen mode

Database

SQLite stores users and purchases:

import sqlite3

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

What I'm Selling

6 categories, 26 products:

  1. SwiftUI Templates — production-ready code
  2. Notion Systems — productivity dashboards
  3. Career Guides — resume, interview, LinkedIn
  4. AI Toolkits — prompts and workflows
  5. Business Tools — freelancer and startup kits
  6. Bundles — discounted collections

Costs

Item Cost
VPS (optional) $5/month
Domain $0
Payment processing $0
Platform fees $0
Total $5/month

Lessons Learned

  1. Keep it simple — a dict beats a CMS
  2. Instant delivery wins — no email, no download links
  3. Low prices convert — 25 stars is an impulse buy
  4. Telegram UX is good enough — inline keyboards work great

Try It

Bot: @SwiftUIDailyBot on Telegram
Channel: t.me/SwiftUIDaily

The full code is under 200 lines of Python. If you can write a basic bot, you can build a store.


Questions? Drop them in the comments. Happy to share more details about the implementation.

Top comments (1)

Collapse
 
apex_stack profile image
Apex Stack

Love the simplicity here. I sell digital products too (Claude Skills on Gumroad) and the "dict beats a CMS" philosophy is spot on. My product catalog is literally a markdown file that feeds into a pipeline — no database, no admin panel.

The Telegram Stars payment flow is interesting. I hadn't considered Telegram as a storefront — zero platform fees is hard to beat. With Gumroad taking 10% per sale, that adds up fast when you're doing volume.

Curious about discovery though. With Gumroad I at least get some marketplace traffic (small, but nonzero). How are people finding your bot? Is the Telegram channel doing most of the heavy lifting, or are you driving traffic from somewhere else?

The $5/month total cost is the dream. Keep shipping.