Collect VietQR Payments in Telegram Bots with AgentPay
The Telegram Payment Problem You're Solving
Imagine you're running a Telegram bot that sells digital courses, coffee vouchers, or consulting services. Your customers are ready to pay—they're texting /buy, clicking buttons, asking "how much?"—but you're stuck. Telegram's native payment system requires a global payment processor (Stripe, PayPal), which isn't ideal for Vietnamese merchants. Your customers want to send money from their bank app using VietQR, the standard for Vietnamese banking. You want to receive it directly into your account without intermediaries.
This is where AgentPay VN changes the game. In this tutorial, you'll learn to build a Telegram payment flow that's native, transparent, and direct—no merchant accounts, no hidden fees, no third-party holding your money.
What Is AgentPay VN? The Five-Second Version
AgentPay VN is an open-source Python SDK (MIT license) that pairs with an MCP server, letting you generate VietQR payment requests and confirm settlements in real time. Here's the mental model:
- Your bot creates a payment request → AgentPay generates a VietQR code pointing to your bank account
- Customer scans & pays → Their bank app handles the transfer (not AgentPay)
- Settlement confirmed → Your bank sends a webhook; AgentPay notifies your bot
AgentPay never touches the money. It's a messenger, not a middleman. This is critical for trust and compliance in Vietnam.
Why Telegram + VietQR + AgentPay?
For Developers
- 3-line payment flow:
create_payment_request()→send(checkout_url)→await_settlement() - Open-source & auditable: MIT license on GitHub
- MCP integration: AI agents (Claude, etc.) can orchestrate payments natively
- No account signup: Point it at any VietBank, VCB, Agribank, etc.
For Vietnamese Users
- Instant, familiar payment UX: Every Vietnamese with a bank app knows VietQR
- Direct settlement: Money lands in your account 2–4 hours, no intermediary
- Cost-effective: No Stripe/PayPal fees (typically 2–4% in Vietnam)
Setup: Installation & Configuration (5 Minutes)
Step 1: Install the SDK
pip install agentpay-vn
Verify the install:
python -c "import agentpay; print(agentpay.__version__)"
Step 2: Gather Your Bank Details
You'll need:
- Bank account number (e.g., 0123456789)
- Bank code (e.g., 970402 for VCB)
- Account holder name (as registered with the bank)
- Business/person name (what appears on the QR)
For a complete bank code list and VietQR integration docs, see the official AgentPay docs.
Step 3: Configure Your Telegram Bot
Assuming you have a Telegram bot token (from @BotFather), create a .env file:
TELEGRAM_TOKEN=<your_bot_token>
BANK_ACCOUNT=0123456789
BANK_CODE=970402
BUSINESS_NAME=My Coffee Shop
Building Your First Telegram Payment Bot
The Complete Workflow
Let's build a bot that sells a $5 USD / ~120,000 VND digital product (an e-book, for example).
Code Block 1: Creating & Sending a Payment Request
import os
from dotenv import load_dotenv
from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
from agentpay import VietQRPaymentRequest, PaymentClient
load_dotenv()
# Initialize the payment client with your bank details
payment_client = PaymentClient(
bank_account=os.getenv("BANK_ACCOUNT"),
bank_code=os.getenv("BANK_CODE"),
account_holder=os.getenv("ACCOUNT_HOLDER"),
)
# Handle the /buy command
async def buy_ebook(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
price_vnd = 120000 # ~$5 USD in Vietnamese Dong
# Step 1: Create a payment request
# create_payment_request() returns a request object with amount, description, etc.
payment_request = payment_client.create_payment_request(
amount=price_vnd,
description=f"E-book Purchase for User {user_id}",
transaction_id=f"ebook_{user_id}_{int(time.time())}", # Unique reference
merchant_name=os.getenv("BUSINESS_NAME"),
)
# Step 2: Generate VietQR checkout URL
# This URL contains the encoded QR data; customers scan it with their bank app
checkout_url = payment_request.generate_checkout_url()
# Step 3: Send the QR to the user
keyboard = [
[InlineKeyboardButton(
"Pay 120,000 VND",
url=checkout_url
)],
[InlineKeyboardButton(
"Cancel",
callback_data="cancel"
)]
]
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
"📚 **E-Book: Python for Beginners**\n\n"
"Price: 120,000 VND\n\n"
"Click the button below to pay with your bank app. "
"You'll scan a QR code and confirm in seconds.",
reply_markup=reply_markup,
parse_mode="Markdown"
)
# Store the payment request for later verification
context.user_data["pending_payment"] = payment_request
# Run the bot
if __name__ == "__main__":
app = Application.builder().token(os.getenv("TELEGRAM_TOKEN")).build()
app.add_handler(CommandHandler("buy", buy_ebook))
app.run_polling()
Line-by-line breakdown:
- Lines 10–15: Initialize PaymentClient with your bank details (credentials from .env)
- Lines 19–32: When a user types /buy, create a VietQRPaymentRequest with the amount (120,000 VND), a unique transaction ID, and merchant name
- Line 35: generate_checkout_url() produces a URL with the encoded QR; the customer's bank app will parse it
- Lines 37–47: Build an inline keyboard with a "Pay" button linking to the checkout URL
- Line 56: Store the payment request object to verify settlement later
Code Block 2: Awaiting & Confirming Settlement
Now, the customer has paid. Your bot needs to confirm and deliver the e-book.
import asyncio
from agentpay import SettlementWatcher
# In your main bot handler, add a command to check payment status
async def check_payment(update: Update, context: ContextTypes.DEFAULT_TYPE):
user_id = update.effective_user.id
if "pending_payment" not in context.user_data:
await update.message.reply_text(
"❌ No pending payment. Use /buy to start."
)
return
payment_request = context.user_data["pending_payment"]
transaction_id = payment_request.transaction_id
# Step 1: Create a settlement watcher
# This polls your bank feed for confirmation that money arrived
watcher = SettlementWatcher(
payment_client=payment_client,
transaction_id=transaction_id,
timeout_seconds=3600 # Wait up to 1 hour (VietQR settles in 2–4 hours)
)
# Step 2: Await settlement (non-blocking)
# This returns True if the bank confirmed the payment, False if timeout
try:
is_settled = await watcher.await_settlement()
if is_settled:
# Payment confirmed! Deliver the product
await update.message.reply_text(
"✅ Payment confirmed! Your e-book is ready.\n\n"
"[Download E-Book](https://example.com/ebook.pdf)",
parse_mode="Markdown"
)
# Log the transaction and clean up
print(f"Payment settled for user {user_id}: {transaction_id}")
del context.user_data["pending_payment"]
else:
await update.message.reply_text(
"⏳ Payment not yet confirmed. Please try again in a few minutes.\n\n"
"VietQR settlements typically take 2–4 hours."
)
except Exception as e:
await update.message.reply_text(
f"⚠️ Error checking payment: {str(e)}\n\n"
"Please contact support."
)
app.add_handler(CommandHandler("check", check_payment))
Key points:
- Lines 14–21: Retrieve the pending payment request from user data
- Lines 23–28: Create a SettlementWatcher pointing to the transaction ID and bank account
- Lines 30–32: await_settlement() is async and non-blocking—your bot doesn't freeze while waiting
- Lines 34–44: Once settled, deliver the e-book and clean up the pending payment
- Lines 45–51: If settlement times out (rare), notify the user to retry
Real-World Example: The Telegram Café Bot
Imagine Trần runs a small café in Saigon and sells "coffee vouchers" via Telegram. Here's his workflow:
- Customer texts
/buy→ Bot shows "1 Latte Voucher: 50,000 VND" - Customer clicks "Pay" → Phone opens their VCB/VietinBank app with the VietQR code pre-filled
- Customer confirms payment → Money goes straight to Trần's café bank account
- VietQR settlement webhook fires → Bot's
SettlementWatcherdetects it (~5 mins, not 2–4 hours if Trần's bank provides a real-time feed) - Bot sends the voucher code → Customer redeems it at the café
No Stripe account. No PayPal fees. No delays. Trần's cash arrives in his account, and he's compliant with Vietnamese banking rules because the QR points directly to him.
MCP Server Integration: AI Agents Handling Payments
If you're using Claude or another AI agent framework, AgentPay provides an MCP server. This lets Claude directly call payment functions.
MCP Configuration (JSON)
Add this to your claude_desktop_config.json:
{
"mcpServers": {
"agentpay-vn": {
"command": "agentpay-mcp",
"env": {
"BANK_ACCOUNT": "0123456789",
"BANK_CODE": "970402",
"ACCOUNT_HOLDER": "Trần Văn A",
"BUSINESS_NAME": "Trần's Café"
}
}
}
}
Now Claude can:
- Understand user intent ("I want to buy a coffee voucher")
- Call create_payment_request() with the right amount
- Compose a payment message with the checkout URL
- Await settlement and trigger fulfillment
This is powerful for AI-driven customer service bots that handle the entire transaction autonomously.
Common Pitfalls & Best Practices
Do's ✅
| What | Why |
|---|---|
| Store payment requests in user context | Lets you correlate users with transactions later |
| Use unique transaction IDs | Prevents duplicate charges if the user retries |
| Implement timeout logic | VietQR can take 2–4 hours; don't leave the user hanging indefinitely |
| Verify bank feed | Check that your bank actually sends settlement webhooks; some banks are slower |
| Log all transactions | Audit trail for accounting and dispute resolution |
Don'ts ❌
| What | Why |
|---|---|
| Don't hold money in AgentPay | It doesn't—money goes straight to your bank. Period. |
| Don't assume instant settlement | VietQR is typically 2–4 hours, not real-time |
| Don't hardcode bank details | Use environment variables (.env file) for security |
| Don't ignore transaction IDs | You need unique IDs to reconcile payments with your records |
| Don't forget error handling | Network hiccups, timeouts, and bank delays happen |
Advanced Tips
1. Webhook Integration for Faster Confirmation
Instead of polling with SettlementWatcher, ask your bank for a webhook endpoint that fires when money settles. Some Vietnamese banks (VCB, VietinBank) support this. Your bot can then react instantly:
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.post("/webhook/settlement")
async def handle_settlement(payload: dict):
transaction_id = payload["transaction_id"]
amount = payload["amount"]
# Find the user by transaction_id and deliver the product
# Send a Telegram message: "✅ Payment confirmed!"
return {"status": "processed"}
2. Multi-Currency Support
If you sell to international customers, quote in USD but request payment in VND:
import requests
# Fetch current USD-to-VND rate
exchange_rate = requests.get(
"https://api.exchangerate-api.com/v4/latest/USD"
).json()["rates"]["VND"]
price_usd = 5
price_vnd = int(price_usd * exchange_rate) # ~120,000 VND
3. Refund Handling
If a customer disputes the transaction, use your bank's API to process refunds. AgentPay creates the request; your bank processes the transfer. Keep records in your bot's database for audit.
FAQ
Q: Will AgentPay hold my money?
A: No. AgentPay is purely a request/confirmation layer. Money flows directly from the customer's bank to your bank account. We never touch it.
Q: What if a customer's bank doesn't support VietQR?
A: Most Vietnamese banks launched VietQR support by 2023 (mandatory requirement). If a customer uses a regional bank without VietQR, they can't pay via this method—you'd need a fallback.
Q: How long does settlement take?
A: Typically 2–4 hours via standard VietQR channels. If your bank offers real-time webhooks, you can confirm in minutes.
Q: Can I use AgentPay with multiple banks?
A: Not in a single payment request yet, but you can run multiple bot instances with different bank accounts. The roadmap may add multi-account support.
Key Takeaways
- AgentPay VN is a lightweight, open-source Python SDK that generates VietQR payment requests and confirms settlements—no intermediary holding your money
- 3-line payment flow:
create_payment_request()→ send checkout URL →await_settlement() - Telegram bots become native payment terminals with zero Stripe/PayPal fees, aligned with Vietnamese banking norms
- Settlement confirmation can be polling-based (slower, simple) or webhook-based (faster, requires bank support)
- MCP integration lets AI agents orchestrate payments autonomously
- Always log transactions, use unique IDs, and handle timeouts gracefully
Get Started Now
- Install AgentPay VN:
pip install agentpay-vn - Read the full docs: https://agentpay.servicesai.vn/v1/docs
- Explore the code: GitHub – phuocdu/agentpay-vn
- Ask questions: Open an issue on GitHub or check the docs FAQ
Your Telegram payment bot is 30 minutes away. Go build it.