Stripe Payment Integration
Production-ready Stripe integration for payments, subscriptions, and webhooks.
Payment Flows
Flow Use Case PCI Burden
Checkout Session Hosted page, fastest setup Minimal
Payment Intents Custom UI, full control Requires Stripe.js
Setup Intents Save card for later Minimal
Quick Start - Checkout Session
import stripe stripe.api_key = "sk_test_..."
session = stripe.checkout.Session.create( payment_method_types=['card'], line_items=[{ 'price_data': { 'currency': 'usd', 'product_data': {'name': 'Premium Plan'}, 'unit_amount': 2000, # $20.00 in cents 'recurring': {'interval': 'month'}, }, 'quantity': 1, }], mode='subscription', success_url='https://example.com/success?session_id={CHECKOUT_SESSION_ID}', cancel_url='https://example.com/cancel', )
Redirect to session.url
Custom Payment Intent Flow
Backend: Create payment intent
def create_payment_intent(amount, customer_id=None): intent = stripe.PaymentIntent.create( amount=amount, # In cents currency='usd', customer=customer_id, automatic_payment_methods={'enabled': True}, ) return intent.client_secret
// Frontend: Confirm payment const stripe = Stripe('pk_test_...'); const {error, paymentIntent} = await stripe.confirmCardPayment( clientSecret, {payment_method: {card: cardElement}} );
Webhook Handling
@app.route('/webhook', methods=['POST']) def webhook(): payload = request.data sig = request.headers.get('Stripe-Signature')
try:
event = stripe.Webhook.construct_event(
payload, sig, 'whsec_...'
)
except stripe.error.SignatureVerificationError:
return 'Invalid signature', 400
if event['type'] == 'payment_intent.succeeded':
handle_payment_success(event['data']['object'])
elif event['type'] == 'customer.subscription.deleted':
handle_subscription_canceled(event['data']['object'])
return 'OK', 200
Critical Webhook Events
Event When to Handle
payment_intent.succeeded
Payment completed
payment_intent.payment_failed
Payment failed
customer.subscription.updated
Subscription changed
customer.subscription.deleted
Subscription canceled
invoice.payment_succeeded
Subscription payment OK
Subscription Management
Create subscription
subscription = stripe.Subscription.create( customer=customer_id, items=[{'price': 'price_xxx'}], payment_behavior='default_incomplete', expand=['latest_invoice.payment_intent'], )
Customer portal for self-service
session = stripe.billing_portal.Session.create( customer=customer_id, return_url='https://example.com/account', )
Redirect to session.url
Refunds
Full refund
stripe.Refund.create(payment_intent='pi_xxx')
Partial refund
stripe.Refund.create( payment_intent='pi_xxx', amount=500, # $5.00 reason='requested_by_customer' )
Test Cards
Card Number Result
4242424242424242
Success
4000000000000002
Declined
4000002500003155
3D Secure required
4000000000009995
Insufficient funds
Best Practices
-
Always use webhooks - Don't rely on client-side confirmation
-
Idempotency - Handle webhook events exactly once
-
Metadata - Link Stripe objects to your database
-
Test mode - Test all flows before production
-
PCI compliance - Never handle raw card data server-side
-
SCA - Implement 3D Secure for European payments
Common Pitfalls
-
Not verifying webhook signatures
-
Hardcoding amounts (use cents!)
-
Missing webhook event handlers
-
No retry logic for API calls
-
Skipping test card scenarios