AI Chatbot Payments Without Holding Funds: AgentPay VN
The Custody Problem Nobody Talks About
You've built an AI chatbot that sells digital products—online courses, design templates, software licenses. Your customers love it. But there's a silent risk lurking: where does the money live while it's being processed?
Most payment platforms require you to either: 1. Hold funds in an escrow account (compliance headache, regulatory scrutiny) 2. Use a third-party processor that takes 3–5% cuts and creates settlement delays 3. Build custom bank integrations (months of work, security nightmares)
None of these are good options for indie developers or small teams running AI agents in Vietnam or Southeast Asia.
AgentPay VN solves this with a radical simplification: your chatbot generates a VietQR code that points directly at your merchant bank account. Money never touches any intermediary.
Why Direct-to-Bank Payments Matter for AI Agents
When an AI agent collects payments, the architecture matters. Traditional setups add latency and friction:
- Escrow delays: Customer pays → platform holds → you request withdrawal → bank processes (3–7 days)
- API sprawl: Integrate with payment processor, reconcile with accounting, sync with bank feeds manually
- Compliance tax: PCI DSS, KYC verification, fund custody rules
AgentPay VN changes the equation:
Direct Settlement
Customer scans VietQR → money lands in your account within minutes → bank API confirms → agent logs completion.
This is especially powerful for: - SaaS trials & mini-payments ($1–$50 transactions where processor fees kill margins) - Course bundles sold by education chatbots - Freelancer rate cards issued by agent-powered services - Café/restaurant loyalty apps with AI personalization
How AgentPay VN Works: The Three-Line Mental Model
The entire flow lives in three operations:
1. create_payment_request() → generates VietQR code + checkout URL
2. send checkout_url() → agent delivers link to customer
3. await_settlement() → bank feed confirms payment, agent resumes
No fund custody. No escrow. No third-party risk.
Installation & Setup
Step 1: Install the SDK
pip install agentpay-vn
This installs:
- agentpay Python module (synchronous + async support)
- agentpay-mcp command (for Claude/MCP server integration)
Step 2: Configure Your Bank Account
You'll need a VietQR-compatible Vietnamese bank account (most major banks: Vietcombank, Techcombank, MB Bank, etc.). AgentPay VN doesn't store credentials—it generates signed requests to your bank's API.
If you're integrating with Claude or another AI agent:
export AGENTPAY_BANK_ID="970418" # Your bank code
export AGENTPAY_ACCOUNT="1234567890" # Your account number
export AGENTPAY_ACCOUNT_NAME="Your Business Name"
Real Code: Building a Course-Selling Chatbot
Let's walk through a live example: an AI agent selling a $29 Python course.
The Payment Request Flow
from agentpay_vn import PaymentClient
import asyncio
from datetime import datetime, timedelta
# Initialize the payment client
client = PaymentClient(
bank_id="970418", # Vietcombank
account_number="1234567890",
account_name="Tech Courses VN"
)
# User wants to buy the Python course
async def handle_course_purchase(user_id: str, course_title: str, price: int):
"""
user_id: "user_12345"
course_title: "Advanced Python for AI"
price: 290000 (in Vietnamese Dong)
"""
# Step 1: Create a payment request
# This generates a unique VietQR code + checkout URL
payment = await client.create_payment_request(
amount=price,
description=f"Purchase: {course_title}",
order_id=f"course_{user_id}_{int(datetime.now().timestamp())}",
metadata={ # Store context about the purchase
"user_id": user_id,
"course_id": "py-ai-001",
"course_title": course_title
},
expires_in=timedelta(hours=1) # QR code valid for 1 hour
)
# payment object contains:
# - payment.checkout_url: "https://checkout.agentpay.vn/pay/abc123xyz"
# - payment.qr_code: Base64-encoded VietQR image
# - payment.amount: 290000
# - payment.order_id: "course_user_12345_1699123456"
return payment
# Step 2: Send the checkout URL to the customer
async def send_checkout_to_customer(user_id: str, payment):
"""
In a real chatbot, this is where you'd send the message to the user.
Could be SMS, email, in-app notification, or Telegram.
"""
message = f"""
🎓 Your Python course is ready!
Click to pay: {payment.checkout_url}
Or scan this QR code:
[VietQR Image]
Payment amount: ₫290,000
Valid for: 1 hour
"""
# Send via your notification service
# await telegram_bot.send_message(user_id, message)
# await email_service.send(user_id, message)
return message
# Step 3: Wait for settlement confirmation
async def grant_course_access_after_payment(user_id: str, order_id: str):
"""
Poll the bank feed or use webhook (if configured) to confirm payment.
Once confirmed, unlock the course.
"""
# Wait for settlement (blocks until payment received or timeout)
settlement = await client.await_settlement(
order_id=order_id,
timeout_seconds=3600 # Give customer 1 hour to pay
)
if settlement.status == "confirmed":
# Bank confirmed the payment
print(f"✅ Payment confirmed! Bank reference: {settlement.bank_reference}")
# Now grant access to the course
await grant_course_access(
user_id=user_id,
course_id="py-ai-001",
download_url="https://courses.example.com/py-ai-001/download",
access_expires=datetime.now() + timedelta(days=365)
)
# Send confirmation
await notify_user_course_ready(user_id)
else:
# Payment not received
print(f"❌ Payment not received. Status: {settlement.status}")
await notify_user_payment_failed(user_id)
# Main agent flow
async def ai_agent_course_sales():
user_id = "user_12345"
course_title = "Advanced Python for AI"
price = 290000 # VND
# Create payment
payment = await handle_course_purchase(user_id, course_title, price)
# Notify customer
await send_checkout_to_customer(user_id, payment)
# Wait for payment
await grant_course_access_after_payment(user_id, payment.order_id)
# Run it
if __name__ == "__main__":
asyncio.run(ai_agent_course_sales())
Line-by-line breakdown:
create_payment_request(): Generates a unique VietQR tied to your account. The QR code is stateless—it's just instructions to transfer money to you.metadata: Store context (user ID, course ID) so when the payment arrives, you know who it's from.expires_in: QR codes can expire. Useful to prevent accidental late payments.await_settlement(): Blocks until the bank confirms the transaction. This is where your agent "knows" the payment is real.- Grant access: Once confirmed, you're free to unlock content, send download links, etc.
Integrating with Claude via MCP
If you're using Claude (or another LLM via Model Context Protocol), AgentPay VN exposes an MCP server:
# Start the MCP server
agentpay-mcp serve --bank-id 970418 --account 1234567890
Then configure Claude's claude_desktop_config.json:
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": ["serve"],
"env": {
"AGENTPAY_BANK_ID": "970418",
"AGENTPAY_ACCOUNT": "1234567890",
"AGENTPAY_ACCOUNT_NAME": "My Business"
}
}
}
}
Now Claude can:
User: "Sell me your $29 course"
Claude (with AgentPay MCP):
1. Calls agentpay.create_payment_request(amount=290000, ...)
2. Gets checkout_url back
3. Sends it to you
4. Calls agentpay.await_settlement() to wait for confirmation
5. Delivers your course once confirmed
This is a fully autonomous AI payment flow.
Real-World Walkthrough: Coffee Shop Loyalty AI
Imagine a café chain using an AI agent to manage loyalty rewards:
The Flow: 1. Customer orders coffee at register 2. Cashier asks: "Want loyalty points? Scan this QR or join via chatbot" 3. Customer chats with AI: "I want a 50-point card" 4. Agent creates payment request: 50,000 VND → 50 loyalty points 5. Customer scans QR, pays directly to café's bank 6. Bank confirms within 30 seconds 7. Agent logs points, sends digital card
Code snippet for this:
async def issue_loyalty_card(customer_phone: str, points: int, price_per_point: int = 1000):
"""
Issue a loyalty card via payment.
price_per_point = 1000 VND, so 50 points = 50,000 VND
"""
total_price = points * price_per_point
payment = await client.create_payment_request(
amount=total_price,
description=f"Loyalty card: {points} points",
order_id=f"loyalty_{customer_phone}_{int(datetime.now().timestamp())}",
metadata={"phone": customer_phone, "points": points}
)
# Send QR via SMS or chatbot
await send_qr_to_phone(customer_phone, payment.qr_code)
# Wait for payment
settlement = await client.await_settlement(payment.order_id)
if settlement.status == "confirmed":
# Create the loyalty account
await loyalty_database.create_card(
phone=customer_phone,
points=points,
created_at=datetime.now()
)
await send_sms(customer_phone, f"✅ Loyalty card created! {points} points available.")
Do's and Don'ts
| ✅ Do | ❌ Don't |
|---|---|
| Use for one-time purchases ($1–$10,000 range) | Hold customer funds between transactions |
| Store metadata (user ID, item ID) for reconciliation | Ignore expired QR codes (implement timeouts) |
| Set short expiry windows (1–2 hours for best UX) | Process payments without confirming via bank feed |
Log every await_settlement() call for auditing |
Use this for gambling or regulated financial services |
| Test with small amounts first ($1 test transactions) | Assume QR codes are secure without TLS (use HTTPS) |
| Use async/await for non-blocking agent flows | Build your own bank integration (use the SDK!) |
Advanced Tips
1. Batch Reconciliation
For high-volume agents, fetch all settled transactions periodically:
async def daily_reconciliation():
# Get all payments settled today
settled = await client.list_settlements(
start_date=datetime.now().date(),
status="confirmed"
)
for settlement in settled:
print(f"Order {settlement.order_id}: {settlement.amount} VND")
# Sync with your accounting system
2. Webhook Notifications
Instead of polling, register a webhook:
await client.register_webhook(
url="https://your-api.com/agentpay/webhook",
events=["payment.confirmed", "payment.expired"]
)
# Your server receives POST:
# {
# "event": "payment.confirmed",
# "order_id": "course_user_12345_1699123456",
# "amount": 290000,
# "bank_reference": "BK202401011234567"
# }
3. Retry Logic for Stalled Payments
async def payment_with_retries(order_id, max_retries=3):
for attempt in range(max_retries):
try:
settlement = await client.await_settlement(order_id, timeout_seconds=300)
return settlement
except TimeoutError:
if attempt < max_retries - 1:
await asyncio.sleep(60) # Wait 1 min, try again
else:
raise
FAQ
Q: What if a customer pays but the amount is wrong?
A: Bank feeds include the exact amount transferred. Your agent can validate settlement.amount against the expected price and either accept (if overpaid), refund (if underpaid), or reject. Refunds are issued back to the customer's bank account.
Q: Does AgentPay VN work with non-Vietnamese banks?
A: It requires a Vietnamese merchant bank account and VietQR compatibility. International transfers are beyond scope—the design assumes VND transactions within Vietnam. International merchants should use a VN bank partner or Wise API instead.
Q: How long does settlement confirmation take?
A: 30 seconds to 5 minutes typically. VietQR transfers are instant inter-bank; the delay is your agent polling the bank feed. Use webhooks for faster notification.
Q: Is this PCI DSS compliant?
A: Yes. AgentPay VN never handles card data. Money goes directly bank-to-bank via VietQR (QR code is just an instruction). You're not storing, processing, or transmitting card details.
Key Takeaways
- 🚀 Direct settlement: Money lands in your bank account; no intermediary holds funds
- 💻 3-line API:
create_payment_request()→send_checkout_url()→await_settlement() - 🤖 Perfect for AI agents: Async/await flow, lightweight, zero custody overhead
- 📦 Open-source MIT license: Audit the code, self-host, no vendor lock-in
- 💰 Ideal for micro-transactions: $1–$50 course bundles, loyalty cards, digital goods
- 🏦 Vietnamese-first: Leverages VietQR ecosystem; requires VN bank account
- 🔐 Compliance-friendly: No PCI DSS burden, no fund escrow, direct bank transfer
Getting Started Now
Install AgentPay VN:
pip install agentpay-vn
Read full documentation:
https://agentpay.servicesai.vn/v1/docs
Browse source code:
https://github.com/phuocdu/agentpay-vn
Next steps: 1. Clone the repo and run the example chatbot 2. Generate a test VietQR code with your bank credentials 3. Build your first payment flow (you can test with small amounts) 4. Integrate into Claude or your LLM of choice via MCP
Your AI agent is now ready to collect payments without ever touching a customer's money. No escrow, no middleman, no excuses.
Happy selling. 🎉