Passed
Pull Request — master (#218)
by
unknown
03:31 queued 01:04
created

JibitClient::paymentRequest()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 10
c 1
b 0
f 1
nc 1
nop 7
dl 0
loc 15
rs 9.9332
1
<?php
2
namespace Shetabit\Multipay\Drivers\Jibit;
3
4
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
5
6
class JibitClient
7
{
8
    /**
9
     * Access token
10
     * @var string
11
     */
12
    public $accessToken;
13
14
    /**
15
     * API key
16
     *
17
     * @var string
18
     */
19
    private $apiKey;
20
21
    /**
22
     * Secret key
23
     *
24
     * @var string
25
     */
26
    private $secretKey;
27
28
    /**
29
     * Refresh token
30
     * @var string
31
     */
32
    private $refreshToken;
33
34
    /**
35
     * Cache
36
     *
37
     * @var JibitFileCache
38
     */
39
    private $cache;
40
41
    /**
42
     * Base URL
43
     *
44
     * @var string
45
     */
46
    public $baseUrl;
47
48
49
    public function __construct($apiKey, $secretKey, $baseUrl, $cachePath)
50
    {
51
        $this->baseUrl = $baseUrl;
52
        $this->apiKey = $apiKey;
53
        $this->secretKey = $secretKey;
54
        $this->cache = new JibitFileCache($cachePath, 'jibit', '.cache');
55
    }
56
57
    /**
58
     * Request payment
59
     *
60
     * @param int $amount
61
     * @param string $referenceNumber
62
     * @param string $userIdentifier
63
     * @param string $callbackUrl
64
     * @param string $currency
65
     * @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...
66
     * @param $additionalData
67
     * @return bool|mixed|string
68
     * @throws PurchaseFailedException
69
     */
70
    public function paymentRequest($amount, $referenceNumber, $userIdentifier, $callbackUrl, $currency = 'IRR', $description = null, $additionalData = null)
71
    {
72
        $this->generateToken();
73
74
        $data = [
75
            'additionalData' => $additionalData,
76
            'amount' => $amount,
77
            'callbackUrl' => $callbackUrl,
78
            'clientReferenceNumber' => $referenceNumber,
79
            'currency' => $currency,
80
            'userIdentifier' => $userIdentifier,
81
            'description' => $description,
82
        ];
83
84
        return $this->callCurl('/purchases', $data, true);
85
    }
86
87
    /**
88
     *
89
     * Get order by ID
90
     * @param $id
91
     * @return bool|mixed|string
92
     * @throws PurchaseFailedException
93
     */
94
    public function getOrderById($id)
95
    {
96
        return  $this->callCurl('/purchases?purchaseId=' . $id, [], true, 0, 'GET');
97
    }
98
99
    /**
100
     * Generate token
101
     *
102
     * @param bool $isForce
103
     * @return string
104
     * @throws PurchaseFailedException
105
     */
106
    private function generateToken($isForce = false)
107
    {
108
        $this->cache->eraseExpired();
109
110
        if ($isForce === false && $this->cache->isCached('accessToken')) {
111
            return $this->setAccessToken($this->cache->retrieve('accessToken'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->setAccessToken($t...etrieve('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...
112
        } elseif ($this->cache->isCached('refreshToken')) {
113
            $refreshToken = $this->refreshTokens();
114
115
            if ($refreshToken !== 'ok') {
116
                return $this->generateNewToken();
117
            }
118
        } else {
119
            return $this->generateNewToken();
120
        }
121
122
        throw new PurchaseFailedException('Token generation encountered an error.');
123
    }
124
125
    private function refreshTokens()
126
    {
127
        $data = [
128
            'accessToken' => str_replace('Bearer ', '', $this->cache->retrieve('accessToken')),
129
            'refreshToken' => $this->cache->retrieve('refreshToken'),
130
        ];
131
132
        $result = $this->callCurl('/tokens/refresh', $data, false);
133
134
        if (empty($result['accessToken'])) {
135
            throw new PurchaseFailedException('Refresh token encountered an error.');
136
        }
137
138
        if (!empty($result['accessToken'])) {
139
            $this->cache->store('accessToken', 'Bearer ' . $result['accessToken'], 24 * 60 * 60 - 60);
140
            $this->cache->store('refreshToken', $result['refreshToken'], 48 * 60 * 60 - 60);
141
142
            $this->setAccessToken('Bearer ' . $result['accessToken']);
143
            $this->setRefreshToken($result['refreshToken']);
144
145
            return 'ok';
146
        }
147
148
        throw new PurchaseFailedException('Refresh token encountered an error.');
149
    }
150
151
    /**
152
     * Call curl
153
     *
154
     * @param $url
155
     * @param $arrayData
156
     * @param bool $haveAuth
157
     * @param int $try
158
     * @param string $method
159
     * @return bool|mixed|string
160
     * @throws PurchaseFailedException
161
     */
162
    private function callCurl($url, $arrayData, $haveAuth = false, $try = 0, $method = 'POST')
163
    {
164
        $data = $arrayData;
165
        $jsonData = json_encode($data);
166
        $accessToken = '';
167
168
        if ($haveAuth) {
169
            $accessToken = $this->getAccessToken();
170
        }
171
172
        $ch = curl_init($this->baseUrl . $url);
173
        curl_setopt($ch, CURLOPT_USERAGENT, 'Jibit.class Rest Api');
174
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
175
        curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
176
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
177
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
178
            'Content-Type: application/json',
179
            'Authorization: ' . $accessToken,
180
            'Content-Length: ' . strlen($jsonData)
181
        ]);
182
183
        $result = curl_exec($ch);
184
        $err = curl_error($ch);
185
        $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

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