Select Page

I recently began using Laravel Cashier in a project to integrate with Stripe for payments. My use case was an application that has a monthly base rate and additional utility style pricing for items within the application. A close comparison would be a telephone number and minutes used. (What a shocker, me working on a telephony app.)

For the most part, the Cashier docs are straightforward, and getting monthly subscriptions going in Stripe was fairly painless. When it came time to add line items to a bill, I found the docs to be less friendly.

The basic premise is this: When a customer’s subscription is about to come due for a given plan, Stripe creates an invoice, just as you would expect. They also fire an invoice.created event that hits your configured webhook, which allows you to catch the event and add additional line items to the invoice before being charged. Perfect!

Laravel Cashier, out of the box, does not handle the invoice.created event, so it’s up to us to implement it.

Following the docs, we create a new controller that extends the Cashier supplied Stripe webhook controller:

And override the default Stripe webhook route and send it to our new controller:

Great! Now, how do we catch the invoice.created event? We just add a pubic method to our extended controller named handleInvoiceCreated( array $payload )!

From here, we can put whatever custom code into handleInvoiceCreated we want. Since we want to add an invoice item, we might do something like this:

Since we are including the “invoice” key corresponding to the user and invoice that Stripe sent us via the webhook (available to our method from $payload through Laravel Cashier parent class), the InvoiceItem gets added to the invoice and will be included when Stripe goes to charge the Invoice. Their documentation states it will usually be about an hour from the time the invoice.created event gets fired to the time the invoice is charged, but if your webhook fails, they will delay it and retry periodically and ultimately give up and just charge the subscription amount.

Another thing to note: If you are extending the controller and want to test various webhooks from the Stripe account dashboard via a test webhook, you’ll need to set the following in your environment (usually done via the .env file in Laravel):

If you fail to do this, all of the test Stripe webhooks initiated from the dashboard will return an empty response. This actually tripped me up fairly significantly — I debugged ngrok, Stripe, my local webserver, my extended class, etc., until I started digging through Laravel Cashier on Github and found the culprit. Even if the PR I created isn’t accepted, I wanted to make sure the information was available for others.


Share This