Sell Digital Products from AI Chatbots Without Holding Funds

2026-06-19 · AgentPay VN

ai-agentsvietqr-paymentspython-sdkdigital-productspayment-integration

The Problem: Why Most AI Commerce Tools Force You to Hold Customer Money

You've built an AI chatbot that sells digital products—online courses, ebooks, design templates, or software licenses. Your customers are ready to pay. But when you look at payment integrations, every solution seems to push you toward the same uncomfortable reality: you become a money custodian.

You hold customer funds in a third-party account. You manage refunds manually. You juggle settlement timelines that span days. You become liable for fraud disputes. Regulators start asking uncomfortable questions about financial compliance. And worst of all, you're sitting on other people's money—legally and ethically risky territory for a solo developer or small team.

This friction exists because traditional payment gateways were built for marketplaces (Shopify, Stripe Connect, etc.), not for autonomous AI agents that need to collect money and immediately fulfill digital goods.

AgentPay VN solves this differently.

What AgentPay VN Does (And Doesn't)

AgentPay VN is a lightweight, open-source Python SDK (MIT licensed) plus an MCP server that lets your AI agents collect VietQR payments without holding a single dong. Here's the critical difference:

The QR code points straight at your actual bank account. No middleman account. No fund holding. A bank feed confirms when the customer's payment lands, and your bot immediately fulfills the order.

This isn't magic—it's pragmatic. Vietnam's VietQR system is designed exactly for this: peer-to-peer QR payments that settle in seconds.

Why This Matters for AI Chatbots

AI agents operate differently from traditional web stores. They need to:

  1. Make real-time decisions based on payment status (did the customer actually pay?)
  2. Execute immediately (generate the download link, unlock the course, send the API key)
  3. Avoid complexity (less code = fewer bugs in production)
  4. Scale without friction (no financial account management overhead)

With AgentPay VN, your agent can create a payment request, send the checkout URL to the customer, wait for settlement confirmation, and unlock the digital product—all in one conversational flow. No webhooks to debug. No reconciliation sheets. No regulatory headaches.

Getting Started: Installation and Setup

Step 1: Install the SDK

pip install agentpay-vn

That's it. The SDK is lightweight (~15 KB) with minimal dependencies.

Step 2: Configure Your Bank Details

You'll need: - Your bank account number - Your bank name (e.g., "VCB", "TCB", "MB") - Your full name (account holder) - Optional: A description for your merchant (e.g., "Alice's AI Course Bot")

Store these in environment variables or a config file. Never hardcode secrets in your agent code.

Step 3: Integrate with Your AI Agent

Here's a minimal example using the SDK directly:

from agentpay_vn import PaymentRequest, SettlementWatcher
import os
import asyncio

# Initialize with your bank details
bank_account = os.getenv("BANK_ACCOUNT_NUMBER")
bank_name = os.getenv("BANK_NAME")  # e.g., "VCB"
account_holder = os.getenv("ACCOUNT_HOLDER")

# Step 1: Create a payment request for a digital product
async def sell_digital_product(customer_id: str, product_id: str, price_vnd: int):
    # Generate a unique order reference
    order_ref = f"order_{customer_id}_{product_id}_{int(time.time())}"

    # Create the payment request
    payment = PaymentRequest(
        amount=price_vnd,
        order_id=order_ref,
        description=f"Digital Product {product_id}",
        customer_id=customer_id,
        bank_account=bank_account,
        bank_name=bank_name,
        account_holder=account_holder
    )

    # Step 2: Get the VietQR checkout URL
    checkout_url = payment.generate_qr_url()

    print(f"Send this to customer: {checkout_url}")
    # In a chatbot, you'd send this via message: "Scan this QR code to pay."

    # Step 3: Wait for settlement confirmation
    watcher = SettlementWatcher(bank_account=bank_account)

    # Poll for settlement (in production, use bank webhooks if available)
    settled = await watcher.await_settlement(
        order_id=order_ref,
        expected_amount=price_vnd,
        timeout_seconds=300  # 5 minutes
    )

    if settled:
        print(f"✓ Payment confirmed! Unlocking {product_id}...")
        # Fulfill the order: generate download link, unlock course access, etc.
        await unlock_digital_product(customer_id, product_id)
        return {"status": "success", "download_link": generate_download_link(product_id)}
    else:
        print("✗ Payment timeout.")
        return {"status": "timeout", "message": "Please try again."}

# Example: Selling a $5 USD (~125,000 VND) course
asyncio.run(sell_digital_product(
    customer_id="user_12345",
    product_id="course_python_101",
    price_vnd=125000
))

Line-by-line breakdown:

Integrating with Claude via MCP

If you're building a Claude-powered agent, use the AgentPay VN MCP server to expose payment functions as tools.

MCP Configuration

Add this to your Claude MCP client config (e.g., claude_desktop_config.json):

{
  "mcpServers": {
    "agentpay-vn": {
      "command": "agentpay-mcp",
      "env": {
        "BANK_ACCOUNT_NUMBER": "0123456789",
        "BANK_NAME": "VCB",
        "ACCOUNT_HOLDER": "Nguyen Van A",
        "BANK_FEED_API_KEY": "your_bank_api_key_here"
      }
    }
  }
}

Now Claude has access to three native tools:

  1. create_payment_request — Generates a payment request object with QR URL.
  2. send_checkout_url — Sends the QR to the customer (via message, email, SMS).
  3. await_settlement — Polls for payment confirmation; returns when funds arrive.

Claude will automatically call these in sequence when a user asks to buy a product.

Real-World Walkthrough: An AI Course Sales Bot

Imagine you're selling a Python programming course for 299,000 VND (~$12 USD). Here's the conversation flow:

Customer: "I want to buy your Python Basics course."

Claude (Agent): 1. Calls create_payment_request(amount=299000, order_id="order_user123_course_python_001", description="Python Basics Course"). 2. Receives a QR URL and calls send_checkout_url(url) to display it in the chat. 3. Tells the customer: "Scan this QR code with your bank app to pay 299,000 VND."

Customer: [Scans QR, transfers 299,000 VND from their bank account directly to yours in ~10 seconds]

Claude (Agent): 4. Calls await_settlement(order_id, expected_amount=299000, timeout=300). 5. Bank feed confirms the transfer landed in your account. 6. Calls an internal function: generate_course_access_token(customer_id) and send_download_link(). 7. Tells the customer: "Payment received! Here's your course link: [link]. Access expires in 30 days."

Total time: ~15 seconds from QR scan to unlocked course. Your liability: Zero. Money went straight to your bank account.

Advanced Tips: Making It Production-Ready

1. Use Bank Webhooks Instead of Polling

Instead of await_settlement() polling every 2 seconds, ask your bank (VCB, TCB, etc.) if they offer webhooks for incoming transfers. AgentPay VN supports webhook handlers:

from agentpay_vn import WebhookHandler

handler = WebhookHandler(secret=os.getenv("BANK_WEBHOOK_SECRET"))

@handler.on_settlement
async def handle_payment_received(order_id: str, amount: int):
    print(f"Payment received for {order_id}!")
    await unlock_digital_product_by_order_id(order_id)
    # No polling needed—this fires the instant money lands.

# In your FastAPI/Flask app:
app.post("/webhooks/agentpay")
async def webhook_endpoint(request):
    await handler.process(request.json)
    return {"status": "ok"}

2. Prevent Double-Spending

Always store the settlement confirmation in your database:

if settled:
    # Check if already fulfilled
    if not db.orders.find_one({"order_id": order_ref, "fulfilled": True}):
        db.orders.insert_one({
            "order_id": order_ref,
            "customer_id": customer_id,
            "amount": price_vnd,
            "fulfilled": True,
            "fulfilled_at": datetime.utcnow()
        })
        await unlock_digital_product(customer_id, product_id)

3. Handle Partial or Incorrect Payments

Sometimes a customer pays slightly less (bank fee) or the wrong amount. Decide your policy:

expected = 100000
actual = await watcher.get_received_amount(order_id)

if actual >= expected * 0.98:  # Allow 2% variance for bank fees
    await unlock_digital_product()
else:
    await request_correction_from_customer()

4. Add Idempotency Keys

If your agent retries a request, use idempotency keys to prevent duplicate payments:

payment = PaymentRequest(
    amount=price_vnd,
    order_id=order_ref,
    idempotency_key=f"{customer_id}_{product_id}"  # Same key = same request
)

Do's and Don'ts

✅ Do ❌ Don't
Store bank details in environment variables (e.g., .env, AWS Secrets Manager) Hardcode bank account numbers in your source code
Use unique order IDs (order_user123_prod456_timestamp) Reuse order IDs across different transactions
Implement idempotency keys for payment requests Assume a payment request succeeded without confirmation
Log settlement events to your database Rely only on in-memory state for payment confirmation
Set a reasonable timeout (5–10 minutes) for settlement waits Wait indefinitely for a payment that may never arrive
Test with small amounts first (10,000 VND) Go live with 10 million VND per transaction immediately

FAQ

Q: Does AgentPay VN hold my money? No. The VietQR points directly at your bank account. Money lands in your account within seconds. AgentPay VN just confirms settlement; it never touches the funds.

Q: What if a customer disputes the charge? Disputes go through your bank, just like any normal transfer. You have transaction records (order ID, timestamp, amount) to defend yourself. This is actually simpler than marketplace payment disputes because it's a straightforward bank transfer.

Q: Can I refund a customer? Yes, but it's a manual process: you initiate a transfer back to their bank account. AgentPay VN doesn't automate refunds (since it's not holding the money). Consider this when setting your refund policy—you might want stricter no-refund terms for digital products.

Q: Which Vietnamese banks are supported? VietQR works with all major Vietnamese banks: VCB, TCB, MB, ACB, Vietinbank, etc. Check the full list at agentpay.servicesai.vn/v1/docs.

Key Takeaways

Getting Started Today

You're three commands away from accepting AI-powered digital product payments:

pip install agentpay-vn
export BANK_ACCOUNT_NUMBER="your_account"
export BANK_NAME="VCB"

Then integrate the payment flow into your chatbot. Full documentation, examples, and the open-source SDK are available at:

📦 Install: pip install agentpay-vn 📚 Docs: https://agentpay.servicesai.vn/v1/docs 🔧 GitHub: https://github.com/phuocdu/agentpay-vn

Start selling today—no fund holding, no middleman, just you and your customers.

Get started →

← All posts