stymiee /
authnetjson
| 1 | <?php |
||
| 2 | |||
| 3 | declare(strict_types=1); |
||
| 4 | |||
| 5 | /** |
||
| 6 | * This file is part of the AuthnetJSON package. |
||
| 7 | * |
||
| 8 | * (c) John Conde <[email protected]> |
||
| 9 | * |
||
| 10 | * For the full copyright and license information, please view the LICENSE |
||
| 11 | * file that was distributed with this source code. |
||
| 12 | */ |
||
| 13 | |||
| 14 | namespace Authnetjson; |
||
| 15 | |||
| 16 | use Authnetjson\Exception\AuthnetInvalidCredentialsException; |
||
| 17 | use Authnetjson\Exception\AuthnetInvalidJsonException; |
||
| 18 | |||
| 19 | /** |
||
| 20 | * Handles a Webhook notification from the Authorize.Net Webhooks API |
||
| 21 | * |
||
| 22 | * @package AuthnetJSON |
||
| 23 | * @author John Conde <[email protected]> |
||
| 24 | * @copyright 2015 - 2023 John Conde <[email protected]> |
||
| 25 | * @license http://www.apache.org/licenses/LICENSE-2.0.html Apache License, Version 2.0 |
||
| 26 | * @link https://github.com/stymiee/authnetjson |
||
| 27 | * @see https://developer.authorize.net/api/reference/ |
||
| 28 | */ |
||
| 29 | class AuthnetWebhook |
||
| 30 | { |
||
| 31 | /** |
||
| 32 | * @var object SimpleXML object representing the Webhook notification |
||
| 33 | */ |
||
| 34 | private $webhook; |
||
| 35 | |||
| 36 | /** |
||
| 37 | * @var string JSON string that is the Webhook notification sent by Authorize.Net |
||
| 38 | */ |
||
| 39 | private $webhookJson; |
||
| 40 | |||
| 41 | /** |
||
| 42 | * @var array HTTP headers sent with the notification |
||
| 43 | */ |
||
| 44 | private $headers; |
||
| 45 | |||
| 46 | /** |
||
| 47 | * @var string Authorize.Net Signature Key |
||
| 48 | */ |
||
| 49 | private $signature; |
||
| 50 | |||
| 51 | /** |
||
| 52 | * Creates the response object with the response json returned from the API call |
||
| 53 | * |
||
| 54 | * @param string $signature Authorize.Net Signature Key |
||
| 55 | * @param string $payload Webhook Notification sent by Authorize.Net |
||
| 56 | * @param array $headers HTTP headers sent with Webhook. Optional if PHP is run as an Apache module |
||
| 57 | * @throws AuthnetInvalidCredentialsException |
||
| 58 | * @throws AuthnetInvalidJsonException |
||
| 59 | */ |
||
| 60 | 3 | public function __construct(string $signature, string $payload, array $headers = []) |
|
| 61 | { |
||
| 62 | 3 | $this->signature = $signature; |
|
| 63 | 3 | $this->webhookJson = $payload; |
|
| 64 | 3 | $this->headers = $headers; |
|
| 65 | 3 | if (empty($this->headers)) { |
|
| 66 | 2 | $this->headers = $this->getAllHeaders(); |
|
| 67 | } |
||
| 68 | 3 | if (empty($this->signature)) { |
|
| 69 | 1 | throw new AuthnetInvalidCredentialsException('You have not configured your signature properly.'); |
|
| 70 | } |
||
| 71 | 2 | if (($this->webhook = json_decode($this->webhookJson, false)) === null) { |
|
| 72 | 1 | throw new AuthnetInvalidJsonException('Invalid JSON sent in the Webhook notification'); |
|
| 73 | } |
||
| 74 | 1 | $this->headers = array_change_key_case($this->headers, CASE_UPPER); |
|
| 75 | 1 | } |
|
| 76 | |||
| 77 | /** |
||
| 78 | * Outputs the response JSON in a human-readable format |
||
| 79 | * |
||
| 80 | * @return string HTML table containing debugging information |
||
| 81 | */ |
||
| 82 | 1 | public function __toString() |
|
| 83 | { |
||
| 84 | 1 | $output = '<table id="authnet-webhook">' . "\n"; |
|
| 85 | 1 | $output .= '<caption>Authorize.Net Webhook</caption>' . "\n"; |
|
| 86 | 1 | $output .= '<tr><th colspan="2"><b>Response HTTP Headers</b></th></tr>' . "\n"; |
|
| 87 | 1 | $output .= '<tr><td colspan="2"><pre>' . "\n"; |
|
| 88 | 1 | $output .= var_export($this->headers, true) . "\n"; |
|
| 89 | 1 | $output .= '</pre></td></tr>' . "\n"; |
|
| 90 | 1 | $output .= '<tr><th colspan="2"><b>Response JSON</b></th></tr>' . "\n"; |
|
| 91 | 1 | $output .= '<tr><td colspan="2"><pre>' . "\n"; |
|
| 92 | 1 | $output .= $this->webhookJson . "\n"; |
|
| 93 | 1 | $output .= '</pre></td></tr>' . "\n"; |
|
| 94 | 1 | $output .= '</table>'; |
|
| 95 | |||
| 96 | 1 | return $output; |
|
| 97 | } |
||
| 98 | |||
| 99 | /** |
||
| 100 | * Gets a response variable from the Webhook notification |
||
| 101 | * |
||
| 102 | * @param string $var |
||
| 103 | * @return string requested variable from the API call response |
||
| 104 | */ |
||
| 105 | 1 | public function __get(string $var) |
|
| 106 | { |
||
| 107 | 1 | return $this->webhook->{$var}; |
|
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * Validates a webhook signature to determine if the webhook is valid |
||
| 112 | * |
||
| 113 | * @return bool |
||
| 114 | */ |
||
| 115 | 3 | public function isValid(): bool |
|
| 116 | { |
||
| 117 | 3 | $hashedBody = strtoupper(hash_hmac('sha512', $this->webhookJson, $this->signature)); |
|
| 118 | 3 | return (isset($this->headers['X-ANET-SIGNATURE']) && |
|
| 119 | 3 | strtoupper(explode('=', $this->headers['X-ANET-SIGNATURE'])[1]) === $hashedBody); |
|
| 120 | } |
||
| 121 | |||
| 122 | /** |
||
| 123 | * Validates a webhook signature to determine if the webhook is valid |
||
| 124 | * |
||
| 125 | * @return string|null |
||
| 126 | */ |
||
| 127 | 1 | public function getRequestId(): ?string |
|
| 128 | { |
||
| 129 | 1 | return $this->headers['X-REQUEST-ID'] ?? null; |
|
| 130 | } |
||
| 131 | |||
| 132 | /** |
||
| 133 | * Retrieves all HTTP headers of a given request |
||
| 134 | * |
||
| 135 | * @return array |
||
| 136 | */ |
||
| 137 | 1 | protected function getAllHeaders(): array |
|
| 138 | { |
||
| 139 | 1 | if (function_exists('apache_request_headers')) { |
|
| 140 | $headers = apache_request_headers(); |
||
| 141 | } else { |
||
| 142 | 1 | $headers = []; |
|
| 143 | 1 | foreach ($_SERVER as $key => $value) { |
|
| 144 | 1 | if (strpos($key, 'HTTP_') === 0) { |
|
| 145 | 1 | $headers[str_replace('_', '-', substr($key, 5))] = $value; |
|
| 146 | } |
||
| 147 | } |
||
| 148 | } |
||
| 149 | 1 | return $headers ?: []; |
|
|
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
| 150 | } |
||
| 151 | } |
||
| 152 |