VietQR Payment Automation for AI Agents

2026-10-02 · AgentPay VN

vietqrai-agentspayment-automationpython-sdkvietnam

The Problem: Why AI Agents Need Native Vietnamese Payment Rails

Imagine you're running an online course platform in Ho Chi Minh City. A customer completes checkout in your AI chatbot, but now what? Stripe and PayPal either don't support direct VND settlement, charge 3–4% fees, or require days of account verification for Vietnamese merchants. Your agent sits stuck—it can't confirm payment, can't trigger course access, can't close the sale. Meanwhile, your customer bounces.

This is the reality for thousands of Vietnamese SaaS founders, e-commerce operators, and AI-first businesses. They need real-time, cheap, agent-native payment infrastructure. Enter AgentPay VN—an open-source Python SDK + MCP server that lets your AI agents collect VietQR payments in three lines of code, with settlement hitting your merchant account within hours, not days.

What Is AgentPay VN?

AgentPay VN is a lightweight, MIT-licensed toolkit designed specifically for AI agents. Here's what makes it different:

Core components: 1. Python SDK (agentpay-vn package) 2. MCP server (agentpay-mcp) for Claude/agent integration 3. A simple three-step flow: create request → send link → await settlement

The Three-Line Payment Flow Explained

AgentPay VN's payment loop is deliberately minimal. Here's the mental model:

Agent creates payment request
    ↓
Agent sends checkout URL to customer
    ↓
Agent polls/listens for bank settlement
    ↓
Agent unlocks/ships/starts service

No webhooks to manage. No iframe complexity. No PCI compliance burden. Just the agent, the bank feed, and the merchant account.

Installation and Setup

Step 1: Install the SDK

pip install agentpay-vn

Verify installation:

python -c "import agentpay_vn; print(agentpay_vn.__version__)"

Step 2: Configure Your Bank Details

You'll need: - Merchant bank account (any Vietnamese bank; ideally one supporting API/feeds) - Bank account number - Account holder name (matches bank records) - Merchant ID (from AgentPay VN dashboard or self-issued)

Create a .env file:

AGENTPAY_MERCHANT_ACCOUNT=1234567890
AGENTPAY_MERCHANT_NAME="Your Business Name"
AGENTPAY_MERCHANT_ID=merchant_abc123
AGENTPAY_BANK_CODE=970016  # Vietcombank example

AgentPay VN can auto-confirm settlements via bank feeds from services like: - Your bank's API (if available) - Third-party aggregators (e.g., Plaid-equivalent for Vietnam) - Manual bank statement parsing (fallback)

This lets your agent know payment arrived without polling.

Core SDK Tutorial: Building a Payment-Aware Agent

Code Example 1: Create and Track a Payment Request

Here's a complete, runnable example of an agent collecting payment:

import asyncio
from agentpay_vn import (
    PaymentRequest,
    PaymentClient,
    SettlementListener
)
import os

# Initialize client with env vars
client = PaymentClient(
    merchant_account=os.getenv("AGENTPAY_MERCHANT_ACCOUNT"),
    merchant_name=os.getenv("AGENTPAY_MERCHANT_NAME"),
    merchant_id=os.getenv("AGENTPAY_MERCHANT_ID"),
    bank_code=os.getenv("AGENTPAY_BANK_CODE")
)

async def sell_course():
    """
    Agent function: A customer wants to buy a Python course.
    Create payment request, send link, and wait for settlement.
    """

    # Step 1: Create a payment request
    payment_req = PaymentRequest(
        amount_vnd=499_000,  # 499k VND for the course
        description="Python Masterclass - Full Access",
        customer_id="cust_john_doe_123",
        reference_id="course_purchase_001",
        expires_in_minutes=15  # Link valid for 15 min
    )

    # Create the request in AgentPay
    result = await client.create_payment_request(payment_req)

    # Step 2: Extract the VietQR checkout URL
    checkout_url = result.checkout_url
    qr_code_data = result.qr_code_raw  # Raw QR string if needed

    print(f"📱 Send this link to customer: {checkout_url}")
    print(f"⏱️  Payment expires at: {result.expires_at}")

    # Step 3: Wait for settlement confirmation
    # Option A: Poll the status (simple, no infra)
    payment_settled = False
    while not payment_settled:
        status = await client.get_payment_status(result.request_id)

        if status.state == "SETTLED":
            print(f"✅ Payment confirmed! Amount: {status.amount_received_vnd}")
            print(f"🏦 Transaction ID: {status.bank_tx_id}")

            # Unlock the course
            await unlock_course_access("john_doe_123")
            payment_settled = True

        elif status.state == "EXPIRED":
            print("❌ Payment link expired. Customer must retry.")
            break

        else:
            # Still pending
            print(f"⏳ Still waiting... (state: {status.state})")
            await asyncio.sleep(5)  # Check every 5 seconds

