app/Customize/Controller/Mypage/MypageController.php line 140

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  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 Customize\Controller\Mypage;
  13. use Customize\Service\MailService;
  14. use Customize\Service\TaxRuleServiceCustom as TaxRuleService;
  15. use Eccube\Controller\AbstractController;
  16. use Eccube\Entity\BaseInfo;
  17. use Eccube\Entity\Customer;
  18. use Eccube\Entity\Master\OrderStatus;
  19. use Eccube\Entity\Order;
  20. use Eccube\Entity\Product;
  21. use Eccube\Entity\ProductClass;
  22. use Eccube\Event\EccubeEvents;
  23. use Eccube\Event\EventArgs;
  24. use Eccube\Exception\CartException;
  25. use Eccube\Form\Type\Front\CustomerLoginType;
  26. use Eccube\Repository\BaseInfoRepository;
  27. use Eccube\Repository\CustomerFavoriteProductRepository;
  28. use Eccube\Repository\Master\OrderStatusRepository;
  29. use Eccube\Repository\OrderRepository;
  30. use Eccube\Repository\PaymentRepository;
  31. use Eccube\Repository\ProductRepository;
  32. use Customize\Service\CartService;
  33. use Eccube\Service\Payment\PaymentDispatcher;
  34. use Eccube\Service\Payment\PaymentMethodInterface;
  35. use Eccube\Service\PurchaseFlow\PurchaseContext;
  36. use Eccube\Service\PurchaseFlow\PurchaseFlow;
  37. use Knp\Component\Pager\PaginatorInterface;
  38. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  39. use Symfony\Component\DependencyInjection\ContainerInterface;
  40. use Symfony\Component\HttpFoundation\RedirectResponse;
  41. use Symfony\Component\HttpFoundation\Request;
  42. use Symfony\Component\HttpFoundation\Response;
  43. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  44. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  45. use Symfony\Component\Routing\Annotation\Route;
  46. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  47. use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
  48. class MypageController extends AbstractController
  49. {
  50.     /**
  51.      * @var ProductRepository
  52.      */
  53.     protected $productRepository;
  54.     /**
  55.      * @var CustomerFavoriteProductRepository
  56.      */
  57.     protected $customerFavoriteProductRepository;
  58.     /**
  59.      * @var BaseInfo
  60.      */
  61.     protected $BaseInfo;
  62.     /**
  63.      * @var CartService
  64.      */
  65.     protected $cartService;
  66.     /**
  67.      * @var OrderRepository
  68.      */
  69.     protected $orderRepository;
  70.     /**
  71.      * @var PurchaseFlow
  72.      */
  73.     protected $purchaseFlow;
  74.     protected $serviceContainer;
  75.     protected $taxRuleService;
  76.     protected $orderStatusRepository;
  77.     protected $paymentRepository;
  78.     protected $mailService;
  79.     /**
  80.      * MypageController constructor.
  81.      *
  82.      * @param OrderRepository $orderRepository
  83.      * @param CustomerFavoriteProductRepository $customerFavoriteProductRepository
  84.      * @param CartService $cartService
  85.      * @param BaseInfoRepository $baseInfoRepository
  86.      * @param PurchaseFlow $purchaseFlow
  87.      */
  88.     public function __construct(
  89.         OrderRepository $orderRepository,
  90.         CustomerFavoriteProductRepository $customerFavoriteProductRepository,
  91.         CartService $cartService,
  92.         BaseInfoRepository $baseInfoRepository,
  93.         PurchaseFlow $purchaseFlow,
  94.         ContainerInterface $serviceContainer,
  95.         TaxRuleService $taxRuleService,
  96.         OrderStatusRepository $orderStatusRepository,
  97.         PaymentRepository $paymentRepository,
  98.         MailService $mailService
  99.     ) {
  100.         $this->orderRepository $orderRepository;
  101.         $this->customerFavoriteProductRepository $customerFavoriteProductRepository;
  102.         $this->BaseInfo $baseInfoRepository->get();
  103.         $this->cartService $cartService;
  104.         $this->purchaseFlow $purchaseFlow;
  105.         $this->serviceContainer $serviceContainer;
  106.         $this->taxRuleService $taxRuleService;
  107.         $this->orderStatusRepository $orderStatusRepository;
  108.         $this->paymentRepository $paymentRepository;
  109.         $this->mailService $mailService;
  110.     }
  111.     /**
  112.      * ログイン画面.
  113.      *
  114.      * @Route("/mypage/login", name="mypage_login", methods={"GET", "POST"})
  115.      * @Template("Mypage/login.twig")
  116.      */
  117.     public function login(Request $requestAuthenticationUtils $utils)
  118.     {
  119.         if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
  120.             log_info('認証済のためログイン処理をスキップ');
  121.             return $this->redirectToRoute('mypage');
  122.         }
  123.         /* @var $form \Symfony\Component\Form\FormInterface */
  124.         $builder $this->formFactory
  125.             ->createNamedBuilder(''CustomerLoginType::class);
  126.         $builder->get('login_memory')->setData((bool) $request->getSession()->get('_security.login_memory'));
  127.         if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
  128.             $Customer $this->getUser();
  129.             if ($Customer instanceof Customer) {
  130.                 $builder->get('login_email')
  131.                     ->setData($Customer->getEmail());
  132.             }
  133.         }
  134.         $event = new EventArgs(
  135.             [
  136.                 'builder' => $builder,
  137.             ],
  138.             $request
  139.         );
  140.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_LOGIN_INITIALIZE);
  141.         $form $builder->getForm();
  142.         return [
  143.             'error' => $utils->getLastAuthenticationError(),
  144.             'form' => $form->createView(),
  145.         ];
  146.     }
  147.     /**
  148.      * マイページ.
  149.      *
  150.      * @Route("/mypage/", name="mypage", methods={"GET"})
  151.      * @Template("Mypage/index.twig")
  152.      */
  153.     public function index(Request $requestPaginatorInterface $paginator)
  154.     {
  155.         $Customer $this->getUser();
  156.         // 購入処理中/決済処理中ステータスの受注を非表示にする.
  157.         $this->entityManager
  158.             ->getFilters()
  159.             ->enable('incomplete_order_status_hidden');
  160.         // paginator
  161.         $qb $this->orderRepository->getQueryBuilderByCustomer($Customer);
  162.         $event = new EventArgs(
  163.             [
  164.                 'qb' => $qb,
  165.                 'Customer' => $Customer,
  166.             ],
  167.             $request
  168.         );
  169.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_INDEX_SEARCH);
  170.         $pagination $paginator->paginate(
  171.             $qb,
  172.             $request->get('pageno'1),
  173.             $this->eccubeConfig['eccube_search_pmax']
  174.         );
  175.         return [
  176.             'pagination' => $pagination,
  177.         ];
  178.     }
  179.     /**
  180.      * 購入履歴詳細を表示する.
  181.      *
  182.      * @Route("/mypage/history/{order_no}", name="mypage_history", methods={"GET"})
  183.      * @Template("Mypage/history.twig")
  184.      */
  185.     public function history(Request $request$order_no)
  186.     {
  187.         $this->entityManager->getFilters()
  188.             ->enable('incomplete_order_status_hidden');
  189.         $Order $this->orderRepository->findOneBy(
  190.             [
  191.                 'order_no' => $order_no,
  192.                 'Customer' => $this->getUser(),
  193.             ]
  194.         );
  195.         $event = new EventArgs(
  196.             [
  197.                 'Order' => $Order,
  198.             ],
  199.             $request
  200.         );
  201.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_HISTORY_INITIALIZE);
  202.         /** @var Order $Order */
  203.         $Order $event->getArgument('Order');
  204.         if (!$Order) {
  205.             throw new NotFoundHttpException();
  206.         }
  207.         $stockOrder true;
  208.         foreach ($Order->getOrderItems() as $orderItem) {
  209.             if ($orderItem->isProduct() && $orderItem->getQuantity() < 0) {
  210.                 $stockOrder false;
  211.                 break;
  212.             }
  213.         }
  214.         $taxRate $this->taxRuleService->getTaxRate();
  215.         $userRate 0;
  216.         if($this->getUser()) {
  217.             $userRate $this->BaseInfo->getOptionUserRate();
  218.         }
  219.         // Check if can repay and detect payment type
  220.         $canRepay $this->canRepay($Order);
  221.         $paymentType $this->detectPaymentType($Order);
  222.         return [
  223.             'Order' => $Order,
  224.             'stockOrder' => $stockOrder,
  225.             'taxRate' => $taxRate,
  226.             'userRate' => $userRate,
  227.             'canRepay' => $canRepay,
  228.             'paymentType' => $paymentType,
  229.         ];
  230.     }
  231.     /**
  232.      * 再購入を行う.
  233.      *
  234.      * @Route("/mypage/order/{order_no}", name="mypage_order", methods={"PUT"})
  235.      */
  236.     public function order(Request $request$order_no)
  237.     {
  238.         $this->isTokenValid();
  239.         log_info('再注文開始', [$order_no]);
  240.         $Customer $this->getUser();
  241.         /* @var $Order \Eccube\Entity\Order */
  242.         $Order $this->orderRepository->findOneBy(
  243.             [
  244.                 'order_no' => $order_no,
  245.                 'Customer' => $Customer,
  246.             ]
  247.         );
  248.         if($Order->getOrderStatus()->getId() == 10) {
  249.             $this->mailService->sendOrderMail($Order);
  250.             $this->entityManager->flush();
  251.             $OrderStatus $this->orderStatusRepository->find(OrderStatus::NEW);
  252.             $Order->setOrderStatus($OrderStatus);
  253.             $this->entityManager->persist($Order);
  254.             $this->entityManager->flush();
  255.             //set flash message
  256.             $this->addFlash('reorder_success''注文を確定しました');
  257. //            return $this->redirect($this->generateUrl('mypage_history', ['order_no' => $Order->getOrderNo()]));
  258.             return $this->redirectToRoute('mypage');
  259.         }
  260.         $event = new EventArgs(
  261.             [
  262.                 'Order' => $Order,
  263.                 'Customer' => $Customer,
  264.             ],
  265.             $request
  266.         );
  267.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_ORDER_INITIALIZE);
  268.         if (!$Order) {
  269.             log_info('対象の注文が見つかりません', [$order_no]);
  270.             throw new NotFoundHttpException();
  271.         }
  272.         $remessageProduct false;
  273.         // エラーメッセージの配列
  274.         $errorMessages = [];
  275.         try {
  276.             foreach ($Order->getOrderItems() as $OrderItem) {
  277.                 if ($OrderItem->getProduct() && $OrderItem->getProductClass()) {
  278.                     $ProductClass $this->entityManager
  279.                         ->getRepository(ProductClass::class)
  280.                         ->find($OrderItem->getProductClass()->getId());
  281.                     if($OrderItem->getProduct()->isEnable() == false || ($OrderItem->getIsPrint() != NULL && $OrderItem->getIsPrint() != && $OrderItem->getProduct()->getProductPrintType() != 1) || ($OrderItem->getIsPrint() != NULL && $OrderItem->getIsPrint() == && $OrderItem->getProduct()->getProductPrintType() == 1) || strpos($OrderItem->getProduct()->getProductPrintColor(), $OrderItem->getProductPrintColor()) === false || (strpos($OrderItem->getProduct()->getProductPrintColor(), $OrderItem->getProductPrintColor()) !== false && $ProductClass->isVisible() == false)) {
  282.                         $remessageProduct true;
  283.                         continue;
  284.                     }
  285.                     $this->cartService->addProduct($OrderItem->getProductClass(), $OrderItem->getQuantity(), [
  286.                         'product_print_type' => $OrderItem->getProductPrintType(),
  287.                         'product_print_fee_type' => $OrderItem->getProductPrintFeeType(),
  288.                         'product_print_fee_price' => $OrderItem->getProductPrintFeePrice(),
  289.                         'product_plate_type_print' => $OrderItem->getProductPlateTypePrint(),
  290.                         'product_print_color' => $OrderItem->getProductPrintColor(),
  291.                         'is_print' => ($OrderItem->getProduct()->getProductPrintType() == 1) ? $OrderItem->getIsPrint(),
  292.                         'is_repurchase' => $OrderItem->getIsRepurchase(),
  293.                         'product_code_by_color' => $OrderItem->getProductCodeByColor()
  294.                     ]);
  295.                     // 明細の正規化
  296.                     $Carts $this->cartService->getCarts();
  297.                     foreach ($Carts as $Cart) {
  298.                         $result $this->purchaseFlow->validate($Cart, new PurchaseContext($Cart$this->getUser()));
  299.                         // 復旧不可のエラーが発生した場合は追加した明細を削除.
  300.                         if ($result->hasError()) {
  301.                             $this->cartService->removeProduct($OrderItem->getProductClass());
  302.                             foreach ($result->getErrors() as $error) {
  303.                                 $errorMessages[] = $error->getMessage();
  304.                             }
  305.                         }
  306.                         foreach ($result->getWarning() as $warning) {
  307.                             $errorMessages[] = $warning->getMessage();
  308.                         }
  309.                     }
  310.                 }
  311.             }
  312.             $this->cartService->save();
  313.         } catch (CartException $e) {
  314.             log_info($e->getMessage(), [$order_no]);
  315.             $this->addRequestError($e->getMessage());
  316.         }
  317.         foreach ($errorMessages as $errorMessage) {
  318.             $this->addRequestError($errorMessage);
  319.         }
  320.         $event = new EventArgs(
  321.             [
  322.                 'Order' => $Order,
  323.                 'Customer' => $Customer,
  324.             ],
  325.             $request
  326.         );
  327.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_ORDER_COMPLETE);
  328.         if ($event->getResponse() !== null) {
  329.             return $event->getResponse();
  330.         }
  331.         log_info('再注文完了', [$order_no]);
  332.         if($remessageProduct == true) {
  333.             return $this->redirect($this->generateUrl('cart', ['no_product' => $remessageProduct]));
  334.         } else {
  335.             return $this->redirect($this->generateUrl('cart'));
  336.         }
  337.     }
  338.     /**
  339.      * 再度のお支払い(未決済の受注).
  340.      *
  341.      * @Route("/mypage/history/{order_no}/repayment", name="mypage_history_repayment", methods={"POST"})
  342.      */
  343.     public function repayment(Request $request$order_no)
  344.     {
  345.         $this->isTokenValid();
  346.         log_info('再支払い開始', [$order_no]);
  347.         $Customer $this->getUser();
  348.         /* @var $Order \Eccube\Entity\Order */
  349.         $Order $this->orderRepository->findOneBy(
  350.             [
  351.                 'order_no' => $order_no,
  352.                 'Customer' => $Customer,
  353.             ]
  354.         );
  355.         if (!$Order) {
  356.             throw new NotFoundHttpException();
  357.         }
  358.         // Check if payment is allowed
  359.         if (!$this->canRepay($Order)) {
  360.             $this->addError('このご注文は再度のお支払いができません。');
  361.             return $this->redirectToRoute('mypage_history', ['order_no' => $order_no]);
  362.         }
  363.         $methodClass $this->getPaymentMethodClass($Order);
  364.         if (!$methodClass) {
  365.             $this->addError('お支払い方法情報が見つかりません。');
  366.             return $this->redirectToRoute('mypage_history', ['order_no' => $order_no]);
  367.         }
  368.         $methodClass ltrim($methodClass'\\');
  369.         if (!$this->serviceContainer->has($methodClass)) {
  370.             log_info('再支払い対象の決済サービスが見つかりません', [$order_no$methodClass]);
  371.             $this->addError('このお支払い方法は再決済に対応していません。');
  372.             return $this->redirectToRoute('mypage_history', ['order_no' => $order_no]);
  373.         }
  374.         $PaymentMethod $this->serviceContainer->get($methodClass);
  375.         if (!$PaymentMethod instanceof PaymentMethodInterface) {
  376.             log_info('決済サービスがPaymentMethodInterfaceではありません', [$order_no$methodClass]);
  377.             $this->addError('このお支払い方法は再決済に対応していません。');
  378.             return $this->redirectToRoute('mypage_history', ['order_no' => $order_no]);
  379.         }
  380.         $PaymentMethod->setOrder($Order);
  381.         // Restore the session context that payment plugins use to look up the Order.
  382.         // apply() was already called when the order was originally created, so we do NOT
  383.         // call it again. We just restore the necessary session state and redirect directly
  384.         // to the payment entry page.
  385.         if ($Order->getPreOrderId()) {
  386.             $this->cartService->setPreOrderId($Order->getPreOrderId())->save();
  387.         }
  388.         $request->getSession()->set('eccube.front.shopping.order.id'$Order->getId());
  389.         // ShoppingController と同じ順序: apply() -> checkout()
  390.         if ($response $this->executeRepaymentApply($PaymentMethod)) {
  391.             log_info('再支払い: PaymentMethod::apply で遷移', [$order_no$methodClass]);
  392.             return $response;
  393.         }
  394.         // Migrateでmethod_classがCash固定になっているケースの救済.
  395.         // まずは現在のmethod_classでShopping同等の処理を試し、
  396.         // 反応がない場合のみ同名/同種別の候補クラスでapply()を再試行する。
  397.         if ($methodClass === 'Eccube\\Service\\Payment\\Method\\Cash') {
  398.             foreach ($this->resolveRepaymentFallbackMethodClasses($Order) as $fallbackClass) {
  399.                 if (!$this->serviceContainer->has($fallbackClass)) {
  400.                     continue;
  401.                 }
  402.                 $FallbackPaymentMethod $this->serviceContainer->get($fallbackClass);
  403.                 if (!$FallbackPaymentMethod instanceof PaymentMethodInterface) {
  404.                     continue;
  405.                 }
  406.                 $FallbackPaymentMethod->setOrder($Order);
  407.                 if ($response $this->executeRepaymentApply($FallbackPaymentMethod)) {
  408.                     log_info('再支払い: fallback apply で遷移', [$order_no$fallbackClass]);
  409.                     return $response;
  410.                 }
  411.             }
  412.         }
  413.         if ($response $this->executeRepaymentCheckout($PaymentMethod$order_no)) {
  414.             log_info('再支払い: PaymentMethod::checkout で遷移', [$order_no$methodClass]);
  415.             return $response;
  416.         }
  417.         $this->entityManager->flush();
  418.         $this->addError('このお支払い方法は再決済に対応していません。');
  419.         return $this->redirectToRoute('mypage_history', ['order_no' => $order_no]);
  420.     }
  421.     /**
  422.      * Resolve fallback payment method classes when migrated data keeps Cash class.
  423.      */
  424.     private function resolveRepaymentFallbackMethodClasses(Order $Order): array
  425.     {
  426.         $paymentMethodName = (string) $Order->getPaymentMethod();
  427.         $paymentType $this->detectPaymentType($Order);
  428.         $candidates = [];
  429.         foreach ($this->paymentRepository->findBy(['visible' => true]) as $Payment) {
  430.             $candidateClass = (string) $Payment->getMethodClass();
  431.             $candidateName = (string) $Payment->getMethod();
  432.             if (!$candidateClass || $candidateClass === 'Eccube\\Service\\Payment\\Method\\Cash') {
  433.                 continue;
  434.             }
  435.             if ($candidateName === $paymentMethodName) {
  436.                 $candidates[] = ltrim($candidateClass'\\');
  437.                 continue;
  438.             }
  439.             if ($paymentType === 'Credit Card' && (strpos($candidateClass'Credit') !== false || strpos($candidateClass'Card') !== false)) {
  440.                 $candidates[] = ltrim($candidateClass'\\');
  441.             }
  442.         }
  443.         return array_values(array_unique($candidates));
  444.     }
  445.     /**
  446.      * Check if order can be repaid.
  447.      *
  448.      * @param Order $Order
  449.      * @return bool
  450.      */
  451.     private function canRepay(Order $Order): bool
  452.     {
  453.         // Only allow repayment if:
  454.         // 1. Order status is NEW, PENDING, or PROCESSING
  455.         // 2. Payment date is null (not yet paid)
  456.         // 3. Payment is not already completed
  457.         // Disallowed statuses: PAID, DELIVERED, CANCEL, RETURNED
  458.         $disallowedStatuses = [
  459.             OrderStatus::PAID,           // 6
  460.             OrderStatus::DELIVERED,      // 8
  461.             OrderStatus::CANCEL,         // 1
  462.             OrderStatus::RETURNED,       // 9
  463.         ];
  464.         if (in_array($Order->getOrderStatus()->getId(), $disallowedStatuses)) {
  465.             return false;
  466.         }
  467.         // If payment_date is not null, already paid
  468.         if ($Order->getPaymentDate() !== null) {
  469.             return false;
  470.         }
  471.         return true;
  472.     }
  473.     /**
  474.      * Get payment method class for detecting payment type.
  475.      *
  476.      * @param Order $Order
  477.      * @return string|null
  478.      */
  479.     private function getPaymentMethodClass(Order $Order): ?string
  480.     {
  481.         if ($Order->getPayment() && $Order->getPayment()->getMethodClass()) {
  482.             return $Order->getPayment()->getMethodClass();
  483.         }
  484.         return null;
  485.     }
  486.     /**
  487.      * PaymentMethod::apply を実行する. (ShoppingController 相当)
  488.      */
  489.     private function executeRepaymentApply(PaymentMethodInterface $paymentMethod): ?Response
  490.     {
  491.         $dispatcher $paymentMethod->apply();
  492.         if (!$dispatcher instanceof PaymentDispatcher) {
  493.             return null;
  494.         }
  495.         return $this->buildRepaymentResponseFromDispatcher($dispatcher);
  496.     }
  497.     /**
  498.      * PaymentMethod::checkout を実行する. (ShoppingController 相当)
  499.      */
  500.     private function executeRepaymentCheckout(PaymentMethodInterface $paymentMethodstring $orderNo): ?Response
  501.     {
  502.         $PaymentResult $paymentMethod->checkout();
  503.         $response $PaymentResult->getResponse();
  504.         if ($response instanceof Response && ($response->isRedirection() || $response->isSuccessful())) {
  505.             $this->entityManager->flush();
  506.             return $response;
  507.         }
  508.         if (!$PaymentResult->isSuccess()) {
  509.             foreach ($PaymentResult->getErrors() as $error) {
  510.                 $this->addError($error);
  511.             }
  512.             return $this->redirectToRoute('mypage_history', ['order_no' => $orderNo]);
  513.         }
  514.         $this->addFlash('success''お支払い処理が完了しました。');
  515.         return $this->redirectToRoute('mypage_history', ['order_no' => $orderNo]);
  516.     }
  517.     /**
  518.      * PaymentDispatcher を Response に変換する. (ShoppingController 相当)
  519.      */
  520.     private function buildRepaymentResponseFromDispatcher(PaymentDispatcher $dispatcher): ?Response
  521.     {
  522.         $response $dispatcher->getResponse();
  523.         // Plugin が相対パスを返す場合、mypage 配下では壊れるため route 判定して正規化する.
  524.         if ($response instanceof RedirectResponse) {
  525.             $targetUrl $response->getTargetUrl();
  526.             try {
  527.                 $this->generateUrl($targetUrl);
  528.                 return $this->redirectToRoute($targetUrl);
  529.             } catch (RouteNotFoundException $e) {
  530.                 // keep original redirect response when target is already URL
  531.             }
  532.         }
  533.         if ($response instanceof Response && ($response->isRedirection() || $response->isSuccessful())) {
  534.             return $response;
  535.         }
  536.         if ($dispatcher->isForward()) {
  537.             return $this->forwardToRoute(
  538.                 $dispatcher->getRoute(),
  539.                 $dispatcher->getPathParameters(),
  540.                 $dispatcher->getQueryParameters()
  541.             );
  542.         }
  543.         return $this->redirectToRoute(
  544.             $dispatcher->getRoute(),
  545.             array_merge($dispatcher->getPathParameters(), $dispatcher->getQueryParameters())
  546.         );
  547.     }
  548.     /**
  549.      * Detect payment method type from Payment entity.
  550.      *
  551.      * @param Order $Order
  552.      * @return string Credit Card, Bank Transfer, Other
  553.      */
  554.     public function detectPaymentType(Order $Order): string
  555.     {
  556.         $methodClass $this->getPaymentMethodClass($Order);
  557.         $paymentMethodName = (string) $Order->getPaymentMethod();
  558.         if (!$methodClass) {
  559.             if (
  560.                 mb_stripos($paymentMethodName'クレジット') !== false
  561.                 || stripos($paymentMethodName'credit') !== false
  562.                 || mb_stripos($paymentMethodName'thẻ') !== false
  563.             ) {
  564.                 return 'Credit Card';
  565.             }
  566.             if (
  567.                 mb_stripos($paymentMethodName'QR') !== false
  568.                 || stripos($paymentMethodName'paypay') !== false
  569.                 || stripos($paymentMethodName'paydesign') !== false
  570.             ) {
  571.                 return 'QR Code';
  572.             }
  573.             if (
  574.                 mb_stripos($paymentMethodName'銀行振込') !== false
  575.                 || mb_stripos($paymentMethodName'chuyển khoản') !== false
  576.                 || stripos($paymentMethodName'bank transfer') !== false
  577.             ) {
  578.                 return 'Bank Transfer';
  579.             }
  580.             return 'Other';
  581.         }
  582.         $normalizedMethodClass ltrim($methodClass'\\');
  583.         // Map payment method classes to types
  584.         $paymentMethods = [
  585.             'Plugin\SlnPayment42\Service\Method\CreditCard' => 'Credit Card',
  586.             'Plugin\SlnPayment42\Service\Method\RegisteredCreditCard' => 'Credit Card',
  587.             'Plugin\paypay4\Service\Method\PayPay' => 'QR Code',
  588.             'Plugin\paydesign4\Service\Method\PayDesign' => 'QR Code',
  589.             'Plugin\adnet4\Service\Method\PayDesign' => 'QR Code',
  590.         ];
  591.         foreach ($paymentMethods as $class => $type) {
  592.             if ($normalizedMethodClass === $class || strpos($normalizedMethodClass$class) !== false) {
  593.                 return $type;
  594.             }
  595.         }
  596.         // Default detection by class name
  597.         if (strpos($methodClass'Credit') !== false || strpos($methodClass'Card') !== false) {
  598.             return 'Credit Card';
  599.         }
  600.         if (strpos($methodClass'PayPay') !== false || strpos($methodClass'QR') !== false) {
  601.             return 'QR Code';
  602.         }
  603.         if (
  604.             mb_stripos($paymentMethodName'銀行振込') !== false
  605.             || mb_stripos($paymentMethodName'chuyển khoản') !== false
  606.             || stripos($paymentMethodName'bank transfer') !== false
  607.         ) {
  608.             return 'Bank Transfer';
  609.         }
  610.         return 'Other';
  611.     }
  612.     /**
  613.      * お気に入り商品を表示する.
  614.      *
  615.      * @Route("/mypage/favorite", name="mypage_favorite", methods={"GET"})
  616.      * @Template("Mypage/favorite.twig")
  617.      */
  618.     public function favorite(Request $requestPaginatorInterface $paginator)
  619.     {
  620.         if (!$this->BaseInfo->isOptionFavoriteProduct()) {
  621.             throw new NotFoundHttpException();
  622.         }
  623.         $Customer $this->getUser();
  624.         // paginator
  625.         $qb $this->customerFavoriteProductRepository->getQueryBuilderByCustomer($Customer);
  626.         $event = new EventArgs(
  627.             [
  628.                 'qb' => $qb,
  629.                 'Customer' => $Customer,
  630.             ],
  631.             $request
  632.         );
  633.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_FAVORITE_SEARCH);
  634.         $pagination $paginator->paginate(
  635.             $qb,
  636.             $request->get('pageno'1),
  637.             $this->eccubeConfig['eccube_search_pmax'],
  638.             ['wrap-queries' => true]
  639.         );
  640.         return [
  641.             'pagination' => $pagination,
  642.         ];
  643.     }
  644.     /**
  645.      * お気に入り商品を削除する.
  646.      *
  647.      * @Route("/mypage/favorite/{id}/delete", name="mypage_favorite_delete", methods={"DELETE"}, requirements={"id" = "\d+"})
  648.      */
  649.     public function delete(Request $requestProduct $Product)
  650.     {
  651.         $this->isTokenValid();
  652.         $Customer $this->getUser();
  653.         log_info('お気に入り商品削除開始', [$Customer->getId(), $Product->getId()]);
  654.         $CustomerFavoriteProduct $this->customerFavoriteProductRepository->findOneBy(['Customer' => $Customer'Product' => $Product]);
  655.         if ($CustomerFavoriteProduct) {
  656.             $this->customerFavoriteProductRepository->delete($CustomerFavoriteProduct);
  657.         } else {
  658.             throw new BadRequestHttpException();
  659.         }
  660.         $event = new EventArgs(
  661.             [
  662.                 'Customer' => $Customer,
  663.                 'CustomerFavoriteProduct' => $CustomerFavoriteProduct,
  664.             ], $request
  665.         );
  666.         $this->eventDispatcher->dispatch($eventEccubeEvents::FRONT_MYPAGE_MYPAGE_DELETE_COMPLETE);
  667.         log_info('お気に入り商品削除完了', [$Customer->getId(), $CustomerFavoriteProduct->getId()]);
  668.         return $this->redirect($this->generateUrl('mypage_favorite'));
  669.     }
  670. }