AI Agents Accepting VietQR Payments in Python

2026-07-30 · AgentPay VN

pythonai-agentsvietqrpaymentsmcp

The Problem: Your AI Agent Needs to Accept Money

Imagine you've built an intelligent chatbot that sells online courses. A customer converses with your agent, decides to buy a course, and then... what? You ask for their bank account details? You hand them a PayPal link that breaks the conversation flow? You lose the sale because friction kills momentum.

Or picture this: you've deployed an AI assistant for a Vietnamese café network. It takes orders, confirms timing, but has no way to actually collect payment. Now your café staff must manually text customers bank details—defeating the entire purpose of automation.

This is the gap AgentPay VN solves. Your AI agent can now natively generate a VietQR payment request, send a checkout link, confirm settlement, and continue the conversation—all without ever touching the customer's money or your merchant funds. The QR code points directly to your bank account.

Why VietQR + AI Agents Is a Game-Changer

VietQR is Vietnam's nationwide interbank QR standard. Every Vietnamese bank account holder recognizes it. But until now, integrating it into AI workflows required:

AgentPay VN (MIT-licensed Python SDK + MCP server) strips this down to 3 lines of code. Your agent creates a payment request, sends the checkout URL to the customer, and waits for settlement confirmation. That's it. The agent never holds funds—only your bank account does.

Core Architecture: How AgentPay VN Works

The Three-Step Flow

Step 1: Create a Payment Request Your agent calls create_payment_request() with amount, description, and optional metadata. AgentPay generates a unique transaction ID and a VietQR payload.

Step 2: Send Checkout URL AgentPay returns a checkout URL. Your agent sends this to the customer (SMS, chat, email—your choice). The customer scans the QR or clicks the link, and their banking app opens to complete the transfer.

Step 3: Await Settlement Your agent polls or subscribes to settlement confirmation. When the customer's bank confirms the transfer, your agent knows the transaction is complete and continues processing (ship product, unlock content, etc.).

Why This Architecture is Safe

Installation & Setup

Install the SDK

In your Python project directory:

pip install agentpay-vn

Verify the installation:

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

Configure Your Bank Details

Create a .env file in your project root:

AGENTPAY_MERCHANT_NAME="Your Shop Name"
AGENTPAY_MERCHANT_ACCOUNT="1234567890"
AGENTPAY_BANK_CODE="970418"  # Example: Techcombank
AGENTPAY_BANK_NAME="Techcombank"
AGENTPAY_WEBHOOK_SECRET="your-secure-webhook-key"

Replace values with your actual merchant account details. Your bank code is provided by your bank; common ones include 970418 (Techcombank), 970407 (VietcomBank), 970405 (VietinBank).

Initialize in Your Agent Code

from agentpay_vn import AgentPayClient
import os
from dotenv import load_dotenv

load_dotenv()

client = AgentPayClient(
    merchant_name=os.getenv("AGENTPAY_MERCHANT_NAME"),
    merchant_account=os.getenv("AGENTPAY_MERCHANT_ACCOUNT"),
    bank_code=os.getenv("AGENTPAY_BANK_CODE"),
    bank_name=os.getenv("AGENTPAY_BANK_NAME"),
    webhook_secret=os.getenv("AGENTPAY_WEBHOOK_SECRET"),
)

print("✓ AgentPay client initialized")

Building Your First Payment-Enabled Agent

Code Example: A Course-Selling Chatbot

Let's build an AI agent that sells Python courses:

from agentpay_vn import AgentPayClient, PaymentRequest
import time
import json

# Initialize client (from setup above)
client = AgentPayClient(
    merchant_name="Python Academy",
    merchant_account="1234567890",
    bank_code="970418",
    bank_name="Techcombank",
    webhook_secret="secret-key-here",
)

