Skip to content

Polar-optional Cloud: what 'billing disabled' means #977

Closed

Description

@nick-potts

Question

Parent map: #973. With no POLAR_ACCESS_TOKEN, billing is disabled and every Organization runs unlimited (decided). Decide: which capability checks exist in modules/billing today and what each returns when billing is off; whether plan/upgrade UI is hidden or shown as informational; whether the billing_plan column keeps free/solo/teams or gains an explicit unlimited; what the Inngest billing sync does when off; how the Polar webhook route responds when off.

Activity

  1. self-assigned this
    on Sep 23, 2026
  2. nick-potts commented on Sep 23, 2026

    @nick-potts
    ContributorAuthor

    Resolution

    Grilled 2026-09-23. "Billing disabled" means the self_hosted mode that already exists: no Polar env at all. A Self-hosted Cloud never configures Polar; billing is a Ployz-hosted concern only.

     POLAR_ACCESS_TOKEN + POLAR_WEBHOOK_SECRET + one product ID
                │
        ┌───────┴────────┐
      set               absent
      "hosted"        "self_hosted"            ◄── names unchanged
        │                 │
     one paid plan     no billing
     checkout/portal   plugin absent ─► webhook/checkout/portal 404
     webhook + nightly sync   Inngest billing functions not registered
     Billing page (1 card)    nav item + route hidden
     row per subscriber       no rows, ever
     custom domains gated     custom domains always allowed   ◄── the one meaning of "unlimited"
    

    Answers to the ticket's questions

    • Capability checks in modules/billing today: one, the custom-domain capability, with no live caller. Kept and cut down: allowed when self-hosted, or when the Organization has an active subscription. Judged from the cached organization_billing_state row, never a live Polar call, so a Polar outage cannot block a deploy. One denial reason. Wired at the existing ponytail comment in service-operations.server.ts. This is the only plan-gated capability at 1.0, and it overrides the cleanup spec's "delete custom-domain capability gating".
    • Plan/upgrade UI when off: hidden entirely. No Billing nav item, no route. Not informational.
    • current_plan column: dropped, along with product id, amount, currency, period start, and the hobby/pro aliases. The row keeps organization id, active subscription id, active flag, current period end, and sync timestamps. Self-hosted never writes the table. No unlimited value exists anywhere.
    • Inngest billing sync when off: the sync and nightly reconcile functions are not registered in self-hosted mode. (Today the nightly cron fans out one failing, thrice-retried sync per Organization on every self-hosted install.)
    • Polar webhook route when off: stays 404, which is what the unmounted better-auth plugin already yields. Client-side Polar auth plugin stays, inert.

    Decisions that fell out

    • One purchasable plan at launch. Free and Teams are dropped. The plan's name is unlocked and is UI copy only; nothing server-side stores or names it. A second plan for multi-member Organizations comes later and is not shown in the UI.
    • Free's signup hook goes. Today handleUserCreated creates a $0 Polar subscription inside the better-auth user-create hook, so a Polar outage fails sign-in. Account creation no longer touches Polar.
    • Deleted machinery: plan change, proration preview, change dialog, product price lookup, plan ranking, unknown-product flag, the "active paid" distinction, free/teams product IDs and checkout slugs.
    • Billing page on hosted: one card. "Get Started" opens embedded checkout; once subscribed, "Manage billing" opens the Polar portal, where cancellation happens.

    Follow-ups (execution, not decisions)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions