Completed
Push — master ( 5971d3...dcc755 )
by
unknown
26s queued 11s
created

Bankly::getPixAddressingKeyValue()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
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
use WeDevBr\Bankly\Types\Pix\PixEntries;
14
use WeDevBr\Bankly\Types\VirtualCard\VirtualCard;
15
16
/**
17
 * Class Bankly
18
 * @author Adeildo Amorim <[email protected]>
19
 * @package WeDevBr\Bankly
20
 */
21
class Bankly
22
{
23
    public $api_url;
24
    public $login_url;
25
    private $client_id;
26
    private $client_secret;
27
    private $token_expiry = 0;
28
    private $token = null;
29
    private $api_version = '1.0';
30
    private $headers;
31
32
    /**
33
     * Bankly constructor.
34
     * @param null|string $client_secret provided by Bankly Staff
35
     * @param null|string $client_id provided by Bankly Staff
36
     */
37
    public function __construct($client_secret = null, $client_id = null)
38
    {
39
        $this->api_url = config('bankly')['api_url'];
40
        $this->login_url = config('bankly')['login_url'];
41
        $this->setClientCredentials(['client_secret' => $client_secret, 'client_id' => $client_id]);
42
        $this->headers = ['API-Version' => $this->api_version];
43
    }
44
45
    /**
46
     * @param array|null $credentials
47
     * @return $this
48
     */
49
    public function setClientCredentials(array $credentials = null)
50
    {
51
        $this->client_secret = $credentials['client_secret'] ?? config('bankly')['client_secret'];
52
        $this->client_id = $credentials['client_id'] ?? config('bankly')['client_id'];
53
        return $this;
54
    }
55
56
    /**
57
     * @return array|mixed
58
     * @throws RequestException
59
     */
60
    public function getBankList()
61
    {
62
        return $this->get('/banklist');
63
    }
64
65
    /**
66
     * Retrieve your balance account
67
     * @param string $branch
68
     * @param string $account
69
     * @return array|mixed
70
     * @throws RequestException
71
     * @note If you have a RequestException on this endpoint in staging environment, please use getAccount() method instead.
72
     */
73
    public function getBalance(string $branch, string $account)
74
75
    {
76
        return $this->get('/account/balance', [
77
            'branch' => $branch,
78
            'account' => $account
79
        ]);
80
    }
81
82
    /**
83
     * @param string $account
84
     * @param string $includeBalance
85
     * @return array|mixed
86
     * @throws RequestException
87
     * @note This method on this date (2020-10-21) works only on staging environment. Contact Bankly/Acesso for more details
88
     */
89
    public function getAccount(string $account, string $includeBalance = 'true')
90
    {
91
        return $this->get('/accounts/' . $account, [
92
            'includeBalance' => $includeBalance,
93
        ]);
94
    }
95
96
    /**
97
     * @param $branch
98
     * @param $account
99
     * @param int $offset
100
     * @param int $limit
101
     * @param string $details
102
     * @param string $detailsLevelBasic
103
     * @return array|mixed
104
     * @throws RequestException
105
     */
106
    public function getStatement(
107
        $branch,
108
        $account,
109
        $offset = 1,
110
        $limit = 20,
111
        string $details = 'true',
112
        string $detailsLevelBasic = 'true'
113
    ) {
114
        return $this->get('/account/statement', array(
115
            'branch' => $branch,
116
            'account' => $account,
117
            'offset' => $offset,
118
            'limit' => $limit,
119
            'details' => $details,
120
            'detailsLevelBasic' => $detailsLevelBasic
121
        ));
122
    }
123
124
    /**
125
     * @param string $branch
126
     * @param string $account
127
     * @param int $page
128
     * @param int $pagesize
129
     * @param string $include_details
130
     * @return array|mixed
131
     * @throws RequestException
132
     * @note This endpoint has been deprecated for some clients.
133
     * You need to check with Acesso/Bankly if your environment has different parameters also.
134
     * The response of this request does not have a default interface between environments.
135
     * Pay attention when use this in your project.
136
     */
137
    public function getEvents(
138
        string $branch,
139
        string $account,
140
        int $page = 1,
141
        int $pagesize = 20,
142
        string $include_details = 'true'
143
    ) {
144
        return $this->get(
145
            '/events',
146
            [
147
                'branch' => $branch,
148
                'account' => $account,
149
                'page' => $page,
150
                'pageSize' => $pagesize,
151
                'includeDetails' => $include_details
152
153
            ]
154
        );
155
    }
156
157
    /**
158
     * @param int $amount
159
     * @param string $description
160
     * @param array $sender
161
     * @param array $recipient
162
     * @param string|null $correlation_id
163
     * @return array|mixed
164
     * @throws RequestException
165
     */
166
    public function transfer(
167
        int $amount,
168
        string $description,
169
        array $sender,
170
        array $recipient,
171
        string $correlation_id = null
172
    ) {
173
        if ($sender['bankCode']) {
174
            unset($sender['bankCode']);
175
        }
176
177
        return $this->post(
178
            '/fund-transfers',
179
            [
180
                'amount' => $amount,
181
                'description' => $description,
182
                'sender' => $sender,
183
                'recipient' => $recipient
184
            ],
185
            $correlation_id,
186
            true
187
        );
188
    }
189
190
    /**
191
     * Get transfer funds from an account
192
     * @param string $branch
193
     * @param string $account
194
     * @param int $pageSize
195
     * @param string|null $nextPage
196
     * @return array|mixed
197
     * @throws RequestException
198
     */
199
    public function getTransferFunds(string $branch, string $account, int $pageSize = 10, string $nextPage = null)
200
    {
201
        $queryParams = [
202
            'branch' => $branch,
203
            'account' => $account,
204
            'pageSize' => $pageSize
205
        ];
206
        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...
207
            $queryParams['nextPage'] = $nextPage;
208
        }
209
        return $this->get('/fund-transfers', $queryParams);
210
    }
