AI Chatbot Digital Product Sales Without Holding Funds

2026-08-30 · AgentPay VN

ai-agentspayment-integrationvietqrpython-sdkdigital-products

The Problem: AI Bots That Sell, But Can't Collect

Imagine you've built an impressive AI chatbot that recommends online courses, sells digital templates, or offers premium access to your SaaS platform. Your bot engages brilliantly, answers questions, and closes the sale. Then reality hits: how does it actually collect payment?

Traditional payment APIs force you into uncomfortable choices:

  1. Hold customer funds in a merchant account (compliance nightmares, regulatory friction, customer distrust).
  2. Redirect users away from the chat experience to a third-party checkout (conversion killer—studies show ~70% don't return).
  3. Store payment credentials yourself (PCI-DSS scope explosion, security liability).
  4. Build complex webhooks and settlement logic (weeks of engineering for something that should take hours).

In Vietnam especially, where VietQR has become the de facto standard for digital payments, this gap is acute. Your bot should work within the conversation, collect payment directly to your bank account, and never touch customer money.

AgentPay VN solves this in 3 lines of Python.

Why AgentPay VN Changes the Game

AgentPay VN is an open-source (MIT license) Python SDK + MCP server that bridges AI agents and instant bank transfers. Here's what makes it different:

Key Architecture: Zero-Custody Design

This is fundamentally different from Stripe, PayPal, or Momo—which require you to trust a third party with customer funds and settlement. AgentPay is a bridge, not a vault.

Understanding the 3-Step Payment Flow

Before we code, let's map the flow your bot will follow:

Step 1: Create Payment Request Your bot generates a unique payment request with an amount, description, and internal order ID.

Step 2: Generate & Send Checkout URL AgentPay produces a VietQR code and checkout URL. Your bot sends this to the customer via chat, email, or webhook.

Step 3: Await Settlement Your bot polls the payment status (or listens to webhooks) until the bank confirms the transfer landed in your account.

Once confirmed, fulfill the digital product: unlock the course, send the download link, activate the license.

Building Your First AI Payment Bot in Python

Installation & Setup

Start with a clean Python 3.8+ environment:

pip install agentpay-vn

Next, configure your bank account details in a .env file or environment variables:

VIETQR_BANK_ACCOUNT_NO="1234567890"  # Your account number
VIETQR_BANK_CODE="970400"             # Your bank code (e.g., Vietcombank)
VIETQR_ACCOUNT_NAME="Your Name"        # Account holder name

Complete Working Example: Course Sales Bot

Let's build a bot that sells a Python course for 99,000 VND:

from agentpay_vn import AgentPayClient, PaymentRequest
import time

# Initialize the AgentPay client
client = AgentPayClient(
    bank_account="1234567890",
    bank_code="970400",
    account_name="Your Business Name"
)

def sell_course_to_customer(customer_id: str, customer_email: str):
    """
    Orchestrates the full payment flow for a digital course sale.
    """

    # Step 1: Create a payment request
    # This generates a unique transaction ID and sets the amount
    payment_request = PaymentRequest(
        order_id=f"course_python_101_{customer_id}_{int(time.time())}",
        amount_vnd=99000,
        description="Python Advanced Course - Full Access",
        customer_email=customer_email,
        product_name="Python 101: Advanced Patterns"
    )

    # Step 2: Generate the checkout URL
    # AgentPay creates a VietQR code and returns a shareable URL
    checkout_response = client.create_payment_request(payment_request)
    checkout_url = checkout_response["checkout_url"]
    qr_code_url = checkout_response["qr_image_url"]

    # Your bot sends this to the customer
    print(f"📱 Please scan this QR code to pay:")
    print(f"QR Image: {qr_code_url}")
    print(f"Or open: {checkout_url}")
    print(f"Amount: 99,000 VND")

    # Step 3: Wait for payment confirmation
    # Poll the settlement status until payment arrives
    payment_confirmed = False
    max_wait_seconds = 300  # 5 minute timeout
    elapsed = 0
    poll_interval = 5  # Check every 5 seconds

    while not payment_confirmed and elapsed < max_wait_seconds:
        settlement_status = client.await_settlement(
            order_id=payment_request.order_id,
            timeout_seconds=poll_interval
        )

        if settlement_status["status"] == "settled":
            payment_confirmed = True
            print(f"✅ Payment received! {settlement_status['amount_vnd']} VND confirmed.")

            # Fulfill the digital product
            send_course_access_link(
                customer_email,
                course_id="python_101_advanced"
            )

            return {"success": True, "order_id": payment_request.order_id}

        elif settlement_status["status"] == "pending":
            print(f"⏳ Waiting for payment... ({elapsed}s elapsed)")

        else:
            print(f"❌ Payment failed: {settlement_status.get('error', 'Unknown error')}")
            return {"success": False, "reason": settlement_status.get('error')}

        time.sleep(poll_interval)
        elapsed += poll_interval

    if not payment_confirmed:
        print("⏱️ Payment timeout. Customer did not complete payment in time.")
        return {"success": False, "reason": "timeout"}

def send_course_access_link(email: str, course_id: str):
    """
    Fulfill the digital product (email, webhook, database update, etc.)
    """
    access_token = generate_course_token(course_id, email)
    send_email(
        to=email,
        subject="🎉 Welcome to Python 101: Advanced Patterns",
        body=f"Access your course here: https://yourplatform.com/course/{course_id}?token={access_token}"
    )

# Usage in your chatbot
if __name__ == "__main__":
    result = sell_course_to_customer(
        customer_id="user_12345",
        customer_email="student@example.com"
    )
    print(result)

Line-by-line breakdown:

Integrating with Claude via MCP

If you're using Claude or another AI agent via the Model Context Protocol (MCP), configure AgentPay as a tool server. This lets Claude call payment functions directly from conversation.

MCP Server Configuration (claude_desktop_config.json):

{
  "mcpServers": {
    "agentpay": {
      "command": "python",
      "args": ["-m", "agentpay_mcp.server"],
      "env": {
        "VIETQR_BANK_ACCOUNT_NO": "1234567890",
        "VIETQR_BANK_CODE": "970400",
        "VIETQR_ACCOUNT_NAME": "Your Business Name"
      }
    }
  }
}

Install the MCP server:

pip install agentpay-vn[mcp]

Now, when you chat with Claude, you can ask: "I want to sell my e-book for 150,000 VND to user@example.com." Claude will:

  1. Call agentpay.create_payment_request() with the amount and customer email.
  2. Receive the checkout URL and QR code.
  3. Return the payment link to you in the chat.
  4. Optionally, poll for settlement and confirm when payment lands.

This makes selling from a chatbot truly hands-off.

Real-World Walkthrough: Digital Café Menu Bot

Let's apply this to a concrete scenario: a café offering digital gift cards via WhatsApp.

Scenario: ThanhCafe wants to sell digital gift cards (50k, 100k, 200k VND) via a WhatsApp bot.

Flow:

  1. Customer texts bot: "I'd like to buy a 100k gift card."
  2. Bot generates payment request with order_id gift_card_100k_{timestamp}.
  3. Bot sends back: "Scan this QR to pay 100,000 VND→" (with QR image).
  4. Customer scans with banking app, payment goes directly to ThanhCafe's bank account.
  5. Bot polls settlement status. When bank confirms the 100k arrived in ThanhCafe's account: - Bot sends a unique gift card code to customer (e.g., GC-THANH-X7K9Q). - Bot sends the code to the café's POS system via webhook. - Customer redeems code at the cafe.

Code snippet for gift card scenario:

GIFT_CARD_DENOMINATIONS = {
    "50k": 50000,
    "100k": 100000,
    "200k": 200000
}

def generate_gift_card_code():
    import uuid
    return f"GC-THANH-{str(uuid.uuid4())[:6].upper()}"

def handle_gift_card_purchase(amount_key: str, customer_phone: str):
    amount_vnd = GIFT_CARD_DENOMINATIONS.get(amount_key, 100000)

    payment_req = PaymentRequest(
        order_id=f"giftcard_{amount_key}_{customer_phone}_{int(time.time())}",
        amount_vnd=amount_vnd,
        description=f"ThanhCafe Digital Gift Card - {amount_key}"
    )

    checkout = client.create_payment_request(payment_req)

    # Send QR to WhatsApp
    whatsapp_send_qr(customer_phone, checkout["qr_image_url"])

    # Wait for settlement
    if client.await_settlement(payment_req.order_id)["status"] == "settled":
        code = generate_gift_card_code()
        whatsapp_send_code(customer_phone, code)
        # Also push to POS
        notify_pos_system(code, amount_vnd)

Do's and Don'ts: Avoiding Common Pitfalls

Do Don't
✅ Use unique order IDs (include timestamp or UUID) ❌ Reuse the same order_id for multiple transactions
✅ Poll settlement every 5–10 seconds ❌ Poll every 1 second (wastes API quota and annoys your bank)
✅ Set a payment timeout (e.g., 5–10 minutes) ❌ Wait indefinitely; let transactions hang
✅ Store order_id and customer email in your database ❌ Lose track of who paid for what
✅ Fulfill after settlement confirmation ❌ Send course/key before payment confirmed
✅ Log all payment events (for debugging & compliance) ❌ Ignore failures; investigate later (chaos)
✅ Use HTTPS for your fulfillment webhook ❌ Send credentials or order IDs over HTTP
✅ Test with small amounts (1,000–10,000 VND) first ❌ Go live with large transactions without testing

Advanced Tips: Scaling Your Payment Bot

1. Batch Processing for High Volume

If you're processing 100+ payments per day, avoid blocking waits:

import asyncio
from collections import deque

pending_orders = deque()  # Track orders being processed

async def poll_all_settlements():
    """Background task that polls all pending orders."""
    while True:
        for order_id in list(pending_orders):
            status = client.await_settlement(order_id, timeout_seconds=1)
            if status["status"] == "settled":
                on_payment_settled(order_id, status)
                pending_orders.remove(order_id)
        await asyncio.sleep(3)  # Check every 3 seconds

2. Webhook Integration Instead of Polling

If AgentPay supports webhooks (check docs), register your fulfillment endpoint:

# In your Flask/FastAPI app
@app.post("/webhook/agentpay-settlement")
def on_settlement_webhook(request):
    order_id = request.json["order_id"]
    status = request.json["status"]

    if status == "settled":
        on_payment_settled(order_id, request.json)

    return {"received": True}

This is faster and more reliable than polling.

3. Idempotency Keys for Duplicate Prevention

Network errors might cause you to create the same order twice. Use idempotency:

def create_payment_safe(customer_id: str, amount: int):
    idempotency_key = f"{customer_id}_{amount}_{date.today()}"
    # If this key was processed before, the server returns the cached result
    return client.create_payment_request(
        payment_request,
        idempotency_key=idempotency_key
    )

4. Retry Logic with Exponential Backoff

import random

def settle_with_retry(order_id: str, max_retries: int = 3):
    for attempt in range(max_retries):
        try:
            status = client.await_settlement(order_id, timeout_seconds=5)
            return status
        except Exception as e:
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt + random.uniform(0, 1)
                print(f"Retry {attempt + 1} after {wait_time:.1f}s")
                time.sleep(wait_time)
            else:
                raise

FAQ

Q: Does AgentPay charge fees?

A: AgentPay itself is free (MIT open-source). Your bank may charge standard transfer fees (typically 0–2,000 VND per transaction in Vietnam). No middleman fees apply.

Q: What if a customer pays but my server crashes before fulfillment?

A: Your bank will confirm the payment landed in your account. Query AgentPay with the order_id later (or check your bank statement) and fulfill retroactively. The payment is not lost; only your fulfillment step failed—which is separate and recoverable.

Q: Can I refund a customer?

A: AgentPay doesn't hold the money, so it can't refund. You would need to initiate a manual bank transfer back to the customer's account. Document the original payment order_id and transaction ID for reconciliation.

Q: Is AgentPay PCI-DSS compliant?

A: AgentPay never processes credit cards or sensitive payment details. It generates a VietQR code that customers pay via their banking app. No card data flows through AgentPay, so PCI scope is minimal. Your bot itself should still follow security best practices (HTTPS, secrets management, etc.).

Q: How fast is settlement confirmation?

A: In Vietnam, VietQR transfers are typically instant (seconds to minutes). AgentPay's bank feed confirms settlement as soon as your bank notifies it—usually within 1–5 minutes. This is much faster than traditional payment gateways (which settle in 1–3 business days).

Key Takeaways

Zero-custody design: Your bank account is your payment processor. AgentPay never holds customer money.
Three-line integration: create_payment_request → get QR URL → await_settlement. Real-world bots need ~50 lines (with error handling and fulfillment).
Direct bank settlement: Customers pay via VietQR; the money lands in your bank immediately. No waiting for third-party processors to clear.
MCP integration ready: Use AgentPay with Claude or any AI agent that supports Model Context Protocol.
Ideal for digital products: Courses, templates, licenses, gift cards, e-books—anything digital that can be fulfilled programmatically.
Open-source and MIT-licensed: Audit the code, modify for your needs, no vendor lock-in.

Ready to Launch?

Your AI agent is ready to collect payments. Here's how to start:

  1. Install AgentPay VN: bash pip install agentpay-vn

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

  3. Explore the open-source code: 👉 https://github.com/phuocdu/agentpay-vn

  4. Run the example above with a 50,000 VND test payment to your own account.

  5. Deploy your bot and start selling digital products—immediately, with money flowing directly to your bank.

Your bot no longer just talks. Now it sells, collects, and settles. And it does it all without holding a single đồng.

Happy shipping! 🚀

Get started →

← All posts