Build a Paid MCP Server: Charge Users Inside Claude

2026-08-10 · AgentPay VN

mcp-serverai-agentspayment-integrationvietqrpython-sdk

The Problem: AI Agents That Can't Earn

Imagine you've built a Claude-powered tutoring bot that genuinely helps students prepare for exams. It's brilliant—personalized explanations, adaptive difficulty, real-time feedback. But there's a problem: you can't charge anyone.

Your agent runs inside Claude's MCP (Model Context Protocol) framework. Users love it. Yet every response it gives is free labor. No Stripe webhook magic, no PayPal integration—those were built for websites and apps, not for AI agents operating inside a conversation.

This is the gap AgentPay VN fills. In the next 15 minutes, you'll learn to turn your Claude agent into a revenue-generating service that charges users directly from their Vietnamese bank accounts—no middleman, no money held by your platform, just instant settlement straight to your merchant account.

Why AgentPay VN Is Different: No Middle Layer

Traditional payment processors were designed for businesses, not agents. They hold your money, take days to settle, require hosting. AgentPay VN inverts this:

For a Vietnamese business, this is decisive. You get VietQR compliance out of the box, and payments clear to your Viet bank account immediately.

The Three-Line Flow

Before we code, understand the heartbeat:

create_payment_request → send checkout_url → await_settlement

That's it. Your agent creates a request, gives the user a VietQR URL, then pauses until the bank confirms the user paid. Once confirmed, unlock the feature.

Setting Up AgentPay VN

Installation

pip install agentpay-vn

That's all. No account signup, no API keys (yet). The SDK works with your own bank details.

Your First Payment Request

Here's a complete, annotated example:

from agentpay_vn import PaymentProcessor
import asyncio

# Initialize with your merchant bank account
processor = PaymentProcessor(
    merchant_account_number="0123456789",
    merchant_bank_code="970403",  # Example: MB Bank
    merchant_name="My Tutoring Bot"
)

async def charge_for_exam_prep():
    # Step 1: Create a payment request for 50,000 VND (exam prep package)
    payment_request = await processor.create_payment_request(
        amount_vnd=50000,
        description="Premium Exam Prep: Full Math & Physics Package",
        user_id="student_123",
        order_id="order_2024_001"
    )

    # payment_request now contains:
    # - checkout_url: The VietQR link the student scans
    # - request_id: Unique ID to track this payment
    # - qr_code: Raw QR image data (optional)

    print(f"Share this URL: {payment_request.checkout_url}")
    print(f"Waiting for payment confirmation...")

    # Step 2: Wait for the bank to confirm settlement
    settlement = await processor.await_settlement(
        request_id=payment_request.request_id,
        timeout_seconds=600  # 10-minute window; user must pay within this time
    )

    if settlement.confirmed:
        print(f"✓ Received {settlement.amount_vnd} VND in {settlement.settlement_time}")
        # Unlock premium content
        return {"status": "paid", "features": ["advanced_problems", "live_explanations"]}
    else:
        print("✗ Payment timed out or failed.")
        return {"status": "unpaid", "features": []}

# Run it
result = asyncio.run(charge_for_exam_prep())

Line-by-line breakdown:

Integrating with Claude via MCP

Now the real power: embed this payment flow directly into a Claude MCP server. Claude calls your tools, your tools can request payment, and the conversation pauses until the user pays.

MCP Server Configuration

Create mcp_server.py:

from mcp.server import Server
from mcp.types import Tool, TextContent
from agentpay_vn import PaymentProcessor
import json
import asyncio

server = Server("paid-tutoring-bot")
processor = PaymentProcessor(
    merchant_account_number="0123456789",
    merchant_bank_code="970403",
    merchant_name="My Tutoring Bot"
)

@server.call_tool()
async def request_premium_explanation(user_id: str, topic: str, amount_vnd: int = 25000):
    """
    User asks for a deep dive on a topic.
    If they haven't paid, request payment.
    If they have, unlock the explanation.
    """
    # Check if user has an active subscription/credit
    is_paid = await processor.check_user_balance(user_id)

    if not is_paid:
        # Initiate payment
        payment = await processor.create_payment_request(
            amount_vnd=amount_vnd,
            description=f"Premium explanation: {topic}",
            user_id=user_id,
            order_id=f"exp_{user_id}_{topic}"
        )
        return TextContent(
            text=f"This premium explanation costs {amount_vnd:,} VND. "
                 f"Scan this QR to pay: {payment.checkout_url}\n"
                 f"Once you pay, I'll give you the full breakdown."
        )
    else:
        # User is paid; serve the premium content
        explanation = generate_deep_explanation(topic)
        return TextContent(text=explanation)

if __name__ == "__main__":
    server.run()

Claude's Claude Settings (claude_config.json)

Tell Claude about your paid service:

{
  "name": "paid-tutoring-bot",
  "description": "Premium tutoring with AI-powered explanations and payment via VietQR",
  "mcp_servers": [
    {
      "name": "tutoring-server",
      "url": "sse://localhost:3000",
      "tools": [
        {
          "name": "request_premium_explanation",
          "description": "Unlock deep-dive explanations on any topic for 25,000 VND",
          "input_schema": {
            "type": "object",
            "properties": {
              "user_id": {"type": "string"},
              "topic": {"type": "string"},
              "amount_vnd": {"type": "integer", "default": 25000}
            },
            "required": ["user_id", "topic"]
          }
        }
      ]
    }
  ]
}

