VietQR Payment Automation for AI Agents

2026-08-12 · AgentPay VN

ai agentsvietqr paymentspython sdkvietnam fintechpayment automation

The Problem: Why Stripe Doesn't Work for Vietnam

You've built an AI agent that books fitness classes, sells online courses, or manages a café's lunch orders. Revenue's flowing—but only from international customers. The moment a Vietnamese user wants to pay, you hit a wall.

Stripe doesn't support VietQR. PayPal's fees are brutal (3.5% + $0.30). Local payment gateways? They require merchant accounts, weeks of paperwork, and monthly minimums that kill small projects. Meanwhile, your agent sits idle, unable to close transactions with 98+ million Vietnamese smartphone users.

That's the gap AgentPay VN fills—without you ever touching the money.

What AgentPay VN Actually Does

AgentPay VN is an MIT-licensed Python SDK + MCP (Model Context Protocol) server that lets AI agents generate VietQR payment requests. Here's the critical difference from traditional payment gateways:

The QR code points directly to the merchant's bank account. Your agent doesn't hold funds, doesn't process settlements, doesn't require PCI compliance. When a customer scans the code and pays, the bank confirms the deposit instantly. Your agent detects it and delivers the service.

Think of it as a webhook bridge between VietQR and your AI workflow.

The Three-Step Payment Flow

1. Create Payment Request

Your agent generates a request with amount, merchant info, and description:

from agentpay_vn import AgentPayVN

# Initialize with your merchant bank account
payment_handler = AgentPayVN(
    merchant_id="your_merchant_id",
    bank_account="1234567890",
    bank_name="Vietcombank",
    account_holder="Business Name"
)

# Agent creates a payment request
payment_request = payment_handler.create_payment_request(
    amount=150000,  # VND
    description="Online Course: Python Basics",
    order_id="course_2024_001",
    customer_name="Nguyễn Văn A"
)

print(f"QR Code URL: {payment_request['qr_url']}")
print(f"Request ID: {payment_request['request_id']}")
print(f"Checkout URL: {payment_request['checkout_url']}")

Line-by-line breakdown: - merchant_id: Unique identifier for your business. - bank_account: The actual account where payments land. - create_payment_request(): Generates a VietQR request object (no payment yet). - amount: Transaction size in VND (150,000 ≈ $6 USD). - order_id: Your internal reference; critical for matching settlements. - The function returns the QR code URL and a checkout_url you send to users.

2. Send Checkout URL

Your agent sends the checkout URL via SMS, email, or displays it in a chat interface:

# Agent sends link to customer
checkout_url = payment_request['checkout_url']
message = f"Pay here: {checkout_url}"

# In a chat bot context:
print(f"Agent: Hi! Please complete payment at: {checkout_url}")
print(f"This QR code is valid for 24 hours.")

The customer clicks the link, scans the QR, and pays via their banking app (Vietcombank, Agribank, BIDV, etc.)—no new app install required.

3. Await Settlement & Deliver Service

Your agent polls the bank feed and detects payment confirmation:

import asyncio
from datetime import datetime, timedelta

async def wait_for_payment(request_id, timeout_minutes=30):
    """
    Poll for payment confirmation.
    Timeout prevents infinite loops; real-world agents should use webhooks.
    """
    start_time = datetime.now()
    timeout = timedelta(minutes=timeout_minutes)

    while datetime.now() - start_time < timeout:
        # Check if payment settled
        status = payment_handler.check_payment_status(request_id)

        if status['settled']:
            print(f"✓ Payment confirmed: {status['amount']} VND")
            print(f"Transaction reference: {status['transaction_id']}")
            return status

        # Wait 3 seconds before next check
        await asyncio.sleep(3)

    print("✗ Payment timeout. User may have abandoned checkout.")
    return None

# Agent waits for settlement
payment_status = await wait_for_payment(payment_request['request_id'])

if payment_status:
    # Deliver service immediately
    course_link = generate_course_access_link(customer_id)
    print(f"Agent: Thanks! Access your course: {course_link}")
else:
    print("Agent: Payment not received. Try again?")

Key points: - check_payment_status() queries the bank feed (updated every 10–15 seconds). - settled: True means money is in your account; funds are guaranteed. - Delivery happens in the same agent loop—no manual reconciliation. - Set realistic timeouts; customers expect confirmation within 2–5 minutes.

Real-World Example: AI Course Sales Agent

Imagine a Discord bot selling Python courses to Vietnamese learners.

User: !buy python-basics

Agent workflow: 1. Look up course price (150,000 VND). 2. Create payment request with order_id="python-basics-{user_id}". 3. Send message: "Course costs 150k VND. Pay here: [link]. QR valid 24h." 4. Poll check_payment_status() every 3 seconds. 5. When settled, send invite link to course GitHub repo and Notion workspace. 6. Log transaction in SQLite: (user_id, order_id, amount, timestamp, status='completed').

Agent code sketch:

async def sell_course(user_id, course_slug):
    # Look up course
    course = COURSES[course_slug]

    # Create payment
    payment = payment_handler.create_payment_request(
        amount=course['price'],
        description=f"Course: {course['title']}",
        order_id=f"{course_slug}-{user_id}"
    )

    # Send checkout link
    await send_user_message(
        user_id,
        f"🎓 {course['title']}\nPrice: {course['price']:,} VND\nPay: {payment['checkout_url']}"
    )

    # Wait for settlement
    status = await wait_for_payment(payment['request_id'], timeout_minutes=30)

    if status:
        # Grant access
        access = grant_course_access(user_id, course_slug)
        await send_user_message(user_id, f"✓ Access granted! Link: {access['link']}")
        log_transaction(user_id, course_slug, status['amount'], 'success')
    else:
        await send_user_message(user_id, "⏱ Payment not received. Try again?")
        log_transaction(user_id, course_slug, course['price'], 'abandoned')

This agent handles 50+ concurrent users, each with their own payment request, without holding any funds.

Setting Up the MCP Server for Claude

If you're using Claude or another AI model, connect AgentPay VN via MCP:

Install the MCP server:

pip install agentpay-vn agentpay-mcp

Configure Claude (claude_desktop_config.json):

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "args": [
        "--merchant-id", "your_merchant_id",
        "--bank-account", "1234567890",
        "--bank-name", "Vietcombank",
        "--account-holder", "Your Business Name"
      ]
    }
  }
}

Now Claude can: - Call create_payment_request() directly in conversations. - Check payment status mid-conversation. - Chain results: "Create a 500k VND request, send the link to user, then wait for settlement and confirm access."

MCP abstracts the SDK—Claude handles orchestration.

Advanced Tips & Patterns

Webhook Integration (Better Than Polling)

Polling works but wastes API calls. If your merchant bank supports webhooks, register one:

# Instead of polling, listen for settlement events
from fastapi import FastAPI, Request

app = FastAPI()

@app.post("/webhook/settlement")
async def handle_settlement(data: Request):
    payload = await data.json()
    request_id = payload['request_id']
    amount = payload['amount']

    # Lookup pending order
    order = db.query(Payment).filter(Payment.request_id == request_id).first()
    if order:
        order.status = 'settled'
        db.commit()
        # Deliver service immediately
        await deliver_service(order.user_id, order.product_id)

    return {"status": "received"}

Webbhooks reduce latency from 3–5 seconds to <500ms.

Idempotency Keys

Prevents duplicate payments if your agent retries:

payment = payment_handler.create_payment_request(
    amount=150000,
    description="Course",
    order_id="course_001",
    idempotency_key=f"user_123_course_001"  # Always same for same user+product
)

If the network drops and your agent retries, the same request ID is returned—no double-charge.

Partial Refunds (If Needed)

# User regrets purchase within 24 hours
refund = payment_handler.refund_payment(
    request_id=original_request['request_id'],
    amount=150000,  # Full refund
    reason="customer_request"
)

print(f"Refund initiated. ID: {refund['refund_id']}")

Refunds post to the customer's bank account within 1–2 business days.

AgentPay VN vs. Alternatives

Feature AgentPay VN Stripe PayPal Local Gateway
VietQR Support ✓ Native ✓ (varies)
No Money Held ✗ Escrow ✗ Escrow ✗ Escrow
Merchant Bank Direct
Python SDK ✓ MIT ✗ (usually)
Setup Time 10 min 1–2 days 1–2 days 1–2 weeks
Transaction Fee ~0.5% 2.9% + $0.30 3.5% 1–2%
AI Agent Ready ✓ MCP

For Vietnamese payments via AI agents, AgentPay VN is the only option that doesn't require a merchant account, extensive KYC, or monthly minimums.

Do's and Don'ts

Do: - Set order IDs uniquely per transaction (no duplicates). - Implement retry logic with exponential backoff (e.g., 1s, 2s, 4s, 8s). - Use webhooks in production; polling is for prototypes. - Test with small amounts (10,000–50,000 VND) first. - Log all requests and settlements for accounting. - Expire payment links after 24 hours.

Don't: - Poll faster than every 2–3 seconds (wastes API quota). - Trust settled=True before checking transaction_id (ensure bank feed confirms it). - Hard-code merchant ID in client-side code (use environment variables). - Deliver service before settlement confirmation. - Forget to handle timeout scenarios (user closes app mid-payment).

FAQ

Q: Does AgentPay VN hold my money? No. The QR points to your bank account directly. Settlement is peer-to-peer between customer and your bank. AgentPay only confirms the transaction happened.

Q: What if the customer's bank isn't supported? VietQR works with 38+ Vietnamese banks (Vietcombank, Agribank, BIDV, Techcombank, TPBank, etc.). ~95% of Vietnamese adults have access. For unsupported banks, offer a fallback payment method.

Q: How long until money appears in my account? Usually 10–30 seconds after the customer confirms the transfer in their banking app. Weekends may add a few hours. You can check check_payment_status() immediately after the customer pays.

Q: Can I use AgentPay VN with non-AI services? Yes. The SDK is a standard Python library. You can use it in Flask APIs, background jobs, or CLI tools. The MCP server is for AI/Claude specifically, but the SDK works everywhere Python runs.

Key Takeaways

For AI agents in Vietnam, AgentPay VN is the fastest path from "user clicks buy" to "service delivered" while keeping compliance simple.

Getting Started

Install now:

pip install agentpay-vn

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

Browse the source: https://github.com/phuocdu/agentpay-vn

Start with the example above, test with 10,000 VND (~$0.40), and ship your first agent-powered payment in under an hour. Questions? Post an issue on GitHub or check the docs FAQ.

Get started →

← All posts