diff --git a/Api/Transaction/RepositoryInterface.php b/Api/Transaction/RepositoryInterface.php index 9ef65e0..fe8cc12 100644 --- a/Api/Transaction/RepositoryInterface.php +++ b/Api/Transaction/RepositoryInterface.php @@ -124,7 +124,7 @@ public function getByOrderId(int $orderId): DataInterface; * @throws InputException * @throws NoSuchEntityException */ - public function getByUuid(string $uuid): DataInterface; + public function getByPaymentUuid(string $uuid): DataInterface; /** * @param string $token diff --git a/Controller/Adminhtml/Credentials/Check.php b/Controller/Adminhtml/Credentials/Check.php index 699a24e..b98b842 100644 --- a/Controller/Adminhtml/Credentials/Check.php +++ b/Controller/Adminhtml/Credentials/Check.php @@ -16,7 +16,7 @@ use Magento\Framework\Filesystem\DirectoryList; use Magento\Framework\Filesystem\Io\File; use TrueLayer\Connect\Api\Config\RepositoryInterface as ConfigRepository; -use TrueLayer\Connect\Service\Api\GetClient; +use TrueLayer\Connect\Service\Api\ClientFactory; use TrueLayer\Interfaces\Client\ClientInterface; /** @@ -31,9 +31,9 @@ class Check extends Action implements HttpPostActionInterface */ private $directoryList; /** - * @var GetClient + * @var ClientFactory */ - private $getClient; + private $clientFactory; /** * @var Json */ @@ -52,7 +52,7 @@ class Check extends Action implements HttpPostActionInterface * * @param Action\Context $context * @param JsonFactory $resultJsonFactory - * @param GetClient $getClient + * @param ClientFactory $clientFactory * @param ConfigRepository $configProvider * @param File $file * @param DirectoryList $directoryList @@ -60,12 +60,12 @@ class Check extends Action implements HttpPostActionInterface public function __construct( Action\Context $context, JsonFactory $resultJsonFactory, - GetClient $getClient, + ClientFactory $clientFactory, ConfigRepository $configProvider, File $file, DirectoryList $directoryList ) { - $this->getClient = $getClient; + $this->clientFactory = $clientFactory; $this->resultJson = $resultJsonFactory->create(); $this->configProvider = $configProvider; $this->file = $file; @@ -106,7 +106,7 @@ private function testCredentials(): ?ClientInterface throw new LocalizedException(__('No Client Secret set!')); } - $result = $this->getClient->execute( + $result = $this->clientFactory->create( (int)$config['store_id'], ['credentials' => $config['credentials']] ); diff --git a/Controller/Checkout/Process.php b/Controller/Checkout/Process.php index ee6b9d7..9750561 100644 --- a/Controller/Checkout/Process.php +++ b/Controller/Checkout/Process.php @@ -52,6 +52,7 @@ public function __construct( public function execute(): Redirect { $resultRedirect = $this->resultRedirectFactory->create(); + if (!$transactionId = $this->getRequest()->getParam('payment_id')) { $this->messageManager->addErrorMessage(__('Error in return data from TrueLayer')); $resultRedirect->setPath('checkout/cart/index'); diff --git a/Model/Transaction/Repository.php b/Model/Transaction/Repository.php index 580d20c..c946734 100644 --- a/Model/Transaction/Repository.php +++ b/Model/Transaction/Repository.php @@ -199,7 +199,7 @@ public function getByOrderId(int $orderId): DataInterface /** * @inheritDoc */ - public function getByUuid(string $uuid): DataInterface + public function getByPaymentUuid(string $uuid): DataInterface { if (!$uuid) { $errorMsg = static::INPUT_EXCEPTION; diff --git a/Model/Webapi/Checkout.php b/Model/Webapi/Checkout.php index 95a0f67..7fe7882 100644 --- a/Model/Webapi/Checkout.php +++ b/Model/Webapi/Checkout.php @@ -8,7 +8,6 @@ namespace TrueLayer\Connect\Model\Webapi; use Magento\Framework\Math\Random; -use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\Order; use Magento\Checkout\Model\Session; use TrueLayer\Connect\Api\Log\RepositoryInterface as LogRepository; @@ -88,6 +87,7 @@ public function orderRequest() * @throws \TrueLayer\Exceptions\ApiResponseUnsuccessfulException * @throws \TrueLayer\Exceptions\InvalidArgumentException * @throws \TrueLayer\Exceptions\SignerException + * @throws \Magento\Framework\Exception\LocalizedException */ private function getHppUrl(): string { @@ -195,6 +195,7 @@ private function getMerchantAccountId(ClientInterface $client): string /** * @return DataInterface + * @throws \Magento\Framework\Exception\LocalizedException */ private function getTransaction(): DataInterface { diff --git a/Model/Webapi/Pending.php b/Model/Webapi/Pending.php index 9dc0f0e..3b767aa 100644 --- a/Model/Webapi/Pending.php +++ b/Model/Webapi/Pending.php @@ -36,7 +36,7 @@ public function __construct( public function checkOrderPlaced(string $token): bool { try { - $transaction = $this->transactionRepository->getByUuid($token); + $transaction = $this->transactionRepository->getByPaymentUuid($token); return (bool)$transaction->getOrderId(); } catch (InputException|NoSuchEntityException $e) { return false; diff --git a/Model/Webapi/Webhook.php b/Model/Webapi/Webhook.php index 89e6a67..d5693c1 100644 --- a/Model/Webapi/Webhook.php +++ b/Model/Webapi/Webhook.php @@ -123,7 +123,7 @@ private function getStoreId(): int return 0; } - $transaction = $this->transactionRepository->getByUuid($postArray['payment_id']); + $transaction = $this->transactionRepository->getByPaymentUuid($postArray['payment_id']); if (!$quoteId = $transaction->getQuoteId()) { return 0; } diff --git a/Service/Order/ProcessReturn.php b/Service/Order/ProcessReturn.php index 7ac2eb7..4c4acdf 100644 --- a/Service/Order/ProcessReturn.php +++ b/Service/Order/ProcessReturn.php @@ -7,17 +7,12 @@ namespace TrueLayer\Connect\Service\Order; -use Magento\Checkout\Model\Session; use Magento\Framework\Exception\InputException; use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; use Magento\Sales\Api\Data\OrderInterface; -use Magento\Sales\Api\OrderRepositoryInterface; -use Magento\Sales\Model\Order; use TrueLayer\Connect\Api\Log\RepositoryInterface as LogRepository; use TrueLayer\Connect\Api\Transaction\RepositoryInterface as TransactionRepository; -use TrueLayer\Connect\Service\Api\GetClient; +use TrueLayer\Connect\Service\Api\ClientFactory; /** * Class ProcessReturn @@ -31,17 +26,9 @@ class ProcessReturn public const UNKNOWN_MSG = 'Unknown error, please try again.'; /** - * @var GetClient + * @var ClientFactory */ - private $getClient; - /** - * @var Session - */ - private $checkoutSession; - /** - * @var CartRepositoryInterface - */ - private $quoteRepository; + private $clientFactory; /** * @var OrderInterface */ @@ -50,10 +37,6 @@ class ProcessReturn * @var TransactionRepository */ private $transactionRepository; - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; /** * @var LogRepository */ @@ -62,28 +45,19 @@ class ProcessReturn /** * ProcessReturn constructor. * - * @param Session $checkoutSession - * @param GetClient $getClient - * @param CartRepositoryInterface $quoteRepository + * @param ClientFactory $clientFactory * @param OrderInterface $orderInterface - * @param OrderRepositoryInterface $orderRepository * @param TransactionRepository $transactionRepository * @param LogRepository $logger */ public function __construct( - Session $checkoutSession, - GetClient $getClient, - CartRepositoryInterface $quoteRepository, + ClientFactory $clientFactory, OrderInterface $orderInterface, - OrderRepositoryInterface $orderRepository, TransactionRepository $transactionRepository, LogRepository $logger ) { - $this->checkoutSession = $checkoutSession; - $this->getClient = $getClient; - $this->quoteRepository = $quoteRepository; + $this->clientFactory = $clientFactory; $this->orderInterface = $orderInterface; - $this->orderRepository = $orderRepository; $this->transactionRepository = $transactionRepository; $this->logger = $logger; } @@ -96,68 +70,33 @@ public function __construct( * @throws \TrueLayer\Exceptions\ApiRequestJsonSerializationException * @throws \TrueLayer\Exceptions\ApiResponseUnsuccessfulException * @throws \TrueLayer\Exceptions\SignerException - * @throws \TrueLayer\Exceptions\ValidationException */ public function execute(string $transactionId): array { - $transaction = $this->transactionRepository->getByUuid($transactionId); - $quote = $this->quoteRepository->get($transaction->getQuoteId()); - $this->checkoutSession->setLoadInactive(true)->replaceQuote($quote); + $transaction = $this->transactionRepository->getByPaymentUuid($transactionId); + $order = $this->orderInterface->loadByAttribute('quote_id', $transaction->getQuoteId()); - $order = $this->orderInterface->loadByAttribute('quote_id', $quote->getId()); - - $client = $this->getClient->execute($quote->getStoreId()); - $payment = $client->getPayment($transactionId); - $transactionStatus = $payment->getStatus(); + $client = $this->clientFactory->create((int) $order->getStoreId()); + $paymentStatus = $client->getPayment($transactionId)->getStatus(); if (!$order->getEntityId()) { - if ($transactionStatus == 'settled' || $transactionStatus == 'executed') { - return ['success' => false, 'status' => $transactionStatus]; + if ($paymentStatus == 'settled' || $paymentStatus == 'executed') { + return ['success' => false, 'status' => $paymentStatus]; } } - switch ($transactionStatus) { + switch ($paymentStatus) { case 'executed': case 'settled': - $this->updateCheckoutSession($quote, $order); - return ['success' => true, 'status' => $transactionStatus]; + return ['success' => true, 'status' => $paymentStatus]; case 'cancelled': - $message = (string)self::CANCELLED_MSG; - return ['success' => false, 'status' => $transactionStatus, 'msg' => __($message)]; + return ['success' => false, 'status' => $paymentStatus, 'msg' => __(self::CANCELLED_MSG)]; case 'failed': - $message = (string)self::FAILED_MSG; - return ['success' => false, 'status' => $transactionStatus, 'msg' => __($message)]; + return ['success' => false, 'status' => $paymentStatus, 'msg' => __(self::FAILED_MSG)]; case 'rejected': - $message = (string)self::REJECTED_MSG; - return ['success' => false, 'status' => $transactionStatus, 'msg' => __($message)]; + return ['success' => false, 'status' => $paymentStatus, 'msg' => __(self::REJECTED_MSG)]; default: - $message = (string)self::UNKNOWN_MSG; - return ['success' => false, 'status' => $transactionStatus, 'msg' => __($message)]; + return ['success' => false, 'status' => $paymentStatus, 'msg' => __(self::UNKNOWN_MSG)]; } } - - /** - * @param CartInterface $quote - * @param Order $order - * @return void - */ - private function updateCheckoutSession(CartInterface $quote, Order $order): void - { - $this->orderRepository->save($order); - - // Remove additional quote for customer - if ($customerId = $quote->getCustomer()->getId()) { - try { - $activeQuote = $this->quoteRepository->getActiveForCustomer($customerId); - $this->quoteRepository->delete($activeQuote); - } catch (NoSuchEntityException $e) { - $this->logger->addErrorLog('Remove customer quote', $e->getMessage()); - } - } - - $this->checkoutSession->setLastQuoteId($quote->getEntityId()) - ->setLastSuccessQuoteId($quote->getEntityId()) - ->setLastRealOrderId($order->getIncrementId()) - ->setLastOrderId($order->getId()); - } } diff --git a/Service/Order/ProcessWebhook.php b/Service/Order/ProcessWebhook.php index de3ca49..f3c34e8 100644 --- a/Service/Order/ProcessWebhook.php +++ b/Service/Order/ProcessWebhook.php @@ -8,10 +8,6 @@ namespace TrueLayer\Connect\Service\Order; use Exception; -use Magento\Checkout\Model\Session as CheckoutSession; -use Magento\Quote\Api\CartManagementInterface; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; use Magento\Sales\Model\Order; @@ -29,83 +25,53 @@ class ProcessWebhook { public const SUCCESS_MSG = 'Order #%1 successfully captured on TrueLayer'; - /** - * @var CheckoutSession - */ - public $checkoutSession; - /** - * @var CartRepositoryInterface - */ - private $quoteRepository; - /** - * @var TransactionRepository - */ - private $transactionRepository; - /** - * @var CartManagementInterface - */ - private $cartManagement; - /** - * @var OrderRepositoryInterface - */ - private $orderRepository; - /** - * @var InvoiceSender - */ - private $invoiceSender; - /** - * @var OrderSender - */ - private $orderSender; - /** - * @var ConfigRepository - */ - private $configRepository; - /** - * @var LogRepository - */ - private $logRepository; - /** - * @var UserRepository - */ - private $userRepository; + + private TransactionRepository $transactionRepository; + + private OrderRepositoryInterface $orderRepository; + + private InvoiceSender $invoiceSender; + + private OrderSender $orderSender; + + private ConfigRepository $configRepository; + + private LogRepository $logRepository; + + private UserRepository $userRepository; + + private OrderInterface $orderInterface; /** * ProcessWebhook constructor. * - * @param CartRepositoryInterface $quoteRepository * @param TransactionRepository $transactionRepository - * @param CartManagementInterface $cartManagement * @param OrderRepositoryInterface $orderRepository * @param OrderSender $orderSender * @param InvoiceSender $invoiceSender * @param ConfigRepository $configRepository * @param LogRepository $logRepository - * @param CheckoutSession $checkoutSession * @param UserRepository $userRepository + * @param OrderInterface $orderInterface */ public function __construct( - CartRepositoryInterface $quoteRepository, TransactionRepository $transactionRepository, - CartManagementInterface $cartManagement, OrderRepositoryInterface $orderRepository, OrderSender $orderSender, InvoiceSender $invoiceSender, ConfigRepository $configRepository, LogRepository $logRepository, - CheckoutSession $checkoutSession, - UserRepository $userRepository + UserRepository $userRepository, + OrderInterface $orderInterface ) { - $this->quoteRepository = $quoteRepository; $this->transactionRepository = $transactionRepository; - $this->cartManagement = $cartManagement; $this->orderRepository = $orderRepository; $this->orderSender = $orderSender; $this->invoiceSender = $invoiceSender; $this->configRepository = $configRepository; $this->logRepository = $logRepository; - $this->checkoutSession = $checkoutSession; $this->userRepository = $userRepository; + $this->orderInterface = $orderInterface; } /** @@ -116,94 +82,52 @@ public function __construct( */ public function execute(string $uuid, string $userId) { - $this->logRepository->addDebugLog('webhook payload uuid', $uuid); - error_log('********* '.$uuid); + $this->logRepository->addDebugLog('webhook - start processing ', $uuid); try { - $transaction = $this->transactionRepository->getByUuid($uuid); + $transaction = $this->transactionRepository->getByPaymentUuid($uuid); - $this->logRepository->addDebugLog( - 'webhook transaction id', - $transaction->getEntityId() . ' quote_id = ' . $transaction->getQuoteId() - ); + $this->logRepository->addDebugLog('webhook', [ + 'transaction id' => $transaction->getEntityId(), + 'quote id' => $transaction->getQuoteId(), + ]); - if (!$quoteId = $transaction->getQuoteId()) { - $this->logRepository->addDebugLog('webhook', 'no quote id found in transaction'); + if (!$transaction->getQuoteId()) { + $this->logRepository->addDebugLog('webhook', 'aborting, no quote found'); return; } - $quote = $this->quoteRepository->get($quoteId); - $this->checkoutSession->setQuoteId($quoteId); - - if (!$this->transactionRepository->isLocked($transaction)) { - $this->logRepository->addDebugLog('webhook', 'start processing accepted transaction'); - $this->transactionRepository->lock($transaction); - - if (!$this->transactionRepository->checkOrderIsPlaced($transaction)) { - $orderId = $this->placeOrder($quote, $uuid, $userId); - $transaction->setOrderId((int)$orderId)->setStatus('payment_settled'); - $this->transactionRepository->save($transaction); - $this->logRepository->addDebugLog('webhook', 'Order placed. Order id = ' . $orderId); - } - - $this->transactionRepository->unlock($transaction); - $this->logRepository->addDebugLog('webhook', 'end processing accepted transaction'); + if ($transaction->getIsLocked()) { + $this->logRepository->addDebugLog('webhook', 'aborting, transaction is locked'); + return; } - } catch (Exception $e) { - $this->logRepository->addDebugLog('webhook exception', $e->getMessage()); - } - } - /** - * @param CartInterface $quote - * @param $uuid - * @param $userId - * @return false|int|null - */ - private function placeOrder(CartInterface $quote, $uuid, $userId) - { - try { - $quote = $this->prepareQuote($quote, $userId); - $orderId = $this->cartManagement->placeOrder($quote->getId()); - $order = $this->orderRepository->get($orderId); - $this->sendOrderEmail($order); + $this->logRepository->addDebugLog('webhook', 'locking transaction and starting order update'); + $this->transactionRepository->lock($transaction); + $order = $this->orderInterface->loadByAttribute('quote_id', $transaction->getQuoteId()); + // Update payment and order status $payment = $order->getPayment(); $payment->setTransactionId($uuid); $payment->setIsTransactionClosed(true); $payment->registerCaptureNotification($order->getGrandTotal(), true); $order->setState(Order::STATE_PROCESSING)->setStatus(Order::STATE_PROCESSING); $this->orderRepository->save($order); - $this->sendInvoiceEmail($order); - } catch (Exception $e) { - $this->logRepository->addDebugLog('place order', $e->getMessage()); - return false; - } + $this->logRepository->addDebugLog('webhook', 'payment and order statuses updated'); - return $order->getEntityId(); - } + // Send emails + $this->sendOrderEmail($order); + $this->sendInvoiceEmail($order); + $this->logRepository->addDebugLog('webhook', 'emails sent'); - /** - * Make sure the quote is valid for order placement. - * - * Force setCustomerIsGuest; see issue: https://github.com/magento/magento2/issues/23908 - * - * @param CartInterface $quote - * - * @return CartInterface - */ - private function prepareQuote(CartInterface $quote, string $userId): CartInterface - { - if ($quote->getCustomerEmail() == null) { - $user = $this->userRepository->getByTruelayerId($userId); - $quote->setCustomerEmail($user['magento_email']); + // Update transaction statis + $transaction->setStatus('payment_settled'); + $this->transactionRepository->save($transaction); + $this->transactionRepository->unlock($transaction); + $this->logRepository->addDebugLog('webhook', 'transaction status set to settled'); + } catch (Exception $e) { + $this->logRepository->addDebugLog('webhook - exception', $e->getMessage()); } - - $quote->setCustomerIsGuest($quote->getCustomerId() == null); - $quote->setIsActive(true); - $quote->getShippingAddress()->setCollectShippingRates(false); - $this->quoteRepository->save($quote); - return $quote; } /** diff --git a/Service/Order/RefundOrder.php b/Service/Order/RefundOrder.php index c8a6480..055b295 100644 --- a/Service/Order/RefundOrder.php +++ b/Service/Order/RefundOrder.php @@ -12,7 +12,7 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Sales\Api\Data\OrderInterface; use TrueLayer\Connect\Api\Transaction\RepositoryInterface as TransactionRepository; -use TrueLayer\Connect\Service\Api\GetClient; +use TrueLayer\Connect\Service\Api\ClientFactory; class RefundOrder { @@ -20,9 +20,9 @@ class RefundOrder public const EXCEPTION_MSG = 'Unable to refund order #%1 on TrueLayer'; /** - * @var GetClient + * @var ClientFactory */ - private $getClient; + private $clientFactory; /** * @var TransactionRepository */ @@ -31,14 +31,14 @@ class RefundOrder /** * RefundOrder constructor. * - * @param GetClient $getClient + * @param ClientFactory $clientFactory * @param TransactionRepository $transactionRepository */ public function __construct( - GetClient $getClient, + ClientFactory $clientFactory, TransactionRepository $transactionRepository ) { - $this->getClient = $getClient; + $this->clientFactory = $clientFactory; $this->transactionRepository = $transactionRepository; } @@ -59,7 +59,7 @@ public function execute(OrderInterface $order, float $amount): array $transaction = $this->transactionRepository->getByOrderId((int)$order->getId()); if ($amount != 0) { - $client = $this->getClient->execute((int)$order->getStoreId()); + $client = $this->clientFactory->create((int)$order->getStoreId()); $refundId = $client->refund() ->payment($transaction->getPaymentUuid()) ->amountInMinor((int)bcmul((string)$amount, '100'))