Collect VietQR Payments in Telegram Bots with AgentPay

2026-07-10 · AgentPay VN

telegramvietqrpythonpaymentsagentpay

The Problem: Manual Payment Friction in Telegram Bots

Imagine you're running an online course business via Telegram. A student wants to enroll in your Python masterclass—$50 course fee. Today, you're sending them a PayPal link, a bank transfer form, or worse, asking for manual payment confirmation. They drop out halfway through checkout. By the time they've copied account numbers and sent a screenshot, the friction has killed the sale.

Or you're a café owner with a delivery bot. Customers order coffee, but payment collection is either impossible (you have no payment gateway) or clunky (third-party checkout that breaks the flow).

This is the gap AgentPay VN closes: native, frictionless VietQR payment collection directly inside Telegram, with zero setup on the merchant's end and zero money touching your platform.

What is AgentPay VN?

AgentPay VN is an open-source (MIT license) Python SDK that lets AI agents and applications collect payments via VietQR—Vietnam's interbank QR standard. The key insight: AgentPay never holds funds. The QR code points directly to your merchant bank account. A bank feed confirms settlement in real time. You build the bot; your bank does the heavy lifting.

Two deployment options:

  1. Python SDK (pip install agentpay-vn): Direct integration into your bot code.
  2. MCP Server (agentpay-mcp): Claude or other AI agents invoke payment functions as tools.

For a Telegram bot, you'll typically use the Python SDK directly, with optional MCP integration if you're running Claude as your bot brain.

Setting Up Your Telegram Bot with AgentPay

Prerequisites

Step 1: Install Dependencies

pip install agentpay-vn python-telegram-bot

This installs: - agentpay-vn: Payment request generation, settlement confirmation. - python-telegram-bot: Telegram API bindings.

Step 2: Create Your First Payment Request

Here's a minimal Python bot that accepts a payment via VietQR:

from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, ContextTypes
from agentpay_vn import create_payment_request, await_settlement
import uuid

# Initialize your Telegram bot
TELEGRAM_TOKEN = "your_telegram_bot_token_here"
MERCHANT_ACCOUNT = "0123456789"  # Your VietQR-enabled bank account
MERCHANT_NAME = "My Telegram Store"

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Send a welcome message with a payment button."""
    await update.message.reply_text(
        "Welcome! 🎉 I sell premium tutorials for $10 each.\n"
        "Click the button below to pay via VietQR."
    )

async def buy_tutorial(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Create a payment request and send the checkout URL."""

    # Step 1: Generate a unique payment request
    payment_id = str(uuid.uuid4())
    amount = 10  # USD equivalent; AgentPay handles conversion
    description = "Premium Python Tutorial Access"

    # Create the payment request (this generates a VietQR string)
    payment_request = create_payment_request(
        merchant_account=MERCHANT_ACCOUNT,
        merchant_name=MERCHANT_NAME,
        amount=amount,
        description=description,
        payment_id=payment_id,
        currency="USD"  # or "VND" if you prefer
    )

    # Step 2: Generate checkout URL (contains the QR or deep link)
    checkout_url = payment_request["checkout_url"]
    qr_image_url = payment_request["qr_image_url"]

    # Step 3: Send to user
    keyboard = [
        [InlineKeyboardButton("Pay Now", url=checkout_url)],
        [InlineKeyboardButton("Show QR Code", callback_data=f"qr_{payment_id}")]
    ]
    reply_markup = InlineKeyboardMarkup(keyboard)

    await update.message.reply_text(
        f"📱 Scan or tap to pay {amount} USD for tutorial access:\n\n"
        f"Transaction ID: {payment_id}",
        reply_markup=reply_markup
    )

    # Store payment_id in context for later confirmation
    context.user_data['pending_payment'] = payment_id

