Stripe API, Tutorials, Nova Framework

Webhooks are Stripe’s way of informing your application of an event such as a payment has failed or a change.

I’m using Nova Framework not setup with Nova? please read Getting Stripe API setup with Nova Framework

To use webhooks you first need to register your endpoint in Stripe’s Dashboard by going to https://dashboard.stripe.com/account/webhooks Once you’ve setup the location to where webhooks will be posted you’ll have access to a signing secret token add that to app/Config/Stripe.php. It will be used to validate incoming requests to ensure the request originated from Stripe.

When using hooks it’s important to validate the request which can be done by first defining your variables. The $secret is the token for webhooks, the $payload is the post request a header will exist to help authenticate the request passing these to Webhook::constructEvent allows you to only deal with valid requests.

If the request fails send back a HTTP status code for 400 otherwise save the request.

I do this by using file_put_contents and passing in a path to where I want a txt file to be created. In this case, I’ve setup a folder called Hooks/Pending located in my Users module. This is better than processing it right away also if something goes wrong you’ve got the request to look back at.

Finally sent back a HTTP status code of 200 this tells Stripe the request has been accepted.

public function webHook()
{
    $secret     = Config::get('stripe.webhookSecret');
    $payload    = file_get_contents("php://input");
    $sig_header = $_SERVER["HTTP_STRIPE_SIGNATURE"];
    $event      = null;

    try {
        $event = Webhook::constructEvent($payload, $sig_header, $secret);
    } catch(\UnexpectedValueException $e) {
        // Invalid payload
        http_response_code(400); // PHP 5.4 or greater
        return;
    } catch(\Stripe\Error\SignatureVerification $e) {
        // Invalid signature
        http_response_code(400); // PHP 5.4 or greater
        return;
    }

    //store incoming hooks
    if ($event != null) {
        file_put_contents(APPDIR.'Modules/Users/Hooks/pending/payload-'.date('d-m-Y-h-i-s').'.txt', json_encode($event));
    }

    //set response HTTP code
    http_response_code(200); // PHP 5.4 or greater
}

At this point every time an event in Stripe is created it will be posted and stored on your server. 

To process these requests setup a cron job that calls a method that will look at the Pending folder read the files process them and move them to a Complete folder.

I have a method called ImportHooks that using foreach and glob to scan my pending folder and read the files, then decodes the files and passed $event->type to a switch statement.

This allows you to choose how to deal with each type of event, below is not the full list of event types but a sub set that I choose to use.

For instance, a payment fails will call the invoice.payment.failed event I can then look at the event and send an email to the customer and the staff to do something about the failure.

Finally, after the switch has run I use rename to actually move the txt from the Pending folder to a Complete folder.

public function importHooks() {

    foreach(glob(APPDIR.'Modules/Users/Hooks/pending/*.txt') as $file) {

        $event = json_decode(file_get_contents($file));

        switch ($event->type) {

            case 'customer.subscription.updated':
                //code for event here 
                break;

            case 'customer.subscription.deleted':
                //code for event here 
                break;

            case 'invoice.payment_succeeded':
                //code for event here 
                break;

            case 'invoice.payment_failed':
                //code for event here 
                break;
        }

        //move to complete folder
        $updatedfile = str_replace('pending', 'complete', $file);
        rename($file, $updatedfile);
    }

}

It’s really important that these files are stored outside of the document root. This ensured only authorized people can see the events. Thankfully with Nova this the default setup only files located in the webroot or asset folders are directly available.