Add VietQR Checkout to Your LLM Agent in 10 Minutes

2026-06-21 · AgentPay VN

ai agentspayment integrationpython sdkvietqrmcp

The Problem: Your AI Agent Stops at the Sale

You've built an intelligent agent that engages customers brilliantly—it answers questions, builds trust, personalizes recommendations. But when it's time to convert, you hit a wall. Your chatbot can't accept payment. It hands off to a third-party checkout (losing context and conversion rates), or worse, asks the user to navigate away entirely.

Imagine a course-selling bot that can: - Chat about learning outcomes - Answer student objections in real-time - Generate a payment link right there in the conversation - Confirm settlement and unlock course access—all without a human in the loop

Or a café ordering agent that takes a customer's order and securely collects payment via their bank's QR code, with settlement confirmed within seconds.

That's what AgentPay VN unlocks. In this tutorial, you'll integrate VietQR payments into your LLM agent in under 10 minutes.

Why AgentPay VN? Three Reasons

1. It Never Touches Your Money Unlike payment platforms that hold funds in escrow, AgentPay VN generates QR codes that point directly at your merchant bank account. No intermediary. No settlement delays. No new custody risk.

2. Open-Source, No Vendor Lock-in Built on MIT license Python SDK. Audit the code. Deploy anywhere. No surprise pricing tiers.

3. Built for AI from Day One The MCP (Model Context Protocol) server integrates seamlessly with Claude and other agents. Your agent doesn't just call payment logic—it reasons about it naturally.

Installation: 30 Seconds

pip install agentpay-vn

That's it. The SDK is lightweight (no heavy dependencies). You also get the MCP server for Claude integration:

agentpay-mcp

The 3-Line Payment Flow

AgentPay VN distills payment collection to three core steps:

  1. Create a payment request → Define amount, description, merchant ID
  2. Send checkout URL → Agent shares the link (or QR) with the customer
  3. Await settlement → Confirm the bank feed; unlock whatever comes next

Let's code it.

Step-by-Step: Your First Payment Integration

Step 1: Import and Initialize

from agentpay_vn import PaymentClient
import os

# Initialize with your merchant details
# (Set these as environment variables for security)
merchant_id = os.getenv("AGENTPAY_MERCHANT_ID")
merchant_name = os.getenv("AGENTPAY_MERCHANT_NAME")
bank_account = os.getenv("AGENTPAY_BANK_ACCOUNT")

client = PaymentClient(
    merchant_id=merchant_id,
    merchant_name=merchant_name,
    bank_account=bank_account
)

What's happening: - merchant_id: Your unique AgentPay identifier (get from https://agentpay.servicesai.vn/v1/docs) - merchant_name: Displayed on the VietQR code for customer confidence - bank_account: Your actual bank account (in format: BANK_CODE|ACCOUNT_NUMBER)

Step 2: Create a Payment Request

# Example: Customer buying a Python course
payment_request = client.create_payment_request(
    amount=299000,  # VND
    description="Python Advanced: Async & Concurrency",
    customer_email="student@example.com",
    order_id="ORDER_001_2024",
    metadata={
        "course_id": "python_async_101",
        "student_name": "Nguyen Van A"
    }
)

print(f"Checkout URL: {payment_request.checkout_url}")
print(f"QR Code: {payment_request.qr_code}")
print(f"Payment ID: {payment_request.payment_id}")

Line-by-line breakdown: - amount: Cost in Vietnamese Dong. No decimals (299,000 VND, not 299.00) - description: Shown on the QR code and settlement record (64 chars max for clarity) - customer_email: Optional, but useful for follow-ups - order_id: Your internal reference. Must be unique per request - metadata: Attach custom data (course IDs, user IDs, etc.). Retrieved on settlement confirmation

The response gives you: - checkout_url: A shareable link (can be shortened or embedded) - qr_code: Base64-encoded QR image for immediate display - payment_id: Internal reference for tracking settlement

Step 3: Await Settlement

import asyncio

async def confirm_payment(payment_id, timeout_seconds=120):
    """
    Poll for settlement confirmation.
    In production, use webhooks for real-time updates.
    """
    settlement = await client.await_settlement(
        payment_id=payment_id,
        timeout=timeout_seconds
    )

    if settlement.status == "confirmed":
        print(f"✓ Payment confirmed!")
        print(f"  Amount: {settlement.amount} VND")
        print(f"  Settled at: {settlement.settled_at}")
        print(f"  Bank ref: {settlement.bank_reference}")

        # Now unlock the course
        course_id = settlement.metadata.get("course_id")
        student_email = settlement.metadata.get("student_name")
        return {"course_id": course_id, "student": student_email}
    else:
        print(f"Payment pending or failed: {settlement.status}")
        return None

# Usage in your agent
result = asyncio.run(confirm_payment("payment_12345"))

What's important here: - await_settlement blocks until the bank confirms the transfer (typically 5–30 seconds) - settlement.status is either "confirmed", "pending", or "failed" - settlement.bank_reference: The actual bank transaction ID (immutable proof) - metadata: Your custom fields come back unchanged, so you know what was purchased

Integrating with Claude (MCP)

If you're using Claude as your agent, set up the MCP server for native payment handling:

{
  "mcpServers": {
    "agentpay-vn": {
      "command": "agentpay-mcp",
      "env": {
        "AGENTPAY_MERCHANT_ID": "your_merchant_id",
        "AGENTPAY_MERCHANT_NAME": "Your Shop",
        "AGENTPAY_BANK_ACCOUNT": "970422|1234567890"
      }
    }
  }
}

