Feature guide

Premium access and billing

Free/Premium entitlement decisions, checkout, customer portal, confirmation, and Stripe webhook handling.

← Technical overview

What this domain owns

Whether a feature may be opened and how a confirmed purchase changes plan access.

Feature flow diagram

Premium feature
        │
        v
Entitlement check ──free──> Paywall
        │ premium                    │
        v                            v
Open feature              /api/billing/checkout ──> Stripe
                                           │ webhook
                                           v
                                  idempotent billing event ──> plan update
                                           │
                                           v
                                    refreshed session / entitlement

This diagram shows the normal direction of control and data. Follow it before changing a boundary or adding a dependency.

Class and relationship diagram

┌──────────────┐ asks ┌──────────────────┐ checkout ┌─────────┐
│ Feature UI   │──────▶│ Entitlements     │─────────▶│ Stripe  │
└──────────────┘       ├──────────────────┤          └────┬────┘
                       │ + canAccess()    │               │ webhook
                       │ + isPremium()    │               ▼
                       └──────────────────┘       ┌──────────────────┐
                                                  │ Billing service  │
                                                  │ + apply event    │
                                                  └────────┬─────────┘
                                                           ▼
                                                   ┌──────────────┐
                                                   │ ProductPlan  │
                                                   └──────────────┘

Boxes show the important data types or services. Their fields and main methods are listed inside; arrows show the primary dependency or data relationship.

Sequence diagram

Learner
Premium feature
Entitlements
Paywall
Checkout API
Stripe
Webhook API
Billing service
Browser
Session API
request locked content
check plan and lesson
allow or deny
deny with return route (if free)
create checkout session
create session
checkout URL
verified payment event
apply idempotent plan update
plan updated
refresh plan and reopen content

Read this from top to bottom: each arrow is the next interaction in the normal successful path.

Start here in the code

Choose any filename to open it directly in VS Code.

How it works

  1. Feature asks entitlement rule for access.
  2. Free learner sees paywall with return route.
  3. Checkout route creates Stripe session.
  4. Webhook records an idempotent payment event.
  5. Session refresh exposes the new plan.

Read this code

src/contexts/entitlements/public.tsOpen in VS Code — Ask one policy boundary about access

export const entitlements = { check: new CheckEntitlement() };

export function canAccessLesson(
  input: { plan: ProductPlan }, lessonId: string,
) {
  return entitlements.check.canAccessLesson({
    plan: input.plan, lessonId,
  });
}

What each part is doing

  1. UI code asks this function a question; it does not compare plan strings itself.
  2. The application service holds the policy for free previews and Premium access.
  3. Keep checkout confirmation server-side; this function only decides current access.

This is a deliberately short excerpt of the real pattern, with unrelated details omitted. Read the named file for the complete implementation.

Related API and server boundaries

  • POST /api/billing/checkout
  • POST /api/billing/confirm
  • POST /api/billing/portal
  • POST /api/billing/webhook

Tests to read and run

Implementation notes

This is the home for detailed explanations, decision records, request examples, and diagrams for this feature. Add new feature-specific documentation here instead of expanding the platform overview.