Collect VietQR Payments in Telegram Bots with AgentPay

2026-10-01 · AgentPay VN

telegramvietqrpythonpaymentsagentpay

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:

  1. Create a payment request with amount, description, and reference
  2. Generate a checkout URL containing an embedded VietQR code
  3. 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:

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:

  1. User types /buy in Telegram
  2. Bot calls create_payment_request() with the course price (299,000 VND)
  3. Bot sends an inline button linking to the checkout URL
  4. Customer clicks the button, scans the VietQR code, and pays from their bank app
  5. Meanwhile, the bot runs await_settlement() in the background
  6. 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_request to generate a payment. Send them the checkout_url. Then use await_settlement to 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

Get Started Now

Your Telegram payment bot is 20 minutes away:

  1. Install the SDK: pip install agentpay-vn
  2. Read the full docs: https://agentpay.servicesai.vn/v1/docs
  3. Clone example code: https://github.com/phuocdu/agentpay-vn
  4. Set your merchant credentials in .env
  5. Deploy your bot and start collecting VietQR payments

Questions? Check the GitHub issues or reach out to the community. Happy building! 🚀

Get started →

← All posts