To build Paystack subscriptions in Flask, create a plan via the Paystack API, initialize a transaction with the plan code, handle subscription webhooks to track status, and provide routes for customers to view and cancel their subscriptions.
@app.route('/subscribe', methods=['POST'])
def subscribe():
plan_code = request.form.get('plan_code')
email = request.form.get('email')
response = http_requests.post(
'https://api.paystack.co/transaction/initialize',
json={
'email': email,
'plan': plan_code,
'callback_url': BASE_URL + '/subscription/callback',
},
headers={
'Authorization': 'Bearer ' + PAYSTACK_SECRET,
'Content-Type': 'application/json',
},
)
data = response.json()
if data.get('status'):
return redirect(data['data']['authorization_url'])
return 'Subscription failed: ' + data.get('message', ''), 400
def handle_new_subscription(data):
db.save_subscription({
'subscription_code': data['subscription_code'],
'customer_email': data['customer']['email'],
'plan_code': data['plan']['plan_code'],
'email_token': data.get('email_token', ''),
'status': 'active',
'next_payment_date': data.get('next_payment_date'),
})
def handle_failed_renewal(data):
sub_code = data.get('subscription', {}).get('subscription_code')
if sub_code:
db.update_subscription_status(sub_code, 'past_due')
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
@app.route('/subscription/cancel', methods=['POST'])
def cancel_subscription():
sub = db.get_user_subscription(current_user.email)
if not sub:
return 'No subscription found', 404
response = http_requests.post(
'https://api.paystack.co/subscription/disable',
json={'code': sub['subscription_code'], 'token': sub['email_token']},
headers={
'Authorization': 'Bearer ' + PAYSTACK_SECRET,
'Content-Type': 'application/json',
},
)
data = response.json()
if data.get('status'):
db.update_subscription_status(sub['subscription_code'], 'cancelled')
return redirect('/dashboard')
return 'Cancellation failed', 400
from functools import wraps
def subscription_required(f):
@wraps(f)
def decorated(*args, **kwargs):
sub = db.get_user_subscription(current_user.email)
if not sub or sub['status'] != 'active':
return redirect('/subscribe')
return f(*args, **kwargs)
return decorated
@app.route('/premium')
@subscription_required
def premium_content():
return render_template('premium.html')
@app.route('/api/subscription/status')
def subscription_status():
sub = db.get_user_subscription(current_user.email)
if not sub:
return jsonify({'has_subscription': False})
return jsonify({
'has_subscription': True,
'status': sub['status'],
'plan_code': sub['plan_code'],
'next_payment_date': sub.get('next_payment_date'),
})
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course