Accept VietQR Payments in Python AI Agents

2026-07-04 · AgentPay VN

pythonvietqrai-agentspaymentsmcp-server

The Problem: Your AI Agent Needs Money, But How?

Imagine you've built an AI chatbot that sells online courses. A customer asks to enroll in your Python masterclass. Your agent confirms the request, generates a nice summary—and then... what? You either ask them to visit a third-party payment page (losing the conversation flow), or you hardcode a static QR code (which can't track individual transactions). Worse, many payment integrations require you to hold customer funds in an escrow account, adding compliance headaches.

The real friction: AI agents can't natively collect payments in Vietnam without breaking the user experience or wrestling with banking complexity.

That's where AgentPay VN changes the game.

AgentPay VN is an open-source Python SDK (MIT license) that lets your AI agents generate VietQR payment requests on the fly. The QR code points directly to your merchant bank account—AgentPay never holds money. A bank feed confirms when payment settles. The flow feels native to conversation, and your bank balance updates automatically.

In this tutorial, you'll learn to: - Generate unique payment QR codes for each transaction - Embed them seamlessly in your agent's responses - Await settlement confirmation before triggering fulfillment - Deploy via MCP for Claude and other AI platforms

Let's build it.

Why VietQR + AI Agents Is a Perfect Match

VietQR (the Vietnamese standard for fast-payment QR codes) is ubiquitous—nearly every Vietnamese bank customer has a smartphone app that reads it in 2 seconds. No wallet signup, no card entry. Compare that to Stripe or PayPal in Vietnam, which require account creation and are less mobile-native.

AI agents benefit because:

  1. Instant QR generation: Your agent creates a unique QR per transaction, so you can track what each user bought.
  2. No escrow risk: Money goes straight to your account; no middleman holding funds or taking 2–3% cuts.
  3. Bank-level settlement confirmation: AgentPay listens to your bank feed, so you know when the user actually paid—your agent can trigger course access, email confirmation, or next steps automatically.
  4. Conversation-native: The user stays in chat. No redirect, no friction.

For merchants in Vietnam, this is transformative. You keep ~100% of revenue (vs. ~97% after platform fees), and your bank handles the compliance—you don't.

Getting Started: Installation & Setup

Step 1: Install the SDK

pip install agentpay-vn

This gives you the core Python library. If you're using Claude or another AI platform via MCP, you'll also want:

pip install agentpay-mcp

Step 2: Authenticate with Your Bank Account

Visit https://agentpay.servicesai.vn/v1/docs to create an account and link your VietQR-enabled merchant bank account. You'll receive:

Store these as environment variables:

export AGENTPAY_MERCHANT_ID="your_merchant_id"
export AGENTPAY_API_KEY="your_api_key"
export AGENTPAY_ACCOUNT_NUMBER="your_account_number"

Core Flow: The 3-Step Payment Dance

Every payment using AgentPay follows this rhythm:

  1. create_payment_request() – Your agent generates a unique request with amount, description, and metadata.
  2. send_checkout_url() – The agent shows the user a VietQR QR code (or payment link).
  3. await_settlement() – Your agent waits for the bank feed to confirm the user paid, then triggers the next action (deliver the product, log the transaction, etc.).

Let's see this in code.

Building Your First Payment: A Practical Example

Scenario: An AI Course-Selling Bot

Your agent teaches Python. A user wants to buy the "Advanced Async" module for 99,000 VND. Here's how your agent handles it:

from agentpay_vn import AgentPay
import os
from datetime import datetime

# Initialize the SDK
agent_pay = AgentPay(
    merchant_id=os.getenv("AGENTPAY_MERCHANT_ID"),
    api_key=os.getenv("AGENTPAY_API_KEY"),
    account_number=os.getenv("AGENTPAY_ACCOUNT_NUMBER")
)

def handle_course_purchase(user_id: str, course_name: str, amount_vnd: int):
    """
    Handle a course purchase request from a user.

    Args:
        user_id: Unique identifier for the user (e.g., "user_12345")
        course_name: Name of the course (e.g., "Advanced Async Patterns")
        amount_vnd: Price in Vietnamese Dong (e.g., 99000)
    """

    # Step 1: Create a payment request
    # This generates a unique VietQR payment QR and returns metadata
    payment_request = agent_pay.create_payment_request(
        amount=amount_vnd,
        description=f"Course: {course_name}",
        reference_id=f"{user_id}_{datetime.now().timestamp()}",  # Unique per transaction
        metadata={
            "user_id": user_id,
            "course_name": course_name,
            "module": "advanced-async"
        }
    )

    print(f"Payment request created: {payment_request.id}")
    print(f"QR Code URL: {payment_request.qr_url}")
    print(f"Amount: {payment_request.amount} VND")

    # Step 2: Send the checkout URL to the user
    # In a real agent, this would be part of the chat response
    checkout_message = f"""
    💳 **Course Purchase**

You're about to buy: *{course_name}*
Price: **{amount_vnd:,} VND**

Scan the QR code below with your banking app:
[QR Code Image: {payment_request.qr_url}]

Or use this link: {payment_request.checkout_url}

Once you pay, I'll grant you access instantly!
    """

    print(checkout_message)

    # Step 3: Wait for settlement
    # This is a blocking call—your agent holds here until payment is confirmed
    # Timeout set to 5 minutes; if user doesn't pay, the request expires
    settlement = agent_pay.await_settlement(
        payment_request_id=payment_request.id,
        timeout_seconds=300
    )

    if settlement.status == "settled":
        print(f"✅ Payment confirmed! User {user_id} has been enrolled.")
        # Now trigger your fulfillment logic
        grant_course_access(user_id, course_name)
        send_welcome_email(user_id, course_name)
        return {"status": "success", "access_granted": True}
    else:
        print(f"❌ Payment failed or timed out.")
        return {"status": "failed", "access_granted": False}


def grant_course_access(user_id: str, course_name: str):
    """Award the user access to the course (e.g., update your DB)."""
    print(f"Granting access to {course_name} for user {user_id}...")
    # UPDATE your_courses SET access=true WHERE user_id=? AND course=?


def send_welcome_email(user_id: str, course_name: str):
    """Send a welcome email with login link and first lesson."""
    print(f"Sending welcome email to {user_id}...")
    # SEND email via your service


# Usage
if __name__ == "__main__":
    handle_course_purchase(
        user_id="user_99001",
        course_name="Advanced Async Patterns in Python",
        amount_vnd=99000
    )

Line-by-line breakdown:

Integrating with Claude via MCP

If you want Claude (or another AI platform) to autonomously collect VietQR payments, use the MCP server. Here's how to configure Claude:

MCP Configuration (Claude Desktop)

Add this to your claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "agentpay": {
      "command": "python",
      "args": ["-m", "agentpay_mcp.server"],
      "env": {
        "AGENTPAY_MERCHANT_ID": "your_merchant_id",
        "AGENTPAY_API_KEY": "your_api_key",
        "AGENTPAY_ACCOUNT_NUMBER": "your_account_number"
      }
    }
  }
}

Restart Claude. You'll now have three tools available:

  1. agentpay_create_payment_request – Create a payment QR for an amount.
  2. agentpay_checkout_url – Generate a shareable link for the user.
  3. agentpay_await_settlement – Block until payment is confirmed.

You can now tell Claude: "You're a course sales agent. When a user expresses interest in a course, create a VietQR payment request and await settlement." Claude will handle the orchestration automatically.

Real-World Walkthrough: An AI Café Ordering Bot

Let's build a concrete example: a café in Saigon uses a Telegram bot (powered by Claude) to take orders.

