Build a Paid MCP Server: Charge Users from Claude

2026-09-16 · AgentPay VN

mcp serversai agentspython sdkpayment integrationclaude

The Problem: Your AI Agent Creates Value, But Can't Get Paid

You've built an incredible Claude MCP server—maybe it writes SEO content, analyzes datasets, or generates design mockups. Users love it. But here's the catch: every time someone uses it, you're burning compute credits. You want to monetize, but integrating payments feels like a Sisyphean task. Payment processors demand PCI compliance, merchant accounts take weeks, and you'd rather focus on building the agent itself, not wrestling with webhooks and settlement callbacks.

What if you could collect payment inside the Claude conversation itself—no redirects, no separate checkout page, no holding user funds?

That's exactly what AgentPay VN enables: a lightweight, open-source Python SDK (MIT licensed) that lets your AI agents request VietQR payments directly. The QR code points straight to your bank account. No middleman holds the money. When the user scans and pays, a bank feed confirms settlement within seconds.

In this guide, you'll build a paid MCP server that charges users for premium agent capabilities—all from inside Claude.


Why MCP Servers Need Built-In Payments

Traditional payment integration creates friction:

With AgentPay VN, the flow is immediate and transparent:

  1. User requests a paid feature (e.g., "Analyze my 10,000-row dataset").
  2. Your agent creates a payment request → AgentPay generates a VietQR code.
  3. User scans the QR in their mobile banking app (Vietnamese users scan VietQR natively).
  4. Payment settles to your bank account in seconds; your agent confirms and delivers the output.

No accounts to create. No tokens to manage. Just direct bank-to-bank settlement.


Getting Started: Installation & Setup

Step 1: Install AgentPay VN SDK

pip install agentpay-vn

This installs the Python SDK. For MCP integration, also install the MCP server:

pip install agentpay-mcp

Step 2: Obtain Your Merchant Credentials

AgentPay VN integrates with your bank's VietQR infrastructure. You'll need:

Each payment request is tied to your account, not AgentPay's. The SDK simply orchestrates the flow.

Step 3: Configure Your Environment

Create a .env file:

AGENTPAY_MERCHANT_ID=your_merchant_id
AGENTPAY_API_KEY=your_api_key
AGENTPAY_BANK_ACCOUNT=your_bank_account_number

Load these in your code:

import os
from dotenv import load_dotenv

load_dotenv()
merchant_id = os.getenv("AGENTPAY_MERCHANT_ID")
api_key = os.getenv("AGENTPAY_API_KEY")
bank_account = os.getenv("AGENTPAY_BANK_ACCOUNT")

Building Your First Paid MCP Server

The 3-Line Payment Flow

AgentPay VN distills payment collection into three core operations:

# 1. Create a payment request
# 2. Send the checkout URL (or QR code) to the user
# 3. Await settlement confirmation

Let's build a real example: a content analysis MCP server that charges users per analysis.

Code Example: Paid Content Analyzer

from agentpay_vn import PaymentClient, PaymentRequest
import asyncio
import os

# Initialize the AgentPay client
client = PaymentClient(
    merchant_id=os.getenv("AGENTPAY_MERCHANT_ID"),
    api_key=os.getenv("AGENTPAY_API_KEY"),
    bank_account=os.getenv("AGENTPAY_BANK_ACCOUNT"),
)

async def analyze_content_with_payment(user_content: str, amount_vnd: int = 50000):
    """
    Premium content analysis. Charges the user before processing.

    Args:
        user_content: The text/document to analyze
        amount_vnd: Price in Vietnamese Dong (default: 50,000 VND ≈ $2 USD)
    """

    # Step 1: Create a payment request
    # This generates a unique payment ID tied to this user's request
    payment_request = PaymentRequest(
        amount=amount_vnd,
        description=f"Content Analysis - {len(user_content)} chars",
        order_id=f"analysis_{int(asyncio.get_event_loop().time())}",
    )

    # Step 2: Request payment from AgentPay
    # Returns a checkout URL with an embedded VietQR code
    checkout_response = await client.create_payment_request(payment_request)

    # The checkout_url can be displayed as a QR code or link to the user
    checkout_url = checkout_response.checkout_url
    qr_code = checkout_response.qr_code  # Base64 or image data

    print(f"🔗 Scan to pay: {checkout_url}")
    print(f"💳 Amount: {amount_vnd:,} VND")

    # Step 3: Wait for settlement
    # This polls the bank feed and confirms when payment is received
    settlement = await client.await_settlement(
        payment_id=checkout_response.payment_id,
        timeout_seconds=300,  # Wait up to 5 minutes
    )

    if settlement.status == "SETTLED":
        # Payment confirmed! Now deliver the premium service
        print(f"✅ Payment received: {settlement.amount} VND")

        # Perform the expensive analysis
        analysis_result = perform_analysis(user_content)
        return {
            "status": "success",
            "analysis": analysis_result,
            "transaction_id": settlement.transaction_id,
        }
    else:
        # Payment failed or timed out
        return {
            "status": "payment_failed",
            "message": "Payment was not received. Please try again.",
        }

def perform_analysis(content: str) -> dict:
    """Simulate premium analysis (replace with real logic)."""
    return {
        "word_count": len(content.split()),
        "sentiment": "positive",
        "readability_score": 72,
        "key_topics": ["AI", "payments", "MCP"],
    }