Now when a user chats with Claude and asks for premium content, Claude automatically calls request_premium_explanation, which presents the VietQR link. Once the user pays, the agent unlocks the goods.

Real-World Walkthrough: An Online Café Shop

Let's ground this in reality. Imagine Cà Phê Thông Minh (Smart Café), a Vietnamese coffee shop offering a Claude-powered ordering bot.

Scenario: A customer asks: "I want your special pour-over latte and a croissant, but suggest the best pairing."

  1. Claude responds: "I'd recommend our Ethiopia Yirgacheffe single-origin with our almond croissant—the brightness complements the nuttiness. Your order is 185,000 VND." Claude calls create_payment_request.

  2. Bot sends VietQR: The customer sees a QR code in the chat. They scan it with their banking app and pay 185,000 VND from their Vietcombank account.

  3. Instant confirmation: AgentPay's bank feed detects the payment within 3 seconds. Your MCP server's await_settlement resolves.

  4. Bot confirms: "Payment received! Your order is confirmed. Pick up at counter 3 in 12 minutes." The café's POS system is already printing the receipt.

  5. Zero friction: No separate checkout page, no third-party app, no fees beyond the standard NAPAS fee (0.5–1%). The money lands in Cà Phê Thông Minh's bank account instantly.

This works because: - The café owns the bank account—no middleman. - VietQR is ubiquitous in Vietnam; every customer recognizes it. - The agent pauses mid-conversation while awaiting payment, creating natural UX. - Settlement is instant, so the kitchen starts working immediately.

Advanced: Subscriptions and Refunds

Once you've mastered basic payments, consider:

Recurring Payments (Monthly Subscription)

async def enable_monthly_subscription(user_id: str, plan: str = "pro"):
    """
    Charge a user monthly (e.g., 199,000 VND for Premium access).
    In production, store the recurring_id in your DB and check monthly.
    """
    pricing = {"basic": 99000, "pro": 199000, "elite": 399000}
    amount = pricing.get(plan, 199000)

    payment = await processor.create_payment_request(
        amount_vnd=amount,
        description=f"Monthly subscription: {plan.upper()}",
        user_id=user_id,
        order_id=f"sub_{user_id}_{int(time.time())}",
        recurring=True  # Mark as recurring
    )

    # In a production system, store payment.recurring_id
    # and schedule a monthly charge via a cron job
    return payment

Handling Refunds

If a user requests a refund (e.g., they're unsatisfied), use:

async def refund_payment(request_id: str, reason: str):
    """
    Refund a payment. AgentPay handles the bank transfer reversal.
    """
    refund = await processor.refund_payment(
        original_request_id=request_id,
        reason=reason
    )
    return refund.status  # "refunded" or "pending"

Do's and Don'ts

Do Don't
Await settlement before unlocking premium features Trust user-submitted confirmation; always verify via bank feed
Set a reasonable timeout (e.g., 10 minutes) so users aren't stuck Leave requests open indefinitely; timeouts prevent zombie processes
Log all payment requests and settlements for accounting Lose track of who paid; use order_id and user_id consistently
Test with small amounts (1,000–10,000 VND) before going live Jump to 10,000,000 VND charges without testing
Display the checkout URL clearly; VietQR works on all banking apps Embed the QR in an image only; provide the clickable link too

Troubleshooting & FAQ

Q: What if the user's bank doesn't support VietQR?

A: VietQR is standard across 77 Vietnamese banks (Vietcombank, MB Bank, Techcombank, ACB, etc.). If a user's bank isn't supported, they can still transfer manually using the account number—but VietQR is the frictionless path.

Q: How long does settlement take?

A: Once the user confirms the payment in their banking app, the bank feed notifies AgentPay within 3–10 seconds. Your agent's await_settlement resolves immediately, so the user gets feedback in near-real-time. The money itself clears to your account within 24 hours (standard Vietnam banking).

Q: What's the fee?

A: AgentPay VN doesn't charge a fee. You pay the standard NAPAS interchange fee (0.5–1%), which is the same whether you use Stripe, PayPal, or any processor. Your bank handles this automatically.

Q: Can I offer a free tier alongside paid features?

A: Absolutely. In your MCP server, check if the user has already paid for a feature. If not, offer a limited free response, then prompt for payment if they want the premium version. You control the paywall logic entirely.

Q: What if payment fails halfway through?

A: await_settlement will timeout if no payment is detected. Your agent catches the timeout and can retry, inform the user, or offer an alternative payment method. The request stays open; there's no zombie transaction.

Key Takeaways

Getting Started Now

You have everything you need:

  1. Install the SDK: pip install agentpay-vn
  2. Read the full docs: https://agentpay.servicesai.vn/v1/docs
  3. Explore the source: https://github.com/phuocdu/agentpay-vn
  4. Build your MCP server: Use the examples above to integrate into your Claude bot.
  5. Test locally: Start with 1,000 VND transactions to verify the flow.

Your Claude agent can now earn money. Not through ads, not through upselling—through genuine value delivered at the moment of use, paid directly by users who want what you're offering. That's the future of agent economics. Go build it.

Get started →

← All posts