def sell_course(customer_name, customer_phone, course_price_vnd):
    """
    Process a course sale from start to payment confirmation.
    """

    # STEP 1: Create Payment Request
    # This generates a VietQR payload and returns a checkout URL
    payment_request = client.create_payment_request(
        amount=course_price_vnd,  # Amount in VND (e.g., 299000)
        description=f"Python Mastery Course - {customer_name}",
        customer_phone=customer_phone,
        customer_name=customer_name,
        metadata={
            "course_id": "python-mastery-101",
            "course_name": "Python Mastery",
            "enrollment_type": "lifetime"
        }
    )

    print(f"\n📋 Payment Request Created")
    print(f"   Transaction ID: {payment_request.transaction_id}")
    print(f"   Amount: {payment_request.amount:,} VND")
    print(f"   Status: {payment_request.status}")

    # STEP 2: Send Checkout URL to Customer
    # In a real chatbot, this would be sent via Telegram, Facebook Messenger, etc.
    checkout_url = payment_request.checkout_url
    print(f"\n💳 Send this to the customer:")
    print(f"   {checkout_url}")
    print(f"\n   Or they can scan the QR code to pay {payment_request.amount:,} VND")

    # STEP 3: Await Settlement
    # Poll for settlement confirmation (in production, use webhooks)
    print(f"\n⏳ Waiting for payment confirmation...")
    max_wait_seconds = 300  # Wait up to 5 minutes
    poll_interval = 5  # Check every 5 seconds
    elapsed = 0

    while elapsed < max_wait_seconds:
        # Check payment status
        status = client.get_payment_status(payment_request.transaction_id)

        if status.is_settled:
            print(f"\n✅ Payment Confirmed!")
            print(f"   Settlement Amount: {status.settled_amount:,} VND")
            print(f"   Timestamp: {status.settled_at}")

            # Payment succeeded—now unlock the course
            print(f"\n🎓 Enrolling {customer_name} in Python Mastery course...")
            # Here you would:
            # - Add enrollment to database
            # - Send course access link
            # - Send welcome email
            # - Log transaction

            return {
                "status": "success",
                "transaction_id": payment_request.transaction_id,
                "customer": customer_name,
                "amount": status.settled_amount,
                "message": f"Welcome to Python Mastery! Check your email for course access."
            }

        elif status.is_failed:
            print(f"\n❌ Payment Failed")
            print(f"   Reason: {status.failure_reason}")
            return {
                "status": "failed",
                "transaction_id": payment_request.transaction_id,
                "message": "Payment was declined. Please try again."
            }

        # Still pending—wait and retry
        time.sleep(poll_interval)
        elapsed += poll_interval
        print(f"   [{elapsed}s] Waiting...")

    print(f"\n⏱️ Payment confirmation timed out")
    return {
        "status": "timeout",
        "transaction_id": payment_request.transaction_id,
        "message": "Payment confirmation took too long. Please contact support."
    }

# Example: Sell a course
if __name__ == "__main__":
    result = sell_course(
        customer_name="Nguyen Van A",
        customer_phone="0901234567",
        course_price_vnd=299000
    )
    print(f"\nFinal Result: {json.dumps(result, indent=2, ensure_ascii=False)}")

Line-by-line breakdown:

  1. Lines 13–15: Initialize the client with your merchant details.
  2. Lines 18–40: create_payment_request() generates a unique VietQR payment request. The returned object contains transaction_id (unique), checkout_url (send to customer), and amount (confirmed).
  3. Lines 42–48: Display the checkout URL. In a real chatbot, send this via your messaging platform.
  4. Lines 50–80: Poll get_payment_status() every 5 seconds. When is_settled becomes True, the customer's bank has confirmed the transfer to your account.
  5. Lines 82–90: Once settled, unlock the course and return success.

MCP Server: Integrate with Claude & Other AI Platforms

If you're using Claude (or another AI platform that supports Model Context Protocol), you can expose AgentPay as a tool directly.

Install MCP Server

pip install agentpay-mcp

Configure Claude

Add to your Claude configuration file (e.g., claude_desktop_config.json):

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "env": {
        "AGENTPAY_MERCHANT_NAME": "Your Shop Name",
        "AGENTPAY_MERCHANT_ACCOUNT": "1234567890",
        "AGENTPAY_BANK_CODE": "970418",
        "AGENTPAY_BANK_NAME": "Techcombank",
        "AGENTPAY_WEBHOOK_SECRET": "your-secure-webhook-key"
      }
    }
  }
}

Now Claude can call AgentPay functions directly:

User: "I want to buy the Advanced Python course for 499,000 VND."

Claude: I'll create a payment request for you.
[Calls agentpay.create_payment_request with amount=499000]

Claude: Here's your checkout link: https://agentpay.vn/pay/...
Please scan the QR code or click the link to complete payment.

Real-World Walkthrough: Vietnamese Café Order Bot

Let's say you operate a café chain in Ho Chi Minh City with 5 locations. You deploy an AI agent that:

  1. Converses with customers in Vietnamese
  2. Takes coffee orders ("Một cà phê đen đá, không đường")
  3. Collects payment via VietQR
  4. Sends order to the nearest café
  5. Notifies customer when the order is ready

