Skip to content

Phase 7 — Entitlements, product keys and licensing #37

Description

@b5463

Part of Milestone D — Paid Products. Ref: #27

Connect payments and subscriptions to customer access, product keys and licence validation.

Note: Full identity/account system (OIDC, BYOI, product users, seats, devices, integration webhooks) is in Phase 7.5 (#40).


Alex tasks

Tables

  • entitlement_grants
  • entitlement_revocations
  • entitlement_resolution_snapshots
  • effective_entitlements
  • licences
  • licence_activations
  • entitlement_events
  • licence_events
  • licence_signing_keys

Entitlement grant resolver

  • Do not treat a single order or subscription as the entitlement itself
  • Resolver calculates effective entitlement from all grants (orders, subscriptions, manual grants, trials, promotions, compensations)
  • Cancelling one source must not revoke access from another valid source
  • Handles: lifetime purchase + active subscription, manual grace during failed payment, refund of one offer while another valid, upgrade/downgrade overlap, temporary admin grant, complimentary access

Product-key rules

  • High entropy key generation
  • Store hash only, never raw key
  • Show raw key only at creation or through secure redemption flow
  • Never encode email/price/plan into key
  • Support revocation
  • Support replacement
  • Support activation limits
  • Audit every action

APIs

  • POST /api/entitlements/check
  • POST /api/entitlements/admin/grant
  • POST /api/entitlements/admin/revoke
  • POST /api/licensing/redeem
  • POST /api/licensing/activate
  • POST /api/licensing/validate
  • POST /api/licensing/deactivate

Signed licence leases

  • validUntil, offlineUntil, features, subscription state, signature
  • Apps do not fail instantly when SYSTEMS. is unavailable

Licence signing-key management

  • licence_signing_keys table
  • Current and previous signing key support
  • Key ID embedded in every lease
  • Public-key endpoint for app verification (asymmetric — apps verify without holding private secrets)
  • Revocation-list endpoint where needed
  • Emergency key-rotation runbook

Subscription state machine

  • Full 13-state model: active → payment_failed → past_due → grace → suspended → recovered | cancelled | expired | refunded | chargeback | manual_revoked
  • Each state has distinct behaviour (do not just mirror Stripe labels)
  • Grace policy per offer: configurable grace duration, full vs. read-only access during grace
  • Payment recovery and reactivation workflows (Stripe retry, self-serve payment update, admin reactivate, data restoration on reactivation)
  • Do not instantly lock users out after first failed payment

Email fulfilment

  • Purchase confirmation
  • Redemption link
  • Billing portal link
  • Licence activation instructions
  • Support link

Tests

  • One-time purchase creates perpetual entitlement
  • Subscription creates renewable entitlement
  • Duplicate webhook does not duplicate entitlement
  • Refund revokes entitlement
  • Chargeback revokes entitlement
  • Licence key redemption works exactly once
  • Activation limit enforced
  • Validation returns signed lease
  • Expired subscription suspends after grace period
  • SYSTEMS outage still allows cached lease until offlineUntil
  • Two simultaneous grants both active — cancelling one does not remove the other

Tomas tasks

  • Licence redemption UI
  • Admin entitlement grant/revoke UI
  • Customer account area: subscription status, active licences, billing portal link
  • Subscription state display with grace/suspension indicators

Exit gate

  • One internal test product can be bought
  • Entitlement is created automatically after payment
  • Licence can be redeemed
  • Full subscription state machine transitions work end to end
  • Admin can manually grant/revoke access
  • All actions are audited

Rollback

Disable automated fulfilment. Keep manual entitlement grants available.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions