Add VietQR Checkout to Your LLM Agent in 10 Minutes
Why Your AI Agent Needs Native Payment Power
Imagine you've built an AI agent that helps customers enroll in online courses. The conversation flows naturally—the bot answers questions, builds trust, and at the perfect moment, the user says: "Okay, I'm ready to buy."
Then what? Your agent can't actually accept payment. You hand off to Stripe's iframe, lose context, and watch 30% of users abandon at checkout. Or worse, you're manually processing bank transfers and chasing confirmations via Telegram.
This is where AgentPay VN changes the game. Instead of redirecting away, your agent generates a VietQR code, sends a checkout URL, and waits for the bank to confirm settlement—all without ever touching the money. The funds go straight to your merchant account.
In this tutorial, you'll add a fully functional checkout system to any LLM agent in under 10 minutes. No webhook gymnastics. No payment processing licenses. Just three Python lines and you're live.
What You'll Build: A Real Payment Flow
By the end of this guide, you'll have:
- An open-source Python SDK (
agentpay-vn) integrated into your agent - An MCP server that Claude (or any LLM) can call directly
- A working example: a course-enrollment bot that collects payment and confirms it
The magic: AgentPay VN uses VietQR (Vietnam's open QR standard) and bank feeds instead of APIs. Your agent creates a payment request, shares the checkout URL, and the bank settlement arrives automatically. You never hold customer funds.
Installation: 60 Seconds
Start by installing the SDK via pip:
pip install agentpay-vn
That's it. No environment variables. No secret keys. The SDK works with your bank account details (which you configure once, locally).
If you're using Claude or another agent that supports MCP (Model Context Protocol), also install the server:
pip install agentpay-mcp
Verify the installation:
python -c "import agentpay_vn; print(agentpay_vn.__version__)"
The 3-Line Core: Payment Request → Checkout → Settlement
Here's the conceptual flow that powers everything:
from agentpay_vn import create_payment_request, await_settlement
# Step 1: Create a payment request
payment = create_payment_request(
amount=299000, # VND (≈ $12 USD)
description="Python Course - Intermediate Level",
merchant_id="YOUR_MERCHANT_ID"
)
# Step 2: Send checkout URL to user (agent returns this to the LLM)
checkout_url = payment.checkout_url
# User scans QR or clicks link, transfers via their bank app
# Step 3: Wait for settlement confirmation (bank feed)
settlement = await_settlement(payment.id, timeout=300)
if settlement.confirmed:
print(f"Payment received: {settlement.amount} VND")
else:
print("Payment not received within timeout")
Breaking Down create_payment_request()
This is your main entry point:
from agentpay_vn import create_payment_request, PaymentConfig
# Initialize with your merchant details (do this once, store securely)
config = PaymentConfig(
merchant_id="1234567890", # Your bank-linked merchant ID
merchant_name="My Online Course",
bank_account="1234567890123", # Your business account
bank_code="970010" # Vietnam bank code (example: Vietcombank)
)
# Create a payment for a specific order
payment = create_payment_request(
amount=500000, # 500k VND
description="Premium Course Bundle + Support",
order_id="order_20250115_001", # Your internal ref
customer_email="student@example.com",
config=config
)
print(payment.checkout_url)
# Output: https://agentpay.servicesai.vn/v1/checkout/pay_abc123xyz...
print(payment.qr_code) # Raw QR code string (render as image)
print(payment.id) # Track this for settlement
Key points:
- amount is in VND (Vietnamese Dong)
- order_id should be unique per transaction
- The returned URL is ready to share—no additional setup
- QR code data can be rendered client-side or embedded in an email
Integrating with Your LLM Agent: MCP Server Setup
If you're using Claude Desktop, VS Code, or any MCP-compatible client, AgentPay VN exposes payment functions as tools.
Step 1: Configure MCP in Claude
Edit your Claude configuration file (usually ~/.claude/config.json or settings):
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": [
"--merchant-id", "1234567890",
"--bank-account", "1234567890123",
"--bank-code", "970010",
"--merchant-name", "My Business"
]
}
}
}
Step 2: Test the Tool in Claude
In Claude, you can now ask:
"Create a payment request for $20 USD (approximately 500k VND) for a customer named Alice. She's buying the Python Mastery course."
Claude automatically calls the MCP tool, and returns:
{
"checkout_url": "https://agentpay.servicesai.vn/v1/checkout/pay_xyz...",
"amount": 500000,
"order_id": "order_claude_1234",
"qr_code": "00020...(raw QR string)..."
}
You can then display this to the customer or send it via email.
Real-World Example: Course Enrollment Bot
Let's build a realistic scenario: an AI agent that sells online courses via VietQR.
import asyncio
from agentpay_vn import create_payment_request, await_settlement, PaymentConfig
class CourseEnrollmentBot:
def __init__(self, merchant_id, bank_account, bank_code):
self.config = PaymentConfig(
merchant_id=merchant_id,
merchant_name="Python Academy",
bank_account=bank_account,
bank_code=bank_code
)
self.courses = {
"python-beginner": {"price": 299000, "name": "Python Basics"},
"python-advanced": {"price": 599000, "name": "Advanced Python"},
"python-bundle": {"price": 899000, "name": "Complete Python Bundle"}
}
async def enroll_student(self, course_id: str, student_email: str):
"""
Initiate enrollment: create payment, wait for confirmation, then grant access.
"""
if course_id not in self.courses:
return {"error": "Course not found"}
course = self.courses[course_id]
# Step 1: Create payment request
payment = create_payment_request(
amount=course["price"],
description=f"Enrollment: {course['name']}",
order_id=f"{course_id}_{student_email}_{int(asyncio.get_event_loop().time())}",
customer_email=student_email,
config=self.config
)
print(f"\n✓ Payment created: {payment.id}")
print(f"Share this link: {payment.checkout_url}")
print(f"Amount: {course['price']:,} VND\n")
# Step 2: Wait for settlement (with 10-minute timeout)
try:
settlement = await_settlement(
payment_id=payment.id,
timeout=600, # 10 minutes
poll_interval=2 # Check every 2 seconds
)
if settlement.confirmed:
print(f"✓ Payment confirmed: {settlement.amount:,} VND")
print(f"✓ Granting access to {course['name']}...")
# TODO: provision course access, send email with login
return {
"status": "enrolled",
"course": course["name"],
"access_link": "https://academy.example.com/dashboard"
}
else:
print(f"✗ Payment not received")
return {"status": "timeout", "message": "Please try again"}
except Exception as e:
print(f"Error waiting for settlement: {e}")
return {"status": "error", "message": str(e)}
# Usage
if __name__ == "__main__":
bot = CourseEnrollmentBot(
merchant_id="1234567890",
bank_account="1234567890123",
bank_code="970010"
)
# Async call
result = asyncio.run(
bot.enroll_student(
course_id="python-advanced",
student_email="learner@example.com"
)
)
print("\nResult:", result)
What happens: 1. Student asks to enroll 2. Bot creates a payment request (VietQR checkout generated) 3. Bot shares the URL/QR with student 4. Student transfers money via their bank app 5. Bank settlement arrives automatically 6. Bot detects confirmation and provisions course access 7. Student receives welcome email with login
No manual intervention. No payment processing fees. Money goes directly to your business bank account.
Do's and Don'ts: Common Pitfalls
| Do ✓ | Don't ✗ |
|---|---|
Store merchant_id and bank_account securely (environment vars, vault) |
Hardcode credentials in public repos |
Poll await_settlement() with a reasonable timeout (5–10 min) |
Wait indefinitely or timeout too quickly (< 30 sec) |
Use unique order_id for each payment |
Reuse order IDs across transactions |
| Display the checkout URL prominently to customers | Hide it or assume they'll guess the payment link |
| Test with small amounts first (10k VND ≈ $0.40) | Deploy to production with untested flows |
| Listen to bank feed confirmations (settlement arrived) | Assume payment is complete when QR is generated |
Advanced Tips: Production Ready
1. Handle Network Timeouts Gracefully
from agentpay_vn import SettlementTimeout
try:
settlement = await_settlement(payment.id, timeout=300)
except SettlementTimeout:
# Log the payment for manual review, send customer a follow-up
log_pending_payment(payment.id, customer_email)
send_email("Your payment is being processed, we'll confirm shortly")
2. Idempotency: Handle Retries
If a network error occurs while creating a payment, the request might succeed server-side but time out client-side. Use order_id as your idempotency key:
order_id = "order_alice_python_course_20250115"
# Even if you call this twice, the second call returns the existing payment
payment = create_payment_request(
amount=500000,
order_id=order_id,
description="Python Course"
)
3. Webhook Simulation for Testing
AgentPay VN doesn't require webhooks, but you can simulate bank feeds locally:
from agentpay_vn import simulate_settlement
# In your test suite
payment = create_payment_request(amount=100000, order_id="test_123")
simulate_settlement(payment.id, confirmed=True, amount=100000)
# Now await_settlement will return immediately
settlement = await_settlement(payment.id)
assert settlement.confirmed
4. Multi-Currency: Convert to VND
If your prices are in USD or another currency:
usd_price = 15.99
exchange_rate = 24500 # 1 USD = ~24,500 VND (check current rate)
vnd_amount = int(usd_price * exchange_rate) # 391,735 VND
payment = create_payment_request(
amount=vnd_amount,
description=f"Item (${usd_price} USD)"
)
FAQ: Common Questions
Q: Does AgentPay VN hold my customer's money? No. The QR code points directly to your bank account. Funds arrive in your business account; AgentPay VN only verifies settlement via your bank's feed.
Q: What if a customer sends the wrong amount?
The await_settlement() function checks the exact amount by default. If they send 490k instead of 500k, you can either accept a variance by setting amount_tolerance=10000 or reject and ask them to resend.
Q: Can I use AgentPay VN with Claude, OpenAI, or other LLMs?
Yes. Claude via MCP (built-in). For OpenAI's API, wrap the SDK in your own tool definitions using the tools parameter. Any agent that can call Python functions can use AgentPay VN.
Q: Is there a fee? AgentPay VN is open-source (MIT license). Bank settlement fees are determined by your bank; typically 0.5–1% for business transfers in Vietnam. You get the full amount minus your bank's settlement fee—no AgentPay fee.
Key Takeaways
- AgentPay VN = Python SDK + MCP server for VietQR payments in AI agents
- Installation: One
pip install agentpay-vncommand - 3-step flow:
create_payment_request()→ share checkout URL →await_settlement() - No money held: Funds go straight to your merchant bank account
- Bank feed confirms: Settlement is automatic; no webhooks required
- MCP-native: Claude and other LLMs can call payment functions directly
- Real-world use cases: Course sales, freelance invoicing, SaaS signups, café orders
- Production-ready: Handle timeouts, idempotency, and multi-currency with ease
Get Started Now
You're ready to add checkout to your agent. Here's the next step:
-
Install the SDK:
bash pip install agentpay-vn -
Set up your merchant config with your bank details (one-time setup).
-
Try the example above with your LLM or Python script.
-
Read the full docs for advanced features, webhooks, and API reference: 👉 https://agentpay.servicesai.vn/v1/docs
-
Star the repo and contribute on GitHub: 👉 https://github.com/phuocdu/agentpay-vn
Your AI agent is now a full-fledged payment processor. In production, your first payment might arrive today. Welcome to agent-powered commerce.