Completed
Push — master ( 865763...2a953d )
by
unknown
16s queued 11s
created

Bankly::setClientCredentials()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace WeDevBr\Bankly;
4
5
use Illuminate\Http\Client\RequestException;
6
use Illuminate\Support\Facades\Http;
7
use Ramsey\Uuid\Uuid;
8
use TypeError;
9
use WeDevBr\Bankly\Inputs\Customer;
10
use WeDevBr\Bankly\Inputs\DocumentAnalysis;
11
use WeDevBr\Bankly\Support\Contracts\CustomerInterface;
12
use WeDevBr\Bankly\Support\Contracts\DocumentInterface;
13
14
/**
15
 * Class Bankly
16
 * @author Adeildo Amorim <[email protected]>
17
 * @package WeDevBr\Bankly
18
 */
19
class Bankly
20
{
21
    public $api_url;
22
    public $login_url;
23
    private $client_id;
24
    private $client_secret;
25
    private $token_expiry = 0;
26
    private $token = null;
27
    private $api_version = '1.0';
28
29
    /**
30
     * Bankly constructor.
31
     * @param null|string $client_secret provided by Bankly Staff
32
     * @param null|string $client_id provided by Bankly Staff
33
     */
34
    public function __construct($client_secret = null, $client_id = null)
35
    {
36
        $this->api_url = config('bankly')['api_url'];
37
        $this->login_url = config('bankly')['login_url'];
38
        $this->setClientCredentials(['client_secret' => $client_secret, 'client_id' => $client_id]);
39
    }
40
41
    /**
42
     * @param array|null $credentials
43
     * @return $this
44
     */
45
    public function setClientCredentials(array $credentials = null)
46
    {
47
        $this->client_secret = $credentials['client_secret'] ?? config('bankly')['client_secret'];
48
        $this->client_id = $credentials['client_id'] ?? config('bankly')['client_id'];
49
        return $this;
50
    }
51
52
    /**
53
     * @return array|mixed
54
     * @throws RequestException
55
     */
56
    public function getBankList()
57
    {
58
        return $this->get('/banklist');
59
    }
60
61
    /**
62
     * Retrieve your balance account
63
     * @param string $branch
64
     * @param string $account
65
     * @return array|mixed
66
     * @throws RequestException
67
     * @note If you have a RequestException on this endpoint in staging environment, please use getAccount() method instead.
68
     */
69
    public function getBalance(string $branch, string $account)
70
71
    {
72
        return $this->get('/account/balance', [
73
            'branch' => $branch,
74
            'account' => $account
75
        ]);
76
    }
77
78
    /**
79
     * @param string $account
80
     * @param string $includeBalance
81
     * @return array|mixed
82
     * @throws RequestException
83
     * @note This method on this date (2020-10-21) works only on staging environment. Contact Bankly/Acesso for more details
84
     */
85
    public function getAccount(string $account, string $includeBalance = 'true')
86
    {
87
        return $this->get('/accounts/' . $account, [
88
            'includeBalance' => $includeBalance,
89
        ]);
90
    }
91
92
    /**
93
     * @param $branch
94
     * @param $account
95
     * @param int $offset
96
     * @param int $limit
97
     * @param string $details
98
     * @param string $detailsLevelBasic
99
     * @return array|mixed
100
     * @throws RequestException
101
     */
102
    public function getStatement(
103
        $branch,
104
        $account,
105
        $offset = 1,
106
        $limit = 20,
107
        string $details = 'true',
108
        string $detailsLevelBasic = 'true'
109
    ) {
110
        return $this->get('/account/statement', array(
111
            'branch' => $branch,
112
            'account' => $account,
113
            'offset' => $offset,
114
            'limit' => $limit,
115
            'details' => $details,
116
            'detailsLevelBasic' => $detailsLevelBasic
117
        ));
118
    }
119
120
    /**
121
     * @param string $branch
122
     * @param string $account
123
     * @param int $page
124
     * @param int $pagesize
125
     * @param string $include_details
126
     * @return array|mixed
127
     * @throws RequestException
128
     * @note This endpoint has been deprecated for some clients.
129
     * You need to check with Acesso/Bankly if your environment has different parameters also.
130
     * The response of this request does not have a default interface between environments.
131
     * Pay attention when use this in your project.
132
     */
133
    public function getEvents(
134
        string $branch,
135
        string $account,
136
        int $page = 1,
137
        int $pagesize = 20,
138
        string $include_details = 'true'
139
    ) {
140
        return $this->get(
141
            '/events',
142
            [
143
                'branch' => $branch,
144
                'account' => $account,
145
                'page' => $page,
146
                'pageSize' => $pagesize,
147
                'includeDetails' => $include_details
148
149
            ]
150
        );
151
    }
152
153
    /**
154
     * @param int $amount
155
     * @param string $description
156
     * @param array $sender
157
     * @param array $recipient
158
     * @param string|null $correlation_id
159
     * @return array|mixed
160
     * @throws RequestException
161
     */
162
    public function transfer(
163
        int $amount,
164
        string $description,
165
        array $sender,
166
        array $recipient,
167
        string $correlation_id = null
168
    ) {
169
        if ($sender['bankCode']) {
170
            unset($sender['bankCode']);
171
        }
172
173
        return $this->post(
174
            '/fund-transfers',
175
            [
176
                'amount' => $amount,
177
                'description' => $description,
178
                'sender' => $sender,
179
                'recipient' => $recipient
180
            ],
181
            $correlation_id,
182
            true
183
        );
184
    }
185
186
    /**
187
     * Get transfer funds from an account
188
     * @param string $branch
189
     * @param string $account
190
     * @param int $pageSize
191
     * @param string|null $nextPage
192
     * @return array|mixed
193
     * @throws RequestException
194
     */
195
    public function getTransferFunds(string $branch, string $account, int $pageSize = 10, string $nextPage = null)
196
    {
197
        $queryParams = [
198
            'branch' => $branch,
199
            'account' => $account,
200
            'pageSize' => $pageSize
201
        ];
202
        if ($nextPage) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $nextPage of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
203
            $queryParams['nextPage'] = $nextPage;
204
        }
205
        return $this->get('/fund-transfers', $queryParams);
206
    }
