Skip to content

Billing (Stripe, per-partner seats)

Billing is charged to the Partner (the MSP account), metered on active seats — machines seen in the last 30 days across all of that partner's clients. Everything is optional: with no Stripe keys set, the billing endpoints report "not configured" and nothing charges.

How it works

  1. A partner admin opens the portal; the Billing panel shows plan, status, active seats, and the billed quantity (max(activeSeats, 1)).
  2. Subscribe calls POST /v1/partner/billing/checkout, which creates a Stripe Checkout session (subscription, one per-seat price × quantity) and redirects to Stripe.
  3. Stripe calls back to POST /v1/stripe/webhook. The Worker verifies the signature (HMAC-SHA256 over t.rawBody with the webhook secret, ±5 min tolerance — unit-tested) and updates the partner's stripe_customer_id, stripe_subscription_id, and status (active / past_due / suspended).
Portal → /checkout → Stripe Checkout → subscription → webhook (verified) → partner.status updated

Configure

# In backend/wrangler.toml [vars]:
#   STRIPE_PRICE_ID = "price_..."   # a per-seat recurring price
# As secrets:
wrangler secret put STRIPE_SECRET_KEY       --config backend/wrangler.toml
wrangler secret put STRIPE_WEBHOOK_SECRET   --config backend/wrangler.toml
wrangler deploy --config backend/wrangler.toml

Then in the Stripe dashboard add a webhook endpoint pointing at https://totlprovision-api.totlcom.com/v1/stripe/webhook, subscribed to checkout.session.completed and customer.subscription.*. Copy its signing secret into STRIPE_WEBHOOK_SECRET.

Invoices

The portal Billing panel has a View invoices button that lists the partner's recent Stripe invoices (number, status, amount, date, and a link to the hosted invoice). Endpoint: GET /v1/partner/billing/invoices.

Seat sync

Checkout sets the subscription quantity once; an hourly Worker cron then reconciles each subscribed partner's Stripe quantity to its live active-seat count (retired machines excluded), so billing tracks usage between checkouts.

Notes

  • Seat enforcement (over-limit warnings) already exists per client via the seat limit; Stripe adds the actual charge at the partner level.
  • The webhook is the only unauthenticated endpoint; it is protected by Stripe signature verification, not a session.