Build a Payment-Collecting Telegram Bot with VietQR

2026-09-10 · AgentPay VN

telegramvietqrpythonpaymentsagentpay

The Problem: Accepting Payments in Telegram Is a Nightmare

You've built a Telegram bot that sells digital products—online courses, design templates, or meal subscriptions. Your customers are already in Telegram. They're ready to pay. But then you hit the wall: integrating a payment gateway requires complex webhook setups, holding customer money in a merchant account, or routing payments through third-party aggregators that take 3–5% cuts and delay settlements by days.

Worse, you need to manage account balances, reconcile transactions, and handle refunds manually. It's 2025; this shouldn't be this hard.

AgentPay VN solves this. It's an open-source Python SDK that lets your Telegram bot generate a VietQR code pointing directly to your bank account—no escrow, no middleman, no delays. Your customer scans the QR with their mobile banking app, pays you instantly, and your bot automatically confirms the order. Settlement lands in your bank within minutes.


What Is AgentPay VN? The Five-Second Version

AgentPay VN is a MIT-licensed Python SDK built for AI agents and bots to collect Vietnamese payments via VietQR. Here's what makes it different:

Installation is one command:

pip install agentpay-vn

That's genuinely it.


How It Works: The Three-Line Payment Flow

Before we code, understand the mental model. Every payment follows this pattern:

1. create_payment_request()  → Generate a unique payment link + QR
2. send checkout_url()       → Display QR to customer in Telegram
3. await_settlement()        → Wait for bank confirmation, unlock order

That's the entire lifecycle. No polling. No cron jobs. Just request → link → settle.


Step 1: Install and Configure AgentPay

Installation

# Install the SDK
pip install agentpay-vn

# If you're building a Claude-integrated bot, also install the MCP server
pip install agentpay-mcp

Set Up Your Bank Details

AgentPay needs your bank account and VietQR information. Create a .env file in your project:

# .env
BANK_ACCOUNT_NUMBER=0123456789
BANK_CODE=BIDV  # or VCOMBANK, ACB, etc.
BANK_ACCOUNT_NAME="Your Full Legal Name"
MERCHANT_ID=your_merchant_id  # From your bank or VietQR provider

These details ensure QR codes point to your account, not AgentPay's.


Step 2: Build Your First Payment-Enabled Telegram Bot

Let's create a simple bot that sells a 50,000 VND e-book. Here's the full code:

Python Code: Core Bot Logic

import os
from dotenv import load_dotenv
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, ContextTypes, CallbackQueryHandler
from agentpay_vn import AgentPayClient, PaymentRequest
import asyncio

load_dotenv()

# Initialize AgentPay client
agentpay = AgentPayClient(
    bank_account=os.getenv("BANK_ACCOUNT_NUMBER"),
    bank_code=os.getenv("BANK_CODE"),
    account_name=os.getenv("BANK_ACCOUNT_NAME"),
    merchant_id=os.getenv("MERCHANT_ID")
)

# Initialize Telegram bot
app = Application.builder().token(os.getenv("TELEGRAM_TOKEN")).build()

# Product catalog
PRODUCTS = {
    "ebook": {"name": "Python Mastery E-Book", "price": 50000, "file_id": "python_ebook.pdf"}
}

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Show product menu."""
    keyboard = [[InlineKeyboardButton("📖 Buy E-Book (50,000 VND)", callback_data="buy_ebook")]]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text(
        "Welcome! Here's what we offer:",
        reply_markup=reply_markup
    )

async def buy_product(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Handle purchase button press."""
    query = update.callback_query
    product_key = query.data.replace("buy_", "")
    product = PRODUCTS[product_key]

    # Step 1: Create payment request
    payment_request = PaymentRequest(
        amount=product["price"],
        description=f"Purchase: {product['name']}",
        order_id=f"order_{update.effective_user.id}_{int(asyncio.get_event_loop().time())}",
        customer_id=str(update.effective_user.id)
    )

    # Step 2: Generate QR and checkout URL
    qr_code, checkout_url = agentpay.create_payment_request(payment_request)

    # Send QR code image to customer
    await query.message.reply_photo(
        photo=qr_code,
        caption=(
            f"🏧 Scan to pay {product['price']:,} VND\n\n"
            f"Or tap here: {checkout_url}\n\n"
            f"We'll deliver your {product['name']} once payment confirms!"
        )
    )

    # Step 3: Wait for settlement (non-blocking)
    context.application.create_task(
        wait_for_payment(update.effective_user.id, payment_request.order_id, product_key, context)
    )

async def wait_for_payment(user_id: int, order_id: str, product_key: str, context: ContextTypes.DEFAULT_TYPE):
    """Poll for payment confirmation."""
    max_wait = 300  # 5 minutes
    elapsed = 0

    while elapsed < max_wait:
        # Check if payment settled
        settlement = agentpay.await_settlement(order_id, timeout=10)

        if settlement["status"] == "confirmed":
            # Payment received!
            product = PRODUCTS[product_key]
            await context.bot.send_message(
                chat_id=user_id,
                text=(
                    f"✅ Payment received! {settlement['amount']:,} VND\n\n"
                    f"Your {product['name']} is ready. Download: [link to file]"
                )
            )
            return

        elapsed += 10
        await asyncio.sleep(10)

    # Timeout
    await context.bot.send_message(
        chat_id=user_id,
        text="⏱️ Payment timeout. Please try again."
    )

# Register handlers
app.add_handler(CommandHandler("start", start))
app.add_handler(CallbackQueryHandler(buy_product, pattern="buy_"))

if __name__ == "__main__":
    app.run_polling()

Line-by-Line Explanation

  1. Lines 13–18: Initialize AgentPay with your bank details from .env.
  2. Lines 21–22: Set up the Telegram bot.
  3. Lines 35–42: /start command shows a button to buy the e-book.
  4. Lines 44–73: When user clicks "Buy", we: - Create a PaymentRequest with amount and order ID. - Call create_payment_request() to get QR code and URL. - Send the QR to the user. - Spawn an async task to monitor payment.
  5. Lines 75–102: wait_for_payment() polls the bank feed every 10 seconds. When settlement is confirmed, deliver the product.

Key insight: The entire flow is non-blocking. Your bot can handle 1,000 concurrent payments without freezing.


Step 3: Connect Claude (or Any AI Agent) via MCP

Want Claude to autonomously handle payment collection? Use the MCP server.

MCP Configuration for Claude Desktop

Create or edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or the Windows equivalent:

{
  "mcpServers": {
    "agentpay": {
      "command": "python",
      "args": ["-m", "agentpay_mcp"],
      "env": {
        "BANK_ACCOUNT_NUMBER": "0123456789",
        "BANK_CODE": "BIDV",
        "BANK_ACCOUNT_NAME": "Your Name",
        "MERCHANT_ID": "your_id"
      }
    }
  }
}

Now Claude can call AgentPay functions natively:

User: "Set up a payment for 100,000 VND for a course enrollment."

Claude: I'll create a payment request for you.
→ Calls: create_payment_request(amount=100000, description="Course Enrollment")
→ Returns QR code and checkout URL

Claude: Here's your QR code. Scan it to pay 100,000 VND. I'll notify you once settled.

Real-World Example: An Online Course Bot

Imagine you run an online Python course and sell it via Telegram for 299,000 VND.

  1. Customer joins Telegram group → Bot shows "Enroll" button.
  2. Customer clicks → Bot generates VietQR payment link.
  3. Customer scans QR → Pays from mobile banking app (2–3 seconds).
  4. Settlement arrives → Bank confirms within 30 seconds.
  5. Bot gets notification → Automatically adds customer to course channel, sends access link.
  6. No refund headaches → If customer disputes, bank handles it (not you).

Money flow:

Customer Bank Account
        ↓ (scans QR, pays 299,000 VND)
Your Bank Account ← Money lands here instantly
        ↓ (AgentPay reads bank feed)
Your Bot ← Confirmation received
        ↓ (unlocks course access)
Customer Gets Course

Total latency: 45 seconds from scan to access.


Advanced Tips & Best Practices

1. Handle Payment Disputes

VietQR payments are bank transfers. If a customer disputes payment, the bank handles the chargeback, not you. Your bot should still keep logs:

# Log all payments to a database
from datetime import datetime

def log_payment(order_id, user_id, amount, status):
    with open("payments.log", "a") as f:
        f.write(f"{datetime.now()} | {order_id} | {user_id} | {amount} | {status}\n")

2. Retry Failed Payments

If await_settlement() times out, regenerate the QR instead of re-using it:

# Don't reuse order_id; create a new one
new_payment = PaymentRequest(
    amount=product["price"],
    order_id=f"order_{user_id}_{int(time.time())}_retry",  # New ID
    description=f"Retry: {product['name']}"
)

3. Test with Mock Settlements

AgentPay supports a test mode. Set AGENTPAY_ENV=test to simulate payments without real money:

AGENTPAY_ENV=test python your_bot.py

Do's and Don'ts

✅ Do ❌ Don't
Generate a unique order ID per payment Reuse order IDs or QR codes
Store order_id and customer_id for records Rely on settlement being instant (use 30–60 sec timeout)
Log all payments for tax/audit purposes Assume QR code is valid forever (regenerate on retry)
Use MCP for AI-to-payment integration Hold customer data longer than needed (GDPR)
Test with AGENTPAY_ENV=test first Hardcode bank details in code (use .env)

Frequently Asked Questions

Q: Does AgentPay hold my money?

A: No. AgentPay never touches your funds. The QR code points directly to your bank account. Money lands in your bank account; AgentPay only reads the settlement from the bank feed.

Q: How fast is payment confirmation?

A: Typically 30–60 seconds after the customer pays. VietQR is a bank transfer, so it's as fast as your bank's feed updates (usually real-time).

Q: Can I use this outside Vietnam?

A: No. VietQR and the bank integration currently work only for Vietnamese banks. If you need international payments, consider Stripe or PayPal alongside AgentPay.

Q: What happens if the customer's payment fails?

A: The money never leaves their account. Your bot will timeout waiting for settlement. You can then ask the customer to retry, and AgentPay will generate a new QR code.


Key Takeaways


Get Started Now

Install AgentPay VN:

pip install agentpay-vn

Read the full docs:https://agentpay.servicesai.vn/v1/docs

Fork the repository:https://github.com/phuocdu/agentpay-vn

Your Telegram bot can accept payments in minutes, not weeks. Build it today.

Get started →

← All posts