Skip to content

Commit

Permalink
Merge pull request #127 from helpscout/make-incoming-webhook-from-glo…
Browse files Browse the repository at this point in the history
…bals

Added ability to build a webhook from globals
  • Loading branch information
bkuhl committed Apr 11, 2019
2 parents 244c649 + d5b2343 commit 07f24de
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 3 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ language: php
php:
- 7.1
- 7.2
- 7.3

before_script:
- composer self-update
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -780,17 +780,20 @@ $client->webhooks()->delete($webhookId);
```

#### Processing an incoming webhook
You can also use the SDK to easily process an incoming webhook. To do this, you'll need a request object that satisfies the PSR-7 `RequestInterface` and the secret key you used when setting up the webhook. Signature validation will happen when creating the new object, so no need to check if it is valid or not. If the signatures do not match, the constructor of the `IncomingWebhook` object will throw an `InvalidSignatureException` to let you know something is wrong.
You can also use the SDK to easily process an incoming webhook. Signature validation will happen when creating the new object, so no need to check if it is valid or not. If the signatures do not match, the constructor of the `IncomingWebhook` object will throw an `InvalidSignatureException` to let you know something is wrong.

```php
// Build using a request object that satisfies the PSR-7 RequestInterface
/** @var RequestInterface $request */
$request = new Request(...);
$secret = 'superSekretKey';

$incoming = new IncomingWebhook($request, $secret);

// Or build it from globals
$incoming = IncomingWebhook::makeFromGlobals($secret);
```

Once you have the incoming webhook object, you can check the type of payload (customer, conversation, or test) as well as retrieve the data. If a customer or conversation, you can retrieve the model associated. Otherwise, you can get the payload as either an associative array or standard class object.
Once you have the incoming webhook object, you can check the type of payload (customer, conversation, or test) as well as retrieve the data ([see example](https://github.com/helpscout/helpscout-api-php/blob/master/examples/incoming_webhook.php)). If a customer or conversation, you can retrieve the model associated. Otherwise, you can get the payload as either an associative array or standard class object.

### Workflows

Expand Down
2 changes: 2 additions & 0 deletions examples/incoming_webhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
];

$request = new Request('POST', 'www.blah.blah', $headers, $body);

// You can use IncomingWebhook::makeFromGlobals($secret) instead of building your own request
$webhook = new IncomingWebhook($request, $secret);

$eventType = $webhook->getEventType();
Expand Down
25 changes: 25 additions & 0 deletions src/Webhooks/IncomingWebhook.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace HelpScout\Api\Webhooks;

use GuzzleHttp\Psr7\Request;
use HelpScout\Api\Conversations\Conversation;
use HelpScout\Api\Customers\Customer;
use HelpScout\Api\Exception\InvalidSignatureException;
Expand Down Expand Up @@ -51,6 +52,25 @@ public function __construct(RequestInterface $request, string $secret)
$this->validateSignature();
}

public static function makeFromGlobals(string $secret): self
{
$headers = [];
foreach ($_SERVER as $key => $value) {
if (stripos($key, 'HTTP_') === 0) {
$headers[$key] = $value;
}
}

$request = new Request(
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'],
$headers,
@file_get_contents('php://input')
);

return new IncomingWebhook($request, $secret);
}

/**
* @throws InvalidSignatureException
*/
Expand Down Expand Up @@ -209,4 +229,9 @@ public function getCustomer(): Customer

return $resource->getEntity();
}

public function getRequest(): RequestInterface
{
return $this->request;
}
}
20 changes: 20 additions & 0 deletions tests/Webhooks/IncomingWebhookTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,26 @@ protected function generateSignature(string $body, string $secret): string
);
}

public function testMakesWebhookFromGlobals()
{
$secret = uniqid();
$body = @file_get_contents('php://input');
$signature = $this->generateSignature($body, $secret);
$_SERVER['REQUEST_METHOD'] = uniqid();
$_SERVER['REQUEST_URI'] = uniqid();
$_SERVER['HTTP_X_HELPSCOUT_SIGNATURE'] = $signature;

$webhook = IncomingWebhook::makeFromGlobals($secret);
$request = $webhook->getRequest();

$this->assertSame(strtoupper($_SERVER['REQUEST_METHOD']), $request->getMethod());
$this->assertSame($_SERVER['REQUEST_URI'], (string) $request->getUri());
$this->assertSame([
$signature,
], $request->getHeader('HTTP_X_HELPSCOUT_SIGNATURE'));
$this->assertSame('', $request->getBody()->getContents());
}

public function testCreateIncomingConvoWebhook()
{
$body = ConversationPayloads::getConversation(123);
Expand Down

0 comments on commit 07f24de

Please sign in to comment.