async def unlock_course_access(customer_id: str):
    """Agent helper: Grant course access after payment."""
    # In real scenario: update DB, send email with access link, etc.
    print(f"🎓 Activated course for {customer_id}")

# Run the agent
asyncio.run(sell_course())

Line-by-line explanation:

Code Example 2: Using MCP Server with Claude

If you want Claude or another agent to automatically handle payments without custom code, use the MCP server:

agentpay-mcp --merchant-account 1234567890 \
             --merchant-name "Your Business" \
             --merchant-id merchant_abc123 \
             --bank-code 970016

Then configure Claude via MCP in your claude_desktop_config.json:

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "args": [
        "--merchant-account", "1234567890",
        "--merchant-name", "Your Business",
        "--merchant-id", "merchant_abc123",
        "--bank-code", "970016"
      ],
      "env": {
        "AGENTPAY_BANK_FEED_URL": "https://your-bank-api.example.com"
      }
    }
  }
}

Now, when you chat with Claude:

User: "I want to buy the advanced Python course for 499k VND."

Claude (with MCP access): "I'll create a payment link for you. [calls create_payment_request] Here's your link: https://checkout.agentpay.vn/pay/req_xyz123. Once you pay, I'll send you access immediately."

Claude can now handle the entire transaction flow without custom integrations.

Real-World Walkthrough: Online Cafe Shop Bot

Let's build a concrete example: Café Automation Bot that takes orders and collects payment via VietQR.

Scenario: A café owner wants an AI chatbot on their website. Customers order coffee, pay via VietQR, and get a receipt with pickup instructions.

Flow:

import asyncio
from agentpay_vn import PaymentClient, PaymentRequest
from datetime import datetime

client = PaymentClient(
    merchant_account="9999888877776666",
    merchant_name="Cafe Hanoi",
    merchant_id="cafe_hanoi_001",
    bank_code="970016"
)

async def cafe_checkout(order_items: list, customer_phone: str):
    """
    Customer orders: ["Cà phê đen", "Bánh mì"] for 85k VND.
    """
    total_vnd = sum(item["price"] for item in order_items)
    order_id = f"order_{datetime.now().timestamp()}"

    # Create VietQR payment
    payment = PaymentRequest(
        amount_vnd=int(total_vnd),
        description=f"Order: {', '.join(o['name'] for o in order_items)}",
        customer_id=customer_phone,
        reference_id=order_id,
        expires_in_minutes=10  # Short window for walk-in café
    )

    result = await client.create_payment_request(payment)

    # Send checkout link
    print(f"☕ Your order total: {total_vnd:,} VND")
    print(f"📱 Pay here: {result.checkout_url}")
    print(f"⏰ Link expires in 10 minutes")

    # Poll for payment
    start = datetime.now()
    while (datetime.now() - start).seconds < 600:  # 10 min timeout
        status = await client.get_payment_status(result.request_id)

        if status.state == "SETTLED":
            print(f"✅ Payment successful!")
            print(f"🎟️ Your receipt number: {order_id}")
            print(f"⏱️ Pickup in 15 minutes at counter 2")
            return True

        await asyncio.sleep(3)

    print("❌ Payment timed out. Please try again.")
    return False

# Example usage
orders = [
    {"name": "Cà phê đen", "price": 25_000},
    {"name": "Bánh mì", "price": 60_000}
]

asyncio.run(cafe_checkout(orders, "0987654321"))

