Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

TheFriendlyHacker's avatar

Free Plan in Spark without requiring a credit card

I want my Spark application to offer a free plan that users will be "subscribed" to by default. Not a trial (aka, so no expiration), and I do not want to require payment information up front.

If users want to have access to "premium" features of the application, they may choose to subscribe to a paid plan.

Does anybody know how to implement something like this?

0 likes
9 replies
QuentinWatt's avatar

Why are you even making a subscription 'plan' at all if the content is free? Surely all you require from the user at that point is an active membership (i.e confirmed email), and not a subscription to a plan.

It sounds like you are making more work for yourself than is actually needed.

How about, don't put those pages behind any kind of gateway check at all. They'll be visible to anyone using your app/website.

TPRAsh's avatar

I am having the same issue where I want to offer a free plan that will have ads for example, and some paid subscriptions that will not have the ads and potentially more features.

Setting the trial number to some large number works but then if the user wanted to subscribe, they would still have the trial days to see out so I am not sure that is best solution.

I have also tried adding free plan stuff that comes out of the box the Spark::freePlan() etc, but that just redirects the user to subscribe so I am not sure what is wrong there.

Bit confused with this one.

brjohnson4's avatar

Me too, but I'm going to just treat most of my routes constrained through middleware as "auth". So if a user is registered but not subscribed, they will be able to access all pages (but with ads). On those same pages, I'll have some checks to see if a user is registered. So like this:

@unless(Auth::user()->subscribed())
    // Show ads
@endunless
nateritter's avatar

I know this is an older post, but figured I'd contribute...

We just left everything the same but added the override to force the trial timestamp field (trial_ends_at) to null.

Example added to the bottom of the booted() method in the SparkServiceProvider.php:

        Spark::createUsersWith(function ($request) {
            $user = Spark::user();
            $data = $request->all();
            $user->forceFill([
                'name' => $data['name'],
                'email' => $data['email'],
                'location' => $data['location'],
                'referral' => ($data['referral'] !== 'undefined' && !empty($data['referral'])) ? $data['referral'] : '',
                'password' => bcrypt($data['password']),
                'last_read_announcements_at' => Carbon::now(),
                'trial_ends_at' => null,
            ])->save();

            return $user;
        });
2 likes
jjgg's avatar

Just to add to @nateritter's reply since it helped me find what I needed, if you want to achieve the same sort of thing but you're using Team Billing, do something like this:

        Spark::swap('TeamRepository@create', function ($user, array $data) {
          $attributes = [
              'owner_id' => $user->id,
              'name' => $data['name'],
              'trial_ends_at' => null,
          ];

          if (Spark::teamsIdentifiedByPath()) {
              $attributes['slug'] = $data['slug'];
          }

          return Spark::team()->forceCreate($attributes);
        });
2 likes

Please or to participate in this conversation.