Skip to content

Commit 27f941a

Browse files
committed
Don't call bank for selectTanMode()
Instead allow storing a plain int (the Tan mode's ID) in the $selectedTanMode field and resolve it later (during login(), execute() or submitTan()), when the application expects server requests to happen anyway. This also means that selectTanMode() won't throw exceptions anymore.
1 parent e63a290 commit 27f941a

File tree

4 files changed

+60
-56
lines changed

4 files changed

+60
-56
lines changed

lib/Fhp/FinTsNew.php

+52-49
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class FinTsNew
4040
private $logger;
4141

4242
// The TAN mode and medium to be used for business transactions that require a TAN.
43-
/** @var VerfahrensparameterZweiSchrittVerfahrenV6|null Note that this is a sub-type of {@link TanMode} */
43+
/** @var VerfahrensparameterZweiSchrittVerfahrenV6|int|null Note that this is a sub-type of {@link TanMode} */
4444
private $selectedTanMode;
4545
/** @var string|null This is a {@link TanMedium#getName()}, but we don't have the {@link TanMedium} instance. */
4646
private $selectedTanMedium;
@@ -169,7 +169,7 @@ public function login()
169169
$this->requireTanMode();
170170
$this->ensureSynchronized();
171171
$this->messageNumber = 1;
172-
$login = new DialogInitialization($this->options, $this->credentials, $this->selectedTanMode,
172+
$login = new DialogInitialization($this->options, $this->credentials, $this->getSelectedTanMode(),
173173
$this->selectedTanMedium, $this->kundensystemId);
174174
$this->execute($login);
175175
return $login;
@@ -210,7 +210,7 @@ public function execute($action)
210210
if ($this->bpd->tanRequiredForRequest($requestSegments)) {
211211
$message->add(HKTANv6::createProzessvariante2Step1($this->requireTanMode(), $this->selectedTanMedium));
212212
}
213-
$request = $this->buildMessage($message);
213+
$request = $this->buildMessage($message, $this->getSelectedTanMode());
214214
$action->setRequestSegmentNumbers(array_map(function ($segment) {
215215
/* @var BaseSegment $segment */
216216
return $segment->getSegmentNumber();
@@ -300,9 +300,10 @@ public function submitTan($action, $tan)
300300
}
301301

302302
// Construct the request.
303+
$tanMode = $this->requireTanMode();
303304
$message = MessageBuilder::create()
304-
->add(HKTANv6::createProzessvariante2Step2($this->requireTanMode(), $tanRequest->getProcessId()));
305-
$request = $this->buildMessage($message, $tan);
305+
->add(HKTANv6::createProzessvariante2Step2($tanMode, $tanRequest->getProcessId()));
306+
$request = $this->buildMessage($message, $tanMode, $tan);
306307

307308
// Execute the request.
308309
$response = $this->sendRequestForAction($action, $request);
@@ -390,7 +391,7 @@ public function getTanMedia($tanMode)
390391
// Execute the GetTanMedia request with the $tanMode swapped in temporarily.
391392
$oldTanMode = $this->selectedTanMode;
392393
$oldTanMedium = $this->selectedTanMedium;
393-
$this->selectedTanMode = $this->resolveTanMode($tanMode);
394+
$this->selectedTanMode = $tanMode instanceof TanMode ? $tanMode->getId() : $tanMode;
394395
$this->selectedTanMedium = '';
395396
try {
396397
$this->executeWeakDialogInitialization('HKTAB');
@@ -408,37 +409,15 @@ public function getTanMedia($tanMode)
408409
}
409410

410411
/**
411-
* Note: While the selected TAN mode is not immediately sent to the bank server by this function, but only used in
412-
* subsequent requests in {@link #execute()}, this function may need to contact the server in order to retrieve the
413-
* BPD and validate the $tanMode.
414412
* @param TanMode|int $tanMode Either a {@link TanMode} instance obtained from {@link #getTanModes()} or its ID.
415413
* @param TanMedium|string|null $tanMedium If the $tanMode has {@link TanMode#needsTanMedium()} set to true, this
416414
* must be the value returned from {@link TanMedium#getName()} for one of the TAN media supported with that TAN
417415
* mode. Use {@link #getTanMedia()} to obtain a list of possible TAN media options.
418-
* @throws CurlException When the connection fails in a layer below the FinTS protocol.
419-
* @throws ServerException When the server resopnds with an error.
420416
*/
421417
public function selectTanMode($tanMode, $tanMedium = null)
422418
{
423-
$this->selectedTanMode = $this->resolveTanMode($tanMode);
424-
if (!($this->selectedTanMode instanceof VerfahrensparameterZweiSchrittVerfahrenV6)) {
425-
throw new UnsupportedException('Only supports VerfahrensparameterZweiSchrittVerfahrenV6');
426-
}
427-
if ($this->selectedTanMode->tanProzess !== VerfahrensparameterZweiSchrittVerfahrenV6::PROZESSVARIANTE_2) {
428-
throw new UnsupportedException('Only supports Prozessvariante 2');
429-
}
430-
431-
if ($this->selectedTanMode->needsTanMedium()) {
432-
if ($tanMedium === null) {
433-
throw new \InvalidArgumentException('tanMedium is mandatory for this tanMode');
434-
}
435-
$this->selectedTanMedium = is_string($tanMedium) ? $tanMedium : $tanMedium->getName();
436-
} else {
437-
if ($tanMedium !== null) {
438-
throw new \InvalidArgumentException('tanMedium not allowed for this tanMode');
439-
}
440-
$this->selectedTanMedium = null;
441-
}
419+
$this->selectedTanMode = $tanMode instanceof TanMode ? $tanMode->getId() : $tanMode;
420+
$this->selectedTanMedium = $tanMedium instanceof TanMedium ? $tanMedium->getName() : $tanMedium;
442421
}
443422

444423
// ------------------------------------------------- IMPLEMENTATION ------------------------------------------------
@@ -526,33 +505,54 @@ private function ensureSynchronized()
526505
}
527506

528507
/**
529-
* @param TanMode|int $tanMode A {@link TanMode} or its ID received from the user.
530-
* @return TanMode The corresponding fresh {@link TanMode} from the BPD.
508+
* If the selected TAN mode was provided as an int, resolves it to a full {@link TanMode} instance, which may
509+
* involve a request to the server to retrieve the BPD. Then returns it.
510+
* @return VerfahrensparameterZweiSchrittVerfahrenV6|null The current TAN mode, null if none was selected, never an int.
531511
* @throws CurlException When the connection fails in a layer below the FinTS protocol.
532-
* @throws ServerException When the server resopnds with an error.
512+
* @throws ServerException When the server resopnds with an error during the BPD fetch.
533513
*/
534-
private function resolveTanMode($tanMode)
514+
private function getSelectedTanMode()
535515
{
536-
if (!is_int($tanMode)) {
537-
$tanMode = $tanMode->getId();
538-
}
539-
$this->ensureBpdAvailable();
540-
if (!array_key_exists($tanMode, $this->bpd->allTanModes)) {
541-
throw new \InvalidArgumentException("Unknown TAN mode: $tanMode");
516+
if (is_int($this->selectedTanMode)) {
517+
$this->ensureBpdAvailable();
518+
if (!array_key_exists($this->selectedTanMode, $this->bpd->allTanModes)) {
519+
throw new \InvalidArgumentException("Unknown TAN mode: $this->selectedTanMode");
520+
}
521+
$this->selectedTanMode = $this->bpd->allTanModes[$this->selectedTanMode];
522+
if (!($this->selectedTanMode instanceof VerfahrensparameterZweiSchrittVerfahrenV6)) {
523+
throw new UnsupportedException('Only supports VerfahrensparameterZweiSchrittVerfahrenV6');
524+
}
525+
if ($this->selectedTanMode->tanProzess !== VerfahrensparameterZweiSchrittVerfahrenV6::PROZESSVARIANTE_2) {
526+
throw new UnsupportedException('Only supports Prozessvariante 2');
527+
}
528+
529+
if ($this->selectedTanMode->needsTanMedium()) {
530+
if ($this->selectedTanMedium === null) {
531+
throw new \InvalidArgumentException('tanMedium is mandatory for this tanMode');
532+
}
533+
} else {
534+
if ($this->selectedTanMedium !== null) {
535+
throw new \InvalidArgumentException('tanMedium not allowed for this tanMode');
536+
}
537+
}
542538
}
543-
return $this->bpd->allTanModes[$tanMode];
539+
return $this->selectedTanMode;
544540
}
545541

546542
/**
543+
* Like {@link #getSelectedTanMode()}, but throws an exception if none was selected.
547544
* @return VerfahrensparameterZweiSchrittVerfahrenV6 The current TAN mode.
548545
* @throws \RuntimeException If no TAN mode has been selected.
546+
* @throws CurlException When the connection fails in a layer below the FinTS protocol.
547+
* @throws ServerException When the server resopnds with an error during the BPD fetch.
549548
*/
550549
private function requireTanMode()
551550
{
552-
if ($this->selectedTanMode === null) {
551+
$tanMode = $this->getSelectedTanMode();
552+
if ($tanMode === null) {
553553
throw new \RuntimeException('selectTanMode() must be called before executing business transactions');
554554
}
555-
return $this->selectedTanMode;
555+
return $tanMode;
556556
}
557557

558558
/**
@@ -645,8 +645,8 @@ private function executeWeakDialogInitialization($hktanRef)
645645
}
646646

647647
$this->messageNumber = 1;
648-
$dialogInitialization = new DialogInitialization($this->options, $this->credentials, $this->selectedTanMode,
649-
$this->selectedTanMedium, $this->kundensystemId, $hktanRef);
648+
$dialogInitialization = new DialogInitialization($this->options, $this->credentials,
649+
$this->getSelectedTanMode(), $this->selectedTanMedium, $this->kundensystemId, $hktanRef);
650650
$this->execute($dialogInitialization);
651651
try {
652652
$dialogInitialization->ensureSuccess();
@@ -690,7 +690,9 @@ protected function endDialog($isAnonymous = false)
690690
try {
691691
if ($this->dialogId !== null) {
692692
$message = MessageBuilder::create()->add(HKENDv1::create($this->dialogId));
693-
$request = $isAnonymous ? Message::createPlainMessage($message) : $this->buildMessage($message);
693+
$request = $isAnonymous
694+
? Message::createPlainMessage($message)
695+
: $this->buildMessage($message, $this->getSelectedTanMode());
694696
$response = $this->sendMessage($request);
695697
if ($response->findRueckmeldung(Rueckmeldungscode::BEENDET) === null) {
696698
throw new UnexpectedResponseException(
@@ -715,17 +717,18 @@ protected function endDialog($isAnonymous = false)
715717
/**
716718
* Injects FinTsOptions/BPD/UPD/Credentials information into the message.
717719
* @param MessageBuilder $message The message to be built.
720+
* @param TanMode|null $tanMode Optionally a TAN mode that will be used when sending this message.
718721
* @param string|null Optionally a TAN to sign this message with.
719722
* @return Message The built message.
720723
*/
721-
private function buildMessage($message, $tan = null)
724+
private function buildMessage($message, $tanMode = null, $tan = null)
722725
{
723726
return Message::createWrappedMessage(
724727
$message,
725728
$this->options,
726729
$this->kundensystemId === null ? '0' : $this->kundensystemId,
727730
$this->credentials,
728-
$this->selectedTanMode,
731+
$tanMode,
729732
$tan
730733
);
731734
}
@@ -741,7 +744,7 @@ private function buildMessage($message, $tan = null)
741744
private function sendMessage($request)
742745
{
743746
if ($request instanceof MessageBuilder) {
744-
$request = $this->buildMessage($request);
747+
$request = $this->buildMessage($request, $this->getSelectedTanMode());
745748
}
746749

747750
$request->header->dialogId = $this->dialogId === null ? '0' : $this->dialogId;

lib/Tests/Fhp/Integration/Consors/ConsorsIntegrationTestBase.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,18 @@ class ConsorsIntegrationTestBase extends FinTsTestCase
4242
*/
4343
protected function initDialog()
4444
{
45-
// We already know the TAN mode, so it will only fetch the BPD (anonymously) to verify it.
45+
// We already know the TAN mode (900 below), so it will only fetch the BPD (anonymously) to verify it.
4646
$this->expectMessage(static::ANONYMOUS_INIT_REQUEST, static::ANONYMOUS_INIT_RESPONSE);
4747
$this->expectMessage(static::ANONYMOUS_END_REQUEST, static::ANONYMOUS_END_RESPONSE);
48-
$this->fints->selectTanMode(900);
49-
$this->assertAllMessagesSeen();
5048

5149
// Then when we initialize a dialog, it's going to request a Kundensystem-ID and UPD.
5250
$this->expectMessage(static::SYNC_REQUEST, static::SYNC_RESPONSE);
5351
$this->expectMessage(static::SYNC_END_REQUEST, static::SYNC_END_RESPONSE);
5452

5553
// And finally it can initialize the main dialog, but the bank wants a TAN.
5654
$this->expectMessage(static::LOGIN_REQUEST, static::LOGIN_RESPONSE);
55+
56+
$this->fints->selectTanMode(900);
5757
$login = $this->fints->login();
5858
$login->maybeThrowError();
5959
$this->assertAllMessagesSeen();

lib/Tests/Fhp/Integration/DKB/DKBIntegrationTestBase.php

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,17 @@ class DKBIntegrationTestBase extends FinTsTestCase
3737
*/
3838
protected function initDialog()
3939
{
40-
// We already know the TAN mode, so it will only fetch the BPD (anonymously) to verify it.
40+
// We already know the TAN mode (921 below), so it will only fetch the BPD (anonymously) to verify it.
4141
$this->expectMessage(static::ANONYMOUS_INIT_REQUEST, static::ANONYMOUS_INIT_RESPONSE);
4242
$this->expectMessage(static::ANONYMOUS_END_REQUEST, static::ANONYMOUS_END_RESPONSE);
43-
$this->fints->selectTanMode(921, 'SomePhone1');
4443

4544
// Then when we initialize a dialog, it's going to request a Kundensystem-ID and UPD.
4645
$this->expectMessage(static::SYNC_REQUEST, static::SYNC_RESPONSE);
4746
$this->expectMessage(static::SYNC_END_REQUEST, static::SYNC_END_RESPONSE);
4847
// And finally it can initialize the main dialog.
4948
$this->expectMessage(static::INIT_REQUEST, static::INIT_RESPONSE);
49+
50+
$this->fints->selectTanMode(921, 'SomePhone1');
5051
$login = $this->fints->login();
5152
$login->ensureSuccess();
5253
$this->assertAllMessagesSeen();

lib/Tests/Fhp/Integration/InitializationErrorTest.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public function testInitializationErrorMissingField()
2222
$this->expectException(ServerException::class);
2323
$this->expectExceptionMessageMatches('/Pflichtfeld nicht gefunden/');
2424
$this->fints->selectTanMode(921, 'SomePhone1');
25-
$this->fints->initDialog();
25+
$this->fints->login();
2626
}
2727

2828
/**
@@ -40,6 +40,6 @@ public function testInitializationErrorInvalidSegment()
4040
$this->expectException(ServerException::class);
4141
$this->expectExceptionMessageMatches('/Falsche Segmentzusammenstellung/');
4242
$this->fints->selectTanMode(921, 'SomePhone1');
43-
$this->fints->initDialog();
43+
$this->fints->login();
4444
}
4545
}

0 commit comments

Comments
 (0)