Completed
Push — master ( dcc755...77cb3d )
by
unknown
14s queued 12s
created

Bankly::phisicalCard()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
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
use WeDevBr\Bankly\Types\Pix\PixEntries;
14
use WeDevBr\Bankly\Types\Card\Card;
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
     * @param string[] $cardProxy
131
     * @param string|null $begin_date
132
     * @param string|null $end_date
133
     * @return array|mixed
134
     * @throws RequestException
135
     * @note This endpoint has been deprecated for some clients.
136
     * You need to check with Acesso/Bankly if your environment has different parameters also.
137
     * The response of this request does not have a default interface between environments.
138
     * Pay attention when use this in your project.
139
     */
140
    public function getEvents(
141
        string $branch,
142
        string $account,
143
        int $page = 1,
144
        int $pagesize = 20,
145
        string $include_details = 'true',
146
        array $cardProxy = [],
147
        string $begin_date = null,
148
        string $end_date = null
149
    ) {
150
        $query = [
151
            'branch' => $branch,
152
            'account' => $account,
153
            'page' => $page,
154
            'pageSize' => $pagesize,
155
            'includeDetails' => $include_details
156
        ];
157
158
        if (!empty($cardProxy)) {
159
            $query['cardProxy'] = $cardProxy;
160
        }
161
162
        if ($begin_date) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $begin_date 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...
163
            $query['beginDateTime'] = $begin_date;
164
        }
165
166
        if ($end_date) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $end_date 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...
167
            $query['endDateTime'] = $end_date;
168
        }
169
170
        return $this->get(
171
            '/events',
172
            $query
173
        );
174
    }
175
176
    /**
177
     * @param int $amount
178
     * @param string $description
179
     * @param array $sender
180
     * @param array $recipient
181
     * @param string|null $correlation_id
182
     * @return array|mixed
183
     * @throws RequestException
184
     */
185
    public function transfer(
186
        int $amount,
187
        string $description,
188
        array $sender,
189
        array $recipient,
190
        string $correlation_id = null
191
    ) {
192
        if ($sender['bankCode']) {
193
            unset($sender['bankCode']);
194
        }
195
196
        return $this->post(
197
            '/fund-transfers',
198
            [
199
                'amount' => $amount,
200
                'description' => $description,
201
                'sender' => $sender,
202
                'recipient' => $recipient
203
            ],
204
            $correlation_id,
205
            true
206
        );
207
    }
208
209
    /**
210
     * Get transfer funds from an account
211
     * @param string $branch
212
     * @param string $account
213
     * @param int $pageSize
214
     * @param string|null $nextPage
215
     * @return array|mixed
216
     * @throws RequestException
217
     */
218
    public function getTransferFunds(string $branch, string $account, int $pageSize = 10, string $nextPage = null)
219
    {
220
        $queryParams = [
221
            'branch' => $branch,
222
            'account' => $account,
223
            'pageSize' => $pageSize
224
        ];
225
        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...
226
            $queryParams['nextPage'] = $nextPage;
227
        }
228
        return $this->get('/fund-transfers', $queryParams);
229
    }
230
231
    /**
232
     * Get Transfer Funds By Authentication Code
233
     * @param string $branch
234
     * @param string $account
235
     * @param string $authenticationCode
236
     * @return array|mixed
237
     * @throws RequestException
238
     */
239
    public function findTransferFundByAuthCode(string $branch, string $account, string $authenticationCode)
240
    {
241
        $queryParams = [
242
            'branch' => $branch,
243
            'account' => $account
244
        ];
245
        return $this->get('/fund-transfers/' . $authenticationCode, $queryParams);
246
    }
247
248
    /**
249
     * @param string $branch
250
     * @param string $account
251
     * @param string $authentication_id
252
     * @return array|mixed
253
     * @throws RequestException
254
     */
255
    public function getTransferStatus(string $branch, string $account, string $authentication_id)
256
    {
257
        return $this->get('/fund-transfers/' . $authentication_id . '/status', [
258
            'branch' => $branch,
259
            'account' => $account
260
        ]);
261
    }
262
263
    /**
264
     * @param string $documentNumber
265
     * @param DocumentAnalysis $document
266
     * @param string $correlationId
267
     * @return array|mixed
268
     * @throws RequestException
269
     */
270
    public function documentAnalysis(
271
        string $documentNumber,
272
        $document,
273
        string $correlationId = null
274
    ) {
275
        if (!$document instanceof DocumentInterface) {
276
            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...
277
        }
278
279
        return $this->put(
280
            "/document-analysis/{$documentNumber}",
281
            [
282
                'documentType' => $document->getDocumentType(),
283
                'documentSide' => $document->getDocumentSide(),
284
            ],
285
            $correlationId,
286
            true,
287
            true,
288
            $document
289
        );
290
    }
291
292
    /**
293
     * @param string $documentNumber
294
     * @param array $tokens
295
     * @param string $resultLevel
296
     * @param string $correlationId
297
     * @return array|mixed
298
     */
