Claude Agent Skill · by Wshobson

Stripe Integration

Handles the full Stripe integration lifecycle from basic checkout sessions to complex subscription billing and webhook processing. Covers both hosted checkout p

Install
Terminal · npx
$npx skills add https://github.com/wshobson/agents --skill stripe-integration
Works with Paperclip

How Stripe Integration fits into a Paperclip company.

Stripe Integration drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.

E
E-Commerce EmpirePaired

Pre-configured AI company — 20 agents, 9 skills, one-time purchase.

$59$89
Explore pack
Source file
SKILL.md492 lines
Expand
---name: stripe-integrationdescription: Implement Stripe payment processing for robust, PCI-compliant payment flows including checkout, subscriptions, and webhooks. Use when integrating Stripe payments, building subscription systems, or implementing secure checkout flows.--- # Stripe Integration Master Stripe payment processing integration for robust, PCI-compliant payment flows including checkout, subscriptions, webhooks, and refunds. ## When to Use This Skill - Implementing payment processing in web/mobile applications- Setting up subscription billing systems- Handling one-time payments and recurring charges- Processing refunds and disputes- Managing customer payment methods- Implementing SCA (Strong Customer Authentication) for European payments- Building marketplace payment flows with Stripe Connect ## Core Concepts ### 1. Payment Flows **Checkout Sessions** - Recommended for most integrations- Supports all UI paths:  - Stripe-hosted checkout page  - Embedded checkout form  - Custom UI with Elements (Payment Element, Express Checkout Element) using `ui_mode='custom'`- Provides built-in checkout capabilities (line items, discounts, tax, shipping, address collection, saved payment methods, and checkout lifecycle events)- Lower integration and maintenance burden than Payment Intents **Payment Intents (Bespoke control)** - You calculate the final amount with taxes, discounts, subscriptions, and currency conversion yourself.- More complex implementation and long-term maintenance burden- Requires Stripe.js for PCI compliance **Setup Intents (Save Payment Methods)** - Collect payment method without charging- Used for subscriptions and future payments- Requires customer confirmation ### 2. Webhooks **Critical Events:** - `payment_intent.succeeded`: Payment completed- `payment_intent.payment_failed`: Payment failed- `customer.subscription.updated`: Subscription changed- `customer.subscription.deleted`: Subscription canceled- `charge.refunded`: Refund processed- `invoice.payment_succeeded`: Subscription payment successful ### 3. Subscriptions **Components:** - **Product**: What you're selling- **Price**: How much and how often- **Subscription**: Customer's recurring payment- **Invoice**: Generated for each billing cycle ### 4. Customer Management - Create and manage customer records- Store multiple payment methods- Track customer metadata- Manage billing details ## Quick Start ```pythonimport stripe stripe.api_key = "sk_test_..." # Create a checkout sessionsession = stripe.checkout.Session.create(    line_items=[{        'price_data': {            'currency': 'usd',            'product_data': {                'name': 'Premium Subscription',            },            'unit_amount': 2000,  # $20.00            'recurring': {                'interval': 'month',            },        },        'quantity': 1,    }],    mode='subscription',    success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',    cancel_url='https://yourdomain.com/cancel') # Redirect user to session.urlprint(session.url)``` ## Payment Implementation Patterns ### Pattern 1: One-Time Payment (Hosted Checkout) ```pythondef create_checkout_session(amount, currency='usd'):    """Create a one-time payment checkout session."""    try:        session = stripe.checkout.Session.create(            line_items=[{                'price_data': {                    'currency': currency,                    'product_data': {                        'name': 'Blue T-shirt',                        'images': ['https://example.com/product.jpg'],                    },                    'unit_amount': amount,  # Amount in cents                },                'quantity': 1,            }],            mode='payment',            success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',            cancel_url='https://yourdomain.com/cancel',            metadata={                'order_id': 'order_123',                'user_id': 'user_456'            }        )        return session    except stripe.error.StripeError as e:        # Handle error        print(f"Stripe error: {e.user_message}")        raise``` ### Pattern 2: Elements with Checkout Sessions ```pythondef create_checkout_session_for_elements(amount, currency='usd'):    """Create a checkout session configured for Payment Element."""    session = stripe.checkout.Session.create(        mode='payment',        ui_mode='custom',        line_items=[{            'price_data': {                'currency': currency,                'product_data': {'name': 'Blue T-shirt'},                'unit_amount': amount,            },            'quantity': 1,        }],        return_url='https://yourdomain.com/complete?session_id={CHECKOUT_SESSION_ID}'    )    return session.client_secret  # Send to frontend``` ```javascriptconst stripe = Stripe("pk_test_...");const appearance = { theme: "stripe" }; const checkout = stripe.initCheckout({  clientSecret,  elementsOptions: { appearance },});const loadActionsResult = await checkout.loadActions(); if (loadActionsResult.type === "success") {  const { actions } = loadActionsResult;  const session = actions.getSession();   const button = document.getElementById("pay-button");  const checkoutContainer = document.getElementById("checkout-container");  const emailInput = document.getElementById("email");  const emailErrors = document.getElementById("email-errors");  const errors = document.getElementById("confirm-errors");   // Display a formatted string representing the total amount  checkoutContainer.append(`Total: ${session.total.total.amount}`);   // Mount Payment Element  const paymentElement = checkout.createPaymentElement();  paymentElement.mount("#payment-element");   // Store email for submission  emailInput.addEventListener("blur", () => {    actions.updateEmail(emailInput.value).then((result) => {      if (result.error) emailErrors.textContent = result.error.message;    });  });   // Handle form submission  button.addEventListener("click", () => {    actions.confirm().then((result) => {      if (result.type === "error") errors.textContent = result.error.message;    });  });}``` ### Pattern 3: Elements with Payment Intents Pattern 2 (Elements with Checkout Sessions) is Stripe's recommended approach, but you can also use Payment Intents as an alternative. ```pythondef create_payment_intent(amount, currency='usd', customer_id=None):    """Create a payment intent for bespoke checkout UI with Payment Element."""    intent = stripe.PaymentIntent.create(        amount=amount,        currency=currency,        customer=customer_id,        automatic_payment_methods={            'enabled': True,        },        metadata={            'integration_check': 'accept_a_payment'        }    )    return intent.client_secret  # Send to frontend``` ```javascript// Mount Payment Element and confirm via Payment Intentsconst stripe = Stripe("pk_test_...");const appearance = { theme: "stripe" };const elements = stripe.elements({ appearance, clientSecret }); const paymentElement = elements.create("payment");paymentElement.mount("#payment-element"); document.getElementById("pay-button").addEventListener("click", async () => {  const { error } = await stripe.confirmPayment({    elements,    confirmParams: {      return_url: "https://yourdomain.com/complete",    },  });   if (error) {    document.getElementById("errors").textContent = error.message;  }});``` ### Pattern 4: Subscription Creation ```pythondef create_subscription(customer_id, price_id):    """Create a subscription for a customer."""    try:        subscription = stripe.Subscription.create(            customer=customer_id,            items=[{'price': price_id}],            payment_behavior='default_incomplete',            payment_settings={'save_default_payment_method': 'on_subscription'},            expand=['latest_invoice.payment_intent'],        )         return {            'subscription_id': subscription.id,            'client_secret': subscription.latest_invoice.payment_intent.client_secret        }    except stripe.error.StripeError as e:        print(f"Subscription creation failed: {e}")        raise``` ### Pattern 5: Customer Portal ```pythondef create_customer_portal_session(customer_id):    """Create a portal session for customers to manage subscriptions."""    session = stripe.billing_portal.Session.create(        customer=customer_id,        return_url='https://yourdomain.com/account',    )    return session.url  # Redirect customer here``` ## Webhook Handling ### Secure Webhook Endpoint ```pythonfrom flask import Flask, requestimport stripe app = Flask(__name__) endpoint_secret = 'whsec_...' @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:        # Invalid payload        return 'Invalid payload', 400    except stripe.error.SignatureVerificationError:        # Invalid signature        return 'Invalid signature', 400     # Handle the event    if event['type'] == 'payment_intent.succeeded':        payment_intent = event['data']['object']        handle_successful_payment(payment_intent)    elif event['type'] == 'payment_intent.payment_failed':        payment_intent = event['data']['object']        handle_failed_payment(payment_intent)    elif event['type'] == 'customer.subscription.deleted':        subscription = event['data']['object']        handle_subscription_canceled(subscription)     return 'Success', 200 def handle_successful_payment(payment_intent):    """Process successful payment."""    customer_id = payment_intent.get('customer')    amount = payment_intent['amount']    metadata = payment_intent.get('metadata', {})     # Update your database    # Send confirmation email    # Fulfill order    print(f"Payment succeeded: {payment_intent['id']}") def handle_failed_payment(payment_intent):    """Handle failed payment."""    error = payment_intent.get('last_payment_error', {})    print(f"Payment failed: {error.get('message')}")    # Notify customer    # Update order status def handle_subscription_canceled(subscription):    """Handle subscription cancellation."""    customer_id = subscription['customer']    # Update user access    # Send cancellation email    print(f"Subscription canceled: {subscription['id']}")``` ### Webhook Best Practices ```pythonimport hashlibimport hmac def verify_webhook_signature(payload, signature, secret):    """Manually verify webhook signature."""    expected_sig = hmac.new(        secret.encode('utf-8'),        payload,        hashlib.sha256    ).hexdigest()     return hmac.compare_digest(signature, expected_sig) def handle_webhook_idempotently(event_id, handler):    """Ensure webhook is processed exactly once."""    # Check if event already processed    if is_event_processed(event_id):        return     # Process event    try:        handler()        mark_event_processed(event_id)    except Exception as e:        log_error(e)        # Stripe will retry failed webhooks        raise``` ## Customer Management ```pythondef create_customer(email, name, payment_method_id=None):    """Create a Stripe customer."""    customer = stripe.Customer.create(        email=email,        name=name,        payment_method=payment_method_id,        invoice_settings={            'default_payment_method': payment_method_id        } if payment_method_id else None,        metadata={            'user_id': '12345'        }    )    return customer def attach_payment_method(customer_id, payment_method_id):    """Attach a payment method to a customer."""    stripe.PaymentMethod.attach(        payment_method_id,        customer=customer_id    )     # Set as default    stripe.Customer.modify(        customer_id,        invoice_settings={            'default_payment_method': payment_method_id        }    ) def list_customer_payment_methods(customer_id):    """List all payment methods for a customer."""    payment_methods = stripe.PaymentMethod.list(        customer=customer_id,        type='card'    )    return payment_methods.data``` ## Refund Handling ```pythondef create_refund(payment_intent_id, amount=None, reason=None):    """Create a refund."""    refund_params = {        'payment_intent': payment_intent_id    }     if amount:        refund_params['amount'] = amount  # Partial refund     if reason:        refund_params['reason'] = reason  # 'duplicate', 'fraudulent', 'requested_by_customer'     refund = stripe.Refund.create(**refund_params)    return refund def handle_dispute(charge_id, evidence):    """Update dispute with evidence."""    stripe.Dispute.modify(        charge_id,        evidence={            'customer_name': evidence.get('customer_name'),            'customer_email_address': evidence.get('customer_email'),            'shipping_documentation': evidence.get('shipping_proof'),            'customer_communication': evidence.get('communication'),        }    )``` ## Testing ```python# Use test mode keysstripe.api_key = "sk_test_..." # Test card numbersTEST_CARDS = {    'success': '4242424242424242',    'declined': '4000000000000002',    '3d_secure': '4000002500003155',    'insufficient_funds': '4000000000009995'} def test_payment_flow():    """Test complete payment flow."""    # Create test customer    customer = stripe.Customer.create(        email="test@example.com"    )     # Create payment intent    intent = stripe.PaymentIntent.create(        amount=1000,        automatic_payment_methods={            'enabled': True        },        currency='usd',        customer=customer.id    )     # Confirm with test card    confirmed = stripe.PaymentIntent.confirm(        intent.id,        payment_method='pm_card_visa'  # Test payment method    )     assert confirmed.status == 'succeeded'```