Collect VietQR Payments in Telegram Bots with AgentPay
The Problem: Why Telegram Bots Struggle with Payments
You've built a Telegram bot that sells course access, café reservations, or digital products. Users type /buy, and you send them a price quote. But then what? A PayPal link feels foreign to Vietnamese users. Bank transfers require manual verification. Your bot sits there, unable to close the sale.
This is the payment gap that kills 40% of Telegram commerce in Southeast Asia. Users want frictionless checkout inside their chat app, and merchants want instant settlement without holding customer money.
AgentPay VN solves this by letting your Telegram bot generate a VietQR code that points directly to the merchant's bank account. No middleman. No escrow. The customer scans, pays, and your bot gets a settlement confirmation in seconds.
Why VietQR + Telegram = Perfect Match
VietQR is Vietnam's unified QR payment standard. It's backed by every major bank—Vietcombank, Techcombank, MB Bank, ACB—and 90% of Vietnamese smartphone users already trust it. When you send a VietQR code in Telegram, customers recognize it immediately and know exactly what happens when they scan.
AgentPay VN is a lightweight, MIT-licensed Python SDK that handles the entire workflow:
- Create a payment request with amount, description, and reference
- Generate a checkout URL containing an embedded VietQR code
- Wait for settlement confirmation from your bank feed
Crucially: AgentPay VN never touches your money. The QR code points straight at your merchant bank account. A bank feed API confirms when the payment clears. Your bot remains a pure payment orchestrator.
Installation & Setup (5 Minutes)
Step 1: Install the SDK
pip install agentpay-vn
This installs the core Python library. Verify it works:
python -c "import agentpay_vn; print(agentpay_vn.__version__)"
Step 2: Set Up Your Merchant Account
You'll need: - A Vietnamese business bank account (or personal account) - Your bank's merchant ID (provided when you register for VietQR) - API credentials from your bank's developer portal
Store these securely in environment variables:
export AGENTPAY_MERCHANT_ID="your_merchant_id"
export AGENTPAY_BANK_API_KEY="your_bank_api_key"
export AGENTPAY_WEBHOOK_SECRET="your_webhook_secret"
Step 3: Install the MCP Server (for AI Agent Integration)
If you're running Claude or another AI agent that needs to process payments autonomously:
pip install agentpay-mcp
This exposes AgentPay functions as an MCP (Model Context Protocol) server. We'll cover this in the advanced section.
The 3-Line Payment Flow
At its core, AgentPay VN operates in three steps:
from agentpay_vn import create_payment_request, await_settlement
# Step 1: Create a payment request
payment = create_payment_request(
amount=150000, # 150,000 VND
description="Online Python Course Access",
reference_id="user_12345_course_001",
merchant_id="MERCHANT_ABC123"
)
# Returns: {"id": "pay_xyz789", "checkout_url": "https://...", "qr_code": "..."}
# Step 2: Send checkout_url to customer (via Telegram, email, etc.)
checkout_url = payment["checkout_url"]
print(f"Pay here: {checkout_url}")
# Step 3: Wait for settlement
settlement = await_settlement(
payment_id=payment["id"],
timeout_seconds=300 # Wait up to 5 minutes
)
# Returns: {"status": "settled", "amount": 150000, "timestamp": "2024-..."}
Line-by-line explanation:
- Line 1-2: Import the two core functions
- Line 5-11:
create_payment_request()generates a unique payment with a VietQR code. Thereference_idties this payment to your user/order in your database - Line 14: Extract the checkout URL (a short link the customer can click or scan a QR code from)
- Line 17-20:
await_settlement()blocks until the bank confirms payment. If payment arrives,statusreturns"settled". If timeout, it returns"timeout"
Real-World Walkthrough: A Course-Selling Telegram Bot
Let's build a practical example: a bot that sells access to a Python course.
import logging
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, CallbackContext
from agentpay_vn import create_payment_request, await_settlement
import os
import asyncio
from datetime import datetime
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
COURSE_PRICE = 299000 # VND
MERCHANT_ID = os.getenv("AGENTPAY_MERCHANT_ID")
async def start(update: Update, context: CallbackContext):
"""Handle /start command"""
await update.message.reply_text(
"Welcome to Python Mastery Course! 🐍\n"
"Type /buy to purchase lifetime access for 299,000 VND"
)
async def buy(update: Update, context: CallbackContext):
"""Handle /buy command - create payment request"""
user_id = update.effective_user.id
# Step 1: Create payment request
payment = create_payment_request(
amount=COURSE_PRICE,
description="Python Mastery Course - Lifetime Access",
reference_id=f"user_{user_id}_course_{int(datetime.now().timestamp())}",
merchant_id=MERCHANT_ID
)
checkout_url = payment["checkout_url"]
payment_id = payment["id"]
# Send checkout button
keyboard = InlineKeyboardButton(
text="💳 Pay with VietQR",
url=checkout_url
)
reply_markup = InlineKeyboardMarkup([[keyboard]])
await update.message.reply_text(
f"🎓 **Python Mastery Course**\n"
f"Price: 299,000 VND\n"
f"Click the button below to scan and pay:\n",
reply_markup=reply_markup,
parse_mode="Markdown"
)
# Store payment_id in context for later verification
context.user_data["current_payment_id"] = payment_id
context.user_data["user_id"] = user_id
# Step 2: Wait for settlement in background
asyncio.create_task(
verify_payment_background(payment_id, user_id, context.bot)
)
async def verify_payment_background(payment_id: str, user_id: int, bot):
"""Background task to wait for payment settlement"""
try:
settlement = await_settlement(
payment_id=payment_id,
timeout_seconds=600 # Wait 10 minutes
)
if settlement["status"] == "settled":
# Payment successful - grant course access
await bot.send_message(
chat_id=user_id,
text="✅ Payment confirmed! Your course access is active.\n"
"Check your email for the course link and password."
)
# TODO: In your app, mark user as paid in database
logger.info(f"Course sold to user {user_id} for {settlement['amount']} VND")
else:
await bot.send_message(
chat_id=user_id,
text="⏱️ Payment timeout. Please try again with /buy"
)
except Exception as e:
logger.error(f"Payment verification failed: {e}")
await bot.send_message(
chat_id=user_id,
text="❌ Error verifying payment. Contact support."
)
async def main():
"""Start the bot"""
token = os.getenv("TELEGRAM_BOT_TOKEN")
app = Application.builder().token(token).build()
app.add_handler(CommandHandler("start", start))
app.add_handler(CommandHandler("buy", buy))
await app.run_polling()
if __name__ == "__main__":
asyncio.run(main())
How this works:
- User types
/buyin Telegram - Bot calls
create_payment_request()with the course price (299,000 VND) - Bot sends an inline button linking to the checkout URL
- Customer clicks the button, scans the VietQR code, and pays from their bank app
- Meanwhile, the bot runs
await_settlement()in the background - When the bank confirms payment, the bot sends a success message and grants access
AI Agent Integration with MCP Server
If you're running Claude or another LLM agent that needs to autonomously handle payments, use the MCP server.
MCP Configuration (for Claude):
{
"mcpServers": {
"agentpay": {
"command": "python",
"args": ["-m", "agentpay_mcp.server"],
"env": {
"AGENTPAY_MERCHANT_ID": "MERCHANT_ABC123",
"AGENTPAY_BANK_API_KEY": "sk_live_...",
"AGENTPAY_WEBHOOK_SECRET": "whsec_..."
}
}
}
}
Once configured, Claude can:
- Call create_payment_request to generate QR codes
- Call await_settlement to check payment status
- Call cancel_payment_request to void unpaid requests
Example agent prompt:
"If a customer asks to buy the premium plan for 500,000 VND, use
create_payment_requestto generate a payment. Send them the checkout_url. Then useawait_settlementto wait for confirmation. When confirmed, reply that their premium access is active."
Common Pitfalls & Best Practices
| Do | Don't |
|---|---|
Store payment_id + reference_id in your database to reconcile payments |
Rely solely on Telegram messages for payment records |
Use unique reference_id per payment (include timestamp or UUID) |
Reuse reference IDs across multiple payments |
Set a reasonable timeout_seconds (300-600) and handle timeouts gracefully |
Wait indefinitely without a timeout |
| Log all payment events for debugging and compliance | Ignore failed settlement attempts |
| Test with small amounts first (e.g., 1,000 VND) | Go live with untested code |
| Verify settlement status before granting access | Grant access immediately after user clicks checkout |
Advanced Tips
Webhook Integration for Real-Time Settlements
Instead of polling with await_settlement(), register a webhook with your bank:
from agentpay_vn import register_webhook
register_webhook(
url="https://yourdomain.com/webhook/payment-settled",
events=["payment.settled", "payment.failed"],
webhook_secret="whsec_your_secret"
)
Your endpoint receives real-time notifications, so your bot responds instantly.
Handling Partial Payments
If a customer pays less than the requested amount, settlement["status"] returns "partial". Decide whether to accept or refund.
Multi-Currency Support
AgentPay VN works in VND only (VietQR standard). For international customers, consider a separate Stripe integration.
FAQ
Q: Does AgentPay VN take a commission? A: No. AgentPay VN is open-source (MIT license). Your only cost is your bank's VietQR fees, typically 0.5–1% per transaction.
Q: What if a customer pays the wrong amount?
A: settlement["amount"] tells you exactly what arrived. You can reject amounts that don't match and refund through your bank dashboard.
Q: Can I refund a payment? A: No—AgentPay VN doesn't hold money, so refunds happen through your bank's normal process. You initiate a transfer from your merchant account back to the customer.
Q: Is my Telegram bot's source code exposed?
A: Only if you commit .env files with secrets. Always use environment variables and .gitignore sensitive data.
Key Takeaways
- AgentPay VN is a lightweight Python SDK for collecting VietQR payments—no money ever touches AgentPay
- 3-line flow:
create_payment_request()→ send checkout URL →await_settlement() - Zero setup friction: Install with
pip install agentpay-vn, set 3 environment variables, you're ready - Telegram bots are a natural use case—VietQR codes feel native to Vietnamese users
- MCP server lets AI agents autonomously handle payments
- Bank feed confirmation is final; no chargebacks once marked settled
- Test small amounts first before scaling to production
Get Started Now
Your Telegram payment bot is 20 minutes away:
- Install the SDK:
pip install agentpay-vn - Read the full docs: https://agentpay.servicesai.vn/v1/docs
- Clone example code: https://github.com/phuocdu/agentpay-vn
- Set your merchant credentials in
.env - Deploy your bot and start collecting VietQR payments
Questions? Check the GitHub issues or reach out to the community. Happy building! 🚀