async def confirm_payment(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Poll the bank feed to check if payment settled."""
    payment_id = context.user_data.get('pending_payment')

    if not payment_id:
        await update.message.reply_text("❌ No pending payment found.")
        return

    # Step 4: Wait for settlement confirmation from the bank
    settlement_data = await_settlement(
        payment_id=payment_id,
        timeout_seconds=300  # Poll for up to 5 minutes
    )

    if settlement_data['settled']:
        user_id = update.message.from_user.id
        # Grant access (e.g., send PDF, unlock content)
        await update.message.reply_text(
            f"✅ Payment confirmed! Amount: {settlement_data['amount']} VND\n"
            f"Access link: [Download Tutorial](https://your-course-link.com)"
        )
        # Log transaction
        print(f"User {user_id}: {settlement_data}")
    else:
        await update.message.reply_text(
            "⏳ Payment still pending. Check your bank app or try again in 2 minutes."
        )

if __name__ == "__main__":
    app = Application.builder().token(TELEGRAM_TOKEN).build()

    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("buy", buy_tutorial))
    app.add_handler(CommandHandler("confirm", confirm_payment))

    app.run_polling()

Line-by-line explanation:

Real-World Walkthrough: Online Course Bot

Let's say you're selling a 4-week "Build with Claude" course for 500,000 VND (~$20 USD):

  1. User sends /buy → Bot generates a payment request with amount=500000, currency="VND".
  2. User taps "Pay Now" → They're taken to their default VietQR app (Vietcombank, Agribank, Momo, etc.) with the QR pre-filled.
  3. They authorize in 3 seconds → Bank debits their account, credits yours.
  4. Bot polls the bank feed → Within 10–30 seconds, AgentPay confirms settlement.
  5. Bot unlocks course → Sends access link, lesson 1 PDF, and Discord invite.

No Stripe account. No PayPal hold. No payment gateway fees (beyond your bank's standard transfer fee, typically 0–0.5% VND). Pure direct-to-bank flow.

MCP Integration for AI Agents

If you're building a multi-agent system where Claude or another LLM orchestrates your bot, use AgentPay's MCP server:

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "env": {
        "AGENTPAY_MERCHANT_ACCOUNT": "0123456789",
        "AGENTPAY_MERCHANT_NAME": "My Telegram Store",
        "AGENTPAY_API_KEY": "your_api_key_here"
      }
    }
  }
}

Now Claude can invoke payment functions as tools:

User: "I want to buy the premium tutorial."
Claude (via MCP): agentpay:create_payment_request(amount=500000, currency="VND")
Claude: "Here's your payment link: [link]. Once paid, I'll unlock your content."

Advanced Tips

Idempotency & Duplicate Prevention

Always use a unique payment_id (UUID or timestamp + user ID). If the same user clicks "Pay" twice, reuse the same payment_id. AgentPay deduplicates on the bank feed, so you won't double-charge.

Graceful Timeout Handling

Bank feeds can take 10–60 seconds depending on the issuing bank. Set timeout_seconds generously:

# Poll for up to 2 minutes; don't block the user
await_settlement(payment_id=payment_id, timeout_seconds=120)

Alternatively, send a "Payment pending" message and ask them to check back later.

Store Metadata

Associate each payment with user data:

# Before creating the payment request
user_id = update.message.from_user.id
user_phone = update.message.from_user.phone_number

payment_request = create_payment_request(
    merchant_account=MERCHANT_ACCOUNT,
    amount=10,
    payment_id=f"{user_id}_{uuid.uuid4()}",
    metadata={"user_id": user_id, "phone": user_phone, "product": "tutorial"}
)

Retrieve metadata when settlement confirms, so you know exactly who paid for what.

Multi-Currency Support

AgentPay auto-converts USD/EUR to VND at market rates. Test with:

# Charge $10 USD
payment_request = create_payment_request(
    amount=10,
    currency="USD",
    ...
)

# Or charge 250,000 VND directly
payment_request = create_payment_request(
    amount=250000,
    currency="VND",
    ...
)

Do's and Don'ts

Do Don't
Use HTTPS for all URLs Hardcode merchant account in repo
Store payment_id in a database Reuse the same payment_id for multiple users
Poll settlement asynchronously (don't block) Charge the user before settlement confirms
Log every transaction for audit Expose API keys in Telegram messages
Set reasonable timeouts (60–120s) Assume instant settlement (takes 10–60s)
Test with small amounts first ($1 USD) Use production merchant account before testing

FAQ

Q: Does AgentPay charge a fee? A: No. AgentPay is open-source (MIT). You only pay your bank's standard wire/transfer fees (typically 0–0.5% in Vietnam).

Q: What if a user scans the QR but cancels payment? A: await_settlement() will timeout and return settled=False. Send a friendly "Payment not received" message; they can retry.

Q: Can I use AgentPay outside Vietnam? A: VietQR is Vietnam-specific, so the merchant account must be at a Vietnamese bank. However, users can pay from international cards via some banks' apps.

Q: Is settlement guaranteed? A: Yes. The bank feed is the source of truth. Once await_settlement() returns settled=True, the money is in your account.

Key Takeaways

Getting Started Now

  1. Install AgentPay VN: bash pip install agentpay-vn

  2. Read the full docs: https://agentpay.servicesai.vn/v1/docs

  3. Clone the GitHub repo for examples: https://github.com/phuocdu/agentpay-vn

  4. Test with a small transaction (e.g., 10,000 VND / ~$0.40 USD) to confirm your merchant account and Telegram bot work end-to-end.

Your Telegram users deserve frictionless payments. AgentPay VN makes that possible—no gateway, no middleman, no delays. Start collecting payments in minutes.

Get started →

← All posts