No results found.

Plans & Billing

Per-app, per-bundle, per-seat, tiered, multi-currency. Stripe Connect under the hood.

Plans & Billing

Every app you register can sell subscriptions. WolfieAuth handles the entire pricing → checkout → recurring → invoicing pipeline. Money flows to your customer’s Stripe Connect account; the platform takes a 5% fee tracked locally and KSeF-invoiced monthly.

Plan scopes

A plan can be scoped two ways:

  • Per-App (default) — every plan you create lives on one OidcClient. Most apps use this. Different apps in the same org can have wildly different pricing.
  • Per-Bundle (opt-in) — a single plan grants access to every app in an AppBundle. Use for “Premium tier unlocks CRM + Mail + Storage” combos.

Both pull money to the same connected Stripe account (the owning org’s). The choice is purely about catalog organisation.

Pricing models

ModelWhen to useStripe shape
Flat per-orgOne subscription = one org pays one feeunit_amount
Per-seatCharge by member countusage_type=licensed, quantity = active members
Volume tieredFirst 10 seats $99, next 50 $89, etc.billing_scheme=tiered, tiers_mode=volume
Graduated tieredFirst 3 seats free, then $5 eachbilling_scheme=tiered, tiers_mode=graduated
Multi-currencyDifferent price for PLN vs USD vs EURMultiple MembershipPrice rows per plan
Per-countrySpecific countries get different pricescountryCodes: ["PL"] etc.; pricing-page picks via cf-ipcountry

Creating a plan

/admin/clients/<your-app-id>#plans → + Nowy plan:

  1. Name (e.g. “Pro”) — plan slug auto-derives from name with diacritic folding (“Plan Złoty” → plan-zloty)
  2. Description, color, feature flags (free-form strings your app interprets, e.g. ksef_enabled, unlimited_storage)
  3. Click “Add price” → opens modal with full options:
    • Amount (in major units, e.g. 99.00 for 99 PLN)
    • Currency (PLN / USD / EUR / etc.)
    • Interval (month / year / one_time)
    • Per-seat toggle + seat label (seats / developers / vendors)
    • Country codes (["PL"] for Polish-only pricing, empty = global)
    • Volume tiers (table editor, monotonically-increasing brackets)
  4. Save → plan exists in DB. “Sync to Stripe” button → provisions the Stripe Product + Price on your Connect account.

How customers subscribe

The pricing page lives at https://auth.wolfieguard.com/billing/<your-app-clientId> — public, no login required. Customer picks a plan, enters seat count if per-seat, gets routed to Stripe Checkout, comes back on success.

The @wolfieauth/sdk-paywall package can also embed this picker IN your app’s signup flow:

// hooks.server.ts
const auth = createWolfieAuthHandle({
  template: { id: 'guest-request', issuer, clientId, branding: {...} },
  sessionSecret,
  paywall: {
    enabled: true,
    stripePublishableKey: process.env.STRIPE_PUBLISHABLE_KEY,
    plans: await fetch(`${issuer}/api/billing/${clientId}/plans`).then(r => r.json()),
    allowSkip: false, // or true for "free trial → upgrade"
  },
});

Per-seat with auto-bump

Per-seat plans have a quirk: when an org admin invites a new member, the seat count needs to bump up. Otherwise you’ve got 5 paid seats and 7 active members, with #6 and #7 effectively free.

WolfieAuth handles this automatically. On every successful invite:

1. New OrgMembership row inserted
2. checkSeatCapBeforeAdd() runs against the active subscription
3. If new member count > current quantity:
     stripe.subscriptions.update(sub.id, {
       quantity: newCount,
       proration_behavior: "create_prorations",
     })
4. Stripe charges the prorated difference on next invoice

Reverse on revoke — auto-decrement, credit on next invoice. Floor at quantity=1 (you can’t have a 0-seat sub).

Comp overrides (“free pass per user”)

Sometimes you want to comp specific accounts — founding customers, beta testers, employees with personal accounts, partner orgs. WolfieAuth has a per-(user, app) override:

/admin/clients/<app-id>#users → ∞ toggle next to the user

-- AppEntitlementOverride row created
INSERT INTO "AppEntitlementOverride" (
  userId, clientId, planSlug, planName, features, expiresAt
) VALUES (
  '<user-sub>', '<app-id>', 'comp', 'Complimentary',
  ARRAY[]::text[],  -- empty = wildcard, all features
  NULL              -- or a date for time-boxed comp
);

The OIDC claim emitter then synth-injects an ACTIVE plan entry into wolfieauth_plans[]:

{
  "planSlug": "comp",
  "planName": "Complimentary",
  "status": "ACTIVE",
  "appClientId": "wolfie-wolfiecrm",
  "features": [],
  "currentPeriodEnd": null
}

SDK helpers (hasActiveFeature, etc.) treat this exactly like a paid sub. The user can’t tell they’re comped — they get the experience, you don’t get the billing.

Money flow + platform fee

Customer pays $99/month for "Pro" plan on your CRM
Stripe Connect routes $99 to your customer's connected acct (their org)
WolfieAuth tracks $4.95 (5%) as a platform fee row
Monthly aggregator job runs on day-1
Generates a wolfiecrm KSeF invoice for that month's aggregate fee
Customer org pays platform invoice via Stripe / bank transfer

The 5% rate is configurable per-org (override via admin); default is 5%. The KSeF integration uses the wolfiecrm API — see wolfieksef Perfex module if your customer uses Perfex CRM.

wolfieauth-platform itself

WolfieAuth charges customer orgs for using WolfieAuth. The “Pro” plan on the wolfieauth-platform self-client ships with this default shape:

  • 3 seats free, $5/seat thereafter
  • billing_scheme: tiered, tiers_mode: graduated, usage_type: licensed
  • Money goes to the platform Stripe account directly (not Connect — different code path)

Members of the platform-owner org (the wolfie org) automatically get a platform-owner-grant synthetic plan via the OIDC claim emitter — they don’t need to subscribe to themselves.

Continue reading

  • Stripe & test mode — how to test billing end-to-end with fake cards before going live with real money
  • Admin — Plans panel, Comp toggle, Sync to Stripe button
  • SDKs@wolfieauth/sdk-paywall for embedded checkout

Last updated: