<?php
namespace Plugin\NZMailSystem42;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Eccube\Common\EccubeConfig;
use Eccube\Repository\PaymentRepository;
use Eccube\Service\MailService;
use Eccube\Repository\OrderRepository;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\MailHistoryRepository;
use Eccube\Entity\Order;
use Eccube\Entity\MailHistory;
use Eccube\Entity\Master\OrderStatus;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Twig\Environment;
class Event implements EventSubscriberInterface
{
/**
* @var MailService
*/
protected $mailService;
/**
* @var OrderRepository
*/
protected $orderRepository;
/**
* @var BaseInfoRepository
*/
protected $baseInfoRepository;
/**
* @var MailHistoryRepository
*/
protected $mailHistoryRepository;
/**
* @var PaymentRepository
*/
protected $paymentRepository;
/**
* @var EntityManagerInterface
*/
protected $entityManager;
/**
* @var Environment
*/
protected $twig;
/**
* @var EccubeConfig
*/
protected $eccubeConfig;
public function __construct(
MailService $mailService,
OrderRepository $orderRepository,
BaseInfoRepository $baseInfoRepository,
MailHistoryRepository $mailHistoryRepository,
PaymentRepository $paymentRepository,
EntityManagerInterface $entityManager,
Environment $twig,
EccubeConfig $eccubeConfig
) {
$this->mailService = $mailService;
$this->orderRepository = $orderRepository;
$this->baseInfoRepository = $baseInfoRepository;
$this->mailHistoryRepository = $mailHistoryRepository;
$this->paymentRepository = $paymentRepository;
$this->entityManager = $entityManager;
$this->twig = $twig;
$this->eccubeConfig = $eccubeConfig;
}
/**
* @return array
*/
public static function getSubscribedEvents()
{
return [
// 注文完了時のイベント(購入完了画面表示前)
EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE => 'onShoppingComplete',
// 注文確定後のイベント
'eccube.event.order.complete' => 'onOrderComplete',
// 管理画面の注文編集画面拡張
'@admin/Order/edit.twig' => 'onAdminOrderEditTwig',
// 手動送信用のカスタムイベント
'nz.mail.payment.confirm' => 'sendPaymentConfirmMail',
'nz.mail.payment.received' => 'sendPaymentReceivedMail',
'nz.mail.order.cancel' => 'sendCancellationMail',
];
}
/**
* 注文完了時の自動メール送信処理
* 銀行振込・代金引換以外の決済方法の場合に自動送信
*/
public function onShoppingComplete(EventArgs $event)
{
$Order = $event->getArgument('Order');
if (!$Order instanceof Order) {
return;
}
// 決済方法をチェック
if ($this->shouldSendAutoMail($Order)) {
// プラグイン設定を確認
$Config = $this->getPluginConfig();
if (!$Config || !$Config->isAutoSendEnabled()) {
return;
}
// 決済完了メールを送信
$this->sendPaymentCompleteMail($Order);
}
}
/**
* 注文確定後の処理(別のタイミングでも送信可能にするため)
*/
public function onOrderComplete(EventArgs $event)
{
if (!$event->hasArgument('Order')) {
return;
}
$Order = $event->getArgument('Order');
if (!$Order instanceof Order) {
return;
}
// 決済方法をチェックして必要なら送信
if ($this->shouldSendAutoMail($Order)) {
$Config = $this->getPluginConfig();
if ($Config && $Config->isAutoSendEnabled()) {
// すでに送信済みかチェック
if (!$this->isAlreadySent($Order, 'payment_complete')) {
$this->sendPaymentCompleteMail($Order);
}
}
}
}
/**
* 自動メール送信が必要かどうかを判定
*/
private function shouldSendAutoMail(Order $Order)
{
$Payment = $Order->getPayment();
if (!$Payment) {
return false;
}
$paymentMethod = $Payment->getMethod();
// 銀行振込、代金引換以外の場合はtrue
// ※決済方法名は環境により異なる可能性があるため、適宜調整してください
$excludedPayments = [
'銀行振込',
'銀行振込み',
'bank_transfer',
'代金引換',
'代引き',
'cod',
'cash_on_delivery'
];
foreach ($excludedPayments as $excluded) {
if (stripos($paymentMethod, $excluded) !== false) {
return false;
}
}
// クレジットカード、コンビニ決済、電子マネーなどの場合はtrue
return true;
}
/**
* 決済完了メール送信(クレジット等の即時決済用)
*/
public function sendPaymentCompleteMail(Order $Order)
{
$Customer = $Order->getCustomer();
if (!$Customer) {
return false;
}
try {
$body = $this->renderMailTemplate('payment_complete', [
'Order' => $Order,
'Customer' => $Customer,
'BaseInfo' => $this->baseInfoRepository->get()
]);
$message = (new \Swift_Message())
->setSubject($this->getMailSubject('payment_complete', $Order))
->setFrom($this->getFromAddress())
->setTo([$Customer->getEmail()])
->setBcc($this->getBccAddress())
->setBody($body);
$this->mailService->sendMail($message);
// 送信履歴を保存
$this->saveMailHistory($Order, 'payment_complete', $body);
// ログに記録
log_info('NZMailSystem42: 決済完了メール送信', [
'order_id' => $Order->getId(),
'order_no' => $Order->getOrderNo(),
'payment' => $Order->getPayment()->getMethod()
]);
return true;
} catch (\Exception $e) {
log_error('NZMailSystem42: メール送信エラー', [$e->getMessage()]);
return false;
}
}
/**
* 決済確認メール送信(銀行振込等の手動確認用)
*/
public function sendPaymentConfirmMail($Order)
{
if ($Order instanceof EventArgs) {
$Order = $Order->getArgument('Order');
}
$Customer = $Order->getCustomer();
if (!$Customer) {
return false;
}
try {
$body = $this->renderMailTemplate('payment_confirm', [
'Order' => $Order,
'Customer' => $Customer,
'BaseInfo' => $this->baseInfoRepository->get()
]);
$message = (new \Swift_Message())
->setSubject($this->getMailSubject('payment_confirm', $Order))
->setFrom($this->getFromAddress())
->setTo([$Customer->getEmail()])
->setBcc($this->getBccAddress())
->setBody($body);
$this->mailService->sendMail($message);
// 送信履歴を保存
$this->saveMailHistory($Order, 'payment_confirm', $body);
return true;
} catch (\Exception $e) {
log_error('NZMailSystem42: メール送信エラー', [$e->getMessage()]);
return false;
}
}
/**
* 入金確認済みメール送信
*/
public function sendPaymentReceivedMail($Order)
{
if ($Order instanceof EventArgs) {
$Order = $Order->getArgument('Order');
}
$Customer = $Order->getCustomer();
if (!$Customer) {
return false;
}
try {
$body = $this->renderMailTemplate('payment_received', [
'Order' => $Order,
'Customer' => $Customer,
'BaseInfo' => $this->baseInfoRepository->get()
]);
$message = (new \Swift_Message())
->setSubject($this->getMailSubject('payment_received', $Order))
->setFrom($this->getFromAddress())
->setTo([$Customer->getEmail()])
->setBcc($this->getBccAddress())
->setBody($body);
$this->mailService->sendMail($message);
// 送信履歴を保存
$this->saveMailHistory($Order, 'payment_received', $body);
return true;
} catch (\Exception $e) {
log_error('NZMailSystem42: メール送信エラー', [$e->getMessage()]);
return false;
}
}
/**
* キャンセルメール送信
*/
public function sendCancellationMail($Order)
{
if ($Order instanceof EventArgs) {
$Order = $Order->getArgument('Order');
}
$Customer = $Order->getCustomer();
if (!$Customer) {
return false;
}
try {
$body = $this->renderMailTemplate('order_cancel', [
'Order' => $Order,
'Customer' => $Customer,
'BaseInfo' => $this->baseInfoRepository->get()
]);
$message = (new \Swift_Message())
->setSubject($this->getMailSubject('order_cancel', $Order))
->setFrom($this->getFromAddress())
->setTo([$Customer->getEmail()])
->setBcc($this->getBccAddress())
->setBody($body);
$this->mailService->sendMail($message);
// 送信履歴を保存
$this->saveMailHistory($Order, 'order_cancel', $body);
return true;
} catch (\Exception $e) {
log_error('NZMailSystem42: メール送信エラー', [$e->getMessage()]);
return false;
}
}
/**
* 管理画面の注文編集画面にボタンを追加
*/
public function onAdminOrderEditTwig(EventArgs $event)
{
$twig = '@NZMailSystem42/admin/order_extension.twig';
$event->addSnippet($twig);
}
/**
* メールテンプレートのレンダリング
*/
private function renderMailTemplate($type, $params)
{
$template = '@NZMailSystem42/mail/' . $type . '.twig';
// テンプレートが存在しない場合はデフォルトテキストを生成
try {
return $this->twig->render($template, $params);
} catch (\Exception $e) {
return $this->createDefaultMailBody($type, $params);
}
}
/**
* デフォルトのメール本文生成
*/
private function createDefaultMailBody($type, $params)
{
$Order = $params['Order'];
$Customer = $params['Customer'];
$BaseInfo = $params['BaseInfo'];
$body = "※このメールは自動配信メールです。\n\n";
$body .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
switch ($type) {
case 'payment_complete':
$body .= " ご注文ありがとうございます\n";
break;
case 'payment_confirm':
$body .= " ご入金確認のお知らせ\n";
break;
case 'payment_received':
$body .= " ご入金完了のお知らせ\n";
break;
case 'order_cancel':
$body .= " ご注文キャンセルのお知らせ\n";
break;
}
$body .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
$body .= $Customer->getName01() . " " . $Customer->getName02() . " 様\n\n";
$body .= "この度は" . $BaseInfo->getShopName() . "をご利用いただき、誠にありがとうございます。\n\n";
switch ($type) {
case 'payment_complete':
$body .= "ご注文および決済が完了いたしました。\n";
$body .= "商品の発送準備を進めさせていただきます。\n\n";
break;
case 'payment_confirm':
$body .= "お客様のご注文の決済処理を確認中です。\n";
$body .= "入金確認が完了次第、商品の発送準備に入らせていただきます。\n\n";
break;
case 'payment_received':
$body .= "お客様のご入金を確認いたしました。\n";
$body .= "商品の発送準備を進めさせていただきます。\n\n";
break;
case 'order_cancel':
$body .= "お客様のご注文をキャンセルいたしました。\n";
$body .= "またのご利用を心よりお待ちしております。\n\n";
// 返金に関する注意事項を追加
$paymentMethod = $Order->getPayment()->getMethod();
if (preg_match('/(クレジット|credit|card)/i', $paymentMethod)) {
$body .= "【クレジットカード決済の場合】\n";
$body .= "決済のキャンセル処理を行っております。\n";
$body .= "カード会社によって返金のタイミングが異なりますので、\n";
$body .= "詳細はご利用のカード会社にお問い合わせください。\n\n";
} elseif (preg_match('/(銀行振込|振込|bank)/i', $paymentMethod)) {
$body .= "【銀行振込の返金について】\n";
$body .= "すでにお振込みいただいている場合は、返金手続きをさせていただきます。\n";
$body .= "返金先口座情報をお知らせください。\n\n";
$body .= "<重要>返金に関するご案内\n";
$body .= "・お客様都合によるキャンセルの場合、返金額は銀行所定の振込手数料を\n";
$body .= " 差し引かせていただいた金額となります。\n";
$body .= "・お支払い金額と返金金額に差異がある場合は、上記の処理が行われて\n";
$body .= " いることをご了承ください。\n";
$body .= "・振込手数料は各金融機関により異なりますが、通常220円~880円程度\n";
$body .= " となります。\n\n";
}
break;
}
$body .= "【ご注文情報】\n";
$body .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
$body .= "ご注文番号:" . $Order->getOrderNo() . "\n";
$body .= "ご注文日時:" . $Order->getOrderDate()->format('Y年m月d日 H:i') . "\n";
$body .= "お支払い金額:" . number_format($Order->getPaymentTotal()) . "円\n";
$body .= "お支払い方法:" . $Order->getPayment()->getMethod() . "\n\n";
$body .= "【ご注文商品】\n";
foreach ($Order->getOrderItems() as $OrderItem) {
if ($OrderItem->isProduct()) {
$body .= "・" . $OrderItem->getProductName() . " × " . $OrderItem->getQuantity() . "\n";
$body .= " " . number_format($OrderItem->getPrice()) . "円\n";
}
}
$body .= "\n";
$body .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
$body .= $BaseInfo->getShopName() . "\n";
$body .= "URL: https://noiezam.com\n";
$body .= "Email: " . $BaseInfo->getEmail01() . "\n";
return $body;
}
/**
* メール件名の取得
*/
private function getMailSubject($type, Order $Order)
{
$BaseInfo = $this->baseInfoRepository->get();
$shopName = $BaseInfo->getShopName();
switch ($type) {
case 'payment_complete':
return '[' . $shopName . '] ご注文ありがとうございます';
case 'payment_confirm':
return '[' . $shopName . '] ご入金確認のお知らせ';
case 'payment_received':
return '[' . $shopName . '] ご入金を確認いたしました';
case 'order_cancel':
return '[' . $shopName . '] ご注文キャンセルのお知らせ';
default:
return '[' . $shopName . '] ご注文について';
}
}
/**
* 送信元アドレスの取得
*/
private function getFromAddress()
{
$BaseInfo = $this->baseInfoRepository->get();
return [$BaseInfo->getEmail01() => $BaseInfo->getShopName()];
}
/**
* BCCアドレスの取得
*/
private function getBccAddress()
{
$BaseInfo = $this->baseInfoRepository->get();
return $BaseInfo->getEmail01();
}
/**
* メール送信履歴の保存
*/
private function saveMailHistory(Order $Order, $mailType, $body)
{
$MailHistory = new MailHistory();
$MailHistory->setOrder($Order);
$MailHistory->setSendDate(new \DateTime());
$MailHistory->setSubject($this->getMailSubject($mailType, $Order));
$MailHistory->setMailBody($body);
$this->entityManager->persist($MailHistory);
$this->entityManager->flush();
}
/**
* 同じタイプのメールがすでに送信済みかチェック
*/
private function isAlreadySent(Order $Order, $mailType)
{
$subject = $this->getMailSubject($mailType, $Order);
$qb = $this->entityManager->createQueryBuilder();
$qb->select('COUNT(mh.id)')
->from(MailHistory::class, 'mh')
->where('mh.Order = :order')
->andWhere('mh.subject = :subject')
->setParameter('order', $Order)
->setParameter('subject', $subject);
$count = $qb->getQuery()->getSingleScalarResult();
return $count > 0;
}
/**
* プラグイン設定の取得
*/
private function getPluginConfig()
{
$qb = $this->entityManager->createQueryBuilder();
$qb->select('c')
->from('Plugin\NZMailSystem42\Entity\Config', 'c')
->where('c.id = :id')
->setParameter('id', 1);
try {
return $qb->getQuery()->getSingleResult();
} catch (\Exception $e) {
// デフォルト設定を返す
$Config = new \Plugin\NZMailSystem42\Entity\Config();
$Config->setAutoSendEnabled(true);
return $Config;
}
}
}