AI Agents Accept VietQR Payments in Python – AgentPay VN

2026-06-17 · AgentPay VN

pythonai-agentsvietqrpaymentsopen-source

The Problem: Your AI Agent Needs Real Money

You've built an impressive AI agent—maybe it's a course-selling chatbot, a ticket reservation system, or a service marketplace. Users love it. But when it comes time to collect payment, you hit a wall.

Your options are grim: - Integrate Stripe or PayPal? Complex, slow, and fees eat 3-5% of every transaction. - Build custom bank integration? That's weeks of compliance work, PCI audits, and infrastructure headaches. - Use a payment processor that holds your money? Now you're managing settlement delays and trust issues.

What if your AI agent could accept payments instantly, directly into your bank account, with zero holding periods? That's what AgentPay VN solves.

AgentPay VN is an open-source Python SDK (MIT license) that turns your AI agent into a payment collector using VietQR—Vietnam's unified QR payment standard. The magic: the QR code points straight at your merchant bank account. No middleman. No held funds. A bank feed confirms settlement in real time.

Why VietQR? Why Now?

VietQR is infrastructure that's already everywhere in Vietnam. Over 60 million users, all major banks participating, and near-instant settlement. If you're building for Vietnam or serving Vietnamese customers, this is the payment highway you didn't know you needed.

But integrating VietQR yourself is painful—EMV QR specs, checksum algorithms, bank API quirks. AgentPay VN handles all that. You focus on your agent logic.

Architecture: How AgentPay VN Works

AgentPay VN sits at the intersection of three things:

  1. Python SDK: A lightweight library (agentpay-vn) that wraps VietQR payment request creation and settlement tracking.
  2. MCP Server: A Claude AI integration layer that lets your Claude instance directly call payment operations.
  3. Bank Settlement: A webhook-based feed that confirms when money arrives—no polling, no guessing.

The three-line flow looks deceptively simple:

create_payment_request() → send_checkout_url_to_user() → await_settlement()

But behind that simplicity is rock-solid cryptography, bank-compliant QR generation, and settlement verification.

Getting Started: Installation & Setup

Step 1: Install the SDK

Open your terminal and run:

pip install agentpay-vn

That's it. No complex dependencies, no C extensions. Pure Python, ready to import.

Step 2: Configure Your Merchant Credentials

You'll need: - Merchant ID: Assigned by your VietQR provider (or test ID if using sandbox). - API Key: Your secret key (keep this in an environment variable, not in Git). - Settlement account details: Your bank account number and bank code.

Store these in a .env file:

AGENTPAY_MERCHANT_ID=your_merchant_id
AGENTPAY_API_KEY=your_api_key
AGENTPAY_BANK_ACCOUNT=your_account_number
AGENTPAY_BANK_CODE=970  # VCB, for example

Step 3: Load Credentials in Python

import os
from dotenv import load_dotenv

load_dotenv()

MERCHANT_ID = os.getenv("AGENTPAY_MERCHANT_ID")
API_KEY = os.getenv("AGENTPAY_API_KEY")

Now you're ready to create payments.

Building Your First Payment Flow

Let's build a concrete example: an online course platform where users pay via VietQR to unlock lessons.

Step 1: Create a Payment Request

from agentpay_vn import AgentPayClient

# Initialize the AgentPay client
client = AgentPayClient(
    merchant_id=MERCHANT_ID,
    api_key=API_KEY,
    bank_account="1234567890",
    bank_code="970"  # Vietcombank
)

# Create a payment request for a course purchase
payment = client.create_payment_request(
    amount=299000,  # 299,000 VND (typical course price)
    description="Advanced Python Course Access",
    order_id="course_py_advanced_001",
    customer_email="student@example.com",
    expiry_minutes=30  # QR code valid for 30 minutes
)

print(f"QR Code URL: {payment.qr_code_url}")
print(f"Checkout Link: {payment.checkout_url}")
print(f"Payment ID: {payment.payment_id}")

What's happening here:

  1. create_payment_request(): Generates a VietQR payment request with your merchant details baked in.
  2. amount: Always in VND (Vietnamese Dong). No currency conversion headaches.
  3. order_id: Your internal identifier. Essential for matching settlements.
  4. expiry_minutes: QR code self-destructs after 30 minutes—security + UX.
  5. qr_code_url & checkout_url: Two flavors. QR for mobile, checkout URL for web.

