Multiple Account Support With Laravel Cashier

Cashier is a great interface into the Stripe PHP API. Simply add your stripe key and stripe secret into your .env file and you’re good to go. But what if you have multiple stripe accounts? Or perhaps you are making charges on behalf of your clients each with their own stripe account? At first glance, manually changing the API key right before calling any Cashier code seems to work. For example:
\Stripe\Stripe::setApiKey($stripeSecret);
$user->updateDefaultPaymentMethod($paymentId);
However, upon further investigation, this doesn’t work for all cases. Digging into the cashier code in vendor/laravel/cashier/src/Billable.php, we noticed many calls that look like this:
StripeCustomer::retrieve($this->stripe_id, $this->stripeOptions());
// ..
StripePaymentIntent::create($options, $this->stripeOptions());
// ..
StripePaymentIntent::retrieve($paymentIntent, $this->stripeOptions());
// etc
$this->stripeOptions() is implemented as:
// vendor/laravel/cashier/src/Billable.php

/**
 * Get the default Stripe API options for the current Billable model.
 *
 * @param  array  $options
 * @return array
 */
public function stripeOptions(array $options = [])
{
    return Cashier::stripeOptions($options);
}
Which in turn comes from:
// vendor/laravel/cashier/src/Cashier.php

/**
 * Get the default Stripe API options.
 *
 * @param  array  $options
 * @return array
 */
public static function stripeOptions(array $options = [])
{
    return array_merge([
        'api_key' => config('cashier.secret'),
        'stripe_version' => static::STRIPE_VERSION,
    ], $options);
}
Apparently, instead of using the secret set manually at the global level via setApiKey(), it is taking the config secret and sending it with every request. It may not be the account we want to use. So how can we solve this? The solution is to override the Billable trait with code setting the proper secret in the $options array. Assume we have a companies model and each company has their own stripe key and secret.
$company->stripe_key;
$company->stripe_secret;
Tip: Instead of storing the secret in plain text, use a library such as Elocryptfive to encrypt in the database.
Normally, we would add the Cashier’s Billable trait to the Company model. But instead, we replace it with our own implemented version.
// app/Company.php

namespace App;

use App\Traits\CustomBillable;
use Laravel\Cashier\Billable;

class Company
{
    // don't use the Cashier Billable trait
    //use Billable;
    
    // use our version instead
    use CustomBillable;
}
Traits can be composed of other traits. So in a way, we can “inherit” from the Cashier trait and overwrite the stripeOptions() method.
// app/Traits/CustomBillable.php 

namespace App\Traits; 

use Laravel\Cashier\Billable as CashierBillable; 

trait CustomBillable 
{
    use CashierBillable; 

    /** 
     * Get the default Stripe API options for the current Billable model. 
     * 
     * @param array $options 
     * @return array 
     */ 
     public function stripeOptions(array $options = []) 
     { 
        // check if this model has it's own stripe_secret and use it 
        if ($this->stripe_secret) {
            $options['api_key'] = $this->stripe_secret;
        }
        return Cashier::stripeOptions($options);
    }
}
Now, whenever Cashier makes any call, it will populate $options with the correct secret!