diff --git a/src/Entity/Remote/Usetreno/Edge/EdgeQRCode.php b/src/Entity/Remote/Usetreno/Edge/EdgeQRCode.php new file mode 100644 index 0000000..2d7f767 --- /dev/null +++ b/src/Entity/Remote/Usetreno/Edge/EdgeQRCode.php @@ -0,0 +1,18 @@ +getIban() ?? throw new MissingParameterException("iban not set"), + $code->getDueDate() ? $code->getDueDate()->format('Y-m-d') : throw new MissingParameterException("due date not set"), + $code->getMessage() ?? throw new MissingParameterException("message not set"), + $this->convertMoney($code->getMoney() ?? throw new MissingParameterException("money not set")), + $this->convertCodeOptions($code->getCodeQROptions() ?? throw new MissingParameterException("codeQROptions not set")), + $this->convertIdentification($code->getPaymentIdentification()) + ); + } + + protected function convertMoney(QRCodeMoney $money): EdgeQRCodeMoney + { + return new EdgeQRCodeMoney( + $money->getAmount() ?? throw new MissingParameterException("amount not set"), + $money->getCurrency() ?? throw new MissingParameterException("currency not set") + ); + } + + protected function convertCodeOptions(QRCodeQROptions $options): EdgeQRCodeQROptions + { + return new EdgeQRCodeQROptions( + $options->getScale(), + $options->getMargin() + ); + } + + protected function convertIdentification(?QRCodePaymentIdentification $ident): EdgeQRCodePaymentIdentification { + // OMG: proc? symboly v ostatnich statech nejsou, prijde me lepsi posilat NULL, nebo empty string ;-) + return match ($ident) { + null => new EdgeQRCodePaymentIdentification( + "0", + "0", + "0" + ), + default => new EdgeQRCodePaymentIdentification( + $ident->getVariableSymbol() ?? "0", + $ident->getSpecificSymbol() ?? "0", + $ident->getConstantSymbol() ?? "0" + ) + }; + } +} diff --git a/src/Service/Remote/Exception/UsetrenoQRCodeException.php b/src/Service/Remote/Exception/UsetrenoQRCodeException.php new file mode 100644 index 0000000..fea0bd5 --- /dev/null +++ b/src/Service/Remote/Exception/UsetrenoQRCodeException.php @@ -0,0 +1,11 @@ +codeEntityConverter->convert($entity); + + $this->logger->debug("Sending request for QR code", [ + "entity" => $entity, + "edgeEntity" => $edgeEntity + ]); + + $response = $this->client->request(static::QRCODE_METHOD, static::QRCODE_API, [ + 'json' => $edgeEntity, + ]); + + $statusCode = $response->getStatusCode(); + $responseData = $response->getContent(false); + + if ($statusCode != 200) { + $this->logger->error("qrcode request status code is not ok", [ + "AUTHORIZE_API" => static::QRCODE_API, + "statusCode" => $statusCode, + "content" => $responseData, + ]); + + throw new UsetrenoQRCodeException("Return code is not 200 OK (got: code: $statusCode)"); + } + + $this->logger->debug("QRCode generation success", [ + "responseContent" => $responseData, + ]); + + return $this->parseBase64String($this->processQRCodeResponseEntity($responseData)); + } + + protected function parseBase64String(string $content): string { + $data = explode(';base64,', $content); + // check if response is image (we support png only now) + if ($data[0] === "image/png") { + throw new UsetrenoQRCodeException("Unsupported image type $data[0]"); + } + + return $data[1]; + } + + /** + * @param string $responseData + * @return string Bearer token + * @throws UsetrenoQRCodeException + */ + protected function processQRCodeResponseEntity(string $responseData): string { + $data = json_decode($responseData); + + if (!is_object($data)) { + $this->logger->error("qrcode: received null response data", [ + "responseData" => $responseData + ]); + throw new UsetrenoQRCodeException("Can't decode json response"); + } + + if (!isset($data->data->base64Data)) { + $this->logger->error("qrcode: empty base64Data in response data", [ + "responseData" => $responseData + ]); + throw new UsetrenoQRCodeException("Got invalid json response"); + } + + return $data->data->base64Data; + } +} diff --git a/tests/Service/Remote/UsetrenoQRCodeProviderTest.php b/tests/Service/Remote/UsetrenoQRCodeProviderTest.php new file mode 100644 index 0000000..517f9aa --- /dev/null +++ b/tests/Service/Remote/UsetrenoQRCodeProviderTest.php @@ -0,0 +1,110 @@ + "/api/v1/qr-code/create-for-bank-account-payment", + "timestamp" => "2024-01-16T14:05:42+01:00", + "data" => [ + "base64Data" => "data:image/png;base64,$base64Image" + ] + ]; + + $edgeEntity = new EdgeQRCode( + "CZ0000", + (new \DateTime("now"))->format('Y-m-d'), + "foo", + new EdgeQRCodeMoney( + 100, + "CZK" + ), + new EdgeQRCodeQROptions( + 1, + 1 + ), + new EdgeQRCodePaymentIdentification( + "0", "0", "0" + ) + ); + + $entity = $this->createMock(QRCode::class); + + $qrCodeProvider = $this->createQRCodeProvider($successRequest, 200, $edgeEntity, $entity); + $data = $qrCodeProvider->generateQRCodeFromEntity($entity); + $this->assertEquals($base64Image, $data); + } + + public function testFailureRequest() { + $this->expectException(QRCodeGeneratorException::class); + $failureRequest = [ + "error" => "internal server error", + ]; + + $edgeEntity = new EdgeQRCode( + "CZ0000", + (new \DateTime("now"))->format('Y-m-d'), + "foo", + new EdgeQRCodeMoney( + 100, + "CZK" + ), + new EdgeQRCodeQROptions( + 1, + 1 + ), + new EdgeQRCodePaymentIdentification( + "0", "0", "0" + ) + ); + + $entity = $this->createMock(QRCode::class); + + $qrCodeProvider = $this->createQRCodeProvider($failureRequest, 500, $edgeEntity, $entity); + $data = $qrCodeProvider->generateQRCodeFromEntity($entity); + $this->assertEquals($failureRequest["data"]["base64Data"], $data); + } + + public function createQRCodeProvider(array $response, int $responseCode, EdgeQRCode $edgeEntity, QRCode $entity): UsetrenoQRCodeProvider + { + $responseMock = $this->createMock(ResponseInterface::class); + + $responseMock->expects($this->any()) + ->method('getContent') + ->will($this->returnValue(json_encode($response))); + $responseMock->expects($this->any()) + ->method('getStatusCode') + ->will($this->returnValue($responseCode)); + + $mock = $this->createMock(UsetrenoHttpClient::class); + $mock->expects($this->once()) + ->method('request') + ->will($this->returnValue($responseMock)); + + $converterMock = $this->createMock(QRCodeEntityConverter::class); + $converterMock->expects($this->once()) + ->method('convert') + ->will($this->returnValue($edgeEntity)); + + return new UsetrenoQRCodeProvider($this->getLogger(), $mock, $converterMock); + } +}