Step 2: Send the Checkout URL to Your User

def send_checkout_to_user(user_email, payment):
    """Send the payment checkout link via email."""
    email_body = f"""
    Hi there!

    Your course is ready. Click below to pay:
    {payment.checkout_url}

    Or scan this QR code with your banking app:
    {payment.qr_code_url}

    Payment expires in 30 minutes.
    """

    # Your email service (sendgrid, ses, mailgun)
    send_email(to=user_email, body=email_body)

    return payment.payment_id

# Trigger it when a user clicks "Enroll"
payment_id = send_checkout_to_user(
    "student@example.com",
    payment
)

Step 3: Await Settlement Confirmation

Now the tricky part: you need to know when money actually lands in your account.

import asyncio
from agentpay_vn import await_settlement

async def monitor_payment(payment_id, max_wait_seconds=1800):
    """
    Poll for settlement confirmation.
    Returns True if settled, False if timeout/failed.
    """
    try:
        settlement = await await_settlement(
            payment_id=payment_id,
            client=client,
            timeout_seconds=max_wait_seconds
        )

        if settlement.status == "settled":
            print(f"✓ Payment {payment_id} settled!")
            print(f"  Amount: {settlement.amount} VND")
            print(f"  Bank ref: {settlement.bank_reference}")

            # Update your database: mark course as PAID
            grant_course_access(payment.customer_email)
            return True

    except TimeoutError:
        print(f"✗ Payment {payment_id} not settled within timeout.")
        return False

# In your main agent loop
if __name__ == "__main__":
    asyncio.run(monitor_payment(payment_id))

Key insight: This is async and non-blocking. Your agent doesn't freeze waiting for settlement. You can serve other users while monitoring backgrounds.

Integrating with Claude via MCP

If you're using Claude as your AI backbone, AgentPay VN ships with an MCP server. This lets Claude directly invoke payment operations without you writing any glue code.

Install the MCP Server

pip install agentpay-mcp

Configure Claude to Use AgentPay MCP

In your Claude configuration (or Claude Desktop config), add:

{
  "mcpServers": {
    "agentpay": {
      "command": "python",
      "args": ["-m", "agentpay_mcp"],
      "env": {
        "AGENTPAY_MERCHANT_ID": "your_merchant_id",
        "AGENTPAY_API_KEY": "your_api_key",
        "AGENTPAY_BANK_ACCOUNT": "1234567890",
        "AGENTPAY_BANK_CODE": "970"
      }
    }
  }
}

Now Claude can run natural-language payment flows:

User: "I want to enroll in the Python course. How much does it cost?"

Claude (internally): 1. Creates payment request via MCP 2. Generates QR code 3. Sends URL to user 4. Monitors settlement in background 5. Confirms to user: "Payment processed! Lessons unlocked."

Real-World Walkthrough: An Online Café Shop

Let's build a Vietnamese café that takes online orders with VietQR payments.

The Scenario

The Code

from agentpay_vn import AgentPayClient
import asyncio

class CafeOrderBot:
    def __init__(self, merchant_id, api_key):
        self.client = AgentPayClient(
            merchant_id=merchant_id,
            api_key=api_key,
            bank_account="9999999999",
            bank_code="970"
        )
        self.pending_orders = {}

    def create_order(self, customer_id, items, phone):
        """Create an order and generate payment QR."""
        total = sum(item["price"] for item in items)

        payment = self.client.create_payment_request(
            amount=total,
            description=f"Cafe Order: {', '.join(i['name'] for i in items)}",
            order_id=f"cafe_order_{customer_id}_{int(time.time())}",
            customer_email=phone,  # Use phone as identifier
            expiry_minutes=15
        )

        self.pending_orders[payment.payment_id] = {
            "customer_id": customer_id,
            "items": items,
            "total": total,
            "phone": phone
        }

        return payment

    async def wait_for_payment(self, payment_id):
        """Wait for payment, then confirm order."""
        try:
            settlement = await self.client.await_settlement(
                payment_id=payment_id,
                timeout_seconds=900  # 15 minutes
            )

            if settlement.status == "settled":
                order = self.pending_orders[payment_id]
                print(f"✓ Order confirmed for {order['phone']}")
                print(f"  Items: {[i['name'] for i in order['items']]}")
                print(f"  Total: {order['total']} VND")

                # Notify shop: print order ticket
                self.print_order_ticket(order)
                return True

        except Exception as e:
            print(f"Payment failed: {e}")
            return False

    def print_order_ticket(self, order):
        """Send to kitchen printer."""
        ticket = f"""
        ========== CAFE ORDER ==========
        Customer: {order['phone']}
        {chr(10).join(f"- {item['name']}: {item['price']} VND" for item in order['items'])}
        TOTAL: {order['total']} VND
        Status: PAID ✓
        ================================
        """
        print(ticket)
        # In production: send to POS system

