VietQR Payment Automation for AI Agents
The Problem: Why AI Agents Need Native Vietnamese Payment Rails
Imagine you're running an online course platform in Ho Chi Minh City. A customer completes checkout in your AI chatbot, but now what? Stripe and PayPal either don't support direct VND settlement, charge 3–4% fees, or require days of account verification for Vietnamese merchants. Your agent sits stuck—it can't confirm payment, can't trigger course access, can't close the sale. Meanwhile, your customer bounces.
This is the reality for thousands of Vietnamese SaaS founders, e-commerce operators, and AI-first businesses. They need real-time, cheap, agent-native payment infrastructure. Enter AgentPay VN—an open-source Python SDK + MCP server that lets your AI agents collect VietQR payments in three lines of code, with settlement hitting your merchant account within hours, not days.
What Is AgentPay VN?
AgentPay VN is a lightweight, MIT-licensed toolkit designed specifically for AI agents. Here's what makes it different:
- No money custody: The QR code points directly at your merchant bank account. AgentPay VN never touches funds—it's just a coordinator.
- VietQR native: Uses Vietnam's instant payment standard, supporting all major banks (Vietcombank, Techcombank, MB Bank, etc.).
- Agent-ready: Works seamlessly with Claude, OpenAI, and any LLM via an MCP (Model Context Protocol) server.
- Open-source: MIT licensed, auditable, no vendor lock-in.
- Fast settlement: Bank feeds confirm payment within minutes; your agent gets real-time callbacks.
Core components:
1. Python SDK (agentpay-vn package)
2. MCP server (agentpay-mcp) for Claude/agent integration
3. A simple three-step flow: create request → send link → await settlement
The Three-Line Payment Flow Explained
AgentPay VN's payment loop is deliberately minimal. Here's the mental model:
Agent creates payment request
↓
Agent sends checkout URL to customer
↓
Agent polls/listens for bank settlement
↓
Agent unlocks/ships/starts service
No webhooks to manage. No iframe complexity. No PCI compliance burden. Just the agent, the bank feed, and the merchant account.
Installation and Setup
Step 1: Install the SDK
pip install agentpay-vn
Verify installation:
python -c "import agentpay_vn; print(agentpay_vn.__version__)"
Step 2: Configure Your Bank Details
You'll need: - Merchant bank account (any Vietnamese bank; ideally one supporting API/feeds) - Bank account number - Account holder name (matches bank records) - Merchant ID (from AgentPay VN dashboard or self-issued)
Create a .env file:
AGENTPAY_MERCHANT_ACCOUNT=1234567890
AGENTPAY_MERCHANT_NAME="Your Business Name"
AGENTPAY_MERCHANT_ID=merchant_abc123
AGENTPAY_BANK_CODE=970016 # Vietcombank example
Step 3: Connect Your Bank Feed (Optional but Recommended)
AgentPay VN can auto-confirm settlements via bank feeds from services like: - Your bank's API (if available) - Third-party aggregators (e.g., Plaid-equivalent for Vietnam) - Manual bank statement parsing (fallback)
This lets your agent know payment arrived without polling.
Core SDK Tutorial: Building a Payment-Aware Agent
Code Example 1: Create and Track a Payment Request
Here's a complete, runnable example of an agent collecting payment:
import asyncio
from agentpay_vn import (
PaymentRequest,
PaymentClient,
SettlementListener
)
import os
# Initialize client with env vars
client = PaymentClient(
merchant_account=os.getenv("AGENTPAY_MERCHANT_ACCOUNT"),
merchant_name=os.getenv("AGENTPAY_MERCHANT_NAME"),
merchant_id=os.getenv("AGENTPAY_MERCHANT_ID"),
bank_code=os.getenv("AGENTPAY_BANK_CODE")
)
async def sell_course():
"""
Agent function: A customer wants to buy a Python course.
Create payment request, send link, and wait for settlement.
"""
# Step 1: Create a payment request
payment_req = PaymentRequest(
amount_vnd=499_000, # 499k VND for the course
description="Python Masterclass - Full Access",
customer_id="cust_john_doe_123",
reference_id="course_purchase_001",
expires_in_minutes=15 # Link valid for 15 min
)
# Create the request in AgentPay
result = await client.create_payment_request(payment_req)
# Step 2: Extract the VietQR checkout URL
checkout_url = result.checkout_url
qr_code_data = result.qr_code_raw # Raw QR string if needed
print(f"📱 Send this link to customer: {checkout_url}")
print(f"⏱️ Payment expires at: {result.expires_at}")
# Step 3: Wait for settlement confirmation
# Option A: Poll the status (simple, no infra)
payment_settled = False
while not payment_settled:
status = await client.get_payment_status(result.request_id)
if status.state == "SETTLED":
print(f"✅ Payment confirmed! Amount: {status.amount_received_vnd}")
print(f"🏦 Transaction ID: {status.bank_tx_id}")
# Unlock the course
await unlock_course_access("john_doe_123")
payment_settled = True
elif status.state == "EXPIRED":
print("❌ Payment link expired. Customer must retry.")
break
else:
# Still pending
print(f"⏳ Still waiting... (state: {status.state})")
await asyncio.sleep(5) # Check every 5 seconds
async def unlock_course_access(customer_id: str):
"""Agent helper: Grant course access after payment."""
# In real scenario: update DB, send email with access link, etc.
print(f"🎓 Activated course for {customer_id}")
# Run the agent
asyncio.run(sell_course())
Line-by-line explanation:
- Lines 1–6: Import the SDK's core classes.
- Lines 8–15: Initialize the
PaymentClientwith your merchant details from environment variables. - Lines 18–46: The main agent function. It creates a payment request (amount, description, customer ID), gets a checkout URL, prints it for the customer, and enters a loop waiting for settlement.
- Lines 36–44: The settlement loop. Every 5 seconds, it checks the payment status. If settled, it unlocks the course. If expired, it exits.
- Line 48: The helper function that would integrate with your course platform (DB update, email, etc.).
Code Example 2: Using MCP Server with Claude
If you want Claude or another agent to automatically handle payments without custom code, use the MCP server:
agentpay-mcp --merchant-account 1234567890 \
--merchant-name "Your Business" \
--merchant-id merchant_abc123 \
--bank-code 970016
Then configure Claude via MCP in your claude_desktop_config.json:
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": [
"--merchant-account", "1234567890",
"--merchant-name", "Your Business",
"--merchant-id", "merchant_abc123",
"--bank-code", "970016"
],
"env": {
"AGENTPAY_BANK_FEED_URL": "https://your-bank-api.example.com"
}
}
}
}
Now, when you chat with Claude:
User: "I want to buy the advanced Python course for 499k VND."
Claude (with MCP access): "I'll create a payment link for you. [calls create_payment_request] Here's your link: https://checkout.agentpay.vn/pay/req_xyz123. Once you pay, I'll send you access immediately."
Claude can now handle the entire transaction flow without custom integrations.
Real-World Walkthrough: Online Cafe Shop Bot
Let's build a concrete example: Café Automation Bot that takes orders and collects payment via VietQR.
Scenario: A café owner wants an AI chatbot on their website. Customers order coffee, pay via VietQR, and get a receipt with pickup instructions.
Flow:
import asyncio
from agentpay_vn import PaymentClient, PaymentRequest
from datetime import datetime
client = PaymentClient(
merchant_account="9999888877776666",
merchant_name="Cafe Hanoi",
merchant_id="cafe_hanoi_001",
bank_code="970016"
)
async def cafe_checkout(order_items: list, customer_phone: str):
"""
Customer orders: ["Cà phê đen", "Bánh mì"] for 85k VND.
"""
total_vnd = sum(item["price"] for item in order_items)
order_id = f"order_{datetime.now().timestamp()}"
# Create VietQR payment
payment = PaymentRequest(
amount_vnd=int(total_vnd),
description=f"Order: {', '.join(o['name'] for o in order_items)}",
customer_id=customer_phone,
reference_id=order_id,
expires_in_minutes=10 # Short window for walk-in café
)
result = await client.create_payment_request(payment)
# Send checkout link
print(f"☕ Your order total: {total_vnd:,} VND")
print(f"📱 Pay here: {result.checkout_url}")
print(f"⏰ Link expires in 10 minutes")
# Poll for payment
start = datetime.now()
while (datetime.now() - start).seconds < 600: # 10 min timeout
status = await client.get_payment_status(result.request_id)
if status.state == "SETTLED":
print(f"✅ Payment successful!")
print(f"🎟️ Your receipt number: {order_id}")
print(f"⏱️ Pickup in 15 minutes at counter 2")
return True
await asyncio.sleep(3)
print("❌ Payment timed out. Please try again.")
return False
# Example usage
orders = [
{"name": "Cà phê đen", "price": 25_000},
{"name": "Bánh mì", "price": 60_000}
]
asyncio.run(cafe_checkout(orders, "0987654321"))
Advantages for the café: - Zero setup complexity (one env config) - Money lands in bank account instantly - Customer confirmation is immediate - No card fraud, no chargebacks, no fees
Comparison: AgentPay VN vs. Alternatives
| Aspect | AgentPay VN | Stripe | PayPal | Direct Bank Transfer |
|---|---|---|---|---|
| Vietnam VND native | ✅ | ❌ (requires workarounds) | ⚠️ (slow) | ✅ |
| Agent-ready (MCP) | ✅ | ❌ | ❌ | ❌ |
| Money held by platform | ❌ | ✅ | ✅ | ❌ |
| Settlement time | Minutes | 1–2 days | 2–3 days | 1–2 hours |
| Fees | 0% (no money touch) | 2.9% + 30¢ | 2.2% + 0.3$ | Bank dependent |
| QR checkout | ✅ | ⚠️ (complex) | ❌ | Manual |
| Open-source | ✅ | ❌ | ❌ | N/A |
| No PCI burden | ✅ | ⚠️ | ⚠️ | ✅ |
Advanced Tips and Patterns
Webhook Alternative: Bank Feed Polling
Instead of polling get_payment_status every 5 seconds, connect a bank feed:
from agentpay_vn import BankFeedListener
listener = BankFeedListener(
bank_api_url="https://api.vietcombank.com/feeds",
api_key=os.getenv("BANK_API_KEY"),
merchant_account=os.getenv("AGENTPAY_MERCHANT_ACCOUNT")
)
# Register callback for incoming transfers
listener.on_transfer(callback=handle_settlement)
await listener.start() # Runs indefinitely, triggers callback on new transaction
Handling Partial/Overpayments
status = await client.get_payment_status(request_id)
if status.amount_received_vnd > status.amount_requested_vnd:
# Customer overpaid (common mistake)
refund_amount = status.amount_received_vnd - status.amount_requested_vnd
print(f"Overpayment detected: {refund_amount} VND. Refunding...")
# Manual refund via your bank (AgentPay VN doesn't refund)
elif status.amount_received_vnd < status.amount_requested_vnd:
# Underpayment
print("Underpayment detected. Asking customer to pay remainder...")
else:
# Exact match
print("✅ Exact payment received.")
Multi-Agent Coordination: Department Store Bot
# Agent 1: Salesman (takes order)
order = {"items": [...], "total": 2_500_000}
payment_request_id = await agent_salesman.create_payment(order)
# Agent 2: Accountant (monitors settlement, async)
async def monitor_payment():
while True:
status = await client.get_payment_status(payment_request_id)
if status.state == "SETTLED":
await agent_warehouse.ship_order(order) # Trigger Agent 3
break
await asyncio.sleep(10)
# Agent 3: Warehouse (ships after payment confirmed)
async def agent_warehouse.ship_order(order):
print(f"🚚 Shipping {order} to customer...")
Troubleshooting & Do's/Don'ts
✅ Do's
- Do store
request_idin your database for reconciliation. - Do use short expiry times (10–30 min) for better UX.
- Do implement idempotency: if a customer clicks "pay" twice, reuse the same request_id.
- Do log all settlement confirmations for accounting.
❌ Don'ts
- Don't create a new payment request for the same order; reuse the
request_id. - Don't assume settlement is instant; always wait for status confirmation.
- Don't expose merchant account details in frontend code (keep them in
.envserver-side). - Don't forget to test with real bank transfers in staging (AgentPay VN sandboxes are optional).
FAQ
Q: Does AgentPay VN store my money? No. AgentPay VN never touches funds. The VietQR points directly to your merchant bank account. Settlement is between the customer's bank and yours.
Q: Can I use AgentPay VN with Claude, GPT-4, or other LLMs?
Yes. Use the MCP server (agentpay-mcp) for Claude integration. For OpenAI/GPT-4, call the Python SDK directly in your backend, or wrap it in a function the agent can invoke.
Q: How long does settlement take? Typically 5–30 minutes for VietQR transfers. Your bank's feed API will confirm the exact moment. Some banks (e.g., Vietcombank) settle even faster with API access.
Q: Do I need a special merchant account? No. Any Vietnamese bank account works. No special "PayTech" license required.
Q: What if the customer's bank doesn't support VietQR? Most Vietnamese banks support VietQR (it's the national standard since 2021). Very old accounts might not; in that case, fall back to a direct bank transfer link or NAPAS card payment as a fallback.
Key Takeaways
- AgentPay VN is a purpose-built payment SDK for AI agents in Vietnam, eliminating Stripe/PayPal friction.
- Three-line flow: create request → send checkout URL → await settlement. No webhooks, no PCI compliance burden.
- Zero fees because the platform never holds money; cost is just your bank's standard VietQR fee (negligible).
- Agent-native via MCP: Claude and other LLMs can orchestrate payments automatically.
- Real-world ready: Works for course sales, café orders, subscription billing, marketplaces, and more.
- Open-source (MIT): Audit it, fork it, self-host the MCP server if needed.
Getting Started Now
- Install:
pip install agentpay-vn - Configure: Set your merchant account in environment variables.
- Code: Copy one of the examples above and adapt to your use case.
- Docs & Support: Visit https://agentpay.servicesai.vn/v1/docs or check the GitHub repo.
Your AI agent is now ready to collect VietQR payments. Ship fast. 🚀