Build a Paid MCP Server: Charge Users Inside Claude

2026-08-29 · AgentPay VN

mcpai-agentspaymentspythonvietqr

The Problem: Your AI Agent Creates Value, But You Can't Monetize It

Imagine you've built an MCP server that helps users generate custom course materials, design logos, or manage inventory. Your Claude agent does the work beautifully—but when the user asks "how do I pay you?" you have nothing to say.

Most MCP servers today are free-to-use because monetization felt impossible. Collecting payment typically means redirecting users to a web checkout, breaking the agent's flow and losing conversion. Even worse, if you're in Southeast Asia, traditional payment gateways charge 3–5% in fees and require complex integrations.

There's a better way. AgentPay VN changes this: your MCP server can collect payments directly from Claude, settling funds instantly to your Vietnamese bank account via VietQR—without ever touching the money yourself.

What Is AgentPay VN, Really?

AgentPay VN is an open-source (MIT) Python SDK and MCP server that does one thing exceptionally well: it connects AI agent requests to instant bank transfers.

Here's the architecture:

  1. Your MCP server invokes create_payment_request() when a user wants a paid feature.
  2. AgentPay generates a VietQR code pointing directly to your merchant bank account—not AgentPay's.
  3. User scans or transfers money using any Vietnamese banking app (BIDV, Techcombank, Vietcombank, etc.).
  4. Bank feed confirms settlement in seconds; your agent resumes the task.
  5. You own 100% of the money—AgentPay never touches it.

The entire SDK is just pip install agentpay-vn, and the MCP server runs as agentpay-mcp. No OAuth redirects. No payment processor accounts. No fees beyond what your bank charges.

Step 1: Install and Configure AgentPay VN

Start by installing the SDK in your Python environment:

pip install agentpay-vn

Next, set up environment variables for your merchant account. You'll need your Vietnamese bank details:

export AGENTPAY_MERCHANT_ID="your_merchant_id"
export AGENTPAY_BANK_ACCOUNT="your_bank_account_number"
export AGENTPAY_BANK_NAME="your_bank_name"  # e.g., "BIDV"
export AGENTPAY_BANK_BRANCH="your_branch_code"

Start the MCP server:

agentpay-mcp

This runs a local server on localhost:3000 (or your configured port). Claude will communicate with it via stdio or network socket depending on your setup.

Step 2: Create Your First Payment Request

Here's a real Python example. Say you're building a course-generation bot. When a user requests a premium course outline, you charge them 50,000 VND:

from agentpay_vn import create_payment_request, await_settlement
import uuid

# Generate a unique request ID (use UUID to prevent duplicates)
request_id = str(uuid.uuid4())

# Step 1: Create the payment request
payment = create_payment_request(
    merchant_id="your_merchant_id",
    amount_vnd=50000,  # 50,000 VND for a premium course outline
    request_id=request_id,
    description="Premium course outline: Advanced Python",
    callback_url="https://yourserver.com/payment-callback"  # Optional
)

print(f"Checkout URL: {payment['checkout_url']}")
print(f"QR Code (base64): {payment['qr_code']}")
# Output:
# Checkout URL: https://agentpay.servicesai.vn/v1/checkout/abc123xyz...
# QR Code (base64): iVBORw0KGgoAAAANSUhEUgAAAQAAAAEA...

Line-by-line explanation: - request_id: A unique identifier for this transaction. If the user retries, the same ID prevents double-charging. - amount_vnd: The price in Vietnamese Dong. No decimals—VietQR works with whole numbers only. - description: Appears on the user's bank statement, so keep it clear and brief. - callback_url: Optional. AgentPay will POST settlement confirmation here with transaction details.

Step 3: Wait for Settlement and Deliver the Service

After the user scans the QR and transfers money, your agent waits for confirmation:

# Step 2: Wait for the payment to settle
timeout_seconds = 300  # Wait up to 5 minutes
settlement = await_settlement(
    request_id=request_id,
    timeout=timeout_seconds
)

