Working With Webhooks

A presentation at PHP[TEK] in May 2019 in Atlanta, GA, USA by Lorna Jane Mitchell

Slide 1

Slide 1

Working with Webhooks Lorna Mitchell, Nexmo

Slide 2

Slide 2

What is a Webhook? An HTTP POST request. @lornajane

Slide 3

Slide 3

Why Webhooks? Event-driven HTTP = Webhooks @lornajane

Slide 4

Slide 4

Webhooks in the Wild @lornajane

Slide 5

Slide 5

Slack Integrations @lornajane

Slide 6

Slide 6

GitHub Builds @lornajane

Slide 7

Slide 7

Webhook Use Cases • Notify of events • Deliver data when available • Broadcast to multiple receivers as-it-happens @lornajane

Slide 8

Slide 8

How APIs Work @lornajane

Slide 9

Slide 9

How APIs Work @lornajane

Slide 10

Slide 10

How APIs Work @lornajane

Slide 11

Slide 11

How APIs Work @lornajane

Slide 12

Slide 12

How Webhooks Work @lornajane

Slide 13

Slide 13

How Webhooks Work @lornajane

Slide 14

Slide 14

How Webhooks Work @lornajane

Slide 15

Slide 15

What About Time? @lornajane

Slide 16

Slide 16

APIs Over Time @lornajane

Slide 17

Slide 17

Webhooks Over Time @lornajane

Slide 18

Slide 18

Slide 19

Slide 19

Where To Webhook To? @lornajane

Slide 20

Slide 20

Webhooks Need Pre-arrangement With APIs, the client calls the server. With Webhooks, the client has to register with the server, to get data later. @lornajane

Slide 21

Slide 21

Designing Webhooks: Example A GitHub “push” event contains nested data. “ref”: “refs/tags/simple-tag”, “before”: “a10867b14bb761a232cd80139fbd4c0d33264240”, “after”: “0000000000000000000000000000000000000000”, “compare”: “https://github.com/Codertocat/Hello-World/compare/a10867b14bb “commits”: [ ], “repository”: { “owner”: { }, “html_url”: “https://github.com/Codertocat/Hello-World” }, “sender”: { } @lornajane

Slide 22

Slide 22

Designing Webhooks Think about how your users will use the API. • What’s their context/technology/device? • What are they doing when they call the endpoint? • What are they likely to do next? @lornajane

Slide 23

Slide 23

Receiving Webhooks Warning: minor tangent ahead @lornajane

Slide 24

Slide 24

Ngrok for Testing Webhooks https://ngrok.com/ - secure tunnel to your dev platform Use this tool to: • webhook into code running locally • inspect the request and response of the webhook • replay requests and see the responses @lornajane

Slide 25

Slide 25

Ngrok for Testing Webhooks Start the tunnel on your laptop: receive a public URL @lornajane

Slide 26

Slide 26

Example: Nexmo SMS When you register a phone number and receive an SMS, your application receives a webhook. @lornajane

Slide 27

Slide 27

I’m running open endpoints on the internet and accepting data, now what? @lornajane

Slide 28

Slide 28

Webhook Security When working with webhooks: • be aware of attack vectors • always use SSL • consider shared secrets and hashing • all good HTTP security practices apply @lornajane

Slide 29

Slide 29

Nexmo SMS Security Nexmo can sign messages using a shared secret. The PHP library https://github.com/nexmo/nexmo-php can do this for you. $signature = new \Nexmo\Client\Signature($_GET, SIGNATURE_SECRET, ‘sha256’); $isValid = $signature->check($_GET[‘sig’]); @lornajane

Slide 30

Slide 30

Slide 31

Slide 31

Receiving Webhooks: Best Practice It’s just an HTTP request! Advice: • DO: accept, store and acknowledge quickly • DON’T: process before acknowledging @lornajane

Slide 32

Slide 32

Using Queues in PHP Applications Queues protect you against bursty traffic. Queues separate work from webservers. • This example uses https://beanstalkd.github.io/ and Laravel • Other good alternatives: Redis or Laravel Horizon, Amazon SQS, RabbitMQ @lornajane

Slide 33

Slide 33

SlimPHP and Beanstalkd 1 $container = $app->getContainer(); 2 3 $container[‘queue’] = function ($container) { 4 return new \Pheanstalk\Pheanstalk( 5 getenv(‘QUEUE_HOST’), 6 getenv(‘QUEUE_PORT’) 7 ); 8 }; Pro-tip: Try vlucas\phpdotenv for dev platform environment vars. @lornajane

Slide 34

Slide 34

