How to configure Laravel Cashier with multiple models
Tags: laravel, cashier, stripe
Published: 2021-01-02 17:49:15

---
title: How to configure Laravel Cashier with multiple models
published: true
description: Guide on how to configure Laravel Cashier with multiple models.
tags: laravel, cashier, stripe
canonical_url: https://swapnil.dev/blog/how-to-configure-laravel-cashier-with-multiple-models/
---

I recently worked on a [Laravel](https://laravel.com) project which had the requirement of two authenticable models along with separate subscriptions. The project of course was using [Laravel Cashier](https://laravel.com/docs/8.x/billing) to manage user's subscriptions.

By default, Laravel Cashier assumes the `App\Model\User` class as a Billable model. We can configure it to use a different model, but in our case, there were two different models. So, I had to follow a different approach.

```bash
CASHIER_MODEL=App\Models\User
```

PS: This is going to be a long tutorial! I am going to explain everything from creating models, updating migrations, configuring webhooks, etc.

But if you are in hurry, here is your solution, the trick is to set Cashier's billable model at the runtime using the `config` helper function.

```php
config(['cashier.model' => 'App\Models\Seller']);
```

## Initial Setup

Let's start by assuming that our application has two billable models, a `User`, and `Seller`. Both models will have subscriptions. There can be multiple ways to use Cashier with multiple models, but for simplicity, we are going to store the details of their subscription in separate tables.

Let's start by installing the Cashier package for Stripe first.

```bash
composer require laravel/cashier
```

The cashier will use `subscriptions` and `subscription_items` tables to store information about the user's subscriptions. Let's publish Cashier's default migrations so that we can take over a look at the table structure.

```bash
php artisan vendor:publish --tag="cashier-migrations"
```

Now we should have the following files in our `database/migrations` directory.

1. `2019_05_03_000001_create_customer_columns.php`
2. `2019_05_03_000002_create_subscriptions_table.php`
3. `2019_05_03_000003_create_subscription_items_table.php`

These files contain schema information about the subscriptions table. Don't worry about them, we will come to these files later.

### The `User` model setup

First, let's set up our first billable model, `User` with Cashier. Add `Billable` trait to our first billable model which at `App\Models\User`.

```php
use Laravel\Cashier\Billable;

class User extends Authenticatable
{
    use Billable;
}
```

This user model is going to have its subscription information stored in the `subscriptions` table.

Next, let's create our second billable model & add migrations for it.

### The `Seller` model setup

Our `Seller` model is going to have subscriptions like the `User` model. But, we need to set up a few more things than just adding a `Billable` trait to our model. We will need to add migrations, configure auth guard, etc. for the `Seller` model.

Along with the `Seller` model, we will create two more models, `SellerSubscription` & `SellerSubscriptionItem`.  The `SellerSubscription` will hold the subscription information for the `Seller` model and the `SellerSubscriptionItem` model will be responsible for holding [Multiplan Subscriptions](https://stripe.com/docs/billing/subscriptions/multiplan).

In short, we are going to need the following models & tables for our `Seller` model.

1. The `Seller` model with `sellers` table.
2. The `SellerSubscription` model with `seller_subscriptions` table.
3. The `SellerSubscriptionItem` model with `seller_subscription_items` table.

Let's start by generating our model using the following artisan command. Also, generate model & migrations files by adding the `-m` flag to our command.

```bash
php artisan make:model Seller -m
```

It should generate these two files at the following locations.

1. **`Seller.php`** (In `/app/models` directory) - The Seller model
2. **`2021_XX_XX_XXXXXX_create_sellers_table.php`** (In `/database/migrations` directory) - The migration file

Now, let's set up our `Seller` model. Just like the `User` model we need to add the `Billable` trait to our `Seller` model sitting at `App\Models\Seller`.

```php
use Laravel\Cashier\Billable;

class Seller extends Authenticatable
{
    use Billable;
}
```

In the migration file (**`2021_XX_XX_XXXXXX_create_sellers_table.php`**) for creating the `sellers` table, add the following schema content. We will also bring columns we got from  ``2019_05_03_000001_create_customer_columns.php`` after publishing Cashier's default migrations.

```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateSellersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('sellers', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();

            // Stripe Cashier's columns
            $table->string('stripe_id')->nullable()->index();
            $table->string('card_brand')->nullable();
            $table->string('card_last_four', 4)->nullable();
            $table->timestamp('trial_ends_at')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('sellers');
    }
}
```

We are almost finished with our `Seller` model. But we still need to add seller specific subscriptions model and migration.

### The `SellerSubscription` model setup

By default, subscription information for model `App\Models\User` will be stored in the `subscriptions` table. By using the `Billable` trait we are instructing Laravel, the `User` model will have a `hasMany` relation with the `Laravel\Cashier\Subscription` model. We can confirm that by the `ManagesSubscriptions` trait.

Here in the `Laravel\Cashier\Concerns\ManagesSubscriptions` trait, we can see the `subscriptions` method, which defines `hasMany` relation with the `Laravel\Cashier\Subscription` model.

```php
use Laravel\Cashier\Subscription;

public function subscriptions()
{
    return $this->hasMany(Subscription::class, $this->getForeignKey())->orderBy('created_at', 'desc');
}
```

So, we are going to create a class called `SellerSubscription` which extends the `Laravel\Cashier\Subscription` model & inherits its properties.

In your console run the following command to generate the `SellerSubscription` model & migration.

```bash
php artisan make:model SellerSubscription -m
```

This will generate the following files.

1. **`SellerSubscription.php`** (In `/app/models` directory)
2. **`2021_XX_XX_XXXXXX_create_seller_subscriptions_table.php`** (In `/database/migrations` directory)

### The `SellerSubscriptionItem` model setup

Our `User` model has [Multiplan Subscriptions](https://stripe.com/docs/billing/subscriptions/multiplan) stored in the `subscription_items` table. Then why should we leave the `Seller` model behind? Let's add multi-plan subscriptions functionality to the `Seller` model by defining a new model called `SellerSubscriptionItem`.

Let's generate the `SellerSubscriptionItem` model along with migration by running the following command in the terminal.

```bash
php artisan make:model SellerSubscriptionItem -m
```

This command should generate the following files.

1. **`SellerSubscriptionItem.php`** (In `/app/models` directory)
2. **`2021_XX_XX_XXXXXX_create_seller_subscription_items_table.php`** (In `/database/migrations` directory)

Next, modify the `SellerSubscription` class slightly to extend `Laravel\Cashier\Subscription` class. And also define `belongsTo` relation with the `Seller` class as well as `hasMany` relation with the `SellerSubscriptionItem` class.

```php
<?php

namespace App\Models;

use Laravel\Cashier\Subscription;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class SellerSubscription extends Subscription
{
    use HasFactory;

    public function owner()
    {
        return $this->belongsTo(Seller::class);
    }

    public function items()
    {
        return $this->hasMany(SellerSubscriptionItem::class);
    }
}
```

Next, also modify `SellerSubscriptionItem` class to extend `Laravel\Cashier\SubscriptionItem` class. And define `belongsTo` relation with the `SellerSubscription` class like this.

```php
<?php

namespace App\Models;

use Laravel\Cashier\SubscriptionItem;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class SellerSubscriptionItem extends SubscriptionItem
{
    use HasFactory;

    public function subscription()
    {
        return $this->belongsTo(SellerSubscription::class);
    }
}
```

Now, it's time to take inspiration from Cashier's default migration, and update migrations for `seller_subscriptions` and `seller_subscription_items` accordingly.

Update migration **`2021_XX_XX_XXXXXX_create_seller_subscriptions_table.php`** for `seller_subscriptions` table with the following schema structure. Pay attention to referencing key `seller_id` & modify it according to your custom model.

```php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateSellerSubscriptionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('seller_subscriptions', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('seller_id');
            $table->string('name');
            $table->string('stripe_id');
            $table->string('stripe_status');
            $table->string('stripe_plan')->nullable();
            $table->integer('quantity')->nullable();
            $table->timestamp('trial_ends_at')->nullable();
            $table->timestamp('ends_at')->nullable();
            $table->timestamps();

            $table->index(['seller_id', 'stripe_status']);
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('seller_subscriptions');
    }
}
```

Next, also update migration **`2021_XX_XX_XXXXXX_create_seller_subscription_items_table.php`** for `seller_subscription_items` with the following schema structure. And also pay attention to referencing key `seller_subscription_id` & modify it according to your custom subscription item model.

```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateSellerSubscriptionItemsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('seller_subscription_items', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('seller_subscription_id');
            $table->string('stripe_id')->index();
            $table->string('stripe_plan');
            $table->integer('quantity');
            $table->timestamps();

            // Short key name to support 64 character limit- http://dev.mysql.com/doc/refman/5.5/en/identifiers.html
            $table->unique(['seller_subscription_id', 'stripe_plan'], 'seller_subscription_id_stripe_plan_unique');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('seller_subscription_items');
    }
}
```

After defining the `SellerSubscription` and `SellerSubscriptionItem` models, define a `hasMany` relation by adding the `subscriptions` method on the `Seller` class.

And finally, run the migration command to create/update tables in the database.

```php
php artisan migrate
```

### The `Seller` model

Now, modify the `Seller` model to override the `subscriptions` relation coming from the `Billable` trait. Instead of defining a relationship between the `Laravel\Cashier\Subscription` class, define it with `App\Models\SellerSubscription`.

Your finished seller model should look like this.

```php
<?php

namespace App\Models;

use Laravel\Cashier\Billable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Seller extends Model
{
    use HasFactory, Billable;

    public function subscriptions()
    {
        return $this->hasMany(SellerSubscription::class)->orderBy('created_at', 'desc');
    }
}
```

Next, let's set up Stripe webhooks for the `Seller` model. By default, the cashier will use the `/stripe/webhook` route to handle Stripe webhooks for the default configured model.

### Webhooks for the `Seller` model.

Stripe can notify your application in case the customer's payment method declined and many such events. We need to ensure that our application is handling it. The Cashier package makes it easy by using the `/stripe/webhook` route to handle these events.

By default, it handles these events for the default configured model `App\Models\User`. In our case, we should register a different route the handle Stripe webhooks for our custom model.

 First, generate a controller called `SellerWebhookController` using artisan command.

```bash
php artisan make:controller SellerWebhookController
```

Next, update `SellerWebhookController` by extending `Laravel\Cashier\Http\Controllers\WebhookController` class. And, also modify the `getUserByStripeId` method to use our custom model `Seller`.

```php
<?php

namespace App\Http\Controllers;

use App\Models\Seller;
use Laravel\Cashier\Http\Controllers\WebhookController;

class SellerWebhookController extends WebhookController
{
    protected function getUserByStripeId($stripeId)
    {
        return Seller::where('stripe_id', $stripeId)->first();
    }
}
```

After controller, register a new route SellerWebhookController to handle webhook events coming from the Stripe.

```php
use App\Http\Controllers\SellerWebhookController;

// Route for handling Stripe events
Route::post('/stripe/seller/webhook', [SellerWebhookController::class, 'handleWebhook']);
```

Next, in your Stripe control panel, you should enable the following webhooks for URL `https://{yourapplication.com}/stripe/seller/webhook`.

- `customer.subscription.updated` - When subscription is updated
- `customer.subscription.deleted` - When subscription is cancelled/deleted.
- `customer.updated` - When customer's information updated.
- `customer.deleted` - When a customer is deleted.
- `invoice.payment_action_required` - When action is required for the payment method. Typically when the customer's card is declined.

That's it our application is now ready to handle Stripe webhooks for the custom billable model `Seller`.

## Configure auth guards & providers

In our application, sellers can authenticate themselves. So it makes sense to configure the `Seller` model to take advantage of Laravel's authentication functionality.

Start by registering a new User provider in the `providers` array in `config/auth.php` for our billable model `Seller` and also register a new guard in the `guards` array in `config/auth.php` file.

```php
'guards' => [
        // ...

        'seller' => [
            'driver' => 'session',
            'provider' => 'sellers',
        ],
	// ...
],

// ...

'providers' => [
        // ...

        'sellers' => [
            'driver' => 'eloquent',
            'model' => App\Models\Seller::class,
        ],
	// ...
]
```

After this, we should able to retrieve our authenticated seller by using the `$request->user('seller')` helper.

## Uses

With all the setup, we are finally ready to use our `Seller` model with the Cashier. Now we should be able to retrieve our authenticated seller using `$request->user('seller')` and override Cashier's default model using `config` helper.

```php
// retrieve authenticated seller
$seller = $request->user('seller');

// override cashiers default model at the runtime
config(['cashier.model' => 'App\Models\Seller']);
```

Let's see it, how we can use it in actual code.

### Creating a new Subscription

When creating a new subscription for our custom model `Seller`, retrieve it first by using the `$request->user('seller')` helper. And then set the cashier billable model to `Seller` at runtime using `config` helper.

```php
use Illuminate\Http\Request;

Route::post('/seller/subscribe', function (Request $request) {
    config(['cashier.model' => 'App\Models\Seller']);

    $request->user('seller')->newSubscription(
        'default', 'price_premium'
    )->create($request->paymentMethodId);

    // ...
});
```

### Retrieving Stripe Customer

When we need to retrieve a customer using Stripe ID, we should use `Cashier::findBillable()`. But before that don't forget to set Cashier's billable model using `config` helper.

```php
use Laravel\Cashier\Cashier;

config(['cashier.model' => 'App\Models\Seller']);

$seller = Cashier::findBillable($stripeId);
```

### Stripe Billing Portal

If you are using Stripe's [billing portal](https://stripe.com/docs/billing/subscriptions/customer-portal) in your application, you can redirect our custom `Seller` model to his billing portal like this.

```php
use Illuminate\Http\Request;

Route::get('/billing-portal', function (Request $request) {
    // Override cashier's default model in case
    config(['cashier.model' => 'App\Models\Seller']);

    // Retrive authenticated seller
    $seller = $request->user('seller');

    return $seller->redirectToBillingPortal();
});
```

That's it! These are few ways to use multiple models with the Cashier package. You can find more information about the Cashier in the [documentation](https://laravel.com/docs/8.x/billing).

## Summary

[Laravel Cashier](https://laravel.com/docs/8.x/billing) is an awesome package for managing subscriptions in Laravel. Of course, there can be other ways to use Cashier with multiple models. This is one of the approaches you can use when you have more than two billable models.

Anyway, this was one long article, and thank you for going through the article! If you have questions about the article, then hit me up on Twitter [@swapnil_bhavsar](https://twitter.com/swapnil_bhavsar).

PS: Here is the source code for the project on Github - [https://github.com/IamSwap/laravel-cashier-multiple-models](https://github.com/IamSwap/laravel-cashier-multiple-models)