# Usage in your MCP tool
if __name__ == "__main__":
    test_content = "AgentPay VN enables AI agents to collect payments..."
    result = asyncio.run(analyze_content_with_payment(test_content))
    print(result)

Line-by-line explanation:


Integrating with Claude via MCP

MCP Server Configuration

To expose your paid analyzer as a Claude tool, define it in your MCP server config:

{
  "mcpServers": {
    "paid-analyzer": {
      "command": "python",
      "args": ["-m", "agentpay_mcp", "serve"],
      "env": {
        "AGENTPAY_MERCHANT_ID": "${AGENTPAY_MERCHANT_ID}",
        "AGENTPAY_API_KEY": "${AGENTPAY_API_KEY}",
        "AGENTPAY_BANK_ACCOUNT": "${AGENTPAY_BANK_ACCOUNT}"
      }
    }
  }
}

Add this to your Claude Desktop config file:

When you restart Claude, the paid-analyzer MCP server appears as an available tool. Users can invoke it naturally:

"Analyze this dataset for me. I'm willing to pay."

Claude invokes the tool, displays the QR code, and waits for settlement before returning results.


Real-World Example: A Paid Course Bot

Imagine you're running an online course platform. You want Claude to answer students' questions, but only if they've paid for access.

The Flow

  1. Student (first-time): "What's the difference between supervised and unsupervised learning?"
  2. Claude: "I'd love to help! This is a premium question (¥50,000). Please scan this QR code to unlock."
  3. Student scans QR in their banking app → payment settles in 5 seconds.
  4. Claude: "Payment confirmed! Here's a detailed answer..." + provides a 500-word explanation.
  5. Future questions in the same session/day: "Granted (already paid)" → instant answer.

Implementation

class PaidCourseBot:
    def __init__(self):
        self.payment_client = PaymentClient(...)
        self.paid_users = set()  # In production, use a database

    async def answer_question(self, user_id: str, question: str) -> str:
        # Check if user has already paid today
        if user_id in self.paid_users:
            return generate_answer(question)  # Instant response

        # First-time asker: charge them
        payment_req = PaymentRequest(
            amount=50000,
            description=f"Course Q&A: {question[:30]}...",
            order_id=f"course_{user_id}_{int(time.time())}",
        )

        checkout = await self.payment_client.create_payment_request(payment_req)

        # Display QR to user
        qr_message = f"""📚 **Premium Answer**
        Cost: 50,000 VND
        Scan to unlock: {checkout.checkout_url}
        """

        # Wait for payment
        settlement = await self.payment_client.await_settlement(
            checkout.payment_id,
            timeout_seconds=300
        )

        if settlement.status == "SETTLED":
            self.paid_users.add(user_id)  # Mark as paid
            return generate_answer(question)
        else:
            return "Payment timed out. Please try again."

Advanced Tips & Best Practices

1. Tiered Pricing

Offer multiple tiers based on analysis complexity:

pricing = {
    "quick_summary": 20000,      # ~$1 USD
    "detailed_analysis": 50000,  # ~$2 USD
    "expert_report": 150000,     # ~$6 USD
}

2. Session-Based Access

Track paid sessions to avoid re-charging:

session_payments = {}  # {session_id: {"paid_until": timestamp, "amount": total}}

def has_active_payment(session_id: str) -> bool:
    if session_id not in session_payments:
        return False
    expiry = session_payments[session_id]["paid_until"]
    return time.time() < expiry

3. Graceful Fallback

Always provide a free tier:

if user_requests_premium:
    await charge_for_premium(user)
else:
    return free_response()  # Limited but functional

4. Error Handling

try:
    settlement = await client.await_settlement(payment_id, timeout=300)
except TimeoutError:
    # User didn't pay in time
    return "Please try again—payment window expired."
except PaymentError as e:
    # Network error, bank error, etc.
    log_error(e)
    return "Temporary issue. Support has been notified."

Do's and Don'ts

Do Don't
Request payment before expensive operations Charge after delivering the service
Display the exact amount upfront Use vague pricing ("From 20,000 VND")
Set reasonable timeouts (3–5 min) Force users to wait indefinitely
Log all transactions for auditing Lose track of which users paid
Offer free tier or trial Charge for every interaction
Use VietQR (native to Vietnamese banks) Redirect users to external payment gates

FAQ

Q: Does AgentPay VN hold my money?

No. The QR code points directly to your bank account. AgentPay acts as an orchestrator only—it generates QR codes and confirms settlement via your bank's feed, but never touches the funds.

Q: How long until I receive payment?

VietQR settlements are typically confirmed within 30 seconds to 2 minutes. Your agent receives confirmation immediately and can deliver the service.

Q: What if the user scans the QR but doesn't complete payment?

The await_settlement() call will timeout (default 5 minutes). Your MCP tool returns an error message; no service is delivered, no charge is incurred.

Q: Can I use AgentPay VN outside Vietnam?

Currently, AgentPay VN is optimized for Vietnamese users with access to VietQR. International support is on the roadmap.

Q: Is AgentPay VN secure?

Yes. The SDK is open-source (MIT), and payments are processed through your bank's VietQR infrastructure—not AgentPay's servers. The code is auditable on GitHub.


Key Takeaways


Get Started Now

Your monetized MCP server is just a few commands away:

pip install agentpay-vn

Then deploy your first paid Claude agent. Every user interaction is an opportunity—now you can capture value directly.

Resources:

Questions? Open an issue on GitHub or reach out in the AgentPay community. Happy monetizing! 🚀

Get started →

← All posts