Sell Digital Products from AI Chatbots Without Holding Funds

2026-07-18 · AgentPay VN

ai-agentsvietqr-paymentspython-sdkdigital-productspayment-integration

The Real Problem: AI Commerce Without the Liability

Imagine you've built an AI chatbot that sells online courses, design templates, or software licenses. Your customers are ready to pay—but now you're stuck with a dilemma:

Do you: - Build a payment system that holds customer money (compliance headaches, fraud risk, regulatory burden)? - Use a third-party processor that takes weeks to verify your business (and 2–3% per transaction)? - Ask developers to choose between complexity and trust?

That's where most AI commerce projects stall. The payment layer either becomes a security liability or a cost sink.

AgentPay VN changes the game. It's an open-source Python SDK that lets your AI agents collect payments directly into your bank account—no escrow, no holding periods, no intermediary risk. The payment flow is instant, transparent, and built for Vietnam's VietQR banking standard.

What Is AgentPay VN? (The 60-Second Version)

AgentPay VN is a MIT-licensed Python SDK + MCP server that bridges AI agents and Vietnamese bank payments. Here's what makes it different:

Install it in seconds:

pip install agentpay-vn

Then spin up the MCP server for Claude integration:

agentpay-mcp

The Three-Line Payment Flow: How It Actually Works

Let's break down the core loop that powers payment collection:

Step 1: Create a Payment Request

Your AI agent receives a customer order. It calls create_payment_request() to initialize the transaction.

Step 2: Send the Checkout URL

The agent generates a VietQR code (or checkout link) and sends it to the customer—via chat, email, or your app UI.

Step 3: Await Settlement

Your code listens for a webhook from the customer's bank confirming the transfer. When it arrives, you deliver the digital product.

No polling. No manual verification. Just an event-driven pipeline.

Real Code: Building a Course-Selling Chatbot

Let's walk through a concrete example: an AI chatbot that sells Python courses.

Setting Up the Payment Handler

from agentpay_vn import AgentPayClient, create_payment_request, await_settlement
import json
from typing import Optional

# Initialize the client (use your merchant credentials)
client = AgentPayClient(
    merchant_id="YOUR_MERCHANT_ID",
    api_key="YOUR_API_KEY"
)

# Define your course catalog
COURSES = {
    "python-101": {"name": "Python 101: Basics", "price_vnd": 199000},
    "django-pro": {"name": "Django Pro: Advanced", "price_vnd": 499000},
    "ai-agents": {"name": "Building AI Agents", "price_vnd": 799000},
}

def create_course_purchase(course_id: str, customer_email: str) -> Optional[dict]:
    """
    Step 1: Create a payment request for a course.
    Returns checkout URL and QR code data.
    """
    if course_id not in COURSES:
        return {"error": "Course not found"}

    course = COURSES[course_id]

    # Create the payment request
    payment = create_payment_request(
        client=client,
        amount_vnd=course["price_vnd"],
        description=f"Purchase: {course['name']}",
        order_id=f"order_{course_id}_{int(time.time())}",
        merchant_email="merchant@example.com",
        customer_email=customer_email
    )

    if payment["status"] == "success":
        # Step 2: Send checkout URL to customer
        return {
            "course": course["name"],
            "price": course["price_vnd"],
            "checkout_url": payment["checkout_url"],
            "qr_data": payment["qr_code"],  # VietQR data
            "payment_id": payment["payment_id"]
        }
    else:
        return {"error": payment["error"]}

async def deliver_course(payment_id: str, customer_email: str) -> dict:
    """
    Step 3: Wait for settlement confirmation and deliver the course.
    Polls the settlement webhook or awaits async event.
    """
    # Wait for bank confirmation (timeout: 15 minutes)
    settlement = await await_settlement(
        client=client,
        payment_id=payment_id,
        timeout_seconds=900
    )

    if settlement["status"] == "settled":
        # Customer's bank has confirmed the transfer
        # Now deliver the digital product
        course_data = {
            "download_url": "https://cdn.example.com/courses/python-101.zip",
            "access_token": generate_access_token(customer_email),
            "expires_at": (datetime.now() + timedelta(days=365)).isoformat()
        }

        # Send course access via email
        send_course_email(customer_email, course_data)

        return {
            "status": "delivered",
            "message": f"Course sent to {customer_email}",
            "access": course_data
        }
    else:
        return {"status": "pending", "message": "Payment not yet confirmed by bank"}

