Add VietQR Checkout to Your LLM Agent in 10 Minutes

2026-08-13 · AgentPay VN

pythonai-agentspaymentsvietqrintegration

The Problem: Your AI Agent Can't Close Sales

You've built a brilliant LLM agent—maybe it books appointments, generates invoices, or sells digital products. It can talk, reason, and solve problems. But when a customer says "I want to buy," your agent hits a wall. It can't actually collect money.

You could pipe users to Stripe, but that breaks the agent's autonomy and creates friction. You could implement Momo or bank transfers manually, but those need polling, callbacks, and complex state management. What you really need is a dead-simple payment tool that your agent can invoke like any other function—no webhooks, no mess, no money touching your infrastructure.

That's where AgentPay VN comes in.

What Is AgentPay VN?

AgentPay VN is an open-source Python SDK and MCP server (MIT licensed) that gives your LLM agent the ability to create and settle VietQR payments in seconds. Here's what makes it special:

In the next 10 minutes, you'll have a working checkout flow in your agent.

Installation: One Command

First, install the SDK:

pip install agentpay-vn

If you're using AgentPay via MCP (recommended for Claude Desktop or similar), also install:

pip install agentpay-mcp

That's step one. No API keys to hunt down, no registration forms. AgentPay uses your bank's VietQR infrastructure directly.

The 3-Step Payment Flow Explained

Step 1: Create a Payment Request

Your agent calls create_payment_request() with a few details: amount, description, and customer info. AgentPay generates a dynamic VietQR code (or checkout URL) that points straight to your merchant bank account.

Step 2: Send the Checkout URL

Your agent shares the checkout URL with the customer (via chat, SMS, email, whatever). The customer scans the QR code with their phone's banking app and pays. No redirect, no third-party login.

Step 3: Await Settlement

Your agent calls await_settlement() and waits (typically 30 seconds to a few minutes) for the bank to confirm the deposit. Once confirmed, your agent knows the payment succeeded and can proceed—deliver the product, send the invoice, update the database.

Code Example: A Simple Payment Agent

Let's build a minimal agent that sells a digital course. Here's how it works:

from agentpay_vn import create_payment_request, await_settlement
import json

# Your merchant bank details (usually set via env or config)
MERCHANT_ACCOUNT = "0712345678"
MERCHANT_BANK = "970422"  # Example: Techcombank
MERCHANT_NAME = "AI Academy"

def sell_course(course_name: str, price: int, customer_email: str) -> dict:
    """
    Process a course sale. Agent calls this when customer agrees to buy.
    """

    # Step 1: Create payment request
    request = create_payment_request(
        amount=price,
        description=f"Course: {course_name}",
        merchant_account=MERCHANT_ACCOUNT,
        merchant_bank=MERCHANT_BANK,
        merchant_name=MERCHANT_NAME,
        order_id=f"course_{course_name}_{int(time.time())}",
        customer_email=customer_email
    )

    # Step 2: Share checkout URL with customer
    checkout_url = request["checkout_url"]
    payment_id = request["payment_id"]

    print(f"Send this link to customer: {checkout_url}")
    print(f"QR code generated. Payment ID: {payment_id}")

    # Step 3: Wait for settlement (blocks until payment arrives or timeout)
    settlement = await_settlement(
        payment_id=payment_id,
        timeout_seconds=300  # Wait up to 5 minutes
    )

    if settlement["status"] == "settled":
        # Payment confirmed! Deliver the course.
        return {
            "success": True,
            "message": f"Payment received! Sending {course_name} to {customer_email}.",
            "amount": settlement["amount"],
            "transaction_id": settlement["transaction_id"]
        }
    else:
        return {
            "success": False,
            "message": "Payment not received within timeout.",
            "payment_id": payment_id
        }

# Example usage from an agent
result = sell_course(
    course_name="Python for LLMs",
    price=299000,  # 299k VND
    customer_email="student@example.com"
)
print(json.dumps(result, indent=2))

Line-by-line breakdown:

  1. Imports: create_payment_request and await_settlement are the two main functions you'll use.
  2. Merchant config: Your bank account, bank code, and name (usually from env variables in production).
  3. create_payment_request(): Generates a unique VietQR checkout for this transaction. Returns a checkout_url and payment_id.
  4. await_settlement(): Polls the bank feed until the exact amount arrives. Blocks your agent's execution until settlement is confirmed or timeout is reached.
  5. Conditional delivery: Once settled, your agent can deliver the product, unlock access, send confirmation emails, etc.

Setting Up Your Agent with MCP (Claude Example)

If you're using Claude Desktop or another MCP-compatible client, you can expose AgentPay as a tool. Here's the config:

{
  "mcpServers": {
    "agentpay-vn": {
      "command": "python",
      "args": ["-m", "agentpay_mcp"],
      "env": {
        "MERCHANT_ACCOUNT": "0712345678",
        "MERCHANT_BANK": "970422",
        "MERCHANT_NAME": "Your Business Name"
      }
    }
  }
}

Add this to your Claude Desktop claude_desktop_config.json file. Restart Claude, and AgentPay functions will appear in your agent's tool menu. Claude can now call create_payment_request and await_settlement directly, without you writing integration code.

Real-World Example: An Online Café Bot

Imagine a WhatsApp bot for a local café. The bot takes orders, recommends drinks, and handles payment.

Scenario: A customer orders a cold brew (85,000 VND).

  1. Bot: "I've prepared your order. That's 85,000 VND. Here's your checkout link: [QR code]."
  2. Customer: Scans QR with banking app, pays 85,000 VND.
  3. Bot (via await_settlement()): Detects the deposit within 30 seconds. "Payment confirmed! Your order is ready for pickup in 5 minutes. Thanks!"
  4. Backend: Prints the order, updates inventory, logs the transaction.

No Stripe account. No Momo delays. The café's bank account gets the money directly, and the bot's execution doesn't break.

Do's and Don'ts

Do Don't
Use await_settlement() with a reasonable timeout (60–300 sec). Leave await_settlement() running indefinitely.
Store payment_id for reconciliation. Assume payment succeeded without calling await_settlement().
Handle timeout gracefully (retry or ask customer to try again). Assume the bank feed is instant—it can take 30 sec to a few min.
Keep merchant account details in env variables. Hardcode credentials in source code.
Test with small amounts first. Go live without testing end-to-end.

Advanced: Webhook-Style Polling with Async

For higher-throughput scenarios (e.g., a marketplace with many simultaneous orders), you might not want to block on await_settlement(). Here's an async pattern:

import asyncio
from agentpay_vn import create_payment_request, check_settlement_status

async def process_payment_async(amount: int, description: str) -> str:
    """
    Non-blocking payment processing. Fire and forget.
    """
    # Create the request
    request = create_payment_request(
        amount=amount,
        description=description,
        merchant_account=MERCHANT_ACCOUNT,
        merchant_bank=MERCHANT_BANK,
        merchant_name=MERCHANT_NAME,
        order_id=f"order_{int(time.time())}"
    )

    payment_id = request["payment_id"]
    checkout_url = request["checkout_url"]

    # Return immediately. Customer pays at their own pace.
    print(f"Payment request created: {payment_id}")
    print(f"Checkout URL: {checkout_url}")

    # In the background, keep checking for settlement.
    asyncio.create_task(wait_for_settlement_async(payment_id))

    return payment_id

async def wait_for_settlement_async(payment_id: str, max_attempts: int = 60):
    """
    Poll for settlement every 5 seconds, up to 5 minutes.
    """
    for attempt in range(max_attempts):
        status = check_settlement_status(payment_id)

        if status["is_settled"]:
            print(f"[{payment_id}] Payment settled! Amount: {status['amount']} VND")
            # Trigger delivery, send email, etc.
            break

        await asyncio.sleep(5)  # Check every 5 seconds
    else:
        print(f"[{payment_id}] Settlement timeout.")

# Usage
async def main():
    payment_id = await process_payment_async(
        amount=500000,
        description="Premium subscription (1 month)"
    )
    # Agent continues with other tasks while payment settles in background.

asyncio.run(main())

This is useful if your agent handles multiple customer interactions simultaneously.

Frequently Asked Questions

Q: Does AgentPay hold my money?

A: No. AgentPay is a facilitator, not a payment processor. The VietQR points directly at your merchant bank account. Money never touches AgentPay's systems. A bank feed confirms settlement.

Q: What happens if the customer doesn't pay within the timeout?

A: await_settlement() returns with status: "timeout". Your agent can retry (generate a new checkout) or ask the customer to try again. No money is lost.

Q: Can I use AgentPay with GPT-4, Gemini, or just Claude?

A: The SDK is bank-agnostic Python. The MCP server works with any MCP-compatible LLM client (Claude, custom agents, etc.). You can also call AgentPay functions directly in your own agent code.

Q: Is there a cost?

A: AgentPay is open-source (MIT). No fees from AgentPay. You pay only standard bank charges for VietQR transfers (typically 0% or minimal).

Key Takeaways

Next Steps

  1. Install: pip install agentpay-vn
  2. Read the docs: https://agentpay.servicesai.vn/v1/docs
  3. Explore the repo: https://github.com/phuocdu/agentpay-vn
  4. Integrate into your agent: Use the code examples above or configure MCP.
  5. Test end-to-end: Create a payment request, scan the QR, and confirm settlement.

Your AI agent is now ready to sell.

Get started →

← All posts