Telegram Bot Payments with VietQR: AgentPay Guide
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:
- Creates payment requests with custom amounts and descriptions
- Generates checkout URLs (including VietQR QR codes)
- Watches for settlement via bank feeds—no polling, no webhooks noise
- Never touches your money—it points the QR straight at your merchant account
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)
- User clicks "Pay" in bot
- Redirected to Stripe / PayPal / third-party processor
- User enters card details (friction, trust issues in VN)
- Processor approves, charges 2–4% fee
- Settlement takes 2–3 business days
- You never see the actual QR code in the bot
AgentPay Approach (✅ Fast & Native)
- User clicks "Pay" in bot
- Bot generates VietQR code instantly, displayed inline
- User scans with any bank app (98% have VietQR support in VN)
- Money lands in your account within seconds to minutes
- 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
- VietQR + AgentPay = Native Vietnamese payments in your Telegram bot—no redirects, no middleman fees, no card friction.
- Three-line flow:
create_payment_request()→send checkout_url→await_settlement(). - Settlement is fast: typically 5–30 seconds; your bot can deliver goods/access almost instantly.
- It's open-source (MIT), meaning you own the integration and can audit it.
- Security: No sensitive payment data touches your server; the QR points to your bank's account directly.
- Scalable: Whether you're processing 10 or 1,000 payments/day, AgentPay just watches your bank feed.
Get Started Now
- Install:
pip install agentpay-vn - Grab your merchant credentials: Contact your bank's VietQR support
- Read the full docs: https://agentpay.servicesai.vn/v1/docs
- Check the GitHub repo for examples and community support: https://github.com/phuocdu/agentpay-vn
- 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.