Skip to main content
RapidDev - Software Development Agency
stripe-guide

How to use Stripe API with Python

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.

What you'll learn

  • How to install and configure the Stripe Python SDK
  • How to create customers and process payments with Flask
  • How to handle webhooks with signature verification in Python
  • How to integrate Stripe with Django views
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner6 min read15 minutesStripe Python SDK v7+, Python 3.8+, Flask 3+ or Django 4+March 2026RapidDev Engineering Team
TL;DR

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

1

Install the Stripe SDK and Flask

Install the required packages with pip.

typescript
1# Install Stripe and Flask
2pip install stripe flask python-dotenv
3
4# Or for Django:
5# pip install stripe django python-dotenv

Expected result: Stripe, Flask, and dotenv packages are installed.

2

Configure Stripe with environment variables

Create a .env file and configure the Stripe client in your application.

typescript
1# .env
2# STRIPE_SECRET_KEY=sk_test_your_key_here
3# STRIPE_PUBLISHABLE_KEY=pk_test_your_key_here
4# STRIPE_WEBHOOK_SECRET=whsec_your_secret_here
5
6# app.py
7import os
8import stripe
9from flask import Flask, request, jsonify
10from dotenv import load_dotenv
11
12load_dotenv()
13
14stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
15stripe.api_version = '2024-12-18.acacia'
16
17app = Flask(__name__)
18
19if not stripe.api_key:
20 raise ValueError('STRIPE_SECRET_KEY environment variable is not set')
21
22print('Stripe initialized with API version:', stripe.api_version)

Expected result: The Stripe client is configured with your test secret key and pinned API version.

3

Create a customer and PaymentIntent with Flask

Build Flask routes for creating customers and processing payments.

typescript
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)}), 400
13
14
15@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 cents
21 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)}), 400

Expected result: Flask routes create Stripe customers and PaymentIntents, returning the client_secret for frontend confirmation.

4

Handle webhooks with signature verification

Verify webhook signatures using the raw request body. This is critical for security.

typescript
1@app.route('/webhook', methods=['POST'])
2def webhook():
3 payload = request.get_data() # Raw body bytes
4 sig_header = request.headers.get('Stripe-Signature')
5 webhook_secret = os.environ.get('STRIPE_WEBHOOK_SECRET')
6
7 try:
8 event = stripe.Webhook.construct_event(
9 payload, sig_header, webhook_secret
10 )
11 except ValueError:
12 return 'Invalid payload', 400
13 except stripe.error.SignatureVerificationError:
14 return 'Invalid signature', 400
15
16 # Handle the event
17 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']}")
23
24 return jsonify({'received': True})

Expected result: Webhook signatures are verified and events are processed securely.

5

Handle Stripe errors properly

Stripe raises specific exception types for different error scenarios.

typescript
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 }), 402
8 elif isinstance(e, stripe.error.RateLimitError):
9 return jsonify({'error': 'Too many requests. Please retry.'}), 429
10 elif isinstance(e, stripe.error.InvalidRequestError):
11 return jsonify({'error': str(e)}), 400
12 elif isinstance(e, stripe.error.AuthenticationError):
13 return jsonify({'error': 'Payment service configuration error.'}), 500
14 elif isinstance(e, stripe.error.APIConnectionError):
15 return jsonify({'error': 'Payment service temporarily unavailable.'}), 503
16 else:
17 return jsonify({'error': 'An unexpected error occurred.'}), 500

Expected result: Each Stripe error type is handled with an appropriate HTTP status code and user-friendly message.

6

Django integration alternative

If you use Django instead of Flask, here is the equivalent view setup.

typescript
1# views.py (Django)
2import json
3import stripe
4from django.http import JsonResponse
5from django.views.decorators.csrf import csrf_exempt
6from django.views.decorators.http import require_POST
7import os
8
9stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
10
11@require_POST
12def 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)
23
24@csrf_exempt
25@require_POST
26def webhook(request):
27 payload = request.body # Raw bytes
28 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)
35
36 if event['type'] == 'payment_intent.succeeded':
37 print('Payment succeeded:', event['data']['object']['id'])
38
39 return JsonResponse({'received': True})

Expected result: Django views handle PaymentIntent creation and webhook verification with the same Stripe SDK.

Complete working example

app.py
1import os
2import stripe
3from flask import Flask, request, jsonify
4from dotenv import load_dotenv
5
6load_dotenv()
7
8stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
9stripe.api_version = '2024-12-18.acacia'
10
11app = Flask(__name__)
12
13@app.route('/api/config')
14def get_config():
15 return jsonify({
16 'publishableKey': os.environ.get('STRIPE_PUBLISHABLE_KEY'),
17 })
18
19@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)}), 400
30
31@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)}), 400
44
45@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)}), 400
56
57@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), 400
67
68 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']}")
72
73 return jsonify({'received': True})
74
75if __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.

ChatGPT Prompt

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.

Stripe Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.