Laravel SaaS: Free Trial Implementation

Laravel is a good framework to create "software-as-a-service" solutions - with monthly payments by users. Usually, it should incorporate some kind of Free Trial mechanism. In this article, will show you my version of how to do it in Laravel 5.8.

What are we creating here

  • We take default Laravel app, run artisan make:auth for default registration form;
  • Will add a new timestamp field users.trial_until and will fill it by default with 14 days after registration;
  • Will make that 14 days configurable in config/app.php - you never know when business owner change their mind on the duration;
  • Show header text of how many free trial days are left, with link to upgrade to premium plan; (we won't cover the upgrade or payments in this article, only Free Trial);
  • Check if free trial is over, if so - show a Bootstrap modal which cannot be turned off, so users would click to upgrade if they want to continue.

Ok, so the plan is clear, let's move on.


Step 1. Users.trial_until field

We run this:

php artisan make:migration add_trial_until_field_to_users_table

Migration code:

Schema::table('users', function (Blueprint $table) {
    $table->timestamp('trial_until')->nullable();
});

Then we need to add this field to $fillable array in app/User.php:

class User extends Authenticatable
{
    use Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password', 'trial_until'
    ];

Finally, we need to automatically fill it in on new user registration. So we add it to the array of app/Http/Controllers/Auth/RegisterController.php:

/**
 * Create a new user instance after a valid registration.
 *
 * @param  array  $data
 * @return \App\User
 */
protected function create(array $data)
{
    return User::create([
        'name' => $data['name'],
        'email' => $data['email'],
        'password' => Hash::make($data['password']),
        'trial_until' => now()->addDays(config('app.free_trial_days')),
    ]);
}

As you can see, we're using a configurable value config('app.free_trial_days'), so let's add the value at the end of config/app.php file:

    'aliases' => [

        'App' => Illuminate\Support\Facades\App::class,
        'Arr' => Illuminate\Support\Arr::class,
        // ...
        'View' => Illuminate\Support\Facades\View::class,

    ],

    'free_trial_days' => 14,

];

Step 2. Header Text and Upgrade Link

To show, how many days of free trial are left, we need to calculate that on every page. It would be convenient to have that as an attribute of app/User.php model, so we will use accessor method.

public function getFreeTrialDaysLeftAttribute()
{
    // Future field that will be implemented after payments
    if ($this->plan_until) {
        return 0;
    }

    return now()->diffInDays($this->trial_until, false);
}

Now we will access this value by calling auth()->user()->free_trial_days_left from wherever in our code.

One thing - to use Carbon operations with $this->trial_until field, we need to add it to app/User.php array of $dates:

protected $dates = [
    'trial_until'
];

And then we go into our header which is located at resources/views/layouts/app.blade.php and let's add another <li> item with info and link - see block after @else directive:

@guest
    <li class="nav-item">
        <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
    </li>
    @if (Route::has('register'))
        <li class="nav-item">
            <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
        </li>
    @endif
@else
    @if (auth()->user()->free_trial_days_left > 0)
    <li class="nav-item">
        <a class="nav-link" href="/upgrade">{{ auth()->user()->free_trial_days_left }} Days of Free Trial left. Upgrade now.</a>
    </li>
    @endif
    <li class="nav-item dropdown">
        <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
            {{ Auth::user()->name }} <span class="caret"></span>
        </a>

For now, we won't implement that /upgrade link, it's outside of the scope of this article. But we do show the days amount.


Step 3. Modal After Free Trial is Over

We will add one more @if-@else in the same file, at the end of resources/views/layouts/app.blade.php:

@if (auth()->check() && auth()->user()->free_trial_days_left < 0)
    <!-- Modal -->
    <div class="modal NO-fade"
         tabindex="-1" role="dialog" style="display: block">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h3 class="modal-title">Upgrade Plan</h3>
                </div>
                <div class="modal-body">
                    <div class="alert alert-danger">
                        Your Free Trial is over. Please choose plan to continue.
                    </div>
                    <div class="row">
                        <div class="text-center col-md-6 offset-md-3">
                            <h4>Unlimited Plan</h4>
                            <b>$199.99 per year</b>
                            <hr />
                            - <b>Unlimited</b> AdminPanels<br />
                            - All CRUDs and Modules<br />
                            - Unlimited Functions<br />
                            - <b>Priority</b> Live-chat Support
                            <hr />
                            <a href="/upgrade" class="btn btn-lg btn-primary">Pay $199.99</a>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endif

</body>
</html>

Visual result:

I've taken the text example from our own QuickAdminPanel.com similar popup, you can make it more customizable and take the texts from the database or config.

So that's it about free trial. If this article gets traction, I will probably continue this SaaS series with payments, plans, invoices etc. So shoot in the comments.

No comments or questions yet...

Like our articles?

Become a Premium Member for $129/year or $29/month
What else you will get:
  • 22 courses (477 lessons, total 38 h 20 min)
  • 2 long-form tutorials (one new every week)
  • access to project repositories
  • access to private Discord