AgentSkillsCN

stripe-integration

在实施Stripe结账流程、订阅管理、Webhook处理,或支付处理功能时加载此模块。

SKILL.md
--- frontmatter
name: stripe-integration
description: Load when implementing Stripe checkout flows, subscription management, webhook handling, or payment processing.

When This Rule Applies

  • Implementing checkout or payment flows
  • Setting up subscription billing
  • Handling Stripe webhooks
  • Managing payment methods or refunds

Critical Decision: Payment Intents vs Checkout Sessions

Use Checkout Sessions when:

  • Standard checkout experience is acceptable
  • You want Stripe-hosted payment page
  • Faster time-to-market is priority

Use Payment Intents when:

  • Custom checkout UI required
  • Complex conditional logic during payment
  • Need fine-grained control over flow

⚠️ The Charges API is legacy. Always use Payment Intents for new integrations.

Quick Start

bash
pip install stripe  # Python
npm install stripe  # Node.js
python
import stripe
import os

stripe.api_key = os.environ["STRIPE_SECRET_KEY"]

# Create a Checkout Session
session = stripe.checkout.Session.create(
    mode="subscription",
    line_items=[{"price": "price_xxx", "quantity": 1}],
    success_url="https://example.com/success?session_id={CHECKOUT_SESSION_ID}",
    cancel_url="https://example.com/cancel",
)

Webhook Handling (CRITICAL)

40% of unprocessed payments come from missing/broken webhook handlers.

Essential Pattern

python
from fastapi import Request, HTTPException
import stripe

WEBHOOK_SECRET = os.environ["STRIPE_WEBHOOK_SECRET"]

@router.post("/webhooks/stripe")
async def stripe_webhook(request: Request):
    payload = await request.body()
    sig = request.headers.get("stripe-signature")
    
    # 1. ALWAYS verify signature
    try:
        event = stripe.Webhook.construct_event(payload, sig, WEBHOOK_SECRET)
    except stripe.error.SignatureVerificationError:
        raise HTTPException(400, "Invalid signature")
    
    # 2. Immediately acknowledge (< 5 seconds!)
    # Process async in background
    
    # 3. Handle idempotently (check event.id)
    if await event_already_processed(event.id):
        return {"received": True}
    
    # 4. Process event
    if event.type == "invoice.payment_succeeded":
        await handle_payment_success(event.data.object)
    elif event.type == "invoice.payment_failed":
        await handle_payment_failure(event.data.object)
    
    await mark_event_processed(event.id)
    return {"received": True}

Must-Handle Events

  • invoice.payment_succeeded - Provision access
  • invoice.payment_failed - Notify customer, schedule retry
  • customer.subscription.updated - Handle plan changes
  • customer.subscription.deleted - Revoke access

Common Gotchas

1. Test Keys in Production

python
# ALWAYS validate at startup
if os.environ.get("ENV") == "production":
    if os.environ["STRIPE_SECRET_KEY"].startswith("sk_test_"):
        raise Exception("Test key in production!")

2. Missing Idempotency Keys (causes duplicate charges!)

python
# ALWAYS use idempotency keys for mutations
import uuid

payment_intent = stripe.PaymentIntents.create(
    amount=2000,
    currency="usd",
    idempotency_key=str(uuid.uuid4()),  # Store this with order!
)

3. 5-Second Webhook Timeout

python
# BAD: Sync processing
@router.post("/webhook")
async def webhook(request: Request):
    # ... verify ...
    await slow_database_operation()  # ❌ May timeout!
    await send_email()  # ❌ May timeout!
    return {"received": True}

# GOOD: Async processing
@router.post("/webhook")
async def webhook(request: Request):
    # ... verify ...
    background_tasks.add_task(process_event, event)  # ✅
    return {"received": True}  # Respond immediately

4. 3D Secure Authentication

When PaymentIntent.status == "requires_action":

  1. Return client_secret to frontend
  2. Call stripe.confirmCardPayment(clientSecret)
  3. Wait for payment_intent.succeeded webhook

5. Subscription Status Transitions

code
Payment succeeds → subscription: active, invoice: paid
Payment fails    → subscription: incomplete, invoice: open
Needs 3DS       → subscription: incomplete, requires_action

Testing with Stripe CLI

bash
# Listen for webhooks locally
stripe listen --forward-to localhost:8080/webhooks/stripe

# Trigger test events
stripe trigger payment_intent.succeeded
stripe trigger invoice.payment_failed

Test Card Numbers

  • 4242 4242 4242 4242 - Always succeeds
  • 4000 0000 0000 0002 - Always declines
  • 4000 0027 6000 3184 - Requires 3D Secure

Security Checklist

  • Webhook signature verification enabled
  • HTTPS endpoints only
  • API keys in environment variables (never in code)
  • Service role key only on server
  • Idempotency keys for all mutations

References