app/Plugin/SheebDlc42Subscription/Event.php line 134

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of Customize of EC-CUBE by Kenji Nakanishi
  4.  *
  5.  * Copyright(c) 2021 Kenji Nakanishi. All Rights Reserved.
  6.  *
  7.  * https://www.facebook.com/web.kenji.nakanishi
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Plugin\SheebDlc42Subscription;
  13. use Doctrine\ORM\EntityManagerInterface;
  14. use Eccube\Common\EccubeConfig;
  15. use Eccube\Entity\Master\OrderStatus;
  16. use Eccube\Entity\Order;
  17. use Eccube\Event\EccubeEvents;
  18. use Eccube\Event\EventArgs;
  19. use Eccube\Event\TemplateEvent;
  20. use Eccube\Repository\Master\OrderStatusRepository;
  21. use Plugin\SheebDlc42Subscription\Entity\Config;
  22. use Plugin\SheebDlc42Subscription\Entity\PaymentStatus;
  23. use Plugin\SheebDlc42Subscription\Repository\ConfigRepository;
  24. use Plugin\SheebDlc42Subscription\Repository\PaymentStatusRepository;
  25. use Plugin\SheebDlc42Subscription\Service\MailService;
  26. use Plugin\SheebDlc42Subscription\Service\Method\CreditCard;
  27. use Plugin\SheebDlc42Subscription\Service\PaymentService;
  28. use Symfony\Component\Asset\Packages;
  29. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  30. use Symfony\Component\HttpFoundation\RedirectResponse;
  31. use Symfony\Component\HttpFoundation\RequestStack;
  32. use Symfony\Component\HttpFoundation\Response;
  33. use Symfony\Component\HttpFoundation\Session\Session;
  34. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  35. use Symfony\Component\Routing\RouterInterface;
  36. class Event implements EventSubscriberInterface
  37. {
  38.     /**
  39.      * リッスンしたいサブスクライバのイベント名の配列を返します。
  40.      * 配列のキーはイベント名、値は以下のどれかをしてします。
  41.      * - 呼び出すメソッド名
  42.      * - 呼び出すメソッド名と優先度の配列
  43.      * - 呼び出すメソッド名と優先度の配列の配列
  44.      * 優先度を省略した場合は0
  45.      *
  46.      * 例:
  47.      * - array('eventName' => 'methodName')
  48.      * - array('eventName' => array('methodName', $priority))
  49.      * - array('eventName' => array(array('methodName1', $priority), array('methodName2')))
  50.      *
  51.      * {@inheritdoc}
  52.      */
  53.     public static function getSubscribedEvents()
  54.     {
  55.         return [
  56.             // 管理画面
  57.             '@admin/index.twig' => 'alertUnable',
  58.             '@admin/Order/index.twig' => 'alertUnable',
  59.             '@admin/Store/plugin.twig' => 'alertUnable',
  60.             '@admin/Order/edit.twig' => 'onAdminOrderEditTwig',
  61.             // フロント画面
  62.             'Shopping/confirm.twig' => ['onShoppingConfirmCreditCardTwig'],
  63.             // 受注正常終了
  64.             EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE => ['onFrontShoppingCompleteInitialize'],
  65.         ];
  66.     }
  67.     /**
  68.      * @var ConfigRepository
  69.      */
  70.     protected $configRepository;
  71.     /**
  72.      * @var PaymentStatusRepository
  73.      */
  74.     protected $paymentStatusRepository;
  75.     /**
  76.      * @var OrderStatusRepository
  77.      */
  78.     protected $orderStatusRepository;
  79.     /**
  80.      * @var EccubeConfig
  81.      */
  82.     protected $eccubeConfig;
  83.     /**
  84.      * @var EntityManagerInterface
  85.      */
  86.     protected $entityManager;
  87.     /**
  88.      * @var RouterInterface
  89.      */
  90.     protected $router;
  91.     /**
  92.      * @var Session
  93.      */
  94.     protected $session;
  95.     /**
  96.      * @var MailService
  97.      */
  98.     protected $mailService;
  99.     public function __construct(
  100.         ConfigRepository $configRepository,
  101.         PaymentStatusRepository $paymentStatusRepository,
  102.         OrderStatusRepository $orderStatusRepository,
  103.         EccubeConfig $eccubeConfig,
  104.         EntityManagerInterface $entityManager,
  105.         RouterInterface $router,
  106.         SessionInterface $session,
  107.         MailService $mailService
  108.     ) {
  109.         $this->configRepository $configRepository;
  110.         $this->paymentStatusRepository $paymentStatusRepository;
  111.         $this->orderStatusRepository $orderStatusRepository;
  112.         $this->eccubeConfig $eccubeConfig;
  113.         $this->entityManager $entityManager;
  114.         $this->router $router;
  115.         $this->session $session;
  116.         $this->mailService $mailService;
  117.     }
  118.     public function alertUnable(TemplateEvent $event)
  119.     {
  120.         // 本体プラグインが非活性の場合はアラート
  121.         $unable PluginManager::unableMainPluginMessage($this->entityManager);
  122.         if (!empty($unable)) {
  123.             $this->session->getFlashBag()->add('eccube.admin.sheeb_dlc42_subscription.error'$unable);
  124.             $event->addSnippet('@SheebDlc42Subscription/admin/alert.twig');
  125.         }
  126.     }
  127.     /**
  128.      * 管理画面: Order Edit
  129.      *
  130.      * @param TemplateEvent $event
  131.      * @return void
  132.      */
  133.     public function onAdminOrderEditTwig(TemplateEvent $event)
  134.     {
  135.         /** @var Order */
  136.         $Order $event->getParameter('Order');
  137.         // Stripe管理画面URL
  138.         $stripeConsoleURL '';
  139.         $paymentIntentId $Order->getSheebDlc42SubscriptionPaymentIntentId();
  140.         if (!empty($paymentIntentId)) {
  141.             $stripeConsoleURL 'https://dashboard.stripe.com';
  142.             if ($Order->getSheebDlc42SubscriptionMode() !== PaymentService::MODE_PRODUCTION) {
  143.                 $stripeConsoleURL .= '/test';
  144.             }
  145.             $stripeConsoleURL .= "/payments/{$paymentIntentId}";
  146.         }
  147.         $event->setParameter('stripe_console_url'$stripeConsoleURL);
  148.         // 有効UI
  149.         $PaymentStatus $Order->getSheebDlc42SubscriptionPaymentStatus();
  150.         $isEnableWaiting = empty($PaymentStatus) ? false $PaymentStatus->isWaiting();
  151.         $isEnableActualSales = empty($PaymentStatus) ? false $PaymentStatus->isActualSales() || $PaymentStatus->isPartiallyRefunded();
  152.         $isEnableProvisionalSales = empty($PaymentStatus) ? false $PaymentStatus->isProvisionalSales();
  153.         $event->setParameter('is_enable_waiting'$isEnableWaiting);
  154.         $event->setParameter('is_enable_actual_sales'$isEnableActualSales);
  155.         $event->setParameter('is_enable_provisional_sales'$isEnableProvisionalSales);
  156.         if ($isEnableActualSales) {
  157.             // 実売上 OR 一部返金済み
  158.             $paymentService PaymentService::getInstance(
  159.                 $this->configRepository->get(),
  160.                 $this->eccubeConfig->get('currency')
  161.             );
  162.             // 返金済み金額
  163.             $refundedAmount $paymentService->getPaymentIntentRefundTotal($Order);
  164.             $event->setParameter('refunded_amount'$refundedAmount);
  165.             // 最大返金指定可能額
  166.             $event->setParameter('refundable_amount'$Order->getPaymentTotal() - $refundedAmount);
  167.         } elseif ($isEnableProvisionalSales) {
  168.             // 仮売上
  169.         }
  170.         // Twig読み込み
  171.         $event->addSnippet('@SheebDlc42Subscription/admin/order_edit.twig');
  172.     }
  173.     /**
  174.      * Shopping/confirm.twig
  175.      * 購入手続き確認画面
  176.      *
  177.      * 支払方法が「クレジットカード決済」の場合
  178.      */
  179.     public function onShoppingConfirmCreditCardTwig(TemplateEvent $event)
  180.     {
  181.         // 公開可能キーをフロントに渡す
  182.         $Config $this->configRepository->get();
  183.         $event->setParameter('public_key'PaymentService::getPublicKey($Config));
  184.         // Twig追加
  185.         $event->addAsset('@SheebDlc42Subscription/credit_confirm/assets.twig');
  186.         $event->addSnippet('@SheebDlc42Subscription/credit_confirm/elements.twig');
  187.         $event->addSnippet('@SheebDlc42Subscription/credit_confirm/logic.twig');
  188.     }
  189.     /**
  190.      * EC-CUBE決済処理が正常終了したら、Stripe決済を完了させる
  191.      *
  192.      * @param EventArgs $event
  193.      * @return void
  194.      */
  195.     public function onFrontShoppingCompleteInitialize(EventArgs $event)
  196.     {
  197.         /** @var Order $Order */
  198.         $Order $event->getArgument('Order');
  199.         // ■ クレジット決済以外は無視
  200.         if ($Order->getPayment()->getMethodClass() !== CreditCard::class) {
  201.             return;
  202.         }
  203.         // ■ 処理済みの場合は無視(支払ステータス: 未決済以外は全て無視)
  204.         if ($Order->getSheebDlc42SubscriptionPaymentStatus()->getId() !== PaymentStatus::OUTSTANDING) {
  205.             return;
  206.         }
  207.         // ■ 処理済みの場合は無視(対応状況: 新規受付以外は無視))
  208.         if ($Order->getOrderStatus()->getId() !== OrderStatus::NEW) {
  209.             return;
  210.         }
  211.         // ■ 素材準備
  212.         $Config $this->configRepository->get();
  213.         $paymentService PaymentService::getInstance(
  214.             $Config,
  215.             $this->eccubeConfig->get('currency')
  216.         );
  217.         // ■ 支払ステータス: 仮売上
  218.         (function (Order $Order) {
  219.             // 支払ステータスを実売上にするのは Webhook で確定成功をStripeから通知を受けた時とする
  220.             $PaymentStatus $this->paymentStatusRepository->find(PaymentStatus::PROVISIONAL_SALES);
  221.             $Order->setSheebDlc42SubscriptionPaymentStatus($PaymentStatus);
  222.             // Webhookよりもステータス変更を先に確実に行う必要があるため、paymentByOrder よりも先に保存
  223.             $this->entityManager->persist($Order);
  224.             $this->entityManager->flush($Order);
  225.         })($Order);
  226.         // ■ PaymentMethodを利用して、PaymentIntentを作成
  227.         $paymentResult = (function (Order $OrderPaymentService $paymentService) {
  228.             // 3D Secure が承認された後にリダイレクトされるURLを準備
  229.             $baseURI = empty($_SERVER['HTTPS']) ? 'http://' 'https://';
  230.             $baseURI .= $_SERVER['HTTP_HOST'];
  231.             $returnURI $baseURI $this->router->generate('sheeb_dlc42_subscription_payment_3d_secure_redirect');
  232.             // PaymentIntentを作成
  233.             $paymentResult $paymentService->paymentByOrder($Order$returnURI);
  234.             $this->entityManager->persist($Order);
  235.             $this->entityManager->flush($Order);
  236.             return $paymentResult;
  237.         })($Order$paymentService);
  238.         // ■ Statusに応じた処理
  239.         $response = (function (EventArgs $eventPaymentService $paymentService$paymentResult) {
  240.             // 正常系ステータス
  241.             $allowStatuses = [
  242.                 'succeeded',
  243.                 'requires_capture',
  244.             ];
  245.             if ($paymentResult['status'] === 'requires_action') {
  246.                 // 3Dセキュアが必要な場合、3Dセキュア画面に遷移する
  247.                 return $this->redirect3dSecure($event$paymentResult$paymentService);
  248.             } elseif (!in_array($paymentResult['status'], $allowStatuses)) {
  249.                 // 売上処理に失敗した場合、エラー画面に遷移する
  250.                 $message trans('sheeb_dlc42_subscription.shopping.error.payment');
  251.                 return $this->redirectError($event$paymentService$message);
  252.             }
  253.             return null;
  254.         })($event$paymentService$paymentResult);
  255.         if ($response instanceof Response) {
  256.             $event->setResponse($response);
  257.             return;
  258.         }
  259.         // ■ 売上を確定
  260.         (function (Config $ConfigOrder $OrderPaymentService $paymentService) {
  261.             // 必要がなければ終了
  262.             if ($Config->getPaymentPattern() !== PaymentService::PAYMENT_PATTERN_CREDIT_AND_SALES) {
  263.                 return;
  264.             }
  265.             // 確定処理
  266.             $paymentService->capture($Order);
  267.             // 本受注メールの送信
  268.             $this->mailService->sendSheebDlc42SubscriptionOrderMail($Order);
  269.             $this->entityManager->flush();
  270.         })($Config$Order$paymentService);
  271.         // ■ 必要があれば 支払方法を削除
  272.         (function (Order $OrderPaymentService $paymentService) {
  273.             // 必要がなければ終了
  274.             if ($Order->getSheebDlc42SubscriptionCanRegisterCard()) {
  275.                 return;
  276.             }
  277.             // 支払方法を削除
  278.             $paymentService->removeRegisteredCard(
  279.                 $Order->getSheebDlc42SubscriptionStripeCustomerId(),
  280.                 $Order->getSheebDlc42SubscriptionPaymentMethodId()
  281.             );
  282.         })($Order$paymentService);
  283.     }
  284.     /**
  285.      * 3D Secure 認証ページに遷移
  286.      *
  287.      * @param EventArgs $event
  288.      * @param [type] $paymentResult
  289.      * @return void
  290.      */
  291.     private function redirect3dSecure(EventArgs $event$paymentResultPaymentService $paymentService)
  292.     {
  293.         /** @var Order $Order */
  294.         $Order $event->getArgument('Order');
  295.         // 支払ステータス: ユーザー認証待ち
  296.         $PaymentStatus $this->paymentStatusRepository->find(PaymentStatus::WAITING);
  297.         $Order->setSheebDlc42SubscriptionPaymentStatus($PaymentStatus);
  298.         // 対応状況: 購入処理中
  299.         $OrderStatus $this->orderStatusRepository->find(OrderStatus::PROCESSING);
  300.         $Order->setOrderStatus($OrderStatus);
  301.         if (!isset($paymentResult['nextAction'])) {
  302.             return $this->redirectError($event$paymentService);
  303.         }
  304.         if (!isset($paymentResult['nextAction']['redirect_to_url'])) {
  305.             return $this->redirectError($event$paymentService);
  306.         }
  307.         if (!isset($paymentResult['nextAction']['redirect_to_url']['url'])) {
  308.             return $this->redirectError($event$paymentService);
  309.         }
  310.         if (!isset($paymentResult['token'])) {
  311.             return $this->redirectError($event$paymentService);
  312.         }
  313.         // Status更新
  314.         $this->entityManager->persist($Order);
  315.         $this->entityManager->flush($Order);
  316.         $iframeURL $paymentResult['nextAction']['redirect_to_url']['url'];
  317.         $this->session->set(PaymentService::PAYMENT_ID$paymentResult['id']);
  318.         $this->session->set(PaymentService::PAYMENT_TOKEN$paymentResult['token']);
  319.         $this->session->set(PaymentService::SECURE_REDIRECT_URL$iframeURL);
  320.         // ■ 3Dセキュア画面
  321.         return new RedirectResponse(
  322.             $this->router->generate('sheeb_dlc42_subscription_payment_3d_secure')
  323.         );
  324.     }
  325.     /**
  326.      * 購入エラーページに遷移
  327.      *
  328.      * @param EventArgs $event
  329.      * @return void
  330.      */
  331.     private function redirectError(EventArgs $eventPaymentService $paymentServicestring $errorMessage '')
  332.     {
  333.         /** @var Order $Order */
  334.         $Order $event->getArgument('Order');
  335.         // 受注ステータス: キャンセル
  336.         $OrderStatus $this->orderStatusRepository->find(OrderStatus::CANCEL);
  337.         $Order->setOrderStatus($OrderStatus);
  338.         // 決済ステータス: 未決済
  339.         $PaymentStatus $this->paymentStatusRepository->find(PaymentStatus::OUTSTANDING);
  340.         $Order->setSheebDlc42SubscriptionPaymentStatus($PaymentStatus);
  341.         $this->entityManager->persist($Order);
  342.         $this->entityManager->flush($Order);
  343.         // 在庫数を元に戻す
  344.         $paymentService->rollbackStock($event->getArgument('Order'), $this->entityManager);
  345.         $this->entityManager->flush();
  346.         // エラーページにアラートメッセージを追加
  347.         if (!empty($errorMessage)) {
  348.             $this->session->getFlashBag()->add('eccube.front.error'$errorMessage);
  349.         }
  350.         // ■ エラー画面
  351.         return new RedirectResponse(
  352.             $this->router->generate('shopping_error')
  353.         );
  354.     }
  355. }