DEV Community

ServicesAI VN
ServicesAI VN

Posted on Originally published at agentpay.servicesai.vn

Collect VietQR Payments in Telegram Bots with AgentPay

The Problem: Selling Digital Products Without a Payment Gateway

You've built a Telegram bot that offers something valuable—an online course, design templates, or consulting services. Every day, customers ask how to pay. Your options feel limited: manually send a bank transfer request, ask for a mess of screenshots, or integrate with a complex payment processor that takes 2–5% and demands endless documentation.

Meanwhile, in Vietnam, VietQR is everywhere. Your customers want to scan a QR code and send money directly to your bank account. No middleman. No waiting. But wiring that into a Telegram bot? That's felt like a custom development nightmare—until now.

AgentPay VN changes this. In under 50 lines of Python, you can turn your Telegram bot into a payment collector that generates VietQR codes, sends checkout links, and confirms settlement directly from your bank feed. No holding customer funds. No complex integrations. Just Python, a Telegram bot, and your bank account.

Let's build it.

What is AgentPay VN and Why It Matters

AgentPay VN is an open-source MIT-licensed Python SDK plus an MCP server that lets AI agents (and your own bots) collect VietQR payments. Here's what makes it different:

  • No escrow or balance: Money flows directly to your merchant bank account. AgentPay never holds a single dong.
  • Bank-feed confirmation: Settlement status comes from your actual bank, not a third-party API.
  • Designed for agents: Built for Claude, custom LLMs, and traditional bot scripts—works everywhere.
  • MIT open-source: Full transparency, no vendor lock-in.
  • Simple 3-step flow: Create a payment request → send a checkout URL → wait for settlement confirmation.

If you're selling in Vietnam and want frictionless payments without fees eating your margin, this is it.

Installation and Initial Setup

Step 1: Install the SDK

pip install agentpay-vn
Enter fullscreen mode Exit fullscreen mode

That's it. The package includes everything you need to create payment requests and monitor settlements.

Step 2: Set Your Merchant Bank Details

You'll need:

  • Bank account number (your business or personal account)
  • Bank code (e.g., 970436 for Techcombank, 970422 for Vietcombank)
  • Account holder name (exactly as registered at the bank)
  • Amount to collect (in VND)

Store these securely. AgentPay uses them to generate VietQR codes that point directly to your bank account.

Step 3: Verify Bank Feed Access (Optional but Recommended)

For automatic settlement confirmation, AgentPay can listen to your bank feed. This requires:

  • Open banking credentials (varies by bank)
  • Or manual webhook setup from your bank's API portal

Check the docs for your specific bank.

Building a Telegram Payment Bot: Complete Walkthrough

Real Scenario: A Course-Selling Bot

Imagine you're selling a 6-week Python course for ₫299,000. You want a Telegram bot where users:

  1. Click /buy_course
  2. Receive a VietQR checkout link
  3. Scan and pay
  4. Get course access automatically when settled

Here's how to build it with AgentPay and python-telegram-bot:

Code Block 1: Create a Payment Request

from telegram import Update
from telegram.ext import Application, CommandHandler, ContextTypes
from agentpay_vn import PaymentRequest, AgentPayClient
import uuid

# Initialize AgentPay with your merchant details
client = AgentPayClient(
    bank_account="1234567890",
    bank_code="970436",  # Techcombank
    account_holder="John Nguyen",
)

# Initialize Telegram bot
app = Application.builder().token("YOUR_TELEGRAM_TOKEN").build()

async def handle_buy_course(update: Update, context: ContextTypes.DEFAULT_TYPE):
    """
    User triggers /buy_course. We create a payment request and send checkout.
    """
    user_id = update.effective_user.id

    # Create a unique payment request
    payment_request = PaymentRequest(
        order_id=f"course_{user_id}_{uuid.uuid4().hex[:8]}",
        amount_vnd=299000,
        description="6-Week Python Mastery Course",
        metadata={"user_id": user_id, "product": "python_course"},
    )

    # Generate VietQR checkout URL
    checkout_url = await client.create_payment_request(payment_request)

    # Send to user with a nice button
    await update.message.reply_text(
        f"🎓 **Python Course - ₫299,000**\n\n"
        f"Ready to level up? Click below to pay securely via VietQR.\n\n",
        reply_markup={
            "inline_keyboard": [
                [{"text": "💳 Pay Now", "url": checkout_url}]
            ]
        },
        parse_mode="Markdown"
    )

    # Store the order_id in context for settlement tracking
    context.user_data["pending_order_id"] = payment_request.order_id

