Extend Larvel's Cashier to use our own Subscription model

posted by 1 year ago and updated 1 year ago

We love Laravel! It is our go to framework for php development projects. At times though we run into trouble using it.

This time we wanted to change the way Laravel\Cashier was working. We wanted to extend the Subscription model. and have Laravel/Cashier use that model.

Why Extend the Subscription Model of Cashier?

Because we wanted to know on our platform which period the subscriptions covers. When did it start? When does it end?

That was not enough though, we had to also override the create() method from SubscriptionBuilder to save Stripe’s response to our new Model without calling Stipe again.

Because Cashier is a vendor package, it is not advisable to hack the vendor files. The reason is because next time there is an update the package all your changes will be lost.

Where to begin? Let’s make a migration

Let’s start with the migration. Create a migration in the terminal

php artisan make:migration alter_subscriptions_table_add_subscription_period

Now edit the migration to add the two columns for the start and end period.

/database/migrations/2017_11_27_130110_alter_subscriptions_table_add_subscription_period.php

We trust you know how to drop the columns. We have included only the up() method in our code.

 public function up()
    {
        Schema::table('subscriptions', function ($table) {
            $table->timestamp('current_period_start')->nullable();
            $table->timestamp('current_period_end')->nullable();
        });
    }

Create Subscription Model

Now we can create our CustomSubscription model to replace Casier’s Subscription model. Head to the terminal and type:

php artisan make:model CustomSubscription

Amend the CustomSubscription class to suite your needs. Such as the namespace. We are using our own name spacing and we also have composer to autoload our folder (app\Underthecocotree\Cashier.

We are extending the Laravel\Cashier\Subscription model that way all the functions will work without changing anything.

We are trying to keep changes to a minimum in order to not break the functionality in case of an update. We extend and only update what we need.

<?php

namespace Underthecocotree\Cashier;

use Laravel\Cashier\Subscription;

class CustomSubscription extends Subscription
{

    /**
     * The subscription class already includes the $dates variable, so we are reusing it and adding our custom columns
     */
    protected $dates = [
        'trial_ends_at', 'ends_at',
        'created_at', 'updated_at',
        'current_period_start', 'current_period_end',
    ];

    /**
     * Set the subscription table otherwise it will point to another table
     */
    protected $table = 'subscriptions';
}

How to swap the Subscription class for CustomSubscription

To update the referenced class of Subscription we need to replace the \Laravel\Cashier\Bilalble class as well. To do that we need to create a trait.

Under the same directory as the CustomSubscription create a CustomBillable.php file. In our case it is under app/Underthecocotree\Cashier\CustomBillable.php

Our CustomBillable trait uses Billable . A trait function can be redefined by just creating that method. We redefine subscriptions() method and pass in the CustomSubscription class that we created earlier.

<?php

namespace Underthecocotree\Cashier;

use Laravel\Cashier\Billable;
use Underthecocotree\Cashier\CustomSubscription;

trait CustomBillable
{
    use Billable;

    /**
     * Override the subscriptions() from Larave\Cashier\Billable
     * to inject CustomSubscription model
     */
    public function subscriptions()
    {
        return $this->hasMany(CustomSubscription::class, $this->getForeignKey())->orderBy('created_at', 'desc');
    }

}

We know this tutorial is getting long but bear with us. It is all worth it if you want to change Cashier’s Subscription model.

Update User.php to reference CustomBillable

As part of the Cashier installation, the User.php class needs to use the Billable trait. Well, guess what? We now need to change that to point to our own CustomBillable trait.

Open up User.php and amend it to use the CustomBillable trait

use Underthecocotree\Cashier\CustomBillable;

class User extends Authenticatable
{
    use Notifiable, CustomBillable;

Cool, we hope you are still with us. Now our User uses the CustomBillable trait which loads the CustomSubscription model.

Add the subscription duration to our Subscription table

Like we said earlier, we did not get into all this trouble just for fun. We want to save the start and end date to the database.

We got to make one last class. Create a CustomSubscriptionBuilder that extends Larvel\Cashier\SubscriptionBuilder under the same folder (app/Underthecocotree\Cashier\CustomBillable.php). We now override the create() method.

We have omitted most of the code that comes in that function to make this tutorial easier to follow. You should copy the whole function and amend it for your needs.

<?php

namespace Underthecocotree\Cashier;

use Laravel\Cashier\SubscriptionBuilder;

class CustomSubscriptionBuilder extends SubscriptionBuilder
{

    public function create($token = null, array $options = [])
    {
        // ...

        return $this->owner->subscriptions()->create([

            // ...

            'current_period_start' => $subscription->current_period_start,
            'current_period_end'   => $subscription->current_period_end,
        ]);
    }
}

Conclusion

So, when we first started looking into this we were frustrated. We thought there must be a way to do this. We don’t think that it is that difficult but with all the code you might feel a bit lost.

Let’s simplify it for you. You need two classes (CustomSubscription, CustomSubscriptionBuilder) and one trait (CustomBillable). Then you need to update the User to use CustomBillable instead of Cashier’s Billable

This will work for every new subscription but when there is a new charge for the next subscription period the start and end date will remain to the original value.

We go under our tree now and go work it out. Stay tuned to figure out how to update the start and end date of the subscription. We are thinking web hooks but we might need to look more at the Cashier’s code.


Want to read more? Follow these links.
Laravel Cashier - Laravel - The PHP Framework For Web Artisans
Kudos @david-navarro for opening our eyes