Comprehensive Stripe integration agent for payments, subscriptions, billing, and marketplace management. Use when Claude needs to work with Stripe API for creating customers, managing subscriptions, processing payments, handling checkout sessions, setting up products/prices, managing webhooks, Connect marketplaces, metered billing, tax calculation, fraud prevention, or any payment-related task. Triggers on mentions of Stripe, payments, subscriptions, billing, checkout, invoices, payment intents, recurring payments, Connect, marketplace, SCA, 3D Secure, or disputes.
/plugin marketplace add acaprino/alfio-claude-plugins/plugin install stripe@alfio-claude-pluginsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
LICENSE.txtreferences/api-cheatsheet.mdreferences/firebase-integration.mdscripts/setup_products.pyscripts/stripe_utils.pyscripts/sync_subscriptions.pyscripts/webhook_handler.pyThis skill enables Claude to interact with Stripe's API for complete payment and subscription management.
Ensure STRIPE_SECRET_KEY environment variable is set. For webhook handling, also set STRIPE_WEBHOOK_SECRET.
export STRIPE_SECRET_KEY="sk_test_..."
export STRIPE_WEBHOOK_SECRET="whsec_..."
Install the Stripe SDK:
pip install stripe --break-system-packages
Create and manage customers before any payment operation.
import stripe
import os
stripe.api_key = os.environ.get("STRIPE_SECRET_KEY")
# Create customer
customer = stripe.Customer.create(
email="user@example.com",
name="John Doe",
metadata={"user_id": "your_app_user_id"}
)
# Retrieve customer
customer = stripe.Customer.retrieve("cus_xxx")
# Update customer
stripe.Customer.modify("cus_xxx", metadata={"plan": "premium"})
# List customers
customers = stripe.Customer.list(limit=10, email="user@example.com")
Always create Products first, then attach Prices. Use lookup_key for easy price retrieval.
# Create product
product = stripe.Product.create(
name="Pro Plan",
description="Full access to all features",
metadata={"tier": "pro"}
)
# Create recurring price (subscription)
price = stripe.Price.create(
product=product.id,
unit_amount=1999, # Amount in cents (€19.99)
currency="eur",
recurring={"interval": "month"},
lookup_key="pro_monthly"
)
# Create one-time price
one_time_price = stripe.Price.create(
product=product.id,
unit_amount=9999,
currency="eur",
lookup_key="pro_lifetime"
)
# Retrieve price by lookup_key
prices = stripe.Price.list(lookup_keys=["pro_monthly"])
Use Checkout Sessions for secure, hosted payment pages.
# Subscription checkout
session = stripe.checkout.Session.create(
customer="cus_xxx", # Optional: attach to existing customer
mode="subscription",
line_items=[{
"price": "price_xxx",
"quantity": 1
}],
success_url="https://yourapp.com/success?session_id={CHECKOUT_SESSION_ID}",
cancel_url="https://yourapp.com/cancel",
metadata={"user_id": "123"}
)
# Redirect user to: session.url
# One-time payment checkout
session = stripe.checkout.Session.create(
mode="payment",
line_items=[{"price": "price_xxx", "quantity": 1}],
success_url="https://yourapp.com/success",
cancel_url="https://yourapp.com/cancel"
)
# Create subscription directly (when you have payment method)
subscription = stripe.Subscription.create(
customer="cus_xxx",
items=[{"price": "price_xxx"}],
payment_behavior="default_incomplete",
expand=["latest_invoice.payment_intent"]
)
# Retrieve subscription
sub = stripe.Subscription.retrieve("sub_xxx")
# Update subscription (change plan)
stripe.Subscription.modify(
"sub_xxx",
items=[{
"id": sub["items"]["data"][0].id,
"price": "price_new_xxx"
}],
proration_behavior="create_prorations"
)
# Cancel subscription
stripe.Subscription.cancel("sub_xxx") # Immediate
# Or cancel at period end:
stripe.Subscription.modify("sub_xxx", cancel_at_period_end=True)
Use when you need full control over the payment flow.
# Create payment intent
intent = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
customer="cus_xxx",
metadata={"order_id": "order_123"}
)
# Return intent.client_secret to frontend
# Confirm payment (server-side)
stripe.PaymentIntent.confirm(
"pi_xxx",
payment_method="pm_xxx"
)
Critical for subscription lifecycle. See scripts/webhook_handler.py for complete implementation.
Key events to handle:
checkout.session.completed - Payment successfulcustomer.subscription.created - New subscriptioncustomer.subscription.updated - Plan changescustomer.subscription.deleted - Cancellationinvoice.paid - Successful renewalinvoice.payment_failed - Failed paymentimport stripe
def handle_webhook(payload, sig_header):
endpoint_secret = os.environ.get("STRIPE_WEBHOOK_SECRET")
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
if event["type"] == "checkout.session.completed":
session = event["data"]["object"]
# Fulfill order, activate subscription
elif event["type"] == "invoice.payment_failed":
invoice = event["data"]["object"]
# Notify user, handle dunning
return {"status": "success"}
For Firebase + Stripe integration, see references/firebase-integration.md.
Quick setup:
| Task | Method |
|---|---|
| Create customer | stripe.Customer.create() |
| Start subscription | stripe.checkout.Session.create(mode="subscription") |
| Cancel subscription | stripe.Subscription.cancel() |
| Change plan | stripe.Subscription.modify() |
| Refund payment | stripe.Refund.create(payment_intent="pi_xxx") |
| Get invoices | stripe.Invoice.list(customer="cus_xxx") |
| Create portal session | stripe.billing_portal.Session.create() |
Let customers manage their own subscriptions:
portal_session = stripe.billing_portal.Session.create(
customer="cus_xxx",
return_url="https://yourapp.com/account"
)
# Redirect to: portal_session.url
Use test mode keys (sk_test_...) and test card numbers:
4242424242424242 - Successful payment4000000000000002 - Declined4000002500003155 - Requires 3D Securetry:
# Stripe operation
except stripe.error.CardError as e:
# Card declined
print(f"Card error: {e.user_message}")
except stripe.error.InvalidRequestError as e:
# Invalid parameters
print(f"Invalid request: {e}")
except stripe.error.AuthenticationError:
# Invalid API key
pass
except stripe.error.StripeError as e:
# Generic Stripe error
pass
Create shareable payment links without code:
# Create a payment link
payment_link = stripe.PaymentLink.create(
line_items=[{"price": "price_xxx", "quantity": 1}],
after_completion={"type": "redirect", "redirect": {"url": "https://yourapp.com/thanks"}}
)
# Share: payment_link.url
# Create reusable link with adjustable quantity
payment_link = stripe.PaymentLink.create(
line_items=[{"price": "price_xxx", "adjustable_quantity": {"enabled": True, "minimum": 1, "maximum": 10}}]
)
For API calls, seats, or consumption-based pricing:
# Create metered price
metered_price = stripe.Price.create(
product="prod_xxx",
currency="eur",
recurring={"interval": "month", "usage_type": "metered"},
billing_scheme="per_unit",
unit_amount=10, # €0.10 per unit
lookup_key="api_calls"
)
# Report usage (do this periodically)
stripe.SubscriptionItem.create_usage_record(
"si_xxx", # subscription item id
quantity=150,
timestamp=int(datetime.now().timestamp()),
action="increment" # or "set" to override
)
# Tiered pricing
tiered_price = stripe.Price.create(
product="prod_xxx",
currency="eur",
recurring={"interval": "month", "usage_type": "metered"},
billing_scheme="tiered",
tiers_mode="graduated", # or "volume"
tiers=[
{"up_to": 100, "unit_amount": 50}, # First 100: €0.50 each
{"up_to": 1000, "unit_amount": 30}, # 101-1000: €0.30 each
{"up_to": "inf", "unit_amount": 10} # 1001+: €0.10 each
]
)
Build platforms where you facilitate payments between buyers and sellers:
# Create connected account (Express - recommended)
account = stripe.Account.create(
type="express",
country="US",
email="seller@example.com",
capabilities={"card_payments": {"requested": True}, "transfers": {"requested": True}}
)
# Generate onboarding link
account_link = stripe.AccountLink.create(
account=account.id,
refresh_url="https://yourapp.com/reauth",
return_url="https://yourapp.com/return",
type="account_onboarding"
)
# Redirect seller to: account_link.url
# Create payment with platform fee (destination charge)
payment_intent = stripe.PaymentIntent.create(
amount=10000,
currency="eur",
application_fee_amount=1000, # Platform takes €10
transfer_data={"destination": "acct_xxx"} # Seller receives €90
)
# Direct charge (charge on connected account)
payment_intent = stripe.PaymentIntent.create(
amount=10000,
currency="eur",
stripe_account="acct_xxx", # Charge on seller's account
application_fee_amount=1000
)
# Transfer funds to connected account
transfer = stripe.Transfer.create(
amount=5000,
currency="eur",
destination="acct_xxx"
)
Automatic tax calculation and collection:
# Enable automatic tax in checkout
session = stripe.checkout.Session.create(
mode="payment",
line_items=[{"price": "price_xxx", "quantity": 1}],
automatic_tax={"enabled": True},
success_url="https://yourapp.com/success",
cancel_url="https://yourapp.com/cancel"
)
# Calculate tax for payment intent
payment_intent = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
automatic_payment_methods={"enabled": True},
# Tax calculated based on customer location
)
# Tax calculation API (preview)
calculation = stripe.tax.Calculation.create(
currency="eur",
line_items=[{"amount": 1000, "reference": "L1"}],
customer_details={"address": {"country": "DE"}, "address_source": "billing"}
)
Handle Strong Customer Authentication (required in EU/UK):
# Payment intent with 3DS when required
payment_intent = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
payment_method="pm_xxx",
confirmation_method="manual",
confirm=True,
return_url="https://yourapp.com/return" # For 3DS redirect
)
# Check if authentication required
if payment_intent.status == "requires_action":
# Redirect customer to: payment_intent.next_action.redirect_to_url.url
pass
# Force 3DS (for high-risk transactions)
payment_intent = stripe.PaymentIntent.create(
amount=50000,
currency="eur",
payment_method_options={
"card": {"request_three_d_secure": "any"} # or "automatic"
}
)
# Webhook: handle authentication
# Event: payment_intent.requires_action
Test cards for 3DS:
4000002500003155 - Requires authentication4000002760003184 - Always authenticates4000008260003178 - Authentication failsBuilt-in fraud protection with Radar:
# Payment with Radar rules
payment_intent = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
payment_method="pm_xxx",
# Radar evaluates automatically
)
# Check radar outcome after payment
charge = stripe.Charge.retrieve("ch_xxx")
radar_outcome = charge.outcome
# radar_outcome.risk_level: "normal", "elevated", "highest"
# radar_outcome.risk_score: 0-100
# Custom metadata for Radar rules
payment_intent = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
metadata={
"customer_account_age": "30", # days
"order_count": "5"
}
)
# Block high-risk in Radar Dashboard:
# Rule: "Block if :risk_level: = 'highest'"
# Rule: "Review if ::customer_account_age:: < 7"
Manage chargebacks and disputes:
# List disputes
disputes = stripe.Dispute.list(limit=10)
# Retrieve dispute details
dispute = stripe.Dispute.retrieve("dp_xxx")
# dispute.reason: "fraudulent", "duplicate", "product_not_received", etc.
# dispute.status: "needs_response", "under_review", "won", "lost"
# Submit evidence
stripe.Dispute.modify(
"dp_xxx",
evidence={
"customer_name": "John Doe",
"customer_email_address": "john@example.com",
"shipping_tracking_number": "1Z999AA10123456784",
"uncategorized_text": "Customer confirmed receipt via email on..."
},
submit=True # Submit evidence
)
# Webhook events for disputes
# charge.dispute.created - New dispute opened
# charge.dispute.updated - Evidence submitted or status changed
# charge.dispute.closed - Dispute resolved
Prevent duplicate operations:
import uuid
# Idempotent request (safe to retry)
payment_intent = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
idempotency_key=f"order_{order_id}" # Unique per operation
)
# For retries, use same key
try:
payment = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
idempotency_key="order_123"
)
except stripe.error.StripeError:
# Safe to retry with same idempotency_key
payment = stripe.PaymentIntent.create(
amount=2000,
currency="eur",
idempotency_key="order_123"
)
# Generate unique keys
def idempotency_key(prefix: str) -> str:
return f"{prefix}_{uuid.uuid4().hex}"
Best Practices:
requires_action status for 3DSsk_test_...)scripts/setup_products.py - Create products and pricesscripts/webhook_handler.py - Flask webhook endpointscripts/sync_subscriptions.py - Sync subscriptions to databasescripts/stripe_utils.py - Common utility functionsreferences/firebase-integration.md - Firebase + Firestore integrationreferences/api-cheatsheet.md - Quick API referenceCreating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.