# Usage
bot = CafeOrderBot(MERCHANT_ID, API_KEY)

# Customer interaction
payment = bot.create_order(
    customer_id="cust_123",
    items=[
        {"name": "Iced Coffee", "price": 45000},
        {"name": "Milk Tea", "price": 50000}
    ],
    phone="0901234567"
)

print(f"Pay here: {payment.checkout_url}")

# Backend waits for settlement
result = asyncio.run(bot.wait_for_payment(payment.payment_id))

Do's and Don'ts

✅ Do ❌ Don't
Store payment_id in your database for reconciliation Assume payment succeeded without calling await_settlement()
Use environment variables for API keys Hardcode credentials in source code
Set realistic expiry_minutes (15-30) Leave QR codes open indefinitely (security risk)
Log settlement confirmations for audit Forget to update your database on settlement
Test with sandbox credentials first Go live without testing the full flow
Implement exponential backoff on API retries Hammer the API with polling every 100ms

Advanced Tips

Webhook Settlement Confirmation

Instead of polling with await_settlement(), you can register a webhook. AgentPay will POST to your endpoint when money lands:

client.register_webhook(
    url="https://yoursite.com/webhooks/agentpay",
    events=["settlement.confirmed"]
)

# In your Flask/FastAPI app:
@app.post("/webhooks/agentpay")
def handle_settlement(payload):
    payment_id = payload["payment_id"]
    amount = payload["amount"]
    bank_ref = payload["bank_reference"]

    # Update your database immediately
    mark_payment_settled(payment_id, amount, bank_ref)
    return {"status": "ok"}

This is faster and more reliable than polling.

Handling Payment Failures

async def handle_failed_payment(payment_id):
    """Called if payment expires or is rejected."""
    # Option 1: Generate a new QR with same order_id
    payment = client.create_payment_request(
        amount=order_amount,
        order_id=original_order_id,  # Same ID
        expiry_minutes=30
    )

    # Option 2: Refund workflow
    # Email customer: "Payment failed. Refund or retry?"

    # Option 3: Free trial or grace period
    # Grant temporary access, remind user to pay later

Multi-Currency Support (Future)

Currently AgentPay VN uses VND only. If you're selling globally:

# Get current USD->VND rate
usd_amount = 10  # $10 USD
vnd_rate = get_usd_to_vnd_rate()  # e.g., 24500
vnd_amount = int(usd_amount * vnd_rate)  # 245,000 VND

payment = client.create_payment_request(
    amount=vnd_amount,
    description=f"${usd_amount} USD Course Access",
    order_id=order_id
)

FAQ

Q: Does AgentPay hold my money? No. The QR code points directly at your bank account. Settlement happens bank-to-bank. AgentPay only manages the request/confirmation flow.

Q: What if a payment fails midway? You can re-generate a payment request with the same order_id. The system treats it as a retry, not a duplicate.

Q: Is the SDK production-ready? Yes. It's MIT licensed, open-source, and used in production by Vietnamese fintech startups. Check GitHub for latest releases and issues.

Q: Can I use this outside Vietnam? VietQR is Vietnam-only. If your users aren't in Vietnam or your bank isn't Vietnamese-registered, you'll need a different payment method.

Key Takeaways

Get Started Now

Your AI agent is ready to accept payments. Here's your next move:

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

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

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

  4. Try the examples in the repo—they cover course platforms, e-commerce, and marketplace use cases.

Monetize your AI agents. Today.

Get started →

← All posts