cd /news/developer-tools/collect-vietqr-payments-in-telegram-… Β· home β€Ί topics β€Ί developer-tools β€Ί article
[ARTICLE Β· art-51876] src=dev.to β†— pub= topic=developer-tools verified=true sentiment=↑ positive

Collect VietQR Payments in Telegram Bots with AgentPay

AgentPay VN, an open-source Python SDK, enables Telegram bots to collect VietQR payments directly to a merchant's bank account without intermediaries. The MIT-licensed tool generates QR codes for instant peer-to-peer payments via Vietnamese banking apps, addressing high fees and integration complexity of traditional payment gateways.

read6 min views1 publishedJul 9, 2026

You've built a Telegram bot that sells digital courses, takes coffee orders, or offers freelance services. Traffic is flowing. But when your bot tries to collect payment, you hit a wall:

A founder we know spent two weeks wrestling with Stripe's Telegram integration, only to discover the monthly fee ate 40% of his margins on $2–5 transactions. He needed something lighter, faster, and truly peer-to-peer.

Enter AgentPay VN: an open-source Python SDK that lets your Telegram bot generate QR codes pointing directly to your bank account. No middleman. No holding funds. Just instant VietQR payments via the banking system Vietnameses already use daily.

AgentPay VN is an MIT-licensed Python SDK + MCP server that orchestrates VietQR payment requests. Here's the mental model:

The key: AgentPay never touches money. The QR points straight at your merchant bank account. A bank feed confirms settlement. You own the transaction end-to-end.

Telegram has 180+ million users, with especially strong adoption in Southeast Asia. Vietnamese merchants already use banking apps (MB Bank, Techcombank, VCB) that natively support VietQR scanning. Your bot becomes a natural extension of their daily workflow:

No authentication screens. No signup. Pure banking rails.

pip install agentpay-vn

Verify the installation:

python -c "import agentpay; print(agentpay.__version__)"

You'll need:

For testing, AgentPay provides a sandbox environment. Check the official docs for your bank.

export AGENTPAY_MERCHANT_ID="your_merchant_id"
export AGENTPAY_API_KEY="your_api_key"
export TELEGRAM_BOT_TOKEN="your_telegram_token"

Here's a minimal Telegram bot that collects a payment:

import os
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, CommandHandler, ContextTypes
from agentpay import AgentPayClient

agentpay_client = AgentPayClient(
    merchant_id=os.getenv("AGENTPAY_MERCHANT_ID"),
    api_key=os.getenv("AGENTPAY_API_KEY"),
)

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Send welcome message with a /buy command."""
    await update.message.reply_text(
        "πŸŽ‰ Welcome! Use /buy to purchase an item.\n"
        "Type /help for more info."
    )

async def buy(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Create a payment request and send checkout link."""
    user_id = update.effective_user.id

    payment_request = agentpay_client.create_payment_request(
        amount=50000,  # VND
        currency="VND",
        merchant_ref_id=f"order_{user_id}_{int(time.time())}",
        description="Premium Course - Python Mastery",
        metadata={"user_id": user_id, "product": "course_python"},
    )

    checkout_url = payment_request.get("checkout_url")
    payment_id = payment_request.get("payment_id")

    keyboard = InlineKeyboardMarkup([
        [InlineKeyboardButton("πŸ’³ Pay with VietQR", url=checkout_url)]
    ])

    await update.message.reply_text(
        f"πŸ“¦ Order Summary:\n"
        f"Product: Premium Course\n"
        f"Price: 50,000 VND\n\n"
        f"Tap the button below to pay with your banking app.",
        reply_markup=keyboard
    )

    context.user_data["pending_payment_id"] = payment_id

async def check_payment(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
    """Poll for payment settlement (in production, use webhooks)."""
    payment_id = context.user_data.get("pending_payment_id")

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

    status = agentpay_client.await_settlement(
        payment_id=payment_id,
        timeout=300,  # Wait max 5 minutes
    )

    if status["settled"]:
        await update.message.reply_text(
            f"βœ… Payment confirmed!\n"
            f"Amount: {status['amount']:,} VND\n"
            f"Your premium course access is now active.\n"
            f"πŸ“š [Open Course](https://example.com/course)"
        )
    else:
        await update.message.reply_text(
            "⏳ Payment not yet confirmed. Try again in a moment."
        )

def main():
    """Start the bot."""
    app = Application.builder().token(
        os.getenv("TELEGRAM_BOT_TOKEN")
    ).build()

    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("buy", buy))
    app.add_handler(CommandHandler("check", check_payment))

    print("πŸ€– Bot running...")
    app.run_polling()

if __name__ == "__main__":
    import time
    main()

Line-by-line breakdown:

