app/Plugin/AmazonPayV2_42/AmazonPayEvent.php line 338

Open in your IDE?
  1. <?php
  2. /*
  3.  * Amazon Pay V2 for EC-CUBE4.2
  4.  * Copyright(c) 2023 EC-CUBE CO.,LTD. all rights reserved.
  5.  *
  6.  * https://www.ec-cube.co.jp/
  7.  *
  8.  * This program is not free software.
  9.  * It applies to terms of service.
  10.  *
  11.  */
  12. namespace Plugin\AmazonPayV2_42;
  13. use Eccube\Event\TemplateEvent;
  14. use Eccube\Event\EventArgs;
  15. use Eccube\Event\EccubeEvents;
  16. use Eccube\Common\EccubeConfig;
  17. use Eccube\Repository\PaymentRepository;
  18. use Eccube\Repository\PluginRepository;
  19. use Eccube\Service\OrderHelper;
  20. use Eccube\Service\CartService;
  21. use Plugin\AmazonPayV2_42\Repository\ConfigRepository;
  22. use Plugin\AmazonPayV2_42\Service\AmazonRequestService;
  23. use Plugin\AmazonPayV2_42\Service\Method\AmazonPay;
  24. use Plugin\AmazonPayV2_42\phpseclib\Crypt\Random;
  25. use Psr\Container\ContainerInterface;
  26. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  27. use Symfony\Component\HttpFoundation\RequestStack;
  28. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  29. use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
  30. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  31. use Eccube\Repository\DeliveryRepository;
  32. use Eccube\Repository\PaymentOptionRepository;
  33. use Plugin\AmazonPayV2_42\Entity\AmazonBanner;
  34. use Plugin\AmazonPayV2_42\Service\AmazonBannerService;
  35. class AmazonPayEvent implements EventSubscriberInterface
  36. {
  37.     /**
  38.      * @var string プロファイル情報キー
  39.      */
  40.     private $sessionAmazonProfileKey 'amazon_pay_v2.profile';
  41.     /**
  42.      * @var string プロファイル情報キー
  43.      */
  44.     private $sessionAmazonCheckoutSessionIdKey 'amazon_pay_v2.checkout_session_id';
  45.     /**
  46.      * @var string Amazonログインステート
  47.      */
  48.     private $sessionAmazonLoginStateKey 'amazon_pay_v2.amazon_login_state';
  49.     /**
  50.      * @var EccubeConfig
  51.      */
  52.     protected $eccubeConfig;
  53.     /**
  54.      * @var UrlGeneratorInterface
  55.      */
  56.     private $router;
  57.     /**
  58.      * @var ConfigRepository
  59.      */
  60.     protected $configRepository;
  61.     /**
  62.      * @var AmazonRequestService
  63.      */
  64.     protected $amazonRequestService;
  65.     /**
  66.      * @var DeliveryRepository
  67.      */
  68.     protected $deliveryRepository;
  69.     /**
  70.      * @var DeliveryRepository
  71.      */
  72.     protected $paymentOptionRepository;
  73.     /**
  74.      * Amazon Payバナーサービス
  75.      *
  76.      * @var AmazonBannerService
  77.      */
  78.     protected $amazonBannerService;
  79.     /**
  80.      * AmazonPayEvent
  81.      *
  82.      * @param eccubeConfig $eccubeConfig
  83.      * @param ConfigRepository $configRepository
  84.      */
  85.     public function __construct(
  86.         RequestStack $requestStack,
  87.          
  88.         TokenStorageInterface $tokenStorage,
  89.         EccubeConfig $eccubeConfig,
  90.         UrlGeneratorInterface $router,
  91.         PaymentRepository $paymentRepository,
  92.         PluginRepository $pluginRepository,
  93.         ConfigRepository $configRepository,
  94.         ContainerInterface $container,
  95.         OrderHelper $orderHelper,
  96.         CartService $cartService,
  97.         AmazonRequestService $amazonRequestService,
  98.         DeliveryRepository $deliveryRepository,
  99.         PaymentOptionRepository $paymentOptionRepository,
  100.         AmazonBannerService $amazonBannerService
  101.     ) {
  102.         $this->requestStack $requestStack;
  103.         $this->session $requestStack->getSession();
  104.         $this->tokenStorage $tokenStorage;
  105.         $this->eccubeConfig $eccubeConfig;
  106.         $this->router $router;
  107.         $this->paymentRepository $paymentRepository;
  108.         $this->pluginRepository $pluginRepository;
  109.         $this->configRepository $configRepository;
  110.         $this->container $container;
  111.         $this->orderHelper $orderHelper;
  112.         $this->cartService $cartService;
  113.         $this->amazonRequestService $amazonRequestService;
  114.         $this->deliveryRepository $deliveryRepository;
  115.         $this->paymentOptionRepository $paymentOptionRepository;
  116.         $this->amazonBannerService $amazonBannerService;
  117.     }
  118.     /**
  119.      * {@inheritdoc}
  120.      */
  121.     public static function getSubscribedEvents()
  122.     {
  123.         return [
  124.             EccubeEvents::FRONT_CART_BUYSTEP_COMPLETE => 'amazon_cart_buystep',
  125.             'Cart/index.twig' => 'cart',
  126.             'Shopping/index.twig' => 'amazon_pay_shopping',
  127.             'Mypage/login.twig' => 'mypage_login',
  128.             'Shopping/confirm.twig' => 'amazon_pay_shopping_confirm',
  129.             'index.twig' => 'add_banner_on_top',
  130.             //ショッピングページでも使用できるよう追加
  131.                         'Shopping/index.twig' => 'shopping'
  132.                         'Shopping/login.twig' => 'shopping_login',
  133.      
  134.             'lp/prostate-care.twig' => ['lp_prostate_care', -99999],
  135.                     
  136.         ];
  137.     }
  138.     /**
  139.      * トップページのイベント
  140.      * Amazon様バナーを挿入する
  141.      *
  142.      * @param TemplateEvent $event
  143.      * @return void
  144.      */
  145.     public function add_banner_on_top(TemplateEvent $event)
  146.     {
  147.         $Config $this->configRepository->get();
  148.         
  149.         if ($Config->getUseAmazonBannerOnTop() == $this->eccubeConfig['amazon_pay_v2']['toggle']['off']) {
  150.             return;
  151.         }
  152.         if ($Config->getAmazonBannerPlaceOnTop() == $this->eccubeConfig['amazon_pay_v2']['button_place']['auto']) {
  153.             $event->addSnippet('@AmazonPayV2_42/default/amazon_banner_auto_on_top.twig');
  154.         }
  155.         
  156.         $event->addSnippet($this->amazonBannerService->getBannerCodeOnTop(), false);
  157.     }
  158.     public function cart(TemplateEvent $event)
  159.     {
  160.         $Config $this->configRepository->get();
  161.         if ($Config->getUseAmazonBannerOnCart() != $this->eccubeConfig['amazon_pay_v2']['toggle']['off']) {
  162.             if ($Config->getAmazonBannerPlaceOnCart() == $this->eccubeConfig['amazon_pay_v2']['button_place']['auto']) {
  163.                 $event->addSnippet('@AmazonPayV2_42/default/amazon_banner_auto_on_cart.twig');
  164.             }
  165.             $event->addSnippet($this->amazonBannerService->getBannerCodeOnCart(), false);
  166.         }
  167.         $parameters $event->getParameters();
  168.         if (empty($parameters['Carts'])) {
  169.             return;
  170.         }
  171.         if ($Config->getUseCartButton() == $this->eccubeConfig['amazon_pay_v2']['toggle']['off']) {
  172.             return;
  173.         }
  174.         // AmazonPayに紐づく商品種別の取得
  175.         $Payment $this->paymentRepository->findOneBy(['method_class' => AmazonPay::class]);
  176.         $AmazonDeliveries $this->paymentOptionRepository->findBy(['payment_id' => $Payment->getId()]);
  177.         $AmazonSaleTypes = [];
  178.         foreach ($AmazonDeliveries as $AmazonDelivery) {
  179.             $Delivery $this->deliveryRepository->findOneBy(['id' => $AmazonDelivery->getDelivery()->getId()]);
  180.             $AmazonSaleTypes[] = $Delivery->getSaleType()->getId();
  181.         }
  182.         $parameters['AmazonSaleTypes'] = $AmazonSaleTypes;
  183.         foreach ($parameters['Carts'] as $Cart) {
  184.             $cartKey $Cart->getCartKey();
  185.             $payload $this->amazonRequestService->createCheckoutSessionPayload($Cart->getCartKey());
  186.             $signature $this->amazonRequestService->signaturePayload($payload);
  187.             $parameters['cart'][$cartKey]['payload'] = $payload;
  188.             $parameters['cart'][$cartKey]['signature'] = $signature;
  189.         }
  190.         $parameters['AmazonPayV2Config'] = $Config;
  191.         if ($Config->getEnv() == $this->eccubeConfig['amazon_pay_v2']['env']['prod']) {
  192.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['prod'];
  193.         } else {
  194.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['sandbox'];
  195.         }
  196.         $event->setParameters($parameters);
  197.         $event->addSnippet('@AmazonPayV2_42/default/Cart/amazon_pay_js.twig');
  198.         if ($Config->getCartButtonPlace() == $this->eccubeConfig['amazon_pay_v2']['button_place']['auto']) {
  199.             $event->addSnippet('@AmazonPayV2_42/default/Cart/button.twig');
  200.         }
  201.     }
  202.     public function amazon_cart_buystep(EventArgs $event)
  203.     {
  204.         // Amazonログインによる仮会員情報がセッションにセットされていたら
  205.         if ($this->orderHelper->getNonmember() && $this->session->get($this->sessionAmazonProfileKey)) {
  206.             // 仮会員情報を削除
  207.             $this->session->remove(OrderHelper::SESSION_NON_MEMBER);
  208.             $this->session->remove($this->sessionAmazonProfileKey);
  209.             $this->cartService->setPreOrderId(null);
  210.             $this->cartService->save();
  211.         }
  212.     }
  213.     public function amazon_pay_shopping(TemplateEvent $event)
  214.     {
  215.         $request $this->requestStack->getMainRequest();
  216.         $uri $request->getUri();
  217.         $parameters $event->getParameters();
  218.         if (preg_match('/shopping\/amazon_pay/'$uri) == false) {
  219.             $referer $request->headers->get('referer');
  220.             $Order $parameters['Order'];
  221.             $Payment $Order->getPayment();
  222.             // AmazonPay決済確認画面→クーポン入力画面→決済確認画面への遷移時にAmazonPay決済確認画面へ戻す
  223.             if ($Payment && $Payment->getMethodClass() === AmazonPay::class && preg_match('/shopping_coupon/'$referer)) {
  224.                 header("Location:" $this->router->generate('amazon_pay_shopping'));
  225.                 exit;
  226.             }
  227.             return;
  228.         }
  229.         $Config $this->configRepository->get();
  230.         $event->addSnippet('@AmazonPayV2_42/default/Shopping/widgets.twig');
  231.         $event->addSnippet('@AmazonPayV2_42/default/Shopping/customer_regist_v2.twig');
  232.         // チェックアウトセッションIDを取得する
  233.         $amazonCheckoutSessionId $this->session->get($this->sessionAmazonCheckoutSessionIdKey);
  234.         $parameters $event->getParameters();
  235.         $parameters['amazonCheckoutSessionId'] = $amazonCheckoutSessionId;
  236.         $parameters['AmazonPayV2Config'] = $Config;
  237.         // メルマガプラグイン利用時はチェックボックスを追加
  238.         if (
  239.             $this->pluginRepository->findOneBy(['code' => 'MailMagazine42''enabled' => true])
  240.             || $this->pluginRepository->findOneBy(['code' => 'PostCarrier42''enabled' => true])
  241.         ) {
  242.             $useMailMagazine true;
  243.         } else {
  244.             $useMailMagazine false;
  245.         }
  246.         $parameters['useMailMagazine'] = $useMailMagazine;
  247.         if ($Config->getEnv() == $this->eccubeConfig['amazon_pay_v2']['env']['prod']) {
  248.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['prod'];
  249.         } else {
  250.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['sandbox'];
  251.         }
  252.         $event->setParameters($parameters);
  253.     }
  254.     public function amazon_pay_shopping_confirm(TemplateEvent $event)
  255.     {
  256.         $request $this->requestStack->getMainRequest();
  257.         $uri $request->getUri();
  258.         if (preg_match('/shopping\/amazon_pay/'$uri) == false) {
  259.             return;
  260.         }
  261.         $Config $this->configRepository->get();
  262.         $event->addSnippet('@AmazonPayV2_42/default/Shopping/confirm_widgets.twig');
  263.         $event->addSnippet('@AmazonPayV2_42/default/Shopping/confirm_customer_regist_v2.twig');
  264.         $parameters $event->getParameters();
  265.         $parameters['AmazonPayV2Config'] = $Config;
  266.         // メルマガプラグイン利用時はチェックボックスを追加
  267.         if (
  268.             $this->pluginRepository->findOneBy(['code' => 'MailMagazine42''enabled' => true])
  269.             || $this->pluginRepository->findOneBy(['code' => 'PostCarrier42''enabled' => true])
  270.         ) {
  271.             $useMailMagazine true;
  272.         } else {
  273.             $useMailMagazine false;
  274.         }
  275.         $parameters['useMailMagazine'] = $useMailMagazine;
  276.         if ($Config->getEnv() == $this->eccubeConfig['amazon_pay_v2']['env']['prod']) {
  277.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['prod'];
  278.         } else {
  279.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['sandbox'];
  280.         }
  281.         $event->setParameters($parameters);
  282.     }
  283.     public function mypage_login(TemplateEvent $event)
  284.     {
  285.         $Config $this->configRepository->get();
  286.         if ($Config->getUseMypageLoginButton() == $this->eccubeConfig['amazon_pay_v2']['toggle']['off']) {
  287.             return;
  288.         }
  289.         $state $this->session->get($this->sessionAmazonLoginStateKey);
  290.         if (!$state) {
  291.             // stateが生成されていなければ、生成及びセッションセット
  292.             $state bin2hex(Random::string(16));
  293.             $this->session->set($this->sessionAmazonLoginStateKey$state);
  294.         }
  295.         $returnUrl $this->router->generate('login_with_amazon', ['state' => $state], UrlGeneratorInterface::ABSOLUTE_URL);
  296.         $parameters $event->getParameters();
  297.         $payload $this->amazonRequestService->createSigninPayload($returnUrl);
  298.         $signature $this->amazonRequestService->signaturePayload($payload);
  299.         $parameters['payload'] = $payload;
  300.         $parameters['signature'] = $signature;
  301.         $parameters['buttonColor'] = $Config->getMypageLoginButtonColor();
  302.         $parameters['AmazonPayV2Config'] = $Config;
  303.         if ($Config->getEnv() == $this->eccubeConfig['amazon_pay_v2']['env']['prod']) {
  304.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['prod'];
  305.         } else {
  306.             $parameters['AmazonPayV2Api'] = $this->eccubeConfig['amazon_pay_v2']['api']['sandbox'];
  307.         }
  308.         $event->setParameters($parameters);
  309.         if ($Config->getMypageLoginButtonPlace() == $this->eccubeConfig['amazon_pay_v2']['button_place']['auto']) {
  310.             $event->addSnippet('@AmazonPayV2_42/default/Mypage/login_page_button.twig');
  311.         }
  312.         $event->addSnippet('@AmazonPayV2_42/default/Mypage/amazon_login_js.twig');
  313.     }
  314.     
  315.     // ショッピングページでも使用できるよう追加
  316.     public function shopping(TemplateEvent $event)
  317.         {
  318.             
  319.             $parameters $event->getParameters();
  320.             if (empty($parameters['Carts'])) {
  321.                 $parameters['Carts'] = $this->cartService->getCarts();
  322.             }
  323.             $event->setParameters($parameters);
  324.             $this->amazon_pay_shopping($event);
  325.             $this->cart($event);
  326.         }
  327.     // ショッピングページでも使用できるよう追加
  328.         public function shopping_login(TemplateEvent $event)
  329.         {
  330.             $parameters $event->getParameters();
  331.             if (empty($parameters['Carts'])) {
  332.                 $parameters['Carts'] = $this->cartService->getCarts();
  333.             }
  334.             $event->setParameters($parameters);
  335.             $this->cart($event);
  336.         }
  337. public function lp_prostate_care(TemplateEvent $event)
  338. {
  339.     $req $this->requestStack->getMainRequest();
  340.     $this->amzDebug('lp_prostate_care fired path=' . ($req $req->getPathInfo() : 'null') . ' qs=' . ($req $req->getQueryString() : 'null'));
  341.     $Config $this->configRepository->get();
  342.     $parameters $event->getParameters();
  343.     if (empty($parameters['Carts'])) {
  344.         $parameters['Carts'] = $this->cartService->getCarts();
  345.     }
  346.     if (!isset($parameters['cart']) || !is_array($parameters['cart'])) {
  347.         $parameters['cart'] = [];
  348.     }
  349.     // Always set these (LP JS needs them)
  350.     $parameters['AmazonPayV2Config'] = $Config;
  351.     $parameters['AmazonPayV2Api'] =
  352.         ($Config->getEnv() == $this->eccubeConfig['amazon_pay_v2']['env']['prod'])
  353.             ? $this->eccubeConfig['amazon_pay_v2']['api']['prod']
  354.             : $this->eccubeConfig['amazon_pay_v2']['api']['sandbox'];
  355.     // Only guard the signing, NOT the snippet injection
  356.     if (empty($parameters['_lp_amz_signed'])) {
  357.         $parameters['_lp_amz_signed'] = true;
  358.         if (!empty($parameters['Carts'])) {
  359.             foreach ($parameters['Carts'] as $Cart) {
  360.                 $cartKey $Cart->getCartKey();
  361.                 $payload $this->amazonRequestService->createCheckoutSessionPayload($cartKey);
  362.                 $signature $this->amazonRequestService->signaturePayload($payload);
  363.                 $parameters['cart'][$cartKey]['payload'] = $payload;
  364.                 $parameters['cart'][$cartKey]['signature'] = $signature;
  365.                 $this->amzDebug(sprintf(
  366.     'cartKey=%s env=%s sellerId=%s publicKeyId=%s payloadLen=%d payloadSha256=%s sigLen=%d',
  367.     $cartKey,
  368.     (string) $Config->getEnv(),
  369.     (string) $Config->getSellerId(),
  370.     (string) $Config->getPublicKeyId(),
  371.     strlen($payload),
  372.     hash('sha256'$payload),
  373.     strlen($signature)
  374. ));
  375. if ($this->amzDebugEnabled()) {
  376.     $sigPlugin $this->amazonRequestService->signaturePayload($payload);
  377.     $this->amzDebug('sig_equal=' . (hash_equals($sigPlugin$signature) ? '1' '0'));
  378. }
  379.             }
  380.         }
  381.     }
  382.           
  383.                
  384.              
  385.                 
  386.   
  387.     $event->setParameters($parameters);
  388.  
  389.     // ALWAYS do this
  390.     $event->addSnippet('lp/amazon_pay_lp_js.twig');
  391. }
  392. public function lp_prostate_care_frame(TemplateEvent $event)
  393. {
  394.     $req $this->requestStack->getMainRequest();
  395.     if (!$req) {
  396.         return;
  397.     }
  398.     // Only for the LP URL
  399.     if ($req->getPathInfo() !== '/lp/prostate-care') {
  400.         return;
  401.     }
  402.     // Reuse your existing LP logic (payload + addSnippet)
  403.     $this->lp_prostate_care($event);
  404. }
  405. private function amzDebugEnabled(): bool
  406. {
  407.     $req $this->requestStack->getMainRequest();
  408.     if (!$req) return false;
  409.     // Only when you visit with ?amzdebug=1
  410.     return $req->query->get('amzdebug') === '1';
  411. }
  412. private function amzDebug(string $msg): void
  413. {
  414.     if ($this->amzDebugEnabled()) {
  415.         error_log('[AMZDEBUG] ' $msg);
  416.     }
  417. }
  418.     /**
  419.      * Resolve the private key PEM from the plugin Config.
  420.      * Supports both "path stored in config" and "PEM stored in config" patterns.
  421.      */
  422.     private function resolveAmazonPayPrivateKeyPem($Config): string
  423.     {
  424.         $candidates = [];
  425.         foreach ([
  426.             'getPrivateKeyPem',
  427.             'getPrivateKey',
  428.             'getPrivateKeyPath',
  429.             'getPrivateKeyFile',
  430.             'getPrivateKeyFilePath',
  431.             'getSecretKeyPath',
  432.             'getSecretKey',
  433.         ] as $method) {
  434.             if (is_object($Config) && method_exists($Config$method)) {
  435.                 try {
  436.                     $candidates[] = $Config->$method();
  437.                 } catch (\Throwable $e) {
  438.                     // ignore and continue
  439.                 }
  440.             }
  441.         }
  442.         foreach ($candidates as $v) {
  443.             if (!is_string($v) || $v === '') continue;
  444.             // Direct PEM
  445.             if (strpos($v'BEGIN') !== false && strpos($v'PRIVATE KEY') !== false) {
  446.                 return $v;
  447.             }
  448.             // Path to PEM
  449.             if (is_readable($v)) {
  450.                 $pem file_get_contents($v);
  451.                 if (is_string($pem) && strpos($pem'BEGIN') !== false) {
  452.                     return $pem;
  453.                 }
  454.             }
  455.         }
  456.         throw new \RuntimeException('AmazonPay: could not resolve private key PEM from Config.');
  457.     }
  458.     /**
  459.      * Create Amazon Pay CV2 signature (RSA-PSS, SHA256, saltLength=20) for the given payload.
  460.      * Avoids relying on OPENSSL_PKCS1_PSS_PADDING which may be unavailable on some hosts.
  461.      */
  462.     private function signPayloadForAmazonPay(string $payload$Config): string
  463.     {
  464.         // JS string-literal interpretation unescapes '\/' -> '/', so normalize before hashing/signing.
  465.         $payloadForSign str_replace('\\\/''/'$payload);
  466.         $payloadHash  hash('sha256'$payloadForSign);
  467.         $stringToSign "AMZN-PAY-RSASSA-PSS\n" $payloadHash;
  468.         try {
  469.             $pem $this->resolveAmazonPayPrivateKeyPem($Config);
  470.         } catch (\Throwable $e) {
  471.             // Fallback to the plugin's existing signer to avoid breaking the page.
  472.             return $this->amazonRequestService->signaturePayload($payload);
  473.         }
  474.         // Prefer OpenSSL PSS if available
  475.         if (defined('OPENSSL_PKCS1_PSS_PADDING')) {
  476.             $privateKey openssl_pkey_get_private($pem);
  477.             if ($privateKey) {
  478.                 $signatureBin '';
  479.                 $ok openssl_sign(
  480.                     $stringToSign,
  481.                     $signatureBin,
  482.                     $privateKey,
  483.                     OPENSSL_ALGO_SHA256,
  484.                     [
  485.                         'rsaPadding' => OPENSSL_PKCS1_PSS_PADDING,
  486.                         'saltLength' => 20,
  487.                     ]
  488.                 );
  489.                 if ($ok) {
  490.                     return base64_encode($signatureBin);
  491.                 }
  492.             }
  493.         }
  494.         // Fallback: phpseclib3 (pure PHP) RSA-PSS
  495.         if (class_exists('\phpseclib3\Crypt\PublicKeyLoader') && class_exists('\phpseclib3\Crypt\RSA')) {
  496.             $private \phpseclib3\Crypt\PublicKeyLoader::loadPrivateKey($pem)
  497.                 ->withPadding(\phpseclib3\Crypt\RSA::SIGNATURE_PSS)
  498.                 ->withHash('sha256')
  499.                 ->withMGFHash('sha256')
  500.                 ->withSaltLength(20);
  501.             return base64_encode($private->sign($stringToSign));
  502.         }
  503.         throw new \RuntimeException('AmazonPay: RSA-PSS signing unavailable. OPENSSL_PKCS1_PSS_PADDING is missing and phpseclib3 is not installed.');
  504.     }
  505. }