feat: implementation UsetrenoQRCodeProvider
This commit is contained in:
		
							parent
							
								
									450187aa23
								
							
						
					
					
						commit
						264e0260cf
					
				
							
								
								
									
										18
									
								
								src/Entity/Remote/Usetreno/Edge/EdgeQRCode.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/Entity/Remote/Usetreno/Edge/EdgeQRCode.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Entity\Remote\Usetreno\Edge;
 | 
			
		||||
 | 
			
		||||
/* Bohuzel jsem se nedozvedel z API co je optional a co je required. Tudiz jsem vychazel z toho ze vsechno je required az na EdgeQRCodeMoney.*
 | 
			
		||||
    ktery uz z principu veci by mel byt optional (cizi staty nepodporuji variable, constant, specific...)
 | 
			
		||||
 */
 | 
			
		||||
readonly class EdgeQRCode {
 | 
			
		||||
    public function __construct(
 | 
			
		||||
        public string $iban,
 | 
			
		||||
        public string $dueDate,
 | 
			
		||||
        public string $message,
 | 
			
		||||
        public EdgeQRCodeMoney $money,
 | 
			
		||||
        public EdgeQRCodeQROptions $qrOptions,
 | 
			
		||||
        public EdgeQRCodePaymentIdentification $paymentIdentification)
 | 
			
		||||
    { }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										9
									
								
								src/Entity/Remote/Usetreno/Edge/EdgeQRCodeMoney.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/Entity/Remote/Usetreno/Edge/EdgeQRCodeMoney.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Entity\Remote\Usetreno\Edge;
 | 
			
		||||
 | 
			
		||||
readonly class EdgeQRCodeMoney
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(public string $amount, public string $currency) { }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Entity\Remote\Usetreno\Edge;
 | 
			
		||||
 | 
			
		||||
readonly class EdgeQRCodePaymentIdentification
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(public string $variableSymbol, public string $specificSymbol, public string $constantSymbol) { }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/Entity/Remote/Usetreno/Edge/EdgeQRCodeQROptions.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Entity/Remote/Usetreno/Edge/EdgeQRCodeQROptions.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Entity\Remote\Usetreno\Edge;
 | 
			
		||||
 | 
			
		||||
readonly class EdgeQRCodeQROptions
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(public int $scale, public int $margin)
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,9 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Service\Remote\Edge\Exceptions;
 | 
			
		||||
 | 
			
		||||
class MissingParameterException extends \RuntimeException
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								src/Service/Remote/Edge/QRCodeEntityConverter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/Service/Remote/Edge/QRCodeEntityConverter.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Service\Remote\Edge;
 | 
			
		||||
 | 
			
		||||
use App\Entity\QRCode\QRCode;
 | 
			
		||||
use App\Entity\QRCode\QRCodeMoney;
 | 
			
		||||
use App\Entity\QRCode\QRCodePaymentIdentification;
 | 
			
		||||
use App\Entity\QRCode\QRCodeQROptions;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCode;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCodeMoney;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCodePaymentIdentification;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCodeQROptions;
 | 
			
		||||
use App\Service\Remote\Edge\Exceptions\MissingParameterException;
 | 
			
		||||
 | 
			
		||||
class QRCodeEntityConverter
 | 
			
		||||
{
 | 
			
		||||
    public function __construct() { }
 | 
			
		||||
 | 
			
		||||
    public function convert(QRCode $code): EdgeQRCode {
 | 
			
		||||
        return new EdgeQRCode(
 | 
			
		||||
            $code->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"
 | 
			
		||||
            )
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/Service/Remote/Exception/UsetrenoQRCodeException.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/Service/Remote/Exception/UsetrenoQRCodeException.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Service\Remote\Exception;
 | 
			
		||||
 | 
			
		||||
use App\Service\Exception\QRCodeGeneratorException;
 | 
			
		||||
 | 
			
		||||
class UsetrenoQRCodeException extends QRCodeGeneratorException
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								src/Service/Remote/UsetrenoQRCodeProvider.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								src/Service/Remote/UsetrenoQRCodeProvider.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
<?php
 | 
			
		||||
declare(strict_types=1);
 | 
			
		||||
 | 
			
		||||
namespace App\Service\Remote;
 | 
			
		||||
 | 
			
		||||
use App\Entity\QRCode\QRCode;
 | 
			
		||||
use App\Service\QRCodeGeneratorInterface;
 | 
			
		||||
use App\Service\Remote\Edge\QRCodeEntityConverter;
 | 
			
		||||
use App\Service\Remote\Exception\AuthorizeException;
 | 
			
		||||
use App\Service\Remote\Exception\UsetrenoQRCodeException;
 | 
			
		||||
use Psr\Log\LoggerInterface;
 | 
			
		||||
 | 
			
		||||
class UsetrenoQRCodeProvider implements QRCodeGeneratorInterface
 | 
			
		||||
{
 | 
			
		||||
    const QRCODE_API = 'https://topapi.top-test.cz/chameleon/api/v1/qr-code/create-for-bank-account-payment';
 | 
			
		||||
    const QRCODE_METHOD = 'POST';
 | 
			
		||||
 | 
			
		||||
    public function __construct(protected readonly LoggerInterface $logger,
 | 
			
		||||
                                protected readonly UsetrenoHttpClient $client,
 | 
			
		||||
                                protected readonly QRCodeEntityConverter $codeEntityConverter) { }
 | 
			
		||||
 | 
			
		||||
    public function generateQRCodeFromEntity(QRCode $entity): string
 | 
			
		||||
    {
 | 
			
		||||
        $edgeEntity = $this->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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										110
									
								
								tests/Service/Remote/UsetrenoQRCodeProviderTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								tests/Service/Remote/UsetrenoQRCodeProviderTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,110 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Tests\Service\Remote;
 | 
			
		||||
 | 
			
		||||
use App\Entity\QRCode\QRCode;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCode;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCodeMoney;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCodePaymentIdentification;
 | 
			
		||||
use App\Entity\Remote\Usetreno\Edge\EdgeQRCodeQROptions;
 | 
			
		||||
use App\Service\Exception\QRCodeGeneratorException;
 | 
			
		||||
use App\Service\Remote\Edge\QRCodeEntityConverter;
 | 
			
		||||
use App\Service\Remote\UsetrenoHttpClient;
 | 
			
		||||
use App\Service\Remote\UsetrenoQRCodeProvider;
 | 
			
		||||
use App\Tests\Common\LoggerTrait;
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
use Symfony\Contracts\HttpClient\ResponseInterface;
 | 
			
		||||
 | 
			
		||||
class UsetrenoQRCodeProviderTest extends TestCase
 | 
			
		||||
{
 | 
			
		||||
    use LoggerTrait;
 | 
			
		||||
 | 
			
		||||
    public function testSuccessRequest() {
 | 
			
		||||
        $base64Image = "iVBORw0KGgoAAAANSUhEUgAAAEkAAABJCAIAAAD+EZyLAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACzklEQVRoge1a0Y6DMAwLp/v/X+49IFWenXaQZNt1qh8mKCnEJHUC7Git2Zfi59MOvBCb25rY3NbE5rYmNrc1sbmtic1tTWxua2JzWxO/sWnHcehga+04Dvw9B3HWZHx+5oCTQW56ve7xuaEEcGK3OQ3IzD1zAHFueFW96+cIEkaPKWg25pN5nZPiNgdmoIG7Ggc3UfOo15LWGlLq7o42dLwKqbipN5SKeIjCiINqXMIzzs1d4ugraqaO4HSyDIsHO/OKd6/qq8rJGxBcbwegj9ijYJx8UO6fBgSn49yYk3Et0RVClQ2z0RXMvusqZ15jCnSS7i4yIT5UBk9WruuTgnEdcW4kcfjr6iTGAbcpja1OS4LcVN9HnYfGCm2oUzMhdmWVjpBab8gHlcOtbLjrisSkyscQ1GXSd/MkRO1dMu6paCOGODcbrC7KOnWR8pCaySpilqzdqI3KlkAhdQNOZsmngWzc6PLaixA9PQPujixjTgb7SVrupCLUQ9KUUbQpVq4s3UK2druuu8JN67Dnnnae9pgXYXo1z90mmYOud4OntQujnalsJ1LPOJNird0GWqqWkrEbzLvI9sq9I3HDYrJgtOLbsybr3TnZ10Nnhf3xxVqHxK40NHeR0hKM1eje9yXnVmrz+KjqBt1L1n7njJdfrboFuqdA3rHUMw7pJN1gSlE1QFDPbY+5GkP8nQKFoq89HCSnO0ySU3U1H7eynJzHRNvlUbeFsywnJ2XvXjEg+KvNlz3GiooklfuMS8XfcYgDtRdu72JSDKoqQap20xLq47hN3TCVDb0do1MFUPwdh3aJhvb42nPiyZOiUvytQysvHiJflaq2LB/rS1xgNdMbT7EaRaakdhd/x8FDE3rmvY1Ey3mhv4hsX4JJSEddextIP010tee2h1W1+x/im/+Dsbmtic1tTWxua2JzWxOb25rY3NbE5rYmNrc18QcV0HKSoAc9PQAAAABJRU5ErkJggg==";
 | 
			
		||||
 | 
			
		||||
        $successRequest = [
 | 
			
		||||
            "uri" => "/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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user