Sell Digital Products from AI Chatbots Without Holding Funds
The Problem: Why AI Chatbots Can't Sell (Yet)
You've built an impressive AI chatbot. It answers questions, generates code, writes emails—but the moment a customer wants to buy something, the conversation hits a wall. They're redirected to a third-party checkout page. The bot loses context. The experience breaks.
Worse, if you're a bootstrapped founder or freelancer, adding payment processing means:
- Integrating merchant accounts (Stripe, PayPal) with weeks of paperwork
- Holding customer funds in an escrow account—creating compliance headaches, fraud risk, and tax complexity
- Building settlement logic to eventually move money to your bank
- Keeping your chatbot stateless while managing payment state externally
For Vietnamese entrepreneurs, there's an additional friction: international payment gateways often require US bank accounts or lengthy KYC processes. VietQR is instant, trusted, and ubiquitous—but there's been no SDK connecting it to AI agents.
Until now.
Enter AgentPay VN: Payments Without the Complexity
AgentPay VN is an open-source Python SDK (MIT license) that lets AI agents create payment requests and await settlement—without ever touching customer money. Here's the magic: the QR code points directly at your merchant's bank account. When the customer scans and pays via their bank app, the settlement confirmation comes back through your bank feed. Your chatbot doesn't hold, route, or process funds. It just orchestrates the request and waits for proof.
Think of it as a bridge: Agent ↔ AgentPay SDK ↔ VietQR ↔ Your Bank Account.
Why This Matters
- Zero fund custody: Money goes straight to your bank. No escrow, no middleman liability.
- Instant settlement: Bank-to-bank transfers confirm within seconds to minutes.
- Local trust: VietQR is embedded in every Vietnamese banking app.
- Agent-native: Built for AI workflows—sync, await, and respond.
- Open source: Full transparency; no vendor lock-in.
Installation & Setup: Get Running in 5 Minutes
Step 1: Install the SDK
pip install agentpay-vn
This gives you the core Python module. Verify:
python -c "import agentpay_vn; print(agentpay_vn.__version__)"
Step 2: Set Environment Variables
You'll need your VietQR merchant credentials (available from your bank's API portal):
export VIETQR_MERCHANT_ID="your_merchant_id"
export VIETQR_MERCHANT_NAME="Your Shop Name"
export VIETQR_ACCOUNT_NUMBER="your_bank_account"
export VIETQR_BANK_CODE="970012" # ACB example; check your bank
Step 3: (Optional) Set Up the MCP Server
If you're using Claude or another MCP-compatible AI, install the server wrapper:
pip install agentpay-mcp
agentpay-mcp --serve
This exposes AgentPay functions as tools Claude can call directly.
The Core Workflow: 3 Steps to Accept Payment
Understanding the flow is essential. AgentPay follows a simple state machine:
- Create: Agent defines what's being sold (price, description, order ID).
- Send: Agent retrieves checkout URL and shares it with the customer (QR code or link).
- Await: Agent polls or subscribes to settlement confirmation, then fulfills the order.
Let's code it.
Code Example 1: Minimal Payment Request
from agentpay_vn import AgentPayClient
from uuid import uuid4
# Initialize the client (uses env vars by default)
client = AgentPayClient()
# Step 1: Create a payment request
order_id = str(uuid4()) # Unique identifier for this order
payment_req = client.create_payment_request(
order_id=order_id,
amount_vnd=299_000, # Price in Vietnamese Dong
description="Professional Python Course (Lifetime Access)",
customer_email="student@example.com",
customer_name="Nguyen Minh Hao",
metadata={"course_id": "python-102", "license_type": "lifetime"}
)
print(f"Payment Request Created: {order_id}")
print(f"Checkout URL: {payment_req.checkout_url}")
print(f"QR Code URL: {payment_req.qr_code_url}")
# Step 2: Send the URL to the customer (via chatbot)
# Your bot would say: "Click here to pay" and embed the QR code
# Step 3: Wait for settlement
print("\nWaiting for customer payment...")
settlement = client.await_settlement(
order_id=order_id,
timeout_seconds=600 # 10-minute timeout
)
if settlement.confirmed:
print(f"✅ Payment received! {settlement.amount_vnd} VND")
print(f"Transaction ID: {settlement.transaction_id}")
# TODO: Grant access to course, send license key, etc.
else:
print("❌ Payment not received within timeout.")
Line-by-line breakdown:
create_payment_request(): Registers the payment intent with your bank's VietQR service. Returns a request object withcheckout_url(HTTP) andqr_code_url(for display in chat or email).customer_email,customer_name: Optional but recommended for bank reconciliation and receipts.metadata: Custom fields your system can reference later (e.g., course ID, subscription tier).await_settlement(): Blocks until the customer scans, pays, and your bank confirms. Returns a settlement object with transaction details.timeout_seconds: Prevents infinite waits; raise an exception if no payment arrives.
Real-World Example: An AI Course-Selling Chatbot
Imagine TechBot, an AI assistant that teaches and sells Python courses. A customer messages:
Customer: "I want to buy the Advanced Async course. How much?"
TechBot: "The Advanced Async course is 399,000 VND. Let me set up your purchase."
(TechBot creates a payment request)
TechBot: "Here's your checkout QR code. Scan it with your bank app to pay securely."
(TechBot displays QR; customer scans and pays via their banking app)
TechBot: "✅ Payment confirmed! Your lifetime license is active. Download the course materials here: [link]. Your license key is: XXX-YYY-ZZZ."
No third-party redirect. No fund custody. Money lands in your bank account. All within the chatbot conversation.
Here's the bot logic:
from agentpay_vn import AgentPayClient
from datetime import datetime
import asyncio
class TechBot:
def __init__(self):
self.agentpay = AgentPayClient()
self.courses = {
"async-101": {"price": 199_000, "title": "Async Basics"},
"async-202": {"price": 399_000, "title": "Advanced Async"},
}
async def buy_course(self, course_key: str, user_email: str, user_name: str):
"""Handle course purchase flow."""
if course_key not in self.courses:
return {"error": "Course not found"}
course = self.courses[course_key]
order_id = f"{user_email}_{course_key}_{int(datetime.now().timestamp())}"
# Create payment request
payment = self.agentpay.create_payment_request(
order_id=order_id,
amount_vnd=course["price"],
description=f"Purchase: {course['title']}",
customer_email=user_email,
customer_name=user_name,
metadata={
"course_id": course_key,
"type": "course_purchase"
}
)
# Return checkout info to the bot (which displays it to user)
return {
"status": "awaiting_payment",
"qr_code_url": payment.qr_code_url,
"checkout_url": payment.checkout_url,
"order_id": order_id,
"amount": course["price"]
}
async def confirm_purchase(self, order_id: str):
"""Poll for settlement and grant access."""
try:
settlement = self.agentpay.await_settlement(
order_id=order_id,
timeout_seconds=900 # 15 minutes
)
if settlement.confirmed:
# Extract course info from order_id metadata
# (In production, store order details in a DB)
return {
"status": "success",
"message": f"Payment confirmed! You now have lifetime access.",
"transaction_id": settlement.transaction_id,
"license_key": self.generate_license(order_id)
}
except TimeoutError:
return {
"status": "timeout",
"message": "Payment not received. Try again or contact support."
}
def generate_license(self, order_id: str) -> str:
"""Dummy license generator."""
import hashlib
return hashlib.sha256(order_id.encode()).hexdigest()[:16].upper()
# Usage in your chatbot
bot = TechBot()
# User says: "Buy Advanced Async"
buy_response = asyncio.run(bot.buy_course(
course_key="async-202",
user_email="hao@example.com",
user_name="Nguyễn Minh Hào"
))
print(f"Show this QR to the user: {buy_response['qr_code_url']}")
# After user pays, poll for settlement
confirm_response = asyncio.run(bot.confirm_purchase(
order_id=buy_response["order_id"]
))
print(confirm_response["message"])
Integrating with Claude via MCP
If you're using Claude (or another MCP-compatible AI), you can configure AgentPay as a tool.
MCP Configuration (JSON)
Add this to your claude_desktop_config.json or MCP server config:
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": ["--serve"],
"env": {
"VIETQR_MERCHANT_ID": "your_merchant_id",
"VIETQR_MERCHANT_NAME": "Your Shop",
"VIETQR_ACCOUNT_NUMBER": "your_account",
"VIETQR_BANK_CODE": "970012"
}
}
}
}
Once configured, Claude can call:
agentpay_create_payment_request(order_id, amount_vnd, description, customer_email, customer_name, metadata)agentpay_await_settlement(order_id, timeout_seconds)agentpay_get_settlement_status(order_id)
Clause can now autonomously handle payments without leaving the conversation.
Advanced Tips & Best Practices
1. Store Order Metadata in Your Database
Don't rely on order IDs alone. Keep a record:
import sqlite3
def save_order(order_id, user_id, course_id, amount, status="pending"):
conn = sqlite3.connect("orders.db")
cursor = conn.cursor()
cursor.execute(
"INSERT INTO orders (order_id, user_id, course_id, amount, status) "
"VALUES (?, ?, ?, ?, ?)",
(order_id, user_id, course_id, amount, status)
)
conn.commit()
conn.close()
When settlement arrives, update the status and trigger fulfillment logic (send license, enroll user, etc.).
2. Handle Timeouts Gracefully
Not every user pays immediately. Implement a retry or reminder system:
async def remind_unpaid_orders():
"""Periodic task to remind users about pending payments."""
conn = sqlite3.connect("orders.db")
cursor = conn.cursor()
cursor.execute(
"SELECT order_id, user_email FROM orders WHERE status = 'pending' "
"AND created_at < datetime('now', '-1 hour')"
)
for order_id, email in cursor.fetchall():
send_reminder_email(email, order_id) # Your email function
conn.close()
3. Deduplicate Payments
If a customer refreshes the checkout page or your bot retries, ensure you don't charge twice:
def create_safe_payment(order_id, amount, description):
"""Check if order already exists before creating."""
if order_exists(order_id):
# Return existing payment request instead
return get_existing_payment(order_id)
return client.create_payment_request(
order_id=order_id,
amount_vnd=amount,
description=description
)
4. Reconcile with Bank Feeds
AgentPay settlement callbacks are a guide, but always cross-check with your actual bank transactions via bank APIs. This catches discrepancies.
5. Custom Metadata for Analytics
Use metadata to track campaign performance:
payment_req = client.create_payment_request(
order_id=order_id,
amount_vnd=amount,
description=description,
metadata={
"utm_source": "facebook_ad",
"utm_campaign": "summer_sale",
"cohort": "early_adopters"
}
)
Do's and Don'ts
| Do | Don't |
|---|---|
| Use unique, idempotent order IDs | Reuse order IDs across different products |
| Store order details in your database | Rely solely on settlement callbacks |
| Set reasonable timeouts (5–15 min) | Use infinite timeouts in production |
| Test with small amounts first | Go live without testing merchant credentials |
| Include metadata for reconciliation | Leave metadata empty |
| Implement retry logic for bot failures | Assume first payment attempt always succeeds |
| Monitor bank feed for confirmation | Treat AgentPay callback as the source of truth |
FAQ
Q1: Does AgentPay hold my customer's money?
No. AgentPay never touches funds. The QR code directs payment straight to your bank account. Your bank confirms settlement; AgentPay merely orchestrates the request and notification.
Q2: How long does settlement take?
VietQR transfers are near-instant (typically 10–30 seconds). You'll receive the settlement confirmation within minutes of the customer scanning and authorizing the payment via their banking app.
Q3: Is AgentPay available outside Vietnam?
Currently, AgentPay is optimized for Vietnamese VietQR payments and Vietnamese bank accounts. International support may be added in future versions. Check the GitHub repository for roadmap updates.
Q4: What if a customer disputes a transaction?
Since payment is directly to your bank account, disputes are handled through your bank's standard chargeback process, not through AgentPay. Maintain good order records (database + metadata) to defend against chargebacks.
Key Takeaways
- AgentPay VN eliminates fund custody: Money goes directly to your bank; you avoid compliance complexity.
- Three-step flow: Create request → Share QR → Await settlement. Simple and synchronous.
- AI-native: Built for chatbots and agents; integrates seamlessly with Claude via MCP.
- Open source (MIT): Full transparency and no vendor lock-in. Audit the code yourself.
- Bank-backed: Leverages VietQR, which is trusted across all Vietnamese banking apps.
- Production-ready: Use metadata for reconciliation, implement retry logic, and maintain your own order database.
Get Started Now
-
Install the SDK:
bash pip install agentpay-vn -
Explore the docs: https://agentpay.servicesai.vn/v1/docs
-
Star the repo: https://github.com/phuocdu/agentpay-vn
-
Build your first payment agent using the examples above. Start with a test merchant account, create a few dummy orders, and verify settlement in your bank dashboard.
Your AI chatbot can now sell. No merchant account paperwork, no fund custody, no hassle. Just ship.