Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Subscriptionify Laravel Package

revoltify/subscriptionify

Subscriptionify is a Laravel package for adding subscription plans and feature access to your app. Define plans, features, and usage limits, subscribe Eloquent models, and check access through a clean, flexible API.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package with Composer, publish its migrations/config, and add the package's subscription trait to every model that should own entitlements (typically User, plus any Team/Workspace/Project models you bill against). Run migrations before writing any feature checks — grants and plan features are persisted, not computed from config.

Where to look first:

  1. The Direct Feature Grants docs section. As of 1.0.2 this is framed as a standalone mechanism, not a plan add-on. Read this before the plan docs, because it changes how you model one-off entitlements.
  2. The "Entitlements without a subscription" recipe if you have parent/child records (an account and its projects, an org and its seats).
  3. The API compatibility table in the docs — it tells you which methods work on grants alone and which require a subscription. This is the single most useful page for avoiding the subscriptionInfo() trap.

First use case — gate a feature behind a grant, no plan involved:

$user->grantFeature('beta-dashboard');

$user->featureInfo('beta-dashboard');   // works: resolves the grant directly
$user->allFeatures();                    // 1.0.2+: includes the grant

Then check it at the boundary (middleware, policy, or Blade directive) rather than scattering featureInfo() calls through controllers.

Implementation Patterns

Plan features for the normal path, grants for the exceptions. Keep your priced tiers in subscription plans and reserve grantFeature() for things a plan can't express: comped accounts, beta flags, one-off purchases, per-record overrides. Since 1.0.2 these two sources are unioned in allFeatures(), so you no longer need a parallel "grants only" code path in your UI.

Enumerate with allFeatures(), inspect with featureInfo(). featureInfo($slug) answers "what are the limits for this one feature" and has always resolved grants correctly. allFeatures() answers "what does this subscribable have" and is what you want for pricing tables, admin screens, usage dashboards, and bulk serialization. Before 1.0.2 these two disagreed; use allFeatures() as the source of truth for enumeration and the disagreement disappears.

Mirror a parent's entitlements onto children using the documented recipe: read allFeatures() off the parent, then grant the relevant slugs on the child. Do not reach for subscriptionInfo() here — it returns an empty DTO when the child has no subscription of its own, which is exactly the case the recipe is for.

Let overlap do its job. When a plan and a grant name the same slug, the package resolves it for you: quota is additive, while unit price and reset cycle are overrides. That means a +50 grant on top of a 100 plan feature yields 150, but the grant's unit price (if set) wins rather than summing. Model top-up purchases as grants and let the package combine them; don't compute the total yourself.

Version your serialized DTOs. subscriptionInfo() now delegates to allFeatures(), so its features collection can contain entries it previously omitted. If you return that DTO from a controller or API resource, treat this as a payload change: bump an API version, or map the DTO into your own resource class so package changes don't leak into your contract.

Test at the grant/plan boundary, not just the happy path. The bugs this release fixed were both "works in isolation, wrong in combination." Cover: grant-only subscribable, plan-only subscribable, overlapping plan + grant, and no subscription at all.

Gotchas and Tips

subscriptionInfo() is plan- and billing-shaped. It still returns an empty DTO for a subscribable with no subscription — that is intentional, not a regression. If your model is grant-only, calling it gives you a hollow object that looks like a bug. Use allFeatures() there.

1.0.2 silently widens serialized output. Anything that snapshots, caches, or asserts on the features collection from subscriptionInfo() will change. Audit JSON snapshot tests and any cached entitlement payloads after upgrading; a stale cache entry is indistinguishable from a permissions bug.

Don't sum quotas by hand. Overlapping slugs are deduplicated in allFeatures() — listed once, with the additive limit already applied. If you iterate the collection and add up limits you'll be fine; if you separately query the plan and the grant and add those, you'll double-count in some paths and miss overrides in others.

Ordering is plan-first. Plan ordering is preserved and grant-only slugs are appended. Don't assume alphabetical or insertion order when rendering a feature list; sort explicitly at the presentation layer if order matters.

Overrides are easy to misread. A grant that sets a reset cycle changes the cycle for the combined feature rather than adding a second schedule. If a customer expects "both cycles," that's not what the package models — express it as two distinct slugs.

Invalidate caches on grant changes. grantFeature() and any revoke path mutate persisted state; if you cache allFeatures() output (reasonable for a pricing page), key the cache to the subscribable and bust it on grant/revoke, not just on subscription changes.

Debug from the builder. When a feature is missing or has the wrong limit, drop to FeatureInfoBuilder::buildAll() and inspect what the union produces for that subscribable before blaming your policy layer. It's the single chokepoint both allFeatures() and subscriptionInfo() now share, so a wrong answer there explains every downstream symptom.

Extension point: wrap, don't fork. Shape output for your frontend in your own API resource around the DTO. Overriding the builder couples you to internal resolution rules that just changed once and will change again.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect