Library · Launch: payments, secrets, rules, logs

Payments & Billing: Stripe MCP, subscriptions, webhooks

Builder70 minUpdated: October 2026
84 of 105 in the library

Time: ~25 min theory + 45 min practice


The gist

Stripe isn't just "accept a payment." It's the operating system for the money in your AI business. And Stripe MCP turns Claude into a finance operator: it creates products, reads transactions and manages subscriptions through plain text, without opening the dashboard.

🎨 Picture this: Money used to move through a bank teller. You had to show up in person, fill out forms, wait. Stripe MCP is like having the bank's API right inside Claude's head. You say "create a payment link for $49/month," and it does it instantly.


Key concepts

  • Stripe MCP: Stripe's official tool for managing your account through Claude
  • Payment Link: a link with no website needed. You send it to a customer, they pay
  • Subscription: a recurring payment (the backbone of SaaS revenue)
  • Webhook: Stripe knocks on your server's door when something happens
  • Dunning: automatically handling failed or overdue payments
  • MRR/ARR: the health metrics of a subscription business

Theory

Why Stripe and not the alternatives

Here's how choosing a payment service looks for an indie developer (fees differ by service and country, so check the current rates on their websites):

Service Type Fee What stands out
Stripe Payment processor As of October 2026 in the US: 2.9% + 30¢ per card charge, plus 1.5% for international cards and 1% for currency conversion. Other countries have their own rates Official MCP; taxes and reporting are on you
Paddle Merchant of Record See their site The service acts as the seller and handles sales tax itself, so the fee is usually higher
Lemon Squeezy Merchant of Record See their site Same model as Paddle
PayPal Payment service See their site Familiar to most buyers

Stripe, not because it's "the best" in a vacuum, but because it has an official MCP, good documentation, and every developer in the world knows its API.

Important: Stripe doesn't work in every country. Before you start, check on Stripe's website whether your country is supported. If it isn't, look at the payment services available where you are.

Stripe MCP: what it is and why it matters

Stripe MCP is Stripe's official server that plugs the Stripe API straight into Claude Code. As of October 2026 it's a remote server at https://mcp.stripe.com, with sign-in through OAuth. Once it's connected, you can run your business through text.

bash
# Connect Stripe MCP (remote server)
claude mcp add --transport http stripe https://mcp.stripe.com/

# Then in Claude Code open /mcp and complete the Stripe authorization (OAuth)

Stripe also offers a one-command setup: npm install -g @stripe/cli@latest, then stripe agent setup. It connects the MCP and the skills for your agent on its own.

After connecting, Claude gets these tools:

  • stripe_api_search and stripe_api_details: find a Stripe API method and see its parameters
  • stripe_api_read: read data such as customers, subscriptions, invoices, balance
  • stripe_api_write: create and change things such as products, prices, payment links, subscriptions
  • get_stripe_account_info: account details
  • stripe_analytics: metrics and reports (in preview as of October 2026)
  • search_stripe_documentation: search Stripe's documentation

The list of tools can change, so check the Stripe MCP documentation for the current one. For risky actions, such as refunds, Stripe asks for confirmation through a link, which keeps a human in the loop. Start in test mode (sandbox) and confirm anything involving money yourself.

If you connect the MCP with a key instead of OAuth: starting 10/31/2026, Stripe MCP no longer accepts regular secret or restricted keys without the Agent label. You need an Agent-type key (agent API key) or OAuth.

🎨 Picture this: Managing Stripe through the web dashboard used to be like driving a car while looking out through a periscope. MCP is like finally sitting properly behind the wheel.

Basic operations through MCP

Create a product and a price:

Type this into the chat
You: Create a product "AI Content Tool" at $49 a month, subscription mode

Claude [stripe_api_write: create product]: {
  "name": "AI Content Tool",
  "description": "Monthly AI content generation subscription"
}

Claude [stripe_api_write: create price]: {
  "product": "prod_xxx",
  "unit_amount": 4900,
  "currency": "usd",
  "recurring": {"interval": "month"}
}

Create a payment link:

