Handling Braintree webhooks in Laravel Cashier

Laravel Cashier is already handling Braintree's 'subscription_canceled' and 'subscription_expired' webhooks and will cancel a subscription when any of those webhooks get triggered. The webhooks are handled in WebhookController, which can be easily extended to add support for other kinds of webhooks.

Let's see how that would work if you wanted to send a notification to a user when a charge is unsuccessful.

namespace App\Http\Controllers;

use App\Jobs\SendChargedUnsuccessfullyNotification;
use App\Models\User;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Laravel\Cashier\Http\Controllers\WebhookController as CashierWebhookController;
use Symfony\Component\HttpFoundation\Response;

class WebhookController extends CashierWebhookController
    use DispatchesJobs;

     * Handle a subscription charged unsuccessfully notification from Braintree.
     * @param  WebhookNotification $webhook
     * @return Response
    public function handleSubscriptionChargedUnsuccessfully($webhook)
        $subscription = $this->getSubscriptionById($webhook->subscription->id);

        if ($subscription) {
            $user = User::findOrFail($subscription->user_id);
            $this->dispatch(new SendChargedUnsuccessfullyNotification($user));

        return new Response('Webhook Handled', 200);

Please note the method name is prefixed with handle and the camel-cased name of the Braintree webhook. For all webhook names see constants defined in WebhookNotification, which is a part of 'braintree_php' library.

Don't forget to point a route to the controller method in routes.php and exclude the URL from CSRF verification. It's all described in the documentation.

Now here is how the 'Destination URL' can be added in Braintree's control panel (go to Settings -> Webhooks):

Edit Webhook

You can quite easily add tests to verify whether the custom webhooks are handled correctly (without actually communicating with Braintree). To write a basic test I used WebhookTesting class from the 'braintree_php' library to generate a sample notification. I also used model factories to insert sample user and subscription into the database right before the test is run.

use App\Jobs\SendChargedUnsuccessfullyNotification;
use App\Models\User;
use Braintree\WebhookNotification;
use Braintree\WebhookTesting;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Laravel\Cashier\Subscription;

class WebhookControllerTest extends TestCase
    use DatabaseTransactions;

    public function testChargedUnsuccessfully()


        $sampleNotification = WebhookTesting::sampleNotification(

        $this->post('/braintree/webhook', $sampleNotification)

     * Create test data (User + Subscription).
    public function createTestData()
        $user = factory(User::class)->create();

            'user_id' => $user->id,
            'braintree_id' => '1b3efx'
Did you like this post?
Previous post

I've been recently using Codeception and really appreciate what it can do. I see Codeception as PHPUnit on steroids. What's nice about Codeception is that existing PHPUnit tests are compatible with it, so not all work is lost for people moving from PHPUnit to Codeception.

Next post

If you find yourself repeating a lot of similar rules when writing stylesheets or your needs go beyond simple variables think of lists and maps. I just discovered how useful lists and maps can be and therefore share a few examples.