if settlement['status'] == 'settled':
    print(f"✓ Payment confirmed! Amount: {settlement['amount_vnd']} VND")
    print(f"  Transaction ID: {settlement['transaction_id']}")
    print(f"  Settled at: {settlement['timestamp']}")

    # NOW deliver the premium content
    course_outline = generate_premium_course(
        topic="Advanced Python",
        depth="expert"
    )
    return course_outline

elif settlement['status'] == 'timeout':
    print("❌ User didn't pay within 5 minutes.")
    return "Payment cancelled. Please try again."

elif settlement['status'] == 'failed':
    print(f"❌ Payment failed: {settlement['reason']}")
    return "Payment failed. Please check your bank account."

Key details: - await_settlement() is blocking—it holds the function open until the bank confirms the transfer or timeout occurs. - Settlement typically happens in 2–15 seconds for intra-bank transfers (same bank as merchant). - Inter-bank transfers may take 30–60 seconds. - Always set a reasonable timeout; 5 minutes is safe for conversational flows. - The transaction_id is crucial—log it in your database to match payments with user accounts.

Integrating into Your MCP Server: Configure Claude

If you're running a custom MCP server, you need to expose AgentPay's tools to Claude. Here's the config for claude_desktop_config.json:

{
  "mcpServers": {
    "agentpay-vn": {
      "command": "python",
      "args": ["-m", "agentpay_vn.mcp"]
    }
  }
}

Alternatively, if you're embedding AgentPay into your own MCP server, expose these tools:

{
  "tools": [
    {
      "name": "create_payment_request",
      "description": "Generate a VietQR payment request. Returns checkout_url and QR code for the user to scan.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "amount_vnd": {"type": "integer", "description": "Amount in Vietnamese Dong"},
          "description": {"type": "string", "description": "What the user is paying for"},
          "request_id": {"type": "string", "description": "Unique transaction ID (use UUID)"}
        },
        "required": ["amount_vnd", "description", "request_id"]
      }
    },
    {
      "name": "await_settlement",
      "description": "Wait for bank confirmation that the payment settled.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "request_id": {"type": "string"},
          "timeout": {"type": "integer", "description": "Seconds to wait (default 300)"}
        },
        "required": ["request_id"]
      }
    }
  ]
}

Now Claude can invoke these tools directly during a conversation.

Real-World Example: Café Menu AI Agent

Let's build a practical example: a Vietnamese café selling custom dessert recommendations via Claude.

The scenario: A customer asks, "I want a dessert recommendation tailored to my mood." Your agent can offer two tiers: - Free: Basic suggestion (cà phê đen, bánh mì). - Premium (15,000 VND): Detailed recipe + local sourcing tips.

from agentpay_vn import create_payment_request, await_settlement
import uuid

def handle_dessert_request(user_message: str, user_mood: str) -> str:
    """
    Process dessert recommendation. Free tier returns 3 options;
    premium tier returns detailed recipes and sourcing.
    """

    # Check if user wants premium
    if "detailed" in user_message.lower() or "recipe" in user_message.lower():
        # Create payment request
        request_id = str(uuid.uuid4())
        payment = create_payment_request(
            merchant_id="cafe_vn_001",
            amount_vnd=15000,
            request_id=request_id,
            description=f"Premium dessert recipe: {user_mood} mood"
        )

        # Tell Claude to show the QR
        print(f"Scan to unlock premium: {payment['checkout_url']}")

        # Wait for payment
        settlement = await_settlement(request_id, timeout=180)

        if settlement['status'] == 'settled':
            # Deliver premium content
            return f"""
            🍰 Premium Dessert Recipe
            Mood: {user_mood}

            Bánh cam nóng (Warm sesame ball)
            - Ingredients: Glutinous rice flour, black sesame...
            - Local source: Hoa Nước Mía (Dist. 1, HCMC)
            - Estimated cost: 8,000 VND

            Chè ba màu (Three-color dessert)
            - Perfect for: Contemplative moods
            - Recipe: [Full instructions]
            """
        else:
            return "Payment not confirmed. Please try again."

    else:
        # Free tier: basic suggestions
        return f"""
        🍨 Quick Suggestions for {user_mood} mood:
        1. Cà phê đen (Iced black coffee)
        2. Bánh mì nóng (Warm baguette)
        3. Sữa chua (Vietnamese yogurt)

        Want detailed recipes? Reply with 'detailed' for premium access (15,000 VND).
        """