207
208
    /**
209
     * Get Transfer Funds By Authentication Code
210
     * @param string $branch
211
     * @param string $account
212
     * @param string $authenticationCode
213
     * @return array|mixed
214
     * @throws RequestException
215
     */
216
    public function findTransferFundByAuthCode(string $branch, string $account, string $authenticationCode)
217
    {
218
        $queryParams = [
219
            'branch' => $branch,
220
            'account' => $account
221
        ];
222
        return $this->get('/fund-transfers/' . $authenticationCode, $queryParams);
223
    }
224
225
    /**
226
     * @param string $branch
227
     * @param string $account
228
     * @param string $authentication_id
229
     * @return array|mixed
230
     * @throws RequestException
231
     */
232
    public function getTransferStatus(string $branch, string $account, string $authentication_id)
233
    {
234
        return $this->get('/fund-transfers/' . $authentication_id . '/status', [
235
            'branch' => $branch,
236
            'account' => $account
237
        ]);
238
    }
239
240
    /**
241
     * @param string $documentNumber
242
     * @param DocumentAnalysis $document
243
     * @param string $correlationId
244
     * @return array|mixed
245
     * @throws RequestException
246
     */
247
    public function documentAnalysis(
248
        string $documentNumber,
249
        $document,
250
        string $correlationId = null
251
    ) {
252
        if (!$document instanceof DocumentInterface) {
253
            throw new TypeError('The document must be an instance of DocumentInterface');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The document must be an...e of DocumentInterface'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
254
        }
255
256
        return $this->put(
257
            "/document-analysis/{$documentNumber}",
258
            [
259
                'documentType' => $document->getDocumentType(),
260
                'documentSide' => $document->getDocumentSide(),
261
            ],
262
            $correlationId,
263
            true,
264
            true,
265
            $document
266
        );
267
    }
268
269
    /**
270
     * @param string $documentNumber
271
     * @param array $tokens
272
     * @param string $resultLevel
273
     * @param string $correlationId
274
     * @return array|mixed
275
     */
276
    public function getDocumentAnalysis(
277
        string $documentNumber,
278
        array $tokens = [],
279
        string $resultLevel = 'ONLY_STATUS',
280
        string $correlationId = null
281
    ) {
282
        $query = ['resultLevel' => $resultLevel];
283
284
        if (!empty($tokens)) {
285
            $query['token'] = $tokens;
286
        }
287
288
        return $this->get(
289
            "/document-analysis/{$documentNumber}",
290
            $query,
291
            $correlationId
0 ignored issues
show
Bug introduced by
It seems like $correlationId defined by parameter $correlationId on line 280 can also be of type string; however, WeDevBr\Bankly\Bankly::get() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
292
        );
293
    }
294
295
    /**
296
     * @param string $documentNumber
297
     * @param Customer $customer
298
     * @param string $correlationId
299
     * @return array|mixed
300
     * @throws RequestException
301
     */
302
    public function customer(
303
        string $documentNumber,
304
        $customer,
305
        string $correlationId = null
306
    ) {
307
        if (!$customer instanceof CustomerInterface) {
308
            throw new TypeError('The customer must be an instance of CustomerInterface');
0 ignored issues
show
Unused Code introduced by
The call to TypeError::__construct() has too many arguments starting with 'The customer must be an...e of CustomerInterface'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
309
        }
310
311
        return $this->put("/customers/{$documentNumber}", $customer->toArray(), $correlationId);
312
    }
