litestripe
A fast, user privacy–preserving, lightweight integration for Stripe subscriptions in your Django application.
Litestripe manages fully encapsulated StripeSubscription and OrphanedPayment models for your application's user transactions.
Your application can access these (real-time or otherwise) via Django signals.
Stripe metadata set on payment links is automatically stashed on the StripeSubscription model, including special handling for client_reference_id.
Key points:
- The
StripeSubscriptionobject contains no SPI/PII (or is designed not to). - The
OrphanedPaymentobject contains an email, specifically to enable fixing entitlement for a paying customer if noguidmatch occurs. - Litestripe relies heavily on Stripe itself to handle sensitive information; we just store minimal references for subscription status and correlation.
How it Works
Models:
StripeSubscription: Contains all relevant subscription data, including metadata.
OrphanedPayment: Captures “should never happen” scenarios where a subscription can’t be matched to a user.
Webhook Handling:
Litestripe intercepts Stripe subscription events (e.g. customer.subscription.created, customer.subscription.updated, checkout.session.completed) and updates your subscription records automatically.
Signal Integration:
If you want real-time hooks, you can use standard Django signals on these models (e.g., post_save) to react to subscription changes or orphaned payment events. If you need a deeper, full sync with Stripe (including all objects, invoices, etc.), consider an alternative like dj-stripe. However, if you only need minimal Stripe subscription data in your app, Litestripe keeps it lean and straightforward.
Summary
- Install with pip install git+https://github.com/thomaswilley/litestripe.git.
- Add 'litestripe.apps.LitestripeConfig' to INSTALLED_APPS.
- Set up your environment variables (STRIPE_PUBLIC_KEY, STRIPE_SECRET_KEY, STRIPE_WEBHOOK_UUID, etc.).
- Run migrations to create the Litestripe models.
- (Optional) Use Docker + Stripe CLI for local webhook integration.
- Leverage the StripeSubscription and OrphanedPayment models in your Django signals or views as needed.
Get Started
pip install git+https://github.com/thomaswilley/litestripe.git
In your Django project's settings.py, add:
INSTALLED_APPS = [ ... 'litestripe.apps.LitestripeConfig', ... ]
Then run migrations:
To use the OrphanedPayment and StripeSubscription models, simply import them in your signals, views, or wherever else:
from litestripe.models import OrphanedPayment, StripeSubscription
Configuration
- .env (Environment Variables) Below is a minimal set of environment variables you might include in your .env file:
# Stripe keys STRIPE_PUBLIC_KEY=pk_test_1234567890 STRIPE_SECRET_KEY=sk_test_1234567890 STRIPE_WEBHOOK_UUID=123e4567-e89b-12d3-a456-426655440000 # Litestripe config LITESTRIPE_API_RATE_LIMIT_CACHE_KEY=litestripe_stripe_api_rate_limit LITESTRIPE_MAX_REQUESTS_PER_MINUTE=75
(This example .env would typically live at the root of your project. Adapt as needed for production or local dev.)
- settings.py In your Django settings, make sure you read the environment variables (and optionally fetch the webhook secret from a file for local dev). Example snippet:
import os import re import time # Stripe environment variables STRIPE_PUBLIC_KEY = os.getenv('STRIPE_PUBLIC_KEY') STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY') STRIPE_WEBHOOK_UUID = os.getenv('STRIPE_WEBHOOK_UUID', None) # Litestripe rate-limiting config LITESTRIPE_API_RATE_LIMIT_CACHE_KEY = os.getenv( 'LITESTRIPE_API_RATE_LIMIT_CACHE_KEY', 'litestripe_stripe_api_rate_limit' ) LITESTRIPE_MAX_REQUESTS_PER_MINUTE = int( os.getenv('LITESTRIPE_MAX_REQUESTS_PER_MINUTE', '75') ) # Optionally read the actual Stripe Webhook Secret from Docker or environment STRIPE_WEBHOOK_SECRET = os.getenv('STRIPE_WEBHOOK_SECRET') def get_webhook_secret_from_file(file_path, max_retries=10, delay=2): """ Reads a Stripe webhook secret from a file with retries (useful in local Docker dev). """ secret_pattern = r"whsec_[a-zA-Z0-9]+" for _ in range(max_retries): if os.path.exists(file_path): with open(file_path, 'r') as f: content = f.read() match = re.search(secret_pattern, content) if match: return match.group(0) time.sleep(delay) return None if not STRIPE_WEBHOOK_SECRET: # e.g., Docker container with stripe-cli writing to /run/secrets_djstripe/djstripe_webhook_secret secret_file_path = '/run/secrets_djstripe/djstripe_webhook_secret' STRIPE_WEBHOOK_SECRET = get_webhook_secret_from_file(secret_file_path) or '' if not STRIPE_WEBHOOK_SECRET: raise RuntimeError("Unable to find STRIPE_WEBHOOK_SECRET. Check your configuration.")
Adjust the paths, function logic, or error handling to match your specific use case. Note: If you have a simpler environment (e.g., pure local dev without Docker secrets), you can simply set STRIPE_WEBHOOK_SECRET in your .env.
Local Development with Docker + stripe-cli
The recommended development setup for Litestripe is Docker containerization with the official Stripe CLI forwarding webhooks to your local Django app. You’d typically start it by running:
docker compose --env-file .env.dev -f compose.yml --profile dev up --build -d
Check container logs to ensure it starts properly and that the ephemeral webhook secret is being written to /run/secrets_djstripe/djstripe_webhook_secret.
Example docker-compose.yml Service Snippet
services: stripe-cli-listener: image: stripe/stripe-cli:latest profiles: - dev volumes: - dev-djstripe-whsecret:/run/secrets_djstripe - nginx-webapp-socket:/run/wsgi:rw environment: - STRIPE_API_KEY # or pass in ${STRIPE_SECRET_KEY} from .env - DJANGO_APP_PORT # whichever port your Django app is on - STRIPE_DEVICE_NAME # name your device for reference - STRIPE_WEBHOOK_UUID # a valid UUID4 used to route the webhook path networks: - internal_private entrypoint: /bin/sh -c command: > "export KEY=$$(stripe listen --api-key ${STRIPE_SECRET_KEY} --print-secret) && \ echo $$KEY > /run/secrets_djstripe/djstripe_webhook_secret && \ echo \"Got whsec key [$$KEY] and also saved to file: $$(cat /run/secrets_djstripe/djstripe_webhook_secret)\" && \ stripe listen \ --api-key ${STRIPE_SECRET_KEY} \ --forward-to https://nginx/ls/hook/${STRIPE_WEBHOOK_UUID}/ \ -H 'Host: your-app-local-uri' \ -H \"x-djstripe-webhook-secret: $$KEY\" \ --skip-verify"
Where:
https://nginx/ls/hook/${STRIPE_WEBHOOK_UUID}/ is your Django endpoint (defined by Litestripe’s urls.py).
Volumes and networks are shared to allow your Django container to read the whsec_... secret.
The container prints the ephemeral secret and also writes it to /run/secrets_djstripe/djstripe_webhook_secret.