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.11.tar.gz (45.0 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.11-py3-none-any.whl (60.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: flask_headless_payments-0.2.11.tar.gz
  • Upload date:
  • Size: 45.0 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.11.tar.gz
Algorithm Hash digest
SHA256 bbefd67de8122ab0bd31349a15158fe8a77e0da9e23df3e4d9c52086776da740
MD5 7f86e657c8a23a63468b9c98f9fd02c4
BLAKE2b-256 9ff9d8d894608a269fdb7e682fed06e309f7efd322cd18a12b7f7cdda0aac683

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for flask_headless_payments-0.2.11-py3-none-any.whl
Algorithm Hash digest
SHA256 e44798ec282ffbfae5b8823167a0f65cbf5cd346ad5f51c4cb85cfad963ce49c
MD5 6a66ce2c33b9f0722fb3625c3604c686
BLAKE2b-256 d3088d46cf1a3e0a91e2b32fb2f6138f8c1b50dd1f17abc55c0024b0838e5573

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