Collect VietQR Payments in Telegram Bots with AgentPay

2026-08-26 · AgentPay VN

telegramvietqrpythonpaymentsagentpay

The Problem: Telegram Bots That Can't Monetize

You've built a Telegram bot. It's gaining users. People ask, "How do I pay you?" and you freeze.

Maybe you're selling: - Online courses (₫49,000–₫2,000,000 per course) - Café loyalty programs (₫15,000 per coffee card) - Freelance gigs (₫500,000–₫5,000,000 per project) - Digital products (ebooks, presets, templates)

You don't want to integrate Stripe (foreign, fees, compliance headaches). You don't want to ask users to visit a separate website. You need payments to happen inside Telegram, instantly, using VietQR—the payment standard every Vietnamese bank supports.

Enter AgentPay VN: an open-source Python SDK + MCP server that lets your Telegram bot (or any AI agent) generate VietQR payment requests, send checkout links, and await settlement confirmation—all without touching the money itself.


Why AgentPay Changes the Game

AgentPay is intentionally minimal. It doesn't hold, collect, or manage funds. Instead:

  1. You create a payment request → AgentPay generates a unique VietQR code
  2. User scans the QR → Payment goes directly to your merchant bank account
  3. Bank confirms settlement → AgentPay notifies your bot, you deliver the product