Putting It in Your Chatbot

Now let's wire this into an AI agent conversation:

from anthropic import Anthropic
import json

client_anthropic = Anthropic()

def run_course_sales_agent():
    """
    AI agent that sells courses via conversation.
    Uses tools to create payments and await settlement.
    """
    tools = [
        {
            "name": "list_courses",
            "description": "Show available courses and prices",
            "input_schema": {
                "type": "object",
                "properties": {},
                "required": []
            }
        },
        {
            "name": "initiate_purchase",
            "description": "Create a payment request for a course",
            "input_schema": {
                "type": "object",
                "properties": {
                    "course_id": {"type": "string", "description": "e.g., 'python-101'"},
                    "customer_email": {"type": "string"}
                },
                "required": ["course_id", "customer_email"]
            }
        },
        {
            "name": "check_payment_status",
            "description": "Check if payment has settled",
            "input_schema": {
                "type": "object",
                "properties": {
                    "payment_id": {"type": "string"},
                    "customer_email": {"type": "string"}
                },
                "required": ["payment_id", "customer_email"]
            }
        }
    ]

    system_prompt = """You are a friendly AI course sales assistant. Help customers:
1. Browse available courses
2. Make purchases via VietQR payment
3. Receive course access once payment settles

Always ask for email before creating a payment request.
Be clear about pricing in VND and delivery timeline (usually instant)."""

    messages = []

    while True:
        user_input = input("\nCustomer: ")
        messages.append({"role": "user", "content": user_input})

        response = client_anthropic.messages.create(
            model="claude-3-5-sonnet-20241022",
            max_tokens=1024,
            system=system_prompt,
            tools=tools,
            messages=messages
        )

        # Handle tool calls
        if response.stop_reason == "tool_use":
            for block in response.content:
                if block.type == "tool_use":
                    tool_name = block.name
                    tool_input = block.input

                    if tool_name == "list_courses":
                        result = json.dumps(COURSES, indent=2, ensure_ascii=False)
                    elif tool_name == "initiate_purchase":
                        result = json.dumps(
                            create_course_purchase(
                                tool_input["course_id"],
                                tool_input["customer_email"]
                            ),
                            ensure_ascii=False
                        )
                    elif tool_name == "check_payment_status":
                        result = json.dumps(
                            await deliver_course(
                                tool_input["payment_id"],
                                tool_input["customer_email"]
                            ),
                            ensure_ascii=False
                        )

                    # Send tool result back to Claude
                    messages.append({"role": "assistant", "content": response.content})
                    messages.append({
                        "role": "user",
                        "content": [{"type": "tool_result", "tool_use_id": block.id, "content": result}]
                    })
        else:
            # Regular text response
            for block in response.content:
                if hasattr(block, "text"):
                    print(f"\nAgent: {block.text}")
            messages.append({"role": "assistant", "content": response.content})

# Run it
run_course_sales_agent()

Using AgentPay VN with Claude via MCP

If you prefer to let Claude manage tool calls natively, use the MCP server:

MCP Configuration (for Claude Desktop)

Add this to your Claude configuration file (~/.claude_desktop_config.json):

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "env": {
        "AGENTPAY_MERCHANT_ID": "YOUR_MERCHANT_ID",
        "AGENTPAY_API_KEY": "YOUR_API_KEY"
      }
    }
  }
}

Now Claude can call AgentPay tools directly. Tell Claude:

"I'm selling three courses. When a customer picks one, create a payment request, send them the VietQR code, and wait for settlement. Once confirmed, give them the download link."

Claude will orchestrate the entire flow.

Real-World Walkthrough: Café Menu Ordering

Let's walk through a different scenario: a small café uses an AI chatbot to take orders and collect payment.

