Passed
Push — master ( f4feef...3322ef )
by payever
10:36
created

CommonApiClient::listChannelSetsRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
/**
4
 * PHP version 5.4 and 7
5
 *
6
 * @category  Core
7
 * @package   Payever\Core
8
 * @author    payever GmbH <[email protected]>
9
 * @author    Andrey Puhovsky <[email protected]>
10
 * @author    Hennadii.Shymanskyi <[email protected]>
11
 * @copyright 2017-2021 payever GmbH
12
 * @license   MIT <https://opensource.org/licenses/MIT>
13
 * @link      https://docs.payever.org/shopsystems/api/getting-started
14
 */
15
16
namespace Payever\ExternalIntegration\Core;
17
18
use Payever\ExternalIntegration\Core\Authorization\DummyOauthTokenList;
19
use Payever\ExternalIntegration\Core\Authorization\OauthToken;
20
use Payever\ExternalIntegration\Core\Authorization\OauthTokenList;
21
use Payever\ExternalIntegration\Core\Base\ClientConfigurationInterface;
22
use Payever\ExternalIntegration\Core\Base\CommonApiClientInterface;
23
use Payever\ExternalIntegration\Core\Base\HttpClientInterface;
24
use Payever\ExternalIntegration\Core\Base\OauthTokenInterface;
25
use Payever\ExternalIntegration\Core\Base\ResponseInterface;
26
use Payever\ExternalIntegration\Core\Http\Client\CurlClient;
27
use Payever\ExternalIntegration\Core\Http\RequestBuilder;
28
use Payever\ExternalIntegration\Core\Http\RequestEntity\AuthenticationRequest;
29
use Payever\ExternalIntegration\Core\Http\ResponseEntity\AuthenticationResponse;
30
use Payever\ExternalIntegration\Core\Http\ResponseEntity\GetCurrenciesResponse;
31
use Payever\ExternalIntegration\Core\Http\ResponseEntity\ListChannelSetsResponse;
32
use Psr\Log\LoggerAwareInterface;
33
use Psr\Log\LogLevel;
34
35
/**
36
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
37
 * @SuppressWarnings(PHPMD.MissingImport)
38
 * @SuppressWarnings(PHPMD.StaticAccess)
39
 */