SlimPHP and Beanstalkd 13 $app->get(‘/webhooks/inbound-sms’, function ($request, $response, $arg 14 $params = $request->getQueryParams(); 15 16 $data = [“event” => “message”, 17 “text” => $params[‘text’], 18 “receivedAt” => date(“U”), 19 “payload” => $params]; 20 error_log(“New message: ” . $params[‘text’]); 21 $this->queue->useTube(‘sms’) 22 ->put(json_encode($data)); 23 }); @lornajane

Slide 35

Slide 35

Laravel and Beanstalkd 1 use App\Jobs\InboundSms; 2 Route::get(‘inbound-sms’, function(Request $request) { 3 // get incoming parameters (includes GET and POST) 4 $params = $request->input(); 5 $data = [“event” => “message”, “text” => $params[‘text’], 6 “receivedAt” => date(“U”), “payload” => $params]; 7 error_log(“New message: ” . $params[‘text’]); 8 9 InboundSms::dispatch($data); 10 return “OK”; 11 }); @lornajane

Slide 36

Slide 36

So The Data is In a Queue. Now What? @lornajane

Slide 37

Slide 37

Let’s talk about Workers Workers are long-running scripts that process a series of jobs. Workers need to be independent: • if things go wrong, exit • separate tool to monitor/restart as needed • beware long-running process hazards • everything processed “at least once” (but maybe more than once, and in any order…) @lornajane

Slide 38

Slide 38

Processing SMSes with PHP 13 while($job = $queue->reserve()) { 14 $received = json_decode($job->getData(), true); 15 error_log($received[‘text’]); 16 17 // delegate to the class that will do the work 18 $worker->process($received); 19 20 $queue->delete($job); 21 } @lornajane

Slide 39

Slide 39

Processing SMSes with PHP 9 class Worker { 10 public function process($data) { 11 $signature = new \Nexmo\Client\Signature( $data[‘payload’], 12 getenv(‘NEXMO_API_SIGNATURE_SECRET’), 13 ‘sha256’); 14 if ($signature->check($data[‘payload’][‘sig’])) { 15 $this->write_to_file($this->outfile, $data[‘text’]); 16 return true; 17 } 18 return false; 19 } @lornajane

Slide 40

Slide 40

Processing SMSes with Laravel 1 Route::get(‘inbound-sms’, function(Request $request) 2 { 3 $params = $request->input(); 4 $data = [“event” => “message”, “text” => $params[‘text’], 5 “receivedAt” => date(“U”), “payload” => $params]; 6 error_log(“New message: ” . $params[‘text’]); 7 8 InboundSms::dispatch($data); 9 return “OK”; 10 }); @lornajane

Slide 41

Slide 41

Slide 42

Slide 42

Publishing Webhooks Sending webhooks is an implementation of Publish/Subscribe pattern. • Put desired webhooks into a queue, and send at a steady rate. • Everything we discussed about queues and workers applies. • DO NOT send from live webserver. @lornajane

Slide 43

Slide 43

Webhooks … are awesome :) @lornajane

Slide 44

Slide 44

Webhooks in Your Applications • Use them WHEN you want to notify other systems • Examples of HOW to use webhooks hopefully gave you some ideas • Webhooks are HTTP: we already understand this @lornajane

Slide 45

Slide 45

Thanks! • Feedback please! https://joind.in/ • PHP Web Services from O’Reilly • Nexmo: https://nexmo.com • Me: https://lornajane.net • Ngrok: https://ngrok.com/ • Code: https://github.com/lornajane/incoming-sms-beanstalkd-php @lornajane

Slide 46

Slide 46

Bonus slides @lornajane

Slide 47

Slide 47

Serverless Webhook Endpoints Serverless technology: • Functions as a Service • Scalable: ideal for bursty workloads • Pay-as-you-go, and with free tiers • PHP supported on some platforms (they all support NodeJS) Code: https://github.com/lornajane/fortunes-by-sms @lornajane

Slide 48

Slide 48

Serverless PHP SMS Reply Code Example 4 function main(array $params) : array { 5 $cookie = getCookies()[random_int(0, 430)]; 6 parse_str($params[‘__ow_query’], $query); 7 8 $url = “https://rest.nexmo.com/sms/json”; 9 $options = [“http” => [ “method” => “POST”, “content” => json_enco 10 $context = stream_context_create($options); 11 $response = file_get_contents($url, false, $context); 12 return [“body” => $cookie]; 13 } @lornajane

Slide 49

Slide 49

Serverless PHP SMS Reply Deployment Zip the code (and dependencies if needed), then deploy rm -f sms-fortune.zip zip -rq sms-fortune.zip index.php ibmcloud fn action update sms-fortune/incoming-php \ —kind php:7.2 —web raw sms-fortune.zip Link your Nexmo number to the URL of this action, share and enjoy. Here’s one I made earlier: 201-579-9898 @lornajane