The flow:

  1. Customer chats with bot: "I want a cappuccino and a croissant."
  2. Bot calculates total: 65,000 VND.
  3. Bot creates payment request using create_payment_request() with order details.
  4. Bot sends VietQR code to customer's phone (via chat or QR display).
  5. Customer scans and pays from their bank app (e.g., VietcomBank, Agribank, Techcombank).
  6. Bank confirms settlement within seconds.
  7. Bot receives webhook and prints order for kitchen.
  8. Order is prepared and ready before customer leaves.

Why this works: - No card fees: VietQR is domestic and cheaper than Visa/Mastercard. - Instant settlement: Money hits the café's account immediately. - Customer trust: Payment goes to a real bank account (café's own), not a mystery platform. - No PCI compliance burden: The café never handles card data.

Do's and Don'ts

Do Don't
Store payment_id and order details in your database Try to hold or redirect customer money—AgentPay won't let you
Listen for settlement webhooks before delivering products Deliver digital goods before bank confirms settlement
Use a unique order_id for each request (prevents duplicates) Hardcode merchant credentials in source code—use environment variables
Set a reasonable timeout for await_settlement() (10–15 min) Assume a payment is settled without checking the webhook
Log all payment requests and responses for auditing Ignore failed payment requests—always inform the customer

Troubleshooting & Advanced Tips

Webhook Verification

Always verify that webhooks come from AgentPay, not a spoofed request:

from agentpay_vn import verify_webhook_signature

def webhook_endpoint(request):
    payload = request.json()
    signature = request.headers.get("X-AgentPay-Signature")

    if not verify_webhook_signature(payload, signature, "YOUR_API_KEY"):
        return {"error": "Invalid signature"}, 401

    # Safe to process payment
    payment_id = payload["payment_id"]
    status = payload["status"]  # "settled" or "failed"
    return {"status": "received"}, 200

Retrying Failed Payments

If a payment times out, don't just give up. Offer the customer a retry:

def retry_payment(original_payment_id, customer_email):
    """
    Create a fresh payment request if the first one timed out.
    """
    # Get the original payment details from your database
    original = db.get_payment(original_payment_id)

    # Create a new request with same amount
    retry = create_payment_request(
        client=client,
        amount_vnd=original["amount_vnd"],
        description=f"Retry: {original['description']}",
        order_id=f"{original['order_id']}_retry_1",  # Add suffix
        customer_email=customer_email
    )

    return retry

Handling Multiple Orders from One Customer

Use the customer_email field to link all their transactions:

def get_customer_order_history(email: str):
    """
    Fetch all orders by a customer (useful for refunds, upsells).
    """
    orders = db.query("SELECT * FROM payments WHERE customer_email = ?", email)
    return [{
        "order_id": o["order_id"],
        "amount": o["amount_vnd"],
        "status": o["status"],
        "created_at": o["created_at"]
    } for o in orders]

FAQ

Q: What happens if a customer scans the QR but doesn't complete payment? A: Nothing. The payment request stays pending. After 15 minutes, it expires. Your await_settlement() call times out gracefully, and you can offer a retry.

Q: Do I need a business license to use AgentPay VN? A: AgentPay VN handles the technical integration only. You're responsible for business compliance in your country. Check your local regulations for digital commerce and cross-border payments.

Q: Can I use AgentPay VN for physical goods or subscriptions? A: Yes. The SDK is payment-agnostic. You can sell anything—digital, physical, recurring. Just handle delivery logistics in your code after settlement.

Q: Is there a transaction fee? A: AgentPay VN itself is free (MIT open source). You only pay your bank's standard VietQR interchange fee (typically 0–0.5%), which is much lower than card processing.

Key Takeaways

Next Steps

Ready to build? Here's your starting point:

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

  2. Get your merchant credentials from AgentPay's dashboard (or reach out if you're in Vietnam).

  3. Read the full documentation: https://agentpay.servicesai.vn/v1/docs

  4. Explore the source code: https://github.com/phuocdu/agentpay-vn

  5. Start with a simple example: Build a single-course seller, a tip jar, or a small shop. Ship it. Iterate.

Your AI agent is now ready to collect payments—without the regulatory headache, the fraud risk, or the fee burden. Good luck, and happy selling.

Get started →

← All posts