Setting up subscriptions with Spike

One of the available ways to get paid for your product is recurring subscriptions. Your users will be charged automatically every month or year, giving you a more predictable and stable income, while providing your users with continuous usage.

Once set up, your users will be able to easily subscribe, upgrade, switch to yearly/monthly, cancel and resume subscriptions - all from the Spike billing dashboard.

Now let's cover the basics of configuring your subscription plans.

Displaying the subscription view

The subscription view will show up automatically once there is at least one subscription plan set up. You can either add it via config/spike.php configuration file, or by providing a custom subscirption resolver.

Hiding the subscriptions view

If you would like to hide the subscriptions page completely, set the 'subscriptions' to an empty array:

'subscriptions' => [],

To set up a paid plan, just add the configuration for the plan to the config/spike.php file within the subscriptions key. You can add as many paid plans as you wish.

'subscriptions' => [
        'id' => 'standard',
        'name' => 'Standard',
        'short_description' => 'Great for small businesses',
        'stripe_price_id_monthly' => 'price_xxxxxx1',
        'stripe_price_id_yearly' => 'price_xxxxxx2',
        'price_in_cents_monthly' => 1000,
        'price_in_cents_yearly' => 10000,
        'monthly_credits' => 5000,
        'options' => [
            'bonus_features' => true,

    // other plans...
idyesIdentifier of the subscription plan. Must be unique and not 'free'.
nameyesName of the plan, which will be visible to the user.
stripe_price_id_monthlyyesStripe's price_id for the monthly plan. Learn how to set up Stripe products.
stripe_price_id_yearlyyesStripe's price_id for the yearly plan.Learn how to set up Stripe products.
price_in_cents_monthlyyesPrice of the monthly plan in cents. This will be displayed to the user
price_in_cents_yearlyyesPrice of the yearly plan in cents. This will be displayed to the user
short_descriptionnoA short description of the plan, which will be visible to the user
monthly_creditsnoThe number of credits this plan provides every month. These credits will be reset every month. If the user switches to a different plan/period, the credits will be pro-rated.
optionsnoAn optional, free-form array that will be accessible when retrieving the user's current plan. Useful for checking whether the user's plan has certain features/options available.

You might find it helpful to use underscores in numbers as thousands/hundreds separators to make them more visually understanding.

E.g. 10000 credits could become 10_000 which much more clear it is ten thousand.

And 1000 cents could become 10_00 to indicate 10 dollars.

Free subscription plans

Free subscriptions are included automatically, and they are the default for every user. There are no credit usage limitations on free plans by default, but you can customize that easily.

In order to customize the free plan, include the configuration of the free plan in config/spike.php file with the 'id' => 'free', like so:

'subscriptions' => [
        'id' => 'free',
        'name' => 'Free',
        'short_description' => 'Small amount of credits to try out our product',
        'monthly_credits' => 200,

Archiving old plans

You should not remove any plan configurations that have subscribers already.

Instead, if you would like to remove a particular plan from being available, you can archive it by setting the archived flag to true.

'subscriptions' => [
        'id' => 'standard',
        'name' => 'Standard',
        'archived' => true,    // <== add this line
        // other attributes...

How to set up Stripe products

  1. Log in to your Stripe dashboard.
  2. Go to products.
  3. Click on Add product.
  4. Enter the name & description for the product.
  5. For the price, select the Standard pricing model, enter the price of the product, and select the One time price option. Make sure the currency is the same as you've set up in Spike.
  6. Click on Save product in the top-right corner.
  7. You will be redirected to the product's info page, and you will find the price ID in the Pricing section:
hey there!
  1. Copy the price ID into the stripe_price_id configuration inside config/spike.php.

Verifying Stripe product setup

Spike has a helper command to help you verify whether your configured products and subscriptions have the correct associated stripe_price_ids configured. Just run this command:

php artisan spike:verify

Stripe webhooks

Although, for the most part, Spike works without setting up Stripe webhooks, there are certain edge cases where the webhooks are necessary, such as being notified from Stripe about failed payments.

In order to keep Spike in sync with Stripe, you should go to Stripe's Webhook dashboard to configure these webhooks to be sent to your application's /stripe/webhook URI. You should enable webhooks for the following events:

  • customer.subscription.created
  • customer.subscription.deleted
  • customer.subscription.updated
  • customer.updated
  • customer.deleted
  • invoice.payment_action_required

Because Spike is built on top of Laravel Cashier, you have access to a convenience cashier:webhook Artisan command. This command will create a webhook in Stripe that listens to all of the events required by Cashier:

php artisan cashier:webhook

By default, the created webhook will point to the URL defined by the APP_URL environment variable and the cashier.webhook route that is included with Cashier. You may provide the --url option when invoking the command if you would like to use a different URL:

php artisan cashier:webhook --url ""

The webhook that is created will use the Stripe API version that your version of Cashier is compatible with. If you would like to use a different Stripe version, you may provide the --api-version option:

php artisan cashier:webhook --api-version="2019-12-03"

After creation, the webhook will be immediately active. If you wish to create the webhook but have it disabled until you're ready, you may provide the --disabled option when invoking the command:

php artisan cashier:webhook --disabled

Webhooks & CSRF protection

Since Stripe webhooks need to bypass Laravel's CSRF protection, be sure to list the URI as an exception in your application's App\Http\Middleware\VerifyCsrfToken middleware or list the route outside of the web middleware group:

protected $except = [

Verifying webhook signatures

To secure your webhooks, you may use Stripe's webhook signatures. For convenience, Spike uses Cashier and automatically includes a middleware which validates that the incoming Stripe webhook request is valid.

To enable webhook verification, ensure that the STRIPE_WEBHOOK_SECRET environment variable is set in your application's .env file. The webhook secret may be retrieved from your Stripe account dashboard.

Credit distribution with subscriptions

The credits are automatically added to the user's balance whenever the subscription is activated for the first time. On subsequent renewals, and especially for yearly plans, the credits have to be added in the background.

For this reason, you should schedule the spike:renew-subscription-credits Artisan command to be run at least once a day. Ideally, it would go into your App\Console\Kernel class:

class Kernel extends ConsoleKernel
     * Define the application's command schedule.
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
    protected function schedule(Schedule $schedule)

How Spike handles subscriptions

Below is a short summary of what will happen in various scenarios when handling subscriptions.

New subscriptions

Spike will set up a new subscription and immediately give the user the credits as configured with monthly_credits.

It will fire a SubscriptionActivated event with the new plan.

Switching from monthly to yearly

Switching from a monthly to a yearly (or vice versa) is considered a new subscription. The existing subscription will be replaced with the new one, and both the price paid and the credits given will be prorated.

It will fire a SubscriptionDeactivated event with the old plan and a SubscriptionActivated event with the new plan.

An example of prorating:

A user has subscribed to a $30 monthly plan and was given 3,000 credits per month, which is ~100 credits per day prorated. 10 days after being on a monthly plan, the user decides to upgrade to a yearly plan for $300.

The user is charged immediately for the full year ($300) minus a refund for any unused time from the monthly plan ($20). The 3,000 credits they were given are also prorated to 1,000 and expired immediately (they belonged to an old subscription that is no longer active). Because a new, yearly subscription is set up, the user is immediately provided with the configured 3,000 credits again.

Switching from one plan to another

If you have multiple subscription plans with different amount of monthly credits available, then switching between these plans will work exactly like switching between time periods. The existing cost & credits will be prorated and the user will be put on the new plan and charged immediately.

See above for an example of how prorating works in Spike.

It will fire a SubscriptionDeactivated event with the old plan and a SubscriptionActivated event with the new plan.


When a user cancels the subscription, it enters the "grace period" - the user can continue using the benefits of the plan, including the allocated credits, until the end of the billing period. At the end of billing period, the user's subscription will no longer renew and the credits will not be renewed.

It will fire a SubscriptionCancelled event with the paid plan that's being cancelled.

Resuming subscriptions

When users are on the "grace period" of their subscription, they can easily resume those subscriptions, and they will be automatically renewed on their next billing cycle.

It will fire a SubscriptionResumed event with the paid plan that's being resumed.


If you have any questions, feedback, or need any help setting up Spike within your project, feel free to reach out to me.