If your application bills customers internationally, sometimes you need multiple Stripe Accounts for all your companies.
Laravel Cashier doesn't support charging customers from different Stripe accounts out-of-the box, but achieving this is pretty easy.
I will not go through the whole setup of Cashier for a single Stripe account, because Laravel's documentation is fantastic: thorough and easy to follow: Laravel Cashier. The account you setup here, will be the fallback/default account if the Billable
model (usually App\Models\User
) can't be used to resolve the correct Stripe account.
After you're done setting Cashier up, create a new trait
called MyBillable
. I tend to keep my traits in a folder inside where they are used. Meaning MyBillable
trait will go inside traits
folder located in app/models
.
After creating the App\Models\Traits\MyBillable
, add this as content:
<?php
namespace App\Models\Traits;
use Laravel\Cashier\Billable;
use Laravel\Cashier\Cashier;
trait MyBillable
{
use Billable;
/**
* Override stripe configuration based on User's country preference
*
* @param array $options
* @return \Stripe\StripeClient
*/
public function stripe(array $options = [])
{
return Cashier::stripe($options);
}
}
The code above does nothing for now, but we will expand the App\Models\Traits\MyBillable::stripe()
method later.
If you have followed the setup instructions from the official documentation, your billable model (usually App\Models\User
) has the Laravel\Cashier\Cashier\Billable
trait use
d. Go ahead and replace it with our new one MyBillable
(which also leverages all provided functionality in Cashier by use
-ing it inside).
Verify your application is working properly, as if we haven't made any changes after the Cashier setup.
Inside your App\Models\Traits\MyBillable::stripe()
method you can use $this
to access your User
model and write your own logic to determine which of your Stripe accounts is correct for that customer.
In my case, my users belongsTo
countries and each country has country code which I use to get previously set .env
variables with my stripe secret, stripe key and currency for Cashier.
To do so, create a new config file billing.php
inside config/
folder:
return [
'stripe' => [
'bg' => [
'key' => env('BG_STRIPE_KEY'),
'secret' => env('BG_STRIPE_SECRET'),
'currency' => env('BG_CASHIER_CURRENCY'),
],
'pl' => [
'key' => env('PL_STRIPE_KEY'),
'secret' => env('PL_STRIPE_SECRET'),
'currency' => env('PL_CASHIER_CURRENCY'),
]
],
];
Modify your .env
key and add:
# Bulgaria Cashier Setup
BG_STRIPE_KEY=
BG_STRIPE_SECRET=
BG_CASHIER_CURRENCY=
# Poland Cashier Setup
PL_STRIPE_KEY=
PL_STRIPE_SECRET=
PL_CASHIER_CURRENCY=
Lastly, update the method in your MyBillable
trait to:
public function stripe(array $options = [])
{
if (!is_null($this->country)) {
// Get country specific cashier key
$config = config('billing.stripe');
if (array_key_exists($this->country->code, $config)) {
// Update cashier's config
config(['cashier.key' => $config[$this->country->code]['key']]);
config(['cashier.secret' => $config[$this->country->code]['secret']]);
config(['cashier.currency' => $config[$this->country->code]['currency']]);
}
}
// use default config
return Cashier::stripe($options);
}
The method above is called every time Cashier needs an instance of StripeClient
(provided by Cashier's dependency: stripe-php
), so by altering Cashier's config
we are getting an instance of the StripeClient
for the specified Stripe account.
That's it! You are now creating customers in the appropriate Stripe account based on your logic.
What's next? Cashier also provides webhooks to tell your application about changes made outside of it (related to your customers, subscriptions, payments, etc).
In my next post I will explain how to create different endpoints in your application to handle the calls issued by your multiple Stripe accounts
Check out the comments below to see a way of handling webhooks.
Top comments (5)
I wanted to be able to select between different payment processors, so I added a mechanism to be able to select between multiple payment providers using service container and contextual binding. In the end I didn't use cashier, but I could use if I implement an interface for it.
Minor correction. It should be "stripeOptions" instead of "stripe"
public function stripeOptions(array $options = [])
{
if (!is_null($this->country)) {
// Get country specific cashier key
$config = config('billing.stripe');
if (array_key_exists($this->country->code, $config)) {
// Update cashier's config
config(['cashier.key' => $config[$this->country->code]['key']]);
config(['cashier.secret' => $config[$this->country->code]['secret']]);
config(['cashier.currency' => $config[$this->country->code]['currency']]);
}
}
// use default config
return Cashier::stripeOptions($options);
}
Thank you for your comment!
Laravel's Cashier latest version (as of writing of this comment) is 13 and I am setting
config()
variables before calling the following method:Cashier::stripe()
.Any updates on the webhook part? Working through this problem myself
Hey,
here's what I ended up doing, however I am not sure if it's the best approach, but it might help you.
Controller that just extends the one provided by Cashier*:
Register a route in
web.php
:Define a middleware
set.webhook.secret
that sets credentials for Cashier based on the parameter$code
in the URL:In Stripe define different webhook url's for your different accounts. For example, I have:
api.my-project.com/stripe/webhook/bg
api.my-project.com/stripe/webhook/pl
and in
.env
:* This is optional, I think, you might want to use the Webhook controller provided by directly when defining the route & applying the middleware to it. I have a bunch of other stuff there that aren't related to handling multiple Stripe accounts.