Skip to content

Subscriptions and billing

Trak uses Stripe + dj-stripe for subscriptions. The core pieces are:

  • Stripe billing data (products/prices)
  • Local Stripe models synced via dj-stripe
  • Trak metadata in trak/apps/subscriptions/metadata.py

Setup

  1. Create products/prices in Stripe (test mode for development).
  2. Set Stripe keys in .env:
  3. STRIPE_TEST_PUBLIC_KEY
  4. STRIPE_TEST_SECRET_KEY
  5. STRIPE_LIVE_PUBLIC_KEY
  6. STRIPE_LIVE_SECRET_KEY
  7. STRIPE_LIVE_MODE
  8. Run bootstrap:
python manage.py bootstrap_subscriptions

If you are not using the embedded Stripe pricing table, update ACTIVE_PRODUCTS and ACTIVE_PLAN_INTERVALS in metadata.py with the output from the bootstrap command.

If you are using the embedded pricing table, set STRIPE_PRICING_TABLE_ID.

Pricing table

Embedded Stripe pricing table

  • Configure products and pricing in Stripe.
  • Set the confirmation URL to:
  • http://localhost:8000/subscriptions/confirm/?session_id={CHECKOUT_SESSION_ID} (dev)
  • https://<yourdomain>/subscriptions/confirm/?session_id={CHECKOUT_SESSION_ID} (prod)

Make sure the confirmation URL is applied to every price.

In-app pricing table

Pricing is defined in metadata.py. Re-run bootstrap_subscriptions whenever Stripe products change.

Customer portal

Enable the Stripe Billing Customer Portal in Stripe and ensure webhooks are configured.

Webhooks

Development

Use the Stripe CLI to create a local webhook and forward events:

stripe listen --print-secret
./manage.py bootstrap_dev_webhooks --secret <your_secret>

Then run the stripe listen --forward-to ... command output by the bootstrap command.

Production

Create a webhook endpoint in Django admin (/admin/djstripe/webhookendpoint/add/) and enable at least:

  • checkout.session.completed
  • customer.subscription.updated
  • customer.subscription.deleted

Dj-stripe will sync the endpoint to Stripe.

Per-seat billing

If you use per-seat billing, update the subscription quantity based on your business logic. Teams-based billing updates quantity automatically.

The periodic sync runs via Celery; you can also trigger it with:

python manage.py sync_subscriptions

Troubleshooting

  • Wrong callback domain: update the Django Site and USE_HTTPS_IN_ABSOLUTE_URLS.
  • Embedded pricing table not updating subscriptions: ensure confirmation URLs are set or webhooks are running.
  • Webhook signature errors: verify Stripe keys and DJSTRIPE_WEBHOOK_SECRET.