Build a Paid MCP Server: Charge Users from Claude
The Problem: Your AI Agent Delivers Value—But How Do You Get Paid?
Imagine you've built a brilliant Claude-powered MCP server that helps users generate marketing copy, analyze documents, or book consultations. It works flawlessly. Users love it. But when someone asks "how much does this cost?"—you freeze.
Traditional payment systems weren't designed for AI agent interactions. They require page redirects, modal popups, and friction. Your agent can't seamlessly collect money within the Claude conversation. You're forced into clunky choices: ask users to visit a separate website, implement complex webhook orchestration, or worse—offer your service free while hoping for sponsorship.
This is where AgentPay VN changes the game.
What AgentPay VN Does (In Plain English)
AgentPay VN is an open-source Python SDK and MCP server that lets your AI agents collect VietQR payments directly from within Claude—without ever touching the money.
Here's the magic:
- Your agent creates a payment request (e.g., "Process this 100,000 VND analysis")
- A QR code URL is generated pointing straight to the merchant's bank account
- The user scans and pays via their VietQR-compatible banking app
- Your system receives a settlement confirmation and delivers the service
- Money lands in your bank account—AgentPay never holds it
It's MIT-licensed, runs on your infrastructure, and requires just one line: pip install agentpay-vn.
Real-World Scenario: A Course-Selling Bot
Let's say you've deployed an MCP server that sells access to a premium AI training course. Here's what happens:
User: "Can I enroll in the advanced prompt engineering course?"
Claude (via AgentPay):
Sure! The 7-week course costs 499,000 VND.
Here's your payment link: [QR code image]
Scan with your banking app and I'll unlock your materials.
Behind the scenes: Your agent detects the payment confirmation and immediately generates a unique access token, enrolls the user in your LMS, and sends a welcome email. All within 5 seconds. No manual work. No merchant account complexity.
Setting Up: The 3-Step Installation
Step 1: Install the SDK
pip install agentpay-vn
This gives you the Python library. If you want to use AgentPay as an MCP server (recommended for Claude integration), also install:
pip install agentpay-mcp
Step 2: Configure Your MCP Server in Claude
Add this to your Claude desktop configuration file (typically ~/.claude_desktop_config.json):
{
"mcpServers": {
"agentpay": {
"command": "agentpay-mcp",
"args": [
"--merchant-id",
"YOUR_MERCHANT_ID",
"--bank-account",
"YOUR_BANK_ACCOUNT",
"--bank-code",
"BANK_CODE"
]
}
}
}
Replace placeholders with your VietQR merchant details. Your bank provides these when you set up QR billing.
Step 3: Restart Claude and Verify
Restart Claude Desktop. AgentPay tools are now available to your agents.
The Core Flow: Create → Send → Settle
Let's walk through the actual code your MCP server would execute:
Creating a Payment Request
from agentpay_vn import PaymentClient
# Initialize the client with your credentials
client = PaymentClient(
merchant_id="YOUR_MERCHANT_ID",
bank_account="0123456789",
bank_code="970436", # Example: Techcombank
api_key="your_api_key_here"
)
# Create a payment request
payment = client.create_payment_request(
amount=499000, # VND
description="Premium Course Access - 7 weeks",
order_id="course_001_user123",
currency="VND"
)
print(f"Payment URL: {payment['checkout_url']}")
print(f"Request ID: {payment['request_id']}")
# Output:
# Payment URL: https://qr.vietqr.io/...
# Request ID: req_abc123xyz
Line-by-line breakdown:
- PaymentClient(): Initializes the SDK with your merchant credentials (never changes per session)
- create_payment_request(): Generates a unique payment request. The order_id is crucial—it links this transaction to your internal system (e.g., user ID, course ID)
- checkout_url: This is the QR code URL you display to the user
- request_id: Store this—you'll poll for settlement status using it
Awaiting Settlement Confirmation
import asyncio
import time
async def wait_for_payment(request_id, timeout_seconds=300):
"""Poll for payment settlement (up to 5 minutes)"""
start_time = time.time()
while time.time() - start_time < timeout_seconds:
# Check settlement status
status = client.await_settlement(
request_id=request_id,
poll_interval=5 # Check every 5 seconds
)
if status['settled']:
print(f"✓ Payment confirmed! Amount: {status['amount']} VND")
print(f"Settlement ID: {status['settlement_id']}")
return status
print(f"Waiting for payment... ({int(time.time() - start_time)}s elapsed)")
await asyncio.sleep(5)
raise TimeoutError("Payment not received within 5 minutes")
# Usage in your MCP tool:
result = await wait_for_payment("req_abc123xyz")
if result['settled']:
# Unlock course access, generate credentials, etc.
unlock_course_for_user(user_id="user123")
Key points:
- await_settlement() polls the bank feed (not AgentPay's database—directly from your bank)
- poll_interval=5 means we check every 5 seconds; adjust based on your UX needs
- Settlement is final and irreversible once confirmed
- The timeout prevents infinite loops; adjust based on your use case
Integrating with Your MCP Server
Here's how to expose AgentPay as a Claude tool:
from typing import Any
from mcp.server.models import Tool
from mcp.types import TextContent
# Define the MCP tool
def create_payment_tool() -> Tool:
return Tool(
name="create_vietqr_payment",
description="Generate a VietQR payment request for user charges",
inputSchema={
"type": "object",
"properties": {
"amount": {
"type": "integer",
"description": "Amount in VND (e.g., 499000)"
},
"description": {
"type": "string",
"description": "Payment description shown to user"
},
"order_id": {
"type": "string",
"description": "Unique order ID (e.g., 'course_001_user123')"
}
},
"required": ["amount", "description", "order_id"]
}
)
# Handle the tool call
async def handle_create_payment(amount: int, description: str, order_id: str) -> str:
payment = client.create_payment_request(
amount=amount,
description=description,
order_id=order_id
)
return f"Payment link: {payment['checkout_url']}\nRequest ID: {payment['request_id']}"
Now Claude can call this tool naturally:
Claude: "I'll create a payment request for 499,000 VND for your course access."
[Calling create_vietqr_payment with amount=499000, description="Premium Course Access", order_id="course_001_user456"]
Advanced Tips: Production Best Practices
1. Store Request IDs in Your Database
Map request_id to user/order records so you can reconcile settlements:
import uuid
from datetime import datetime
# When creating a payment:
order_id = f"course_{course_id}_{user_id}_{int(datetime.now().timestamp())}"
payment = client.create_payment_request(
amount=499000,
description="Course Access",
order_id=order_id
)
# Save to your DB:
db.insert('pending_payments', {
'request_id': payment['request_id'],
'order_id': order_id,
'user_id': user_id,
'amount': 499000,
'created_at': datetime.now(),
'status': 'pending'
})
2. Implement Webhook Listeners (Optional)
Instead of polling, listen for bank-triggered webhooks (if your bank supports it):
# Your FastAPI/Flask endpoint
@app.post("/webhooks/settlement")
async def handle_settlement_webhook(payload: dict):
request_id = payload.get('request_id')
amount = payload.get('amount')
# Mark as settled in your DB
db.update('pending_payments',
{'request_id': request_id},
{'status': 'settled', 'settled_at': datetime.now()})
# Unlock access immediately
unlock_course_for_order(request_id)
return {"status": "ok"}
3. Set Reasonable Timeouts
VietQR users have varying response times. A 5-minute timeout is standard, but for high-friction scenarios (enterprise purchases), extend to 15 minutes.
4. Display the QR Code Correctly
AgentPay returns a QR URL. Render it as an image in Claude:
from PIL import Image
import qrcode
# If you need to generate the QR locally:
qr = qrcode.QRCode(version=1, box_size=10, border=4)
qr.add_data(payment['checkout_url'])
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
img.save("payment_qr.png")
Do's and Don'ts
| Do ✓ | Don't ✗ |
|---|---|
Store request_id for reconciliation |
Assume settlement happened without confirmation |
| Set a reasonable timeout (5-15 min) | Wait indefinitely for payment |
| Include order_id in every request | Reuse request_ids across different transactions |
| Test with small amounts first | Go live with production amounts immediately |
| Log all payment events | Ignore failed/expired requests |
| Use unique order_id per transaction | Create multiple requests for same purchase |
FAQ
Q: Does AgentPay hold my money? No. The QR code points directly to your bank account. AgentPay is purely a request/settlement bridge—never a custodian.
Q: What if the user doesn't pay within 5 minutes? The request expires. Generate a new one. You can offer a "resend payment link" option in Claude.
Q: Can I use this outside Vietnam? VietQR is Vietnam-specific. AgentPay VN is optimized for Vietnamese merchants and banking infrastructure. For international payments, you'd need a different provider (Stripe, etc.).
Q: What banks support VietQR? Most Vietnamese banks do (Techcombank, VietcomBank, BIDV, Vietinbank, etc.). Check your bank's documentation for merchant QR setup.
Q: Is there a transaction fee? Your bank determines fees (typically 0.5–1.5% per transaction). AgentPay itself is free and open-source.
Key Takeaways
- AgentPay VN solves AI agent monetization by embedding VietQR payments directly into Claude conversations
- The 3-step flow (create → send → settle) is simple: one function call, one QR display, one confirmation check
- No money ever touches AgentPay; payments go straight to your bank account
- Production-ready: Use request_id storage, timeouts, and optional webhooks for robust handling
- Best for Vietnam-based services: Premium courses, consulting bots, analysis tools, reservation systems
- MIT-licensed and open-source: Full transparency, run on your own infrastructure
Getting Started Now
You're 3 minutes away from your first payment integration:
pip install agentpay-vn
Then visit the full documentation for detailed API reference, webhook examples, and troubleshooting.
Explore the code and contribute on GitHub.
Start building agents that earn their keep. Your users are ready to pay for real value—AgentPay makes it frictionless.