Advantages for the café: - Zero setup complexity (one env config) - Money lands in bank account instantly - Customer confirmation is immediate - No card fraud, no chargebacks, no fees

Comparison: AgentPay VN vs. Alternatives

Aspect AgentPay VN Stripe PayPal Direct Bank Transfer
Vietnam VND native ✅ ❌ (requires workarounds) ⚠️ (slow) ✅
Agent-ready (MCP) ✅ ❌ ❌ ❌
Money held by platform ❌ ✅ ✅ ❌
Settlement time Minutes 1–2 days 2–3 days 1–2 hours
Fees 0% (no money touch) 2.9% + 30¢ 2.2% + 0.3$ Bank dependent
QR checkout ✅ ⚠️ (complex) ❌ Manual
Open-source ✅ ❌ ❌ N/A
No PCI burden ✅ ⚠️ ⚠️ ✅

Advanced Tips and Patterns

Webhook Alternative: Bank Feed Polling

Instead of polling get_payment_status every 5 seconds, connect a bank feed:

from agentpay_vn import BankFeedListener

listener = BankFeedListener(
    bank_api_url="https://api.vietcombank.com/feeds",
    api_key=os.getenv("BANK_API_KEY"),
    merchant_account=os.getenv("AGENTPAY_MERCHANT_ACCOUNT")
)

# Register callback for incoming transfers
listener.on_transfer(callback=handle_settlement)
await listener.start()  # Runs indefinitely, triggers callback on new transaction

Handling Partial/Overpayments

status = await client.get_payment_status(request_id)

if status.amount_received_vnd > status.amount_requested_vnd:
    # Customer overpaid (common mistake)
    refund_amount = status.amount_received_vnd - status.amount_requested_vnd
    print(f"Overpayment detected: {refund_amount} VND. Refunding...")
    # Manual refund via your bank (AgentPay VN doesn't refund)
elif status.amount_received_vnd < status.amount_requested_vnd:
    # Underpayment
    print("Underpayment detected. Asking customer to pay remainder...")
else:
    # Exact match
    print("✅ Exact payment received.")

Multi-Agent Coordination: Department Store Bot

# Agent 1: Salesman (takes order)
order = {"items": [...], "total": 2_500_000}
payment_request_id = await agent_salesman.create_payment(order)

# Agent 2: Accountant (monitors settlement, async)
async def monitor_payment():
    while True:
        status = await client.get_payment_status(payment_request_id)
        if status.state == "SETTLED":
            await agent_warehouse.ship_order(order)  # Trigger Agent 3
            break
        await asyncio.sleep(10)

# Agent 3: Warehouse (ships after payment confirmed)
async def agent_warehouse.ship_order(order):
    print(f"🚚 Shipping {order} to customer...")

Troubleshooting & Do's/Don'ts

✅ Do's

❌ Don'ts

FAQ

Q: Does AgentPay VN store my money? No. AgentPay VN never touches funds. The VietQR points directly to your merchant bank account. Settlement is between the customer's bank and yours.

Q: Can I use AgentPay VN with Claude, GPT-4, or other LLMs? Yes. Use the MCP server (agentpay-mcp) for Claude integration. For OpenAI/GPT-4, call the Python SDK directly in your backend, or wrap it in a function the agent can invoke.

Q: How long does settlement take? Typically 5–30 minutes for VietQR transfers. Your bank's feed API will confirm the exact moment. Some banks (e.g., Vietcombank) settle even faster with API access.

Q: Do I need a special merchant account? No. Any Vietnamese bank account works. No special "PayTech" license required.

Q: What if the customer's bank doesn't support VietQR? Most Vietnamese banks support VietQR (it's the national standard since 2021). Very old accounts might not; in that case, fall back to a direct bank transfer link or NAPAS card payment as a fallback.

Key Takeaways

Getting Started Now

  1. Install: pip install agentpay-vn
  2. Configure: Set your merchant account in environment variables.
  3. Code: Copy one of the examples above and adapt to your use case.
  4. Docs & Support: Visit https://agentpay.servicesai.vn/v1/docs or check the GitHub repo.

Your AI agent is now ready to collect VietQR payments. Ship fast. 🚀

Get started →

← All posts