Passed
Pull Request — master (#130)
by
unknown
02:13
created

FinTs::createStateOfAccountMessage()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 89

Duplication

Lines 16
Ratio 17.98 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
dl 16
loc 89
ccs 0
cts 43
cp 0
rs 7.9288
c 0
b 0
f 0
cc 5
nc 5
nop 5
crap 30

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Fhp;
4
5
use Fhp\Adapter\AdapterInterface;
6
use Fhp\Adapter\Curl;
7
use Fhp\DataTypes\Kik;
8
use Fhp\DataTypes\Kti;
9
use Fhp\DataTypes\Ktv;
10
use Fhp\Dialog\Dialog;
11
use Fhp\Message\AbstractMessage;
12
use Fhp\Message\Message;
13
use Fhp\Model\SEPAAccount;
14
use Fhp\Response\GetAccounts;
15
use Fhp\Response\GetSaldo;
16
use Fhp\Response\GetSEPAAccounts;
17
use Fhp\Response\GetStatementOfAccount;
18
use Fhp\Segment\HKKAZ;
19
use Fhp\Segment\HKSAL;
20
use Fhp\Segment\HKSPA;
21
use Psr\Log\LoggerInterface;
22
use Psr\Log\NullLogger;
23
24
/**
25
 * Class FinTs.
26
 *
27
 * @package Fhp
28
 */
29
class FinTs
30
{
31
    const DEFAULT_COUNTRY_CODE = 280;
32
33
    /** @var LoggerInterface */
34
    protected $logger;
35
    /** @var  string */
36
    protected $server;
37
    /** @var int */
38
    protected $port;
39
    /** @var string */
40
    protected $bankCode;
41
    /** @var string */
42
    protected $username;
43
    /** @var string */
44
    protected $pin;
45
    /** @var  Connection */
46
    protected $connection;
47
    /** @var  AdapterInterface */
48
    protected $adapter;
49
    /** @var int */
50
    protected $systemId = 0;
51
    /** @var string */
52
    protected $bankName;
53
    /** @var string */
54
    protected $productName;
55
    /** @var string */
56
    protected $productVersion;
57
58
    /**
59
     * FinTs constructor.
60
     * @param string $server Base URL (should start with https://) where the bank's server can be reached.
61
     * @param int $port The port to use for the connection. Use 443 for HTTPS.
62
     * @param string $bankCode The bank's identifier (Bankleitzahl).
63
     * @param string $username The username for authentication. This is assigned by the bank initially, but most banks
64
     *     allow users to customize it.
65
     * @param string $pin The PIN for authentication.
66
     * @param string $productName Identifies the product (i.e. the application in which the fints-hbci-php library is
67
     *     being used). This is used to show users which products/applications have access to their bank account. Note
68
     *     that this shouldn't just be an arbitrary string, but rather the registration number obtained from the
69
     *     registration on https://www.hbci-zka.de/register/prod_register.htm.
70
     * @param string $productVersion The product version, which can be an arbitrary string, though if your the
71
     *     application displays a version number somewhere on its own user interface, it should match that.
72
     * @param LoggerInterface|null $logger
73
     */
74
    public function __construct(
75
        $server,
76
        $port,
77
        $bankCode,
78
        $username,
79
        $pin,
80
        $productName,
81
        $productVersion,
82
        LoggerInterface $logger = null
83
    ) {
84
        $this->server = $server;
85
        $this->port = $port;
86
        $this->logger = null == $logger ? new NullLogger() : $logger;
87
88
        // escaping of bank code not really needed here as it should
89
        // never have special chars. But we just do it to ensure
90
        // that the request will not get messed up and the user
91
        // can receive a valid error response from the HBCI server.
92
        $this->bankCode = $this->escapeString($bankCode);
93
94
        // Here, escaping is needed for usernames or pins with
95
        // HBCI special chars.
96
        $this->username = $this->escapeString($username);
97
        $this->pin = $this->escapeString($pin);
98
99
        $this->productName = $productName;
100
        $this->productVersion = $productVersion;
101
102
        $this->adapter = new Curl($this->server, $this->port);
103
        $this->connection = new Connection($this->adapter);
104
    }
105
106
    /**
107
     * Sets the adapter to use.
108
     *
109
     * @param AdapterInterface $adapter
110
     */
111
    public function setAdapter(AdapterInterface $adapter)
112
    {
113
        $this->adapter = $adapter;
114
        $this->connection = new Connection($this->adapter);
115
    }
116
117
    /**
118
     * Gets array of all accounts.
119
     *
120
     * @return Model\Account[]
121
     */
122
    public function getAccounts()
123
    {
124
        $dialog = $this->getDialog();
125
        $result = $dialog->syncDialog();
126
        $this->bankName = $dialog->getBankName();
127
        $accounts = new GetAccounts($result);
128
129
        return $accounts->getAccountsArray();
130
    }
131
132
    /**
133
     * Gets array of all SEPA Accounts.
134
     *
135
     * @return Model\SEPAAccount[]
136
     * @throws Adapter\Exception\AdapterException
137
     * @throws Adapter\Exception\CurlException
138
     */
139
    public function getSEPAAccounts()
140
    {
141
        $dialog = $this->getDialog();
142
        $dialog->syncDialog();
143
        $dialog->initDialog();
144
145
        $message = $this->getNewMessage(
146
            $dialog,
147
            array(new HKSPA(3)),
148
            array(AbstractMessage::OPT_PINTAN_MECH => $dialog->getSupportedPinTanMechanisms())
149
        );
150
151
        $result = $dialog->sendMessage($message);
152
        $dialog->endDialog();
153
        $sepaAccounts = new  GetSEPAAccounts($result->rawResponse);
154
155
        return $sepaAccounts->getSEPAAccountsArray();
156
    }
157
158
    /**
159
     * Gets the bank name.
160
     *
161
     * @return string
162
     */
163
    public function getBankName()
164
    {
165
        if (null == $this->bankName) {
166
            $this->getDialog()->syncDialog();
167
        }
168
169
        return $this->bankName;
170
    }
171
172
    /**
173
     * Gets statement of account.
174
     *
175
     * @param SEPAAccount $account
176
     * @param \DateTime $from
177
     * @param \DateTime $to
178
     * @return Model\StatementOfAccount\StatementOfAccount|null
179
     * @throws \Exception
180
     */
181
    public function getStatementOfAccount(SEPAAccount $account, \DateTime $from, \DateTime $to)
182
    {
183
        $responses = array();
184
185
        $this->logger->info('Start fetching statement of account results');
186
        $this->logger->info('Start date: ' . $from->format('Y-m-d'));
187
        $this->logger->info('End date  : ' . $to->format('Y-m-d'));
188
189
        $dialog = $this->getDialog();
190
        $dialog->syncDialog();
191
        $dialog->initDialog();
192
193
        $message = $this->createStateOfAccountMessage($dialog, $account, $from, $to, null);
194
        $response = $dialog->sendMessage($message);
195
        $touchdowns = $response->getTouchdowns($message);
196
        $soaResponse = new GetStatementOfAccount($response->rawResponse);
197
        $responses[] = $soaResponse->getRawMt940();
198
199
        $touchdownCounter = 1;
200
        while (isset($touchdowns[HKKAZ::NAME])) {
201
            $this->logger->info('Fetching more statement of account results (' . $touchdownCounter++ . ') ...');
202
            $message = $this->createStateOfAccountMessage(
203
                $dialog,
204
                $account,
205
                $from,
206
                $to,
207
                $touchdowns[HKKAZ::NAME]
208
            );
209
210
            $r = $dialog->sendMessage($message);
211
            $touchdowns = $r->getTouchDowns($message);
212
            $soaResponse = new GetStatementOfAccount($r->rawResponse);
213
            $responses[] = $soaResponse->getRawMt940();
214
        }
215
216
        $this->logger->info('Fetching of ' . $touchdownCounter . ' pages done.');
217
        $this->logger->debug('HKKAZ response:');
218
219
        $dialog->endDialog();
220
221
        return GetStatementOfAccount::createModelFromRawMt940(implode('', $responses));
222
    }
223
224
    /**
225
     * Helper method to create a "Statement of Account Message".
226
     *
227
     * @param Dialog $dialog
228
     * @param SEPAAccount $account
229
     * @param \DateTime $from
230
     * @param \DateTime $to
231
     * @param string|null $touchdown
232
     * @return Message
233
     * @throws \Exception
234
     */
235
    protected function createStateOfAccountMessage(
236
        Dialog $dialog,
237
        SepaAccount $account,
238
        \DateTime $from,
239
        \DateTime $to,
240
        $touchdown = null
241
    ) {
242
        // version 4, 5, 6, 7
243
244
        // version 5
245
        /*
246
            1 Segmentkopf                   DEG         M 1
247
            2 Kontoverbindung Auftraggeber  DEG ktv #   M 1
248
            3 Alle Konten                   DE  jn  #   M 1
249
            4 Von Datum                     DE dat  #   K 1
250
            5 Bis Datum                     DE dat  #   K 1
251
            6 Maximale Anzahl Einträge      DE num ..4  K 1 >0
252
            7 Aufsetzpunkt                  DE an ..35  K 1
253
         */
254
255
        // version 6
256
        /*
257
            1 Segmentkopf                   1 DEG           M 1
258
            2 Kontoverbindung Auftraggeber  2 DEG ktv #     M 1
259
            3 Alle Konten                   1 DE jn #       M 1
260
            4 Von Datum                     1 DE dat #      O 1
261
            5 Bis Datum                     1 DE dat #      O 1
262
            6 Maximale Anzahl Einträge      1 DE num ..4    C 1 >0
263
            7 Aufsetzpunkt                  1 DE an ..35    C 1
264
         */
265
266
        // version 7
267
        /*
268
            1 Segmentkopf                   1 DEG       M 1
269
            2 Kontoverbindung international 1 DEG kti # M 1
270
            3 Alle Konten                   1 DE jn #   M 1
271
            4 Von Datum                     1 DE dat #  O 1
272
            5 Bis Datum                     1 DE dat #  O 1
273
            6 Maximale Anzahl Einträge      1 DE num ..4 C 1 >0
274
            7 Aufsetzpunkt                  1 DE an ..35 C 1
275
         */
276
277
        switch ($dialog->getHkkazMaxVersion()) {
278
            case 4:
279
            case 5:
280
                $konto = new Deg();
281
                $konto->addDataElement($account->getAccountNumber());
282
                $konto->addDataElement($account->getSubAccount());
283
                $konto->addDataElement(static::DEFAULT_COUNTRY_CODE);
284
                $konto->addDataElement($account->getBlz());
285
                break;
286 View Code Duplication
            case 6:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
287
                $konto = new Ktv(
288
                    $account->getAccountNumber(),
289
                    $account->getSubAccount(),
290
                    new Kik(280, $account->getBlz())
291
                );
292
                break;
293 View Code Duplication
            case 7:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
294
                $konto = new Kti(
295
                    $account->getIban(),
296
                    $account->getBic(),
297
                    $account->getAccountNumber(),
298
                    $account->getSubAccount(),
299
                    new Kik(280, $account->getBlz())
300
                );
301
                break;
302
            default:
303
                throw new \Exception('Unsupported HKKAZ version: ' . $dialog->getHkkazMaxVersion());
304
        }
305
306
        $message = $this->getNewMessage(
307
            $dialog,
308
            array(
309
                new HKKAZ(
310
                    $dialog->getHkkazMaxVersion(),
311
                    3,
312
                    $konto,
313
                    HKKAZ::ALL_ACCOUNTS_N,
314
                    $from,
315
                    $to,
316
                    $touchdown
317
                )
318
            ),
319
            array(AbstractMessage::OPT_PINTAN_MECH => $dialog->getSupportedPinTanMechanisms())
320
        );
321
322
        return $message;
323
    }
324
325
    /**
326
     * Gets the saldo of given SEPAAccount.
327
     *
328
     * @param SEPAAccount $account
329
     * @return Model\Saldo|null
330
     * @throws Adapter\Exception\AdapterException
331
     * @throws Adapter\Exception\CurlException
332
     * @throws \Exception
333
     */
334
    public function getSaldo(SEPAAccount $account)
335
    {
336
        $dialog = $this->getDialog();
337
        $dialog->syncDialog();
338
        $dialog->initDialog();
339
340
        switch ((int) $dialog->getHksalMaxVersion()) {
341
            case 4:
342
            case 5:
343
                $hksalAccount = new Deg(
344
                    $account->getAccountNumber(),
0 ignored issues
show
Unused Code introduced by
The call to Deg::__construct() has too many arguments starting with $account->getAccountNumber().

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...
345
                    $account->getSubAccount(),
346
                    static::DEFAULT_COUNTRY_CODE, $account->getBlz()
347
                );
348
                $hksalAccount->addDataElement($account->getAccountNumber());
349
                $hksalAccount->addDataElement($account->getSubAccount());
350
                $hksalAccount->addDataElement(static::DEFAULT_COUNTRY_CODE);
351
                $hksalAccount->addDataElement($account->getBlz());
352
                break;
353 View Code Duplication
            case 6:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
354
                $hksalAccount = new Ktv(
355
                    $account->getAccountNumber(),
356
                    $account->getSubAccount(),
357
                    new Kik(280, $account->getBlz())
358
                );
359
                break;
360 View Code Duplication
            case 7:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
361
                $hksalAccount = new Kti(
362
                    $account->getIban(),
363
                    $account->getBic(),
364
                    $account->getAccountNumber(),
365
                    $account->getSubAccount(),
366
                    new Kik(280, $account->getBlz())
367
                );
368
                break;
369
            default:
370
                throw new \Exception('Unsupported HKSAL version: ' . $dialog->getHksalMaxVersion());
371
        }
372
373
        $message = new Message(
374
            $this->bankCode,
375
            $this->username,
376
            $this->pin,
377
            $dialog->getSystemId(),
378
            $dialog->getDialogId(),
379
            $dialog->getMessageNumber(),
380
            array(
381
                new HKSAL($dialog->getHksalMaxVersion(), 3, $hksalAccount, HKSAL::ALL_ACCOUNTS_N)
382
            ),
383
            array(
384
                AbstractMessage::OPT_PINTAN_MECH => $dialog->getSupportedPinTanMechanisms()
385
            )
386
        );
387
388
        $response = $dialog->sendMessage($message);
389
        $response = new GetSaldo($response->rawResponse);
390
391
        return $response->getSaldoModel();
392
    }
393
394
    /**
395
     * Helper method to retrieve a pre configured message object.
396
     * Factory for poor people :)
397
     *
398
     * @param Dialog $dialog
399
     * @param array $segments
400
     * @param array $options
401
     * @return Message
402
     */
403
    protected function getNewMessage(Dialog $dialog, array $segments, array $options)
404
    {
405
        return new Message(
406
            $this->bankCode,
407
            $this->username,
408
            $this->pin,
409
            $dialog->getSystemId(),
410
            $dialog->getDialogId(),
411
            $dialog->getMessageNumber(),
412
            $segments,
413
            $options
414
        );
415
    }
416
417
    /**
418
     * Helper method to retrieve a pre configured dialog object.
419
     * Factory for poor people :)
420
     *
421
     * @return Dialog
422
     */
423
    protected function getDialog()
424
    {
425
        return new Dialog(
426
            $this->connection,
427
            $this->bankCode,
428
            $this->username,
429
            $this->pin,
430
            $this->systemId,
431
            $this->productName,
432
            $this->productVersion,
433
            $this->logger
434
        );
435
    }
436
437
    /**
438
     * Needed for escaping userdata.
439
     * HBCI escape char is "?"
440
     *
441
     * @param string $string
442
     * @return string
443
     */
444 10
    protected function escapeString($string)
445
    {
446 10
        return str_replace(
447 10
            array('?', '@', ':', '+', '\''),
448 10
            array('??', '?@', '?:', '?+', '?\''),
449
            $string
450 10
        );
451
    }
452
}
453