Concepts
Entitlements
The account → app → user → entitlement hierarchy
stubkit has four concepts. An account is a customer of stubkit. An app is a mobile or web product that belongs to an account. A user is an end user of that app, identified by a stable external ID the customer controls. An entitlement is a named feature flag gated on subscription state — typically pro.
Why the extra layer
Keeping entitlements distinct from products means you can ship two different product IDs (monthly vs yearly) and have both resolve to the same pro entitlement. Your app only ever checks isActive(userId, 'pro') — it does not care which product was purchased.
Entitlement status values
| Status | Meaning | Grants access? |
|---|---|---|
active | Current billing period is paid and unexpired. | yes |
grace | Billing retry in progress. Typically lasts up to 16 days on iOS, 30 on Android. | yes |
cancelled | User turned off auto-renew but the current period still has time left. | yes (until expires_at) |
expired | Natural end of billing, no retry. Hard loss of access. | no |
refunded | Apple, Google, or Stripe refunded the transaction. | no |
How stubkit decides
Every webhook from Apple, Google, and Stripe — plus every admin grant — flows through an isolated, serialized state engine keyed on (app, user). Concurrent updates for the same user are applied one at a time, and the canonical result is persisted before any API read returns. See state machine for the transition rules.