Billing and entitlements

Synchronize subscription state, project purchased access into stable grants, and enforce capabilities without scattering plan-name checks through application code.

Supported approaches

Subscription lifecycle

available

Keep a rebuildable local projection of provider-owned subscription state through idempotent webhooks and reconciliation.

Packages

@playstack/billing@playstack/nest-billing

Frameworks and integrations

Entitlement projection

available

Translate billing, registration, licence, or staff sources into independently replaceable boolean and numeric grants.

Request enforcement

available

Resolve user or account grants behind NestJS guards while keeping authentication and entitlement policy separate.

No-card product trials

available

Project full trial access and a smaller read-only grace set with immutable per-account eligibility.

Billing operations

available

Inspect current state and revision history, surface scheduled changes, and reconcile from provider truth.

Frameworks and integrations

framework

NestJS

Connect portable Playstack capabilities to dependency injection, guards, decorators, request context, workers, and lifecycle hooks.

integration

Prisma

Persist Playstack capabilities through explicit application-owned Prisma clients, transactions, and managed schema fragments.

integration

Stripe

Create hosted subscription and one-time checkout, then reconcile verified Stripe events into separate billing and payment projections.

Package reference

@playstack/billing

Purchases are not authorization checks

Billing answers what an account bought. Entitlements answer what a subject may use now. Keeping that boundary explicit means application routes ask for packages.pro or projects.maximum, not whether a subscription happens to be named pro.

Define stable grants

ts
import { defineEntitlements } from '@playstack/entitlements'

export const entitlements = defineEntitlements({
  'packages.plus': { kind: 'boolean' },
  'packages.pro': { kind: 'boolean' },
  'projects.maximum': { kind: 'number' },
})

The billing projection can replace only the grants sourced from one subscription. Manual access, registration benefits, and future licence grants remain isolated and survive an unrelated billing update.

Treat provider state as authoritative

Checkout redirects do not grant access. The billing service consumes verified provider events, projects their latest object state idempotently, and then replaces the matching entitlement source.

ts
await billing.consumeWebhook(verifiedEvent)

const access = await entitlementService.resolve({
  type: 'account',
  id: request.account.id,
})

access.require('packages.pro')

Reconciliation uses the same path, so replaying current subscription state should be a no-op rather than a second implementation.

Each provider event carries its immutable ID and occurrence time. Exact retries and stale deliveries cannot overwrite newer state, while accepted projections append revision history. Invoice payment, payment failure, cancellation, resumption, scheduled plan changes, and trial-ending events are normalized for notification and audit consumers.

Billing requires an explicit entitlement consistency mode. A shared database can project grants on the billing transaction; separate stores use durable, idempotent eventual projection. resolveMany() and snapshot() support account settings and administration views without one database read per entitlement.

Go

Playstack Pro tag
OriginsPricingBlogNewsletterChangelogStatusRoadmap
ContributorsCommunityIn Use ShowcaseCase StudiesPartnersSponsors
FAQsSupportContact

© 2026 Playstack. All rights reserved.

With OSS
Terms of ServicePrivacy PolicyCookie PolicyImprint

By

Commune Software