Build a Paid MCP Server: Charge Users Inside Claude

2026-08-06 · AgentPay VN

mcp-serverai-agentspayment-integrationpython-sdkvietqr

The Problem: Monetizing Claude Agents Is Still Broken

You've built an incredible MCP server—maybe a productivity assistant, a research tool, or a specialized business automation layer. It works beautifully inside Claude. But how do you actually charge for it?

Today's reality: Claude users can access your MCP server for free, or you're stuck cobbling together Stripe webhooks, managing refunds, and dealing with the complexity of holding customer funds. If you're running a bootstrapped SaaS or AI agent business, that overhead kills you. You need something that works inside Claude's context window, settles instantly to your bank account, and requires zero infrastructure overhead.

This is where AgentPay VN changes the game. It's a lightweight Python SDK that lets your MCP server collect real VietQR payments—Vietnam's instant QR banking standard—without ever touching the money. The QR code points straight at your merchant bank account, and confirmation happens in seconds.

Let's build a paid MCP server that actually works.

Why VietQR? Why AgentPay VN?

VietQR is Vietnam's national standard for instant bank transfers via QR code. Every Vietnamese bank supports it. For foreign merchants, it's the fastest way to accept payments from Vietnamese users without third-party processors.

AgentPay VN (open-source, MIT license) strips away the complexity:

How the Payment Flow Works

Understand the three-step lifecycle before you code:

Step 1: Create Payment Request Your agent calls create_payment_request() with an amount and customer ID. AgentPay generates a unique request ID.

Step 2: Send Checkout URL The agent displays a VietQR checkout URL to the user. They scan with their bank app and approve the transfer. Your agent waits.

Step 3: Await Settlement Your agent polls await_settlement() (or listens to a webhook). When the bank confirms the transfer, your agent unblocks the feature or service.

Nothing is held in between. The payment goes from customer's bank → your bank. Simple.

Getting Started: Install & Configure

Installation

pip install agentpay-vn

That's it. No API keys, no OAuth flows—at least not yet. AgentPay works with your own merchant credentials.

Quick SDK Example

Here's a minimal Python script that collects a payment:

from agentpay_vn import PaymentClient
import asyncio
import time

# Initialize the payment client
client = PaymentClient(
    merchant_id="YOUR_MERCHANT_ID",
    merchant_name="My AI Agent Service",
    merchant_account="1234567890",  # Your bank account number
    merchant_bank="970418"  # Bank code (e.g., 970418 for Techcombank)
)

async def charge_user():
    # Step 1: Create a payment request for 50,000 VND
    payment_request = await client.create_payment_request(
        amount_vnd=50000,
        customer_id="user_12345",
        description="Premium AI Report Generation"
    )

    print(f"QR Code URL: {payment_request.checkout_url}")
    print(f"Request ID: {payment_request.request_id}")

    # Step 2: Wait for settlement (with timeout)
    max_wait = 300  # 5 minutes
    start = time.time()

    while time.time() - start < max_wait:
        settlement = await client.await_settlement(
            request_id=payment_request.request_id,
            timeout_seconds=10
        )

        if settlement.is_settled:
            print(f"✓ Payment confirmed! Transaction: {settlement.transaction_id}")
            return True

        print("Waiting for bank confirmation...")
        await asyncio.sleep(2)

    print("✗ Payment timeout")
    return False

# Run it
asyncio.run(charge_user())

Line-by-line breakdown:

Deploying as an MCP Server for Claude

Instead of embedding the SDK in your own code, you can deploy AgentPay as an MCP server. Claude connects to it and treats payment functions as native tools.

Install MCP Server

agentpay-mcp --start --port 9000

This daemon exposes create_payment_request and await_settlement as MCP tools.

Configure Claude to Use It

In your Claude desktop settings or API config, add the MCP server:

{
  "mcpServers": {
    "agentpay": {
      "command": "agentpay-mcp",
      "args": ["--port", "9000"],
      "env": {
        "AGENTPAY_MERCHANT_ID": "YOUR_MERCHANT_ID",
        "AGENTPAY_MERCHANT_ACCOUNT": "1234567890",
        "AGENTPAY_MERCHANT_BANK": "970418"
      }
    }
  }
}

Now, inside Claude's conversation, you can say:

"I need to charge the user 100,000 VND for the premium report. Use the payment tool."

Claude will automatically call create_payment_request, display the QR link, and wait for settlement—all without leaving the chat.

Real-World Example: An AI-Powered Course Sales Bot

Let's build something concrete. Imagine an AI agent that sells short courses about Vietnamese digital marketing:

The Scenario: A user chats with Claude. They say: "I want the Advanced SEO course."

The agent (running your MCP server):

  1. Confirms the course details: "Advanced SEO (6 modules, 500K VND). Ready to pay?"
  2. User says yes.
  3. Agent calls create_payment_request(amount_vnd=500000, customer_id="user_abc", description="Advanced SEO Course").
  4. Agent displays the QR code in the chat.
  5. User scans with their bank app, approves 500K VND transfer.
  6. Agent polls await_settlement() every 2 seconds.
  7. After 8 seconds, the bank confirms the transfer.
  8. Agent unlocks the course: "✓ Payment confirmed! You now have access. Here's your first module..."

The entire flow takes ~20 seconds. No Stripe account. No third-party dashboard. Your bank account receives the money. Done.

Python MCP Handler (simplified):

from agentpay_vn import PaymentClient
import asyncio

client = PaymentClient(
    merchant_id="seo_course_bot",
    merchant_name="AI SEO Academy",
    merchant_account="9999888877",
    merchant_bank="970418"
)

async def sell_course(course_id: str, user_id: str) -> dict:
    """
    MCP handler: Sell a course module to a user.
    """
    course_prices = {
        "advanced_seo": 500000,
        "paid_ads": 300000,
        "analytics": 200000
    }

    if course_id not in course_prices:
        return {"success": False, "error": "Course not found"}

    amount = course_prices[course_id]

    # Create payment
    payment = await client.create_payment_request(
        amount_vnd=amount,
        customer_id=user_id,
        description=f"Course: {course_id}"
    )

    # Wait for payment
    settlement = await client.await_settlement(
        request_id=payment.request_id,
        timeout_seconds=30
    )

    if settlement.is_settled:
        # Grant access to course
        return {
            "success": True,
            "message": f"Payment confirmed! You now have access to {course_id}.",
            "transaction_id": settlement.transaction_id,
            "course_access_link": f"https://academy.local/courses/{course_id}?user={user_id}"
        }
    else:
        return {
            "success": False,
            "error": "Payment timeout. Please try again.",
            "request_id": payment.request_id
        }

This handler becomes an MCP tool. Claude calls it, waits, and reports the result.

Do's and Don'ts: Best Practices

Do Don't
Set reasonable timeouts (30–120 sec) Wait forever for settlement
Store request_id for reconciliation Assume immediate confirmation
Test with small amounts first (10K–50K VND) Charge before settlement is confirmed
Log transaction IDs to your database Rely only on in-memory state
Display the QR code prominently Hide or minimize the QR link
Handle network timeouts gracefully Let errors crash your agent

Advanced Tips

Webhook Confirmations

Instead of polling, AgentPay can POST to your webhook when settlement is confirmed:

payment = await client.create_payment_request(
    amount_vnd=100000,
    customer_id="user_xyz",
    webhook_url="https://yourserver.com/webhooks/agentpay"
)

Your endpoint receives a JSON payload with request_id, transaction_id, and status. This is faster than polling.

Reconciliation

Always sync your MCP server's records with your bank feed once daily:

await client.reconcile_transactions(
    start_date="2025-01-01",
    end_date="2025-01-31"
)

This ensures no payments slip through the cracks.

Multi-Currency (Future)

AgentPay currently supports VND. If you need USD or other currencies, use a fixed VND rate and document it clearly to users.

FAQ

Q: Does AgentPay hold my money? No. The QR code points directly to your merchant bank account. AgentPay is a transaction coordinator, not a wallet. Your bank confirms the deposit.

Q: How long does settlement actually take? VietQR transfers are instant at the bank level (seconds to minutes). AgentPay's await_settlement() typically confirms within 10–30 seconds. In rare cases, internal bank delays can extend this to 2–3 minutes.

Q: Can I use this outside Vietnam? Not currently. VietQR is Vietnam-specific. If you're a foreign merchant with Vietnamese customers, it works great. If your customers are in other countries, you'll need to integrate Stripe, PayPal, etc. in parallel.

Q: What's the minimum payment amount? Most Vietnamese banks accept transfers of 1,000 VND or more. Set a sensible floor for your use case (e.g., 50,000 VND ≈ $2 USD) to avoid micro-transactions.

Q: Is there a transaction fee? AgentPay itself is free (MIT open-source). Your bank may charge 0–2% per transfer depending on your merchant account tier. Clarify with your bank.

Key Takeaways

Next Steps

Ready to build your first paid MCP server?

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

  2. Get your merchant credentials from your Vietnamese bank's business portal (merchant ID, account number, bank code).

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

  4. Explore the GitHub repo: https://github.com/phuocdu/agentpay-vn

  5. Start small: Test with a 50K VND charge in your local Claude environment. Once it works, scale to production.

Your AI agents can now earn real money. Build something great.

Get started →

← All posts