Issues (19)

src/Client.php (4 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Greenlyst\BaseCommerce;
6
7
use Greenlyst\BaseCommerce\Core\TripleDESService;
8
9
final class Client
10
{
11
    private $sdkUsername;
12
    private $sdkPassword;
13
    private $sdkKey;
14
    private $production = false;
15
    private $tripleDESService;
16
    private $sessionId;
17
18
    private const PRODUCTION_URL = 'https://gateway.basecommerce.com';
19
    private const SANDBOX_URL = 'https://gateway.basecommercesandbox.com';
20
    private const USER_AGENT = 'BaseCommerceClientPHP/5.2.00';
21
22
    private const URI_VALIDATE_PING = '/pcms/?f=API_PingPong';
23
24
    /**
25
     * Client constructor.
26
     *
27
     * @param $sdkUsername
28
     * @param $sdkPassword
29
     * @param $sdkKey
30
     * @param bool $production
31
     */
32
    public function __construct($sdkUsername, $sdkPassword, $sdkKey, bool $production = false)
33
    {
34
        $this->sdkUsername = $sdkUsername;
35
        $this->sdkPassword = $sdkPassword;
36
        $this->sdkKey = $sdkKey;
37
        $this->production = $production;
38
39
        $this->tripleDESService = new TripleDESService($this->getKey());
40
    }
41
42
    /**
43
     * @param $uri
44
     * @param $data
45
     * @param int $retryCounter
46
     *
47
     * @throws ClientException
48
     *
49
     * @return array
50
     */
51
    public function postRequest($uri, $data, $retryCounter = 0)
52
    {
53
        $data = array_merge($this->toArray(), [
54
            'payload' => $this->getTripleDESService()->encrypt((string) $data),
55
        ]);
56
57
        $response = $this->sendRequest($uri, $data);
58
59
        if (!$response) {
0 ignored issues
show
$response is of type false|resource, thus it always evaluated to false.
Loading history...
60
            $this->checkErrorsAndRetryRequest($retryCounter, $uri, $data);
61
        }
62
63
        return $this->processResponse($response, $retryCounter, $uri, $data);
64
    }
65
66
    /**
67
     * @throws ClientException
68
     */
69
    public function validateCredentials()
70
    {
71
        $this->postRequest(self::URI_VALIDATE_PING, ['PING' => 'Ping Ping']);
72
73
        return true;
74
    }
75
76
    /**
77
     * @return mixed
78
     */
79
    public function getSessionId()
80
    {
81
        return $this->sessionId;
82
    }
83
84
    /**
85
     * @return string
86
     */
87
    private function getEndpointURL()
88
    {
89
        return $this->production == true ? self::PRODUCTION_URL : self::SANDBOX_URL;
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
90
    }
91
92
    /**
93
     * @return mixed
94
     */
95
    private function getUsername()
96
    {
97
        return $this->sdkUsername;
98
    }
99
100
    /**
101
     * @return mixed
102
     */
103
    private function getPassword()
104
    {
105
        return $this->sdkPassword;
106
    }
107
108
    /**
109
     * @return mixed
110
     */
111
    private function getKey()
112
    {
113
        return $this->sdkKey;
114
    }
115
116
    /**
117
     * @return TripleDESService
118
     */
119
    private function getTripleDESService(): TripleDESService
120
    {
121
        return $this->tripleDESService;
122
    }
123
124
    private function toArray()
125
    {
126
        return [
127
            'gateway_username' => $this->getUsername(),
128
            'gateway_password' => $this->getPassword(),
129
        ];
130
    }
131
132
    /**
133
     * @param $uri
134
     * @param $data
135
     *
136
     * @return bool|resource
137
     */
138
    private function sendRequest($uri, $data)
139
    {
140
        $url = $this->getEndpointURL().$uri;
141
142
        $params = [
143
            'http' => [
144
                'method'  => 'POST',
145
                'content' => json_encode($data),
146
                'header'  => 'Content-type: application/x-www-form-urlencoded',
147
            ],
148
        ];
149
150
        $ctx = stream_context_create($params);
151
152
        ini_set('user_agent', 'GL_BaseCommerceClientPHP/1.0');
153
154
        return fopen($url, 'rb', false, $ctx);
155
    }
156
157
    /**
158
     * @param $retryCounter
159
     * @param $uri
160
     * @param $data
161
     *
162
     * @throws ClientException
163
     *
164
     * @return array
165
     */
166
    private function checkErrorsAndRetryRequest($retryCounter, $uri, $data): array
167
    {
168
        $lastError = error_get_last();
169
        $error = $lastError['message'];
170
171
        if (strpos($error, '403') !== false) {
172
            throw ClientException::invalidCredentials();
173
        } elseif (strpos($error, '500') !== false) {
174
            throw ClientException::internalServerError();
175
        } elseif (strpos($error, '404') != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($error, '404') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
176
            throw ClientException::invalidURLOrHost();
177
        } elseif (strpos($error, '400') != false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing strpos($error, '400') of type integer to the boolean false. If you are specifically checking for non-zero, consider using something more explicit like > 0 or !== 0 instead.
Loading history...
178
            if ($retryCounter < 10) {
179
                sleep(3);
180
181
                return $this->postRequest($uri, $data, $retryCounter);
182
            } else {
183
                throw ClientException::errorConnectingToEnvironment();
184
            }
185
        }
186
187
        throw ClientException::unknownError($error);
188
    }
189
190
    /**
191
     * adapted from http://us.php.net/manual/en/function.stream-get-meta-data.php.
192
     *
193
     * @param $response
194
     *
195
     * @return void
196
     */
197
    private function setSessionIdFromMetaData($response): void
198
    {
199
        $meta = stream_get_meta_data($response);
200
        foreach (array_keys($meta) as $h) {
201
            $v = $meta[$h];
202
            if (is_array($v)) {
203
                foreach (array_keys($v) as $hh) {
204
                    $vv = $v[$hh];
205
                    if (is_string($vv) && substr_count($vv, 'JSESSIONID')) {
206
                        $this->sessionId = substr($vv, strpos($vv, '=') + 1, 24);
207
                    }
208
                }
209
            }
210
        }
211
    }
212
213
    /**
214
     * @param $response
215
     * @param $retryCounter
216
     * @param $uri
217
     * @param $data
218
     *
219
     * @throws ClientException
220
     *
221
     * @return array
222
     */
223
    private function processResponse($response, $retryCounter, $uri, $data): array
224
    {
225
        $responseString = stream_get_contents($response);
226
227
        $this->setSessionIdFromMetaData($response);
228
229
        if ($responseString === false) {
230
            $this->checkErrorsAndRetryRequest($retryCounter, $uri, $data);
231
        }
232
233
        $decrypted_response = $this->tripleDESService->decrypt($responseString);
234
235
        $trimmedResponse = trim($decrypted_response, "\x00..\x1F");
236
237
        echo $trimmedResponse;
238
        fclose($response);
239
240
        return json_decode($trimmedResponse, true);
241
    }
242
}
243