stripe-agent
npx skills add https://github.com/acaprino/alfio-claude-plugins --skill stripe-agent
Agent 安装分布
Skill 文档
Stripe Agent
This skill enables Claude to interact with Stripe’s API for complete payment and subscription management.
Prerequisites
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
Core Workflows
1. Customer Management
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")
2. Products and Prices
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"])
3. Checkout Sessions (Recommended for Web)
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"
)
4. Subscription Management
# 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)
5. Payment Intents (Custom Integration)
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"
)
6. Webhook Handling
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 payment
import 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"}
Firebase Integration Pattern
For Firebase + Stripe integration, see references/firebase-integration.md.
Quick setup:
- Store Stripe customer_id in Firestore user document
- Sync subscription status via webhooks to Firestore
- Use Firebase Security Rules to check subscription status
Common Operations Quick Reference
| 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() |
Customer Portal (Self-Service)
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
Testing
Use test mode keys (sk_test_...) and test card numbers:
4242424242424242– Successful payment4000000000000002– Declined4000002500003155– Requires 3D Secure
Error Handling
try:
# 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
Payment Links (No-Code Payments)
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}}]
)
Metered & Usage-Based Billing
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
]
)
Stripe Connect (Marketplaces)
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"
)
Tax Calculation (Stripe Tax)
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"}
)
3D Secure & SCA Compliance
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 fails
Fraud Prevention (Stripe Radar)
Built-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"
Dispute Handling
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
Idempotency & Best Practices
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:
- Always use idempotency keys for create/update operations
- Store payment intent ID before confirming
- Use webhooks as source of truth (not API responses)
- Handle
requires_actionstatus for 3DS - Never log full card numbers or CVV
- Use test mode for development (
sk_test_...)
Scripts Reference
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 functions
Additional Resources
references/firebase-integration.md– Firebase + Firestore integrationreferences/api-cheatsheet.md– Quick API reference