AI Chatbot Payment Processing Without Holding Funds
The Real Problem: Why Your AI Chatbot Can't Sell Yet
You've built an impressive AI chatbot—it answers questions, provides recommendations, even closes sales conversations. But the moment a customer says "I'm ready to buy," you hit a wall. You either:
- Direct them to your website (friction, cart abandonment, you lose the conversation)
- Ask for credit card details in chat (security nightmare, compliance liability, customer distrust)
- Use a payment gateway that holds funds (cash flow delays, complexity, regulatory headaches)
- Do nothing (leave money on the table)
This is the gap AgentPay VN fills: a lightweight Python SDK that lets your AI agent collect payments directly into your bank account—no intermediary holding funds, no PCI compliance burden, no friction.
Imagine a customer chatting with your AI, asking about your $29 e-book. They say "I'll take it." Your agent generates a VietQR payment link, the customer scans and pays their bank, settlement hits your account instantly, and your agent delivers the product—all within the chat. That's the workflow we'll build today.
Why VietQR + AI Agents Is a Game Changer for Vietnamese Creators
VietQR is Vietnam's fastest-growing interbank payment standard. Unlike traditional payment gateways, it's:
- Ubiquitous: Every Vietnamese has a bank app that scans QR codes
- Settlement-direct: Money goes straight to your merchant account—no middleman holds it
- Instant confirmation: Bank feeds give you real-time payment status
- Zero PCI burden: Your chatbot never touches card data
AgentPay VN wraps VietQR in a dead-simple Python API designed for AI agents. The SDK is open-source (MIT license), meaning you own the code, no vendor lock-in.
Why this matters: You can launch a 24/7 AI cashier for your digital products in under an hour, without payment processing certification or escrow accounts.
The Architecture: How AgentPay VN Works Under the Hood
Before we code, understand the flow—it's surprisingly clean:
Customer → AI Chatbot → create_payment_request() → VietQR URL
↓
Bank App (scan QR) → Payment processed
↓
Bank Feed → await_settlement() confirms receipt
↓
AI Agent → Delivers product, sends receipt
Three critical facts:
- AgentPay VN never holds money. The QR code points directly at your merchant bank account. Settlement is between customer and your bank.
- Bank feed confirmation is the lock. Your server only confirms payment after your bank confirms it—not by a timeout, not by third-party assertion.
- The AI agent orchestrates. It creates requests, monitors settlement, delivers goods—it's the cashier.
Step 1: Install and Configure AgentPay VN
Installation
First, add AgentPay VN to your Python environment:
pip install agentpay-vn
Verify the install:
python -c "import agentpay_vn; print(agentpay_vn.__version__)"
Configuration
You'll need three environment variables (get these from your bank's VietQR merchant dashboard):
export AGENTPAY_MERCHANT_ID="your_merchant_id_here"
export AGENTPAY_MERCHANT_NAME="Your Business Name"
export AGENTPAY_BANK_ACCOUNT="your_bank_account_number"
Optionally, for MCP server integration (if using Claude or another AI), set:
export AGENTPAY_MCP_PORT="8765"
Step 2: Create Your First Payment Request (Code Walkthrough)
Here's a real-world example: an AI agent selling a Python course ($49).
from agentpay_vn import PaymentClient, PaymentRequest
import uuid
from datetime import datetime, timedelta
# Initialize the payment client
client = PaymentClient()
# Create a payment request
payment_request = PaymentRequest(
order_id=str(uuid.uuid4()), # Unique ID for this transaction
amount=49.00, # Amount in USD (or your currency)
description="Python Advanced Course – 12-week program",
customer_name="Nguyen Van A", # Optional, but helpful for bank reconciliation
callback_url="https://yourdomain.com/api/payment-webhook", # Where we'll send settlement confirmation
expires_at=datetime.now() + timedelta(hours=24), # QR expires in 24 hours
)
# Generate the VietQR checkout URL
checkout_url = client.create_payment_request(payment_request)
print(f"Send this link to customer: {checkout_url}")
# Output example: https://vietqr.io/checkout/abc123xyz789
Line-by-Line Explanation:
- order_id: Unique identifier for this sale. Use
uuid.uuid4()to guarantee no collisions. - amount: The price, in your base currency (typically VND for Vietnam; AgentPay handles conversion).
- description: Appears on the customer's bank statement—be clear ("Python Course" is better than "item").
- customer_name: Optional but recommended. Helps reconciliation if a payment fails.
- callback_url: Your server endpoint that receives settlement confirmation from the bank feed. We'll cover webhooks below.
- expires_at: How long the QR code is valid. 24 hours is typical for course sales.
The client.create_payment_request() returns a checkout URL—this is what you send to the customer via chat, SMS, or email.
Step 3: Wait for Settlement (The Critical Part)
Once the customer scans and pays, your server must await confirmation. Here's where most payment systems fail—they timeout-guess or trust an unverified callback. AgentPay VN uses a bank feed instead:
from agentpay_vn import SettlementStatus
import asyncio
from typing import Optional
async def wait_for_payment(order_id: str, timeout_seconds: int = 3600) -> Optional[dict]:
"""
Poll for settlement confirmation from the bank feed.
Returns payment details if confirmed, None if timeout.
"""
try:
settlement = await client.await_settlement(
order_id=order_id,
timeout_seconds=timeout_seconds, # Wait up to 1 hour
)
if settlement.status == SettlementStatus.CONFIRMED:
print(f"✓ Payment confirmed for {order_id}")
print(f" Amount: {settlement.amount}")
print(f" Customer: {settlement.customer_name}")
print(f" Settled at: {settlement.settled_at}")
return settlement.to_dict()
elif settlement.status == SettlementStatus.FAILED:
print(f"✗ Payment failed for {order_id}")
return None
except asyncio.TimeoutError:
print(f"⏱ Payment timeout for {order_id} – customer didn't pay within window")
return None
# Usage in your AI chatbot flow:
order_id = "your-uuid-from-step-2"
payment_result = asyncio.run(wait_for_payment(order_id))
if payment_result:
# Deliver the product
print("Sending course access link to customer...")
else:
print("Payment not received. Offer retry or alternative.")
What's Happening Here:
- await_settlement(): Polls your bank feed in real-time (via encrypted connection). It doesn't guess or timeout—it waits for actual bank confirmation.
- timeout_seconds: Safety valve. If the customer doesn't pay within 1 hour, we stop waiting and return
None. - SettlementStatus.CONFIRMED: Bank confirmed the money landed in your account. Safe to deliver goods.
- to_dict(): Converts the settlement object to a dictionary for logging, webhooks, or database storage.
This is the magic: you only deliver when the bank says the money is yours.
Step 4: Integrate with Your AI Agent (MCP Server Example)
If you're using Claude, Anthropic's MCP (Model Context Protocol) lets your AI agent call AgentPay directly:
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"env": {
"AGENTPAY_MERCHANT_ID": "your_merchant_id",
"AGENTPAY_MERCHANT_NAME": "Your Business",
"AGENTPAY_BANK_ACCOUNT": "your_account_number"
},
"args": ["--port", "8765"]
}
}
}
Save this to ~/.claude/mcp.json (or your tool config path). Now Claude can:
- Call
agentpay_create_payment_requestto generate checkout links - Call
agentpay_await_settlementto confirm payments - All within the conversation context
Your AI chatbot workflow becomes:
Customer: "I want to buy the course."
Claude: "Great! Here's your secure payment link: [link]."
[waits for settlement in the background]
"Payment confirmed! Here's your access: [course-link]."
Real-World Walkthrough: An Online Course Chatbot
Let's build a complete example: a chatbot that sells a Vietnamese language course.
from agentpay_vn import PaymentClient, PaymentRequest
import uuid
import asyncio
from datetime import datetime, timedelta
class CourseBot:
def __init__(self):
self.payment_client = PaymentClient()
self.courses = {
"beginner": {"name": "Vietnamese 101", "price": 29.00},
"intermediate": {"name": "Conversational Vietnamese", "price": 49.00},
"advanced": {"name": "Business Vietnamese", "price": 99.00},
}
async def handle_purchase(self, customer_name: str, course_level: str):
"""
End-to-end: create payment → wait for settlement → deliver product.
"""
if course_level not in self.courses:
return f"Sorry, we don't have a '{course_level}' course."
course = self.courses[course_level]
order_id = str(uuid.uuid4())
# Step 1: Create payment request
payment_req = PaymentRequest(
order_id=order_id,
amount=course["price"],
description=f"Course: {course['name']}",
customer_name=customer_name,
expires_at=datetime.now() + timedelta(hours=24),
)
checkout_url = self.payment_client.create_payment_request(payment_req)
print(f"[Bot] Payment link created: {checkout_url}")
# Step 2: Wait for settlement
settlement = await self.payment_client.await_settlement(
order_id=order_id,
timeout_seconds=3600,
)
if settlement:
# Step 3: Deliver the course
access_token = f"course_{order_id}_access"
return f"""
✓ Payment confirmed!
Course: {course['name']}
Amount paid: VND {settlement.amount}
Your access token: {access_token}
Dashboard: https://yoursite.com/dashboard?token={access_token}
"""
else:
return "Payment not received. Please try again or contact support."
# Usage:
bot = CourseBot()
result = asyncio.run(bot.handle_purchase("Tran Thi B", "intermediate"))
print(result)
Why this works:
- No payment processor account needed (beyond your bank's VietQR merchant setup).
- No fund holding – money is yours instantly.
- No PCI compliance – you never see card numbers.
- Asyncio-friendly – scales to hundreds of concurrent customers.
- Auditable – each
order_idmaps to a real bank transaction.
Do's and Don'ts for Payment Integration
| Do | Don't |
|---|---|
Store the order_id in your database immediately after create_payment_request() |
Don't deliver products before await_settlement() returns confirmed |
Set a reasonable expires_at (24 hours for digital, 30 min for food) |
Don't poll faster than 5-second intervals (wastes resources) |
| Log every settlement confirmation with timestamp and amount | Don't ignore failed settlements—contact the customer |
Use unique order_id (UUID) for every request |
Don't reuse order IDs across transactions |
Encrypt and secure callback_url endpoints |
Don't expose merchant ID in client-side code |
| Test with the sandbox environment first | Don't go live without testing 5+ payments end-to-end |
Advanced: Webhook Confirmation for Scale
For high-volume sales, polling can be slow. AgentPay supports bank feed webhooks:
# In your FastAPI/Flask app:
from fastapi import FastAPI, Request
import hmac
import hashlib
app = FastAPI()
@app.post("/api/settlement-webhook")
async def settlement_webhook(request: Request):
"""
Bank feeds POST here when payment is confirmed.
AgentPay signs the payload—verify the signature.
"""
payload = await request.json()
signature = request.headers.get("X-Agentpay-Signature")
# Verify signature (HMAC-SHA256)
computed_sig = hmac.new(
key=b"your-webhook-secret",
msg=str(payload).encode(),
digestmod=hashlib.sha256,
).hexdigest()
if signature != computed_sig:
return {"error": "Invalid signature"}, 403
# Safe to process
order_id = payload["order_id"]
amount = payload["amount"]
# Deliver product, log transaction, etc.
deliver_course(order_id)
return {"status": "received"}, 200
Webhooks eliminate polling lag—you're notified the instant settlement is confirmed.
FAQ
Q: What if the customer's payment fails midway?
A: await_settlement() will timeout after your specified window (default 1 hour) and return None. Your bot can ask them to retry, offer an alternative payment method, or refund (if they overpaid).
Q: Can I use AgentPay for recurring subscriptions? A: Not yet—AgentPay VN currently handles one-time payments. For subscriptions, create a new payment request each month and track via your database.
Q: Is there a transaction limit or fee? A: Limits depend on your bank's VietQR merchant tier (typically VND 100M+/day). AgentPay itself is open-source and free. You pay your bank's standard VietQR settlement fees (usually 0.5–1% or flat fee).
Q: Can I refund a customer? A: AgentPay VN doesn't handle refunds—your bank does. Once settled, you initiate a refund via your bank's dashboard or API. AgentPay confirms the settlement; refund mechanics are between you and your bank.
Key Takeaways
- AgentPay VN is a 3-line Python SDK that turns your AI chatbot into a payment processor—no fund holding, no PCI liability.
- VietQR + bank feed = trust. Payments go directly to your account and are confirmed by the bank, not a third party.
- Install in 30 seconds:
pip install agentpay-vn. Integrate in under an hour. - Real settlement confirmation matters: wait for
SettlementStatus.CONFIRMED, not timeouts. - Scale from day one: async/await support, webhook integration, and no payment processor account needed.
- Open-source (MIT): You own the code. No vendor lock-in, no surprise shutdowns.
Next Steps
Ready to launch? Start here:
- Install AgentPay VN: Run
pip install agentpay-vnin your project. - Get your VietQR merchant credentials from your bank (usually 24-hour turnaround).
- Read the full docs: https://agentpay.servicesai.vn/v1/docs
- Clone the example repo: https://github.com/phuocdu/agentpay-vn (includes MCP server config, course bot, and test suite).
- Test end-to-end with a small payment before going live.
Your AI cashier is waiting. Stop leaving money on the table. Start selling today.