211
212
    /**
213
     * Get Transfer Funds By Authentication Code
214
     * @param string $branch
215
     * @param string $account
216
     * @param string $authenticationCode
217
     * @return array|mixed
218
     * @throws RequestException
219
     */
220
    public function findTransferFundByAuthCode(string $branch, string $account, string $authenticationCode)
221
    {
222
        $queryParams = [
223
            'branch' => $branch,
224
            'account' => $account
225
        ];
226
        return $this->get('/fund-transfers/' . $authenticationCode, $queryParams);
227
    }
228
229
    /**
230
     * @param string $branch
231
     * @param string $account
232
     * @param string $authentication_id
233
     * @return array|mixed
234
     * @throws RequestException
235
     */
236
    public function getTransferStatus(string $branch, string $account, string $authentication_id)
237
    {
238
        return $this->get('/fund-transfers/' . $authentication_id . '/status', [
239
            'branch' => $branch,
240
            'account' => $account
241
        ]);
242
    }
243
244
    /**
245
     * @param string $documentNumber
246
     * @param DocumentAnalysis $document
247
     * @param string $correlationId
248
     * @return array|mixed
249
     * @throws RequestException
250
     */
251
    public function documentAnalysis(
252
        string $documentNumber,
253
        $document,
254
        string $correlationId = null
255
    ) {
256
        if (!$document instanceof DocumentInterface) {
257
            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...
258
        }
259
260
        return $this->put(
261
            "/document-analysis/{$documentNumber}",
262
            [
263
                'documentType' => $document->getDocumentType(),
264
                'documentSide' => $document->getDocumentSide(),
265
            ],
266
            $correlationId,
267
            true,
268
            true,
269
            $document
270
        );
271
    }
272
273
    /**
274
     * @param string $documentNumber
275
     * @param array $tokens
276
     * @param string $resultLevel
277
     * @param string $correlationId
278
     * @return array|mixed
279
     */
280
    public function getDocumentAnalysis(
281
        string $documentNumber,
282
        array $tokens = [],
283
        string $resultLevel = 'ONLY_STATUS',
284
        string $correlationId = null
285
    ) {
286
        $query = collect($tokens)
287
            ->map(function ($token) {
288
                return "token={$token}";
289
            })
290
            ->concat(["resultLevel={$resultLevel}"])
291
            ->implode('&');
292
293
        return $this->get(
294
            "/document-analysis/{$documentNumber}",
295
            $query,
296
            $correlationId
0 ignored issues
show
Bug introduced by
It seems like $correlationId defined by parameter $correlationId on line 284 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...
297
        );
298
    }
