stripe-integration

Stripe payment processing with checkout sessions, subscriptions, webhooks, and customer management. Supports three checkout approaches: Stripe-hosted checkout (lowest maintenance), custom UI with Payment Element, and Payment Intents for bespoke control Handles subscriptions, one-time payments, refunds, and disputes with built-in webhook event handling for payment success, failure, and subscription lifecycle changes Includes customer management, payment method storage, and customer portal for self-service subscription management Provides secure webhook verification, idempotent event processing patterns, and test mode support with example card numbers for development

INSTALLATION
npx skills add https://github.com/wshobson/agents --skill stripe-integration
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

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

import stripe

stripe.api_key = "sk_test_..."

# Create a checkout session

session = 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.url

print(session.url)

Payment Implementation Patterns

Pattern 1: One-Time Payment (Hosted Checkout)

def 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

def 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
const 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.

def 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
// Mount Payment Element and confirm via Payment Intents

const 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

def 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

def 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

from flask import Flask, request

import 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

import hashlib

import 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

def 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

def 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

# Use test mode keys

stripe.api_key = "sk_test_..."

# Test card numbers

TEST_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'
BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card