Flow:

Customer: "Tôi muốn một cà phê đen đá."
Bot: "Tuyệt vời! Một cà phê đen đá. Bạn chọn quán ở đâu?"
Customer: "Quán Nguyễn Huệ."
Bot: "Đẹp lắm. Cà phê đen đá - 25,000 VND. Bạn quét mã QR này để thanh toán."
[Displays VietQR checkout URL]
Customer: [Scans QR in banking app, pays 25,000 VND]
Bot: "✓ Thanh toán thành công! Đơn hàng của bạn sẽ sẵn sàng trong 5 phút."
[Sends order to Nguyễn Huệ café POS system]
Bot: "Nhân viên sẽ gọi bạn để nhận."

Why this works:

Best Practices & Do/Don't Table

Do Don't
Store only the transaction ID in your database Store customer bank account details
Use webhooks for settlement confirmation in production Poll continuously in a tight loop (wastes CPU)
Include metadata (order ID, customer ID) for reconciliation Leave metadata empty—you'll regret it during audits
Test with small amounts first (5,000 VND) Go live with 1,000,000 VND transactions immediately
Handle timeout gracefully (tell customer to try again) Assume the payment succeeded just because the request was sent
Use environment variables for bank details Hardcode credentials in your source code
Implement idempotency (check if transaction exists before creating) Create duplicate payment requests for the same order

Advanced Tips

Implement Idempotent Payments

If your agent retries due to network issues, ensure you don't create duplicate payment requests:

def safe_create_payment(order_id, amount):
    # Check if payment request already exists
    existing = client.get_payment_by_order_id(order_id)
    if existing and existing.status in ["pending", "settled"]:
        return existing

    # Create new payment request
    return client.create_payment_request(
        amount=amount,
        description=f"Order {order_id}",
        metadata={"order_id": order_id}
    )

Use Webhooks Instead of Polling

For production, replace polling with webhook callbacks:

from flask import Flask, request

app = Flask(__name__)

@app.route("/agentpay-webhook", methods=["POST"])
def handle_payment_webhook():
    # AgentPay sends settlement notification here
    payload = request.json

    # Verify webhook signature
    if not client.verify_webhook(payload):
        return {"error": "Invalid signature"}, 403

    transaction_id = payload["transaction_id"]
    status = payload["status"]

    if status == "settled":
        # Process the successful payment
        print(f"Payment {transaction_id} settled!")
        # Unlock course, ship product, etc.

    return {"ok": True}, 200

Configure your webhook URL in AgentPay dashboard or .env:

AGENTPAY_WEBHOOK_URL=https://yourdomain.com/agentpay-webhook

Add Retry Logic

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=2, max=10)
)
def create_payment_with_retry(amount, description):
    return client.create_payment_request(
        amount=amount,
        description=description
    )

FAQ

Q: Does AgentPay take a cut of the payment?

A: No. AgentPay is open-source and doesn't touch the money. The payment goes directly from the customer's bank to your bank account. You pay only what your bank charges for interbank transfers (usually 0–1,000 VND per transaction).

Q: What if the customer's payment fails?

A: The get_payment_status() call will return is_failed=True with a failure_reason (insufficient funds, incorrect account, etc.). Your agent can ask the customer to try again or use an alternative payment method.

Q: How long does settlement take?

A: VietQR transfers are typically confirmed within 10–30 seconds. Your agent can consider the payment complete as soon as is_settled=True.

Q: Can I use this with non-Vietnamese banks?

A: VietQR is designed for Vietnamese banks. Non-Vietnamese customers would need a Vietnamese bank account to pay via VietQR. For international payments, consider integrating a separate provider (Stripe, PayPal).

Q: Is AgentPay PCI-compliant?

A: Yes. AgentPay never stores or transmits sensitive banking information—customers interact directly with their banks via the VietQR link. No PCI audit needed for AgentPay itself.

Key Takeaways

Next Steps

  1. Install the SDK: bash pip install agentpay-vn

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

  3. Explore the GitHub repository: https://github.com/phuocdu/agentpay-vn

  4. Build your first agent: Start with the course-selling example above. Test with a 5,000 VND transaction. Scale to production.

  5. Join the community: Open an issue or discussion on GitHub if you hit any roadblocks.

Your AI agent is now ready to accept real Vietnamese payments. Go build something amazing.

Get started →

← All posts