This structure is simple but powerful: free users get a taste, and those willing to pay get full recipes and sourcing advice.

Do's and Don'ts: Common Mistakes

Do Don't
Use uuid.uuid4() for request_id to prevent duplicates Reuse the same request_id across multiple users
Set timeout to 3–5 minutes for conversational agents Set timeout to 30+ minutes (user context expires)
Log transaction_id + request_id in your database Trust client-side payment claims without bank confirmation
Display the checkout_url or QR code to the user Attempt to process QR codes yourself (AgentPay handles it)
Handle timeout and failed statuses gracefully Assume all settlement calls succeed
Test with small amounts (5,000–10,000 VND) first Jump to production without testing

Advanced Tips: Building Production-Grade Systems

1. Idempotency: Prevent Double-Charging

Always generate request_id as a hash of the user + feature:

import hashlib

def generate_idempotent_request_id(user_id: str, feature: str) -> str:
    combined = f"{user_id}:{feature}:{int(time.time() / 60)}"  # Per minute
    return hashlib.sha256(combined.encode()).hexdigest()[:32]

2. Async Waiting Without Blocking

For high-concurrency servers, use async patterns:

import asyncio

async def wait_for_payment_async(request_id: str, timeout: int = 300):
    start = time.time()
    while time.time() - start < timeout:
        result = await_settlement(request_id, timeout=5)
        if result['status'] in ['settled', 'failed']:
            return result
        await asyncio.sleep(2)  # Poll every 2 seconds
    return {'status': 'timeout'}

3. Webhook for Async Settlement

If you set callback_url during create_payment_request(), AgentPay will POST to it:

# In your FastAPI/Flask app:
@app.post("/payment-callback")
def handle_settlement(request: dict):
    """
    AgentPay sends: {
      "request_id": "abc123",
      "transaction_id": "TXN20240115001",
      "amount_vnd": 50000,
      "status": "settled",
      "timestamp": "2024-01-15T10:30:00Z"
    }
    """
    request_id = request['request_id']

    # Update your database
    db.update_payment(request_id, status='confirmed')

    # Notify the agent/user asynchronously
    notify_user(request_id, "Payment confirmed!")

    return {"acknowledged": True}

FAQ

Q: Does AgentPay take a cut of the payment? A: No. AgentPay never touches the money. VietQR transfers go directly to your bank account. You pay only your bank's standard transfer fees (typically 0–2,000 VND per transfer, or sometimes free for same-bank transfers).

Q: What if my Claude agent crashes after the user pays but before I deliver the service? A: The settlement is confirmed by your bank—the money is already in your account. Log every transaction_id and implement a "claim" system where users can re-request the service with proof of payment.

Q: Can I use AgentPay for non-Vietnamese banks? A: Currently, VietQR targets Vietnamese banking networks. If you have international users, you'd need to combine AgentPay with a secondary payment processor (Stripe, etc.). This is on the roadmap.

Q: How do I test locally without real transactions? A: AgentPay provides a sandbox mode. Set AGENTPAY_MODE=sandbox in your environment. Sandbox transactions settle immediately without hitting real banks—perfect for testing your agent's flow.

Key Takeaways

Get Started Now

Ready to monetize your AI agent? Here's the fastest path:

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

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

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

  4. Start with a small test payment (5,000–10,000 VND) to verify your bank account integration.

  5. Deploy your MCP server and let Claude handle the rest.

Your AI agent's expertise is valuable. AgentPay makes it easy for users to pay—and for you to receive every dong. No waiting, no fees, no friction.

Get started →

← All posts