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 Session (Hosted)
- Stripe-hosted payment page
- Minimal PCI compliance burden
- Fastest implementation
- Supports one-time and recurring payments
Payment Intents (Custom UI)
- Full control over payment UI
- Requires Stripe.js for PCI compliance
- More complex implementation
- Better customization options
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
python
1import stripe
2
3stripe.api_key = "sk_test_..."
4
5# Create a checkout session
6session = stripe.checkout.Session.create(
7 payment_method_types=['card'],
8 line_items=[{
9 'price_data': {
10 'currency': 'usd',
11 'product_data': {
12 'name': 'Premium Subscription',
13 },
14 'unit_amount': 2000, # $20.00
15 'recurring': {
16 'interval': 'month',
17 },
18 },
19 'quantity': 1,
20 }],
21 mode='subscription',
22 success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',
23 cancel_url='https://yourdomain.com/cancel',
24)
25
26# Redirect user to session.url
27print(session.url)
Payment Implementation Patterns
Pattern 1: One-Time Payment (Hosted Checkout)
python
1def create_checkout_session(amount, currency='usd'):
2 """Create a one-time payment checkout session."""
3 try:
4 session = stripe.checkout.Session.create(
5 payment_method_types=['card'],
6 line_items=[{
7 'price_data': {
8 'currency': currency,
9 'product_data': {
10 'name': 'Purchase',
11 'images': ['https://example.com/product.jpg'],
12 },
13 'unit_amount': amount, # Amount in cents
14 },
15 'quantity': 1,
16 }],
17 mode='payment',
18 success_url='https://yourdomain.com/success?session_id={CHECKOUT_SESSION_ID}',
19 cancel_url='https://yourdomain.com/cancel',
20 metadata={
21 'order_id': 'order_123',
22 'user_id': 'user_456'
23 }
24 )
25 return session
26 except stripe.error.StripeError as e:
27 # Handle error
28 print(f"Stripe error: {e.user_message}")
29 raise
Pattern 2: Custom Payment Intent Flow
python
1def create_payment_intent(amount, currency='usd', customer_id=None):
2 """Create a payment intent for custom checkout UI."""
3 intent = stripe.PaymentIntent.create(
4 amount=amount,
5 currency=currency,
6 customer=customer_id,
7 automatic_payment_methods={
8 'enabled': True,
9 },
10 metadata={
11 'integration_check': 'accept_a_payment'
12 }
13 )
14 return intent.client_secret # Send to frontend
15
16# Frontend (JavaScript)
17"""
18const stripe = Stripe('pk_test_...');
19const elements = stripe.elements();
20const cardElement = elements.create('card');
21cardElement.mount('#card-element');
22
23const {error, paymentIntent} = await stripe.confirmCardPayment(
24 clientSecret,
25 {
26 payment_method: {
27 card: cardElement,
28 billing_details: {
29 name: 'Customer Name'
30 }
31 }
32 }
33);
34
35if (error) {
36 // Handle error
37} else if (paymentIntent.status === 'succeeded') {
38 // Payment successful
39}
40"""
Pattern 3: Subscription Creation
python
1def create_subscription(customer_id, price_id):
2 """Create a subscription for a customer."""
3 try:
4 subscription = stripe.Subscription.create(
5 customer=customer_id,
6 items=[{'price': price_id}],
7 payment_behavior='default_incomplete',
8 payment_settings={'save_default_payment_method': 'on_subscription'},
9 expand=['latest_invoice.payment_intent'],
10 )
11
12 return {
13 'subscription_id': subscription.id,
14 'client_secret': subscription.latest_invoice.payment_intent.client_secret
15 }
16 except stripe.error.StripeError as e:
17 print(f"Subscription creation failed: {e}")
18 raise
Pattern 4: Customer Portal
python
1def create_customer_portal_session(customer_id):
2 """Create a portal session for customers to manage subscriptions."""
3 session = stripe.billing_portal.Session.create(
4 customer=customer_id,
5 return_url='https://yourdomain.com/account',
6 )
7 return session.url # Redirect customer here
Webhook Handling
Secure Webhook Endpoint
python
1from flask import Flask, request
2import stripe
3
4app = Flask(__name__)
5
6endpoint_secret = 'whsec_...'
7
8@app.route('/webhook', methods=['POST'])
9def webhook():
10 payload = request.data
11 sig_header = request.headers.get('Stripe-Signature')
12
13 try:
14 event = stripe.Webhook.construct_event(
15 payload, sig_header, endpoint_secret
16 )
17 except ValueError:
18 # Invalid payload
19 return 'Invalid payload', 400
20 except stripe.error.SignatureVerificationError:
21 # Invalid signature
22 return 'Invalid signature', 400
23
24 # Handle the event
25 if event['type'] == 'payment_intent.succeeded':
26 payment_intent = event['data']['object']
27 handle_successful_payment(payment_intent)
28 elif event['type'] == 'payment_intent.payment_failed':
29 payment_intent = event['data']['object']
30 handle_failed_payment(payment_intent)
31 elif event['type'] == 'customer.subscription.deleted':
32 subscription = event['data']['object']
33 handle_subscription_canceled(subscription)
34
35 return 'Success', 200
36
37def handle_successful_payment(payment_intent):
38 """Process successful payment."""
39 customer_id = payment_intent.get('customer')
40 amount = payment_intent['amount']
41 metadata = payment_intent.get('metadata', {})
42
43 # Update your database
44 # Send confirmation email
45 # Fulfill order
46 print(f"Payment succeeded: {payment_intent['id']}")
47
48def handle_failed_payment(payment_intent):
49 """Handle failed payment."""
50 error = payment_intent.get('last_payment_error', {})
51 print(f"Payment failed: {error.get('message')}")
52 # Notify customer
53 # Update order status
54
55def handle_subscription_canceled(subscription):
56 """Handle subscription cancellation."""
57 customer_id = subscription['customer']
58 # Update user access
59 # Send cancellation email
60 print(f"Subscription canceled: {subscription['id']}")
Webhook Best Practices
python
1import hashlib
2import hmac
3
4def verify_webhook_signature(payload, signature, secret):
5 """Manually verify webhook signature."""
6 expected_sig = hmac.new(
7 secret.encode('utf-8'),
8 payload,
9 hashlib.sha256
10 ).hexdigest()
11
12 return hmac.compare_digest(signature, expected_sig)
13
14def handle_webhook_idempotently(event_id, handler):
15 """Ensure webhook is processed exactly once."""
16 # Check if event already processed
17 if is_event_processed(event_id):
18 return
19
20 # Process event
21 try:
22 handler()
23 mark_event_processed(event_id)
24 except Exception as e:
25 log_error(e)
26 # Stripe will retry failed webhooks
27 raise
Customer Management
python
1def create_customer(email, name, payment_method_id=None):
2 """Create a Stripe customer."""
3 customer = stripe.Customer.create(
4 email=email,
5 name=name,
6 payment_method=payment_method_id,
7 invoice_settings={
8 'default_payment_method': payment_method_id
9 } if payment_method_id else None,
10 metadata={
11 'user_id': '12345'
12 }
13 )
14 return customer
15
16def attach_payment_method(customer_id, payment_method_id):
17 """Attach a payment method to a customer."""
18 stripe.PaymentMethod.attach(
19 payment_method_id,
20 customer=customer_id
21 )
22
23 # Set as default
24 stripe.Customer.modify(
25 customer_id,
26 invoice_settings={
27 'default_payment_method': payment_method_id
28 }
29 )
30
31def list_customer_payment_methods(customer_id):
32 """List all payment methods for a customer."""
33 payment_methods = stripe.PaymentMethod.list(
34 customer=customer_id,
35 type='card'
36 )
37 return payment_methods.data
Refund Handling
python
1def create_refund(payment_intent_id, amount=None, reason=None):
2 """Create a refund."""
3 refund_params = {
4 'payment_intent': payment_intent_id
5 }
6
7 if amount:
8 refund_params['amount'] = amount # Partial refund
9
10 if reason:
11 refund_params['reason'] = reason # 'duplicate', 'fraudulent', 'requested_by_customer'
12
13 refund = stripe.Refund.create(**refund_params)
14 return refund
15
16def handle_dispute(charge_id, evidence):
17 """Update dispute with evidence."""
18 stripe.Dispute.modify(
19 charge_id,
20 evidence={
21 'customer_name': evidence.get('customer_name'),
22 'customer_email_address': evidence.get('customer_email'),
23 'shipping_documentation': evidence.get('shipping_proof'),
24 'customer_communication': evidence.get('communication'),
25 }
26 )
Testing
python
1# Use test mode keys
2stripe.api_key = "sk_test_..."
3
4# Test card numbers
5TEST_CARDS = {
6 'success': '4242424242424242',
7 'declined': '4000000000000002',
8 '3d_secure': '4000002500003155',
9 'insufficient_funds': '4000000000009995'
10}
11
12def test_payment_flow():
13 """Test complete payment flow."""
14 # Create test customer
15 customer = stripe.Customer.create(
16 email="test@example.com"
17 )
18
19 # Create payment intent
20 intent = stripe.PaymentIntent.create(
21 amount=1000,
22 currency='usd',
23 customer=customer.id,
24 payment_method_types=['card']
25 )
26
27 # Confirm with test card
28 confirmed = stripe.PaymentIntent.confirm(
29 intent.id,
30 payment_method='pm_card_visa' # Test payment method
31 )
32
33 assert confirmed.status == 'succeeded'
Resources
- references/checkout-flows.md: Detailed checkout implementation
- references/webhook-handling.md: Webhook security and processing
- references/subscription-management.md: Subscription lifecycle
- references/customer-management.md: Customer and payment method handling
- references/invoice-generation.md: Invoicing and billing
- assets/stripe-client.py: Production-ready Stripe client wrapper
- assets/webhook-handler.py: Complete webhook processor
- assets/checkout-config.json: Checkout configuration templates
Best Practices
- Always Use Webhooks: Don't rely solely on client-side confirmation
- Idempotency: Handle webhook events idempotently
- Error Handling: Gracefully handle all Stripe errors
- Test Mode: Thoroughly test with test keys before production
- Metadata: Use metadata to link Stripe objects to your database
- Monitoring: Track payment success rates and errors
- PCI Compliance: Never handle raw card data on your server
- SCA Ready: Implement 3D Secure for European payments
Common Pitfalls
- Not Verifying Webhooks: Always verify webhook signatures
- Missing Webhook Events: Handle all relevant webhook events
- Hardcoded Amounts: Use cents/smallest currency unit
- No Retry Logic: Implement retries for API calls
- Ignoring Test Mode: Test all edge cases with test cards