<?php
/*
役割:最終チェック・進行可否判定
不定貫あり → 与信のみ
不定貫なし → 通常決済
*/
namespace Plugin\SDream\EventSubscriber;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Eccube\Event\ShoppingFlowEvent;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Plugin\SDream\Service\CustomerGuard;
use Plugin\SDream\Service\ProductPurchaseRuleChecker;
use Plugin\SDream\Service\PurchaseHistoryLimiter;
use Plugin\SDream\Service\PaymentRestrictionService;
use Psr\Log\LoggerInterface;
use Symfony\Component\Security\Core\Security;
use Eccube\Entity\Customer;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Eccube\Service\OrderHelper;
use Doctrine\ORM\EntityManagerInterface;
class ShoppingFlowSubscriber implements EventSubscriberInterface
{
private Security $security;
private OrderHelper $orderHelper;
private CustomerGuard $customerGuard;
private ProductPurchaseRuleChecker $productPurchaseRuleChecker;
private PurchaseHistoryLimiter $purchaseHistoryLimiter;
private PaymentRestrictionService $paymentRestrictionService;
private FlashBagInterface $flashBag;
private RouterInterface $router;
private LoggerInterface $logger;
private EntityManagerInterface $em;
public function __construct(
Security $security,
OrderHelper $orderHelper,
CustomerGuard $customerGuard,
ProductPurchaseRuleChecker $productPurchaseRuleChecker,
PurchaseHistoryLimiter $purchaseHistoryLimiter,
PaymentRestrictionService $paymentRestrictionService,
FlashBagInterface $flashBag,
LoggerInterface $logger,
RouterInterface $router,
EntityManagerInterface $em
) {
$this->security = $security;
$this->orderHelper = $orderHelper;
$this->customerGuard = $customerGuard;
$this->productPurchaseRuleChecker = $productPurchaseRuleChecker;
$this->purchaseHistoryLimiter = $purchaseHistoryLimiter;
$this->paymentRestrictionService = $paymentRestrictionService;
$this->flashBag = $flashBag;
$this->logger = $logger;
$this->router = $router;
$this->em = $em;
}
private function getCurrentCustomer(): ?Customer
{
$user = $this->security->getUser();
return $user instanceof Customer ? $user : null;
}
public static function getSubscribedEvents(): array
{
return [
EccubeEvents::FRONT_SHOPPING_INDEX_INITIALIZE => 'onConIndexInitialilze',
EccubeEvents::FRONT_SHOPPING_CONFIRM_INITIALIZE => 'onConfirmInitialilze',
EccubeEvents::FRONT_SHOPPING_CONFIRM_PROCESSING => 'onConfirmProcessing',
EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE => 'onCompleteInit',
];
}
public function onConIndexInitialilze(EventArgs $event): void
{
//デバッグ用 26.02.16時点イベント反応なし(ここは通らない 他のプラグインなどカスタマイズも影響している)
$this->logger->info('onConIndexInitialilze called (test log)FRONT_SHOPPING_INDEX_INITIALIZE');
}
public function onConfirmInitialilze(EventArgs $event): void
{
//デバッグ用 26.02.16時点イベント反応なし(ここは通らない 他のプラグインなどカスタマイズも影響している)
$this->logger->info('onConfirmInitialilze called (test log)FRONT_SHOPPING_CONFIRM_INITIALIZE');
}
public function onCompleteInit(EventArgs $event): void
{
//デバッグ用
//$this->logger->info('route name: '.$event->getRequest()->attributes->get('_route'));
//$this->logger->info('onCompleteInit called (test log)FRONT_SHOPPING_COMPLETE_INITIALIZE');
$Order = $event->getArgument('Order');
if (!$Order) {
return;
}
//確認用
/*
$this->logger->info('[ShoppingFlow] payment validate start', [
'order_id' => $Order->getId(),
'payment' => $Order->getPayment()
? $Order->getPayment()->getMethodClass()
: null,
]);
*/
//チェックする
$errors = [];
//ログインユーザー
$Customer =
$this->getCurrentCustomer() ?: $this->orderHelper->getNonMember();
//1.会員状態・会員区分チェック
try {
$this->customerGuard->assertPurchasable($Customer);
} catch (AccessDeniedException $e) {
$this->logger->warning('[PurchaseBlock] customerGuard failed', [
'customer_id' => $Customer ? $Customer->getId() : null,
'message' => $e->getMessage(),
]);
$errors[] = $e->getMessage();
}
/*
* 2.決済許可確認
* 不定貫 + ペイド決済 NG
* 3.商品 × 会員 × 決済制御
* 4.再購入制限
*/
$errors = array_merge(
$errors,
$this->paymentRestrictionService->assertPaymentAllowed($Order),
$this->productPurchaseRuleChecker->assertPurchasable($Order),
$this->purchaseHistoryLimiter->assertNotLimited($Order)
);
if (!empty($errors)) {
$this->logger->info('[PurchaseBlock] order blocked before complete', [
'order_id' => $Order->getId(),
'customer_id' => $Customer ? $Customer->getId() : null,
'errors' => $errors,
]);
foreach ($errors as $message) {
$this->flashBag->add(
'eccube.front.cart.error',
$message
);
}
// 先に「進ませない」
$event->setResponse(
new RedirectResponse($this->router->generate('shopping'))
);
return;
}
/*
ここから「正常注文のみ」の処理
★★★余計な変更をしないように注意★★★
★★★決済前なので不定貫属性以外は変えない!★★★
★★★Paymentを変更したりしない★★★
*/
// 不定貫判定
$hasVariable = false;
foreach ($Order->getOrderItems() as $item) {
$pc = $item->getProductClass();
if (!$pc) {
continue;
}
$product = method_exists($pc, 'getProduct') ? $pc->getProduct() : null;
if (!$product) {
continue;
}
// ProductTrait のプロパティ firmvariableattribute を getter で持っている前提
if (method_exists($product, 'getFirmvariableattribute')) {
$flag = (int) $product->getFirmvariableattribute(); // 1なら不定貫
if ($flag === 1) {
$hasVariable = true;
break;
}
}
}
$Order->setHasVariableWeight($hasVariable);
if ($hasVariable) {
$Order->setVariableWeightStatus('unprocessed'); // 仮で直接文字列
}
//不定貫であれば売価変更時にもともとの売価も保持する(再計算用)
if ($hasVariable) {
foreach ($Order->getOrderItems() as $item) {
// 商品明細のみ
if (!$item->isProduct()) {
continue;
}
// まだ保存されていない場合のみ
if ($item->getMotoPrice() === null) {
$item->setMotoPrice($item->getPrice());
}
}
}
$this->em->flush();
$this->logger->info('[VariableWeight] flag initialized', [
'order_id' => $Order->getId(),
'has_variable_weight' => $hasVariable,
]);
}
/**
* ここは通らない
* 決済確定前の最終チェック
*/
public function onConfirmProcessing(EventArgs $event): void
{
//デバッグ用 26.02.16時点イベント反応なし(ここは通らない 他のプラグインなどカスタマイズも影響している)
$this->logger->info('route name: '.$event->getRequest()->attributes->get('_route'));
$this->logger->info('onConfirmProcessing called (test log)FRONT_SHOPPING_CONFIRM_PROCESSING');
}
}