Bonaventure OgetoBy Bonaventure Ogeto|

Build Paystack Subscriptions in Flask

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.

Subscribe Route

@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

Subscription Webhook Handlers

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')

Cancel Subscription

@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

Gate Content Behind Subscription

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')

Subscription Status

@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'),
    })

Ship Payments Faster

Key Takeaways

  • Paystack handles all recurring billing. Create plans once and reuse them.
  • The first payment creates the subscription. No separate subscription API call is needed.
  • Webhook events drive subscription state. Handle subscription.create, charge.success, and invoice.payment_failed.
  • Store subscription_code and email_token for managing subscriptions via the API.
  • Use Flask decorators or before_request hooks to gate content behind active subscriptions.
  • Flask-Login or Flask-JWT-Extended pair well with subscription checks for authenticated access control.

Frequently Asked Questions

How do I create plans in Flask?
You can create plans via the Paystack dashboard or by calling POST https://api.paystack.co/plan with the plan name, amount, and interval. Most teams create plans in the dashboard and store the plan codes in their app config.
Can I offer both monthly and yearly plans?
Yes. Create separate plans with different intervals and amounts. Let the user choose on your pricing page.
What happens when a renewal fails?
Paystack retries the charge. You get an invoice.payment_failed webhook. Update the subscription status to past_due and notify the customer to update their payment method.

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