Api   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 192
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 11
dl 0
loc 192
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A setUrl() 0 6 1
A setUsername() 0 6 1
A setPassword() 0 6 1
A callService() 0 6 1
A call() 0 11 2
B makeCallRequest() 0 47 9
A authorize() 0 22 4
A token() 0 11 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace AcquiroPay;
6
7
use Exception;
8
use GuzzleHttp\Client;
9
use Illuminate\Support\Str;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, AcquiroPay\Str.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
10
use GuzzleHttp\Psr7\Request;
11
use AcquiroPay\Contracts\Cache;
12
use Psr\Http\Message\StreamInterface;
13
use AcquiroPay\Exceptions\BaseException;
14
use GuzzleHttp\Exception\GuzzleException;
15
use GuzzleHttp\Exception\RequestException;
16
use AcquiroPay\Exceptions\NotFoundException;
17
use AcquiroPay\Exceptions\ForbiddenException;
18
use AcquiroPay\Exceptions\UnauthorizedException;
19
20
class Api
21
{
22
    protected $cache;
23
    protected $http;
24
25
    protected $url;
26
    protected $username;
27
    protected $password;
28
29
    public function __construct(Cache $cache, Client $http)
30
    {
31
        $this->cache = $cache;
32
        $this->http = $http;
33
    }
34
35
    public function setUrl(string $url): self
36
    {
37
        $this->url = $url;
38
39
        return $this;
40
    }
41
42
    public function setUsername(string $username): self
43
    {
44
        $this->username = $username;
45
46
        return $this;
47
    }
48
49
    public function setPassword(string $password): self
50
    {
51
        $this->password = $password;
52
53
        return $this;
54
    }
55
56
    /**
57
     * @param string $service
58
     * @param string $method
59
     * @param string $endpoint
60
     * @param array|null $parameters
61
     *
62
     * @param array $headers
63
     * @return mixed|string
64
     *
65
     * @throws BaseException
66
     * @throws ForbiddenException
67
     * @throws GuzzleException
68
     * @throws NotFoundException
69
     */
70
    public function callService(string $service, string $method, string $endpoint, array $parameters = null, array $headers = [])
71
    {
72
        $headers = array_merge(['Endpoint' => $endpoint], $headers);
73
74
        return $this->call($method, '/services/'.$service, $headers, $parameters);
75
    }
76
77
    /**
78
     * @param string $method
79
     * @param string $endpoint
80
     * @param array $headers
81
     * @param array|null $parameters
82
     *
83
     * @return mixed|string
84
     *
85
     * @throws ForbiddenException
86
     * @throws NotFoundException
87
     * @throws BaseException
88
     * @throws \GuzzleHttp\Exception\GuzzleException
89
     */
90
    public function call(string $method, string $endpoint, array $headers = [], array $parameters = null)
91
    {
92
        $stream = $this->makeCallRequest($method, $endpoint, $headers, $parameters);
93
        $json = json_decode((string) $stream);
94
95
        if (json_last_error() === JSON_ERROR_NONE) {
96
            return $json;
97
        }
98
99
        return (string) $stream;
100
    }
101
102
    /**
103
     * @param string $method
104
     * @param string $endpoint
105
     * @param array $headers
106
     * @param array|null $parameters
107
     * @param bool $retry
108
     *
109
     * @return StreamInterface
110
     *
111
     * @throws ForbiddenException
112
     * @throws NotFoundException
113
     * @throws BaseException
114
     * @throws GuzzleException
115
     */
116
    public function makeCallRequest(
117
        string $method,
118
        string $endpoint,
119
        array $headers = [],
120
        array $parameters = null,
121
        bool $retry = true
122
    ): StreamInterface {
123
        $method = Str::upper($method);
124
        if (!Str::startsWith($endpoint, ['http://', 'https://'])) {
125
            $endpoint = $this->url.'/'.ltrim($endpoint, '/');
126
        }
127
128
        try {
129
            $options = [
130
                'headers' => array_merge([
131
                    'Accept' => 'application/json',
132
                    'Content-Type' => 'application/json',
133
                    'Authorization' => 'Bearer '.$this->token(),
134
                ], $headers),
135
            ];
136
137
            if ($method === 'GET') {
138
                $options['query'] = $parameters;
139
            } else {
140
                $options['json'] = $parameters;
141
            }
142
143
            return $this->http->request($method, $endpoint, $options)->getBody();
144
        } catch (RequestException $exception) {
145
            $response = $exception->getResponse();
146
147
            if ($retry && $response && $response->getStatusCode() === 401) {
148
                $this->token();
149
150
                return $this->makeCallRequest($method, $endpoint, $headers, $parameters, false);
151
            }
152
153
            switch ($response->getStatusCode()) {
154
                case 404:
155
                    throw new NotFoundException((string) $response->getBody());
156
                case 403:
157
                    throw new ForbiddenException;
158
                default:
159
                    throw BaseException::fromGuzzle($exception);
160
            }
161
        }
162
    }
163
164
    /**
165
     * Authorize token for performing request.
166
     *
167
     * @param string $token
168
     * @param string $service
169
     * @param string $method
170
     * @param string $endpoint
171
     *
172
     * @return Consumer
173
     *
174
     * @throws UnauthorizedException
175
     * @throws \GuzzleHttp\Exception\GuzzleException
176
     */
177
    public function authorize(string $token, string $service, string $method, string $endpoint): Consumer
178
    {
179
        try {
180
            $headers = ['Content-Type' => 'application/json'];
181
182
            $url = $this->url.'/authorize';
183
184
            $body = json_encode(compact('token', 'service', 'method', 'endpoint'));
185
186
            $response = $this->http->send(new Request('POST', $url, $headers, $body));
187
188
            $json = \GuzzleHttp\json_decode((string) $response->getBody());
189
190
            if (!isset($json->authorized, $json->consumer_id) || $json->authorized !== true) {
191
                throw new UnauthorizedException('Consumer is not authorized for this request.');
192
            }
193
194
            return Consumer::create($json->consumer_id);
195
        } catch (Exception $exception) {
196
            throw new UnauthorizedException('Something went wrong.', 0, $exception);
197
        }
198
    }
199
200
    protected function token(): ?string
201
    {
202
        return $this->cache->remember('acquiropay_api_token_'.md5($this->url), 10, function () {
203
            $response = $this->http->post(
204
                $this->url.'/login',
205
                ['form_params' => ['username' => $this->username, 'password' => $this->password]]
206
            );
207
208
            return (string) $response->getBody();
209
        });
210
    }
211
}
212