Add this to your Claude client configuration. Now Claude can: - Call create_payment_request directly in its reasoning loop - Suggest payment when appropriate ("Would you like to enroll now?") - Handle settlement confirmation without custom Python code

Real-World Walkthrough: An Online Course Bot

Let's build a mini agent that sells courses:

from agentpay_vn import PaymentClient
import asyncio

class CourseBot:
    def __init__(self):
        self.client = PaymentClient(
            merchant_id="demo_123",
            merchant_name="TechSchool VN",
            bank_account="970422|1234567890"
        )
        self.courses = {
            "python_async": {"name": "Python Async & Concurrency", "price": 299000},
            "go_web": {"name": "Go Web Development", "price": 399000},
        }

    async def sell_course(self, course_key, customer_email):
        # Step 1: Get course details
        course = self.courses[course_key]

        # Step 2: Create payment request
        payment = self.client.create_payment_request(
            amount=course["price"],
            description=course["name"],
            customer_email=customer_email,
            order_id=f"order_{course_key}_{int(time.time())}",
            metadata={"course_key": course_key, "email": customer_email}
        )

        # Step 3: Share with customer
        print(f"Course: {course['name']}")
        print(f"Price: {course['price']:,} VND")
        print(f"Pay here: {payment.checkout_url}")

        # Step 4: Await confirmation
        settlement = await self.client.await_settlement(payment.payment_id, timeout=300)

        if settlement.status == "confirmed":
            course_id = settlement.metadata["course_key"]
            student = settlement.metadata["email"]
            self.enroll_student(student, course_id)
            return f"✓ {student} enrolled in {course_id}!"
        else:
            return f"✗ Payment failed or timed out."

    def enroll_student(self, email, course_id):
        # Your enrollment logic here
        print(f"[DB] Enrolled {email} in {course_id}")

# Usage
bot = CourseBot()
result = asyncio.run(bot.sell_course("python_async", "student@example.com"))
print(result)

What happens: 1. Bot identifies the course and price 2. Creates a payment request with metadata (course ID, student email) 3. Shares the checkout link or displays QR in chat 4. Blocks until settlement is confirmed by the bank 5. On success, automatically enrolls the student 6. All without touching money or managing tokens

Best Practices: Do's and Don'ts

✅ DO ❌ DON'T
Store payment_id for auditing Hardcode merchant credentials
Set realistic timeouts (60–300s) Trust client-side "payment confirmed" signals
Include order_id to prevent duplicates Assume settlement without calling await_settlement
Use metadata for business logic Store sensitive data in metadata
Test with small amounts first Deploy without testing the full flow
Handle await_settlement timeouts gracefully Show checkout URL without explaining payment protection

Advanced Tips

Webhook Integration (Production)

Polling with await_settlement is simple but blocks your agent. For production, use webhooks:

from flask import Flask, request

app = Flask(__name__)

@app.route("/agentpay/webhook", methods=["POST"])
def handle_settlement():
    data = request.json
    payment_id = data["payment_id"]
    status = data["status"]
    metadata = data["metadata"]

    if status == "confirmed":
        # Async job: enroll student, send email, etc.
        enroll_student_async(metadata["email"], metadata["course_id"])

    return {"ok": True}, 200

Set the webhook URL in your AgentPay dashboard.

Idempotency: Prevent Double Charges

Always use unique order_id values. If your agent retries, the SDK detects duplicates:

import hashlib

def generate_order_id(course_id, customer_id, timestamp):
    unique_str = f"{course_id}_{customer_id}_{timestamp}"
    return hashlib.md5(unique_str.encode()).hexdigest()[:16]

order_id = generate_order_id("python_async", "user_42", 1704067200)

Batch Payments (Multiple Items)

For a cart with multiple courses:

total = sum([item["price"] for item in cart])
item_names = ", ".join([item["name"] for item in cart])

payment = client.create_payment_request(
    amount=total,
    description=f"Purchase: {item_names[:60]}...",
    metadata={"items": cart, "cart_total": total}
)

FAQ

Q: Does AgentPay VN hold my money? No. QR codes point directly to your bank account. Settlement is confirmed by your bank's feed, not by AgentPay's database.

Q: What's the transaction fee? AgentPay VN is MIT-licensed and open-source. There's no transaction fee—use it freely. Your bank may charge standard VietQR fees (typically 0–0.5%).

Q: Can I use this with international customers? Not yet. VietQR requires Vietnamese bank accounts. For international payments, integrate Stripe or PayPal alongside AgentPay VN.

Q: How do I test without real payments? Use AgentPay's sandbox mode (set AGENTPAY_SANDBOX=true in your environment). Sandbox QR codes won't charge real accounts.

Key Takeaways

Next Steps

  1. Install: pip install agentpay-vn
  2. Grab credentials: Sign up at https://agentpay.servicesai.vn/v1/docs and get your merchant_id
  3. Test locally: Run the course bot example above with sandbox mode
  4. Deploy: Add MCP config to Claude or integrate the SDK into your agent framework
  5. Monitor: Check settlement confirmations and reconcile with your bank feed

Your AI agent is now ready to collect payments. No middleman. No delays. No risk. Go sell.


Questions or issues? Check the full documentation or GitHub repo.

Get started →

← All posts