299
300
    /**
301
     * @param string $documentNumber
302
     * @param Customer $customer
303
     * @param string $correlationId
304
     * @return array|mixed
305
     * @throws RequestException
306
     */
307
    public function customer(
308
        string $documentNumber,
309
        $customer,
310
        string $correlationId = null
311
    ) {
312
        if (!$customer instanceof CustomerInterface) {
313
            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...
314
        }
315
316
        return $this->put("/customers/{$documentNumber}", $customer->toArray(), $correlationId);
317
    }
318
319
    /**
320
     * Validate of boleto or dealership
321
     *
322
     * @param string $code - Digitable line
323
     * @param string $correlationId
324
     * @return array|mixed
325
     * @throws RequestException
326
     */
327
    public function paymentValidate(string $code, string $correlationId)
328
    {
329
        return $this->post('/bill-payment/validate', ['code' => $code], $correlationId, true);
330
    }
331
332
    /**
333
     * Confirmation of payment of boleto or dealership
334
     *
335
     * @param BillPayment $billPayment
336
     * @param string $correlationId
337
     * @return array|mixed
338
     */
339
    public function paymentConfirm(
340
        BillPayment $billPayment,
341
        string $correlationId
342
    ) {
343
        return $this->post('/bill-payment/confirm', $billPayment->toArray(), $correlationId, true);
344
    }
345
346
    /**
347
     * Create a new PIX key link with account.
348
     *
349
     * @param PixEntries $pixEntries
350
     * @return array|mixed
351
     */
352
    public function registerPixKey(PixEntries $pixEntries)
353
    {
354
        return $this->post('/pix/entries', [
355
            'addressingKey' => $pixEntries->addressingKey->toArray(),
356
            'account' => $pixEntries->account->toArray(),
357
        ], null, true);
358
    }
359
360
    /**
361
     * Gets the list of address keys linked to an account.
362
     *
363
     * @param string $accountNumber
364
     * @return array|mixed
365
     */
366
    public function getPixAddressingKeys(string $accountNumber)
367
    {
368
        return $this->get("/accounts/$accountNumber/addressing-keys");
369
    }
370
371
    /**
372
     * Gets details of the account linked to an addressing key.
373
     *
374
     * @param string $documentNumber
375
     * @param string $addressinKeyValue
376
     * @return array|mixed
377
     */
378
    public function getPixAddressingKeyValue(string $documentNumber, string $addressinKeyValue)
379
    {
380
        $this->setHeaders(['x-bkly-pix-user-id' => $documentNumber]);
381
        return $this->get("/pix/entries/$addressinKeyValue");
382
    }
383
384
    /**
385
     * Delete a key link with account.
386
     *
387
     * @param string $addressingKeyValue
388
     * @return array|mixed
389
     */
390
    public function deletePixAddressingKeyValue(string $addressingKeyValue)
391
    {
392
        return $this->delete("/pix/entries/$addressingKeyValue");
393
    }
394
395
    /**
396
     * @param string $endpoint
397
     * @param array|string|null $query
398
     * @param null $correlation_id
399
     * @return array|mixed
400
     * @throws RequestException
401
     */
402
    private function get(string $endpoint, $query = null, $correlation_id = null)
403
    {
404
        if (now()->unix() > $this->token_expiry || !$this->token) {
405
            $this->auth();
406
        }
407
408
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
409
            $correlation_id = Uuid::uuid4()->toString();
410
        }
411
412
        return Http::withToken($this->token)
413
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
414
            ->get($this->getFinalUrl($endpoint), $query)
415
            ->throw()
416
            ->json();
417
    }
418
419
    /**
420
     * Create a new virtual card
421
     *
422
     * @param VirtualCard $virtualCard
423
     * @return array|mixed
424
     * @throws RequestException
425
     */
426
    public function virtualCard(VirtualCard $virtualCard)
427
    {
428
        return $this->post('/cards/virtual', $virtualCard->toArray(), null, true);
429
    }
430
431
    /**
432
     * @param string $endpoint
433
     * @param array|null $body
434
     * @param string|null $correlation_id
435
     * @param bool $asJson
436
     * @return array|mixed
437
     * @throws RequestException
438
     */
439
    private function post(string $endpoint, array $body = null, string $correlation_id = null, bool $asJson = false)
