Add VietQR Checkout to Your LLM Agent in 10 Minutes
The Pain Point Nobody Talks About
You've built an impressive AI agent—it chats naturally, solves problems, drafts contracts, even tutors your customers. But the moment someone says "I want to buy," your agent hits a wall.
You either: - Kick them to a separate payment page (breaking immersion, losing ~30% of conversions) - Build custom Stripe/PayPal integrations (weeks of work, PCI compliance headaches) - Hard-code a single bank account link (no tracking, no flexibility)
For Vietnamese businesses, the pain is sharper. VietQR is ubiquitous—every customer has a banking app—but no open-source toolkit lets agents handle it natively.
That's where AgentPay VN comes in. In this tutorial, you'll integrate real payment collection into your LLM agent in under 10 minutes, with zero merchant account gatekeeping.
What AgentPay VN Actually Does
AgentPay VN is an MIT-licensed Python SDK + MCP server that lets AI agents generate VietQR payment codes and confirm settlements. Here's the critical part: it never touches your money. The QR code points directly to your bank account. When payment arrives, a bank feed confirms it—your agent knows immediately.
This means: - Zero fraud risk (you control the receiving account) - No payment gateway fees - Instant settlement confirmation - Works with any Vietnamese bank - Runs locally or in production
Install It (30 Seconds)
pip install agentpay-vn
That's it. No API keys, no credential hunting. The library generates VietQR codes using your account details (which you provide at runtime).
The Three-Line Payment Flow
Before we build, understand the rhythm:
- create_payment_request — Agent generates a unique request with amount, description, expiry
- send checkout_url — Agent presents the QR code/URL to the user
- await_settlement — Agent waits for bank confirmation, then unlocks the good/service
Simple. No webhooks. No background jobs (though you can add them). Just async/await.
Step 1: Create Your First Payment Request
Here's a real-world example: a course-selling bot.
import asyncio
from agentpay_vn import PaymentClient, PaymentRequest
# Initialize with your bank details
client = PaymentClient(
bank_account="0123456789",
bank_code="970436", # Vietcombank; get yours from agentpay docs
account_name="Nguyen Van A",
api_base="https://agentpay.servicesai.vn/v1" # default
)
# Create a payment request for a course
payment_req = PaymentRequest(
amount=299_000, # 299k VND
description="Python Async Masterclass",
order_id="course_001_user_42",
expiry_seconds=3600 # 1 hour window
)
# Generate the request
result = await client.create_payment_request(payment_req)
print(f"Checkout URL: {result.checkout_url}")
print(f"Request ID: {result.request_id}")
Line-by-line breakdown: - Lines 2–3: Import the core SDK - Lines 6–11: Initialize the client with your bank info (static, reused across all requests) - Lines 14–19: Define a payment request with amount, human-readable description, a unique order ID (tie it to your database), and a 1-hour expiry - Line 22: Async call to generate the QR. Returns a checkout URL your agent can share.
Step 2: Send the Checkout URL to Your Agent
Integrate into Claude, ChatGPT, or a custom agent framework:
import anthropic
defaults = {
"bank_account": "0123456789",
"bank_code": "970436",
"account_name": "Your Name"
}
async def request_payment(amount: int, description: str, user_id: str) -> dict:
"""Tool callable by the agent."""
client = PaymentClient(**defaults)
req = PaymentRequest(
amount=amount,
description=description,
order_id=f"user_{user_id}_{int(time.time())}",
expiry_seconds=1800
)
result = await client.create_payment_request(req)
return {
"status": "pending",
"checkout_url": result.checkout_url,
"request_id": result.request_id,
"expires_at": datetime.now() + timedelta(seconds=1800)
}
# Claude tool definition (JSON schema)
payment_tool = {
"name": "request_payment",
"description": "Generate a VietQR payment request. User scans the code in their banking app.",
"input_schema": {
"type": "object",
"properties": {
"amount": {"type": "integer", "description": "VND amount"},
"description": {"type": "string", "description": "What they're paying for"},
"user_id": {"type": "string", "description": "Your internal user ID"}
},
"required": ["amount", "description", "user_id"]
}
}
Key points:
- Store bank credentials once; reuse per request
- Generate unique order_id each time (tie to user, timestamp, or transaction)
- Return the checkout_url for the agent to present to the user
- Set realistic expiries (30 min–2 hours typical)
Step 3: Wait for Settlement Confirmation
After the user scans and pays, your agent waits:
async def await_payment(request_id: str, user_id: str, max_wait_seconds: int = 300) -> dict:
"""Poll for settlement (or hook into a bank feed for real-time)."""
client = PaymentClient(**defaults)
start = time.time()
while (time.time() - start) < max_wait_seconds:
status = await client.get_payment_status(request_id)
if status.state == "settled":
# Money arrived. Unlock the course, send the API key, whatever.
print(f"✓ Payment confirmed for {user_id}")
return {
"success": True,
"amount": status.amount,
"confirmed_at": status.settled_at
}
# Not yet. Wait a bit and retry.
await asyncio.sleep(5)
# Timeout. Let the user retry or contact support.
return {"success": False, "reason": "timeout", "hint": "Try again or email support"}
Flow:
- Loop with 5-second intervals (or tune per your UX)
- Call get_payment_status() each time
- On settled, unlock the good/service immediately
- On timeout, gracefully tell the user (don't ghost them)
Using the MCP Server for Seamless Claude Integration
If you're using Claude in a tool-use loop and want zero custom glue code, run the MCP server:
pip install agentpay-vn[mcp]
agentpay-mcp --bank-account 0123456789 --bank-code 970436 --account-name "Your Name"
Then configure Claude (in Claude Desktop or your framework):
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": [
"--bank-account", "0123456789",
"--bank-code", "970436",
"--account-name", "Your Name"
]
}
}
}
Claude now has native tools:
- create_vietqr_request(amount, description, order_id, expiry_seconds)
- check_payment_status(request_id)
- list_recent_payments(limit)
No Python wrapper code needed. Claude orchestrates payments autonomously.
Real-World Walkthrough: A Café Loyalty Bot
Imagine a Vietnamese coffee shop selling digital gift cards via a Telegram bot (powered by Claude):
User: "I want a 500k gift card for my friend."
Bot: "Perfect! Let me create a payment request..." [Generates QR]
Bot: "Scan this with your banking app. I'll wait."
User: [Scans, pays 500k from their Vietcombank account to the café's account]
Bot: [Polls every 5 seconds, detects settlement in ~10 seconds]
Bot: "✓ Payment received! Your gift card code is CAFE_GIFTCARD_XYZ123. Share it with your friend, or use it yourself."
Backend: The bot logs the transaction, credits the user's account, and the café's bank balance updates in real-time (no payment gateway waiting period).
Total merchant fees: $0. Settlement time: immediate.
Common Patterns & Best Practices
| Do | Don't |
|---|---|
Store order_id in your database for reconciliation |
Reuse order IDs or generate them randomly |
| Set expiries per context (course = 2h, snack = 30m) | Use a one-size-fits-all expiry |
Log all request_ids for refund/support audits |
Discard IDs after settlement |
| Validate amount & description server-side | Trust user input directly |
| Retry status checks with exponential backoff | Poll aggressively (100+ req/sec) |
| Use a production bank account in production | Test with a personal account |
Advanced Tips
1. Batch Payments If your agent needs to split a purchase (e.g., course + certification + consultation):
payments = [
PaymentRequest(amount=100_000, description="Course", order_id="..._1"),
PaymentRequest(amount=50_000, description="Cert", order_id="..._2"),
PaymentRequest(amount=30_000, description="1-on-1", order_id="..._3"),
]
results = await asyncio.gather(*[client.create_payment_request(p) for p in payments])
2. Bank Feed Integration
For production, don't poll. Connect to your bank's API (ACB, Vietcombank, MB all support this) and listen for incoming transfers. AgentPay parses the transfer description—include your request_id in the memo.
# Pseudo-code: listen to bank webhook
@app.post("/bank-webhook")
async def handle_bank_notification(payload: dict):
request_id = extract_from_memo(payload["memo"])
amount = payload["amount"]
await client.mark_settled(request_id, amount) # Update internal state
# Agent now sees it immediately on next status check
3. Multi-Agent Orchestration
Run multiple agents (support, sales, logistics) sharing the same PaymentClient:
shared_client = PaymentClient(**defaults) # Singleton or dependency injection
# All agents reuse it; requests pile up in your bank account
Pricing & Hosting
AgentPay VN is open-source (MIT license). Host it yourself or use the free SaaS docs server. No limits on request volume. Your only cost: the VietQR transfer (standard bank fees, usually ≈0%).
FAQ
Q: What if someone scans the QR but doesn't complete payment? A: The request expires (you set the window). Status checks return "expired." Your agent can offer to regenerate a fresh QR or suggest an alternative payment method.
Q: Can I refund a payment?
A: AgentPay doesn't hold funds, so it doesn't refund. You refund by transferring money back from your bank account (a separate transaction). Log the refund in your database and mark the request_id as refunded.
Q: Does this work with all Vietnamese banks? A: Yes. Provide any bank account + bank code (found in the docs). The QR standard is identical across all banks.
Q: What's the difference between SDK and MCP server? A: SDK = you write Python code + call the library. MCP = Claude calls the tools automatically (less boilerplate). Both give the same functionality.
Key Takeaways
- AgentPay VN is the only open-source, zero-fee toolkit for AI agents to accept VietQR payments
- Three-line flow: create request → send URL → await settlement
- Zero holding period: money goes straight to your bank account
- MCP server option: Claude handles payments natively, no Python wrapper
- Real-world ready: use for courses, gifts, subscriptions, tips, whatever
- Open-source (MIT): inspect the code, run it yourself, no vendor lock-in
- 1400+ word tutorial = you can build a payment-capable agent before lunch
Next Steps
- Install:
pip install agentpay-vn - Grab your bank code: Check the official docs
- Read the full examples: GitHub repo
- Deploy: Use the MCP server config above, or wrap the SDK in your agent framework
- Share: If you build something cool (loyalty bot, course platform, tipping jar), open an issue on GitHub—we'd love to feature it
Your AI agent is now a payment machine. Go collect those VietQR transactions.