diff --git a/.version b/.version index eca07e4..ac2cdeb 100644 --- a/.version +++ b/.version @@ -1 +1 @@ -2.1.2 +2.1.3 diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 074a4f6..a45b9e0 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -5,6 +5,17 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.1.3] + + +### Added + +- Adding minimum amount validation + +### Fixed + +- Performance improvements + ## [2.1.1] ### Fixed diff --git a/Service/TpayService.php b/Service/TpayService.php index b8501fc..6afc64e 100644 --- a/Service/TpayService.php +++ b/Service/TpayService.php @@ -6,9 +6,11 @@ use Exception; use Magento\Framework\Event\ManagerInterface as EventManagerInterface; +use Magento\Sales\Api\Data\InvoiceInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\OrderPaymentInterface; use Magento\Sales\Api\Data\TransactionInterface; +use Magento\Sales\Api\InvoiceRepositoryInterface; use Magento\Sales\Api\OrderPaymentRepositoryInterface; use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Invoice; @@ -30,6 +32,9 @@ class TpayService extends RegisterCaptureNotificationOperation /** @var OrderPaymentRepositoryInterface */ protected $orderPaymentRepository; + /** @var InvoiceRepositoryInterface */ + protected $invoiceRepository; + /** @var BuilderInterface */ protected $builder; @@ -39,6 +44,7 @@ class TpayService extends RegisterCaptureNotificationOperation public function __construct( OrderRepositoryInterface $orderRepository, OrderPaymentRepositoryInterface $orderPaymentRepository, + InvoiceRepositoryInterface $invoiceRepository, BuilderInterface $builder, CommandInterface $stateCommand, BuilderInterface $transactionBuilder, @@ -48,6 +54,7 @@ public function __construct( ) { $this->orderRepository = $orderRepository; $this->orderPaymentRepository = $orderPaymentRepository; + $this->invoiceRepository = $invoiceRepository; $this->builder = $builder; $this->invoiceService = $invoiceService; parent::__construct( @@ -91,6 +98,16 @@ public function getPayment(string $orderId): OrderPaymentInterface return $order->getPayment(); } + public function getOrderById(string $orderId): OrderInterface + { + return $this->orderRepository->getByIncrementId($orderId); + } + + public function saveOrderPayment(OrderPaymentInterface $payment): OrderPaymentInterface + { + return $this->orderPaymentRepository->save($payment); + } + /** * @throws Exception * @@ -99,57 +116,24 @@ public function getPayment(string $orderId): OrderPaymentInterface public function setOrderStatus(string $orderId, array $validParams, TpayConfigInterface $tpayConfig) { $order = $this->getOrderById($orderId); + $emailNotify = false; if (!$order->getId()) { return false; } - $sendNewInvoiceMail = (bool) $tpayConfig->getInvoiceSendMail(); - $orderAmount = (float) number_format((float) $order->getBaseGrandTotal(), 2, '.', ''); - $trStatus = $validParams['tr_status']; - $emailNotify = false; - - if ('TRUE' === $trStatus && ((float) number_format($validParams['tr_paid'], 2, '.', '') === $orderAmount)) { - if (Order::STATE_PROCESSING !== $order->getState()) { - $emailNotify = true; - } - $status = Order::STATE_PROCESSING; - $this->registerCaptureNotificationTpay($order->getPayment(), $order->getBaseGrandTotal(), $validParams); - } elseif ('CHARGEBACK' === $trStatus) { - $order->addCommentToStatusHistory($this->getTransactionDesc($validParams)); - $this->orderRepository->save($order); + $status = $this->determineOrderStatus($order, $validParams, $emailNotify); + if (!$status) { return $order; - } else { - if (Order::STATE_HOLDED !== $order->getState()) { - $emailNotify = true; - } - $comment = __('The order has been holded: ').'
'.$this->getTransactionDesc($validParams); - $status = Order::STATE_HOLDED; - $order->addStatusToHistory($status, $comment, true); } - if ($emailNotify) { - $order->setSendEmail(true); - } - - $order->setStatus($status)->setState($status); - $this->orderRepository->save($order); - if ($sendNewInvoiceMail) { - foreach ($order->getInvoiceCollection() as $invoice) { - $invoiceId = $invoice->getId(); - $this->invoiceService->notify($invoiceId); - } - } + $order = $this->updateOrderStatus($order, $status, $emailNotify); + $this->sendInvoiceEmailsIfNeeded($order, $tpayConfig); return $order; } - public function getOrderById(string $orderId): OrderInterface - { - return $this->orderRepository->getByIncrementId($orderId); - } - public function setCardOrderStatus($orderId, array $validParams, TpayConfigInterface $tpayConfig) { /** @var Order $order */ @@ -159,45 +143,18 @@ public function setCardOrderStatus($orderId, array $validParams, TpayConfigInter return false; } - $sendNewInvoiceMail = (bool) $tpayConfig->getInvoiceSendMail(); - $transactionDesc = $this->getCardTransactionDesc($validParams); - $orderAmount = (float) number_format((float) $order->getBaseGrandTotal(), 2, '.', ''); - $emailNotify = false; - - $order = $this->updateTransactionId($order, $validParams); - $amountCheck = (float) number_format((float) $validParams['amount'], 2, '.', '') !== $orderAmount; - - if (!isset($validParams['status']) || 'correct' !== $validParams['status'] || $amountCheck) { - $comment = __('Payment has been declined. ').'
'.$transactionDesc; - $this->addCommentToHistory($orderId, $comment); - } else { - if (Order::STATE_PROCESSING != $order->getState()) { - $emailNotify = true; - } - $this->registerCardCaptureNotificationTpay($order->getPayment(), $order->getBaseGrandTotal(), $validParams); - } + $emailNotify = $this->commentCardOrder($order, $validParams, $orderId); if ($emailNotify) { $order->setSendEmail(true); } + $this->sendInvoiceEmailsIfNeeded($order, $tpayConfig); $this->orderRepository->save($order); - if ($sendNewInvoiceMail) { - foreach ($order->getInvoiceCollection() as $invoice) { - $invoiceId = $invoice->getId(); - $this->invoiceService->notify($invoiceId); - } - } - return $order; } - public function saveOrderPayment(OrderPaymentInterface $payment): OrderPaymentInterface - { - return $this->orderPaymentRepository->save($payment); - } - /** @return bool|string */ protected function getTransactionDesc(array $validParams) { @@ -227,18 +184,19 @@ protected function getCardTransactionDesc($validParams) if (empty($validParams)) { return false; } + if (isset($validParams['err_desc'])) { return 'Payment error: '.$validParams['err_desc'].', error code: '.$validParams['err_code']; } + $error = null; + if ('declined' === $validParams['status']) { $error = $validParams['reason']; } $transactionDesc = (is_null($error)) ? ' ' : ' Reason: '.$error.''; - $transactionDesc .= (isset($validParams['test_mode'])) && 1 === (int) $validParams['test_mode'] - ? ' TEST ' - : ' '; + $transactionDesc .= (isset($validParams['test_mode'])) && 1 === (int) $validParams['test_mode'] ? ' TEST ' : ' '; return $transactionDesc; } @@ -247,13 +205,26 @@ protected function getCardTransactionDesc($validParams) * @param float|string $amount * @param bool|int $skipFraudDetection */ - private function registerCaptureNotificationTpay( - OrderPaymentInterface $payment, - $amount, - array $validParams, - $skipFraudDetection = false - ) { - // @var $payment Payment + private function registerCaptureNotificationTpay(OrderPaymentInterface $payment, $amount, array $validParams, bool $skipFraudDetection = false) + { + $this->setTransactionIdForPayment($payment); + + $order = $payment->getOrder(); + $amount = (float) $amount; + $invoice = $this->getInvoiceForTransactionId($order, $payment->getTransactionId()); + + if (!$invoice && $payment->isCaptureFinal($amount)) { + $invoice = $this->createAndRegisterInvoice($order, $payment); + } else { + $this->handlePotentialFraud($payment, $amount, $skipFraudDetection); + } + + $this->finalizePayment($payment, $invoice, $amount, $order); + $this->createTransactionAndAddComments($payment, $invoice, $validParams, $amount, $order); + } + + private function setTransactionIdForPayment(OrderPaymentInterface $payment): void + { $payment->setTransactionId( $this->transactionManager->generateTransactionId( $payment, @@ -262,52 +233,82 @@ private function registerCaptureNotificationTpay( ) ); - $order = $payment->getOrder(); - $amount = (float) $amount; - $invoice = $this->getInvoiceForTransactionId($order, $payment->getTransactionId()); + $this->orderPaymentRepository->save($payment); + } - if (!$invoice && $payment->isCaptureFinal($amount)) { - $invoice = $order->prepareInvoice()->register(); - $invoice->setOrder($order); - $order->addRelatedObject($invoice); - $payment->setCreatedInvoice($invoice); - $payment->setShouldCloseParentTransaction(true); - } else { - $payment->setIsFraudDetected(!$skipFraudDetection); - $this->updateTotals($payment, ['base_amount_paid_online' => $amount]); + private function createAndRegisterInvoice(OrderInterface $order, OrderPaymentInterface $payment, ?string $state = null): InvoiceInterface + { + $invoice = $order->prepareInvoice()->register(); + $invoice->setOrder($order); + $order->addRelatedObject($invoice); + $payment->setCreatedInvoice($invoice); + $payment->setShouldCloseParentTransaction(true); + + if ($state) { + $order->setState($state); } + $this->invoiceRepository->save($invoice); + $this->orderRepository->save($order); + $this->orderPaymentRepository->save($payment); + + return $invoice; + } + + private function handlePotentialFraud(OrderPaymentInterface $payment, float $amount, bool $skipFraudDetection): void + { + $payment->setIsFraudDetected(!$skipFraudDetection); + $this->updateTotals($payment, ['base_amount_paid_online' => $amount]); + + $this->orderPaymentRepository->save($payment); + } + + private function finalizePayment(OrderPaymentInterface $payment, ?InvoiceInterface $invoice, float $amount, OrderInterface $order): void + { if (!$payment->getIsTransactionPending() && $invoice && Invoice::STATE_OPEN === $invoice->getState()) { $invoice->setOrder($order); $invoice->pay(); $this->updateTotals($payment, ['base_amount_paid_online' => $amount]); $order->addRelatedObject($invoice); + + $this->invoiceRepository->save($invoice); + $this->orderRepository->save($order); + $this->orderPaymentRepository->save($payment); } + } + private function createTransactionAndAddComments(OrderPaymentInterface $payment, ?InvoiceInterface $invoice, array $validParams, float $amount, OrderInterface $order): void + { $message = $this->stateCommand->execute($payment, $amount, $order); - $payment - ->setTransactionId($validParams['tr_id']) + $payment->setTransactionId($validParams['tr_id']) ->setTransactionAdditionalInfo(Transaction::RAW_DETAILS, $validParams); $transaction = $payment->addTransaction(Transaction::TYPE_ORDER, $invoice, true); $message = $payment->prependMessage($message); $payment->addTransactionCommentsToOrder($transaction, $message); + + $this->orderPaymentRepository->save($payment); + $this->orderRepository->save($order); } - private function registerCardCaptureNotificationTpay( - OrderPaymentInterface $payment, - $amount, - $validParams, - $skipFraudDetection = false - ) { - // @var $payment Payment - $payment->setTransactionId( - $this->transactionManager->generateTransactionId( - $payment, - Transaction::TYPE_CAPTURE, - $payment->getAuthorizationTransaction() - ) - ); + private function createCardTransactionAndAddComments(OrderPaymentInterface $payment, ?InvoiceInterface $invoice, array $validParams, float $amount, OrderInterface $order): void + { + $payment + ->setTransactionId($validParams['sale_auth']) + ->setTransactionAdditionalInfo(Transaction::RAW_DETAILS, $validParams); + + $transaction = $payment->addTransaction(TransactionInterface::TYPE_CAPTURE, $invoice, true); + $message = $this->stateCommand->execute($payment, $amount, $order); + $message = $payment->prependMessage($message); + $payment->addTransactionCommentsToOrder($transaction, $message); + + $this->orderPaymentRepository->save($payment); + $this->orderRepository->save($order); + } + + private function registerCardCaptureNotificationTpay(OrderPaymentInterface $payment, $amount, $validParams, $skipFraudDetection = false) + { + $this->setTransactionIdForPayment($payment); $order = $payment->getOrder(); $amount = (float) $amount; @@ -317,40 +318,17 @@ private function registerCardCaptureNotificationTpay( if (!in_array($orderCurrencyCode, CurrencyCodesDictionary::CODES)) { throw new Exception(sprintf('Order currency %s does not exist in Tpay dictionary!', $orderCurrencyCode)); } + $orderCurrency = array_search($orderCurrencyCode, CurrencyCodesDictionary::CODES); - // register new capture - if ( - !$invoice - && $payment->isCaptureFinal($amount) - && ($orderCurrency === (int) $validParams['currency'] || $orderCurrencyCode === $validParams['currency']) - ) { - $invoice = $order->prepareInvoice()->register(); - $invoice->setOrder($order); - $order->addRelatedObject($invoice); - $payment->setCreatedInvoice($invoice); - $payment->setShouldCloseParentTransaction(true); - $order->setState(Order::STATE_PROCESSING); - $this->orderRepository->save($order); - } else { - $payment->setIsFraudDetected(!$skipFraudDetection); - $this->updateTotals($payment, ['base_amount_paid_online' => $amount]); - } - if (!$payment->getIsTransactionPending() && $invoice && Invoice::STATE_OPEN === $invoice->getState()) { - $invoice->setOrder($order); - $invoice->pay(); - $this->updateTotals($payment, ['base_amount_paid_online' => $amount]); - $order->addRelatedObject($invoice); + if (!$invoice && $payment->isCaptureFinal($amount) && ($orderCurrency === (int) $validParams['currency'] || $orderCurrencyCode === $validParams['currency'])) { + $invoice = $this->createAndRegisterInvoice($order, $payment, Order::STATE_PROCESSING); + } else { + $this->handlePotentialFraud($payment, $amount, $skipFraudDetection); } - $payment - ->setTransactionId($validParams['sale_auth']) - ->setTransactionAdditionalInfo(Transaction::RAW_DETAILS, $validParams); - - $transaction = $payment->addTransaction(TransactionInterface::TYPE_CAPTURE, $invoice, true); - $message = $this->stateCommand->execute($payment, $amount, $order); - $message = $payment->prependMessage($message); - $payment->addTransactionCommentsToOrder($transaction, $message); + $this->finalizePayment($payment, $invoice, $amount, $order); + $this->createCardTransactionAndAddComments($payment, $invoice, $validParams, $amount, $order); } private function updateTransactionId(Order $order, array $validParams): Order @@ -363,4 +341,81 @@ private function updateTransactionId(Order $order, array $validParams): Order return $order; } + + private function determineOrderStatus(OrderInterface $order, array $validParams, bool &$emailNotify): ?string + { + $orderAmount = (float) number_format((float) $order->getBaseGrandTotal(), 2, '.', ''); + $trStatus = $validParams['tr_status']; + + if ('TRUE' === $trStatus && ((float) number_format($validParams['tr_paid'], 2, '.', '') === $orderAmount)) { + if (Order::STATE_PROCESSING !== $order->getState()) { + $emailNotify = true; + } + + $this->registerCaptureNotificationTpay($order->getPayment(), $order->getBaseGrandTotal(), $validParams); + + return Order::STATE_PROCESSING; + } + if ('CHARGEBACK' === $trStatus) { + $order->addCommentToStatusHistory($this->getTransactionDesc($validParams)); + $this->orderRepository->save($order); + + return null; + } + + if (Order::STATE_HOLDED !== $order->getState()) { + $emailNotify = true; + } + + $comment = __('The order has been holded: ').'
'.$this->getTransactionDesc($validParams); + $status = Order::STATE_HOLDED; + $order->addStatusToHistory($status, $comment, true); + + return $status; + } + + private function commentCardOrder(OrderInterface &$order, array $validParams, string $orderId): bool + { + $transactionDesc = $this->getCardTransactionDesc($validParams); + $orderAmount = (float) number_format((float) $order->getBaseGrandTotal(), 2, '.', ''); + $emailNotify = false; + + $order = $this->updateTransactionId($order, $validParams); + $amountCheck = (float) number_format((float) $validParams['amount'], 2, '.', '') !== $orderAmount; + + if (!isset($validParams['status']) || 'correct' !== $validParams['status'] || $amountCheck) { + $comment = __('Payment has been declined. ').'
'.$transactionDesc; + $this->addCommentToHistory($orderId, $comment); + } else { + if (Order::STATE_PROCESSING != $order->getState()) { + $emailNotify = true; + } + + $this->registerCardCaptureNotificationTpay($order->getPayment(), $order->getBaseGrandTotal(), $validParams); + } + + return $emailNotify; + } + + private function updateOrderStatus(OrderInterface $order, string $status, bool $emailNotify): OrderInterface + { + if ($emailNotify) { + $order->setSendEmail(true); + } + + $order->setStatus($status)->setState($status); + $this->orderRepository->save($order); + + return $order; + } + + private function sendInvoiceEmailsIfNeeded(OrderInterface $order, TpayConfigInterface $tpayConfig): void + { + if ($tpayConfig->getInvoiceSendMail()) { + foreach ($order->getInvoiceCollection() as $invoice) { + $invoiceId = $invoice->getId(); + $this->invoiceService->notify($invoiceId); + } + } + } }