Vectorwyse
Tutorials

Multiple Account Support With Laravel Cashier

Jeffrey Li
#laravel#stripe#php#cashier
Feature image

If you’ve worked with Laravel Cashier, you’ve probably appreciated how simple it makes integrating Stripe into your application. But what happens when your app needs to support multiple Stripe accounts — for example, a multi-tenant platform where each company connects their own Stripe account?

You might think you can just set the API key manually before each request:

Stripe::setApiKey($company->stripe_secret);

But this doesn’t work consistently. Let me explain why, and how to fix it.

The Problem

When Cashier makes a Stripe API call, it doesn’t solely rely on the globally-set API key. Instead, it retrieves the secret from the configuration file with every request. Take a look at how Cashier calls Stripe internally:

StripeCustomer::retrieve($this->stripe_id, $this->stripeOptions());

The stripeOptions() method pulls from config('cashier.secret') rather than respecting any manually-set key. This means even if you set Stripe::setApiKey(...) beforehand, Cashier may still use the default key from your config file — potentially charging the wrong account.

The Solution: Override the Billable Trait

The fix is to create a custom trait that extends Cashier’s Billable trait and overrides the stripeOptions() method so it checks for model-specific credentials first.

Step 1: Update Your Model

Instead of using Cashier’s Billable trait directly on your model, use a custom trait:

use App\Traits\CustomBillable;

class Company extends Model
{
    use CustomBillable;
}

Step 2: Create the CustomBillable Trait

Create a new trait that composes the original Billable trait and overrides the stripeOptions method:

<?php

namespace App\Traits;

use Laravel\Cashier\Billable;
use Laravel\Cashier\Cashier;

trait CustomBillable
{
    use Billable;

    public function stripeOptions(array $options = [])
    {
        if ($this->stripe_secret) {
            $options['api_key'] = $this->stripe_secret;
        }

        return Cashier::stripeOptions($options);
    }
}

Now, every time Cashier makes an API call on behalf of a Company, it will check if that model has its own stripe_secret attribute. If it does, it uses that key; otherwise, it falls back to the default configuration.

Security Note

If you’re storing Stripe secrets on each model, do not store them in plain text. Use an encryption library like Elocryptfive to encrypt sensitive columns at rest. This way, even if your database is compromised, the API keys remain protected.

Wrapping Up

Laravel Cashier is great for single-account setups, but with a small override to the Billable trait, you can easily extend it to support multiple Stripe accounts. This pattern is especially useful for SaaS platforms and marketplaces where each tenant manages their own Stripe integration.

← Back to Blog