40
class CommonApiClient implements CommonApiClientInterface
41
{
42
    const URL_SANDBOX = 'https://proxy.staging.devpayever.com/';
43
    const URL_LIVE    = 'https://proxy.payever.org/';
44
45
    const SUB_URL_AUTH              = 'oauth/v2/token';
46
    const SUB_URL_LIST_CHANNEL_SETS = 'api/shop/%s/channel-sets';
47
    const SUB_URL_CURRENCY          = 'api/rest/v1/currency';
48
49
    /**
50
     * Stores oAuth Authentication Tokens
51
     *
52
     * @var OauthTokenList $tokens
53
     */
54
    protected $tokens;
55
56
    /**
57
     * Stores API Configuration
58
     *
59
     * @var ClientConfigurationInterface $configuration
60
     */
61
    protected $configuration;
62
63
    /**
64
     * Stores current Client
65
     *
66
     * @var HttpClientInterface $httpClient
67
     */
68
    protected $httpClient;
69
70
    /**
71
     * @param ClientConfigurationInterface $clientConfiguration
72
     * @param OauthTokenList $oauthTokenList
73
     * @param HttpClientInterface $httpClient
74
     *
75
     * @throws \Exception
76
     */
77
    public function __construct(
78
        ClientConfigurationInterface $clientConfiguration,
79
        OauthTokenList $oauthTokenList = null,
80
        HttpClientInterface $httpClient = null
81
    ) {
82
        $this->configuration = $clientConfiguration;
83
        $this->httpClient = $httpClient;
84
        $this->loadTokens($oauthTokenList);
85
    }
86
87
    /**
88
     * Returns Base URL to payever Payments API
89
     *
90
     * @return string
91
     */
92
    public function getBaseUrl()
93
    {
94
        return $this->getBaseEntrypoint(true);
95
    }
96
97
    /**
98
     * Returns current configuration
99
     *
100
     * @return ClientConfigurationInterface
101
     */
102
    public function getConfiguration()
103
    {
104
        return $this->configuration;
105
    }
106
107
    /**
108
     * Returns current OauthToken list
109
     *
110
     * @return OauthTokenList
111
     */
112
    public function getTokens()
113
    {
114
        return $this->tokens;
115
    }
116
117
    /**
118
     * Overrides configuration
119
     *
120
     * @param ClientConfigurationInterface $configuration
121
     */
122
    public function setConfiguration(ClientConfigurationInterface $configuration)
123
    {
124
        $this->configuration = $configuration;
125
126
        $this->getTokens()->clear()->save();
127
    }
128
129
    /**
130
     * {@inheritdoc}
131
     */
132
    public function getHttpClient()
133
    {
134
        if ($this->httpClient === null) {
135
            $this->httpClient = new CurlClient();
136
        }
137
138
        if ($this->httpClient instanceof LoggerAwareInterface) {
139
            $this->httpClient->setLogger($this->configuration->getLogger());
140
        }
141
142
        return $this->httpClient;
143
    }
144
145
    /**
146
     * @param string $logLevel
147
     * @return $this
148
     */
149
    public function setHttpClientRequestFailureLogLevel($logLevel = LogLevel::CRITICAL)
150
    {
151
        if ($this->getHttpClient() instanceof CurlClient) {
152
            $this->getHttpClient()->setLogLevel($logLevel);
153
        }
154
155
        return $this;
156
    }
157
158
    /**
159
     * @param string $logLevel
160
     * @return $this
161
     */
162
    public function setHttpClientRequestFailureLogLevelOnce($logLevel)
163
    {
164
        if ($this->getHttpClient() instanceof CurlClient) {
165
            $this->getHttpClient()->setLogLevelOnce($logLevel);
166
        }
167
168
        return $this;
169
    }
170
171
    /**
172
     * {@inheritdoc}
173
     */
174
    public function setHttpClient(HttpClientInterface $httpClient)
175
    {
176
        $this->configuration->getLogger()->debug(
177
            sprintf('Got new HTTP Client: %s', get_class($httpClient))
178
        );
179
180
        $this->httpClient = $httpClient;
181
182
        return $this;
183
    }
184
185
    /**
186
     * Returns Authentication OauthToken
187
     *
188
     * @param string $scope OauthToken scope
189
     *
190
     * @return OauthTokenInterface
191
     *
192
     * @throws \Exception
193
     */
194
    public function getToken($scope = OauthTokenInterface::SCOPE_PAYMENT_ACTIONS)
195
    {
196
        $key = md5($this->getConfiguration()->getHash() . $scope);
197
198
        $this->configuration->getLogger()->debug(sprintf('Getting OAuth token. Hash: %s', $key));
199
200
        /** @var OauthTokenInterface|false $token */
201
        $token = $this->getTokens()->get($key);
202
203
        if (!$token || ($token instanceof OauthTokenInterface && $token->isExpired() && !$token->isRefreshable())) {
204
            $tokenData = $this->obtainTokenRequest($scope)->getResponseEntity()->toArray();
205
206
            $token = $this->getTokens()->add(
207
                $key,
208
                $this->getTokens()->create()->load($tokenData)->setUpdatedAt()
209
            )->get($key);
210
211
            $this->getTokens()->save();
212
        } elseif ($token instanceof OauthTokenInterface && $token->isExpired() && $token->isRefreshable()) {
213
            $tokenData = $this->refreshTokenRequest($token)->getResponseEntity()->toArray();
214
215
            $token->load($tokenData)->setUpdatedAt();
216
217
            $this->getTokens()->save();
218
        }
219
220
        $this->configuration->getLogger()->debug(
221
            sprintf('Got OAuth token. Hash: %s', $key),
222
            $token->getParams()
223
        );
224
225
        return $token;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $token; (array|boolean|object) is incompatible with the return type declared by the interface Payever\ExternalIntegrat...ientInterface::getToken of type Payever\ExternalIntegrat...ase\OauthTokenInterface.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     *
231
     * @throws \Exception
232
     */
233
    public function getCurrenciesRequest($lang = '')
234
    {
235
        $request = RequestBuilder::get($this->getCurrenciesURL($lang))
236
            ->setResponseEntity(new GetCurrenciesResponse())
237
            ->build()
238
        ;
239
240
        return $this->getHttpClient()->execute($request);
241
    }
242
243
    /**
244
     * {@inheritdoc}
245
     *
246
     * @throws \Exception
247
     */
248
    public function listChannelSetsRequest($businessUuid)
249
    {
250
        $this->configuration->assertLoaded();
251
252
        $request = RequestBuilder::get($this->getListChannelSetsURL($businessUuid))
253
            ->setResponseEntity(new ListChannelSetsResponse())
254
            ->build()
255
        ;
256
257
        return $this->getHttpClient()->execute($request);
258
    }
259
260
    /**
261
     * @param bool $staticBind
262
     * @return string
263
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
264
     */
265
    protected function getBaseEntrypoint($staticBind = false)
266
    {
267
        switch ($this->configuration->getApiMode()) {
268
            case ClientConfiguration::API_MODE_SANDBOX:
269
                $default = $staticBind ? static::URL_SANDBOX : self::URL_SANDBOX;
270
                $url = $this->configuration->getCustomSandboxUrl() ?: $default;
271
                break;
272
            case ClientConfiguration::API_MODE_LIVE:
273
            default:
274
                $default = $staticBind ? static::URL_LIVE : self::URL_LIVE;
275
                $url = $this->configuration->getCustomLiveUrl() ?: $default;
276
                break;
277
        }
278
279
        if (substr($url, -1) != '/') {
280
            $url .= '/';
281
        }
282
283
        return $url;
284
    }
285
286
    /**
287
     * Loads Tokens
288
     *
289
     * @param OauthTokenList|null $oauthTokenList
290
     *
291
     * @return $this
292
     */
293
    protected function loadTokens(OauthTokenList $oauthTokenList = null)
294
    {
295
        if (is_null($oauthTokenList)) {
296
            $oauthTokenList = new DummyOauthTokenList();
297
        }
298
299
        $this->tokens = $oauthTokenList->load();
300
301
        return $this;
302
    }
303
304
    /**
305
     * Requests new oAuth OauthToken which will be used further
306
     *
307
     * @link https://docs.payever.org/shopsystems/api/getting-started/authentication Documentation
308
     *
309
     * @param string $scope Scope in which the token will be used
310
     *
311
     * @return ResponseInterface
312
     *
313
     * @throws \Exception
314
     */
315
    protected function obtainTokenRequest($scope)
316
    {
317
        $this->configuration->assertLoaded();
318
319
        if (!in_array($scope, OauthToken::getScopes())) {
320
            throw new \Exception('Scope provided is not valid');
321
        }
322
323
        $requestEntity = new AuthenticationRequest();
324
        $requestEntity
325
            ->setClientId($this->configuration->getClientId())
326
            ->setClientSecret($this->configuration->getClientSecret())
327
            ->setScope($scope)
328
            ->setGrantType(OauthTokenInterface::GRAND_TYPE_OBTAIN_TOKEN);
329
330
        $request = RequestBuilder::post($this->getAuthenticationURL())
331
            ->setRequestEntity($requestEntity)
332
            ->setResponseEntity(new AuthenticationResponse())
333
            ->build();
334
335
        return $this->getHttpClient()->execute($request);
336
    }
337
338
    /**
339
     * Requests for an updated oAuth OauthToken data
340
     *
341
     * @param OauthTokenInterface|object|array $token OauthToken for the update
342
     *
343
     * @return ResponseInterface
344
     *
345
     * @throws \Exception
346
     */
347
    protected function refreshTokenRequest(OauthTokenInterface $token)
348
    {
349
        $this->configuration->assertLoaded();
350
351
        $requestEntity = new AuthenticationRequest();
352
        $requestEntity
353
            ->setClientId($this->configuration->getClientId())
354
            ->setClientSecret($this->configuration->getClientSecret())
355
            ->setScope($token->getScope())
356
            ->setGrantType(OauthTokenInterface::GRAND_TYPE_REFRESH_TOKEN)
357
            ->setRefreshToken($token->getRefreshToken());
358
359
        $request = RequestBuilder::post($this->getAuthenticationURL())
360
            ->setRequestEntity($requestEntity)
361
            ->setResponseEntity(new AuthenticationResponse())
362
            ->build();
363
364
        return $this->getHttpClient()->execute($request);
365
    }
366
367
    /**
368
     * Returns URL for Authentication path
369
     *
370
     * @return string
371
     */
372
    protected function getAuthenticationURL()
373
    {
374
        return $this->getBaseEntrypoint() . self::SUB_URL_AUTH;
375
    }
376
377
    /**
378
     * Returns URL for Available Channel Sets path
379
     *
380
     * @param string $businessUuid
381
     *
382
     * @return string
383
     */
384
    protected function getListChannelSetsURL($businessUuid)
385
    {
386
        return $this->getBaseEntrypoint() . sprintf(self::SUB_URL_LIST_CHANNEL_SETS, $businessUuid);
387
    }
388
389
    /**
390
     * Returns URL to Currencies path
391
     *
392
     * @param string $lang
393
     *
394
     * @return string
395
     */
396
    protected function getCurrenciesURL($lang = '')
397
    {
398
        return $this->getBaseEntrypoint() . self::SUB_URL_CURRENCY . (empty($lang) ? '' : '?_locale=' . $lang);
399
    }
400
}
401