<?php
/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* https://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Plugin\CodeacLinepay\Controller;
use Eccube\Controller\AbstractController;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Entity\Order;
use Eccube\Repository\Master\OrderStatusRepository;
use Eccube\Repository\OrderRepository;
use Eccube\Service\CartService;
use Eccube\Service\PurchaseFlow\PurchaseContext;
use Eccube\Service\PurchaseFlow\PurchaseFlow;
use Eccube\Service\OrderStateMachine;
use Exception;
use GuzzleHttp\Exception\ConnectException;
use Plugin\CodeacLinepay\Entity\PaymentStatus;
use Plugin\CodeacLinepay\Repository\ConfigRepository;
use Plugin\CodeacLinepay\Repository\PaymentStatusRepository;
use Plugin\CodeacLinepay\Repository\CvsPaymentStatusRepository;
use Plugin\CodeacLinepay\Service\LinePay\LineClient;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Plugin\CodeacLinepay\Util\PayClient;
use Plugin\CodeacLinepay\Repository\PayPayConfigRepository;
/**
* リンク式決済の注文/戻る/完了通知を処理する.
*/
class PaymentController extends AbstractController
{
/**
* @var OrderRepository
*/
protected $orderRepository;
/**
* @var OrderStatusRepository
*/
protected $orderStatusRepository;
/**
* @var PaymentStatusRepository
*/
protected $paymentStatusRepository;
/**
* @var CvsPaymentStatusRepository
*/
protected $cvsPaymentStatusRepository;
/**
* @var PurchaseFlow
*/
protected $purchaseFlow;
/**
* @var CartService
*/
protected $cartService;
/**
* @var OrderStateMachine
*/
protected $orderStateMachine;
/**
* @var ConfigRepository
*/
public $configRepository;
/**
* @var PayPayConfigRepository
*/
public $payconfigRepository;
/**
* PaymentController constructor.
*
* @param OrderRepository $orderRepository
* @param OrderStatusRepository $orderStatusRepository
* @param PaymentStatusRepository $paymentStatusRepository
* @param PurchaseFlow $shoppingPurchaseFlow,
* @param CartService $cartService
* @param OrderStateMachine $orderStateMachine
*/
public function __construct(
OrderRepository $orderRepository,
OrderStatusRepository $orderStatusRepository,
PaymentStatusRepository $paymentStatusRepository,
PurchaseFlow $shoppingPurchaseFlow,
CartService $cartService,
OrderStateMachine $orderStateMachine,
ConfigRepository $configRepository,
PayPayConfigRepository $payconfigRepository
) {
$this->orderRepository = $orderRepository;
$this->orderStatusRepository = $orderStatusRepository;
$this->paymentStatusRepository = $paymentStatusRepository;
$this->purchaseFlow = $shoppingPurchaseFlow;
$this->cartService = $cartService;
$this->orderStateMachine = $orderStateMachine;
$this->configRepository = $configRepository;
$this->payconfigRepository = $payconfigRepository;
}
/**
* @Route("/line_payment_back", name="line_payment_back")
*
* @param Request $request
*
* @return RedirectResponse
*/
public function back(Request $request)
{
$orderNo = $request->get('orderId');
$Order = $this->getOrderByNo($orderNo);
if (!$Order) {
throw new NotFoundHttpException();
}
if ($this->getUser() != $Order->getCustomer()) {
throw new NotFoundHttpException();
}
// change status to during purchase process
$OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
$Order->setOrderStatus($OrderStatus);
// change status to unsettled
$PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::OUTSTANDING);
$Order->setCodeacLinepayPaymentStatus($PaymentStatus);
// purchaseFlow::rollbackを呼び出し, 購入処理をロールバックする.
$this->purchaseFlow->rollback($Order, new PurchaseContext());
$this->entityManager->flush();
return $this->redirectToRoute('shopping');
}
/**
* 完了画面へ遷移する.
*
* @Route("/line_payment_complete", name="line_payment_complete")
* @throws Exception
*/
public function complete(Request $request, SessionInterface $session)
{
$orderNo = $request->get('orderId');
$transactionId = $request->get('transactionId');
$lineOrder = $session->get('linePayOrder');
$Order = $this->getOrderByNo($orderNo);
if (!$Order) {
throw new NotFoundHttpException();
}
if ($this->getUser() != $Order->getCustomer()) {
throw new NotFoundHttpException();
}
if (!$Order) {
throw new NotFoundHttpException();
}
//check for valid transaction id
if ($lineOrder['transactionId'] != $transactionId) {
return $this->redirectToRoute('shopping_error');
}
//confirm line payment
try {
$config = $this->configRepository->get(1);
$linePay = new LineClient([
'channelId' => $config->getLineChannelId(),
'channelSecret' => $config->getLineChannelSecret(),
'isSandbox' => (bool)$config->getSandbox(),
]);
$response = $linePay->confirm($lineOrder['transactionId'], [
'amount' => (int) $lineOrder['params']['amount'],
'currency' => $lineOrder['params']['currency'],
]);
//redirect to error page if confirmation is not successful
if (!$response->isSuccessful()) {
$lineOrder['confirmCode'] = $response['returnCode'];
$lineOrder['confirmMessage'] = $response['returnMessage'];
$lineOrder['isSuccessful'] = false;
$session->set('linePayOrder', $lineOrder);
return $this->redirectToRoute('shopping_error');
} else {
//make sale paid
$paymentData = $response->getInfo();
// change order status to new
$OrderStatus = $this->orderStatusRepository->find(OrderStatus::PAID);
$Order->setOrderStatus($OrderStatus);
//change payment status to actual sale
$PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::ACTUAL_SALES);
$Order->setCodeacLinepayPaymentStatus($PaymentStatus);
$Order->setCodeacLinepayPaymentData(json_encode($paymentData));
// add message to order completion email
$Order->appendCompleteMailMessage('');
// purchaseFlow::commitを呼び出し, 購入処理を完了させる.
$this->purchaseFlow->commit($Order, new PurchaseContext());
//$this->entityManager->flush();
}
} catch (ConnectException $e) {
// die("Confirm API timeout! A recheck mechanism should be implemented.");
return $this->redirectToRoute('shopping_error');
}
// カートを削除する
$this->cartService->clear();
// FIXME 完了画面を表示するため, 受注IDをセッションに保持する
$this->session->set('eccube.front.shopping.order.id', $Order->getId());
$this->entityManager->flush();
return $this->redirectToRoute('shopping_complete');
}
/**
* 完了画面へ遷移する.
*
* @Route("/paypay_payment_complete", name="paypay_payment_complete")
* @throws Exception
*/
public function paypay_complete(Request $request, SessionInterface $session)
{
$orderNo = $request->get('orderId');
$transactionId = $request->get('transactionId');
$payOrder = $session->get('payPayOrder');
$Order = $this->getOrderByNo($orderNo);
if (!$Order) {
$last_payorder = $session->get('payPayOrder');
if(isset($last_payorder['LAST_ORDER'])){
if($last_payorder['LAST_ORDER'] == $orderNo){
$session->set('payPayOrder', []);
return $this->redirectToRoute('mypage');
}
}
throw new NotFoundHttpException();
}
if ($this->getUser() != $Order->getCustomer()) {
throw new NotFoundHttpException();
}
//check for valid transaction id
if ($payOrder['transactionId'] != $transactionId) {
return $this->redirectToRoute('shopping_error');
}
//confirm line payment
try {
$config = $this->payconfigRepository->get(1);
$payPay = new PayClient([
'API_KEY' => $config->getApiId(),
'API_SECRET' => $config->getApiSecret(),
'MERCHANT_ID' => $config->getMemberId()
], (bool) $config->getProduction());
$response = $payPay->code->getPaymentDetails($payOrder['transactionId']);
$data = $response['data'];
if ($response['resultInfo']['code'] == 'SUCCESS') {
if ($data['status'] === "COMPLETED") {
//make sale paid
$paymentData = $data;
$OrderStatus = $this->orderStatusRepository->find(OrderStatus::PAID);
$Order->setOrderStatus($OrderStatus);
$PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::ACTUAL_SALES);
$Order->setCodeacPaypayPaymentStatus($PaymentStatus);
$Order->setCodeacPaypayPaymentData(json_encode($paymentData));
$Order->appendCompleteMailMessage('');
$payOrder = [];
$payOrder['LAST_ORDER'] = $orderNo;
$session->set('payPayOrder', $payOrder);
$this->purchaseFlow->commit($Order, new PurchaseContext());
} else if ($data['status'] === "CREATED") {
//make sale paid
$paymentData = $data;
$OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
$Order->setOrderStatus($OrderStatus);
$PaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::OUTSTANDING);
$Order->setCodeacPaypayPaymentStatus($PaymentStatus);
$Order->setCodeacPaypayPaymentData(json_encode($paymentData));
$this->purchaseFlow->rollback($Order, new PurchaseContext());
$this->entityManager->flush();
return $this->redirectToRoute('shopping');
} else {
$payOrder['confirmCode'] = $response['resultInfo']['code'];
$payOrder['confirmMessage'] = $response['resultInfo']['message'];
$payOrder['isSuccessful'] = false;
$session->set('payPayOrder', $payOrder);
return $this->redirectToRoute('shopping_error');
}
} else {
$payOrder['confirmCode'] = $response['resultInfo']['code'];
$payOrder['confirmMessage'] = $response['resultInfo']['message'];
$payOrder['isSuccessful'] = false;
$session->set('payPayOrder', $payOrder);
return $this->redirectToRoute('shopping_error');
}
} catch (ConnectException $e) {
return $this->redirectToRoute('shopping_error');
}
// カートを削除する
$this->cartService->clear();
// FIXME 完了画面を表示するため, 受注IDをセッションに保持する
$this->session->set('eccube.front.shopping.order.id', $Order->getId());
$this->entityManager->flush();
return $this->redirectToRoute('shopping_complete');
}
/**
* 注文番号で受注を検索する.
*
* @param $orderNo
*
* @return Order
*/
private function getOrderByNo($orderNo)
{
/** @var OrderStatus $pendingOrderStatus */
$pendingOrderStatus = $this->orderStatusRepository->find(OrderStatus::PENDING);
$outstandingPaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::OUTSTANDING);
/** @var Order $Order */
$Order = $this->orderRepository->findOneBy([
'order_no' => $orderNo,
'OrderStatus' => $pendingOrderStatus,
'CodeacLinepayPaymentStatus' => $outstandingPaymentStatus,
]);
return $Order;
}
private function getCompletedOrder($orderNo)
{
/** @var OrderStatus $pendingOrderStatus */
// $pendingOrderStatus = $this->orderStatusRepository->find(OrderStatus::PAID);
// $outstandingPaymentStatus = $this->paymentStatusRepository->find(PaymentStatus::ACTUAL_SALES);
/** @var Order $Order */
$Order = $this->orderRepository->findOneBy([
'order_no' => $orderNo
]);
return $Order;
}
}