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.
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.
# 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_searchandstripe_api_details: find a Stripe API method and see its parametersstripe_api_read: read data such as customers, subscriptions, invoices, balancestripe_api_write: create and change things such as products, prices, payment links, subscriptionsget_stripe_account_info: account detailsstripe_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.
Basic operations through MCP
Create a product and a price:
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:
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_XXXXXYou 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:
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, amountWebhooks: 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:
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 '', 200The subscription model: how Claude helps build SaaS billing
How a subscription SaaS works with Claude:
Customer → Payment Link → Stripe webhook → your server
↓
Claude analyzes the event
↓
creates an account / activates / blocks
↓
sends a welcome emailClaude can handle these events intelligently. For example, when a subscription is canceled:
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:
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
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 reportPractice
Step 1: Get a Stripe API key
- Sign up at stripe.com
- Dashboard → Developers → API keys (Stripe renames its menu items from time to time; work in test mode)
- 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
- Save them in
.env:Code STRIPE_SECRET_KEY=sk_test_xxx STRIPE_PUBLISHABLE_KEY=pk_test_xxx
Step 2: Connect Stripe MCP
# 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 listStep 3: Create your first product through Claude
In Claude Code, type:
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
- Open the payment link in your browser
- Use Stripe's test card:
4242 4242 4242 4242 - Any future date, any CVC
- Check in Stripe Dashboard → Payments
Step 5: Run a local webhook (optional)
# 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.succeededStep 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