Issues (19)

src/Authnetjson/AuthnetWebhook.php (1 issue)

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
The expression return $headers ?: array() could return the type true which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
150
    }
151
}
152