299
    public function getDocumentAnalysis(
300
        string $documentNumber,
301
        array $tokens = [],
302
        string $resultLevel = 'ONLY_STATUS',
303
        string $correlationId = null
304
    ) {
305
        $query = collect($tokens)
306
            ->map(function ($token) {
307
                return "token={$token}";
308
            })
309
            ->concat(["resultLevel={$resultLevel}"])
310
            ->implode('&');
311
312
        return $this->get(
313
            "/document-analysis/{$documentNumber}",
314
            $query,
315
            $correlationId
0 ignored issues
show
Bug introduced by
It seems like $correlationId defined by parameter $correlationId on line 303 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...
316
        );
317
    }
318
319
    /**
320
     * @param string $documentNumber
321
     * @param Customer $customer
322
     * @param string $correlationId
323
     * @return array|mixed
324
     * @throws RequestException
325
     */
326
    public function customer(
327
        string $documentNumber,
328
        $customer,
329
        string $correlationId = null
330
    ) {
331
        if (!$customer instanceof CustomerInterface) {
332
            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...
333
        }
334
335
        return $this->put("/customers/{$documentNumber}", $customer->toArray(), $correlationId);
336
    }
337
338
    /**
339
     * Validate of boleto or dealership
340
     *
341
     * @param string $code - Digitable line
342
     * @param string $correlationId
343
     * @return array|mixed
344
     * @throws RequestException
345
     */
346
    public function paymentValidate(string $code, string $correlationId)
347
    {
348
        return $this->post('/bill-payment/validate', ['code' => $code], $correlationId, true);
349
    }
350
351
    /**
352
     * Confirmation of payment of boleto or dealership
353
     *
354
     * @param BillPayment $billPayment
355
     * @param string $correlationId
356
     * @return array|mixed
357
     */
358
    public function paymentConfirm(
359
        BillPayment $billPayment,
360
        string $correlationId
361
    ) {
362
        return $this->post('/bill-payment/confirm', $billPayment->toArray(), $correlationId, true);
363
    }
364
365
    /**
366
     * Create a new PIX key link with account.
367
     *
368
     * @param PixEntries $pixEntries
369
     * @return array|mixed
370
     */
371
    public function registerPixKey(PixEntries $pixEntries)
372
    {
373
        return $this->post('/pix/entries', [
374
            'addressingKey' => $pixEntries->addressingKey->toArray(),
375
            'account' => $pixEntries->account->toArray(),
376
        ], null, true);
377
    }
378
379
    /**
380
     * Gets the list of address keys linked to an account.
381
     *
382
     * @param string $accountNumber
383
     * @return array|mixed
384
     */
385
    public function getPixAddressingKeys(string $accountNumber)
386
    {
387
        return $this->get("/accounts/$accountNumber/addressing-keys");
388
    }
389
390
    /**
391
     * Gets details of the account linked to an addressing key.
392
     *
393
     * @param string $documentNumber
394
     * @param string $addressinKeyValue
395
     * @return array|mixed
396
     */
397
    public function getPixAddressingKeyValue(string $documentNumber, string $addressinKeyValue)
398
    {
399
        $this->setHeaders(['x-bkly-pix-user-id' => $documentNumber]);
400
        return $this->get("/pix/entries/$addressinKeyValue");
401
    }
402
403
    /**
404
     * Delete a key link with account.
405
     *
406
     * @param string $addressingKeyValue
407
     * @return array|mixed
408
     */
409
    public function deletePixAddressingKeyValue(string $addressingKeyValue)
410
    {
411
        return $this->delete("/pix/entries/$addressingKeyValue");
412
    }
413
414
    /**
415
     * @param string $endpoint
416
     * @param array|string|null $query
417
     * @param null $correlation_id
418
     * @return array|mixed
419
     * @throws RequestException
420
     */
421
    private function get(string $endpoint, $query = null, $correlation_id = null)
422
    {
423
        if (now()->unix() > $this->token_expiry || !$this->token) {
424
            $this->auth();
425
        }
426
427
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
428
            $correlation_id = Uuid::uuid4()->toString();
429
        }
430
431
        return Http::withToken($this->token)
432
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
433
            ->get($this->getFinalUrl($endpoint), $query)
434
            ->throw()
435
            ->json();
436
    }
437
438
    /**
439
     * Create a new virtual card
440
     *
441
     * @param Card $virtualCard
442
     * @return array|mixed
443
     * @throws RequestException
444
     */
445
    public function virtualCard(Card $virtualCard)
446
    {
447
        return $this->post('/cards/virtual', $virtualCard->toArray(), null, true);
448
    }
449
450
    /**
451
     * Create a new virtual card
452
     *
453
     * @param Card $virtualCard
0 ignored issues
show
Bug introduced by
There is no parameter named $virtualCard. 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...
454
     * @return array|mixed
455
     * @throws RequestException
456
     */
457
    public function phisicalCard(Card $phisicalCard)
458
    {
459
        return $this->post('/cards/phisical', $phisicalCard->toArray(), null, true);
460
    }
461
462
    /**
463
     * @param string $endpoint
464
     * @param array|null $body
465
     * @param string|null $correlation_id
466
     * @param bool $asJson
467
     * @return array|mixed
468
     * @throws RequestException
469
     */
470
    private function post(string $endpoint, array $body = null, string $correlation_id = null, bool $asJson = false)
471
    {
472
        if (now()->unix() > $this->token_expiry || !$this->token) {
473
            $this->auth();
474
        }
475
476
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
477
            $correlation_id = Uuid::uuid4()->toString();
478
        }
479
480
        $body_format = $asJson ? 'json' : 'form_params';
481
482
        return Http
483
            ::withToken($this->token)
484
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
485
            ->bodyFormat($body_format)
486
            ->post($this->getFinalUrl($endpoint), $body)
0 ignored issues
show
Bug introduced by
It seems like $body defined by parameter $body on line 470 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...
487
            ->throw()
488
            ->json();
489
    }
490
491
    /**
492
     * @param string $endpoint
493
     * @param array|null $body
494
     * @param string|null $correlation_id
495
     * @param bool $asJson
496
     * @param bool $attachment
497
     * @param DocumentAnalysis $document
498
     * @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...
499
     * @return array|mixed
500
     * @throws RequestException
501
     */
502
    private function put(
503
        string $endpoint,
504
        array $body = [],
505
        string $correlation_id = null,
506
        bool $asJson = false,
507
        bool $attachment = false,
508
        DocumentAnalysis $document = null
509
    ) {
510
        if (now()->unix() > $this->token_expiry || !$this->token) {
511
            $this->auth();
512
        }
513
514
        if (is_null($correlation_id) && $this->requireCorrelationId($endpoint)) {
515
            $correlation_id = Uuid::uuid4()->toString();
516
        }
517
518
        $body_format = $asJson ? 'json' : 'form_params';
519
520
        $request = Http
521
            ::withToken($this->token)
522
            ->withHeaders($this->getHeaders(['x-correlation-id' => $correlation_id]))
523
            ->bodyFormat($body_format);
524
525
        if ($attachment) {
526
            $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...
527
        }
528
529
        return $request->put($this->getFinalUrl($endpoint), $body)
530
            ->throw()
531
            ->json();
532
    }
533
534
    /**
535
     * Http delete method.
536
     *
537
     * @param string $endpoint
538
     * @return array|mixed
539
     * @throws RequestException
540
     */
541
    private function delete(string $endpoint)
542
    {
543
        if (now()->unix() > $this->token_expiry || !$this->token) {
544
            $this->auth();
545
        }
546
547
        $request = Http::withToken($this->token)
548
            ->withHeaders($this->getHeaders($this->headers));
549
550
        return $request->delete($this->getFinalUrl($endpoint))
551
            ->throw()
552
            ->json();
553
    }
554
555
    /**
556
     * @param string $version API version
557
     * @return $this
558
     */
559
    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...
560
    {
561
        $this->api_version = $version;
562
        return $this;
563
    }
564
565
    /**
566
     * @param array $headers
567
     * @return array|string[]
568
     */
569
    private function getHeaders($headers = [])
570
    {
571
        $default_headers = $this->headers;
572
573
        if (count($headers) > 0) {
574
            $default_headers = array_merge($headers, $default_headers);
575
        }
576
577
        return $default_headers;
578
    }
579
580
    /**
581
     * @param array $header
582
     * @return void
583
     */
584
    private function setHeaders($header)
585
    {
586
        $this->headers = array_merge($this->headers, $header);
587
    }
588
589
    /**
590
     * @param string $endpoint
591
     * @return bool
592
     */
593
    private function requireCorrelationId(string $endpoint)
594
    {
595
        $not_required_endpoints = [
596
            '/banklist',
597
            '/connect/token'
598
        ];
599
600
        return !in_array($endpoint, $not_required_endpoints);
601
    }
602
603
    /**
604
     * @param string $endpoint
605
     * @return string
606
     */
607
    private function getFinalUrl(string $endpoint)
608
    {
609
        return $this->api_url . $endpoint;
610
    }
611
612
    /**
613
     * Do authentication
614
     * @param string $grant_type Default sets to 'client_credentials'
615
     * @throws RequestException
616
     */
617
    private function auth($grant_type = 'client_credentials'): void
618
    {
619
        //TODO: Add auth for username and password
620
        $body = [
621
            'grant_type' => $grant_type,
622
            'client_secret' => $this->client_secret,
623
            'client_id' => $this->client_id
624
        ];
625
626
        $response = Http::asForm()->post($this->login_url, $body)->throw()->json();
627
        $this->token = $response['access_token'];
628
        $this->token_expiry = now()->addSeconds($response['expires_in'])->unix();
629
    }
630
}
631