JibitClient::setAccessToken()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
namespace Shetabit\Multipay\Drivers\Jibit;
3
4
use chillerlan\SimpleCache\CacheException;
5
use chillerlan\SimpleCache\CacheOptions;
6
use chillerlan\SimpleCache\FileCache;
7
use Psr\SimpleCache\InvalidArgumentException;
8
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
9
10
class JibitClient
11
{
12
    /**
13
     * Access token
14
     * @var string
15
     */
16
    public $accessToken;
17
18
    /**
19
     * API key
20
     *
21
     * @var string
22
     */
23
    private $apiKey;
24
25
    /**
26
     * Secret key
27
     *
28
     * @var string
29
     */
30
    private $secretKey;
31
32
    /**
33
     * Refresh token
34
     * @var string
35
     */
36
    private $refreshToken;
37
38
    /**
39
     * Cache
40
     *
41
     * @var FileCache
42
     */
43
    private $cache;
44
45
    /**
46
     * Base URL
47
     *
48
     * @var string
49
     */
50
    public $baseUrl;
51
52
53
    /**
54
     * @throws CacheException
55
     */
56
    public function __construct($apiKey, $secretKey, $baseUrl, $cachePath)
57
    {
58
        $this->baseUrl = $baseUrl;
59
        $this->apiKey = $apiKey;
60
        $this->secretKey = $secretKey;
61
        $this->cache = new FileCache(
62
            new CacheOptions([
63
                'filestorage' => $cachePath,
64
            ])
65
        );
66
    }
67
68
    /**
69
     * Request payment
70
     *
71
     * @param int $amount
72
     * @param string $referenceNumber
73
     * @param string $userIdentifier
74
     * @param string $callbackUrl
75
     * @param string $currency
76
     * @param null $description
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $description is correct as it would always require null to be passed?
Loading history...
77
     * @param $additionalData
78
     * @return bool|mixed|string
79
     * @throws PurchaseFailedException
80
     */
81
    public function paymentRequest($amount, $referenceNumber, $userIdentifier, $callbackUrl, $currency = 'IRR', $description = null, $additionalData = null)
82
    {
83
        $this->generateToken();
84
85
        $data = [
86
            'additionalData' => $additionalData,
87
            'amount' => $amount,
88
            'callbackUrl' => $callbackUrl,
89
            'clientReferenceNumber' => $referenceNumber,
90
            'currency' => $currency,
91
            'userIdentifier' => $userIdentifier,
92
            'description' => $description,
93
        ];
94
95
        return $this->callCurl('/purchases', $data, true);
96
    }
97
98
    /**
99
     *
100
     * Get order by ID
101
     * @param $id
102
     * @return bool|mixed|string
103
     * @throws PurchaseFailedException
104
     */
105
    public function getOrderById($id)
106
    {
107
        return  $this->callCurl('/purchases?purchaseId=' . $id, [], true, 0, 'GET');
108
    }
109
110
    /**
111
     * Generate token
112
     *
113
     * @param bool $isForce
114
     * @return string
115
     * @throws PurchaseFailedException
116
     * @throws InvalidArgumentException
117
     */
118
    private function generateToken($isForce = false)
119
    {
120
        if ($isForce === false && $this->cache->has('accessToken')) {
121
            return $this->setAccessToken($this->cache->get('accessToken'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setAccessToken($t...he->get('accessToken')) targeting Shetabit\Multipay\Driver...lient::setAccessToken() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
122
        } elseif ($this->cache->has('refreshToken')) {
123
            $refreshToken = $this->refreshTokens();
124
125
            if ($refreshToken !== 'ok') {
126
                return $this->generateNewToken();
127
            }
128
        } else {
129
            return $this->generateNewToken();
130
        }
131
132
        throw new PurchaseFailedException('Token generation encountered an error.');
133
    }
134
135
    /**
136
     * Refresh tokens
137
     * @throws PurchaseFailedException
138
     * @throws InvalidArgumentException
139
     */
140
    private function refreshTokens()
141
    {
142
        $data = [
143
            'accessToken' => str_replace('Bearer ', '', $this->cache->get('accessToken')),
144
            'refreshToken' => $this->cache->get('refreshToken'),
145
        ];
146
147
        $result = $this->callCurl('/tokens/refresh', $data, false);
148
149
        if (empty($result['accessToken'])) {
150
            throw new PurchaseFailedException('Refresh token encountered an error.');
151
        }
152
153
        if (!empty($result['accessToken'])) {
154
            $this->cache->set('accessToken', 'Bearer ' . $result['accessToken'], 24 * 60 * 60 - 60);
155
            $this->cache->set('refreshToken', $result['refreshToken'], 48 * 60 * 60 - 60);
156
157
            $this->setAccessToken('Bearer ' . $result['accessToken']);
158
            $this->setRefreshToken($result['refreshToken']);
159
160
            return 'ok';
161
        }
162
163
        throw new PurchaseFailedException('Refresh token encountered an error.');
164
    }
165
166
    /**
167
     * Call curl
168
     *
169
     * @param $url
170
     * @param $arrayData
171
     * @param bool $haveAuth
172
     * @param int $try
173
     * @param string $method
174
     * @return bool|mixed|string
175
     * @throws PurchaseFailedException
176
     */
177
    private function callCurl($url, $arrayData, $haveAuth = false, $try = 0, $method = 'POST')
178
    {
179
        $data = $arrayData;
180
        $jsonData = json_encode($data);
181
        $accessToken = '';
182
183
        if ($haveAuth) {
184
            $accessToken = $this->getAccessToken();
185
        }
186
187
        $ch = curl_init($this->baseUrl . $url);
188
        curl_setopt($ch, CURLOPT_USERAGENT, 'Jibit.class Rest Api');
189
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
190
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
191
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
192
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
193
            'Content-Type: application/json',
194
            'Authorization: ' . $accessToken,
195
            'Content-Length: ' . strlen($jsonData)
196
        ]);
197
198
        $result = curl_exec($ch);
199
        $err = curl_error($ch);
200
        $result = json_decode($result, true);
0 ignored issues
show
Bug introduced by
It seems like $result can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

200
        $result = json_decode(/** @scrutinizer ignore-type */ $result, true);
Loading history...
201
        curl_close($ch);
202
203
        if ($err) {
204
            throw new PurchaseFailedException('cURL Error #:' . $err);
205
        }
206
207
        if (empty($result['errors'])) {
208
            return $result;
209
        }
210
211
        if ($haveAuth === true && $result['errors'][0]['code'] === 'security.auth_required') {
212
            $this->generateToken(true);
213
214
            if ($try === 0) {
215
                return $this->callCurl($url, $arrayData, $haveAuth, 1, $method);
216
            }
217
218
            throw new PurchaseFailedException('Authentication encountered an error.');
219
        }
220
221
        return $result;
222
    }
223
224
    /**
225
     * Get access token
226
     *
227
     * @return mixed
228
     */
229
    public function getAccessToken()
230
    {
231
        return $this->accessToken;
232
    }
233
234
    /**
235
     * Set access token
236
     *
237
     * @param mixed $accessToken
238
     */
239
    public function setAccessToken($accessToken)
240
    {
241
        $this->accessToken = $accessToken;
242
    }
243
244
    /**
245
     * Set refresh token
246
     *
247
     * @param mixed $refreshToken
248
     */
249
    public function setRefreshToken($refreshToken)
250
    {
251
        $this->refreshToken = $refreshToken;
252
    }
253
254
    /**
255
     * Generate new token
256
     *
257
     * @return string
258
     * @throws PurchaseFailedException
259
     * @throws InvalidArgumentException
260
     */
261
    private function generateNewToken()
262
    {
263
        $data = [
264
            'apiKey' => $this->apiKey,
265
            'secretKey' => $this->secretKey,
266
        ];
267
268
        $result = $this->callCurl('/tokens', $data);
269
270
        if (empty($result['accessToken'])) {
271
            throw new PurchaseFailedException('Token generation encoutered an error.');
272
        }
273
274
        if (! empty($result['accessToken'])) {
275
            $this->cache->set('accessToken', 'Bearer ' . $result['accessToken'], 24 * 60 * 60 - 60);
276
            $this->cache->set('refreshToken', $result['refreshToken'], 48 * 60 * 60 - 60);
277
278
            $this->setAccessToken('Bearer ' . $result['accessToken']);
279
            $this->setRefreshToken($result['refreshToken']);
280
281
            return 'ok';
282
        }
283
284
        throw new PurchaseFailedException('Token generation encoutered an error.');
285
    }
286
287
    /**
288
     * Verify payment
289
     *
290
     * @param string $purchaseId
291
     * @return bool|mixed|string
292
     * @throws PurchaseFailedException
293
     */
294
    public function paymentVerify($purchaseId)
295
    {
296
        $this->generateToken();
297
        $data = [];
298
299
        return $this->callCurl('/purchases/' . $purchaseId . '/verify', $data, true, 0, 'GET');
300
    }
301
}
302