diff --git a/README.md b/README.md index c9e5c1f..775f535 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,33 @@ When you try another request, the request will fail. This client will throw you We also have an option `$apiClient->enableRetryOnRateLimitHit()` you can use to enable retry's of requests when you hit a rate limit. When the client hits the rate limit, it will sleep for 20 seconds and try the same request again. +## Webhooks helper +This client also contains a helper for receiving webhooks, including a signature checker. This is included in the `PicqerWebhook` class. + +To receive a webhook, you only need the following: + +```php +getName() . ' that was triggered at ' . $webhook->getEventTriggeredAt() . PHP_EOL; +echo $webhook->getData(); +``` + +We recommend using signature validation to be sure the webhook was sent by Picqer. Create a hook with a secret, then check the signature of the webhook with that secret as follows: + +```php +getName() . ' that was triggered at ' . $webhook->getEventTriggeredAt() . PHP_EOL; +echo $webhook->getData(); diff --git a/src/PicqerWebhook.php b/src/PicqerWebhook.php new file mode 100644 index 0000000..f2c94c1 --- /dev/null +++ b/src/PicqerWebhook.php @@ -0,0 +1,88 @@ + + * @license http://creativecommons.org/licenses/MIT/ MIT + */ +class PicqerWebhook +{ + protected $idhook; + protected $name; + protected $event; + protected $data; + protected $event_triggered_at; + + public function __construct($webhookPayload) + { + $this->rawPayload = $webhookPayload; + + $fieldsToParse = ['idhook', 'name', 'event', 'data', 'event_triggered_at']; + + foreach ($fieldsToParse as $field) { + if (array_key_exists($field, $webhookPayload)) { + $this->$field = $webhookPayload[$field]; + } + } + } + + public static function retrieve() + { + $webhookPayloadRaw = file_get_contents('php://input'); + + $webhookPayloadDecoded = json_decode($webhookPayloadRaw, true); + + if ($webhookPayloadDecoded === false) { + throw new WebhookException('Could not decode webhook payload'); + } + + return new self($webhookPayloadDecoded); + } + + public static function retrieveWithSecret($secret) + { + if (! isset($_SERVER) || ! array_key_exists('HTTP_X_PICQER_SIGNATURE', $_SERVER)) { + throw new WebhookSignatureMismatchException('Could not find signature header in webhook'); + } + + $webhookPayloadRaw = file_get_contents('php://input'); + + $signatureHeader = $_SERVER['HTTP_X_PICQER_SIGNATURE']; + + $calculatedSignature = base64_encode(hash_hmac('sha256', $webhookPayloadRaw, $secret, true)); + + if (! hash_equals($calculatedSignature, $signatureHeader)) { + throw new WebhookSignatureMismatchException('Signatures do not match'); + } + + return self::retrieve(); + } + + public function getIdhook() + { + return $this->idhook; + } + + public function getName() + { + return $this->name; + } + + public function getEvent() + { + return $this->event; + } + + public function getData() + { + return $this->data; + } + + public function getEventTriggeredAt() + { + return $this->event_triggered_at; + } +} diff --git a/src/WebhookException.php b/src/WebhookException.php new file mode 100644 index 0000000..596efb4 --- /dev/null +++ b/src/WebhookException.php @@ -0,0 +1,5 @@ +