Using and understanding Stripe’s webhooks can be a vital part of using Stripe for processing
payments, especially when it comes to subscriptions or recurring billing. According to their
docs:
Interacting with third party APIs like Stripe often suffers from two important problems:
Services not directly responsible for making an API request may still need to know the response
Some events, like disputed charges and many recurring billing events, are not the result of a direct API request
Webhooks solve these problems by letting you register a URL that we will POST anytime an event happens in your account.
As webhooks are just JSON requests POSTed to a receiving url you set in Stripe’s account settings,
handling them are easy in CakePHP. No special routes need to be created, and the webhook request can
be turned into a PHP object with one line of code:
The Stripe docs recommend using only the event id from the received webhook, and then retrieving the
event via the Stripe API.
If security is a concern, or if it’s important to confirm that Stripe sent the webhook, you should only use the ID sent in your webhook and should request the remaining details from the Stripe API directly.
In the following example, the controller method will:
Receive the webhook
Turn it into a PHP object
Get the event id
Retrieve the event
Do something based on what the event was
Reply with an HTTP status code (200) telling Stripe the webhook was recieved
<?phppublicfunctionprocessWebhook(){$this->autoRender=false;// Stripe sends webhooks as POST onlyif($this->request->is('post')){// If using my CakePHP Stripe plugin, these are set in Config/bootstrap.php$mode=Configure::read('Stripe.mode');$key=Configure::read('Stripe.'.$mode.'Secret');// If not using the plugin, set $key to your secret API keyStripe::setApiKey($key);// This receives the request and runs json_decode, turning the JSON request into a PHP// object.$event_json=$this->request->input('json_decode');// Retrieve the event based on the event id, throw an exception and log what happened if the// event wasn't found or couldn't be retrieved. try{$event=Stripe_Event::retrieve($event_json->id);}catch(Exception$e){CakeLog::write('hook','No event found for: '.$event_json->id);// We still return a HTTP 200 because the webhook was successfully recieved, even if the// event was invalid.return$this->response->statusCode(200);}// In this example, a User.stripe_id field exists in the users table, containing the Stripe// customer id. Retrieve the user based on the event's customer id. $user=$this->User->findByStripeId($event->data->object->customer);if($user){// An invoice was paid. Send an email confirmation, update the database, etc.if($event->type=='invoice.payment_succeeded'){// do stuff for $user['User']['id']}// An invoice payment failed, email the user, etc.if($event->type=='invoice.payment_failed'){// do stuff for $user['User']['id']}// A customer's subscription was canceled at Stripe (invalid card or expired card,// etc.), cancel their account.if($event->type=='customer.subscription.deleted'){// cancel account for $user['User']['id']}}// Tell Stripe the webhook request was received so they don't try to resend it.return$this->response->statusCode(200);}}