Type this into the chat
You: Create a payment link for this price

Claude [stripe_api_write: create payment link]: {
  "line_items": [{"price": "price_xxx", "quantity": 1}]
}

Result: https://buy.stripe.com/test_XXXXX

You send this link to a customer by text or email. They pay, the money goes to you. No website, no shopping cart.

Look at your customers:

Type this into the chat
You: Show me the last 10 customers and their subscription status

Claude [stripe_api_read: list customers]: {"limit": 10}
Claude [stripe_api_read: customer subscriptions]: {for each one}

Result: a table with names, email, status, amount

Webhooks: Stripe tells you what's happening

A webhook is an HTTP request that Stripe sends to your server when something happens. There are dozens of events; the most important ones:

Event What it means Your action
payment_intent.succeeded The payment went through Activate the customer's account
customer.subscription.deleted Subscription canceled Cut off access
invoice.payment_failed The payment failed Start dunning
customer.subscription.updated Upgrade/downgrade Change the plan

A basic webhook handler:

python
import os
import stripe
from flask import Flask, request

app = Flask(__name__)
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
endpoint_secret = os.environ["STRIPE_WEBHOOK_SECRET"]

@app.route('/webhook', methods=['POST'])
def webhook():
    payload = request.data
    sig_header = request.headers.get('Stripe-Signature')
    
    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except ValueError:
        return 'Invalid payload', 400
    except stripe.SignatureVerificationError:
        return 'Invalid signature', 400
    
    if event['type'] == 'payment_intent.succeeded':
        payment_intent = event['data']['object']
        # Activate the customer
        activate_customer(payment_intent['customer'])
    
    elif event['type'] == 'invoice.payment_failed':
        invoice = event['data']['object']
        # Start the dunning sequence
        start_dunning(invoice['customer'])
    
    return '', 200

🎨 Picture this: A webhook is like a doorbell. You don't stand at the door 24/7 asking "is anyone there?" Stripe rings when it needs to.

The subscription model: how Claude helps build SaaS billing

How a subscription SaaS works with Claude:

Code
Customer → Payment Link → Stripe webhook → your server
                                              ↓
                                    Claude analyzes the event
                                              ↓
                              creates an account / activates / blocks
                                              ↓
                                    sends a welcome email

Claude can handle these events intelligently. For example, when a subscription is canceled:

python
def handle_subscription_cancelled(customer_id):
    # Claude analyzes the customer's history
    client = anthropic.Anthropic()
    
    customer_history = get_customer_history(customer_id)
    
    response = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""
            A customer canceled their subscription. History:
            - Tenure: {customer_history['months']} months
            - Usage: {customer_history['usage']} requests/month
            - Last login: {customer_history['last_login']}
            
            Write a personal win-back email to send in 7 days.
            Tone: friendly, no pressure.
            """
        }]
    ).content if b.type == "text")
    
    schedule_email(customer_id, response, delay_days=7)

Dunning: failed payments are money that's already yours

Dunning is the process of dealing with failed payments. Cards get blocked, expire, run out of funds. Without dunning, part of your revenue disappears simply because nobody reminded the customer. Stripe already has built-in retry attempts and failed-payment emails: turn those on first, and bring in Claude for a personal tone.

Smart dunning with Claude:

python
def smart_dunning_email(customer_id, attempt_number):
    """
    Attempt 1 (right away): gentle reminder
    Attempt 2 (day 3): a bit firmer  
    Attempt 3 (day 7): final notice
    """
    customer = get_customer(customer_id)
    
    tone_map = {
        1: "friendly, they probably just forgot to update their card",
        2: "a bit more serious, offer help",
        3: "clear, explain what happens if they don't pay"
    }
    
    client = anthropic.Anthropic()
    email = "".join(b.text for b in client.messages.create(
        model="claude-haiku-4-5",  # Haiku is enough for an email
        max_tokens=300,
        messages=[{
            "role": "user",
            "content": f"""
            Attempt {attempt_number}/3. Tone: {tone_map[attempt_number]}
            Customer: {customer['name']}, product: {customer['product']}
            Write a short email about a failed payment.
            Include a direct link to update the card.
            """
        }]
    ).content if b.type == "text")
    
    send_email(customer['email'], email)

MRR/ARR analytics with Claude + Stripe data

python
def monthly_revenue_report():
    # Pull data from Stripe
    subscriptions = stripe.Subscription.list(status='active', limit=100)
    
    # Divide yearly plans by 12 to get the monthly amount
    mrr = sum(
        item['price']['unit_amount'] * item.get('quantity', 1) / 100
        for sub in subscriptions.data
        for item in sub['items']['data']
    )
    
    churned_last_month = get_churned_count()
    new_last_month = get_new_customers_count()
    
    # Claude does the analysis
    client = anthropic.Anthropic()
    report = "".join(b.text for b in client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=800,
        messages=[{
            "role": "user",
            "content": f"""
            Data for the month:
            - MRR: ${mrr:.2f}
            - ARR (projected): ${mrr * 12:.2f}
            - New customers: {new_last_month}
            - Canceled: {churned_last_month}
            - Churn rate: {churned_last_month / (len(subscriptions.data)) * 100:.1f}%
            
            Give a short analysis (5-7 sentences) and 2-3 specific actions.
            """
        }]
    ).content if b.type == "text")
    
    return report

Practice

Step 1: Get a Stripe API key

  1. Sign up at stripe.com
  2. Dashboard → Developers → API keys (Stripe renames its menu items from time to time; work in test mode)
  3. Copy the Publishable key (pk_test_...) and the Secret key (sk_test_...). Stripe MCP itself doesn't need the keys, since it signs in through OAuth: you'll need the keys for the webhook script
  4. Save them in .env:
    Code
    STRIPE_SECRET_KEY=sk_test_xxx
    STRIPE_PUBLISHABLE_KEY=pk_test_xxx

Step 2: Connect Stripe MCP

bash
# Connect Stripe MCP (remote server, sign-in through OAuth)
claude mcp add --transport http stripe https://mcp.stripe.com/

# Check the connection and complete authorization
claude
> /mcp  # should show stripe in the list

Step 3: Create your first product through Claude

In Claude Code, type:

Type this into the chat
Using Stripe MCP, create:
1. A product "My AI Service" with a description in English
2. A price of $29 a month (recurring, monthly)
3. A payment link for this price
Show me the final link.

Step 4: Test a payment

  1. Open the payment link in your browser
  2. Use Stripe's test card: 4242 4242 4242 4242
  3. Any future date, any CVC
  4. Check in Stripe Dashboard → Payments

Step 5: Run a local webhook (optional)

bash
# Install the Stripe CLI
brew install stripe/stripe-cli/stripe

# Listen for events locally
stripe listen --forward-to localhost:5000/webhook

# In another terminal, simulate a payment
stripe trigger payment_intent.succeeded

Step 6: Practice assignment

Using Claude, create:

  • A product with 3 plans: Basic ($9), Pro ($29), Agency ($99)
  • A payment link for each plan
  • A simple script that lists all active subscriptions
  • An email template for a failed payment (dunning attempt 1)

Tools and resources

  • Stripe MCP: https://mcp.stripe.com (sign-in through OAuth), details at docs.stripe.com/mcp
  • Stripe CLI: brew install stripe/stripe-cli/stripe
  • Stripe Docs: docs.stripe.com
  • Stripe test cards: docs.stripe.com/testing
  • Webhooks: docs.stripe.com/webhooks
  • Stripe Python SDK: pip install stripe

Key takeaways

Stripe MCP turns Claude into your billing manager. Creating products, managing subscriptions, dunning, all through text. Webhooks are the system's ears: Stripe tells you what's happening, and Claude decides what to do about it. A payment link with no website is the minimum viable billing for your first customer. Start there and add complexity as you grow. Confirm anything involving money yourself.


Next lesson

→ MLOps for indie builders: monitoring, drift, retraining without a DevOps team

The mark stays in this browser only and is never sent anywhere. My progress