app.add_handler(CommandHandler("buy_course", handle_buy_course))
Enter fullscreen mode Exit fullscreen mode

Line-by-line breakdown:

  1. Lines 6–11: Initialize AgentPayClient with your bank details. These credentials are used to generate VietQR codes pointing to your account.
  2. Line 18: Extract the user's Telegram ID—useful for tracking purchases per user.
  3. Lines 21–27: Create a PaymentRequest object with a unique order ID, the amount (₫299,000), a description, and metadata (we'll use this later to grant access).
  4. Line 30: Call create_payment_request() to generate the VietQR checkout URL. This is a fully formed link with the QR embedded.
  5. Lines 33–40: Send the checkout URL as an inline button. The user taps it, scans the QR, and pays from their banking app.
  6. Line 44: Store the order ID so we can track settlement later.

Code Block 2: Monitor Settlement and Grant Access

import asyncio
from agentpay_vn import SettlementChecker

# Initialize settlement checker (uses bank feed)
settlement_checker = SettlementChecker(
    bank_account="1234567890",
    bank_code="970436",
    # Bank feed credentials or webhook endpoint (see docs)
)

async def monitor_settlement(user_id: int, order_id: str, context: ContextTypes.DEFAULT_TYPE):
    """
    Poll for settlement confirmation, then grant course access.
    """
    max_wait_seconds = 600  # Wait up to 10 minutes
    check_interval = 5     # Check every 5 seconds
    elapsed = 0

    while elapsed < max_wait_seconds:
        # Check if payment settled from bank feed
        settlement_status = await settlement_checker.await_settlement(
            order_id=order_id,
            amount_vnd=299000,
            timeout_seconds=5,
        )

        if settlement_status.is_settled:
            # Payment confirmed! Grant access.
            await context.bot.send_message(
                chat_id=user_id,
                text=(
                    "✅ **Payment Received!**\n\n"
                    "Welcome to the 6-Week Python Course. Your course access is active.\n\n"
                    "📚 Start here: <a href="https://yoursite.com/course">Course Dashboard</a>\n"
                ),
                parse_mode="Markdown"
            )

            # Log success (you'd typically save to DB)
            print(f"User {user_id} paid for course. Order: {order_id}")
            return True

        # Not yet settled; wait and retry
        await asyncio.sleep(check_interval)
        elapsed += check_interval

    # Timeout: payment didn't come through
    await context.bot.send_message(
        chat_id=user_id,
        text="⏱️ Payment not received after 10 minutes. Please try again."
    )
    return False

# Attach to the payment creation flow:
async def handle_buy_course(update: Update, context: ContextTypes.DEFAULT_TYPE):
    # ... [payment request code from above] ...

    # Start settlement monitoring in background
    asyncio.create_task(
        monitor_settlement(
            user_id=update.effective_user.id,
            order_id=payment_request.order_id,
            context=context
        )
    )
Enter fullscreen mode Exit fullscreen mode

What's happening here:

  1. Lines 4–8: Create a SettlementChecker tied to your bank. This queries your actual bank account to confirm money arrived.
  2. Lines 10–45: Define monitor_settlement(). It polls the bank feed every 5 seconds for up to 10 minutes.
  3. Lines 16–22: Call await_settlement() with the order ID and amount. AgentPay checks your bank feed for a matching transaction.
  4. Lines 24–33: If settled, send a confirmation message and grant access (in real apps, you'd update your DB).
  5. Lines 42–48: If timeout occurs (no payment after 10 minutes), remind the user to try again.

The key insight: AgentPay never holds the money. It's simply confirming that your bank received it.

Setting Up AgentPay with MCP Servers (For AI Agents)

If you're building AI agents that handle payments (e.g., Claude + AgentPay), use the MCP server:

Install the MCP Server

pip install agentpay-mcp
Enter fullscreen mode Exit fullscreen mode

MCP Configuration for Claude (Claude.json)

{
  "mcpServers": {
    "agentpay": {
      "command": "python",
      "args": ["-m", "agentpay_mcp"],
      "env": {
        "AGENTPAY_BANK_ACCOUNT": "1234567890",
        "AGENTPAY_BANK_CODE": "970436",
        "AGENTPAY_ACCOUNT_HOLDER": "John Nguyen",
        "AGENTPAY_BANK_FEED_KEY": "your_bank_feed_api_key"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Once configured, Claude can:

  • Call agentpay:create_payment_request to generate checkout links
  • Call agentpay:check_settlement to confirm payments
  • Automate refunds or adjustments

This is powerful for agentic workflows where you want Claude to handle payments autonomously.

Best Practices and Do's/Don'ts

Do's and Don'ts

Do Don't
Store order_id in your database to reconcile Rely on user's word that they paid
Use bank feed for settlement confirmation Trust Telegram notifications alone
Set timeouts (5–10 min) for settlement checks Loop forever if payment doesn't arrive
Log all transactions (order_id, user_id, amount) Forget to track who paid for what
Regenerate QR for retry (new order_id) Reuse QR codes or send identical links
Keep bank credentials in environment variables Hardcode credentials in your repo

Advanced Tip: Webhook-Based Settlement

Instead of polling, configure your bank to send webhooks when money arrives:

from fastapi import FastAPI, HTTPException

app = FastAPI()

@app.post("/webhook/bank-settlement")
async def handle_bank_webhook(payload: dict):
    """
    Your bank sends a webhook when transaction arrives.
    Payload includes order_id, amount, timestamp.
    """
    order_id = payload.get("order_id")
    amount = payload.get("amount")

    # Verify signature (varies by bank)
    if not verify_bank_signature(payload):
        raise HTTPException(status_code=403, detail="Invalid signature")

    # Grant access, send confirmation, etc.
    await grant_course_access(order_id)
    return {"status": "success"}
Enter fullscreen mode Exit fullscreen mode

Webhooks are faster and more reliable than polling.

Real-World Example: Café Merch Shop

A coffee roastery uses AgentPay to sell their signature blend (₫185,000/kg) via Telegram:

  1. User: /buy_merch
  2. Bot shows product + VietQR
  3. User scans and pays from their bank
  4. Bank settles in ~5 seconds
  5. AgentPay confirms → Bot sends shipping address form
  6. Café prepares and ships

Result: Zero payment friction, no fees, direct bank settlement. The café owner checks their bank statement the next day and sees the order clearly labeled with customer metadata.

FAQ

Q: Does AgentPay hold my customer's money?
No. AgentPay generates a VietQR that points directly to your bank account. Money bypasses AgentPay entirely.

Q: What if my bank doesn't support open banking?
You can manually confirm settlements or ask your bank for a webhook API. See docs for your specific bank.

Q: Can I use AgentPay without Telegram?
Absolutely. It's a general Python SDK—use it with Discord, WhatsApp, Slack, or any bot platform.

Q: What are typical VietQR payment times?
Most transfers settle in 5–30 seconds. AgentPay checks your bank feed, so you see confirmation almost instantly.

Q: Is there a transaction fee?
No. AgentPay is open-source and free. Your bank may charge a standard domestic transfer fee (usually ₫0–5,000).

Key Takeaways

  • AgentPay VN is a Python SDK + MCP server for collecting VietQR payments in bots and AI agents.
  • Install in seconds with pip install agentpay-vn; no complex integrations.
  • Money flows directly to your bank account—AgentPay never holds customer funds.
  • Three-step flow: create a payment request, send the checkout URL, await settlement confirmation from your bank.
  • Built for scale: Works with Telegram bots, AI agents (Claude/MCP), Discord, and more.
  • MIT open-source: Full control, no vendor lock-in, transparent code on GitHub.
  • Use bank feeds or webhooks for settlement confirmation—polling works, but webhooks are faster.

Next Steps: Get Started

Ready to add VietQR payments to your bot?

  1. Install AgentPay: pip install agentpay-vn
  2. Review the full docs: agentpay.servicesai.vn/v1/docs
  3. Explore the source: github.com/phuocdu/agentpay-vn
  4. Try the examples: Copy the code blocks above and adapt them to your bot.

Your customers in Vietnam want to pay via VietQR. AgentPay makes it dead simple. Build it today.

Top comments (0)