Build a Paid MCP Server: Charge Users Inside Claude
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:
- Your MCP server invokes
create_payment_request()when a user wants a paid feature. - AgentPay generates a VietQR code pointing directly to your merchant bank account—not AgentPay's.
- User scans or transfers money using any Vietnamese banking app (BIDV, Techcombank, Vietcombank, etc.).
- Bank feed confirms settlement in seconds; your agent resumes the task.
- 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
- AgentPay VN is purpose-built for AI agents: It eliminates friction by letting users pay without leaving Claude.
- Three-line implementation:
create_payment_request()→ show QR →await_settlement()→ deliver service. - You own 100% of the money: VietQR points to your bank account; no middleman fees.
- Settlement is instant (2–60 seconds depending on bank), so agent handoff is near-immediate.
- Idempotency matters: Always use UUID-based
request_idto prevent accidental double-charges. - Integrate into your MCP server by exposing AgentPay's tools and letting Claude invoke them during conversations.
- Real-world monetization is now possible for course bots, café agents, design assistants, and any AI service in Vietnam.
Get Started Now
Ready to monetize your AI agent? Here's the fastest path:
-
Install the SDK:
bash pip install agentpay-vn -
Read the full documentation: https://agentpay.servicesai.vn/v1/docs
-
Explore the source code and examples: https://github.com/phuocdu/agentpay-vn
-
Start with a small test payment (5,000–10,000 VND) to verify your bank account integration.
-
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.