Skip to content

Handle free users with Filament and Laravel Spark

Blog  ✺  Laravel
AI generated illustration about the blog post Handle free users with Filament and Laravel Spark

For a SaaS I'm working on, I wanted to allow myself, friends and partners to have forever free accounts. I'm using Laravel Spark for the billing and Filament for the app admin panel.

How to implement free accounts in Laravel Spark

There are multiple ways to do this, all depending on your individual setup. For the app I'm currently working on, here is how I have set it up:


Managing multi-tenancy in Filament

To setup multi-tenancy in Filament, there is excellent documentation to get you started, check it out:

Multi-tenancy - Panel Builder - Filament

To get you up to speed with Filament, I can highly recommend the Filament 3 From Scratch course on Laravel Daily. You need a paid subscription, but I can highly recommend it. Check out the Laravel Daily and Filament Daily channels on YouTube to understand why I highly recommend a premium subscription.


Handling free accounts in Filament multi-tenancy

Let me describe my case in greater detail first. Users can create a project and collect feedback from users. Every project is tied to a website and can have multiple users that manage the collected feedback. The basic structure in Filament looks like this:

My goal is to protect the FeedbackResource by a paywall for paying customers and allow forever free users to access it.

I'm going to show 2 ways to implement this. The first one is only for demo purposes, because at the time I didn't know better. Thankfully the Filament team helped me out on twitter and pointed me into the right direction.

Getting the current tenant in isTenantSubscriptionRequired on the FeedbackResource

⚠️
Don't do it this way! There is a much better way documented below.

I didn't know any better and couldn't use Filament::getTenant() in the static method isTenantSubscriptionRequired(), but I needed the current tenant Project to check if free_forever was set to true .

The following code does work, but it's not ideal. It involves parsing the path from the $request and reading out the tenant id.

I knew it right away, there must be a better way to handle this and the smart folks at Filament sure had something better in store. So I asked on twitter and got pointed in the right direction.

2nd try, extending the VerifySparkBillableIsSubscribed middleware

As pointed out by Filament on Twitter, there is a much better way. Let's see what I came up with implementing this:

The answer is actually to extend `VerifySparkBillableIsSubscribed`, bind the new middleware to the container, and then add in a check before for "free" users. @Filamentphp on Twitter

Let's extend the VerifySparkBillableIsSubscribed middleware. I created a new middleware called VerifyProjectIsBillable and added the following code to it:

While this is much much better already, in my case, I can further simplify this without needing to check the current route in the middleware. Again, this was pointed out by the fantastic Filament team on twitter.

3rd try's the charm, the code used to allow free users and enforce payment for billable users

I think you can still use isTenantSubscriptionRequired on resources/pages without having to manually check the route name. That will remove the middleware for those routes. @Filamentphp on Twitter

Here is the code I'm using in my app. Adjust the resources and pages in Filament that you need to protect by implementing isTenantSubscriptionRequired . Bind the middleware in the AppServiceProvider and optionally implement helpers in your tenant model.

That's it. Feel free to send me suggestions and improvements or questions how to implement it in your case, I can't wait to see what you are building.


Before you go, let me know on twitter if you like this type of content? Feel free to sign up to my newsletter for more.

Comments

You might like

Code Snippet: Remove Jetpack Related Posts from Woo Testimonials

WordPress  ✺  WooCommerce  ✺  Code Snippet

Filament: Get the default Tenant for the User model

Laravel  ✺  Code Snippet