440
    {
441
        if (now()->unix() > $this->token_expiry || !$this->token) {
442
            $this->auth();
443
        }
444
445
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
446
            $correlation_id = Uuid::uuid4()->toString();
447
        }
448
449
        $body_format = $asJson ? 'json' : 'form_params';
450
451
        return Http
452
            ::withToken($this->token)
453
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
454
            ->bodyFormat($body_format)
455
            ->post($this->getFinalUrl($endpoint), $body)
0 ignored issues
show
Bug introduced by
It seems like $body defined by parameter $body on line 439 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...
456
            ->throw()
457
            ->json();
458
    }
459
460
    /**
461
     * @param string $endpoint
462
     * @param array|null $body
463
     * @param string|null $correlation_id
464
     * @param bool $asJson
465
     * @param bool $attachment
466
     * @param DocumentAnalysis $document
467
     * @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...
468
     * @return array|mixed
469
     * @throws RequestException
470
     */
471
    private function put(
472
        string $endpoint,
473
        array $body = [],
474
        string $correlation_id = null,
475
        bool $asJson = false,
476
        bool $attachment = false,
477
        DocumentAnalysis $document = null
478
    ) {
479
        if (now()->unix() > $this->token_expiry || !$this->token) {
480
            $this->auth();
481
        }
482
483
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
484
            $correlation_id = Uuid::uuid4()->toString();
485
        }
486
487
        $body_format = $asJson ? 'json' : 'form_params';
488
489
        $request = Http
490
            ::withToken($this->token)
491
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
492
            ->bodyFormat($body_format);
493
494
        if ($attachment) {
495
            $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...
496
        }
497
498
        return $request->put($this->getFinalUrl($endpoint), $body)
499
            ->throw()
500
            ->json();
501
    }
502
503
    /**
504
     * Http delete method.
505
     *
506
     * @param string $endpoint
507
     * @return array|mixed
508
     * @throws RequestException
509
     */
510
    private function delete(string $endpoint)
511
    {
512
        if (now()->unix() > $this->token_expiry || !$this->token) {
513
            $this->auth();
514
        }
515
516
        $request = Http::withToken($this->token)
517
            ->withHeaders($this->getHeaders($this->headers));
518
519
        return $request->delete($this->getFinalUrl($endpoint))
520
            ->throw()
521
            ->json();
522
    }
523
524
    /**
525
     * @param string $version API version
526
     * @return $this
527
     */
528
    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...
529
    {
530
        $this->api_version = $version;
531
        return $this;
532
    }
533
534
    /**
535
     * @param array $headers
536
     * @return array|string[]
537
     */
538
    private function getHeaders($headers = [])
539
    {
540
        $default_headers = $this->headers;
541
542
        if (count($headers) > 0) {
543
            $default_headers = array_merge($headers, $default_headers);
544
        }
545
546
        return $default_headers;
547
    }
548
549
    /**
550
     * @param array $header
551
     * @return void
552
     */
553
    private function setHeaders($header)
554
    {
555
        $this->headers = array_merge($this->headers, $header);
556
    }
557
558
    /**
559
     * @param string $endpoint
560
     * @return bool
561
     */
562
    private function requireCorrelationId(string $endpoint)
563
    {
564
        $not_required_endpoints = [
565
            '/banklist',
566
            '/connect/token'
567
        ];
568
569
        return !in_array($endpoint, $not_required_endpoints);
570
    }
571
572
    /**
573
     * @param string $endpoint
574
     * @return string
575
     */
576
    private function getFinalUrl(string $endpoint)
577
    {
578
        return $this->api_url . $endpoint;
579
    }
580
581
    /**
582
     * Do authentication
583
     * @param string $grant_type Default sets to 'client_credentials'
584
     * @throws RequestException
585
     */
586
    private function auth($grant_type = 'client_credentials'): void
587
    {
588
        //TODO: Add auth for username and password
589
        $body = [
590
            'grant_type' => $grant_type,
591
            'client_secret' => $this->client_secret,
592
            'client_id' => $this->client_id
593
        ];
594
595
        $response = Http::asForm()->post($this->login_url, $body)->throw()->json();
596
        $this->token = $response['access_token'];
597
        $this->token_expiry = now()->addSeconds($response['expires_in'])->unix();
598
    }
599
}
600