This is radically different from Stripe or PayPal, which act as intermediaries. With AgentPay: - ✅ Zero intermediary fees (pay only your bank's standard VietQR fee: ~0.5%) - ✅ Money lands in your account immediately - ✅ Full control—no waiting for settlement windows - ✅ Open-source (MIT license)—audit the code, self-host the MCP server - ✅ AI-native—built for Claude, OpenAI, and any MCP-compatible agent


Installation & Setup

Step 1: Install the SDK

pip install agentpay-vn

Verify the installation:

python -c "from agentpay_vn import create_payment_request; print('✓ AgentPay installed')"

If you're using Claude or another MCP-compatible AI agent, run:

agentpay-mcp

This starts a local MCP server that exposes AgentPay functions to Claude. Your AI agent can now create and await payments autonomously.

Step 3: Get Your Merchant Info

You need: - Bank name (e.g., "Vietcombank", "Techcombank") - Account number (your merchant bank account) - Account holder name (registered with your bank)

These values are public (users need them to scan the QR code), so don't stress over secrecy here. However, keep your bank account secure from actual transfers outside AgentPay flows.


The 3-Line Payment Flow

Here's the core pattern you'll use in every Telegram bot:

from agentpay_vn import create_payment_request, await_settlement
import asyncio

# Step 1: Create a payment request
payment = create_payment_request(
    amount=99000,  # ₫99,000 for a course
    description="Python FastAPI Masterclass",
    merchant_name="Your Store Name",
    merchant_bank="Techcombank",
    merchant_account="1234567890",
    order_id="order_20250115_user123"  # Unique per transaction
)

print(f"Checkout URL: {payment['checkout_url']}")
# User scans the QR code or clicks the link

# Step 2: Wait for settlement confirmation
settlement = await_settlement(
    order_id="order_20250115_user123",
    timeout_seconds=600  # 10-minute timeout
)

if settlement['confirmed']:
    print(f"✓ Payment confirmed! User {settlement['amount']} VND")
    # Deliver the course, unlock features, etc.
else:
    print("✗ Payment not received or timed out")

Line-by-line breakdown:

  1. create_payment_request(...): Generates a VietQR payment request. AgentPay creates a checkout URL (either a hosted page or a deep-link to the user's banking app).
  2. payment['checkout_url']: The URL you send to the user via Telegram. They click it, their bank app opens, they confirm the payment.
  3. await_settlement(...): Polls your bank's API (through AgentPay's bank feed adapter) to confirm the transfer has landed. Returns {'confirmed': True, 'amount': 99000, 'timestamp': ...} when the payment is settled.

Real-World Example: A Course-Selling Telegram Bot

Let's build a bot that sells Python courses.

from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes, ConversationHandler
from agentpay_vn import create_payment_request, await_settlement
import asyncio

# Course catalog
COURSES = {
    "python_basics": {"name": "Python Basics", "price": 99000},
    "fastapi_pro": {"name": "FastAPI Pro", "price": 299000},
    "ai_agents": {"name": "AI Agents Masterclass", "price": 499000},
}

MERCHANT_INFO = {
    "name": "TechSchool VN",
    "bank": "Techcombank",
    "account": "1234567890",
}

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Show course catalog."""
    courses_text = "📚 **Available Courses:**\n\n"
    for code, course in COURSES.items():
        courses_text += f"/{code} — {course['name']} (₫{course['price']:,})\n"

    await update.message.reply_text(courses_text, parse_mode="Markdown")

async def buy_course(update: Update, context: ContextTypes.DEFAULT_TYPE, course_code: str):
    """Handle course purchase: create payment request and await settlement."""
    user_id = update.effective_user.id
    course = COURSES.get(course_code)

    if not course:
        await update.message.reply_text("❌ Course not found.")
        return

    # Create payment request
    order_id = f"order_{user_id}_{course_code}_{int(time.time())}"
    payment = create_payment_request(
        amount=course["price"],
        description=f"Purchase: {course['name']}",
        merchant_name=MERCHANT_INFO["name"],
        merchant_bank=MERCHANT_INFO["bank"],
        merchant_account=MERCHANT_INFO["account"],
        order_id=order_id,
    )

    # Send checkout link to user
    await update.message.reply_text(
        f"🛒 **{course['name']}**\n"
        f"Price: ₫{course['price']:,}\n\n"
        f"[💳 Pay Now]({payment['checkout_url']})",
        parse_mode="Markdown",
    )

    # Wait for payment in background
    asyncio.create_task(
        handle_payment_settlement(update, order_id, course_code)
    )

async def handle_payment_settlement(update, order_id: str, course_code: str):
    """Poll for settlement and deliver course on confirmation."""
    try:
        settlement = await_settlement(order_id=order_id, timeout_seconds=900)

        if settlement["confirmed"]:
            # Payment successful—deliver course
            course = COURSES[course_code]
            await update.message.reply_text(
                f"✅ **Payment Confirmed!**\n"
                f"Amount: ₫{settlement['amount']:,}\n\n"
                f"📥 Your {course['name']} course materials are being sent...\n\n"
                f"🔗 [Access your course](https://courses.techschool.vn/dashboard)\n"
                f"📧 Check your email for login credentials.",
                parse_mode="Markdown",
            )
            # TODO: Provision course access in your database
        else:
            await update.message.reply_text(
                "⏱️ **Payment Timeout**\n"
                "We didn't receive your payment within 15 minutes. "
                "Please try again: /start"
            )
    except Exception as e:
        await update.message.reply_text(
            f"❌ **Error**\nSomething went wrong: {str(e)}\n"
            "Please contact support."
        )

# Register handlers
app = Application.builder().token("YOUR_TELEGRAM_BOT_TOKEN").build()
app.add_handler(CommandHandler("start", start))

# Dynamic handlers for each course
for course_code in COURSES.keys():
    async def course_handler(update, context, code=course_code):
        await buy_course(update, context, code)
    app.add_handler(CommandHandler(course_code, course_handler))

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

What happens:

  1. User types /python_basics in Telegram
  2. Bot creates a payment request for ₫99,000
  3. Bot sends a checkout link (inline button or Markdown link)
  4. User clicks → their banking app opens → they confirm the payment
  5. Bot polls await_settlement() every 2 seconds (under the hood)
  6. When the bank feed confirms the ₫99,000 arrived in your account, settlement returns confirmed=True
  7. Bot sends course access credentials

Integrating with Claude via MCP

If you want Claude to autonomously manage payments (e.g., a support bot that upsells and charges), configure Claude with the AgentPay MCP server:

Claude Desktop config (~/.claude_desktop_config.json on Mac/Linux, %APPDATA%\Claude\claude_desktop_config.json on Windows):

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "args": []
    }
  }
}

Now restart Claude Desktop. In any conversation, Claude can:

User: "I want to buy the FastAPI course. My account is techbank 9876543210."

Claude: "I'll create a payment request for ₫299,000."
[Claude invokes create_payment_request via MCP]
"Here's your checkout link: https://agentpay.servicesai.vn/v1/checkout?id=xyz"
[Claude then awaits_settlement in the background]
"Payment confirmed! Your course access is ready."

Do's and Don'ts

✅ DO ❌ DON'T
Store order_id in your database to track purchases Hardcode the same order_id for multiple users
Set realistic timeout_seconds (5–15 minutes) Set timeout to 60+ seconds—VietQR can be slow in peak hours
Validate settlement['amount'] matches your price Trust the amount without verification—always check
Use unique order IDs (e.g., order_{user_id}_{timestamp}) Reuse order IDs across sessions
Keep your merchant bank account private Share your bank account number in client-side code
Handle await_settlement() exceptions gracefully Let timeout errors crash your bot
Test with small amounts first (₫1,000–₫10,000) Go live with big prices immediately

Advanced Tips

Batch Payments for Subscriptions

For monthly subscriptions, create a recurring payment:

import schedule

def charge_monthly_subscription(user_id: str, email: str):
    payment = create_payment_request(
        amount=49000,
        description=f"Monthly subscription for {email}",
        merchant_name="Your SaaS",
        merchant_bank="Techcombank",
        merchant_account="9876543210",
        order_id=f"sub_{user_id}_{datetime.now().strftime('%Y%m')}",
    )
    # Send payment link via email or Telegram
    send_payment_link(email, payment['checkout_url'])

schedule.every().month.at("00:00").do(charge_all_subscribers)

Webhook Notifications (Future Feature)

AgentPay currently uses polling. For high-volume bots, set up a webhook listener:

from fastapi import FastAPI
app = FastAPI()

@app.post("/webhook/settlement")
async def settlement_webhook(payload: dict):
    order_id = payload["order_id"]
    amount = payload["amount"]

    # Deliver product immediately without polling
    deliver_course(order_id, amount)
    return {"status": "ok"}

(This is on the roadmap for AgentPay v2.)

Multi-Currency (Future)

Currently, AgentPay supports VND only. USD/THB support is planned.


FAQ

Q: Does AgentPay hold my money?

No. AgentPay never touches the funds. Payment goes directly from the user's bank to your merchant account. AgentPay only orchestrates the QR generation and settlement confirmation.

Q: What if a user scans the QR but doesn't complete the payment?

The await_settlement() function times out after your specified duration (default 10 minutes). You'll get confirmed=False. You can ask the user to retry or provide a new checkout link.

Q: Is AgentPay free?

Yes, the SDK is free (MIT license, open-source). You pay only your bank's standard VietQR fee (~0.5% of the transaction). No AgentPay markup.

Q: Can I use AgentPay outside Telegram?

Absolutely. It works in any Python app: Discord bots, Slack bots, web apps, CLI tools, AI agents. It's payment-method-agnostic.

Q: What if I need invoicing or receipts?

AgentPay provides settlement data. You can generate invoices in your app using libraries like reportlab or integrate with accounting software (Wave, Xero) via their APIs.


Key Takeaways


Get Started Now

  1. Install AgentPay: bash pip install agentpay-vn

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

  3. Explore the source code: https://github.com/phuocdu/agentpay-vn

  4. Build your first payment bot using the course-selling example above.

Your Telegram bot can start accepting VietQR payments today. No bank calls, no paperwork, no intermediaries. Just Python, AgentPay, and instant settlement.

Happy selling! 🚀

Get started →

← All posts