Skip to main content

Modern, headless payment integration for Flask APIs. Stripe subscriptions + webhooks + plan management in one line.

Project description

Flask-Headless-Payments

Modern, headless payment integration for Flask APIs. Stripe subscriptions + webhooks + plan management in one line.

PyPI version Python 3.8+ License: MIT

✨ Features

  • Drop-in Integration - Add payments to any Flask app in minutes
  • Stripe Checkout - Pre-built checkout sessions with trial support
  • Subscription Management - Upgrade, downgrade, cancel subscriptions
  • Customer Portal - Self-service billing management
  • Webhook Handling - Automatic event processing with extensibility
  • Plan-Based Access Control - Decorators to protect routes by plan
  • Usage Tracking - Built-in metered billing support
  • Headless Architecture - Perfect for SPAs, mobile apps, and APIs
  • Works with flask-headless-auth - Seamless integration with authentication

🚀 Quick Start

Installation

pip install flask-headless-payments

Basic Setup (5 minutes)

from flask import Flask
from flask_headless_auth import AuthSvc
from flask_headless_payments import PaymentSvc

app = Flask(__name__)

# Configure
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['JWT_SECRET_KEY'] = 'your-jwt-secret'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'

# Stripe configuration
app.config['STRIPE_API_KEY'] = 'sk_test_...'
app.config['STRIPE_WEBHOOK_SECRET'] = 'whsec_...'

# Initialize auth (required)
auth = AuthSvc(app)

# Initialize payments
payments = PaymentSvc(
    app,
    plans={
        'free': {
            'name': 'Free',
            'price_id': None,
            'features': ['basic_features'],
            'limits': {'api_calls': 100}
        },
        'pro': {
            'name': 'Pro',
            'price_id': 'price_1234567890',  # From Stripe Dashboard
            'features': ['basic_features', 'advanced_features'],
            'limits': {'api_calls': 1000}
        },
        'enterprise': {
            'name': 'Enterprise',
            'price_id': 'price_0987654321',
            'features': ['basic_features', 'advanced_features', 'premium_features'],
            'limits': {'api_calls': 10000}
        }
    }
)

if __name__ == '__main__':
    app.run()

Your API now has payment endpoints ready at /api/payments/* 🎉

📚 API Endpoints

All endpoints are automatically registered:

Endpoint Method Auth Description
/api/payments/plans GET No Get all available plans
/api/payments/subscription GET Yes Get current user's subscription
/api/payments/checkout POST Yes Create checkout session
/api/payments/portal POST Yes Create customer portal session
/api/payments/cancel POST Yes Cancel subscription
/api/payments/upgrade POST Yes Upgrade/downgrade plan
/api/payments/webhook POST No Stripe webhook handler

🎯 Usage Examples

1. Protect Routes by Plan

from flask_jwt_extended import jwt_required
from flask_headless_payments import requires_plan

@app.route('/api/premium-feature')
@jwt_required()
@requires_plan('pro', 'enterprise')
def premium_feature():
    return {'message': 'Premium content'}

2. Require Any Active Subscription

from flask_headless_payments import requires_active_subscription

@app.route('/api/subscriber-only')
@jwt_required()
@requires_active_subscription
def subscriber_feature():
    return {'message': 'Subscriber content'}

3. Check for Specific Features

from flask_headless_payments import requires_feature

@app.route('/api/advanced-feature')
@jwt_required()
@requires_feature('advanced_features')
def advanced_feature():
    return {'message': 'Advanced feature'}

4. Track Usage (Metered Billing)

from flask_headless_payments import track_usage

@app.route('/api/convert-pdf')
@jwt_required()
@track_usage('pdf_conversion', quantity=1)
def convert_pdf():
    return {'message': 'PDF converted'}

🔧 Configuration

Required Settings

# Stripe credentials
app.config['STRIPE_API_KEY'] = 'sk_test_...'
app.config['STRIPE_WEBHOOK_SECRET'] = 'whsec_...'

Optional Settings

# URL prefix for payment routes (default: '/api/payments')
app.config['PAYMENTSVC_URL_PREFIX'] = '/api/payments'

# Frontend URLs for redirects
app.config['PAYMENTSVC_SUCCESS_URL'] = 'http://localhost:3000/success'
app.config['PAYMENTSVC_CANCEL_URL'] = 'http://localhost:3000/cancel'
app.config['PAYMENTSVC_RETURN_URL'] = 'http://localhost:3000/account'

# Default trial period (days)
app.config['PAYMENTSVC_DEFAULT_TRIAL_DAYS'] = 14

# Default currency
app.config['PAYMENTSVC_DEFAULT_CURRENCY'] = 'usd'

# Feature flags
app.config['PAYMENTSVC_ENABLE_TRIALS'] = True
app.config['PAYMENTSVC_ENABLE_USAGE_TRACKING'] = True

# CORS origins
app.config['PAYMENTSVC_CORS_ORIGINS'] = ['http://localhost:3000']

💡 Frontend Integration

Create Checkout Session

// JavaScript/React example
async function subscribe(planName) {
  const response = await fetch('/api/payments/checkout', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${accessToken}`
    },
    body: JSON.stringify({ plan: planName })
  });
  
  const { url } = await response.json();
  window.location.href = url;  // Redirect to Stripe Checkout
}

Open Customer Portal

async function manageBilling() {
  const response = await fetch('/api/payments/portal', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  });
  
  const { url } = await response.json();
  window.location.href = url;  // Redirect to Stripe Portal
}

Get Current Subscription

async function getSubscription() {
  const response = await fetch('/api/payments/subscription', {
    headers: {
      'Authorization': `Bearer ${accessToken}`
    }
  });
  
  const { subscription } = await response.json();
  console.log(subscription);
  // {
  //   plan_name: 'pro',
  //   plan_status: 'active',
  //   is_subscribed: true,
  //   days_until_renewal: 25,
  //   ...
  // }
}

🔌 Advanced: Custom Models

Extend Your User Model

from flask_sqlalchemy import SQLAlchemy
from flask_headless_payments import SubscriptionMixin

db = SQLAlchemy()

class User(db.Model, SubscriptionMixin):
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(255), unique=True, nullable=False)
    password_hash = db.Column(db.String(1024))
    
    # SubscriptionMixin adds:
    # - stripe_customer_id
    # - stripe_subscription_id
    # - plan_name, plan_status
    # - current_period_start, current_period_end
    # - trial_start, trial_end
    # - is_subscribed(), has_plan(), etc.

# Initialize with custom model
payments = PaymentSvc(app, user_model=User, plans={...})

🪝 Webhooks

Setting Up Webhooks

  1. In Stripe Dashboard:

    • Go to Developers → Webhooks
    • Add endpoint: https://yourdomain.com/api/payments/webhook
    • Select events to listen to (or select all)
    • Copy the webhook signing secret
  2. In Your App:

    app.config['STRIPE_WEBHOOK_SECRET'] = 'whsec_...'
    
  3. Test Locally with Stripe CLI:

    stripe listen --forward-to localhost:5000/api/payments/webhook
    

Custom Webhook Handlers

def custom_invoice_handler(event_data, db, user_model):
    """Custom handler for invoice.paid events."""
    invoice_id = event_data['id']
    customer_id = event_data['customer']
    
    # Your custom logic
    print(f"Invoice {invoice_id} paid by {customer_id}")

# Register custom handler
payments.register_webhook_handler('invoice.paid', custom_invoice_handler)

🎨 Plan Configuration

Define Your Plans

plans = {
    'free': {
        'name': 'Free',
        'price_id': None,  # Free tier has no Stripe price
        'features': ['basic_pdf', 'compress'],
        'limits': {
            'pdf_conversions': 10,
            'storage_mb': 100
        }
    },
    'pro': {
        'name': 'Pro',
        'price_id': 'price_1234567890',  # Monthly price from Stripe
        'features': ['basic_pdf', 'compress', 'merge', 'split'],
        'limits': {
            'pdf_conversions': 100,
            'storage_mb': 1000
        }
    },
    'enterprise': {
        'name': 'Enterprise',
        'price_id': 'price_0987654321',
        'features': ['basic_pdf', 'compress', 'merge', 'split', 'api_access', 'priority_support'],
        'limits': {
            'pdf_conversions': -1,  # Unlimited
            'storage_mb': -1
        }
    }
}

payments = PaymentSvc(app, plans=plans)

Check Features in Code

from flask import current_app

@app.route('/api/check-limit')
@jwt_required()
def check_limit():
    user_id = get_jwt_identity()
    user = User.query.get(user_id)
    
    payment_svc = current_app.extensions['paymentsvc']
    limit = payment_svc.plan_manager.get_plan_limit(user.plan_name, 'pdf_conversions')
    
    return {'limit': limit}

🔒 Security Best Practices

  1. Always use HTTPS in production
  2. Keep your Stripe keys secure (use environment variables)
  3. Verify webhook signatures (handled automatically)
  4. Use JWT authentication with flask-headless-auth
  5. Validate plan changes server-side

🤝 Integration with flask-headless-auth

Flask-Headless-Payments is designed to work seamlessly with flask-headless-auth:

from flask_headless_auth import AuthSvc
from flask_headless_payments import PaymentSvc

# Initialize auth first
auth = AuthSvc(app)

# Payments automatically detects and uses auth's User model
payments = PaymentSvc(app, plans={...})

# Now protect routes with both auth AND plan requirements
@app.route('/api/premium')
@jwt_required()
@requires_plan('pro')
def premium():
    return {'message': 'Premium feature'}

📦 What's Included

Models (with Mixins)

  • Customer - Stripe customer data
  • Payment - Payment transaction records
  • WebhookEvent - Webhook event log
  • UsageRecord - Metered billing usage

Managers

  • SubscriptionManager - Subscription CRUD operations
  • CheckoutManager - Checkout & portal sessions
  • WebhookManager - Webhook event processing
  • PlanManager - Plan configuration & access control

Decorators

  • @requires_plan() - Require specific plan(s)
  • @requires_active_subscription - Require any active subscription
  • @requires_feature() - Require specific feature
  • @track_usage() - Track usage for metered billing

🎓 Examples

Check out the examples/ directory for complete working examples:

  • Basic SaaS - Simple subscription app
  • With flask-headless-auth - Full auth + payments
  • Metered Billing - Usage-based pricing
  • Custom Webhooks - Advanced webhook handling

🐛 Troubleshooting

Issue: "User model does not support subscriptions"

Solution: Add SubscriptionMixin to your User model:

from flask_headless_payments import SubscriptionMixin

class User(db.Model, SubscriptionMixin):
    # ... your fields

Issue: "STRIPE_WEBHOOK_SECRET not configured"

Solution: Set the webhook secret in your config:

app.config['STRIPE_WEBHOOK_SECRET'] = 'whsec_...'

Get it from: Stripe Dashboard → Developers → Webhooks

Issue: "No active subscription found"

Solution: Ensure user has completed checkout and webhook has been processed.

📄 License

MIT License - See LICENSE file for details.

🙏 Credits

Developed with ❤️ by Dhruv Agnihotri

Built with Flask, Stripe Python SDK, and Flask-SQLAlchemy.

Companion to flask-headless-auth.


Made for modern SaaS applications. Deploy with confidence. 💳

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flask_headless_payments-0.2.1.tar.gz (44.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flask_headless_payments-0.2.1-py3-none-any.whl (59.5 kB view details)

Uploaded Python 3

File details

Details for the file flask_headless_payments-0.2.1.tar.gz.

File metadata

  • Download URL: flask_headless_payments-0.2.1.tar.gz
  • Upload date:
  • Size: 44.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for flask_headless_payments-0.2.1.tar.gz
Algorithm Hash digest
SHA256 a30e10252ef65342e4e13d9ac0dcd4fdca1859bfdbe62fa49afa664f85026194
MD5 40333a90a0f8eebee104dfb7641723fc
BLAKE2b-256 d255c36676a8aaaeb9164fa963153d01d1660dcb8c6b97f2e59167593bc2dd4d

See more details on using hashes here.

File details

Details for the file flask_headless_payments-0.2.1-py3-none-any.whl.

File metadata

File hashes

Hashes for flask_headless_payments-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7711570d3737bf77705b50604ca7c18e8f3806258eabef1b60659298500ac8ff
MD5 c141d9e25f67b7ff02783772ca79eb78
BLAKE2b-256 481677878f8670281e810cc1e709174cf890f88e1d49a86165e39e077de470cb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page