Scenario: A user types "I want an iced coffee and a croissant."

  1. Agent suggests: "Perfect! That's 65,000 VND. Let me generate a payment QR for you."
  2. Agent calls create_payment_request(65000, "Iced Coffee + Croissant").
  3. Agent sends the QR code via inline image in Telegram.
  4. User scans and pays with their bank app (3 seconds).
  5. Agent calls await_settlement(), which blocks for up to 5 minutes.
  6. Settlement confirmed → Agent sends: "✅ Order confirmed! Your order will be ready in 10 mins. Pick up at counter #3."
  7. Café staff see the order in their POS system (integrated with your settlement webhook).

No payment gateway signup, no fraud risk, no escrow. Money lands in the café's account immediately.

Advanced Tips & Gotchas

Tip 1: Handle Timeout Gracefully

If await_settlement() times out, the user might still be in the process of paying. Instead of failing, you could offer them a retry link:

try:
    settlement = agent_pay.await_settlement(
        payment_request_id=payment_request.id,
        timeout_seconds=300
    )
except TimeoutError:
    print("Payment window closed. User can retry:")
    print(f"Retry link: {payment_request.checkout_url}")

Tip 2: Idempotent Reference IDs

Always use a unique, idempotent reference ID (e.g., user_id + timestamp). If your agent retries due to a network glitch, the same reference ID prevents duplicate charges.

Tip 3: Metadata for Tracking

Use the metadata field to store context you'll need later. For example, if selling multiple courses, store the course ID so you can look it up when settlement is confirmed.

Gotcha: Async vs. Synchronous

await_settlement() is blocking. Your agent will pause until payment is confirmed or timeout occurs. This is fine for short transactions (5–10 minutes), but if you're running 100 agents in parallel, you might hit CPU/thread limits. For high-volume scenarios, consider:

Do's and Don'ts

✅ Do ❌ Don't
Store reference_id in your database to match payments Reuse the same reference_id across transactions
Use unique amounts per transaction (avoids accidental duplicates) Hard-code amounts in your agent's logic
Set reasonable timeouts (300–600 sec) for user experience Set timeouts shorter than 60 sec (users need time to unlock app)
Log settlement confirmations for reconciliation Assume payment succeeded before await_settlement() returns
Monitor your bank feed for anomalies Rely solely on AgentPay's status (cross-check with your bank)

FAQ

Q: Does AgentPay hold my money?

No. AgentPay is a connector only. The VietQR code points directly to your merchant bank account. Your bank processes the payment and deposits funds to you; AgentPay simply confirms the settlement by reading your bank feed. You retain 100% of the funds minus any bank network fees (usually <1%).

Q: What if a user scans the QR but doesn't complete payment?

Your agent's await_settlement() call will timeout after the duration you specified (default 300 seconds). You can then offer the user a retry link or ask if they'd like to proceed with a different payment method.

Q: Can I use AgentPay for subscriptions?

Not yet. AgentPay is currently one-time, per-transaction payments. For subscriptions, you'd need to call create_payment_request() monthly on behalf of your user, or integrate with a subscription billing service (e.g., Stripe Billing) alongside AgentPay.

Q: Is AgentPay available outside Vietnam?

Currently, it's Vietnam-focused (VietQR is a Vietnamese standard). If you need global payments, pair AgentPay with Stripe or PayPal for non-VN users, and AgentPay for VN users.

Key Takeaways

Next Steps

Ready to let your AI agent accept VietQR payments?

  1. Install: pip install agentpay-vn
  2. Link your bank: Visit https://agentpay.servicesai.vn/v1/docs to create an account and authenticate.
  3. Copy the example code above and adapt it to your use case (course, café, coaching, etc.).
  4. Deploy with MCP if using Claude or another AI platform (config example provided above).
  5. Monitor: Watch your bank feed for settlements; log reference_id + metadata for reconciliation.

For detailed API reference, examples, and troubleshooting, see the full documentation.

For bugs, feature requests, or contributions, visit the GitHub repo.

Happy collecting! 🚀

Get started →

← All posts