Telegram Bot Payments with VietQR: AgentPay Guide

2026-10-08 · AgentPay VN

telegramvietqrpythonpaymentsagentpay

Why Most Telegram Bots Still Can't Accept Payments in Vietnam

You've built a thriving Telegram bot—maybe it sells online courses, takes coffee preorders, or manages membership subscriptions. Users love it. But every time someone wants to pay, you're stuck directing them to an external link, asking for bank transfers, or worse, handling cash on delivery. You're losing conversions in those friction points.

The truth: most payment solutions either take days to settle, charge 3–5% fees, or require clunky integrations. In Vietnam, where VietQR has unified QR payment across all banks, there's a better way. With AgentPay VN, your Telegram bot can generate an instant payment QR code, and the money lands directly in your merchant account without a middleman.

This tutorial shows you exactly how to do it.

What Is AgentPay VN?

AgentPay VN is an open-source Python SDK (MIT license) that bridges AI agents and VietQR payments. Think of it as a lightweight, purpose-built connector:

You can install it in one line: pip install agentpay-vn. For AI agents (Claude, etc.), there's also an MCP server: agentpay-mcp.

Key fact: AgentPay is not a payment gateway that holds funds. It's a bridge. Settlement confirmation comes from your bank's API feed, making it faster and cheaper than traditional processors.

The Problem: Traditional Payment Flows vs. VietQR Reality

Traditional Approach (❌ Slow & Painful)

  1. User clicks "Pay" in bot
  2. Redirected to Stripe / PayPal / third-party processor
  3. User enters card details (friction, trust issues in VN)
  4. Processor approves, charges 2–4% fee
  5. Settlement takes 2–3 business days
  6. You never see the actual QR code in the bot

AgentPay Approach (✅ Fast & Native)

  1. User clicks "Pay" in bot
  2. Bot generates VietQR code instantly, displayed inline
  3. User scans with any bank app (98% have VietQR support in VN)
  4. Money lands in your account within seconds to minutes
  5. Bot confirms settlement and triggers order fulfillment

No redirect. No card. No middleman fee. Just pure, native Vietnamese payments.

Setting Up AgentPay in Your Telegram Bot

Step 1: Install the SDK

pip install agentpay-vn

This gives you the core library. If you're also using Claude or another AI agent, install the MCP server:

pip install agentpay-mcp

Step 2: Initialize AgentPay in Your Bot

Here's a minimal Telegram bot that accepts payments:

import os
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, ContextTypes
from agentpay_vn import AgentPay

# Initialize AgentPay
# Credentials come from https://agentpay.servicesai.vn/v1/docs
agent_pay = AgentPay(
    api_key=os.getenv("AGENTPAY_API_KEY"),
    merchant_account="your_vietqr_account"
)

