Gating a Feature
Using RequireEntitlement to put a resource behind payment.
Putting a resource behind payment is one line: include RequireEntitlement in its controller.
Leaving it out is also one line: don't include it. There's no separate configuration file listing
which features are paid; the controller itself is the declaration.
Gating a controller
class InvoicesController < ApplicationController
include RequireEntitlement
# ...
end
On every action, this checks Billing.for(Current.account).entitled? and redirects to the pricing
page when it's false. The example ProjectsController uses exactly this include; copy it as the
reference when you scaffold a new paid resource with
Adding a Resource.
Who gets through anyway
Admin accounts (User#role == "admin") are exempt from the entitlement check, so an admin can
always reach a paid controller regardless of subscription status. This matters for support and for
console-driven operations: see Roles and Teams for what
admin? covers today.
What this doesn't do
RequireEntitlement answers one question: is this workspace allowed to use the paid surface at
all. It says nothing about which specific records the signed-in user can see or modify within that
surface; that's Pundit's job, and it applies regardless of whether the feature is paid or free. A
controller almost always needs both: include RequireEntitlement at the top, and authorize
@record / policy_scope(...) in each action. See
Authorization for why these stay separate.
Testing the gate
Add an entitlement-gate test alongside the tenant-isolation test in your request spec: an
unsubscribed account hitting a paid controller's action should be redirected to pricing, not shown
the feature and not met with an error. The Project resource's request spec is the reference for
this assertion.
Next
See how to configure the plan the gate checks against: Stripe Setup.