Sell Digital Products from AI Chatbots Without Holding Funds

2026-06-25 · AgentPay VN

ai-paymentsvietqrpython-sdkchatbot-commerceopen-source

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:

  1. Integrating merchant accounts (Stripe, PayPal) with weeks of paperwork
  2. Holding customer funds in an escrow account—creating compliance headaches, fraud risk, and tax complexity
  3. Building settlement logic to eventually move money to your bank
  4. 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


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:

  1. Create: Agent defines what's being sold (price, description, order ID).
  2. Send: Agent retrieves checkout URL and shares it with the customer (QR code or link).
  3. 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:


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:

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


Get Started Now

  1. Install the SDK: bash pip install agentpay-vn

  2. Explore the docs: https://agentpay.servicesai.vn/v1/docs

  3. Star the repo: https://github.com/phuocdu/agentpay-vn

  4. 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.

Get started →

← All posts