symfony_example_app/src/Service/Remote/UsetrenoQRCodeProvider.php

120 lines
4.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service\Remote;
use App\Entity\DTO\QRCode\QRCode;
use App\Service\CacheableQRCodeGeneratorInterface;
use App\Service\Remote\Edge\QRCodeEntityConverter;
use App\Service\Remote\Exception\UsetrenoQRCodeException;
use App\Service\Remote\Exception\UsetrenoQRCodeRemoteServerErrorException;
use Nubium\Exception\ThisShouldNeverHappenException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpClient\Exception\TransportException;
class UsetrenoQRCodeProvider implements CacheableQRCodeGeneratorInterface
{
use RetryingFailClientTrait;
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,
protected readonly float $retryWaitSeconds,
protected readonly int $retryCount) { }
public function generateQRCodeFromEntity(QRCode $entity): string
{
$edgeEntity = $this->codeEntityConverter->convert($entity);
$this->logger->debug("Sending request for QR code", [
"entity" => $entity,
"edgeEntity" => $edgeEntity
]);
$responseData = $this->retryingFailRequest($this->retryCount, $this->retryWaitSeconds,
[TransportException::class, UsetrenoQRCodeRemoteServerErrorException::class], $this->logger,
function() use ($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,
]);
if ($statusCode > 500) {
throw new UsetrenoQRCodeRemoteServerErrorException("Return code is not 200 OK (got: code: $statusCode)");
}
throw new UsetrenoQRCodeException("Return code is not 200 OK (got: code: $statusCode)");
}
$this->logger->debug("QRCode generation success", [
"responseContent" => $responseData,
]);
return $responseData;
});
if (!is_string($responseData)) {
throw new ThisShouldNeverHappenException("responseData is not a string");
}
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;
}
public function getCacheKey(QRCode $entity): string
{
$edgeEntity = $this->codeEntityConverter->convert($entity);
$encodedEntity = json_encode($edgeEntity);
if ($encodedEntity === false) {
throw new \RuntimeException("Can't serialize edge entity");
}
return base64_encode($encodedEntity);
}
}