/start

sends a welcome message/buy

creates a payment request with amount, reference ID, and metadata/check

polls for settlement status (use webhooks in production!)Polling is fine for demos, but slow and unreliable. Use bank webhooks to get instant payment confirmation:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route("/webhook/agentpay", methods=["POST"])
def handle_payment_webhook():
    """AgentPay calls this when payment settles."""
    payload = request.json

    if not agentpay_client.verify_webhook_signature(
        payload, request.headers.get("X-Signature")
    ):
        return {"error": "Invalid signature"}, 401

    payment_id = payload["payment_id"]
    status = payload["status"]  # "settled" or "failed"
    amount = payload["amount"]
    merchant_ref_id = payload["merchant_ref_id"]

    if status == "settled":
        user_id = int(merchant_ref_id.split("_")[1])

        grant_access(user_id)

        asyncio.run(
            send_telegram_message(
                user_id,
                f"βœ… Payment received! ({amount:,} VND)\n"
                "Your access is active."
            )
        )

    return {"status": "ok"}, 200

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Configure your webhook URL in the AgentPay dashboard to https://yourdomain.com/webhook/agentpay

.

If you're using Claude as your AI agent orchestrator, install the MCP server:

pip install agentpay-mcp

Configure Claude's MCP settings:

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "env": {
        "AGENTPAY_MERCHANT_ID": "your_merchant_id",
        "AGENTPAY_API_KEY": "your_api_key"
      }
    }
  }
}

Now Claude can call AgentPay directly:

User: "I want to sell a course for 200,000 VND."
Claude: I'll create a payment request for you.
[Calls agentpay.create_payment_request()]
Claude: Payment link: https://checkout.agentpay.vn/...

Imagine a cafΓ© selling espresso shots via Telegram:

/order

β†’ "1x Espresso (40,000 VND)"The entire flow takes 30 seconds. No payment processor fees. No settlement delays.

Always include a unique merchant_ref_id

to prevent duplicate charges if the network retries:

import uuid

merchant_ref_id = f"order_{user_id}_{uuid.uuid4()}"

Not all webhooks arrive instantly. Implement retry logic:

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(5), wait=wait_exponential())
async def notify_user_via_telegram(user_id, message):
    await bot.send_message(user_id, message)

Use a database (PostgreSQL, MongoDB) to track payment status:

class PaymentRecord(db.Model):
    payment_id = db.Column(db.String, primary_key=True)
    user_id = db.Column(db.Integer)
    amount = db.Column(db.Integer)  # VND
    status = db.Column(db.String)  # pending, settled, failed
    created_at = db.Column(db.DateTime, default=datetime.now)

AgentPay provides a sandbox environment. Always test before going live:

agentpay_client = AgentPayClient(
    merchant_id="sandbox_merchant_id",
    api_key="sandbox_api_key",
    environment="sandbox",  # Switch to "production" later
)
βœ… Do ❌ Don't
Use webhook callbacks for payment confirmation Poll the API in a tight loop
Store merchant_ref_id; match it in webhook Trust the order of webhook events
Verify webhook signatures Skip signature verification
Implement idempotent request IDs Assume network calls always succeed
Start with sandbox environment Go live without testing
Monitor settlement times Assume instant settlement (usually seconds, but be safe)
Log all payment events Ignore payment failures silently

Q: Can AgentPay handle payments in other currencies (USD, etc.)?

A: Currently, AgentPay VN is designed for VND (Vietnamese Dong) via VietQR, which is a Vietnamese banking standard. International currency support would require additional bank partnerships.

Q: What if the customer's bank doesn't support VietQR?

A: Most Vietnamese banks (MB, Vietcombank, Techcombank, ACB, VP Bank, etc.) support VietQR. If a customer's bank doesn't, they can ask their bank to enable it, or you can provide a fallback (e.g., direct bank transfer details).

Q: How much does AgentPay cost?

A: AgentPay itself is free (MIT open-source). You pay your bank's standard VietQR transaction fees (typically 0–0.5%), which is much cheaper than Stripe (2.9% + $0.30) or PayPal (3.5%).

Q: How long does settlement take?

A: Usually 1–5 seconds. The money goes directly to your bank account, so it's as fast as an interbank transfer. Some banks may batch settlements end-of-day, but that's rare.

pip install agentpay-vn

and get your first payment in under 10 minutespip install agentpay-vn

Your Telegram payment bot is minutes away. No more payment processor headaches. Just you, your customers, and the Vietnamese banking system doing what it does best.

── more in #developer-tools 4 stories Β· sorted by recency
── more on @agentpay vn 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/collect-vietqr-payme…] indexed:0 read:6min 2026-07-09 Β· β€”