This guide covers integrating Stripe with Python using Flask and Django. Install the stripe package with pip, configure your API keys via environment variables, create customers, process payments with PaymentIntents, and handle webhooks with signature verification. All examples use test mode with the 4242424242424242 test card and amounts in cents.
Getting Started with the Stripe API in Python
The Stripe Python SDK provides a clean, Pythonic interface to the Stripe REST API. It supports both Flask and Django, with built-in error handling and webhook signature verification. This guide covers both frameworks, starting with Flask for simplicity and showing Django equivalents. All examples use test mode so you can follow along without processing real payments.
Prerequisites
- Python 3.8 or later installed
- A Stripe account with test API keys
- pip for package installation
- Basic familiarity with Flask or Django
Step-by-step guide
Install the Stripe SDK and Flask
Install the Stripe SDK and Flask
Install the required packages with pip.
1# Install Stripe and Flask2pip install stripe flask python-dotenv34# Or for Django:5# pip install stripe django python-dotenvExpected result: Stripe, Flask, and dotenv packages are installed.
Configure Stripe with environment variables
Configure Stripe with environment variables
Create a .env file and configure the Stripe client in your application.
1# .env2# STRIPE_SECRET_KEY=sk_test_your_key_here3# STRIPE_PUBLISHABLE_KEY=pk_test_your_key_here4# STRIPE_WEBHOOK_SECRET=whsec_your_secret_here56# app.py7import os8import stripe9from flask import Flask, request, jsonify10from dotenv import load_dotenv1112load_dotenv()1314stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')15stripe.api_version = '2024-12-18.acacia'1617app = Flask(__name__)1819if not stripe.api_key:20 raise ValueError('STRIPE_SECRET_KEY environment variable is not set')2122print('Stripe initialized with API version:', stripe.api_version)Expected result: The Stripe client is configured with your test secret key and pinned API version.
Create a customer and PaymentIntent with Flask
Create a customer and PaymentIntent with Flask
Build Flask routes for creating customers and processing payments.
1@app.route('/api/customers', methods=['POST'])2def create_customer():3 try:4 data = request.get_json()5 customer = stripe.Customer.create(6 email=data.get('email'),7 name=data.get('name'),8 metadata={'app_user_id': data.get('userId', '')},9 )10 return jsonify({'customerId': customer.id})11 except stripe.error.StripeError as e:12 return jsonify({'error': str(e)}), 400131415@app.route('/api/create-payment-intent', methods=['POST'])16def create_payment_intent():17 try:18 data = request.get_json()19 intent = stripe.PaymentIntent.create(20 amount=data['amount'], # Amount in cents21 currency='usd',22 customer=data.get('customerId'),23 automatic_payment_methods={'enabled': True},24 metadata={'order_id': data.get('orderId', '')},25 )26 return jsonify({'clientSecret': intent.client_secret})27 except stripe.error.StripeError as e:28 return jsonify({'error': str(e)}), 400Expected result: Flask routes create Stripe customers and PaymentIntents, returning the client_secret for frontend confirmation.
Handle webhooks with signature verification
Handle webhooks with signature verification
Verify webhook signatures using the raw request body. This is critical for security.
1@app.route('/webhook', methods=['POST'])2def webhook():3 payload = request.get_data() # Raw body bytes4 sig_header = request.headers.get('Stripe-Signature')5 webhook_secret = os.environ.get('STRIPE_WEBHOOK_SECRET')67 try:8 event = stripe.Webhook.construct_event(9 payload, sig_header, webhook_secret10 )11 except ValueError:12 return 'Invalid payload', 40013 except stripe.error.SignatureVerificationError:14 return 'Invalid signature', 4001516 # Handle the event17 if event['type'] == 'payment_intent.succeeded':18 intent = event['data']['object']19 print(f"Payment succeeded: {intent['id']} (${intent['amount'] / 100})")20 elif event['type'] == 'payment_intent.payment_failed':21 intent = event['data']['object']22 print(f"Payment failed: {intent['id']}")2324 return jsonify({'received': True})Expected result: Webhook signatures are verified and events are processed securely.
Handle Stripe errors properly
Handle Stripe errors properly
Stripe raises specific exception types for different error scenarios.
1def handle_stripe_error(e):2 if isinstance(e, stripe.error.CardError):3 return jsonify({4 'error': e.user_message,5 'code': e.code,6 'decline_code': e.json_body.get('error', {}).get('decline_code'),7 }), 4028 elif isinstance(e, stripe.error.RateLimitError):9 return jsonify({'error': 'Too many requests. Please retry.'}), 42910 elif isinstance(e, stripe.error.InvalidRequestError):11 return jsonify({'error': str(e)}), 40012 elif isinstance(e, stripe.error.AuthenticationError):13 return jsonify({'error': 'Payment service configuration error.'}), 50014 elif isinstance(e, stripe.error.APIConnectionError):15 return jsonify({'error': 'Payment service temporarily unavailable.'}), 50316 else:17 return jsonify({'error': 'An unexpected error occurred.'}), 500Expected result: Each Stripe error type is handled with an appropriate HTTP status code and user-friendly message.
Django integration alternative
Django integration alternative
If you use Django instead of Flask, here is the equivalent view setup.
1# views.py (Django)2import json3import stripe4from django.http import JsonResponse5from django.views.decorators.csrf import csrf_exempt6from django.views.decorators.http import require_POST7import os89stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')1011@require_POST12def create_payment_intent(request):13 data = json.loads(request.body)14 try:15 intent = stripe.PaymentIntent.create(16 amount=data['amount'],17 currency='usd',18 automatic_payment_methods={'enabled': True},19 )20 return JsonResponse({'clientSecret': intent.client_secret})21 except stripe.error.StripeError as e:22 return JsonResponse({'error': str(e)}, status=400)2324@csrf_exempt25@require_POST26def webhook(request):27 payload = request.body # Raw bytes28 sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')29 try:30 event = stripe.Webhook.construct_event(31 payload, sig_header, os.environ.get('STRIPE_WEBHOOK_SECRET')32 )33 except (ValueError, stripe.error.SignatureVerificationError):34 return JsonResponse({'error': 'Invalid'}, status=400)3536 if event['type'] == 'payment_intent.succeeded':37 print('Payment succeeded:', event['data']['object']['id'])3839 return JsonResponse({'received': True})Expected result: Django views handle PaymentIntent creation and webhook verification with the same Stripe SDK.
Complete working example
1import os2import stripe3from flask import Flask, request, jsonify4from dotenv import load_dotenv56load_dotenv()78stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')9stripe.api_version = '2024-12-18.acacia'1011app = Flask(__name__)1213@app.route('/api/config')14def get_config():15 return jsonify({16 'publishableKey': os.environ.get('STRIPE_PUBLISHABLE_KEY'),17 })1819@app.route('/api/customers', methods=['POST'])20def create_customer():21 try:22 data = request.get_json()23 customer = stripe.Customer.create(24 email=data.get('email'),25 name=data.get('name'),26 )27 return jsonify({'customerId': customer.id})28 except stripe.error.StripeError as e:29 return jsonify({'error': str(e)}), 4003031@app.route('/api/create-payment-intent', methods=['POST'])32def create_payment_intent():33 try:34 data = request.get_json()35 intent = stripe.PaymentIntent.create(36 amount=data['amount'],37 currency='usd',38 customer=data.get('customerId'),39 automatic_payment_methods={'enabled': True},40 )41 return jsonify({'clientSecret': intent.client_secret})42 except stripe.error.StripeError as e:43 return jsonify({'error': str(e)}), 4004445@app.route('/api/payments/<payment_id>')46def get_payment(payment_id):47 try:48 intent = stripe.PaymentIntent.retrieve(payment_id)49 return jsonify({50 'id': intent.id,51 'status': intent.status,52 'amount': intent.amount,53 })54 except stripe.error.StripeError as e:55 return jsonify({'error': str(e)}), 4005657@app.route('/webhook', methods=['POST'])58def webhook():59 payload = request.get_data()60 sig = request.headers.get('Stripe-Signature')61 try:62 event = stripe.Webhook.construct_event(63 payload, sig, os.environ.get('STRIPE_WEBHOOK_SECRET')64 )65 except (ValueError, stripe.error.SignatureVerificationError) as e:66 return str(e), 4006768 if event['type'] == 'payment_intent.succeeded':69 print(f"Payment confirmed: {event['data']['object']['id']}")70 elif event['type'] == 'payment_intent.payment_failed':71 print(f"Payment failed: {event['data']['object']['id']}")7273 return jsonify({'received': True})7475if __name__ == '__main__':76 app.run(port=3000, debug=True)Common mistakes when using Stripe API with Python
Why it's a problem: Using request.json instead of request.get_data() for webhook verification in Flask
How to avoid: Webhook signature verification requires the raw request body. Use request.get_data() in Flask or request.body in Django.
Why it's a problem: Setting stripe.api_key to the publishable key
How to avoid: Use the secret key (sk_test_ or sk_live_) for stripe.api_key. The publishable key is for frontend use only.
Why it's a problem: Not handling stripe.error.StripeError exceptions
How to avoid: Wrap all Stripe API calls in try/except blocks catching stripe.error.StripeError or its subclasses.
Why it's a problem: Forgetting @csrf_exempt on Django webhook views
How to avoid: Stripe webhook requests do not include CSRF tokens. Add @csrf_exempt decorator to the webhook view in Django.
Best practices
- Pin the API version with stripe.api_version to prevent unexpected changes
- Use environment variables for API keys — never hardcode them
- Use request.get_data() (Flask) or request.body (Django) for webhook signature verification
- Handle all Stripe error types with appropriate HTTP status codes
- Use test card 4242424242424242 with any future expiry and any CVC for testing
- Test webhooks locally with the Stripe CLI: stripe listen --forward-to localhost:3000/webhook
- Add metadata to PaymentIntents to link them to your internal order records
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
How do I use the Stripe API with Python? I want to set up a Flask server that creates customers, processes payments with PaymentIntents, and handles webhooks with signature verification. Show me complete code.
Build a Stripe payment server in Python using Flask. I need customer creation, PaymentIntent creation, payment status retrieval, and webhook handling with signature verification. Include proper error handling for all Stripe error types.
Frequently asked questions
Which Python version does the Stripe SDK require?
The Stripe Python SDK v7+ requires Python 3.6 or later. We recommend Python 3.10+ for the best experience.
Can I use Stripe with FastAPI?
Yes. The Stripe SDK works with any Python web framework. For FastAPI, use Request.body() to get raw bytes for webhook verification and return JSONResponse for API responses.
How do I use async/await with the Stripe Python SDK?
The Stripe Python SDK v7+ supports async operations natively. Use stripe.PaymentIntent.create_async() and await the result in async frameworks like FastAPI.
What is the test card number for Stripe?
Use 4242424242424242 with any future expiry date and any 3-digit CVC. This always succeeds in test mode. Use 4000000000000002 for a declined card.
Can RapidDev help with Python Stripe integrations?
Yes. RapidDev can build and maintain Stripe integrations in Python using Flask, Django, or FastAPI, including complex subscription billing and Connect marketplace flows.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation