Sell Digital Products from AI Chatbot Without Holding Funds
The Problem: Why Most AI Commerce Startups Hit a Compliance Wall
Imagine you've built a brilliant AI chatbot that sells online courses, design templates, or software licenses. Your bot engages customers perfectly, the conversion rate is solid—but then you hit the regulatory brick wall.
You're being asked to hold customer funds, get a payment institution license, maintain segregated accounts, and comply with financial regulations that were written before AI agents existed. Suddenly, your indie project or startup needs lawyers, compliance officers, and a mountain of paperwork just to process payments.
Worse: you're holding money that isn't yours, creating liability and trust issues with both customers and regulators.
There's a better way. What if your AI agent could collect payments without ever touching the money?
Why Direct Bank Settlement Changes the Game
AgentPay VN solves this by doing something elegant: the payment flow points directly at your merchant bank account via VietQR codes. No intermediary holdings. No escrow accounts. No special licenses.
Here's the architecture:
- Your AI agent calls
create_payment_request()when a customer wants to buy - A VietQR QR code is generated pointing to your bank account
- Customer scans and pays using their banking app
- Bank feed confirms the settlement
- Agent delivers the product (instantly or with webhook verification)
The entire transaction flow—from sale initiation to delivery—happens with zero fund custody. Your agent is an orchestrator, not a money handler.
Getting Started: Installation and Setup
Step 1: Install the SDK
pip install agentpay-vn
The package includes the core Python SDK plus CLI utilities. For AI agent integration, you'll also want the MCP server:
pip install agentpay-mcp
Step 2: Configure Your Merchant Details
Create a simple config file or environment variables with your merchant information:
# config.py
import os
from agentpay_vn import MerchantConfig
merchant = MerchantConfig(
merchant_id="YOUR_MERCHANT_ID",
merchant_name="Your Business Name",
bank_account="YOUR_VIETQR_ENABLED_ACCOUNT",
bank_code="BIDV", # or ACB, Vietcombank, etc.
)
Your bank account must support VietQR (most Vietnamese banks do as of 2024). Check with your bank if unsure.
Step 3: Set Up MCP Server for Claude Integration
If you're building an agent that runs on Claude or similar LLM platforms, configure the MCP server:
{
"mcpServers": {
"agentpay": {
"command": "python",
"args": ["-m", "agentpay_mcp"],
"env": {
"MERCHANT_ID": "YOUR_MERCHANT_ID",
"MERCHANT_NAME": "Your Business",
"BANK_ACCOUNT": "YOUR_ACCOUNT_NUMBER",
"BANK_CODE": "BIDV"
}
}
}
}
This exposes AgentPay functions as tools your AI agent can call directly.
The Core Workflow: 3-Line Payment Flow
At its heart, AgentPay VN is built on three core operations:
Create Payment Request
from agentpay_vn import PaymentClient, PaymentRequest
# Initialize client
client = PaymentClient(merchant_id="YOUR_ID", bank_account="123456789")
# Create a payment request for a digital product
request = PaymentRequest(
request_id="course_001_user_42", # Unique identifier
amount_vnd=299000, # Price in Vietnamese Dong
description="Python Mastery Online Course",
customer_phone="0987654321", # Optional but useful
metadata={ # Custom data for your bot
"product_type": "course",
"course_id": "python-101",
"user_id": "user_42",
"delivery_method": "email"
}
)
# Generate VietQR QR code
qr_response = client.create_payment_request(request)
print(f"QR Code URL: {qr_response.qr_url}")
print(f"Checkout URL: {qr_response.checkout_url}")
What happens here:
- request_id must be unique per transaction; use UUIDs or timestamp combos
- amount_vnd is the total; no decimal places (VND is whole numbers)
- metadata stores context your agent needs later (product ID, user ID, etc.)
- The returned qr_url can be displayed in your chatbot interface
Send Checkout URL to Customer
Your AI agent displays the QR or sends the checkout URL:
# In your chatbot logic
def sell_product(user_id, product_id, price_vnd):
request = PaymentRequest(
request_id=f"{product_id}_{user_id}_{int(time.time())}",
amount_vnd=price_vnd,
description=f"Purchase: {product_id}",
metadata={"user_id": user_id, "product_id": product_id}
)
response = client.create_payment_request(request)
message = (
f"Great! Here's your checkout link:\n"
f"{response.checkout_url}\n\n"
f"Or scan this QR code in your banking app.\n"
f"Once paid, you'll get instant access!"
)
return message, response
Await Settlement and Deliver Product
import time
from agentpay_vn import SettlementChecker
# Poll for settlement confirmation
checker = SettlementChecker(client)
def deliver_on_payment(request_id, user_id, product_id, timeout_seconds=300):
"""
Wait for payment, then deliver product.
"""
start_time = time.time()
while time.time() - start_time < timeout_seconds:
settlement = checker.await_settlement(
request_id=request_id,
poll_interval=3 # Check every 3 seconds
)
if settlement.status == "CONFIRMED":
print(f"✓ Payment confirmed! Transaction: {settlement.transaction_id}")
print(f" Amount: {settlement.amount_vnd} VND")
print(f" Bank reference: {settlement.bank_ref}")
# Deliver the product
deliver_course(user_id, product_id)
send_email(user_id, generate_download_link(product_id))
return {"status": "delivered", "transaction": settlement.transaction_id}
time.sleep(3)
raise TimeoutError(f"Payment not received within {timeout_seconds}s")
Key points:
- await_settlement() polls the bank feed; confirmations typically arrive within 10-30 seconds
- The metadata you stored earlier is included in the settlement object
- Once confirmed, your agent can immediately deliver the digital product
- Use transaction_id for your records and customer receipts
Real-World Example: AI Course Selling Bot
Let's walk through a complete scenario: an AI chatbot that sells a Python course.
Setup: - Course price: 299,000 VND - Merchant: "AI Academy" - Delivery: Instant email with access link
User interaction:
User: "I want to buy the Python Mastery course."
Bot: "Excellent choice! The Python Mastery course is 299,000 VND.
Would you like to proceed?"
User: "Yes, please."
Bot: [Calls create_payment_request]
"Great! Here's your checkout link: [URL]
Or scan this QR code in your banking app. Payment takes about
10-30 seconds to confirm. You'll get instant access once paid!"
User: [Opens banking app, scans QR, transfers 299,000 VND]
Bot: [Polls await_settlement every 3 seconds]
[After 15 seconds, settlement confirmed]
"Perfect! Payment received! 🎉
Here's your access link: [download_link]
Login credentials sent to your email.
Enjoy the course! Email us if you have questions."
Backend code:
from datetime import datetime
import uuid
class CourseBot:
def __init__(self, client):
self.client = client
self.checker = SettlementChecker(client)
async def handle_purchase_request(self, user_id, course_id):
# Look up course price
courses = {
"python-101": {"name": "Python Mastery", "price": 299000},
"web-dev": {"name": "Web Dev Bootcamp", "price": 599000},
}
course = courses.get(course_id)
if not course:
return {"error": "Course not found"}
# Create payment request
request = PaymentRequest(
request_id=f"{course_id}_{user_id}_{uuid.uuid4().hex[:8]}",
amount_vnd=course["price"],
description=f"Course: {course['name']}",
metadata={
"user_id": user_id,
"course_id": course_id,
"timestamp": datetime.now().isoformat()
}
)
qr_response = self.client.create_payment_request(request)
return {
"checkout_url": qr_response.checkout_url,
"qr_url": qr_response.qr_url,
"request_id": request.request_id,
"amount": course["price"]
}
async def await_and_deliver(self, request_id, user_id, course_id):
settlement = self.checker.await_settlement(
request_id=request_id,
poll_interval=3
)
if settlement.status == "CONFIRMED":
# Generate access link
access_link = self.generate_course_link(user_id, course_id)
# Send email
self.send_confirmation_email(
user_id,
course_id,
access_link,
settlement.amount_vnd
)
return {
"status": "delivered",
"access_link": access_link,
"transaction_id": settlement.transaction_id
}
Best Practices: Do's and Don'ts
| ✅ Do | ❌ Don't |
|---|---|
Use unique request_id per transaction (UUID or timestamp-based) |
Reuse request IDs; this breaks settlement matching |
Store metadata with user/product context for delivery logic |
Rely solely on request_id; metadata is essential |
| Set polling intervals to 3-5 seconds for fast delivery UX | Poll every second (wastes bandwidth) or every 60s (slow UX) |
Log transaction_id and bank_ref for audit trails |
Ignore bank references; you need these for disputes |
| Use timeout logic (300-600 seconds) to prevent infinite waits | Wait indefinitely without a timeout |
| Test with small amounts first (10,000-50,000 VND) | Go live with untested flows |
| Handle network errors gracefully (retry with backoff) | Assume perfect connectivity |
Advanced Tips for Production
1. Idempotency and Retries
Network hiccups happen. Implement idempotent delivery:
def deliver_product_idempotent(transaction_id, user_id, product_id):
# Check if already delivered
if is_delivery_record_exists(transaction_id):
return get_existing_delivery(transaction_id)
# Deliver and record
result = deliver_course(user_id, product_id)
record_delivery(transaction_id, user_id, product_id, result)
return result
2. Webhook Notifications (Future Enhancement)
Polling works, but for high-volume stores, consider webhook callbacks once AgentPay VN adds them:
# Pseudocode for future webhook support
request = PaymentRequest(
request_id="...",
amount_vnd=299000,
description="Course",
webhook_url="https://yourbot.com/agentpay/settlement"
)
3. Logging and Analytics
Track payment flows for insights:
import logging
logger = logging.getLogger("agentpay_sales")
logger.info(
f"Payment created: request_id={request.request_id}, "
f"user={metadata['user_id']}, amount={request.amount_vnd}"
)
logger.info(f"Settlement confirmed: txn_id={settlement.transaction_id}")
4. Multi-Currency (Future)
Currently, AgentPay VN works in VND only. If you serve international customers, implement a conversion layer:
USD_TO_VND = 24500 # Approximate
def price_in_vnd(usd_amount):
return int(usd_amount * USD_TO_VND)
FAQ
Q: Does AgentPay VN hold my customer's money? A: No. The VietQR code points directly to your merchant bank account. Settlement goes straight to your bank account within minutes. AgentPay VN is a payment orchestration layer only.
Q: Do I need a special license to use this? A: Not for basic payment orchestration. You're not a payment institution—you're simply directing customers to pay your merchant account. However, always check local regulations or consult a lawyer if required.
Q: What if a customer disputes a payment?
A: Bank reference (bank_ref) and transaction ID are logged. You can provide these to your bank or customer for dispute resolution. AgentPay VN logs all details for audit trails.
Q: Can I use this for subscription or recurring payments? A: Currently, AgentPay VN handles one-off payments. For subscriptions, create a new payment request each billing cycle and track it separately in your bot logic.
Q: How long does settlement confirmation take?
A: Typically 10-30 seconds. Some banks are faster. The await_settlement() function polls until confirmed; set a timeout appropriate for your UX (300-600 seconds is reasonable).
Key Takeaways
- Zero fund custody: VietQR points directly to your bank account; you never hold customer money
- Instant compliance: No payment institution licenses or escrow accounts needed
- 3-line workflow: Create request → send checkout URL → await settlement
- AI-native: Built for agents; MCP server integrates with Claude and similar platforms
- Open source (MIT): Full transparency; audit and self-host if needed
- Production-ready: Idempotency, logging, and bank feeds included
- Easy integration: One Python package (
pip install agentpay-vn) for SDK + MCP
Next Steps
Ready to build?
- Install AgentPay VN:
pip install agentpay-vn - Read the full docs: https://agentpay.servicesai.vn/v1/docs
- Explore the GitHub repo: https://github.com/phuocdu/agentpay-vn
- Start with a test flow: Use small amounts (10k-50k VND) to verify your setup
- Deploy your AI agent: Sell digital products with confidence
Your AI chatbot is about to become a sales engine—without the compliance headaches. Let's build.