vendor/shopware/core/Checkout/Payment/Cart/Token/JWTFactoryV2.php line 70

  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Checkout\Payment\Cart\Token;
  3. use Doctrine\DBAL\Connection;
  4. use Lcobucci\JWT\Configuration;
  5. use Lcobucci\JWT\UnencryptedToken;
  6. use Shopware\Core\Checkout\Payment\Exception\InvalidTokenException;
  7. use Shopware\Core\Checkout\Payment\Exception\TokenInvalidatedException;
  8. use Shopware\Core\Defaults;
  9. use Shopware\Core\Framework\Log\Package;
  10. use Shopware\Core\Framework\Uuid\Uuid;
  11. #[Package('checkout')]
  12. class JWTFactoryV2 implements TokenFactoryInterfaceV2
  13. {
  14.     /**
  15.      * @var Configuration
  16.      */
  17.     protected $configuration;
  18.     /**
  19.      * @internal
  20.      */
  21.     public function __construct(Configuration $configuration, private readonly Connection $connection)
  22.     {
  23.         $this->configuration $configuration;
  24.     }
  25.     public function generateToken(TokenStruct $tokenStruct): string
  26.     {
  27.         $expires = new \DateTimeImmutable('@' time());
  28.         // @see https://github.com/php/php-src/issues/9950
  29.         if ($tokenStruct->getExpires() > 0) {
  30.             $expires $expires->modify(
  31.                 sprintf('+%d seconds'$tokenStruct->getExpires())
  32.             );
  33.         } else {
  34.             $expires $expires->modify(
  35.                 sprintf('-%d seconds'abs($tokenStruct->getExpires()))
  36.             );
  37.         }
  38.         $jwtToken $this->configuration->builder()
  39.             ->identifiedBy(Uuid::randomHex())
  40.             ->issuedAt(new \DateTimeImmutable('@' time()))
  41.             ->canOnlyBeUsedAfter(new \DateTimeImmutable('@' time()))
  42.             ->expiresAt($expires)
  43.             ->relatedTo($tokenStruct->getTransactionId() ?? '')
  44.             ->withClaim('pmi'$tokenStruct->getPaymentMethodId())
  45.             ->withClaim('ful'$tokenStruct->getFinishUrl())
  46.             ->withClaim('eul'$tokenStruct->getErrorUrl())
  47.             ->getToken($this->configuration->signer(), $this->configuration->signingKey());
  48.         $this->write($jwtToken->toString(), $expires);
  49.         return $jwtToken->toString();
  50.     }
  51.     /**
  52.      * @throws InvalidTokenException
  53.      */
  54.     public function parseToken(string $token): TokenStruct
  55.     {
  56.         try {
  57.             /** @var UnencryptedToken $jwtToken */
  58.             $jwtToken $this->configuration->parser()->parse($token);
  59.         } catch (\Throwable) {
  60.             throw new InvalidTokenException($token);
  61.         }
  62.         if (!$this->configuration->validator()->validate($jwtToken, ...$this->configuration->validationConstraints())) {
  63.             throw new InvalidTokenException($token);
  64.         }
  65.         if (!$this->has($token)) {
  66.             throw new TokenInvalidatedException($token);
  67.         }
  68.         $errorUrl $jwtToken->claims()->get('eul');
  69.         /** @var \DateTimeImmutable $expires */
  70.         $expires $jwtToken->claims()->get('exp');
  71.         return new TokenStruct(
  72.             $jwtToken->claims()->get('jti'),
  73.             $token,
  74.             $jwtToken->claims()->get('pmi'),
  75.             $jwtToken->claims()->get('sub'),
  76.             $jwtToken->claims()->get('ful'),
  77.             $expires->getTimestamp(),
  78.             $errorUrl
  79.         );
  80.     }
  81.     public function invalidateToken(string $token): bool
  82.     {
  83.         $this->delete($token);
  84.         return false;
  85.     }
  86.     private function write(string $token\DateTimeImmutable $expires): void
  87.     {
  88.         $this->connection->insert('payment_token', [
  89.             'token' => self::normalize($token),
  90.             'expires' => $expires->format(Defaults::STORAGE_DATE_TIME_FORMAT),
  91.         ]);
  92.     }
  93.     private function delete(string $token): void
  94.     {
  95.         $this->connection->executeStatement(
  96.             'DELETE FROM payment_token WHERE token = :token',
  97.             ['token' => self::normalize($token)]
  98.         );
  99.     }
  100.     private function has(string $token): bool
  101.     {
  102.         $valid $this->connection->fetchOne('SELECT token FROM payment_token WHERE token = :token', ['token' => self::normalize($token)]);
  103.         return $valid !== false;
  104.     }
  105.     private static function normalize(string $token): string
  106.     {
  107.         return substr(hash('sha256'$token), 032);
  108.     }
  109. }