313
314
    /**
315
     * @param string $endpoint
316
     * @param array|null $query
317
     * @param null $correlation_id
318
     * @return array|mixed
319
     * @throws RequestException
320
     */
321
    private function get(string $endpoint, array $query = null, $correlation_id = null)
322
    {
323
        if (now()->unix() > $this->token_expiry || !$this->token) {
324
            $this->auth();
325
        }
326
327
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
328
            $correlation_id = Uuid::uuid4()->toString();
329
        }
330
331
        return Http::withToken($this->token)
332
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
333
            ->get($this->getFinalUrl($endpoint), $query)
334
            ->throw()
335
            ->json();
336
    }
337
338
    /**
339
     * @param string $endpoint
340
     * @param array|null $body
341
     * @param string|null $correlation_id
342
     * @param bool $asJson
343
     * @return array|mixed
344
     * @throws RequestException
345
     */
346
    private function post(string $endpoint, array $body = null, string $correlation_id = null, bool $asJson = false)
347
    {
348
        if (now()->unix() > $this->token_expiry || !$this->token) {
349
            $this->auth();
350
        }
351
352
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
353
            $correlation_id = Uuid::uuid4()->toString();
354
        }
355
356
        $body_format = $asJson ? 'json' : 'form_params';
357
358
        return Http
359
            ::withToken($this->token)
360
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
361
            ->bodyFormat($body_format)
362
            ->post($this->getFinalUrl($endpoint), $body)
0 ignored issues
show
Bug introduced by
It seems like $body defined by parameter $body on line 346 can also be of type null; however, Illuminate\Http\Client\PendingRequest::post() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
363
            ->throw()
364
            ->json();
365
    }
366
367
    /**
368
     * @param string $endpoint
369
     * @param array|null $body
370
     * @param string|null $correlation_id
371
     * @param bool $asJson
372
     * @param bool $attachment
373
     * @param DocumentAnalysis $document
374
     * @param string $fieldName
0 ignored issues
show
Bug introduced by
There is no parameter named $fieldName. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
375
     * @return array|mixed
376
     * @throws RequestException
377
     */
378
    private function put(
379
        string $endpoint,
380
        array $body = [],
381
        string $correlation_id = null,
382
        bool $asJson = false,
383
        bool $attachment = false,
384
        DocumentAnalysis $document = null
385
    ) {
386
        if (now()->unix() > $this->token_expiry || !$this->token) {
387
            $this->auth();
388
        }
389
390
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
391
            $correlation_id = Uuid::uuid4()->toString();
392
        }
393
394
        $body_format = $asJson ? 'json' : 'form_params';
395
396
        $request = Http
397
            ::withToken($this->token)
398
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
399
            ->bodyFormat($body_format);
400
401
        if ($attachment) {
402
            $request->attach($document->getFieldName(), $document->getFileContents(), $document->getFileName());
0 ignored issues
show
Bug introduced by
It seems like $document is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
403
        }
404
405
        return $request->put($this->getFinalUrl($endpoint), $body)
406
            ->throw()
407
            ->json();
408
    }
409
410
    /**
411
     * @param string $version API version
412
     * @return $this
413
     */
414
    private function setApiVersion($version = '1.0')
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
415
    {
416
        $this->api_version = $version;
417
        return $this;
418
    }
419
420
    /**
421
     * @param array $headers
422
     * @return array|string[]
423
     */
424
    private function getHeaders($headers = [])
425
    {
426
        $default_headers = [
427
            'API-Version' => $this->api_version
428
        ];
429
430
        if (count($headers) > 0) {
431
            $default_headers = array_merge($headers, $default_headers);
432
        }
433
434
        return $default_headers;
435
    }
436
437
    /**
438
     * @param string $endpoint
439
     * @return bool
440
     */
441
    private function requireCorrelationId(string $endpoint)
442
    {
443
        $not_required_endpoints = [
444
            '/banklist',
445
            '/connect/token'
446
        ];
447
448
        return !in_array($endpoint, $not_required_endpoints);
449
    }
450
451
    /**
452
     * @param string $endpoint
453
     * @return string
454
     */
455
    private function getFinalUrl(string $endpoint)
456
    {
457
        return $this->api_url . $endpoint;
458
    }
459
460
    /**
461
     * Do authentication
462
     * @param string $grant_type Default sets to 'client_credentials'
463
     * @throws RequestException
464
     */
465
    private function auth($grant_type = 'client_credentials'): void
466
    {
467
        //TODO: Add auth for username and password
468
        $body = [
469
            'grant_type' => $grant_type,
470
            'client_secret' => $this->client_secret,
471
            'client_id' => $this->client_id
472
        ];
473
474
        $response = Http::asForm()->post($this->login_url, $body)->throw()->json();
475
        $this->token = $response['access_token'];
476
        $this->token_expiry = now()->addSeconds($response['expires_in'])->unix();
477
    }
478
}
479