# Telegram bot token
TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Start command—user sees a simple menu."""
    keyboard = [
        [InlineKeyboardButton("💳 Buy Premium ($5)", callback_data="buy_premium_5")],
        [InlineKeyboardButton("💳 Buy Pro ($15)", callback_data="buy_premium_15")]
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)
    await update.message.reply_text(
        "Welcome! Choose a plan:",
        reply_markup=reply_markup
    )

async def button_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """Handle payment button clicks."""
    query = update.callback_query
    await query.answer()

    # Extract price from callback data
    plan = query.data  # "buy_premium_5" or "buy_premium_15"
    amount = int(plan.split("_")[-1]) * 1000  # Convert to VND (multiply by 1000)

    # Create payment request
    payment = agent_pay.create_payment_request(
        amount=amount,
        description=f"AgentPay Bot: {plan.replace('_', ' ')}",
        order_id=f"order_{update.effective_user.id}_{int(time.time())}"
    )

    # Get checkout URL with QR code
    checkout_url = payment.checkout_url
    qr_code_url = payment.qr_image_url

    # Send QR code directly in Telegram
    await query.edit_message_text(
        text=f"Scan to pay {amount:,} VND:\n\nCheckout: {checkout_url}",
        reply_markup=InlineKeyboardMarkup([
            [InlineKeyboardButton("Open Checkout", url=checkout_url)]
        ])
    )

    # Store payment in context for polling
    context.user_data["payment_id"] = payment.id
    context.user_data["amount"] = amount
    context.user_data["user_id"] = update.effective_user.id

if __name__ == "__main__":
    app = Application.builder().token(TOKEN).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("pay", button_callback))
    app.run_polling()

Line-by-line breakdown: - Lines 8–12: Initialize AgentPay with your merchant credentials - Lines 18–26: /start shows two payment options as inline buttons - Lines 28–53: When user clicks a button, we create a payment request with create_payment_request(), which returns a checkout_url and qr_image_url - Line 42–43: The payment object has all the data we need; we extract amount and generate a unique order ID - Line 56: We send the checkout URL to the user; they can open it to see the QR or scan it directly with their bank app

Step 3: Listen for Payment Settlement

Here's the critical part—waiting for settlement and confirming the user's purchase:

import asyncio
from agentpay_vn import await_settlement

async def confirm_payment(context: ContextTypes.DEFAULT_TYPE):
    """Poll for payment settlement every 5 seconds."""
    user_data = context.user_data
    payment_id = user_data.get("payment_id")
    user_id = user_data.get("user_id")
    amount = user_data.get("amount")

    if not payment_id:
        return

    # Wait for settlement (blocks until settlement or timeout)
    settlement = await_settlement(
        payment_id=payment_id,
        timeout_seconds=600  # Wait up to 10 minutes
    )

    if settlement.status == "settled":
        # Payment confirmed! Deliver the product.
        await context.bot.send_message(
            chat_id=user_id,
            text=f"✅ Payment of {amount:,} VND confirmed!\n\n"
                 f"Your premium access is now active. Enjoy!"
        )
        # Trigger fulfillment: send access key, add to database, etc.
        deliver_premium_access(user_id, amount)
    else:
        # Payment failed or timed out
        await context.bot.send_message(
            chat_id=user_id,
            text=f"❌ Payment not confirmed. Please try again.\n"
                 f"Order ID: {payment_id}"
        )

# Add to your Application setup:
app.add_handler(MessageHandler(filters.TEXT, confirm_payment))

How this works: - await_settlement() connects to your bank's feed and waits for the VietQR payment to clear - Once settlement is confirmed (usually 5–30 seconds), you can immediately trigger fulfillment - No webhook complexity, no polling loops that time out—it's built in

Integration: Using AgentPay with Claude via MCP

If you're building an AI-driven payment flow (e.g., a chatbot that negotiates price and then accepts payment), you can use the AgentPay MCP server:

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "env": {
        "AGENTPAY_API_KEY": "your-api-key-here",
        "AGENTPAY_MERCHANT": "your-merchant-account"
      }
    }
  }
}

Now Claude can: 1. Call agentpay.create_payment_request() to propose a price 2. Generate a checkout URL 3. Wait for settlement 4. Confirm fulfillment

All within a single conversation loop.

Real-World Example: Online Course Bot

Let's walk through a concrete scenario: you sell Python courses via Telegram.

User journey: 1. User sends /courses 2. Bot lists 3 courses: Beginner ($5), Intermediate ($12), Advanced ($25) 3. User clicks "Buy Advanced" 4. AgentPay generates VietQR for 25,000 VND 5. User scans with Vietcombank app → payment settles in 10 seconds 6. Bot detects settlement and immediately sends: - Access link to course materials (Google Drive, S3, etc.) - Login credentials for learning platform - Welcome video 7. User starts learning within 30 seconds of scanning the QR

Code sketch:

async def send_course_access(user_id, course_level):
    """Send course materials after payment settles."""
    credentials = generate_course_credentials(user_id, course_level)

    message = (
        f"🎓 Welcome to the {course_level.title()} Course!\n\n"
        f"📚 Access Link: {credentials['access_url']}\n"
        f"🔑 Password: {credentials['password']}\n\n"
        f"Your 2-year access starts now. Happy learning!"
    )
    await context.bot.send_message(chat_id=user_id, text=message)

    # Log purchase to database
    db.log_purchase(user_id, course_level, 25000)  # or whatever amount

No email delays. No manual activation. Pure automation from scan to learning.

Do's and Don'ts

Do ✅ Don't ❌
Use unique order_id for each payment (prevents duplicates) Hard-code merchant account in code (use environment variables)
Set a reasonable timeout_seconds (10 min is typical) Assume settlement happens instantly (it's fast, but not instant)
Confirm settlement before delivering goods/access Store sensitive API keys in version control
Test with small amounts first (500 VND = ~2 cents) Rely on QR codes expiring—they don't; use order IDs for replay protection
Log all payments to a database for auditing Ignore bank feed errors (implement retry logic)

Advanced Tips

Tip 1: Retry Logic for Flaky Connections

from agentpay_vn import SettlementError

async def robust_settlement_check(payment_id, retries=3):
    for attempt in range(retries):
        try:
            settlement = await_settlement(payment_id, timeout_seconds=120)
            return settlement
        except SettlementError as e:
            if attempt < retries - 1:
                await asyncio.sleep(2 ** attempt)  # Exponential backoff
            else:
                raise

Tip 2: Multi-Currency (If You Have International Customers)

VietQR works in VND only, but you can accept USD and convert:

import requests

def usd_to_vnd(usd_amount):
    # Fetch live rate
    rate = requests.get("https://api.exchangerate-api.com/v4/latest/USD").json()
    vnd_rate = rate["rates"]["VND"]
    return int(usd_amount * vnd_rate)

Tip 3: Handle Payment Cancellations

Store payment state in Redis or your database:

cancel_button = InlineKeyboardButton(
    "Cancel Order",
    callback_data=f"cancel_{payment_id}"
)

# Later, in callback:
if query.data.startswith("cancel_"):
    payment_id = query.data.split("_")[1]
    # Don't deliver goods; log cancellation
    db.mark_cancelled(payment_id)

FAQ

Q: Does AgentPay hold my money? No. AgentPay is not a payment processor. The VietQR code points directly to your merchant bank account. Settlement confirmation comes from your bank's API feed.

Q: What's the settlement time? Most transfers settle within 5–30 seconds. During high-volume periods (e.g., Tet sales), it can be up to 2 minutes. AgentPay monitors your bank feed continuously.

Q: Do I need a special merchant account? You need a VietQR-enabled merchant account with any Vietnamese bank (Vietcombank, Techcombank, ACB, etc.). Ask your bank for credentials; AgentPay uses them to generate QR codes and monitor settlement.

Q: Can I use this with payment splits (e.g., seller + platform commission)? Not directly through VietQR—splits happen post-settlement. You'd transfer funds after confirming payment. AgentPay doesn't prevent this; it's your post-payment logic.

Key Takeaways

Get Started Now

  1. Install: pip install agentpay-vn
  2. Grab your merchant credentials: Contact your bank's VietQR support
  3. Read the full docs: https://agentpay.servicesai.vn/v1/docs
  4. Check the GitHub repo for examples and community support: https://github.com/phuocdu/agentpay-vn
  5. Test with a 500 VND payment (2 cents) to confirm everything works

Then go build the fastest-settling Telegram bot in Vietnam. Your users will love the simplicity, and you'll love the instant settlement.

Get started →

← All posts