Accept Payments with Paystack in Flask
To accept Paystack payments in Flask, create a route that initializes a transaction with the Paystack API using the requests library, redirects the customer to Paystack checkout, and verifies the payment in a callback route before granting value.
Project Setup
pip install flask requests python-dotenv
# app.py
import os
import uuid
import requests as http_requests
from flask import Flask, request, redirect, render_template, jsonify
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
PAYSTACK_SECRET = os.environ.get('PAYSTACK_SECRET_KEY')
BASE_URL = os.environ.get('BASE_URL', 'http://localhost:5000')
Initialize a Transaction
@app.route('/pay', methods=['POST'])
def initialize_payment():
email = request.form.get('email')
amount = float(request.form.get('amount', 5000))
reference = 'order_' + uuid.uuid4().hex[:12]
response = http_requests.post(
'https://api.paystack.co/transaction/initialize',
json={
'email': email,
'amount': int(amount * 100),
'reference': reference,
'callback_url': BASE_URL + '/payment/callback',
},
headers={
'Authorization': 'Bearer ' + PAYSTACK_SECRET,
'Content-Type': 'application/json',
},
)
data = response.json()
if data.get('status'):
# TODO: Save order to database
return redirect(data['data']['authorization_url'])
return 'Initialization failed: ' + data.get('message', 'Unknown error'), 400
Handle the Callback
@app.route('/payment/callback')
def payment_callback():
reference = request.args.get('reference') or request.args.get('trxref')
if not reference:
return 'Missing reference', 400
response = http_requests.get(
'https://api.paystack.co/transaction/verify/' + reference,
headers={'Authorization': 'Bearer ' + PAYSTACK_SECRET},
)
data = response.json()
if data.get('status') and data['data']['status'] == 'success':
amount = data['data']['amount'] / 100
currency = data['data']['currency']
# TODO: Verify amount matches database, mark order as paid
return 'Payment successful! ' + currency + ' ' + str(amount)
return 'Payment not successful', 400
JSON API for SPA Frontends
If your frontend is a separate SPA (React, Vue, etc.), return JSON instead of redirecting:
@app.route('/api/pay', methods=['POST'])
def api_initialize():
data = request.get_json()
email = data.get('email')
amount = data.get('amount', 5000)
reference = 'order_' + uuid.uuid4().hex[:12]
response = http_requests.post(
'https://api.paystack.co/transaction/initialize',
json={
'email': email,
'amount': int(amount * 100),
'reference': reference,
},
headers={
'Authorization': 'Bearer ' + PAYSTACK_SECRET,
'Content-Type': 'application/json',
},
)
result = response.json()
if result.get('status'):
return jsonify({
'authorization_url': result['data']['authorization_url'],
'access_code': result['data']['access_code'],
'reference': result['data']['reference'],
})
return jsonify({'error': result.get('message')}), 400
@app.route('/api/verify')
def api_verify():
reference = request.args.get('reference')
if not reference:
return jsonify({'error': 'Missing reference'}), 400
response = http_requests.get(
'https://api.paystack.co/transaction/verify/' + reference,
headers={'Authorization': 'Bearer ' + PAYSTACK_SECRET},
)
data = response.json()
if data.get('status') and data['data']['status'] == 'success':
return jsonify({
'success': True,
'amount': data['data']['amount'] / 100,
'currency': data['data']['currency'],
})
return jsonify({'success': False}), 400
Production Checklist
- Use gunicorn. Do not run Flask's development server in production. Use gunicorn behind nginx.
- HTTPS required. Paystack requires HTTPS for callback and webhook URLs.
- CORS. Install flask-cors if your frontend is on a different domain.
- Set up webhooks. See Handle Paystack Webhooks in Flask.
- Validate amounts. Check amounts against your database, not client input.
- Switch keys. Use sk_live_ in production.
Key Takeaways
- ✓Flask routes handle the Paystack API calls server-side. Use the requests library for HTTP calls.
- ✓Store your Paystack secret key in environment variables. Access it with os.environ or Flask config.
- ✓Amounts are in kobo (NGN), pesewas (GHS), or cents. Multiply by 100 before sending to Paystack.
- ✓Always verify the transaction server-side after the redirect callback.
- ✓Flask is minimal by design. You bring your own database, ORM, and templating as needed.
- ✓For production, use a WSGI server like gunicorn behind nginx with HTTPS.
Frequently Asked Questions
- Is Flask too minimal for payment processing?
- No. Flask is a web framework. The Paystack integration is HTTP calls with the requests library. Flask handles routing and request/response. What matters is your server-side verification logic, which is the same regardless of framework.
- Do I need Flask-SQLAlchemy for payments?
- You need some way to store orders and track payment status. Flask-SQLAlchemy is a popular choice. You could also use plain SQLite, MongoDB, or any other database.
- Can I use Flask async with Paystack?
- Flask 2.0+ supports async views with async def. Use httpx instead of requests for async HTTP calls. The Paystack API endpoints and logic are the same.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon