Stripe billing setup¶
InvoicePDFs uses Stripe Checkout to start a subscription and the Stripe
Customer Portal for all ongoing management (upgrade/downgrade, payment method,
invoices, cancellation) — so the app carries the least possible billing burden.
The backend (checkout / portal / webhook) lives in app/api/v1/billing.py and
app/billing/stripe_service.py; the dashboard page is /ui/billing.
One-time setup¶
-
Install the Stripe extra (it's optional so the app runs without it):
-
In the Stripe dashboard
- Create a Product + recurring Price for each paid tier (e.g. Pro, Business).
- Configure the Customer Portal (Billing → Customer portal): enable "update subscriptions" and pick the products customers may switch between.
-
Add a webhook endpoint pointing at
https://<your-host>/api/v1/billing/webhook, subscribed to:checkout.session.completed,customer.subscription.updated,customer.subscription.deleted,invoice.payment_failed. Copy its signing secret. -
Environment variables
-
Wire plans to prices. Each internal
Planrow that should be purchasable needs itsstripe_price_idset to the matching Stripe Price id. Only plans with astripe_price_idappear on the billing page and inGET /api/v1/billing/plans.
Flow¶
- New customer →
/ui/billingshows "Choose a plan" → Subscribe creates a Checkout session (POST /api/v1/billing/checkout-session) and redirects to Stripe. - Existing subscriber → Manage billing opens the Customer Portal
(
POST /api/v1/billing/portal-session). - Webhooks keep
Account.subscription_statusandAccount.plan_idin sync; quota enforcement (enforce_monthly_quota) then uses the resolved plan'smonthly_render_quota.
When STRIPE_SECRET_KEY is unset the billing endpoints return 501 not_configured
and the UI shows a "billing isn't configured" note — nothing else breaks.