VietQR Payment Automation for AI Agents
The Problem: Why Stripe Doesn't Work for Vietnam
You've built an AI agent that books fitness classes, sells online courses, or manages a café's lunch orders. Revenue's flowing—but only from international customers. The moment a Vietnamese user wants to pay, you hit a wall.
Stripe doesn't support VietQR. PayPal's fees are brutal (3.5% + $0.30). Local payment gateways? They require merchant accounts, weeks of paperwork, and monthly minimums that kill small projects. Meanwhile, your agent sits idle, unable to close transactions with 98+ million Vietnamese smartphone users.
That's the gap AgentPay VN fills—without you ever touching the money.
What AgentPay VN Actually Does
AgentPay VN is an MIT-licensed Python SDK + MCP (Model Context Protocol) server that lets AI agents generate VietQR payment requests. Here's the critical difference from traditional payment gateways:
The QR code points directly to the merchant's bank account. Your agent doesn't hold funds, doesn't process settlements, doesn't require PCI compliance. When a customer scans the code and pays, the bank confirms the deposit instantly. Your agent detects it and delivers the service.
Think of it as a webhook bridge between VietQR and your AI workflow.
The Three-Step Payment Flow
1. Create Payment Request
Your agent generates a request with amount, merchant info, and description:
from agentpay_vn import AgentPayVN
# Initialize with your merchant bank account
payment_handler = AgentPayVN(
merchant_id="your_merchant_id",
bank_account="1234567890",
bank_name="Vietcombank",
account_holder="Business Name"
)
# Agent creates a payment request
payment_request = payment_handler.create_payment_request(
amount=150000, # VND
description="Online Course: Python Basics",
order_id="course_2024_001",
customer_name="Nguyễn Văn A"
)
print(f"QR Code URL: {payment_request['qr_url']}")
print(f"Request ID: {payment_request['request_id']}")
print(f"Checkout URL: {payment_request['checkout_url']}")
Line-by-line breakdown:
- merchant_id: Unique identifier for your business.
- bank_account: The actual account where payments land.
- create_payment_request(): Generates a VietQR request object (no payment yet).
- amount: Transaction size in VND (150,000 ≈ $6 USD).
- order_id: Your internal reference; critical for matching settlements.
- The function returns the QR code URL and a checkout_url you send to users.
2. Send Checkout URL
Your agent sends the checkout URL via SMS, email, or displays it in a chat interface:
# Agent sends link to customer
checkout_url = payment_request['checkout_url']
message = f"Pay here: {checkout_url}"
# In a chat bot context:
print(f"Agent: Hi! Please complete payment at: {checkout_url}")
print(f"This QR code is valid for 24 hours.")
The customer clicks the link, scans the QR, and pays via their banking app (Vietcombank, Agribank, BIDV, etc.)—no new app install required.
3. Await Settlement & Deliver Service
Your agent polls the bank feed and detects payment confirmation:
import asyncio
from datetime import datetime, timedelta
async def wait_for_payment(request_id, timeout_minutes=30):
"""
Poll for payment confirmation.
Timeout prevents infinite loops; real-world agents should use webhooks.
"""
start_time = datetime.now()
timeout = timedelta(minutes=timeout_minutes)
while datetime.now() - start_time < timeout:
# Check if payment settled
status = payment_handler.check_payment_status(request_id)
if status['settled']:
print(f"✓ Payment confirmed: {status['amount']} VND")
print(f"Transaction reference: {status['transaction_id']}")
return status
# Wait 3 seconds before next check
await asyncio.sleep(3)
print("✗ Payment timeout. User may have abandoned checkout.")
return None
# Agent waits for settlement
payment_status = await wait_for_payment(payment_request['request_id'])
if payment_status:
# Deliver service immediately
course_link = generate_course_access_link(customer_id)
print(f"Agent: Thanks! Access your course: {course_link}")
else:
print("Agent: Payment not received. Try again?")
Key points:
- check_payment_status() queries the bank feed (updated every 10–15 seconds).
- settled: True means money is in your account; funds are guaranteed.
- Delivery happens in the same agent loop—no manual reconciliation.
- Set realistic timeouts; customers expect confirmation within 2–5 minutes.
Real-World Example: AI Course Sales Agent
Imagine a Discord bot selling Python courses to Vietnamese learners.
User: !buy python-basics
Agent workflow:
1. Look up course price (150,000 VND).
2. Create payment request with order_id="python-basics-{user_id}".
3. Send message: "Course costs 150k VND. Pay here: [link]. QR valid 24h."
4. Poll check_payment_status() every 3 seconds.
5. When settled, send invite link to course GitHub repo and Notion workspace.
6. Log transaction in SQLite: (user_id, order_id, amount, timestamp, status='completed').
Agent code sketch:
async def sell_course(user_id, course_slug):
# Look up course
course = COURSES[course_slug]
# Create payment
payment = payment_handler.create_payment_request(
amount=course['price'],
description=f"Course: {course['title']}",
order_id=f"{course_slug}-{user_id}"
)
# Send checkout link
await send_user_message(
user_id,
f"🎓 {course['title']}\nPrice: {course['price']:,} VND\nPay: {payment['checkout_url']}"
)
# Wait for settlement
status = await wait_for_payment(payment['request_id'], timeout_minutes=30)
if status:
# Grant access
access = grant_course_access(user_id, course_slug)
await send_user_message(user_id, f"✓ Access granted! Link: {access['link']}")
log_transaction(user_id, course_slug, status['amount'], 'success')
else:
await send_user_message(user_id, "⏱ Payment not received. Try again?")
log_transaction(user_id, course_slug, course['price'], 'abandoned')
This agent handles 50+ concurrent users, each with their own payment request, without holding any funds.
Setting Up the MCP Server for Claude
If you're using Claude or another AI model, connect AgentPay VN via MCP:
Install the MCP server:
pip install agentpay-vn agentpay-mcp
Configure Claude (claude_desktop_config.json):
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": [
"--merchant-id", "your_merchant_id",
"--bank-account", "1234567890",
"--bank-name", "Vietcombank",
"--account-holder", "Your Business Name"
]
}
}
}
Now Claude can:
- Call create_payment_request() directly in conversations.
- Check payment status mid-conversation.
- Chain results: "Create a 500k VND request, send the link to user, then wait for settlement and confirm access."
MCP abstracts the SDK—Claude handles orchestration.
Advanced Tips & Patterns
Webhook Integration (Better Than Polling)
Polling works but wastes API calls. If your merchant bank supports webhooks, register one:
# Instead of polling, listen for settlement events
from fastapi import FastAPI, Request
app = FastAPI()
@app.post("/webhook/settlement")
async def handle_settlement(data: Request):
payload = await data.json()
request_id = payload['request_id']
amount = payload['amount']
# Lookup pending order
order = db.query(Payment).filter(Payment.request_id == request_id).first()
if order:
order.status = 'settled'
db.commit()
# Deliver service immediately
await deliver_service(order.user_id, order.product_id)
return {"status": "received"}
Webbhooks reduce latency from 3–5 seconds to <500ms.
Idempotency Keys
Prevents duplicate payments if your agent retries:
payment = payment_handler.create_payment_request(
amount=150000,
description="Course",
order_id="course_001",
idempotency_key=f"user_123_course_001" # Always same for same user+product
)
If the network drops and your agent retries, the same request ID is returned—no double-charge.
Partial Refunds (If Needed)
# User regrets purchase within 24 hours
refund = payment_handler.refund_payment(
request_id=original_request['request_id'],
amount=150000, # Full refund
reason="customer_request"
)
print(f"Refund initiated. ID: {refund['refund_id']}")
Refunds post to the customer's bank account within 1–2 business days.
AgentPay VN vs. Alternatives
| Feature | AgentPay VN | Stripe | PayPal | Local Gateway |
|---|---|---|---|---|
| VietQR Support | ✓ Native | ✗ | ✗ | ✓ (varies) |
| No Money Held | ✓ | ✗ Escrow | ✗ Escrow | ✗ Escrow |
| Merchant Bank Direct | ✓ | ✗ | ✗ | ✓ |
| Python SDK | ✓ MIT | ✓ | ✓ | ✗ (usually) |
| Setup Time | 10 min | 1–2 days | 1–2 days | 1–2 weeks |
| Transaction Fee | ~0.5% | 2.9% + $0.30 | 3.5% | 1–2% |
| AI Agent Ready | ✓ MCP | ✗ | ✗ | ✗ |
For Vietnamese payments via AI agents, AgentPay VN is the only option that doesn't require a merchant account, extensive KYC, or monthly minimums.
Do's and Don'ts
✅ Do: - Set order IDs uniquely per transaction (no duplicates). - Implement retry logic with exponential backoff (e.g., 1s, 2s, 4s, 8s). - Use webhooks in production; polling is for prototypes. - Test with small amounts (10,000–50,000 VND) first. - Log all requests and settlements for accounting. - Expire payment links after 24 hours.
❌ Don't:
- Poll faster than every 2–3 seconds (wastes API quota).
- Trust settled=True before checking transaction_id (ensure bank feed confirms it).
- Hard-code merchant ID in client-side code (use environment variables).
- Deliver service before settlement confirmation.
- Forget to handle timeout scenarios (user closes app mid-payment).
FAQ
Q: Does AgentPay VN hold my money? No. The QR points to your bank account directly. Settlement is peer-to-peer between customer and your bank. AgentPay only confirms the transaction happened.
Q: What if the customer's bank isn't supported? VietQR works with 38+ Vietnamese banks (Vietcombank, Agribank, BIDV, Techcombank, TPBank, etc.). ~95% of Vietnamese adults have access. For unsupported banks, offer a fallback payment method.
Q: How long until money appears in my account?
Usually 10–30 seconds after the customer confirms the transfer in their banking app. Weekends may add a few hours. You can check check_payment_status() immediately after the customer pays.
Q: Can I use AgentPay VN with non-AI services? Yes. The SDK is a standard Python library. You can use it in Flask APIs, background jobs, or CLI tools. The MCP server is for AI/Claude specifically, but the SDK works everywhere Python runs.
Key Takeaways
- VietQR is native: 98M+ Vietnamese have VietQR access; no app install needed.
- AgentPay VN removes friction: AI agents collect payments in 3 lines of code without holding funds.
- Direct to merchant bank: You keep 99.5% of revenue; no escrow, no 2.9% Stripe markup.
- Python + MCP ready: Works with Discord bots, Telegram bots, Claude conversations, FastAPI services.
- Open-source & MIT: Full transparency; modify and self-host if needed.
- Production-ready: Bank feed integration, idempotency, refunds, and webhook support.
For AI agents in Vietnam, AgentPay VN is the fastest path from "user clicks buy" to "service delivered" while keeping compliance simple.
Getting Started
Install now:
pip install agentpay-vn
Read the full docs: https://agentpay.servicesai.vn/v1/docs
Browse the source: https://github.com/phuocdu/agentpay-vn
Start with the example above, test with 10,000 VND (~$0.40), and ship your first agent-powered payment in under an hour. Questions? Post an issue on GitHub or check the docs FAQ.