{"slug": "collect-vietqr-payments-in-telegram-bots-with-agentpay", "title": "Collect VietQR Payments in Telegram Bots with AgentPay", "summary": "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.", "body_md": "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:\n\nA 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.\n\nEnter **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.\n\nAgentPay VN is an MIT-licensed Python SDK + MCP server that orchestrates VietQR payment requests. Here's the mental model:\n\nThe 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.\n\nTelegram 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:\n\nNo authentication screens. No signup. Pure banking rails.\n\n```\npip install agentpay-vn\n```\n\nVerify the installation:\n\n``` python\npython -c \"import agentpay; print(agentpay.__version__)\"\n```\n\nYou'll need:\n\nFor testing, AgentPay provides a sandbox environment. Check the [official docs](https://agentpay.servicesai.vn/v1/docs) for your bank.\n\n```\nexport AGENTPAY_MERCHANT_ID=\"your_merchant_id\"\nexport AGENTPAY_API_KEY=\"your_api_key\"\nexport TELEGRAM_BOT_TOKEN=\"your_telegram_token\"\n```\n\nHere's a minimal Telegram bot that collects a payment:\n\n``` python\nimport os\nfrom telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup\nfrom telegram.ext import Application, CommandHandler, ContextTypes\nfrom agentpay import AgentPayClient\n\n# Initialize AgentPay\nagentpay_client = AgentPayClient(\n    merchant_id=os.getenv(\"AGENTPAY_MERCHANT_ID\"),\n    api_key=os.getenv(\"AGENTPAY_API_KEY\"),\n)\n\nasync def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:\n    \"\"\"Send welcome message with a /buy command.\"\"\"\n    await update.message.reply_text(\n        \"🎉 Welcome! Use /buy to purchase an item.\\n\"\n        \"Type /help for more info.\"\n    )\n\nasync def buy(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:\n    \"\"\"Create a payment request and send checkout link.\"\"\"\n    user_id = update.effective_user.id\n\n    # Step 1: Create a payment request\n    # This tells AgentPay: \"I want 50,000 VND from user X\"\n    payment_request = agentpay_client.create_payment_request(\n        amount=50000,  # VND\n        currency=\"VND\",\n        merchant_ref_id=f\"order_{user_id}_{int(time.time())}\",\n        description=\"Premium Course - Python Mastery\",\n        metadata={\"user_id\": user_id, \"product\": \"course_python\"},\n    )\n\n    # Step 2: Get the checkout URL with embedded QR\n    checkout_url = payment_request.get(\"checkout_url\")\n    payment_id = payment_request.get(\"payment_id\")\n\n    # Step 3: Send to user with inline button\n    keyboard = InlineKeyboardMarkup([\n        [InlineKeyboardButton(\"💳 Pay with VietQR\", url=checkout_url)]\n    ])\n\n    await update.message.reply_text(\n        f\"📦 Order Summary:\\n\"\n        f\"Product: Premium Course\\n\"\n        f\"Price: 50,000 VND\\n\\n\"\n        f\"Tap the button below to pay with your banking app.\",\n        reply_markup=keyboard\n    )\n\n    # Store payment_id for later confirmation\n    context.user_data[\"pending_payment_id\"] = payment_id\n\nasync def check_payment(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:\n    \"\"\"Poll for payment settlement (in production, use webhooks).\"\"\"\n    payment_id = context.user_data.get(\"pending_payment_id\")\n\n    if not payment_id:\n        await update.message.reply_text(\"No pending payment found.\")\n        return\n\n    # Step 3: Await settlement confirmation\n    # In production, listen to bank webhooks instead of polling\n    status = agentpay_client.await_settlement(\n        payment_id=payment_id,\n        timeout=300,  # Wait max 5 minutes\n    )\n\n    if status[\"settled\"]:\n        await update.message.reply_text(\n            f\"✅ Payment confirmed!\\n\"\n            f\"Amount: {status['amount']:,} VND\\n\"\n            f\"Your premium course access is now active.\\n\"\n            f\"📚 [Open Course](https://example.com/course)\"\n        )\n        # Grant access to course, send API key, etc.\n    else:\n        await update.message.reply_text(\n            \"⏳ Payment not yet confirmed. Try again in a moment.\"\n        )\n\ndef main():\n    \"\"\"Start the bot.\"\"\"\n    app = Application.builder().token(\n        os.getenv(\"TELEGRAM_BOT_TOKEN\")\n    ).build()\n\n    app.add_handler(CommandHandler(\"start\", start))\n    app.add_handler(CommandHandler(\"buy\", buy))\n    app.add_handler(CommandHandler(\"check\", check_payment))\n\n    print(\"🤖 Bot running...\")\n    app.run_polling()\n\nif __name__ == \"__main__\":\n    import time\n    main()\n```\n\n**Line-by-line breakdown:**\n\n`/start`\n\nsends a welcome message`/buy`\n\ncreates a payment request with amount, reference ID, and metadata`/check`\n\npolls for settlement status (use webhooks in production!)Polling is fine for demos, but slow and unreliable. Use **bank webhooks** to get instant payment confirmation:\n\n``` python\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route(\"/webhook/agentpay\", methods=[\"POST\"])\ndef handle_payment_webhook():\n    \"\"\"AgentPay calls this when payment settles.\"\"\"\n    payload = request.json\n\n    # Verify the signature (AgentPay docs explain this)\n    if not agentpay_client.verify_webhook_signature(\n        payload, request.headers.get(\"X-Signature\")\n    ):\n        return {\"error\": \"Invalid signature\"}, 401\n\n    payment_id = payload[\"payment_id\"]\n    status = payload[\"status\"]  # \"settled\" or \"failed\"\n    amount = payload[\"amount\"]\n    merchant_ref_id = payload[\"merchant_ref_id\"]\n\n    if status == \"settled\":\n        # Extract user ID from merchant_ref_id\n        user_id = int(merchant_ref_id.split(\"_\")[1])\n\n        # Grant access to the user\n        # e.g., send them an API key, activate subscription, etc.\n        grant_access(user_id)\n\n        # Notify user via Telegram\n        asyncio.run(\n            send_telegram_message(\n                user_id,\n                f\"✅ Payment received! ({amount:,} VND)\\n\"\n                \"Your access is active.\"\n            )\n        )\n\n    return {\"status\": \"ok\"}, 200\n\nif __name__ == \"__main__\":\n    app.run(host=\"0.0.0.0\", port=5000)\n```\n\nConfigure your webhook URL in the AgentPay dashboard to `https://yourdomain.com/webhook/agentpay`\n\n.\n\nIf you're using Claude as your AI agent orchestrator, install the MCP server:\n\n```\npip install agentpay-mcp\n```\n\nConfigure Claude's MCP settings:\n\n```\n{\n  \"mcpServers\": {\n    \"agentpay\": {\n      \"command\": \"agentpay-mcp\",\n      \"env\": {\n        \"AGENTPAY_MERCHANT_ID\": \"your_merchant_id\",\n        \"AGENTPAY_API_KEY\": \"your_api_key\"\n      }\n    }\n  }\n}\n```\n\nNow Claude can call AgentPay directly:\n\n```\nUser: \"I want to sell a course for 200,000 VND.\"\nClaude: I'll create a payment request for you.\n[Calls agentpay.create_payment_request()]\nClaude: Payment link: https://checkout.agentpay.vn/...\n```\n\nImagine a café selling espresso shots via Telegram:\n\n`/order`\n\n→ \"1x Espresso (40,000 VND)\"The entire flow takes 30 seconds. No payment processor fees. No settlement delays.\n\nAlways include a unique `merchant_ref_id`\n\nto prevent duplicate charges if the network retries:\n\n``` python\nimport uuid\n\nmerchant_ref_id = f\"order_{user_id}_{uuid.uuid4()}\"\n```\n\nNot all webhooks arrive instantly. Implement retry logic:\n\n``` python\nfrom tenacity import retry, stop_after_attempt, wait_exponential\n\n@retry(stop=stop_after_attempt(5), wait=wait_exponential())\nasync def notify_user_via_telegram(user_id, message):\n    # Attempt up to 5 times with exponential backoff\n    await bot.send_message(user_id, message)\n```\n\nUse a database (PostgreSQL, MongoDB) to track payment status:\n\n```\nclass PaymentRecord(db.Model):\n    payment_id = db.Column(db.String, primary_key=True)\n    user_id = db.Column(db.Integer)\n    amount = db.Column(db.Integer)  # VND\n    status = db.Column(db.String)  # pending, settled, failed\n    created_at = db.Column(db.DateTime, default=datetime.now)\n```\n\nAgentPay provides a sandbox environment. Always test before going live:\n\n```\nagentpay_client = AgentPayClient(\n    merchant_id=\"sandbox_merchant_id\",\n    api_key=\"sandbox_api_key\",\n    environment=\"sandbox\",  # Switch to \"production\" later\n)\n```\n\n| ✅ Do | ❌ Don't |\n|---|---|\n| Use webhook callbacks for payment confirmation | Poll the API in a tight loop |\n| Store merchant_ref_id; match it in webhook | Trust the order of webhook events |\n| Verify webhook signatures | Skip signature verification |\n| Implement idempotent request IDs | Assume network calls always succeed |\n| Start with sandbox environment | Go live without testing |\n| Monitor settlement times | Assume instant settlement (usually seconds, but be safe) |\n| Log all payment events | Ignore payment failures silently |\n\n**Q: Can AgentPay handle payments in other currencies (USD, etc.)?**\n\nA: 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.\n\n**Q: What if the customer's bank doesn't support VietQR?**\n\nA: 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).\n\n**Q: How much does AgentPay cost?**\n\nA: 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%).\n\n**Q: How long does settlement take?**\n\nA: 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.\n\n`pip install agentpay-vn`\n\nand get your first payment in under 10 minutes`pip install agentpay-vn`\n\nYour 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.", "url": "https://wpnews.pro/news/collect-vietqr-payments-in-telegram-bots-with-agentpay", "canonical_source": "https://dev.to/ai_services_f9c382bdb33b9/collect-vietqr-payments-in-telegram-bots-with-agentpay-4neb", "published_at": "2026-07-09 00:35:20+00:00", "updated_at": "2026-07-09 00:41:04.587224+00:00", "lang": "en", "topics": ["developer-tools", "ai-tools"], "entities": ["AgentPay VN", "Telegram", "Stripe", "MB Bank", "Techcombank", "VCB"], "alternates": {"html": "https://wpnews.pro/news/collect-vietqr-payments-in-telegram-bots-with-agentpay", "markdown": "https://wpnews.pro/news/collect-vietqr-payments-in-telegram-bots-with-agentpay.md", "text": "https://wpnews.pro/news/collect-vietqr-payments-in-telegram-bots-with-agentpay.txt", "jsonld": "https://wpnews.pro/news/collect-vietqr-payments-in-telegram-bots-with-agentpay.jsonld"}}