From 5fc426df823a094ecb84580af970b95e2e7a0fd1 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Fri, 3 Aug 2018 10:14:20 +0200 Subject: [PATCH 001/121] Fixed Undefined Index for Website level configuration --- Plugin/Config/Model/Config.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugin/Config/Model/Config.php b/Plugin/Config/Model/Config.php index 04dff801..78b8eb73 100644 --- a/Plugin/Config/Model/Config.php +++ b/Plugin/Config/Model/Config.php @@ -111,8 +111,8 @@ private function areCredentialsChanged($requiredFields): bool { $hasChanged = false; foreach (self::$encryptedFields as $fieldKey) { - $value = (string)$requiredFields[$fieldKey]['value']; - if (!preg_match('/^\*+$/', $value)) { + $value = $requiredFields[$fieldKey]['value'] ?? false; + if ($value && !preg_match('/^\*+$/', (string)$value)) { $hasChanged = true; break; } From 9650a457659a4838293432c9578e4a21408a688d Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Fri, 10 Aug 2018 14:39:02 +0200 Subject: [PATCH 002/121] VIPPS-147: Publication v.1.0.4 - Fixed issue with canceling order --- Model/OrderPlace.php | 51 +++++++------------------------------------- 1 file changed, 8 insertions(+), 43 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index e1e64a1b..e36cbbe7 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -15,8 +15,8 @@ */ namespace Vipps\Payment\Model; -use Magento\Framework\{ - Exception\CouldNotSaveException, Exception\NoSuchEntityException +use Magento\Framework\Exception\{ + CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException }; use Magento\Sales\Api\{ OrderManagementInterface, Data\OrderInterface, OrderRepositoryInterface @@ -121,15 +121,14 @@ public function __construct( } /** - * Place order - * * @param CartInterface $quote * @param Transaction $transaction * * @return OrderInterface|null * @throws CouldNotSaveException * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException + * @throws AlreadyExistsException + * @throws InputException */ public function execute(CartInterface $quote, Transaction $transaction) { @@ -153,7 +152,8 @@ public function execute(CartInterface $quote, Transaction $transaction) * @param CartInterface $quote * * @return bool|string - * @throws \Magento\Framework\Exception\InputException + * @throws AlreadyExistsException + * @throws InputException */ private function acquireLock(CartInterface $quote) { @@ -171,7 +171,7 @@ private function acquireLock(CartInterface $quote) * @param $lockName * * @return bool - * @throws \Magento\Framework\Exception\InputException + * @throws InputException */ private function releaseLock($lockName) { @@ -198,7 +198,7 @@ private function canPlaceOrder(Transaction $transaction) } $lastHistoryItem = $transaction->getTransactionLogHistory()->getLastItem(); - if ($lastHistoryItem->getOperation() == Transaction::TRANSACTION_OPERATION_RESERVE + if ($lastHistoryItem && $lastHistoryItem->getOperation() == Transaction::TRANSACTION_OPERATION_RESERVE && $lastHistoryItem->isOperationSuccess() ) { return true; @@ -285,41 +285,6 @@ private function authorize(OrderInterface $order, Transaction $transaction) $this->notify($order); } - /** - * Capture - * - * @param OrderInterface $order - * @param Transaction $transaction - */ - private function capture(OrderInterface $order, Transaction $transaction) - { - if ($order->getState() !== Order::STATE_NEW) { - return; - } - - // preconditions - $totalDue = $order->getTotalDue(); - $baseTotalDue = $order->getBaseTotalDue(); - - /** @var Payment $payment */ - $payment = $order->getPayment(); - $payment->setAmountAuthorized($totalDue); - $payment->setBaseAmountAuthorized($baseTotalDue); - - $transactionId = $transaction->getTransactionId(); - $payment->setTransactionId($transactionId); - $payment->setTransactionAdditionalInfo( - PaymentTransaction::RAW_DETAILS, - $transaction->getTransactionInfo()->getData() - ); - - // do capture - $this->processor->registerCaptureNotification($payment, $baseTotalDue); - $this->orderRepository->save($order); - - $this->notify($order); - } - /** * Send order conformation email if not sent * From 10c1954befdab35e5f0fae60cfe17f4279572cda Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Wed, 15 Aug 2018 09:58:59 +0200 Subject: [PATCH 003/121] VIPPS-151: Cron job wrong behavior for express payment orders --- Controller/Payment/Fallback.php | 83 ++++++++++------------------- Cron/FetchOrderFromVipps.php | 49 ++++++----------- Gateway/Command/CommandManager.php | 4 ++ Gateway/Transaction/Transaction.php | 5 -- Model/OrderPlace.php | 34 ++++++------ Model/QuoteUpdater.php | 76 +++++++++++++++++--------- 6 files changed, 114 insertions(+), 137 deletions(-) diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index 4da5a934..d7608d8b 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -16,27 +16,14 @@ namespace Vipps\Payment\Controller\Payment; use Magento\Framework\{ - Controller\ResultFactory, - Controller\ResultInterface, - Exception\CouldNotSaveException, - Exception\LocalizedException, - Exception\NoSuchEntityException, - Session\SessionManagerInterface, - Controller\Result\Redirect, - App\Action\Action, - App\Action\Context, + Controller\ResultFactory, Controller\ResultInterface, Exception\CouldNotSaveException, + Exception\LocalizedException, Exception\NoSuchEntityException, Exception\InputException, + Session\SessionManagerInterface, Controller\Result\Redirect, App\Action\Action, App\Action\Context, App\ResponseInterface }; use Vipps\Payment\{ - Api\CommandManagerInterface, - Gateway\Exception\MerchantException, - Gateway\Request\Initiate\MerchantDataBuilder, - Model\OrderLocator, - Model\OrderPlace, - Gateway\Exception\VippsException, - Gateway\Transaction\TransactionBuilder, - Gateway\Transaction\Transaction, - Model\QuoteLocator + Api\CommandManagerInterface, Gateway\Exception\MerchantException, Gateway\Request\Initiate\MerchantDataBuilder, + Model\OrderLocator, Model\OrderPlace, Gateway\Transaction\TransactionBuilder, Model\QuoteLocator }; use Magento\Quote\{ Api\Data\CartInterface, Api\CartRepositoryInterface, Model\Quote @@ -151,12 +138,10 @@ public function execute() $order = $this->getOrder(); if (!$order) { - $transaction = $this->getPaymentDetails(); - $order = $this->placeOrder($quote, $transaction); + $order = $this->placeOrder($quote); } $this->updateCheckoutSession($quote, $order); - /** @var ZendResponse $result */ $resultRedirect->setPath('checkout/onepage/success', ['_secure' => true]); } catch (LocalizedException $e) { @@ -238,56 +223,45 @@ private function getOrder() return $this->order; } + /** - * Get payment details from vipps + * @param CartInterface $quote * - * @return Transaction + * @return OrderInterface|null + * @throws CouldNotSaveException * @throws LocalizedException - * @throws MerchantException * @throws NoSuchEntityException - * @throws VippsException + * @throws InputException */ - private function getPaymentDetails() + private function placeOrder(CartInterface $quote) { try { - $response = $this->commandManager - ->getOrderStatus($this->getRequest()->getParam('order_id')); - - return $this->transactionBuilder->setData($response)->build(); + $response = $this->commandManager->getOrderStatus( + $this->getRequest()->getParam('order_id') + ); + $transaction = $this->transactionBuilder->setData($response)->build(); + if ($transaction->isTransactionAborted()) { + $this->restoreQuote(); + } + $order = $this->orderPlace->execute($quote, $transaction); + if (!$order) { + throw new LocalizedException( + __('Couldn\'t get information about order status right now. Please contact a store administrator.') + ); + } + return $order; } catch (MerchantException $e) { //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { $this->restoreQuote(); - throw new LocalizedException(__('Your order was canceled in Vipps.')); + } else { + throw $e; } - throw $e; } } /** - * @param CartInterface $quote - * @param Transaction $transaction - * - * @return OrderInterface|null - * @throws CouldNotSaveException * @throws LocalizedException - * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - */ - private function placeOrder(CartInterface $quote, Transaction $transaction) - { - if ($transaction->isTransactionAborted()) { - $this->restoreQuote(); - throw new LocalizedException(__('Your order was canceled in Vipps.')); - } - $order = $this->orderPlace->execute($quote, $transaction); - if (!$order) { - throw new LocalizedException(__('Couldn\'t get information about order status right now. Please contact a store administrator.')); //@codingStandardsIgnoreLine - } - return $order; - } - - /** * @throws NoSuchEntityException */ private function restoreQuote() @@ -301,6 +275,7 @@ private function restoreQuote() $this->checkoutSession->setLastQuoteId($quote->getId()); $this->checkoutSession->replaceQuote($quote); + throw new LocalizedException(__('Your order was canceled in Vipps.')); } /** diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index e67d2b1a..7389e6aa 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -15,10 +15,8 @@ */ namespace Vipps\Payment\Cron; -use Magento\Framework\Exception\CouldNotSaveException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; +use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; +use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; use Magento\Sales\Api\Data\OrderInterface; use Vipps\Payment\{ @@ -115,9 +113,17 @@ public function execute() foreach ($quoteCollection as $quote) { try { - $transaction = $this->getPaymentDetails($quote); + $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); + $transaction = $this->transactionBuilder->setData($response)->build(); $this->placeOrder($quote, $transaction); - } catch (\Exception $e) { + } catch (MerchantException $e) { + //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine + if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { + $this->cancelQuote($quote); + } else { + $this->logger->critical($e->getMessage()); + } + } catch (\Throwable $e) { $this->logger->critical($e->getMessage()); } finally { usleep(1000000); //delay for 1 second @@ -127,33 +133,6 @@ public function execute() } while ($currentPage <= $quoteCollection->getLastPageNumber()); } - /** - * Get payment details from vipps - * - * @param CartInterface $quote - * - * @return Transaction - * @throws MerchantException - * @throws VippsException - */ - private function getPaymentDetails(CartInterface $quote) - { - try { - $response = $this->commandManager - ->getOrderStatus($quote->getReservedOrderId()); - - return $this->transactionBuilder->setData($response)->build(); - } catch (MerchantException $e) { - //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine - if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { - $this->cancelQuote($quote); - } else { - throw $e; - } - } - return null; - } - /** * @param CartInterface $quote * @param Transaction $transaction @@ -161,7 +140,9 @@ private function getPaymentDetails(CartInterface $quote) * @return OrderInterface|null * @throws CouldNotSaveException * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException + * @throws VippsException + * @throws AlreadyExistsException + * @throws InputException */ private function placeOrder(CartInterface $quote, Transaction $transaction) { diff --git a/Gateway/Command/CommandManager.php b/Gateway/Command/CommandManager.php index 0f861871..64a90c96 100644 --- a/Gateway/Command/CommandManager.php +++ b/Gateway/Command/CommandManager.php @@ -51,6 +51,8 @@ public function __construct( * @param array $arguments * * @return ResultInterface|null + * @throws CommandException + * @throws NotFoundException */ public function initiatePayment(InfoInterface $payment, $arguments) { @@ -107,6 +109,8 @@ public function execute(CommandInterface $command, InfoInterface $payment = null * @param array $arguments * * @return ResultInterface|null + * @throws CommandException + * @throws NotFoundException */ public function executeByCode($commandCode, InfoInterface $payment = null, array $arguments = []) { diff --git a/Gateway/Transaction/Transaction.php b/Gateway/Transaction/Transaction.php index af039a3e..2dd5b186 100644 --- a/Gateway/Transaction/Transaction.php +++ b/Gateway/Transaction/Transaction.php @@ -222,11 +222,6 @@ public function isExpressCheckout() */ public function isTransactionAborted() { - $lastHistoryItem = $this->getTransactionLogHistory()->getLastItem(); - if ($lastHistoryItem && $lastHistoryItem->getOperation() == Transaction::TRANSACTION_OPERATION_CANCEL) { - return true; - } - $abortedStatuses = [ Transaction::TRANSACTION_STATUS_CANCEL, Transaction::TRANSACTION_STATUS_CANCELLED, diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index e36cbbe7..3f6ab533 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -29,8 +29,8 @@ CartRepositoryInterface, CartManagementInterface, Data\CartInterface }; use Magento\Quote\Model\Quote; -use Vipps\Payment\{ - Gateway\Transaction\Transaction +use Vipps\Payment\Gateway\{ + Transaction\Transaction, Exception\VippsException }; /** @@ -125,19 +125,25 @@ public function __construct( * @param Transaction $transaction * * @return OrderInterface|null - * @throws CouldNotSaveException - * @throws NoSuchEntityException * @throws AlreadyExistsException + * @throws CouldNotSaveException * @throws InputException + * @throws NoSuchEntityException + * @throws VippsException */ public function execute(CartInterface $quote, Transaction $transaction) { + if (!$this->canPlaceOrder($transaction)) { + return null; + } + $lockName = $this->acquireLock($quote); if (!$lockName) { return null; } + try { - $order = $this->placeOrder($quote, $transaction); + $order = $this->placeOrder($quote); if ($order) { $this->authorize($order, $transaction); } @@ -197,30 +203,19 @@ private function canPlaceOrder(Transaction $transaction) return true; } - $lastHistoryItem = $transaction->getTransactionLogHistory()->getLastItem(); - if ($lastHistoryItem && $lastHistoryItem->getOperation() == Transaction::TRANSACTION_OPERATION_RESERVE - && $lastHistoryItem->isOperationSuccess() - ) { - return true; - } - return false; } /** * @param CartInterface|Quote $quote - * @param Transaction $transaction * * @return OrderInterface|null * @throws CouldNotSaveException * @throws NoSuchEntityException + * @throws VippsException */ - private function placeOrder(CartInterface $quote, Transaction $transaction) + private function placeOrder(CartInterface $quote) { - if (!$this->canPlaceOrder($transaction)) { - return null; - } - $reservedOrderId = $quote->getReservedOrderId(); if (!$reservedOrderId) { return null; @@ -231,7 +226,8 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) return $order; } - $this->quoteUpdater->execute($quote, $transaction); + //this is used only for express checkout + $this->quoteUpdater->execute($quote); /** @var Quote $quote */ $quote = $this->cartRepository->get($quote->getId()); diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index 1cc4ddae..a93dbc33 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -15,9 +15,15 @@ */ namespace Vipps\Payment\Model; -use Magento\Quote\{Api\CartRepositoryInterface, Model\Quote, Model\Quote\Address}; +use Magento\Quote\{ + Api\CartRepositoryInterface, Api\Data\CartInterface, Model\Quote, Model\Quote\Address +}; use Magento\Braintree\Model\Paypal\Helper\AbstractHelper; -use Vipps\Payment\Gateway\Transaction\{ShippingDetails, Transaction}; +use Vipps\Payment\Gateway\Command\PaymentDetailsProvider; +use Vipps\Payment\Gateway\Exception\VippsException; +use Vipps\Payment\Gateway\Transaction\{ + ShippingDetails, Transaction, TransactionBuilder +}; /** * Class QuoteUpdater @@ -30,44 +36,64 @@ class QuoteUpdater extends AbstractHelper */ private $cartRepository; + /** + * @var PaymentDetailsProvider + */ + private $paymentDetailsProvider; + + /** + * @var TransactionBuilder + */ + private $transactionBuilder; + /** * QuoteUpdater constructor. * * @param CartRepositoryInterface $cartRepository + * @param PaymentDetailsProvider $paymentDetailsProvider + * @param TransactionBuilder $transactionBuilder */ public function __construct( - CartRepositoryInterface $cartRepository + CartRepositoryInterface $cartRepository, + PaymentDetailsProvider $paymentDetailsProvider, + TransactionBuilder $transactionBuilder ) { $this->cartRepository = $cartRepository; + $this->paymentDetailsProvider = $paymentDetailsProvider; + $this->transactionBuilder = $transactionBuilder; } /** - * Method to update Quote data. + * @param CartInterface $quote * - * @param Quote $quote - * @param Transaction $transaction + * @return bool|CartInterface|Quote + * @throws VippsException */ - public function execute(Quote $quote, Transaction $transaction) + public function execute(CartInterface $quote) { - if ($transaction->isExpressCheckout()) { - $payment = $quote->getPayment(); - $payment->setMethod('vipps'); - - $quote->setMayEditShippingAddress(false); - $quote->setMayEditShippingMethod(true); - - $this->updateQuoteAddress($quote, $transaction); - $this->disabledQuoteAddressValidation($quote); - - /** - * Unset shipping assignment to prevent from saving / applying outdated data - * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment - */ - if ($quote->getExtensionAttributes()) { - $quote->getExtensionAttributes()->setShippingAssignments(null); - } - $this->cartRepository->save($quote); + $response = $this->paymentDetailsProvider->get(['orderId' => $quote->getReservedOrderId()]); + $transaction = $this->transactionBuilder->setData($response)->build(); + if (!$transaction->isExpressCheckout()) { + return false; + } + $payment = $quote->getPayment(); + $payment->setMethod('vipps'); + + $quote->setMayEditShippingAddress(false); + $quote->setMayEditShippingMethod(true); + + $this->updateQuoteAddress($quote, $transaction); + $this->disabledQuoteAddressValidation($quote); + + /** + * Unset shipping assignment to prevent from saving / applying outdated data + * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment + */ + if ($quote->getExtensionAttributes()) { + $quote->getExtensionAttributes()->setShippingAssignments(null); } + $this->cartRepository->save($quote); + return $quote; } /** From abd1eb8a637c6aa015a915f2226f6cf343990ff3 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Wed, 15 Aug 2018 13:31:54 +0200 Subject: [PATCH 004/121] VIPPS-151: Cron job wrong behavior for express payment orders --- Controller/Payment/Fallback.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index d7608d8b..36ee098a 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -129,6 +129,7 @@ public function __construct( */ public function execute() { + die; /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); try { @@ -223,7 +224,6 @@ private function getOrder() return $this->order; } - /** * @param CartInterface $quote * From da41211f04d0085a78c94eb9a618011b90131430 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Wed, 15 Aug 2018 13:36:31 +0200 Subject: [PATCH 005/121] VIPPS-151: Cron job wrong behavior for express payment orders --- Controller/Payment/Fallback.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index 36ee098a..13f4e291 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -129,7 +129,6 @@ public function __construct( */ public function execute() { - die; /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); try { From ccab2216cde5f7f579c6e0edb8f87efb3d6d95aa Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Wed, 15 Aug 2018 09:58:59 +0200 Subject: [PATCH 006/121] VIPPS-151: Cron job wrong behavior for express payment orders --- Controller/Payment/Fallback.php | 82 ++++++++++------------------- Cron/FetchOrderFromVipps.php | 49 ++++++----------- Gateway/Command/CommandManager.php | 4 ++ Gateway/Transaction/Transaction.php | 5 -- Model/OrderPlace.php | 34 ++++++------ Model/QuoteUpdater.php | 76 +++++++++++++++++--------- 6 files changed, 113 insertions(+), 137 deletions(-) diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index 4da5a934..13f4e291 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -16,27 +16,14 @@ namespace Vipps\Payment\Controller\Payment; use Magento\Framework\{ - Controller\ResultFactory, - Controller\ResultInterface, - Exception\CouldNotSaveException, - Exception\LocalizedException, - Exception\NoSuchEntityException, - Session\SessionManagerInterface, - Controller\Result\Redirect, - App\Action\Action, - App\Action\Context, + Controller\ResultFactory, Controller\ResultInterface, Exception\CouldNotSaveException, + Exception\LocalizedException, Exception\NoSuchEntityException, Exception\InputException, + Session\SessionManagerInterface, Controller\Result\Redirect, App\Action\Action, App\Action\Context, App\ResponseInterface }; use Vipps\Payment\{ - Api\CommandManagerInterface, - Gateway\Exception\MerchantException, - Gateway\Request\Initiate\MerchantDataBuilder, - Model\OrderLocator, - Model\OrderPlace, - Gateway\Exception\VippsException, - Gateway\Transaction\TransactionBuilder, - Gateway\Transaction\Transaction, - Model\QuoteLocator + Api\CommandManagerInterface, Gateway\Exception\MerchantException, Gateway\Request\Initiate\MerchantDataBuilder, + Model\OrderLocator, Model\OrderPlace, Gateway\Transaction\TransactionBuilder, Model\QuoteLocator }; use Magento\Quote\{ Api\Data\CartInterface, Api\CartRepositoryInterface, Model\Quote @@ -151,12 +138,10 @@ public function execute() $order = $this->getOrder(); if (!$order) { - $transaction = $this->getPaymentDetails(); - $order = $this->placeOrder($quote, $transaction); + $order = $this->placeOrder($quote); } $this->updateCheckoutSession($quote, $order); - /** @var ZendResponse $result */ $resultRedirect->setPath('checkout/onepage/success', ['_secure' => true]); } catch (LocalizedException $e) { @@ -239,55 +224,43 @@ private function getOrder() } /** - * Get payment details from vipps + * @param CartInterface $quote * - * @return Transaction + * @return OrderInterface|null + * @throws CouldNotSaveException * @throws LocalizedException - * @throws MerchantException * @throws NoSuchEntityException - * @throws VippsException + * @throws InputException */ - private function getPaymentDetails() + private function placeOrder(CartInterface $quote) { try { - $response = $this->commandManager - ->getOrderStatus($this->getRequest()->getParam('order_id')); - - return $this->transactionBuilder->setData($response)->build(); + $response = $this->commandManager->getOrderStatus( + $this->getRequest()->getParam('order_id') + ); + $transaction = $this->transactionBuilder->setData($response)->build(); + if ($transaction->isTransactionAborted()) { + $this->restoreQuote(); + } + $order = $this->orderPlace->execute($quote, $transaction); + if (!$order) { + throw new LocalizedException( + __('Couldn\'t get information about order status right now. Please contact a store administrator.') + ); + } + return $order; } catch (MerchantException $e) { //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { $this->restoreQuote(); - throw new LocalizedException(__('Your order was canceled in Vipps.')); + } else { + throw $e; } - throw $e; } } /** - * @param CartInterface $quote - * @param Transaction $transaction - * - * @return OrderInterface|null - * @throws CouldNotSaveException * @throws LocalizedException - * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException - */ - private function placeOrder(CartInterface $quote, Transaction $transaction) - { - if ($transaction->isTransactionAborted()) { - $this->restoreQuote(); - throw new LocalizedException(__('Your order was canceled in Vipps.')); - } - $order = $this->orderPlace->execute($quote, $transaction); - if (!$order) { - throw new LocalizedException(__('Couldn\'t get information about order status right now. Please contact a store administrator.')); //@codingStandardsIgnoreLine - } - return $order; - } - - /** * @throws NoSuchEntityException */ private function restoreQuote() @@ -301,6 +274,7 @@ private function restoreQuote() $this->checkoutSession->setLastQuoteId($quote->getId()); $this->checkoutSession->replaceQuote($quote); + throw new LocalizedException(__('Your order was canceled in Vipps.')); } /** diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index e67d2b1a..7389e6aa 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -15,10 +15,8 @@ */ namespace Vipps\Payment\Cron; -use Magento\Framework\Exception\CouldNotSaveException; -use Magento\Framework\Exception\NoSuchEntityException; -use Magento\Quote\Api\CartRepositoryInterface; -use Magento\Quote\Api\Data\CartInterface; +use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; +use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; use Magento\Sales\Api\Data\OrderInterface; use Vipps\Payment\{ @@ -115,9 +113,17 @@ public function execute() foreach ($quoteCollection as $quote) { try { - $transaction = $this->getPaymentDetails($quote); + $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); + $transaction = $this->transactionBuilder->setData($response)->build(); $this->placeOrder($quote, $transaction); - } catch (\Exception $e) { + } catch (MerchantException $e) { + //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine + if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { + $this->cancelQuote($quote); + } else { + $this->logger->critical($e->getMessage()); + } + } catch (\Throwable $e) { $this->logger->critical($e->getMessage()); } finally { usleep(1000000); //delay for 1 second @@ -127,33 +133,6 @@ public function execute() } while ($currentPage <= $quoteCollection->getLastPageNumber()); } - /** - * Get payment details from vipps - * - * @param CartInterface $quote - * - * @return Transaction - * @throws MerchantException - * @throws VippsException - */ - private function getPaymentDetails(CartInterface $quote) - { - try { - $response = $this->commandManager - ->getOrderStatus($quote->getReservedOrderId()); - - return $this->transactionBuilder->setData($response)->build(); - } catch (MerchantException $e) { - //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine - if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { - $this->cancelQuote($quote); - } else { - throw $e; - } - } - return null; - } - /** * @param CartInterface $quote * @param Transaction $transaction @@ -161,7 +140,9 @@ private function getPaymentDetails(CartInterface $quote) * @return OrderInterface|null * @throws CouldNotSaveException * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\InputException + * @throws VippsException + * @throws AlreadyExistsException + * @throws InputException */ private function placeOrder(CartInterface $quote, Transaction $transaction) { diff --git a/Gateway/Command/CommandManager.php b/Gateway/Command/CommandManager.php index 0f861871..64a90c96 100644 --- a/Gateway/Command/CommandManager.php +++ b/Gateway/Command/CommandManager.php @@ -51,6 +51,8 @@ public function __construct( * @param array $arguments * * @return ResultInterface|null + * @throws CommandException + * @throws NotFoundException */ public function initiatePayment(InfoInterface $payment, $arguments) { @@ -107,6 +109,8 @@ public function execute(CommandInterface $command, InfoInterface $payment = null * @param array $arguments * * @return ResultInterface|null + * @throws CommandException + * @throws NotFoundException */ public function executeByCode($commandCode, InfoInterface $payment = null, array $arguments = []) { diff --git a/Gateway/Transaction/Transaction.php b/Gateway/Transaction/Transaction.php index af039a3e..2dd5b186 100644 --- a/Gateway/Transaction/Transaction.php +++ b/Gateway/Transaction/Transaction.php @@ -222,11 +222,6 @@ public function isExpressCheckout() */ public function isTransactionAborted() { - $lastHistoryItem = $this->getTransactionLogHistory()->getLastItem(); - if ($lastHistoryItem && $lastHistoryItem->getOperation() == Transaction::TRANSACTION_OPERATION_CANCEL) { - return true; - } - $abortedStatuses = [ Transaction::TRANSACTION_STATUS_CANCEL, Transaction::TRANSACTION_STATUS_CANCELLED, diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index e36cbbe7..3f6ab533 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -29,8 +29,8 @@ CartRepositoryInterface, CartManagementInterface, Data\CartInterface }; use Magento\Quote\Model\Quote; -use Vipps\Payment\{ - Gateway\Transaction\Transaction +use Vipps\Payment\Gateway\{ + Transaction\Transaction, Exception\VippsException }; /** @@ -125,19 +125,25 @@ public function __construct( * @param Transaction $transaction * * @return OrderInterface|null - * @throws CouldNotSaveException - * @throws NoSuchEntityException * @throws AlreadyExistsException + * @throws CouldNotSaveException * @throws InputException + * @throws NoSuchEntityException + * @throws VippsException */ public function execute(CartInterface $quote, Transaction $transaction) { + if (!$this->canPlaceOrder($transaction)) { + return null; + } + $lockName = $this->acquireLock($quote); if (!$lockName) { return null; } + try { - $order = $this->placeOrder($quote, $transaction); + $order = $this->placeOrder($quote); if ($order) { $this->authorize($order, $transaction); } @@ -197,30 +203,19 @@ private function canPlaceOrder(Transaction $transaction) return true; } - $lastHistoryItem = $transaction->getTransactionLogHistory()->getLastItem(); - if ($lastHistoryItem && $lastHistoryItem->getOperation() == Transaction::TRANSACTION_OPERATION_RESERVE - && $lastHistoryItem->isOperationSuccess() - ) { - return true; - } - return false; } /** * @param CartInterface|Quote $quote - * @param Transaction $transaction * * @return OrderInterface|null * @throws CouldNotSaveException * @throws NoSuchEntityException + * @throws VippsException */ - private function placeOrder(CartInterface $quote, Transaction $transaction) + private function placeOrder(CartInterface $quote) { - if (!$this->canPlaceOrder($transaction)) { - return null; - } - $reservedOrderId = $quote->getReservedOrderId(); if (!$reservedOrderId) { return null; @@ -231,7 +226,8 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) return $order; } - $this->quoteUpdater->execute($quote, $transaction); + //this is used only for express checkout + $this->quoteUpdater->execute($quote); /** @var Quote $quote */ $quote = $this->cartRepository->get($quote->getId()); diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index 1cc4ddae..a93dbc33 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -15,9 +15,15 @@ */ namespace Vipps\Payment\Model; -use Magento\Quote\{Api\CartRepositoryInterface, Model\Quote, Model\Quote\Address}; +use Magento\Quote\{ + Api\CartRepositoryInterface, Api\Data\CartInterface, Model\Quote, Model\Quote\Address +}; use Magento\Braintree\Model\Paypal\Helper\AbstractHelper; -use Vipps\Payment\Gateway\Transaction\{ShippingDetails, Transaction}; +use Vipps\Payment\Gateway\Command\PaymentDetailsProvider; +use Vipps\Payment\Gateway\Exception\VippsException; +use Vipps\Payment\Gateway\Transaction\{ + ShippingDetails, Transaction, TransactionBuilder +}; /** * Class QuoteUpdater @@ -30,44 +36,64 @@ class QuoteUpdater extends AbstractHelper */ private $cartRepository; + /** + * @var PaymentDetailsProvider + */ + private $paymentDetailsProvider; + + /** + * @var TransactionBuilder + */ + private $transactionBuilder; + /** * QuoteUpdater constructor. * * @param CartRepositoryInterface $cartRepository + * @param PaymentDetailsProvider $paymentDetailsProvider + * @param TransactionBuilder $transactionBuilder */ public function __construct( - CartRepositoryInterface $cartRepository + CartRepositoryInterface $cartRepository, + PaymentDetailsProvider $paymentDetailsProvider, + TransactionBuilder $transactionBuilder ) { $this->cartRepository = $cartRepository; + $this->paymentDetailsProvider = $paymentDetailsProvider; + $this->transactionBuilder = $transactionBuilder; } /** - * Method to update Quote data. + * @param CartInterface $quote * - * @param Quote $quote - * @param Transaction $transaction + * @return bool|CartInterface|Quote + * @throws VippsException */ - public function execute(Quote $quote, Transaction $transaction) + public function execute(CartInterface $quote) { - if ($transaction->isExpressCheckout()) { - $payment = $quote->getPayment(); - $payment->setMethod('vipps'); - - $quote->setMayEditShippingAddress(false); - $quote->setMayEditShippingMethod(true); - - $this->updateQuoteAddress($quote, $transaction); - $this->disabledQuoteAddressValidation($quote); - - /** - * Unset shipping assignment to prevent from saving / applying outdated data - * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment - */ - if ($quote->getExtensionAttributes()) { - $quote->getExtensionAttributes()->setShippingAssignments(null); - } - $this->cartRepository->save($quote); + $response = $this->paymentDetailsProvider->get(['orderId' => $quote->getReservedOrderId()]); + $transaction = $this->transactionBuilder->setData($response)->build(); + if (!$transaction->isExpressCheckout()) { + return false; + } + $payment = $quote->getPayment(); + $payment->setMethod('vipps'); + + $quote->setMayEditShippingAddress(false); + $quote->setMayEditShippingMethod(true); + + $this->updateQuoteAddress($quote, $transaction); + $this->disabledQuoteAddressValidation($quote); + + /** + * Unset shipping assignment to prevent from saving / applying outdated data + * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment + */ + if ($quote->getExtensionAttributes()) { + $quote->getExtensionAttributes()->setShippingAssignments(null); } + $this->cartRepository->save($quote); + return $quote; } /** From 555beb7e9317ac9b649727836c4f81716792dd41 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 17 Aug 2018 06:40:29 +0000 Subject: [PATCH 007/121] Jenkinsfile created online with Bitbucket --- Jenkinsfile | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Jenkinsfile diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..a1992cb9 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,27 @@ +#!groovy + +@Library('platform-jenkins-pipeline') _ + +pipeline { + agent { label 'magento2' } + + stages { + stage('PHP Mess Detector') { + steps { + sh 'composer install --no-ansi' + sh './vendor/phpmd/phpmd/src/bin/phpmd . text phpmd.xml' + } + } + stage('Build Module') { + steps { + buildModule('magento2-module') + } + } + } + + post { + always { + sendNotifications() + } + } +} From b7a8a561109da0918d5f16d249145bafd796f6f0 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 24 Aug 2018 02:08:05 +0200 Subject: [PATCH 008/121] ENKL-251: Critical: Not possible to buy products with Klarna or Vipps --- Cron/FetchOrderFromVipps.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 7389e6aa..03f3da2c 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -15,6 +15,7 @@ */ namespace Vipps\Payment\Cron; +use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; @@ -70,6 +71,11 @@ class FetchOrderFromVipps */ private $cartRepository; + /** + * @var StoreManagerInterface + */ + private $storeManager; + /** * FetchOrderFromVipps constructor. * @@ -79,6 +85,7 @@ class FetchOrderFromVipps * @param OrderPlace $orderManagement * @param CartRepositoryInterface $cartRepository * @param LoggerInterface $logger + * @param StoreManagerInterface $storeManager */ public function __construct( CollectionFactory $quoteCollectionFactory, @@ -86,7 +93,8 @@ public function __construct( TransactionBuilder $transactionBuilder, OrderPlace $orderManagement, CartRepositoryInterface $cartRepository, - LoggerInterface $logger + LoggerInterface $logger, + StoreManagerInterface $storeManager ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; @@ -94,6 +102,7 @@ public function __construct( $this->orderPlace = $orderManagement; $this->cartRepository = $cartRepository; $this->logger = $logger; + $this->storeManager = $storeManager; } /** @@ -113,6 +122,7 @@ public function execute() foreach ($quoteCollection as $quote) { try { + $this->storeManager->setCurrentStore($quote->getStore()->getId()); $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); $transaction = $this->transactionBuilder->setData($response)->build(); $this->placeOrder($quote, $transaction); @@ -124,7 +134,7 @@ public function execute() $this->logger->critical($e->getMessage()); } } catch (\Throwable $e) { - $this->logger->critical($e->getMessage()); + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); } finally { usleep(1000000); //delay for 1 second } From c457f65e30649b9e37c16188baa3475f0f1c419d Mon Sep 17 00:00:00 2001 From: vagrant Date: Mon, 27 Aug 2018 15:19:17 +0200 Subject: [PATCH 009/121] VIPPS-152: Move Vipps configuration out from PayPal section --- etc/adminhtml/system.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index e22de0c2..f6f3014b 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -18,13 +18,14 @@
- + + + complex vipps-section Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment payment/vipps/active - recommended_solutions Magento\Config\Model\Config\Source\Yesno @@ -92,6 +93,7 @@ +
From b2a6e7a4a01131719c3c9ce9ef7da3c11dc568e1 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Mon, 27 Aug 2018 17:14:22 +0200 Subject: [PATCH 010/121] ENKL-252-1: Added order amount validation --- Model/OrderPlace.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 3f6ab533..07229174 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -292,4 +292,6 @@ private function notify($order) $this->orderManagement->notify($order->getEntityId()); } } + + } From 33ac6513ddf68e456646f4ebfc632d16a9eb14e5 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Mon, 27 Aug 2018 17:17:14 +0200 Subject: [PATCH 011/121] Added order amount validation --- Model/OrderPlace.php | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 07229174..d2eeae3c 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -143,7 +143,7 @@ public function execute(CartInterface $quote, Transaction $transaction) } try { - $order = $this->placeOrder($quote); + $order = $this->placeOrder($quote, $transaction); if ($order) { $this->authorize($order, $transaction); } @@ -208,13 +208,14 @@ private function canPlaceOrder(Transaction $transaction) /** * @param CartInterface|Quote $quote + * @param Transaction $transaction * * @return OrderInterface|null * @throws CouldNotSaveException * @throws NoSuchEntityException * @throws VippsException */ - private function placeOrder(CartInterface $quote) + private function placeOrder(CartInterface $quote, Transaction $transaction) { $reservedOrderId = $quote->getReservedOrderId(); if (!$reservedOrderId) { @@ -238,6 +239,7 @@ private function placeOrder(CartInterface $quote) // set quote active, collect totals and place order $quote->setIsActive(true); $quote->collectTotals(); + $this->validateAmount($quote, $transaction); $orderId = $this->cartManagement->placeOrder($quote->getId()); $quote->setReservedOrderId(null); @@ -293,5 +295,16 @@ private function notify($order) } } - + /** + * Check if reserved Order amount in vipps is he same as in Magento. + * + * @param CartInterface|Quote $quote + * @param Transaction $transaction + */ + private function validateAmount(CartInterface $quote, Transaction $transaction) + { + if ($quote->getGrandTotal() != $transaction->getTransactionInfo()->getAmount()) { + throw new LocalizedException(__("Reserved amount in Vipps is not equal to order amount.")); + } + } } From 1c6c6e473fa18c8b52a17db051db7fe5a761b474 Mon Sep 17 00:00:00 2001 From: Volodymyr Sevostianov Date: Mon, 27 Aug 2018 17:39:53 +0200 Subject: [PATCH 012/121] VIPPS-152: Move Vipps configuration out from PayPal section --- etc/adminhtml/system.xml | 140 +++++++++++++++++++-------------------- 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index f6f3014b..9e74b2ab 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -19,80 +19,80 @@
- - - - complex vipps-section - Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment - payment/vipps/active - - - Magento\Config\Model\Config\Source\Yesno - payment/vipps/active - - - - - - Link to documentation with vipps configuration details - Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint - - - - 1 - Magento\Config\Block\System\Config\Form\Fieldset - - - Vipps\Payment\Model\Adminhtml\Source\Environment - payment/vipps/environment - - - - Magento\Config\Model\Config\Source\Yesno - payment/vipps/debug - - - + + + + complex vipps-section + Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment + payment/vipps/active + + Magento\Config\Model\Config\Source\Yesno - payment/vipps/profiling - - - - payment/vipps/merchant_serial_number - Magento\Config\Model\Config\Backend\Encrypted - - - - payment/vipps/client_id - Magento\Config\Model\Config\Backend\Encrypted - - - - payment/vipps/client_secret - Magento\Config\Model\Config\Backend\Encrypted - - - - - - - This can be found in User Profile page on Merchant developer portal after merchant account is created. - payment/vipps/subscription_key1 - Magento\Config\Model\Config\Backend\Encrypted - - - - - - - This can be found in User Profile page on Merchant developer portal after merchant account is created. - payment/vipps/subscription_key2 - Magento\Config\Model\Config\Backend\Encrypted + payment/vipps/active + + + + + Link to documentation with vipps configuration details + Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint + + + + 1 + Magento\Config\Block\System\Config\Form\Fieldset + + + Vipps\Payment\Model\Adminhtml\Source\Environment + payment/vipps/environment + + + + Magento\Config\Model\Config\Source\Yesno + payment/vipps/debug + + + + Magento\Config\Model\Config\Source\Yesno + payment/vipps/profiling + + + + payment/vipps/merchant_serial_number + Magento\Config\Model\Config\Backend\Encrypted + + + + payment/vipps/client_id + Magento\Config\Model\Config\Backend\Encrypted + + + + payment/vipps/client_secret + Magento\Config\Model\Config\Backend\Encrypted + + + + + + + This can be found in User Profile page on Merchant developer portal after merchant account is created. + payment/vipps/subscription_key1 + Magento\Config\Model\Config\Backend\Encrypted + + + + + + + This can be found in User Profile page on Merchant developer portal after merchant account is created. + payment/vipps/subscription_key2 + Magento\Config\Model\Config\Backend\Encrypted + + + - -
From 02361d59f549d000a18a87b6bc6bb837b758f086 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Tue, 28 Aug 2018 09:47:46 +0200 Subject: [PATCH 013/121] ENKL-252: Order amount is different in Vipps and Magento when using Express Checkout --- Model/OrderPlace.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index d2eeae3c..b88438e3 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -296,7 +296,7 @@ private function notify($order) } /** - * Check if reserved Order amount in vipps is he same as in Magento. + * Check if reserved Order amount in vipps is the same as in Magento. * * @param CartInterface|Quote $quote * @param Transaction $transaction @@ -304,7 +304,7 @@ private function notify($order) private function validateAmount(CartInterface $quote, Transaction $transaction) { if ($quote->getGrandTotal() != $transaction->getTransactionInfo()->getAmount()) { - throw new LocalizedException(__("Reserved amount in Vipps is not equal to order amount.")); + throw new LocalizedException(__("Reserved amount in Vipps is not equal to order amount.")); } } } From db054b4f07056815697d3abf8ea529e959400ab4 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 28 Aug 2018 14:14:49 +0200 Subject: [PATCH 014/121] VIPPS-153: Set currect store for quote in cron --- Cron/FetchOrderFromVipps.php | 63 +++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 03f3da2c..740e8c5a 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -107,40 +107,45 @@ public function __construct( /** * Create orders from Vipps that are not created in Magento yet + * + * @throws NoSuchEntityException */ public function execute() { - $currentPage = 1; - do { - $quoteCollection = $this->createCollection($currentPage); - - $this->logger->debug(sprintf( - 'Fetched payment details, page: "%s", quotes: "%s"', - $currentPage, - $quoteCollection->count() //@codingStandardsIgnoreLine - )); - - foreach ($quoteCollection as $quote) { - try { - $this->storeManager->setCurrentStore($quote->getStore()->getId()); - $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); - $transaction = $this->transactionBuilder->setData($response)->build(); - $this->placeOrder($quote, $transaction); - } catch (MerchantException $e) { - //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine - if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { - $this->cancelQuote($quote); - } else { - $this->logger->critical($e->getMessage()); + try { + $currentStore = $this->storeManager->getStore()->getId(); + $currentPage = 1; + do { + $quoteCollection = $this->createCollection($currentPage); + $this->logger->debug(sprintf( + 'Fetched payment details, page: "%s", quotes: "%s"', + $currentPage, + $quoteCollection->count() //@codingStandardsIgnoreLine + )); + foreach ($quoteCollection as $quote) { + try { + $this->storeManager->setCurrentStore($quote->getStore()->getId()); + $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); + $transaction = $this->transactionBuilder->setData($response)->build(); + $this->placeOrder($quote, $transaction); + } catch (MerchantException $e) { + //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine + if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { + $this->cancelQuote($quote); + } else { + $this->logger->critical($e->getMessage()); + } + } catch (\Throwable $e) { + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); + } finally { + usleep(1000000); //delay for 1 second } - } catch (\Throwable $e) { - $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); - } finally { - usleep(1000000); //delay for 1 second } - } - $currentPage++; - } while ($currentPage <= $quoteCollection->getLastPageNumber()); + $currentPage++; + } while ($currentPage <= $quoteCollection->getLastPageNumber()); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } } /** From 6e5ae9727b083b602a419cfe6a76904ad4c6f27a Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Tue, 28 Aug 2018 14:59:30 +0200 Subject: [PATCH 015/121] Vipps-154: Wrong table new when prefix specified --- Model/TokenProvider.php | 6 +++--- Plugin/Config/Model/Config.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Model/TokenProvider.php b/Model/TokenProvider.php index 350b5b32..876008ab 100644 --- a/Model/TokenProvider.php +++ b/Model/TokenProvider.php @@ -205,7 +205,7 @@ private function loadTokenRecord() if (!$this->jwtRecord) { $connection = $this->resourceConnection->getConnection(); $select = $connection->select(); //@codingStandardsIgnoreLine - $select->from($connection->getTableName('vipps_payment_jwt')) //@codingStandardsIgnoreLine + $select->from($this->resourceConnection->getTableName('vipps_payment_jwt')) //@codingStandardsIgnoreLine ->where('scope_id = ' . $this->getScopeId()) //@codingStandardsIgnoreLine ->limit(1) //@codingStandardsIgnoreLine ->order("token_id DESC"); //@codingStandardsIgnoreLine @@ -230,14 +230,14 @@ private function refreshJwt($jwt) $this->jwtRecord = array_merge($this->jwtRecord, $jwt); if (isset($this->jwtRecord['token_id'])) { $connection->update( - $connection->getTableName('vipps_payment_jwt'), + $this->resourceConnection->getTableName('vipps_payment_jwt'), $this->jwtRecord, 'token_id = ' . $this->jwtRecord['token_id'] ); } else { $this->jwtRecord['scope_id'] = $this->getScopeId(); $connection->insert( //@codingStandardsIgnoreLine - $connection->getTableName('vipps_payment_jwt'), + $this->resourceConnection->getTableName('vipps_payment_jwt'), $this->jwtRecord ); } diff --git a/Plugin/Config/Model/Config.php b/Plugin/Config/Model/Config.php index 78b8eb73..25e3ef65 100644 --- a/Plugin/Config/Model/Config.php +++ b/Plugin/Config/Model/Config.php @@ -147,7 +147,7 @@ private function deleteJwt() $connection = $this->resourceConnection->getConnection(); try { $where = 'scope_id = ' . $this->getScopeId(); - $number = $connection->delete($connection->getTableName('vipps_payment_jwt'), $where); + $number = $connection->delete($this->resourceConnection->getTableName('vipps_payment_jwt'), $where); if ($number) { $this->logger->debug(__('Deleted JWT data from database.')); } From 0884f42f3eabb90b24e0fbdd7c196ab34a72e8f2 Mon Sep 17 00:00:00 2001 From: Eduard Melnyk Date: Tue, 4 Sep 2018 16:41:59 +0200 Subject: [PATCH 016/121] Vipps Publication 1.0.7 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9657fc2c..6b8bbb33 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "magento2-module", "description": "Vipps Payment Method", "license": "proprietary", - "version": "1.0.6", + "version": "1.0.7", "require": { "magento/framework": "101.0.*", "magento/module-sales": "101.0.*", From e813f9570e2c011766220ff578a7dd1b46fe3097 Mon Sep 17 00:00:00 2001 From: Volodymyr Sevostianov Date: Thu, 6 Sep 2018 15:16:59 +0200 Subject: [PATCH 017/121] VIPPS-136: Issue when compare order amount in Vipps and Magento --- Model/OrderPlace.php | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index b88438e3..5586a20c 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -18,6 +18,8 @@ use Magento\Framework\Exception\{ CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException }; +use Magento\Framework\Exception\LocalizedException; +use Magento\Payment\Helper\Formatter; use Magento\Sales\Api\{ OrderManagementInterface, Data\OrderInterface, OrderRepositoryInterface }; @@ -40,6 +42,8 @@ */ class OrderPlace { + use Formatter; + /** * @var OrderRepositoryInterface */ @@ -303,8 +307,13 @@ private function notify($order) */ private function validateAmount(CartInterface $quote, Transaction $transaction) { - if ($quote->getGrandTotal() != $transaction->getTransactionInfo()->getAmount()) { - throw new LocalizedException(__("Reserved amount in Vipps is not equal to order amount.")); + $quoteAmount = $this->formatPrice($quote->getGrandTotal()) * 100; + $vippsAmount = $transaction->getTransactionInfo()->getAmount(); + + if ($quoteAmount != $vippsAmount) { + throw new LocalizedException( + __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $quoteAmount, $vippsAmount) + ); } } } From 0d535383179d5dfb0c27aca502fd1e896eda98db Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Wed, 10 Oct 2018 14:11:47 +0200 Subject: [PATCH 018/121] VIPPS-157: Move configurations from website level to store level --- Model/TokenProvider.php | 14 +++++- Setup/UpgradeSchema.php | 52 +++++++++++++++++++++++ etc/adminhtml/system.xml | 18 ++++---- etc/adminhtml/system/express_checkout.xml | 4 +- etc/module.xml | 2 +- 5 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 Setup/UpgradeSchema.php diff --git a/Model/TokenProvider.php b/Model/TokenProvider.php index 876008ab..02672e1f 100644 --- a/Model/TokenProvider.php +++ b/Model/TokenProvider.php @@ -206,7 +206,8 @@ private function loadTokenRecord() $connection = $this->resourceConnection->getConnection(); $select = $connection->select(); //@codingStandardsIgnoreLine $select->from($this->resourceConnection->getTableName('vipps_payment_jwt')) //@codingStandardsIgnoreLine - ->where('scope_id = ' . $this->getScopeId()) //@codingStandardsIgnoreLine + ->where('scope = ?', $this->getScopeType()) // @codingStandardsIgnoreLine + ->where('scope_id = ?', $this->getScopeId()) //@codingStandardsIgnoreLine ->limit(1) //@codingStandardsIgnoreLine ->order("token_id DESC"); //@codingStandardsIgnoreLine $this->jwtRecord = $connection->fetchRow($select) ?: []; //@codingStandardsIgnoreLine @@ -235,6 +236,7 @@ private function refreshJwt($jwt) 'token_id = ' . $this->jwtRecord['token_id'] ); } else { + $this->jwtRecord['scope'] = $this->getScopeType(); $this->jwtRecord['scope_id'] = $this->getScopeId(); $connection->insert( //@codingStandardsIgnoreLine $this->resourceConnection->getTableName('vipps_payment_jwt'), @@ -258,6 +260,16 @@ private function getScopeId() return $this->scopeResolver->getScope()->getId(); } + /** + * Return current scope type. + * + * @return string + */ + private function getScopeType() + { + return $this->scopeResolver->getScope()->getScopeType(); + } + /** * Method to validate JWT token. * diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php new file mode 100644 index 00000000..5bd0dd09 --- /dev/null +++ b/Setup/UpgradeSchema.php @@ -0,0 +1,52 @@ +startSetup(); + + if (version_compare($context->getVersion(), '1.1.0', '<')) { + $table = $installer->getConnection()->addColumn( + $installer->getTable('vipps_payment_jwt'), + 'scope', + [ + 'type' => Table::TYPE_TEXT, + 'length' => 8, + 'after' => 'token_id', + 'nullable' => false, + 'default' => 'default', + 'comment' => 'Scope' + ] + ); + } + + $installer->endSetup(); + } +} diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 9e74b2ab..cafe0185 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -18,7 +18,7 @@
- + @@ -26,7 +26,7 @@ complex vipps-section Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Payment payment/vipps/active - + Magento\Config\Model\Config\Source\Yesno payment/vipps/active @@ -38,36 +38,36 @@ Link to documentation with vipps configuration details Magento\Paypal\Block\Adminhtml\System\Config\Fieldset\Hint - + 1 Magento\Config\Block\System\Config\Form\Fieldset - + Vipps\Payment\Model\Adminhtml\Source\Environment payment/vipps/environment - + Magento\Config\Model\Config\Source\Yesno payment/vipps/debug - + Magento\Config\Model\Config\Source\Yesno payment/vipps/profiling - + payment/vipps/merchant_serial_number Magento\Config\Model\Config\Backend\Encrypted - + payment/vipps/client_id Magento\Config\Model\Config\Backend\Encrypted - + payment/vipps/client_secret Magento\Config\Model\Config\Backend\Encrypted diff --git a/etc/adminhtml/system/express_checkout.xml b/etc/adminhtml/system/express_checkout.xml index bad7b442..4d831923 100644 --- a/etc/adminhtml/system/express_checkout.xml +++ b/etc/adminhtml/system/express_checkout.xml @@ -18,7 +18,7 @@ - + Magento\Config\Model\Config\Source\Yesno payment/vipps/express_checkout @@ -28,7 +28,7 @@ - + payment/vipps/checkout_cart_display Magento\Config\Model\Config\Source\Yesno diff --git a/etc/module.xml b/etc/module.xml index 9bae27ad..7087d9a5 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + From ac4f21a5c327aaf2483019fd1011427154f791ef Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Thu, 11 Oct 2018 16:28:46 +0200 Subject: [PATCH 019/121] VIPPS-158: Improve JWT token regeneration proccess --- Gateway/Http/Client/Curl.php | 77 ++++++++++++++++++++++------------ Model/OrderPlace.php | 2 +- Plugin/Config/Model/Config.php | 17 +++++++- Setup/UpgradeSchema.php | 4 +- 4 files changed, 69 insertions(+), 31 deletions(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index 76706d41..627317ae 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -57,6 +57,12 @@ class Curl implements ClientInterface */ private $logger; + /** + * HTTP Unauthorized Error Response code. + * @var string + */ + const HTTP_UNAUTHORIZED = 401; + /** * Curl constructor. * @@ -89,41 +95,58 @@ public function __construct( public function placeRequest(TransferInterface $transfer) { try { - $adapter = null; - /** @var MagentoCurl $adapter */ - $adapter = $this->adapterFactory->create(); - $options = $this->getBasicOptions(); - - if ($transfer->getMethod() === Request::METHOD_PUT) { - $options = $options + - [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CUSTOMREQUEST => Request::METHOD_PUT, - CURLOPT_POSTFIELDS => $this->jsonEncoder->encode($transfer->getBody()) - ]; + $response = $this->place($transfer); + if ($response->getStatusCode() == self::HTTP_UNAUTHORIZED) { + $this->tokenProvider->regenerate(); + $response = $this->place($transfer); } - $adapter->setOptions($options); - - // send request - $adapter->write( - $transfer->getMethod(), - $transfer->getUri(), - '1.1', - $this->getHeaders($transfer->getHeaders()), - $this->jsonEncoder->encode($transfer->getBody()) - ); - - $responseSting = $adapter->read(); - $response = ZendResponse::fromString($responseSting); + return ['response' => $response]; } catch (\Throwable $t) { $this->logger->critical($t->__toString()); throw new \Exception($t->getMessage(), $t->getCode(), $t); //@codingStandardsIgnoreLine - } finally { - $adapter ? $adapter->close() : null; } } + /** + * @param TransferInterface $transfer + * + * @return ZendResponse + * @throws AuthenticationException + */ + private function place(TransferInterface $transfer) + { + $adapter = null; + /** @var MagentoCurl $adapter */ + $adapter = $this->adapterFactory->create(); + $options = $this->getBasicOptions(); + + if ($transfer->getMethod() === Request::METHOD_PUT) { + $options = $options + + [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => Request::METHOD_PUT, + CURLOPT_POSTFIELDS => $this->jsonEncoder->encode($transfer->getBody()) + ]; + } + $adapter->setOptions($options); + + // send request + $adapter->write( + $transfer->getMethod(), + $transfer->getUri(), + '1.1', + $this->getHeaders($transfer->getHeaders()), + $this->jsonEncoder->encode($transfer->getBody()) + ); + + $responseSting = $adapter->read(); + $adapter ? $adapter->close() : null; + $response = ZendResponse::fromString($responseSting); + + return $response; + } + /** * @param $headers * diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 5586a20c..548fd1a7 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -310,7 +310,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) $quoteAmount = $this->formatPrice($quote->getGrandTotal()) * 100; $vippsAmount = $transaction->getTransactionInfo()->getAmount(); - if ($quoteAmount != $vippsAmount) { + if ((int)$quoteAmount != (int)$vippsAmount) { throw new LocalizedException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $quoteAmount, $vippsAmount) ); diff --git a/Plugin/Config/Model/Config.php b/Plugin/Config/Model/Config.php index 25e3ef65..27454379 100644 --- a/Plugin/Config/Model/Config.php +++ b/Plugin/Config/Model/Config.php @@ -90,7 +90,7 @@ public function aroundSave(CoreConfig $subject, \Closure $proceed) return $subject; } $groups = $subject->getGroups(); - $fields = $groups['vipps']['groups']['vipps_required']['fields'] ?? null; + $fields = $groups['vipps_section']['groups']['vipps']['groups']['vipps_required']['fields'] ?? null; if (!$fields) { return $subject; } @@ -146,7 +146,10 @@ private function deleteJwt() { $connection = $this->resourceConnection->getConnection(); try { - $where = 'scope_id = ' . $this->getScopeId(); + $where = [ + 'scope = ?' => $this->getScopeType(), + 'scope_id = ?' => $this->getScopeId() + ]; $number = $connection->delete($this->resourceConnection->getTableName('vipps_payment_jwt'), $where); if ($number) { $this->logger->debug(__('Deleted JWT data from database.')); @@ -166,4 +169,14 @@ private function getScopeId() { return $this->scopeResolver->getScope()->getId(); } + + /** + * Return current scope type. + * + * @return string + */ + private function getScopeType() + { + return $this->scopeResolver->getScope()->getScopeType(); + } } diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 5bd0dd09..15c4c65e 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -33,8 +33,9 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con $installer->startSetup(); if (version_compare($context->getVersion(), '1.1.0', '<')) { + $tableName = $installer->getTable('vipps_payment_jwt'); $table = $installer->getConnection()->addColumn( - $installer->getTable('vipps_payment_jwt'), + $tableName, 'scope', [ 'type' => Table::TYPE_TEXT, @@ -45,6 +46,7 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con 'comment' => 'Scope' ] ); + $installer->getConnection()->truncateTable($tableName); } $installer->endSetup(); From 1df12bc085d28afdaa96169ec8d76b0db6fa4568 Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Thu, 11 Oct 2018 18:03:34 +0200 Subject: [PATCH 020/121] VIPPS-158: Improve JWT token regeneration proccess - PR fixes --- Gateway/Http/Client/Curl.php | 13 +++++++------ Setup/UpgradeSchema.php | 4 +--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index 627317ae..dc62d906 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -32,6 +32,13 @@ */ class Curl implements ClientInterface { + + /** + * HTTP Unauthorized Error Response code. + * @var string + */ + const HTTP_UNAUTHORIZED = 401; + /** * @var ConfigInterface */ @@ -57,12 +64,6 @@ class Curl implements ClientInterface */ private $logger; - /** - * HTTP Unauthorized Error Response code. - * @var string - */ - const HTTP_UNAUTHORIZED = 401; - /** * Curl constructor. * diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 15c4c65e..2f8c302d 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -16,9 +16,7 @@ namespace Vipps\Payment\Setup; -use Magento\Framework\Setup\SchemaSetupInterface; -use Magento\Framework\Setup\UpgradeSchemaInterface; -use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\{SchemaSetupInterface, UpgradeSchemaInterface, ModuleContextInterface}; use Magento\Framework\DB\Ddl\Table; class UpgradeSchema implements UpgradeSchemaInterface // @codingStandardsIgnoreLine From 6c8af61d103eb16137c5265cb6bbdca2b91e4247 Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Fri, 12 Oct 2018 10:22:56 +0200 Subject: [PATCH 021/121] VIPPS-158: Improve JWT token regeneration proccess - PR fixes --- Gateway/Http/Client/Curl.php | 65 ++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 36 deletions(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index dc62d906..2e30338c 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -32,13 +32,6 @@ */ class Curl implements ClientInterface { - - /** - * HTTP Unauthorized Error Response code. - * @var string - */ - const HTTP_UNAUTHORIZED = 401; - /** * @var ConfigInterface */ @@ -97,7 +90,7 @@ public function placeRequest(TransferInterface $transfer) { try { $response = $this->place($transfer); - if ($response->getStatusCode() == self::HTTP_UNAUTHORIZED) { + if ($response->getStatusCode() == ZendResponse::STATUS_CODE_401) { $this->tokenProvider->regenerate(); $response = $this->place($transfer); } @@ -117,35 +110,35 @@ public function placeRequest(TransferInterface $transfer) */ private function place(TransferInterface $transfer) { - $adapter = null; - /** @var MagentoCurl $adapter */ - $adapter = $this->adapterFactory->create(); - $options = $this->getBasicOptions(); - - if ($transfer->getMethod() === Request::METHOD_PUT) { - $options = $options + - [ - CURLOPT_RETURNTRANSFER => true, - CURLOPT_CUSTOMREQUEST => Request::METHOD_PUT, - CURLOPT_POSTFIELDS => $this->jsonEncoder->encode($transfer->getBody()) - ]; + try { + $adapter = null; + /** @var MagentoCurl $adapter */ + $adapter = $this->adapterFactory->create(); + $options = $this->getBasicOptions(); + if ($transfer->getMethod() === Request::METHOD_PUT) { + $options = $options + + [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_CUSTOMREQUEST => Request::METHOD_PUT, + CURLOPT_POSTFIELDS => $this->jsonEncoder->encode($transfer->getBody()) + ]; + } + $adapter->setOptions($options); + // send request + $adapter->write( + $transfer->getMethod(), + $transfer->getUri(), + '1.1', + $this->getHeaders($transfer->getHeaders()), + $this->jsonEncoder->encode($transfer->getBody()) + ); + $responseSting = $adapter->read(); + $response = ZendResponse::fromString($responseSting); + + return $response; + } finally { + $adapter ? $adapter->close() : null; } - $adapter->setOptions($options); - - // send request - $adapter->write( - $transfer->getMethod(), - $transfer->getUri(), - '1.1', - $this->getHeaders($transfer->getHeaders()), - $this->jsonEncoder->encode($transfer->getBody()) - ); - - $responseSting = $adapter->read(); - $adapter ? $adapter->close() : null; - $response = ZendResponse::fromString($responseSting); - - return $response; } /** From c27efcaa8b5db21bb1bffc4377f9431b310fddcf Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Fri, 12 Oct 2018 11:11:53 +0200 Subject: [PATCH 022/121] VIPPS-161: Send "consentRemovalPrefix" parameter during initiate payment request --- Controller/Payment/ConsentRemoval.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Controller/Payment/ConsentRemoval.php diff --git a/Controller/Payment/ConsentRemoval.php b/Controller/Payment/ConsentRemoval.php new file mode 100644 index 00000000..e69de29b From d4a6211ebbab32dbdcbe4c2c66e471e60feb8175 Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Fri, 12 Oct 2018 11:13:36 +0200 Subject: [PATCH 023/121] VIPPS-161: Send "consentRemovalPrefix" parameter during initiate payment request --- Controller/Payment/ConsentRemoval.php | 40 +++++++++++++++++++ .../Request/Initiate/MerchantDataBuilder.php | 8 ++++ 2 files changed, 48 insertions(+) diff --git a/Controller/Payment/ConsentRemoval.php b/Controller/Payment/ConsentRemoval.php index e69de29b..83de71dd 100644 --- a/Controller/Payment/ConsentRemoval.php +++ b/Controller/Payment/ConsentRemoval.php @@ -0,0 +1,40 @@ +resultFactory->create(ResultFactory::TYPE_JSON); + /** @var Json $result */ + $result->setHttpResponseCode(ZendResponse::STATUS_CODE_200); + + return $result; + } +} diff --git a/Gateway/Request/Initiate/MerchantDataBuilder.php b/Gateway/Request/Initiate/MerchantDataBuilder.php index 5cd84bd6..8600ab6d 100755 --- a/Gateway/Request/Initiate/MerchantDataBuilder.php +++ b/Gateway/Request/Initiate/MerchantDataBuilder.php @@ -109,6 +109,13 @@ class MerchantDataBuilder implements BuilderInterface */ private static $shippingDetailsPrefix = 'shippingDetailsPrefix'; + /** + * This is to delete Customer data according to GDPR. + * + * @var string + */ + private static $consentRemovalPrefix = 'consentRemovalPrefix'; + /** * @var UrlInterface */ @@ -174,6 +181,7 @@ public function build(array $buildSubject) 'order_id' => $quote->getReservedOrderId() ] ), + self::$consentRemovalPrefix => $this->urlBuilder->getUrl('vipps/payment/consentRemoval'), self::$isApp => false, self::PAYMENT_TYPE => $buildSubject[self::PAYMENT_TYPE], ] From c7d30362c365e5c311647895601c67af6b371dd4 Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Fri, 12 Oct 2018 11:35:17 +0200 Subject: [PATCH 024/121] VIPPS-161: Send "consentRemovalPrefix" parameter during initiate payment request --- Controller/Payment/ConsentRemoval.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Controller/Payment/ConsentRemoval.php b/Controller/Payment/ConsentRemoval.php index 83de71dd..2301ea95 100644 --- a/Controller/Payment/ConsentRemoval.php +++ b/Controller/Payment/ConsentRemoval.php @@ -17,6 +17,8 @@ namespace Vipps\Payment\Controller\Payment; use Magento\Framework\App\Action\Action; +use Magento\Framework\Controller\ResultFactory; +use Zend\Http\Response as ZendResponse; /** * Class ConsentRemoval From f7fa26b3ae570e751c72d7270fbc234c12477e18 Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Fri, 12 Oct 2018 12:40:40 +0300 Subject: [PATCH 025/121] VIPPS-161: Send "consentRemovalPrefix" parameter during initiate payment request --- Gateway/Request/Initiate/MerchantDataBuilder.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Gateway/Request/Initiate/MerchantDataBuilder.php b/Gateway/Request/Initiate/MerchantDataBuilder.php index 8600ab6d..0b7cf2be 100755 --- a/Gateway/Request/Initiate/MerchantDataBuilder.php +++ b/Gateway/Request/Initiate/MerchantDataBuilder.php @@ -110,7 +110,9 @@ class MerchantDataBuilder implements BuilderInterface private static $shippingDetailsPrefix = 'shippingDetailsPrefix'; /** - * This is to delete Customer data according to GDPR. + * Allows Vipps to send consent removal request to merchant. + * After this merchant is obliged to remove the user details from merchant system permanently, + * as per the GDPR guidelines. * * @var string */ From 7539c6c2ba43310a59822ea5aab5a20703d7941e Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 16 Oct 2018 11:35:02 +0200 Subject: [PATCH 026/121] VIPPS-162: Improvement of background processor --- Cron/FetchOrderFromVipps.php | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 740e8c5a..f3ead07c 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -18,11 +18,10 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; -use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; +use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory, Quote, Quote\Payment}; use Magento\Sales\Api\Data\OrderInterface; use Vipps\Payment\{ Api\CommandManagerInterface, - Gateway\Exception\MerchantException, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Model\OrderPlace, @@ -109,6 +108,7 @@ public function __construct( * Create orders from Vipps that are not created in Magento yet * * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\LocalizedException */ public function execute() { @@ -123,18 +123,20 @@ public function execute() $quoteCollection->count() //@codingStandardsIgnoreLine )); foreach ($quoteCollection as $quote) { + /** @var Quote $quote */ try { $this->storeManager->setCurrentStore($quote->getStore()->getId()); $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); $transaction = $this->transactionBuilder->setData($response)->build(); $this->placeOrder($quote, $transaction); - } catch (MerchantException $e) { - //@todo workaround for vipps issue with order cancellation (delete this condition after fix) //@codingStandardsIgnoreLine - if ($e->getCode() == MerchantException::ERROR_CODE_REQUESTED_ORDER_NOT_FOUND) { - $this->cancelQuote($quote); - } else { - $this->logger->critical($e->getMessage()); - } + } catch (VippsException $e) { + /** @var Payment $payment */ + $payment = $quote->getPayment(); + $payment->setAdditionalInformation('reserved_order_id', $quote->getReservedOrderId()); + $payment->setAdditionalInformation('cancel_reason_code', $e->getCode()); + $payment->setAdditionalInformation('cancel_reason_phrase', $e->getMessage()); + $this->cancelQuote($quote); + $this->logger->critical($e->getMessage()); } catch (\Throwable $e) { $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); } finally { From 0cbac76aa2a9db977d39ec1c43e2a5dd4489225b Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Tue, 16 Oct 2018 13:31:40 +0200 Subject: [PATCH 027/121] VIPPS-163: Issue with round amount --- Gateway/Command/CaptureCommand.php | 6 +++--- Gateway/Command/RefundCommand.php | 6 +++--- Gateway/Request/TransactionDataBuilder.php | 2 +- Model/OrderPlace.php | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gateway/Command/CaptureCommand.php b/Gateway/Command/CaptureCommand.php index 7437f0df..5ca87339 100644 --- a/Gateway/Command/CaptureCommand.php +++ b/Gateway/Command/CaptureCommand.php @@ -183,7 +183,7 @@ public function __construct( public function execute(array $commandSubject) { $amount = $this->subjectReader->readAmount($commandSubject); - $amount = $this->formatPrice($amount) * 100; + $amount = (int)($this->formatPrice($amount) * 100); $response = $this->paymentDetailsProvider->get($commandSubject); $transaction = $this->transactionBuilder->setData($response)->build(); @@ -219,14 +219,14 @@ private function captureBasedOnPaymentDetails($commandSubject, Transaction $tran { $payment = $this->subjectReader->readPayment($commandSubject); $amount = $this->subjectReader->readAmount($commandSubject); - $amount = $this->formatPrice($amount) * 100; + $amount = (int)($this->formatPrice($amount) * 100); $orderAdapter = $payment->getOrder(); $orderIncrementId = $orderAdapter->getOrderIncrementId(); $order = $this->orderRepository->get($orderAdapter->getId()); - $magentoTotalDue = $this->formatPrice($order->getTotalDue()) * 100; + $magentoTotalDue = (int)($this->formatPrice($order->getTotalDue()) * 100); $vippsTotalDue = $transaction->getTransactionSummary()->getRemainingAmountToCapture(); $deltaTotalDue = $magentoTotalDue - $vippsTotalDue; diff --git a/Gateway/Command/RefundCommand.php b/Gateway/Command/RefundCommand.php index 86ec1bbe..f0d8cf1a 100644 --- a/Gateway/Command/RefundCommand.php +++ b/Gateway/Command/RefundCommand.php @@ -184,7 +184,7 @@ public function __construct( public function execute(array $commandSubject) { $amount = $this->subjectReader->readAmount($commandSubject); - $amount = $this->formatPrice($amount) * 100; + $amount = (int)($this->formatPrice($amount) * 100); $response = $this->paymentDetailsProvider->get($commandSubject); $transaction = $this->transactionBuilder->setData($response)->build(); @@ -220,14 +220,14 @@ private function refundBasedOnPaymentDetails($commandSubject, Transaction $trans { $payment = $this->subjectReader->readPayment($commandSubject); $amount = $this->subjectReader->readAmount($commandSubject); - $amount = $this->formatPrice($amount) * 100; + $amount = (int)($this->formatPrice($amount) * 100); $orderAdapter = $payment->getOrder(); $orderIncrementId = $orderAdapter->getOrderIncrementId(); $order = $this->orderRepository->get($orderAdapter->getId()); - $magentoTotalRefunded = $this->formatPrice($order->getTotalRefunded()) * 100; + $magentoTotalRefunded = (int)($this->formatPrice($order->getTotalRefunded()) * 100); $vippsTotalRefunded = $transaction->getTransactionSummary()->getRefundedAmount(); $deltaTotalRefunded = $vippsTotalRefunded - $magentoTotalRefunded; diff --git a/Gateway/Request/TransactionDataBuilder.php b/Gateway/Request/TransactionDataBuilder.php index 7fd09f75..4a0c0612 100644 --- a/Gateway/Request/TransactionDataBuilder.php +++ b/Gateway/Request/TransactionDataBuilder.php @@ -83,7 +83,7 @@ public function build(array $buildSubject) $amount = $this->subjectReader->readAmount($buildSubject); if ($amount) { - $transactionData[self::$transaction][self::$amount] = $this->formatPrice($amount) * 100; + $transactionData[self::$transaction][self::$amount] = (int)($this->formatPrice($amount) * 100); } return $transactionData; diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 548fd1a7..70a37f30 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -307,10 +307,10 @@ private function notify($order) */ private function validateAmount(CartInterface $quote, Transaction $transaction) { - $quoteAmount = $this->formatPrice($quote->getGrandTotal()) * 100; - $vippsAmount = $transaction->getTransactionInfo()->getAmount(); + $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); + $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); - if ((int)$quoteAmount != (int)$vippsAmount) { + if ($quoteAmount != $vippsAmount) { throw new LocalizedException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $quoteAmount, $vippsAmount) ); From b39a30978cf545125911332287336d565587977b Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Tue, 16 Oct 2018 14:37:16 +0300 Subject: [PATCH 028/121] VIPPS-163: Issue with round amount --- Model/OrderPlace.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 70a37f30..aa8a03e6 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -310,7 +310,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); - if ($quoteAmount != $vippsAmount) { + if ($quoteAmount !== $vippsAmount) { throw new LocalizedException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $quoteAmount, $vippsAmount) ); From 2ace5d43029cf404852080e99958d0cbb8d6001f Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 16 Oct 2018 14:22:02 +0200 Subject: [PATCH 029/121] VIPPS-162: Improvement of background processor --- Gateway/Exception/ExceptionFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gateway/Exception/ExceptionFactory.php b/Gateway/Exception/ExceptionFactory.php index 5ccfeb22..e4df0579 100644 --- a/Gateway/Exception/ExceptionFactory.php +++ b/Gateway/Exception/ExceptionFactory.php @@ -95,7 +95,7 @@ private function findErrorGroupByCode($errorCode) return $groupName; } } - return false; + return VippsException::class; } /** From d57dde33a1e2c1e97d3b52ecd75e44d987fed61b Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 19 Oct 2018 15:08:42 +0300 Subject: [PATCH 030/121] VIPPS-165: Reserved amount in Vipps is not the same as order amount # Conflicts: # Gateway/Exception/ExceptionFactory.php --- Controller/Payment/Express.php | 7 +- Controller/Payment/Regular.php | 7 +- Cron/FetchOrderFromVipps.php | 58 ++++++--- Gateway/Exception/ExceptionFactory.php | 3 +- .../Request/Initiate/CustomerDataBuilder.php | 3 +- .../Initiate/InitiateBuilderInterface.php | 56 +++++++++ .../Request/Initiate/MerchantDataBuilder.php | 41 +------ .../Initiate/TransactionDataBuilder.php | 29 ++++- Gateway/Request/TransactionDataBuilder.php | 45 +------ .../Request/TransactionTextDataBuilder.php | 112 ++++++++++++++++++ Model/OrderPlace.php | 6 +- Model/TokenProvider.php | 8 +- etc/di.xml | 5 +- 13 files changed, 267 insertions(+), 113 deletions(-) create mode 100644 Gateway/Request/Initiate/InitiateBuilderInterface.php create mode 100644 Gateway/Request/TransactionTextDataBuilder.php diff --git a/Controller/Payment/Express.php b/Controller/Payment/Express.php index 827bac5a..6499ba6b 100644 --- a/Controller/Payment/Express.php +++ b/Controller/Payment/Express.php @@ -21,7 +21,9 @@ }; use Magento\Payment\Gateway\ConfigInterface; use Vipps\Payment\{ - Api\CommandManagerInterface ,Gateway\Exception\VippsException, Gateway\Request\Initiate\MerchantDataBuilder + Api\CommandManagerInterface, + Gateway\Exception\VippsException, + Gateway\Request\Initiate\InitiateBuilderInterface }; use Psr\Log\LoggerInterface; @@ -92,7 +94,8 @@ public function execute() $quote->getPayment(), [ 'amount' => $quote->getGrandTotal(), - MerchantDataBuilder::PAYMENT_TYPE => MerchantDataBuilder::EXPRESS_CHECKOUT + InitiateBuilderInterface::PAYMENT_TYPE_KEY + => InitiateBuilderInterface::PAYMENT_TYPE_EXPRESS_CHECKOUT ] ); $this->session->clearStorage(); diff --git a/Controller/Payment/Regular.php b/Controller/Payment/Regular.php index 91a0f5f2..13eb9347 100644 --- a/Controller/Payment/Regular.php +++ b/Controller/Payment/Regular.php @@ -20,7 +20,9 @@ Exception\LocalizedException,App\ResponseInterface,Session\SessionManagerInterface }; use Vipps\Payment\{ - Api\CommandManagerInterface, Gateway\Exception\VippsException, Gateway\Request\Initiate\MerchantDataBuilder + Api\CommandManagerInterface, + Gateway\Exception\VippsException, + Gateway\Request\Initiate\InitiateBuilderInterface }; use Psr\Log\LoggerInterface; @@ -80,7 +82,8 @@ public function execute() $quote->getPayment(), [ 'amount' => $quote->getGrandTotal(), - MerchantDataBuilder::PAYMENT_TYPE => MerchantDataBuilder::REGULAR_PAYMENT + InitiateBuilderInterface::PAYMENT_TYPE_KEY + => InitiateBuilderInterface::PAYMENT_TYPE_REGULAR_PAYMENT ] ); $this->session->clearStorage(); diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index f3ead07c..222dfb4a 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -27,6 +27,7 @@ Model\OrderPlace, Gateway\Transaction\TransactionBuilder }; +use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; /** @@ -125,17 +126,19 @@ public function execute() foreach ($quoteCollection as $quote) { /** @var Quote $quote */ try { + // set quote store as current store $this->storeManager->setCurrentStore($quote->getStore()->getId()); - $response = $this->commandManager->getOrderStatus($quote->getReservedOrderId()); - $transaction = $this->transactionBuilder->setData($response)->build(); - $this->placeOrder($quote, $transaction); + + // fetch order status from vipps + $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); + + if ($transaction->isTransactionAborted()) { + $this->cancelQuote($quote); + } else { + $this->processQuote($quote, $transaction); + } } catch (VippsException $e) { - /** @var Payment $payment */ - $payment = $quote->getPayment(); - $payment->setAdditionalInformation('reserved_order_id', $quote->getReservedOrderId()); - $payment->setAdditionalInformation('cancel_reason_code', $e->getCode()); - $payment->setAdditionalInformation('cancel_reason_phrase', $e->getMessage()); - $this->cancelQuote($quote); + $this->processVippsException($quote, $e); $this->logger->critical($e->getMessage()); } catch (\Throwable $e) { $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); @@ -150,6 +153,18 @@ public function execute() } } + /** + * @param $orderId + * + * @return Transaction + * @throws VippsException + */ + private function fetchOrderStatus($orderId) + { + $response = $this->commandManager->getOrderStatus($orderId); + return $this->transactionBuilder->setData($response)->build(); + } + /** * @param CartInterface $quote * @param Transaction $transaction @@ -161,13 +176,8 @@ public function execute() * @throws AlreadyExistsException * @throws InputException */ - private function placeOrder(CartInterface $quote, Transaction $transaction) + private function processQuote(CartInterface $quote, Transaction $transaction) { - if ($transaction->isTransactionAborted()) { - $this->cancelQuote($quote); - return null; - } - $order = $this->orderPlace->execute($quote, $transaction); if (!$order) { $this->logger->critical(sprintf( @@ -181,6 +191,24 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) return $order; } + /** + * @param CartInterface $quote + * @param VippsException $e + * + * @throws \Magento\Framework\Exception\LocalizedException + */ + private function processVippsException(CartInterface $quote, VippsException $e) + { + if ($e->getCode() < ZendResponse::STATUS_CODE_500) { + /** @var Payment $payment */ + $payment = $quote->getPayment(); + $payment->setAdditionalInformation('reserved_order_id', $quote->getReservedOrderId()); + $payment->setAdditionalInformation('cancel_reason_code', $e->getCode()); + $payment->setAdditionalInformation('cancel_reason_phrase', $e->getMessage()); + $this->cancelQuote($quote); + } + } + /** * Cancel quote by setting reserved_order_id to null * diff --git a/Gateway/Exception/ExceptionFactory.php b/Gateway/Exception/ExceptionFactory.php index e4df0579..3be43f24 100644 --- a/Gateway/Exception/ExceptionFactory.php +++ b/Gateway/Exception/ExceptionFactory.php @@ -74,7 +74,8 @@ public function create($errorCode, $errorMessage) ]); } $errorMessage = $this->getMessageByErrorCode($errorCode, $errorMessage); - return new $groupName(__($errorMessage), null, $errorCode); //@codingStandardsIgnoreLine + $exceptionObject = new $groupName(__($errorMessage), null, (int)$errorCode); //@codingStandardsIgnoreLine + return $exceptionObject; } /** diff --git a/Gateway/Request/Initiate/CustomerDataBuilder.php b/Gateway/Request/Initiate/CustomerDataBuilder.php index e2dd6a3d..9a49d9be 100644 --- a/Gateway/Request/Initiate/CustomerDataBuilder.php +++ b/Gateway/Request/Initiate/CustomerDataBuilder.php @@ -15,14 +15,13 @@ */ namespace Vipps\Payment\Gateway\Request\Initiate; -use Magento\Payment\Gateway\Request\BuilderInterface; use Vipps\Payment\Gateway\Request\SubjectReader; /** * Class CustomerInfo * @package Vipps\Payment\Gateway\Request\InitiateData */ -class CustomerDataBuilder implements BuilderInterface +class CustomerDataBuilder implements InitiateBuilderInterface { /** * Customer info block name diff --git a/Gateway/Request/Initiate/InitiateBuilderInterface.php b/Gateway/Request/Initiate/InitiateBuilderInterface.php new file mode 100644 index 00000000..b52a5b98 --- /dev/null +++ b/Gateway/Request/Initiate/InitiateBuilderInterface.php @@ -0,0 +1,56 @@ + $this->urlBuilder->getUrl('vipps/payment/consentRemoval'), self::$isApp => false, - self::PAYMENT_TYPE => $buildSubject[self::PAYMENT_TYPE], + self::PAYMENT_TYPE_KEY => $buildSubject[self::PAYMENT_TYPE_KEY], ] ]; - if ($buildSubject[self::PAYMENT_TYPE] == self::EXPRESS_CHECKOUT) { + if ($buildSubject[self::PAYMENT_TYPE_KEY] == self::PAYMENT_TYPE_EXPRESS_CHECKOUT) { $merchantInfo[self::$merchantInfo][self::$shippingDetailsPrefix] = $this->urlBuilder->getUrl( 'vipps/payment/shippingDetails' ); diff --git a/Gateway/Request/Initiate/TransactionDataBuilder.php b/Gateway/Request/Initiate/TransactionDataBuilder.php index fee2cb7f..bc9a9b3d 100644 --- a/Gateway/Request/Initiate/TransactionDataBuilder.php +++ b/Gateway/Request/Initiate/TransactionDataBuilder.php @@ -15,7 +15,7 @@ */ namespace Vipps\Payment\Gateway\Request\Initiate; -use Magento\Payment\Gateway\Request\BuilderInterface; +use Magento\Payment\Helper\Formatter; use Magento\Quote\Model\Quote\Payment; use Vipps\Payment\Gateway\Request\SubjectReader; @@ -24,8 +24,10 @@ * @package Vipps\Payment\Gateway\Request\InitiateData * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ -class TransactionDataBuilder implements BuilderInterface +class TransactionDataBuilder implements InitiateBuilderInterface { + use Formatter; + /** * Transaction block name * @@ -40,6 +42,13 @@ class TransactionDataBuilder implements BuilderInterface */ private static $orderId = 'orderId'; + /** + * Amount in order. 32 Bit Integer (2147483647) + * + * @var string + */ + private static $amount = 'amount'; + /** * @var SubjectReader */ @@ -67,9 +76,23 @@ public function build(array $buildSubject) $paymentDO = $this->subjectReader->readPayment($buildSubject); /** @var Payment $payment */ $payment = $paymentDO->getPayment(); + $quote = $payment->getQuote(); + + $amount = $this->subjectReader->readAmount($buildSubject); + $amount = (int)($this->formatPrice($amount) * 100); + + if ($buildSubject[self::PAYMENT_TYPE_KEY] == self::PAYMENT_TYPE_EXPRESS_CHECKOUT) { + $shippingAddress = $quote->getShippingAddress(); + $shippingAddress->setShippingMethod(null); + $quote->collectTotals(); + + $amount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); + } + return [ self::$transaction => [ - self::$orderId => $payment->getQuote()->getReservedOrderId() + self::$orderId => $quote->getReservedOrderId(), + self::$amount => $amount ] ]; } diff --git a/Gateway/Request/TransactionDataBuilder.php b/Gateway/Request/TransactionDataBuilder.php index 4a0c0612..a38ded17 100644 --- a/Gateway/Request/TransactionDataBuilder.php +++ b/Gateway/Request/TransactionDataBuilder.php @@ -40,13 +40,6 @@ class TransactionDataBuilder implements BuilderInterface */ private static $transaction = 'transaction'; - /** - * Transaction text that can be displayed to end user. Value must be less than or equal to 100 characters. - * - * @var string - */ - private static $transactionText = 'transactionText'; - /** * @var ScopeConfigInterface */ @@ -79,7 +72,7 @@ public function __construct( */ public function build(array $buildSubject) { - $transactionData[self::$transaction][self::$transactionText] = $this->getTransactionText($buildSubject); + $transactionData = []; $amount = $this->subjectReader->readAmount($buildSubject); if ($amount) { @@ -88,40 +81,4 @@ public function build(array $buildSubject) return $transactionData; } - - /** - * @param $buildSubject - * @return string - */ - private function getTransactionText($buildSubject) - { - $paymentDO = $this->subjectReader->readPayment($buildSubject); - - $storeName = $this->getStoreName(); - $text = $storeName ? __( - 'Thank you for shopping at %1.', - $storeName - ) : __( - 'Thank you for shopping.', - $storeName - ); - $transactionText[] = $text->render(); - - if ($paymentDO) { - $text = __('Order Id: %1', $paymentDO->getOrder()->getOrderIncrementId()); - $transactionText[] = $text->render(); - } - - return implode(' ', $transactionText); - } - - /** - * @return mixed - */ - private function getStoreName() - { - return $this->scopeConfig->getValue( - 'general/store_information/name' - ); - } } diff --git a/Gateway/Request/TransactionTextDataBuilder.php b/Gateway/Request/TransactionTextDataBuilder.php new file mode 100644 index 00000000..b7887120 --- /dev/null +++ b/Gateway/Request/TransactionTextDataBuilder.php @@ -0,0 +1,112 @@ +scopeConfig = $scopeConfig; + $this->subjectReader = $subjectReader; + } + + /** + * Get merchant related data for transaction request. + * + * @param array $buildSubject + * @return array + */ + public function build(array $buildSubject) + { + $transactionData[self::$transaction][self::$transactionText] = $this->getTransactionText($buildSubject); + return $transactionData; + } + + /** + * @param $buildSubject + * @return string + */ + private function getTransactionText($buildSubject) + { + $paymentDO = $this->subjectReader->readPayment($buildSubject); + + $storeName = $this->getStoreName(); + $text = $storeName ? __( + 'Thank you for shopping at %1.', + $storeName + ) : __( + 'Thank you for shopping.', + $storeName + ); + $transactionText[] = $text->render(); + + if ($paymentDO) { + $text = __('Order Id: %1', $paymentDO->getOrder()->getOrderIncrementId()); + $transactionText[] = $text->render(); + } + + return implode(' ', $transactionText); + } + + /** + * @return mixed + */ + private function getStoreName() + { + return $this->scopeConfig->getValue( + 'general/store_information/name' + ); + } +} diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index aa8a03e6..20e3971e 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -302,8 +302,10 @@ private function notify($order) /** * Check if reserved Order amount in vipps is the same as in Magento. * - * @param CartInterface|Quote $quote + * @param CartInterface $quote * @param Transaction $transaction + * + * @throws LocalizedException */ private function validateAmount(CartInterface $quote, Transaction $transaction) { @@ -312,7 +314,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) if ($quoteAmount !== $vippsAmount) { throw new LocalizedException( - __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $quoteAmount, $vippsAmount) + __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $vippsAmount, $quoteAmount) ); } } diff --git a/Model/TokenProvider.php b/Model/TokenProvider.php index 02672e1f..222a0c57 100644 --- a/Model/TokenProvider.php +++ b/Model/TokenProvider.php @@ -112,10 +112,11 @@ public function __construct( } /** - * @inheritdoc + * {@inheritdoc} * - * @return string + * @return mixed|string * @throws AuthenticationException + * @throws CouldNotSaveException */ public function get() { @@ -169,7 +170,6 @@ private function readJwt() ]; /** @var ZendClient $client */ $client = $this->httpClientFactory->create(); - $jwt = []; try { $client->setConfig(['strict' => false]); $client->setUri($this->urlResolver->getUrl(self::$endpointUrl)); @@ -187,7 +187,7 @@ private function readJwt() if (!$this->isJwtValid($jwt)) { throw new \Exception('Not valid JWT data returned from Vipps. Response: '. $response); //@codingStandardsIgnoreLine } - $this->logger->debug($response); + $this->logger->debug('Token fetched from Vipps'); } catch (\Exception $e) { $this->logger->critical($e->getMessage()); throw new AuthenticationException(__('Can\'t retrieve access token from Vipps.')); diff --git a/etc/di.xml b/etc/di.xml index 13995fcd..c1cd6ff2 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -142,7 +142,7 @@ Vipps\Payment\Gateway\Request\Initiate\MerchantDataBuilder Vipps\Payment\Gateway\Request\MerchantDataBuilder Vipps\Payment\Gateway\Request\Initiate\TransactionDataBuilder - Vipps\Payment\Gateway\Request\TransactionDataBuilder + Vipps\Payment\Gateway\Request\TransactionTextDataBuilder @@ -172,6 +172,7 @@ Vipps\Payment\Gateway\Request\GenericDataBuilder Vipps\Payment\Gateway\Request\MerchantDataBuilder Vipps\Payment\Gateway\Request\TransactionDataBuilder + Vipps\Payment\Gateway\Request\TransactionTextDataBuilder @@ -201,6 +202,7 @@ Vipps\Payment\Gateway\Request\GenericDataBuilder Vipps\Payment\Gateway\Request\MerchantDataBuilder Vipps\Payment\Gateway\Request\TransactionDataBuilder + Vipps\Payment\Gateway\Request\TransactionTextDataBuilder @@ -282,6 +284,7 @@ Vipps\Payment\Gateway\Request\GenericDataBuilder Vipps\Payment\Gateway\Request\MerchantDataBuilder Vipps\Payment\Gateway\Request\TransactionDataBuilder + Vipps\Payment\Gateway\Request\TransactionTextDataBuilder From 72607f97ab8d90e6163b75901540ca22fdebcbe0 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 19 Oct 2018 14:30:36 +0200 Subject: [PATCH 031/121] Added phpmd.xml file --- .gitignore | 3 --- phpcs.xml | 11 +++++++++++ phpmd.xml | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 phpcs.xml create mode 100644 phpmd.xml diff --git a/.gitignore b/.gitignore index db76c317..d80466c4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,5 @@ vendor/ .idea/ .hg/ -phpcs.xml -phpmd.xml -phpunit.xml composer.lock Jenkinsfile diff --git a/phpcs.xml b/phpcs.xml new file mode 100644 index 00000000..1b0bf617 --- /dev/null +++ b/phpcs.xml @@ -0,0 +1,11 @@ + + + + Code Sniffer Configuration for Verktoy_PengVinIntegration + + + diff --git a/phpmd.xml b/phpmd.xml new file mode 100644 index 00000000..e7df9e41 --- /dev/null +++ b/phpmd.xml @@ -0,0 +1,56 @@ + + + + + Magento Code Check Rules + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vendor/ + From a6e1f97ce91b799e6ed6452a766766b9a3aef64b Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 19 Oct 2018 14:36:38 +0200 Subject: [PATCH 032/121] VIPPS-165: Fixed static tests --- Controller/Payment/Fallback.php | 1 + Cron/FetchOrderFromVipps.php | 1 + Setup/UpgradeSchema.php | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index 13f4e291..a9142cd0 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -36,6 +36,7 @@ /** * Class Fallback * @package Vipps\Payment\Controller\Payment + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Fallback extends Action { diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 222dfb4a..7c2d0a3e 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -33,6 +33,7 @@ /** * Class FetchOrderStatus * @package Vipps\Payment\Cron + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FetchOrderFromVipps { diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 2f8c302d..80472d72 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -32,7 +32,7 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con if (version_compare($context->getVersion(), '1.1.0', '<')) { $tableName = $installer->getTable('vipps_payment_jwt'); - $table = $installer->getConnection()->addColumn( + $installer->getConnection()->addColumn( $tableName, 'scope', [ From 6c86d93d237de41381f971250b369a0c4f0da405 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 19 Oct 2018 14:36:38 +0200 Subject: [PATCH 033/121] VIPPS-165: Fixed static tests --- Model/LockManager.php | 32 ++++++++++++++++---------------- phpcs.xml | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/Model/LockManager.php b/Model/LockManager.php index a1b1fb16..9e600223 100644 --- a/Model/LockManager.php +++ b/Model/LockManager.php @@ -29,23 +29,23 @@ class LockManager implements LockManagerInterface { /** - * @var ResourceConnection + * @var ResourceConnection */ private $resource; - + /** - * @var DeploymentConfig + * @var DeploymentConfig */ private $deploymentConfig; - + /** - * @var string + * @var string */ private $prefix; - /** + /** * Holds current lock name if set, otherwise false - * @var string|false + * @var string|false */ private $currentLock = false; @@ -85,16 +85,16 @@ public function lock(string $name, int $timeout = -1): bool * currently we support MySQL 5.6 way only. */ if ($this->currentLock) { - throw new AlreadyExistsException( - new Phrase( + throw new AlreadyExistsException(//@codingStandardsIgnoreLine + new Phrase(//@codingStandardsIgnoreLine 'Current connection is already holding lock for $1, only single lock allowed', [$this->currentLock] ) ); } - $result = (bool)$this->resource->getConnection()->query( - "SELECT GET_LOCK(?, ?);", + $result = (bool)$this->resource->getConnection()->query(//@codingStandardsIgnoreLine + "SELECT GET_LOCK(?, ?);",//@codingStandardsIgnoreLine [(string)$name, (int)$timeout] )->fetchColumn(); @@ -116,8 +116,8 @@ public function unlock(string $name): bool { $name = $this->addPrefix($name); - $result = (bool)$this->resource->getConnection()->query( - "SELECT RELEASE_LOCK(?);", + $result = (bool)$this->resource->getConnection()->query(//@codingStandardsIgnoreLine + "SELECT RELEASE_LOCK(?);",//@codingStandardsIgnoreLine [(string)$name] )->fetchColumn(); @@ -139,8 +139,8 @@ public function isLocked(string $name): bool { $name = $this->addPrefix($name); - return (bool)$this->resource->getConnection()->query( - "SELECT IS_USED_LOCK(?);", + return (bool)$this->resource->getConnection()->query(//@codingStandardsIgnoreLine + "SELECT IS_USED_LOCK(?);",//@codingStandardsIgnoreLine [(string)$name] )->fetchColumn(); } @@ -159,7 +159,7 @@ private function addPrefix(string $name): string $name = $this->getPrefix() . '|' . $name; if (strlen($name) > 64) { - throw new InputException(new Phrase('Lock name too long: %1...', [substr($name, 0, 64)])); + throw new InputException(new Phrase('Lock name too long: %1...', [substr($name, 0, 64)]));//@codingStandardsIgnoreLine } return $name; diff --git a/phpcs.xml b/phpcs.xml index 1b0bf617..60c3c01d 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -6,6 +6,20 @@ See LICENSE.txt for license details. --> Code Sniffer Configuration for Verktoy_PengVinIntegration + Api/ + Controller/ + Cron/ + Gateway/ + Model/ + Observer/ + Plugin/ + Setup/ + Ui/ + view/ + + + * + From eaf5927aff73882ee59fe8a4499f86c4db9f9019 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 19 Oct 2018 14:36:38 +0200 Subject: [PATCH 034/121] VIPPS-165: Fixed static tests --- Model/TokenProvider.php | 2 +- Test/Unit/Model/TokenProviderTest.php | 24 ++++++++++++----- phpunit.xml | 37 +++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 phpunit.xml diff --git a/Model/TokenProvider.php b/Model/TokenProvider.php index 222a0c57..554a6ae6 100644 --- a/Model/TokenProvider.php +++ b/Model/TokenProvider.php @@ -246,7 +246,7 @@ private function refreshJwt($jwt) $this->logger->debug(__('Refreshed Jwt data.')); } catch (\Exception $e) { $this->logger->critical($e->getMessage()); - throw new CouldNotSaveException(__('Can\'t save jwt data to database.')); + throw new CouldNotSaveException(__('Can\'t save jwt data to database.' . $e->getMessage())); } } diff --git a/Test/Unit/Model/TokenProviderTest.php b/Test/Unit/Model/TokenProviderTest.php index b0cb9504..34d4130b 100644 --- a/Test/Unit/Model/TokenProviderTest.php +++ b/Test/Unit/Model/TokenProviderTest.php @@ -104,11 +104,11 @@ protected function setUp() { $this->resourceConnection = $this->getMockBuilder(ResourceConnection::class) ->disableOriginalConstructor() - ->setMethods(['getConnection']) + ->setMethods(['getConnection', 'getTableName']) ->getMock(); $this->connection = $this->getMockBuilder(AdapterInterface::class) ->disableOriginalConstructor() - ->setMethods(['select', 'fetchRow']) + ->setMethods(['select', 'fetchRow', 'insert', 'update']) ->getMockForAbstractClass(); $this->select = $this->getMockBuilder(Select::class) ->disableOriginalConstructor() @@ -188,7 +188,7 @@ public function testGetWithValidTokenRecord() "access_token" => "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImlCakwxUmNx" ]; - $this->scopeResolver->expects($this->once()) + $this->scopeResolver->expects($this->any()) ->method('getScope') ->willReturn($this->scope); @@ -200,6 +200,10 @@ public function testGetWithValidTokenRecord() ->method('getConnection') ->willReturn($this->connection); + $this->resourceConnection->expects(self::once()) + ->method('getTableName') + ->willReturn('vipps_payment_jwt'); + $this->connection->expects($this->once()) ->method('fetchRow') ->willReturn($jwtRecord); @@ -225,14 +229,18 @@ public function testGetWithoutValidTokenRecord() ->method('create') ->willReturn($this->httpClient); - $this->scopeResolver->expects($this->exactly(2)) + $this->scopeResolver->expects($this->any()) ->method('getScope') ->willReturn($this->scope); - $this->resourceConnection->expects($this->exactly(2)) + $this->resourceConnection->expects($this->any()) ->method('getConnection') ->willReturn($this->connection); + $this->resourceConnection->expects(self::any()) + ->method('getTableName') + ->willReturn('vipps_payment_jwt'); + $this->connection->expects($this->once()) ->method('select') ->willReturn($this->select); @@ -241,6 +249,10 @@ public function testGetWithoutValidTokenRecord() ->method('fetchRow') ->willReturn($jwtRecord); + $this->connection->expects($this->once()) + ->method('insert') + ->willReturnSelf(); + $this->httpClient->expects($this->once()) ->method('request') ->willReturn($this->httpClientResponse); @@ -278,7 +290,7 @@ public function testGetCouldNotSaveException() ->method('select') ->willReturn($this->select); - $this->scopeResolver->expects($this->once()) + $this->scopeResolver->expects($this->any()) ->method('getScope') ->willReturn($this->scope); diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 00000000..f4942ad3 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,37 @@ + + + + + Test/Unit + + + + + + + + + + + + + From 16c99179cd4a1397af90cde612fb81082319a3de Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 19 Oct 2018 14:36:38 +0200 Subject: [PATCH 035/121] VIPPS-165: Fixed static tests --- phpcs.xml | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/phpcs.xml b/phpcs.xml index 60c3c01d..6c3c70c2 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -1,8 +1,19 @@ Code Sniffer Configuration for Verktoy_PengVinIntegration From 2ade4f4665235986af452050b36f68b9d0d21617 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 8 Nov 2018 23:44:18 +0100 Subject: [PATCH 036/121] VIPPS-133: Issue with scope resolver from cron --- Gateway/Config/Config.php | 66 +++++++++++++++++++++++++++++++++++++++ etc/adminhtml/di.xml | 2 +- etc/di.xml | 20 +++++------- etc/frontend/di.xml | 8 ++--- 4 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 Gateway/Config/Config.php diff --git a/Gateway/Config/Config.php b/Gateway/Config/Config.php new file mode 100644 index 00000000..64e2aa70 --- /dev/null +++ b/Gateway/Config/Config.php @@ -0,0 +1,66 @@ +storeManager = $storeManager; + } + + /** + * @param string $field + * @param null $storeId + * + * @return mixed + * @throws NoSuchEntityException + */ + public function getValue($field, $storeId = null) + { + if (null === $storeId) { + $storeId = $this->storeManager->getStore()->getId(); + } + return OriginConfig::getValue($field, $storeId); + } +} diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml index b5028037..3df9a8fd 100644 --- a/etc/adminhtml/di.xml +++ b/etc/adminhtml/di.xml @@ -30,7 +30,7 @@ - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config Vipps\Payment\Model\Logger diff --git a/etc/di.xml b/etc/di.xml index c1cd6ff2..e7f7c61e 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -34,32 +34,32 @@ - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config Vipps\Payment\Model\Logger - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config Vipps\Payment\Model\Logger - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config @@ -96,13 +96,7 @@ - VippsGatewayConfig - - - - - - vipps + Vipps\Payment\Gateway\Config\Config @@ -358,7 +352,7 @@ - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index 9e476877..8a55000a 100755 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -36,7 +36,7 @@ Magento\Checkout\Model\Session\Proxy Vipps\Payment\Model\Logger - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config @@ -48,7 +48,7 @@ - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config Vipps\Payment\Model\Logger @@ -62,13 +62,13 @@ - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config - VippsGatewayConfig + Vipps\Payment\Gateway\Config\Config From 85aa6e75b8a8bbbfe956c4293f66b003461c76ea Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 8 Nov 2018 23:44:18 +0100 Subject: [PATCH 037/121] VIPPS-133: Issue with scope resolver from cron --- Gateway/Config/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gateway/Config/Config.php b/Gateway/Config/Config.php index 64e2aa70..2905a8ab 100644 --- a/Gateway/Config/Config.php +++ b/Gateway/Config/Config.php @@ -15,10 +15,10 @@ */ namespace Vipps\Payment\Gateway\Config; -use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Payment\Gateway\Config\Config as OriginConfig; use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\Exception\NoSuchEntityException; +use Magento\Framework\App\Config\ScopeConfigInterface; /** * Class Config From 0f347f74e6da55c046f1a82afc9f30b25f0c77d8 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 13 Nov 2018 18:21:38 +0100 Subject: [PATCH 038/121] VIPPS-156: Vipps support ticket --- Gateway/Command/CaptureCommand.php | 2 +- Gateway/Command/RefundCommand.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gateway/Command/CaptureCommand.php b/Gateway/Command/CaptureCommand.php index 5ca87339..bb6effe9 100644 --- a/Gateway/Command/CaptureCommand.php +++ b/Gateway/Command/CaptureCommand.php @@ -193,7 +193,7 @@ public function execute(array $commandSubject) return true; } - // try to capture based on capture servise itself + // try to capture based on capture service itself if ($transaction->getTransactionSummary()->getRemainingAmountToCapture() < $amount) { throw new LocalizedException(__('Captured amount is higher then remaining amount to capture')); } diff --git a/Gateway/Command/RefundCommand.php b/Gateway/Command/RefundCommand.php index f0d8cf1a..2916e9a1 100644 --- a/Gateway/Command/RefundCommand.php +++ b/Gateway/Command/RefundCommand.php @@ -194,7 +194,7 @@ public function execute(array $commandSubject) return true; } - // try to refund based on capture servise itself + // try to refund based on refund service itself if ($transaction->getTransactionSummary()->getRemainingAmountToRefund() < $amount) { throw new LocalizedException(__('Refund amount is higher then remaining amount to refund')); } @@ -236,7 +236,7 @@ private function refundBasedOnPaymentDetails($commandSubject, Transaction $trans // It can happened if previous operation was successful in vipps // but for some reason Magento didn't get response - // Check that we are trying to refund the same amount as has been already captured in Vipps + // Check that we are trying to refund the same amount as has been already refunded in Vipps // otherwise - show an error about desync if ((int)$amount === (int)$deltaTotalRefunded) { //prepare refund response based on data from getPaymentDetails service From 08dd58ea5e3dfea3fd2cdc44dbfe693ef8c7d95c Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Wed, 21 Nov 2018 11:35:44 +0100 Subject: [PATCH 039/121] VIPPS-169: Set address data on shipping details call from Vipps. Do not update address details in quote updater. --- Model/Quote/AddressUpdater.php | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Model/Quote/AddressUpdater.php diff --git a/Model/Quote/AddressUpdater.php b/Model/Quote/AddressUpdater.php new file mode 100644 index 00000000..d9298398 --- /dev/null +++ b/Model/Quote/AddressUpdater.php @@ -0,0 +1,97 @@ +cartRepository = $cartRepository; + } + + /** + * Update quote addresses from source address. + * + * @param Quote $quote + * @param Address $sourceAddress + */ + public function fromSourceAddress(Quote $quote, Address $sourceAddress) + { + $quote->setMayEditShippingAddress(false); + + $this->updateQuoteAddresses($quote, $sourceAddress); + $this->disabledQuoteAddressValidation($quote); + + /** + * Unset shipping assignment to prevent from saving / applying outdated data + * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment + */ + if ($quote->getExtensionAttributes()) { + $quote->getExtensionAttributes()->setShippingAssignments(null); + } + $this->cartRepository->save($quote); + } + + /** + * @param Quote $quote + * @param Address $address + */ + private function updateQuoteAddresses(Quote $quote, Address $address) + { + if (!$quote->getIsVirtual()) { + $shippingAddress = $quote->getShippingAddress(); + $this->updateAddress($shippingAddress, $address); + } + + $billingAddress = $quote->getBillingAddress(); + $this->updateAddress($billingAddress, $address); + $billingAddress->setSameAsBilling(false); + } + + private function updateAddress(Address $destAddress, Address $sourceAddress) + { + $destAddress + ->setStreet($sourceAddress->getStreet()) + ->setCity($sourceAddress->getCity()) + ->setCountryId(ShippingDetails::NORWEGIAN_COUNTRY_ID) + ->setPostcode($sourceAddress->getPostcode()) + ->setSaveInAddressBook(false) + ->setSameAsBilling(true) + ->setCustomerAddressId(null); + } +} \ No newline at end of file From ef79fb49ef3409276025e83b8f0b5d60250f4ae0 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Wed, 21 Nov 2018 11:37:19 +0100 Subject: [PATCH 040/121] VIPPS-169: Set address data on shipping details call from Vipps. Do not update address details in quote updater. --- Controller/Payment/ShippingDetails.php | 12 +++++++++++- Model/Quote/AddressUpdater.php | 20 ++++++++++++++------ Model/QuoteUpdater.php | 18 ------------------ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Controller/Payment/ShippingDetails.php b/Controller/Payment/ShippingDetails.php index ba746cf2..5e4415a9 100644 --- a/Controller/Payment/ShippingDetails.php +++ b/Controller/Payment/ShippingDetails.php @@ -24,7 +24,9 @@ }; use Magento\Quote\Model\Quote; use Vipps\Payment\Gateway\Transaction\ShippingDetails as TransactionShippingDetails; -use Vipps\Payment\Model\QuoteLocator; +use Vipps\Payment\Model\{ + QuoteLocator, Quote\AddressUpdater +}; use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; @@ -64,6 +66,10 @@ class ShippingDetails extends Action * @var LoggerInterface */ private $logger; + /** + * @var AddressUpdater + */ + private $addressUpdater; /** * ShippingDetails constructor. @@ -73,6 +79,7 @@ class ShippingDetails extends Action * @param QuoteLocator $quoteLocator * @param ShipmentEstimationInterface $shipmentEstimation * @param AddressInterfaceFactory $addressFactory + * @param AddressUpdater $addressUpdater * @param Json $serializer * @param LoggerInterface $logger */ @@ -82,6 +89,7 @@ public function __construct( QuoteLocator $quoteLocator, ShipmentEstimationInterface $shipmentEstimation, AddressInterfaceFactory $addressFactory, + AddressUpdater $addressUpdater, Json $serializer, LoggerInterface $logger ) { @@ -92,6 +100,7 @@ public function __construct( $this->shipmentEstimation = $shipmentEstimation; $this->addressFactory = $addressFactory; $this->logger = $logger; + $this->addressUpdater = $addressUpdater; } /** @@ -119,6 +128,7 @@ public function execute() * As Quote is deactivated, so we need to activate it for estimating shipping methods */ $quote = $this->cartRepository->get($quote->getId()); + $this->addressUpdater->fromSourceAddress($quote, $address); $quote->setIsActive(true); $shippingMethods = $this->shipmentEstimation->estimateByExtendedAddress($quote->getId(), $address); $responseData = [ diff --git a/Model/Quote/AddressUpdater.php b/Model/Quote/AddressUpdater.php index d9298398..ac2a1f05 100644 --- a/Model/Quote/AddressUpdater.php +++ b/Model/Quote/AddressUpdater.php @@ -16,10 +16,10 @@ namespace Vipps\Payment\Model\Quote; -use Magento\Braintree\Model\Paypal\Helper\AbstractHelper; +use \Magento\Braintree\Model\Paypal\Helper\AbstractHelper; use Magento\Quote\{ - Api\CartRepositoryInterface, Api\Data\CartInterface, Model\Quote, Model\Quote\Address + Api\CartRepositoryInterface, Model\Quote, Model\Quote\Address }; use Vipps\Payment\Gateway\Transaction\ShippingDetails; @@ -68,21 +68,29 @@ public function fromSourceAddress(Quote $quote, Address $sourceAddress) } /** + * Update quote addresses from source address. + * * @param Quote $quote - * @param Address $address + * @param Address $sourceAddress */ - private function updateQuoteAddresses(Quote $quote, Address $address) + private function updateQuoteAddresses(Quote $quote, Address $sourceAddress) { if (!$quote->getIsVirtual()) { $shippingAddress = $quote->getShippingAddress(); - $this->updateAddress($shippingAddress, $address); + $this->updateAddress($shippingAddress, $sourceAddress); } $billingAddress = $quote->getBillingAddress(); - $this->updateAddress($billingAddress, $address); + $this->updateAddress($billingAddress, $sourceAddress); $billingAddress->setSameAsBilling(false); } + /** + * Update destination address from source. + * + * @param Address $destAddress + * @param Address $sourceAddress + */ private function updateAddress(Address $destAddress, Address $sourceAddress) { $destAddress diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index a93dbc33..a751d985 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -125,7 +125,6 @@ private function updateShippingAddress(Quote $quote, Transaction $transaction) $shippingAddress->setCollectShippingRates(true); $shippingAddress->setShippingMethod($shippingDetails->getShippingMethodId()); $shippingAddress->setShippingAmount($shippingDetails->getShippingCost()); - $this->updateAddressData($shippingAddress, $shippingDetails); //We do not save user address from vipps in Magento $shippingAddress->setSaveInAddressBook(false); @@ -141,7 +140,6 @@ private function updateBillingAddress(Quote $quote, Transaction $transaction) { $userDetails = $transaction->getUserDetails(); $billingAddress = $quote->getBillingAddress(); - $this->updateAddressData($billingAddress, $transaction->getShippingDetails()); $billingAddress->setLastname($userDetails->getLastName()); $billingAddress->setFirstname($userDetails->getFirstName()); @@ -152,20 +150,4 @@ private function updateBillingAddress(Quote $quote, Transaction $transaction) $billingAddress->setSameAsBilling(false); $billingAddress->unsCustomerAddressId(); } - - /** - * @param Address $address - * @param ShippingDetails $shippingDetails - */ - private function updateAddressData(Address $address, ShippingDetails $shippingDetails) - { - $address->setStreet($shippingDetails->getStreet()); - $address->setCity($shippingDetails->getCity()); - $address->setCountryId(ShippingDetails::NORWEGIAN_COUNTRY_ID); - $address->setPostcode($shippingDetails->getPostCode()); - - $address->setSaveInAddressBook(false); - $address->setSameAsBilling(true); - $address->setCustomerAddressId(null); - } } From b6b633ebc06725ad78625126ecf24c1dd19d09b0 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 22 Nov 2018 09:27:56 +0100 Subject: [PATCH 041/121] VIPPS-170: Add option "Authorize and Capture" --- Model/Adminhtml/Source/PaymentAction.php | 12 ++--- Model/OrderPlace.php | 63 ++++++++++++++++++++++-- etc/adminhtml/system.xml | 6 +++ etc/di.xml | 6 +++ 4 files changed, 78 insertions(+), 9 deletions(-) diff --git a/Model/Adminhtml/Source/PaymentAction.php b/Model/Adminhtml/Source/PaymentAction.php index 571b5a5f..8a385f92 100644 --- a/Model/Adminhtml/Source/PaymentAction.php +++ b/Model/Adminhtml/Source/PaymentAction.php @@ -25,12 +25,12 @@ class PaymentAction implements ArrayInterface /** * @var string */ - const ACTION_CAPTURE = 'capture'; + const ACTION_AUTHORIZE = 'authorize'; /** * @var string */ - const ACTION_DIRECT_CAPTURE = 'direct_capture'; + const ACTION_AUTHORIZE_CAPTURE = 'authorize_capture'; /** * Possible actions on order place @@ -41,12 +41,12 @@ public function toOptionArray() { return [ [ - 'value' => self::ACTION_CAPTURE, - 'label' => __('Capture'), + 'value' => self::ACTION_AUTHORIZE, + 'label' => __('Authorize'), ], [ - 'value' => self::ACTION_DIRECT_CAPTURE, - 'label' => __('Direct Capture'), + 'value' => self::ACTION_AUTHORIZE_CAPTURE, + 'label' => __('Authorize and Capture'), ] ]; } diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 20e3971e..9cd73dff 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -20,6 +20,7 @@ }; use Magento\Framework\Exception\LocalizedException; use Magento\Payment\Helper\Formatter; +use Magento\Payment\Gateway\ConfigInterface; use Magento\Sales\Api\{ OrderManagementInterface, Data\OrderInterface, OrderRepositoryInterface }; @@ -31,9 +32,11 @@ CartRepositoryInterface, CartManagementInterface, Data\CartInterface }; use Magento\Quote\Model\Quote; +use Magento\Store\Model\ScopeInterface; use Vipps\Payment\Gateway\{ Transaction\Transaction, Exception\VippsException }; +use Vipps\Payment\Model\Adminhtml\Source\PaymentAction; /** * Class OrderManagement @@ -89,6 +92,11 @@ class OrderPlace */ private $lockManager; + /** + * @var ConfigInterface + */ + private $config; + /** * OrderPlace constructor. * @@ -101,6 +109,7 @@ class OrderPlace * @param Processor $processor * @param QuoteUpdater $quoteUpdater * @param LockManager $lockManager + * @param ConfigInterface $config */ public function __construct( OrderRepositoryInterface $orderRepository, @@ -111,7 +120,8 @@ public function __construct( QuoteLocator $quoteLocator, Processor $processor, QuoteUpdater $quoteUpdater, - LockManager $lockManager + LockManager $lockManager, + ConfigInterface $config ) { $this->orderRepository = $orderRepository; $this->cartRepository = $cartRepository; @@ -122,6 +132,7 @@ public function __construct( $this->processor = $processor; $this->quoteUpdater = $quoteUpdater; $this->lockManager = $lockManager; + $this->config = $config; } /** @@ -132,6 +143,7 @@ public function __construct( * @throws AlreadyExistsException * @throws CouldNotSaveException * @throws InputException + * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException */ @@ -149,7 +161,14 @@ public function execute(CartInterface $quote, Transaction $transaction) try { $order = $this->placeOrder($quote, $transaction); if ($order) { - $this->authorize($order, $transaction); + $paymentAction = $this->config->getValue('vipps_payment_action'); + switch ($paymentAction) { + case PaymentAction::ACTION_AUTHORIZE_CAPTURE: + $this->capture($order, $transaction); + break; + default: + $this->authorize($order, $transaction); + } } return $order; @@ -211,11 +230,12 @@ private function canPlaceOrder(Transaction $transaction) } /** - * @param CartInterface|Quote $quote + * @param CartInterface $quote * @param Transaction $transaction * * @return OrderInterface|null * @throws CouldNotSaveException + * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException */ @@ -287,6 +307,43 @@ private function authorize(OrderInterface $order, Transaction $transaction) $this->notify($order); } + /** + * Capture + * + * @param OrderInterface $order + * @param Transaction $transaction + * + * @throws LocalizedException + */ + private function capture(OrderInterface $order, Transaction $transaction) + { + if ($order->getState() !== Order::STATE_NEW) { + return; + } + + // preconditions + $totalDue = $order->getTotalDue(); + $baseTotalDue = $order->getBaseTotalDue(); + + /** @var Payment $payment */ + $payment = $order->getPayment(); + $payment->setAmountAuthorized($totalDue); + $payment->setBaseAmountAuthorized($baseTotalDue); + + $transactionId = $transaction->getTransactionId(); + $payment->setTransactionId($transactionId); + $payment->setTransactionAdditionalInfo( + PaymentTransaction::RAW_DETAILS, + $transaction->getTransactionInfo()->getData() + ); + + // do capture + $this->processor->capture($payment, null); + $this->orderRepository->save($order); + + $this->notify($order); + } + /** * Send order conformation email if not sent * diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index cafe0185..a3a2eea0 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -57,6 +57,12 @@ Magento\Config\Model\Config\Source\Yesno payment/vipps/profiling + + + payment/vipps/vipps_payment_action + Vipps\Payment\Model\Adminhtml\Source\PaymentAction + 1 + payment/vipps/merchant_serial_number diff --git a/etc/di.xml b/etc/di.xml index e7f7c61e..0ddb17d8 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -45,6 +45,12 @@ + + + Vipps\Payment\Gateway\Config\Config + + + Vipps\Payment\Gateway\Config\Config From 97955a883fae38fd6b644078a1a313d88ee1eb1e Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 22 Nov 2018 10:36:06 +0100 Subject: [PATCH 042/121] VIPPS-170: Add option "Authorize and Capture" --- Model/OrderPlace.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 9cd73dff..f07a3e82 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -110,6 +110,8 @@ class OrderPlace * @param QuoteUpdater $quoteUpdater * @param LockManager $lockManager * @param ConfigInterface $config + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( OrderRepositoryInterface $orderRepository, From a52df9d937f3731f4c721a99f729dcdc77fa0af8 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 22 Nov 2018 11:05:57 +0100 Subject: [PATCH 043/121] VIPPS-168: Initial gdpr compliance. --- Controller/Payment/ShippingDetails.php | 9 ++- Model/Gdpr/Compliance.php | 89 ++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 Model/Gdpr/Compliance.php diff --git a/Controller/Payment/ShippingDetails.php b/Controller/Payment/ShippingDetails.php index ba746cf2..e9461f87 100644 --- a/Controller/Payment/ShippingDetails.php +++ b/Controller/Payment/ShippingDetails.php @@ -64,6 +64,10 @@ class ShippingDetails extends Action * @var LoggerInterface */ private $logger; + /** + * @var \Vipps\Model\Gdpr\Compliance + */ + private $gdprCompliance; /** * ShippingDetails constructor. @@ -82,6 +86,7 @@ public function __construct( QuoteLocator $quoteLocator, ShipmentEstimationInterface $shipmentEstimation, AddressInterfaceFactory $addressFactory, + \Vipps\Model\Gdpr\Compliance $compliance, Json $serializer, LoggerInterface $logger ) { @@ -92,6 +97,7 @@ public function __construct( $this->shipmentEstimation = $shipmentEstimation; $this->addressFactory = $addressFactory; $this->logger = $logger; + $this->gdprCompliance = $compliance; } /** @@ -151,7 +157,8 @@ public function execute() 'message' => __('An error occurred during Shipping Details processing.') ]); } finally { - $this->logger->debug($this->getRequest()->getContent()); + $compliantString = $this->gdprCompliance->process($this->getRequest()->getContent()); + $this->logger->debug($compliantString); } return $result; } diff --git a/Model/Gdpr/Compliance.php b/Model/Gdpr/Compliance.php new file mode 100644 index 00000000..2c3b1dcd --- /dev/null +++ b/Model/Gdpr/Compliance.php @@ -0,0 +1,89 @@ +serializer = $serializer; + $this->logger = $logger; + } + + /** + * @return array + */ + private function getReplacementSchema(): array + { + $schema = [ + 'addressId' => self::MODIFIER, + 'addressLine1' => self::MODIFIER, + 'addressLine2' => self::MODIFIER, + 'city' => self::MODIFIER, + 'country' => self::MODIFIER, + 'postCode' => self::MODIFIER, + 'shippingDetails' => [ + 'address' => [ + 'addressLine1' => self::MODIFIER, + 'addressLine2' => self::MODIFIER, + 'city' => self::MODIFIER, + 'country' => self::MODIFIER, + 'zipCode' => self::MODIFIER + ] + ], + 'userDetails' => [ + 'email' => self::MODIFIER, + 'firstName' => self::MODIFIER, + 'lastName' => self::MODIFIER, + 'mobileNumber' => self::MODIFIER, + 'userId' => self::MODIFIER] + ]; + + return $schema; + } + + public function process($string) + { + try { + $data = $this->serializer->unserialize($string); + + $replacementSchema = $this->getReplacementSchema(); + + $data = array_replace_recursive($data, array_intersect_key($data, $replacementSchema)); + + $string = $this->serializer->serialize($data); + } catch (\Exception $e) { + $this->logger->critical($e->getMessage()); + } + + return $string; + } +} \ No newline at end of file From e66d94b9a08c61669fbc93c77e3fe17a037324eb Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 22 Nov 2018 16:17:49 +0100 Subject: [PATCH 044/121] VIPPS-170: Add option "Authorize and Capture" --- Cron/FetchOrderFromVipps.php | 44 ++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 7c2d0a3e..18513ee8 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -135,6 +135,17 @@ public function execute() if ($transaction->isTransactionAborted()) { $this->cancelQuote($quote); + } else if ($transaction->getTransactionInfo()->getStatus() + == Transaction::TRANSACTION_STATUS_INITIATE) { + // we process quotes that have last update 15 min passed + // vipps cancel if order not approved within 5 min + // so if quote is still initiated we can 'bravely' :) cancel it + $this->cancelQuote( + $quote, + [ + 'cancel_reason_phrase' => 'expired' + ] + ); } else { $this->processQuote($quote, $transaction); } @@ -171,11 +182,12 @@ private function fetchOrderStatus($orderId) * @param Transaction $transaction * * @return OrderInterface|null + * @throws AlreadyExistsException * @throws CouldNotSaveException + * @throws InputException * @throws NoSuchEntityException * @throws VippsException - * @throws AlreadyExistsException - * @throws InputException + * @throws \Magento\Framework\Exception\LocalizedException */ private function processQuote(CartInterface $quote, Transaction $transaction) { @@ -202,11 +214,13 @@ private function processVippsException(CartInterface $quote, VippsException $e) { if ($e->getCode() < ZendResponse::STATUS_CODE_500) { /** @var Payment $payment */ - $payment = $quote->getPayment(); - $payment->setAdditionalInformation('reserved_order_id', $quote->getReservedOrderId()); - $payment->setAdditionalInformation('cancel_reason_code', $e->getCode()); - $payment->setAdditionalInformation('cancel_reason_phrase', $e->getMessage()); - $this->cancelQuote($quote); + $this->cancelQuote( + $quote, + [ + 'cancel_reason_code' => $e->getCode(), + 'cancel_reason_phrase', $e->getMessage() + ] + ); } } @@ -214,12 +228,22 @@ private function processVippsException(CartInterface $quote, VippsException $e) * Cancel quote by setting reserved_order_id to null * * @param CartInterface $quote + * @param array $additionalInformation */ - private function cancelQuote(CartInterface $quote) + private function cancelQuote(CartInterface $quote, $additionalInformation = []) { $reservedOrderId = $quote->getReservedOrderId(); - $quote->setReservedOrderId(null); + + $additionalInformation = array_merge( + $additionalInformation, + [ + 'reserved_order_id' => $reservedOrderId + ] + ); + $payment = $quote->getPayment(); + $payment->setAdditionalInformation('vipps', $additionalInformation); + $this->cartRepository->save($quote); $this->logger->debug(sprintf( @@ -249,7 +273,7 @@ private function createCollection($currentPage) ); $collection->addFieldToFilter('p.method', ['eq' => 'vipps']); $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); - $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 1800)]); + $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 900)]); $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); return $collection; } From fe2f25781e5a9e51aad1b8dba7c7e0bdca6a675d Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 22 Nov 2018 16:27:12 +0100 Subject: [PATCH 045/121] VIPPS-170: Code review fixes --- Cron/FetchOrderFromVipps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 18513ee8..91c2d771 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -139,7 +139,7 @@ public function execute() == Transaction::TRANSACTION_STATUS_INITIATE) { // we process quotes that have last update 15 min passed // vipps cancel if order not approved within 5 min - // so if quote is still initiated we can 'bravely' :) cancel it + // so if quote is still initiated we can cancel it $this->cancelQuote( $quote, [ From efadc7003fc541ad1391d9fe45769ee52dc61aec Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 22 Nov 2018 16:29:01 +0100 Subject: [PATCH 046/121] VIPPS-170: Code review fixes --- Cron/FetchOrderFromVipps.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 91c2d771..e0c0bc10 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -218,7 +218,7 @@ private function processVippsException(CartInterface $quote, VippsException $e) $quote, [ 'cancel_reason_code' => $e->getCode(), - 'cancel_reason_phrase', $e->getMessage() + 'cancel_reason_phrase' => $e->getMessage() ] ); } From 4988a38da020fc399ea664bd246797eb6eb9102d Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 22 Nov 2018 17:50:49 +0200 Subject: [PATCH 047/121] VIPPS-168: Match responses for gdpr compliance. --- Controller/Payment/Callback.php | 12 ++++- Controller/Payment/Fallback.php | 13 ++++- Controller/Payment/ShippingDetails.php | 6 ++- Model/Gdpr/Compliance.php | 67 ++++++++++++++------------ Model/Profiling/Profiler.php | 20 ++++++-- 5 files changed, 77 insertions(+), 41 deletions(-) diff --git a/Controller/Payment/Callback.php b/Controller/Payment/Callback.php index 5aead60e..5db4cb4d 100755 --- a/Controller/Payment/Callback.php +++ b/Controller/Payment/Callback.php @@ -27,7 +27,8 @@ Gateway\Request\Initiate\MerchantDataBuilder, Gateway\Transaction\TransactionBuilder, Model\OrderPlace, - Model\QuoteLocator + Model\QuoteLocator, + Model\Gdpr\Compliance }; use Magento\Quote\{ Api\Data\CartInterface, Model\Quote @@ -71,6 +72,10 @@ class Callback extends Action * @var CartInterface */ private $quote; + /** + * @var Compliance + */ + private $gdprCompliance; /** * Callback constructor. @@ -88,6 +93,7 @@ public function __construct( QuoteLocator $quoteLocator, Json $jsonDecoder, TransactionBuilder $transactionBuilder, + Compliance $compliance, LoggerInterface $logger ) { parent::__construct($context); @@ -96,6 +102,7 @@ public function __construct( $this->jsonDecoder = $jsonDecoder; $this->transactionBuilder = $transactionBuilder; $this->logger = $logger; + $this->gdprCompliance = $compliance; } /** @@ -125,7 +132,8 @@ public function execute() 'message' => __('An error occurred during callback processing.') ]); } finally { - $this->logger->debug($this->getRequest()->getContent()); + $compliant = $this->gdprCompliance->process($this->getRequest()->getContent()); + $this->logger->debug($compliant); } return $result; } diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index a9142cd0..14ed9b09 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -23,7 +23,8 @@ }; use Vipps\Payment\{ Api\CommandManagerInterface, Gateway\Exception\MerchantException, Gateway\Request\Initiate\MerchantDataBuilder, - Model\OrderLocator, Model\OrderPlace, Gateway\Transaction\TransactionBuilder, Model\QuoteLocator + Model\OrderLocator, Model\OrderPlace, Gateway\Transaction\TransactionBuilder, Model\QuoteLocator, + Model\Gdpr\Compliance }; use Magento\Quote\{ Api\Data\CartInterface, Api\CartRepositoryInterface, Model\Quote @@ -89,6 +90,10 @@ class Fallback extends Action * @var LoggerInterface */ private $logger; + /** + * @var Compliance + */ + private $gdprCompliance; /** * Fallback constructor. @@ -101,6 +106,7 @@ class Fallback extends Action * @param CartRepositoryInterface $cartRepository * @param QuoteLocator $quoteLocator * @param OrderLocator $orderLocator + * @param Compliance $compliance * @param LoggerInterface $logger */ public function __construct( @@ -112,6 +118,7 @@ public function __construct( CartRepositoryInterface $cartRepository, QuoteLocator $quoteLocator, OrderLocator $orderLocator, + Compliance $compliance, LoggerInterface $logger ) { parent::__construct($context); @@ -123,6 +130,7 @@ public function __construct( $this->quoteLocator = $quoteLocator; $this->orderLocator = $orderLocator; $this->logger = $logger; + $this->gdprCompliance = $compliance; } /** @@ -154,7 +162,8 @@ public function execute() $this->messageManager->addErrorMessage(__('An error occurred during payment status update.')); $resultRedirect->setPath('checkout/onepage/failure', ['_secure' => true]); } finally { - $this->logger->debug($this->getRequest()->getRequestString()); + $compliant = $this->gdprCompliance->process($this->getRequest()->getRequestString()); + $this->logger->debug($compliant); } return $resultRedirect; } diff --git a/Controller/Payment/ShippingDetails.php b/Controller/Payment/ShippingDetails.php index e9461f87..2cc1660f 100644 --- a/Controller/Payment/ShippingDetails.php +++ b/Controller/Payment/ShippingDetails.php @@ -23,6 +23,7 @@ CartRepositoryInterface, Data\CartInterface, ShipmentEstimationInterface, Data\AddressInterfaceFactory }; use Magento\Quote\Model\Quote; +use Vipps\Payment\Model\Gdpr\Compliance; use Vipps\Payment\Gateway\Transaction\ShippingDetails as TransactionShippingDetails; use Vipps\Payment\Model\QuoteLocator; use Zend\Http\Response as ZendResponse; @@ -65,7 +66,7 @@ class ShippingDetails extends Action */ private $logger; /** - * @var \Vipps\Model\Gdpr\Compliance + * @var Compliance */ private $gdprCompliance; @@ -77,6 +78,7 @@ class ShippingDetails extends Action * @param QuoteLocator $quoteLocator * @param ShipmentEstimationInterface $shipmentEstimation * @param AddressInterfaceFactory $addressFactory + * @param Compliance $compliance * @param Json $serializer * @param LoggerInterface $logger */ @@ -86,7 +88,7 @@ public function __construct( QuoteLocator $quoteLocator, ShipmentEstimationInterface $shipmentEstimation, AddressInterfaceFactory $addressFactory, - \Vipps\Model\Gdpr\Compliance $compliance, + Compliance $compliance, Json $serializer, LoggerInterface $logger ) { diff --git a/Model/Gdpr/Compliance.php b/Model/Gdpr/Compliance.php index 2c3b1dcd..c6a6ce18 100644 --- a/Model/Gdpr/Compliance.php +++ b/Model/Gdpr/Compliance.php @@ -14,15 +14,14 @@ * IN THE SOFTWARE. */ -namespace Vipps\Model\Gdpr; +namespace Vipps\Payment\Model\Gdpr; +use Magento\Framework\Exception\SerializationException; use Magento\Framework\Serialize\Serializer\Json; use Psr\Log\LoggerInterface; class Compliance { - const MODIFIER = 'gdprcompliance'; - /** * @var Json */ @@ -39,51 +38,59 @@ public function __construct(Json $serializer, LoggerInterface $logger) } /** + * Fields that require masking. + * * @return array */ private function getReplacementSchema(): array { $schema = [ - 'addressId' => self::MODIFIER, - 'addressLine1' => self::MODIFIER, - 'addressLine2' => self::MODIFIER, - 'city' => self::MODIFIER, - 'country' => self::MODIFIER, - 'postCode' => self::MODIFIER, - 'shippingDetails' => [ - 'address' => [ - 'addressLine1' => self::MODIFIER, - 'addressLine2' => self::MODIFIER, - 'city' => self::MODIFIER, - 'country' => self::MODIFIER, - 'zipCode' => self::MODIFIER - ] - ], - 'userDetails' => [ - 'email' => self::MODIFIER, - 'firstName' => self::MODIFIER, - 'lastName' => self::MODIFIER, - 'mobileNumber' => self::MODIFIER, - 'userId' => self::MODIFIER] + 'addressLine1' => 1, + 'addressLine2' => 1, + 'email' => 1, + 'firstName' => 1, + 'lastName' => 1, + 'mobileNumber' => 1, ]; return $schema; } - public function process($string) + /** + * Mask response fields. + * + * @param array|string $responseData + * + * @return array|string + */ + public function process($responseData) { + $wasPacked = false; + try { - $data = $this->serializer->unserialize($string); + if (\is_string($responseData)) { + $responseData = $this->serializer->unserialize($responseData); + $wasPacked = true; + } - $replacementSchema = $this->getReplacementSchema(); + if (!\is_array($responseData)) { + throw new SerializationException(__('Unserialization result is not an array')); + } - $data = array_replace_recursive($data, array_intersect_key($data, $replacementSchema)); + array_walk_recursive($responseData, function (&$item, $key, $schema) { + if (isset($schema[$key])) { + $item = str_repeat('x', \strlen($item)); + } + }, $this->getReplacementSchema()); - $string = $this->serializer->serialize($data); + if ($wasPacked) { + $responseData = $this->serializer->serialize($responseData); + } } catch (\Exception $e) { + $this->logger->critical('Gdpr compliance failed'); $this->logger->critical($e->getMessage()); } - return $string; + return $responseData; } } \ No newline at end of file diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index 24fada09..5ee2c337 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -18,11 +18,13 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Payment\Gateway\Http\TransferInterface; use Magento\Store\Model\ScopeInterface; -use Vipps\Payment\Api\Profiling\Data\ItemInterface; -use Vipps\Payment\Api\Profiling\Data\ItemInterfaceFactory; -use Vipps\Payment\Api\Profiling\ItemRepositoryInterface; +use Vipps\Payment\Api\Profiling\ { + Data\ItemInterface, Data\ItemInterfaceFactory, ItemRepositoryInterface +}; + use Zend\Http\Response; use Magento\Framework\Json\DecoderInterface; +use Vipps\Payment\Model\Gdpr\Compliance; class Profiler implements ProfilerInterface { @@ -46,6 +48,11 @@ class Profiler implements ProfilerInterface */ private $jsonDecoder; + /** + * @var Compliance + */ + private $gdprCompliance; + /** * Profiler constructor. * @@ -58,12 +65,14 @@ public function __construct( ScopeConfigInterface $config, ItemInterfaceFactory $dataItemFactory, ItemRepositoryInterface $itemRepository, - DecoderInterface $jsonDecoder + DecoderInterface $jsonDecoder, + Compliance $gdprCompliance ) { $this->config = $config; $this->dataItemFactory = $dataItemFactory; $this->itemRepository = $itemRepository; $this->jsonDecoder = $jsonDecoder; + $this->gdprCompliance = $gdprCompliance; } /** @@ -150,7 +159,8 @@ private function parseResponse(Response $response) private function depersonalizedResponse($response) { unset($response['url']); - return $response; + + return $this->gdprCompliance->process($response); } /** From 0f4084e66fae66b4ceb4fd9561db9acd23ba93dc Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 22 Nov 2018 18:07:51 +0200 Subject: [PATCH 048/121] VIPPS-168: Fix phpcs. --- Model/Gdpr/Compliance.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Model/Gdpr/Compliance.php b/Model/Gdpr/Compliance.php index c6a6ce18..c4dda45c 100644 --- a/Model/Gdpr/Compliance.php +++ b/Model/Gdpr/Compliance.php @@ -93,4 +93,4 @@ public function process($responseData) return $responseData; } -} \ No newline at end of file +} From 13c5ca1853ce29a30505427d0543cdb6e3441c70 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Fri, 23 Nov 2018 11:38:15 +0200 Subject: [PATCH 049/121] VIPPS-168: Code review. --- Controller/Payment/Callback.php | 1 + Controller/Payment/Fallback.php | 1 + Controller/Payment/ShippingDetails.php | 2 ++ Model/Gdpr/Compliance.php | 12 ++++++++++++ Model/Profiling/Profiler.php | 1 + 5 files changed, 17 insertions(+) diff --git a/Controller/Payment/Callback.php b/Controller/Payment/Callback.php index 5db4cb4d..9233e6c2 100755 --- a/Controller/Payment/Callback.php +++ b/Controller/Payment/Callback.php @@ -72,6 +72,7 @@ class Callback extends Action * @var CartInterface */ private $quote; + /** * @var Compliance */ diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index 14ed9b09..d5c79952 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -90,6 +90,7 @@ class Fallback extends Action * @var LoggerInterface */ private $logger; + /** * @var Compliance */ diff --git a/Controller/Payment/ShippingDetails.php b/Controller/Payment/ShippingDetails.php index bc4ff112..25b59002 100644 --- a/Controller/Payment/ShippingDetails.php +++ b/Controller/Payment/ShippingDetails.php @@ -67,10 +67,12 @@ class ShippingDetails extends Action * @var LoggerInterface */ private $logger; + /** * @var Compliance */ private $gdprCompliance; + /** * @var AddressUpdater */ diff --git a/Model/Gdpr/Compliance.php b/Model/Gdpr/Compliance.php index c4dda45c..fd6e429b 100644 --- a/Model/Gdpr/Compliance.php +++ b/Model/Gdpr/Compliance.php @@ -20,17 +20,29 @@ use Magento\Framework\Serialize\Serializer\Json; use Psr\Log\LoggerInterface; +/** + * Applies Gdpr compliance. + * + * Class Compliance + * @package Vipps\Payment\Model\Gdpr + */ class Compliance { /** * @var Json */ private $serializer; + /** * @var LoggerInterface */ private $logger; + /** + * Compliance constructor. + * @param Json $serializer + * @param LoggerInterface $logger + */ public function __construct(Json $serializer, LoggerInterface $logger) { $this->serializer = $serializer; diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index 5ee2c337..95bdfed0 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -60,6 +60,7 @@ class Profiler implements ProfilerInterface * @param ItemInterfaceFactory $dataItemFactory * @param ItemRepositoryInterface $itemRepository * @param DecoderInterface $jsonDecoder + * @param Compliance $gdprCompliance */ public function __construct( ScopeConfigInterface $config, From 35b9166075eccdb0f55d9b1902aa49483f9ecf2a Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 23 Nov 2018 10:55:22 +0100 Subject: [PATCH 050/121] VIPPS-170: Code review fixes --- Cron/FetchOrderFromVipps.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index e0c0bc10..3051bdb6 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -29,6 +29,7 @@ }; use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\LocalizedException; /** * Class FetchOrderStatus @@ -110,7 +111,7 @@ public function __construct( * Create orders from Vipps that are not created in Magento yet * * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function execute() { @@ -135,7 +136,7 @@ public function execute() if ($transaction->isTransactionAborted()) { $this->cancelQuote($quote); - } else if ($transaction->getTransactionInfo()->getStatus() + } elseif ($transaction->getTransactionInfo()->getStatus() == Transaction::TRANSACTION_STATUS_INITIATE) { // we process quotes that have last update 15 min passed // vipps cancel if order not approved within 5 min @@ -187,7 +188,7 @@ private function fetchOrderStatus($orderId) * @throws InputException * @throws NoSuchEntityException * @throws VippsException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ private function processQuote(CartInterface $quote, Transaction $transaction) { @@ -207,8 +208,6 @@ private function processQuote(CartInterface $quote, Transaction $transaction) /** * @param CartInterface $quote * @param VippsException $e - * - * @throws \Magento\Framework\Exception\LocalizedException */ private function processVippsException(CartInterface $quote, VippsException $e) { From 7a58f7b74718bdbdf444424730c4008c1a7800e2 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 23 Nov 2018 22:54:10 +0100 Subject: [PATCH 051/121] VIPPS-133: Improve background processing - Cancel quote when order initiated more then 5 min - Cancel quote if amount in vipps and magento not the same --- Cron/FetchOrderFromVipps.php | 67 +++++++++++++--------- Gateway/Exception/ExceptionFactory.php | 2 +- Gateway/Exception/WrongAmountException.php | 24 ++++++++ Gateway/Response/InitiateHandler.php | 1 + Model/OrderPlace.php | 7 ++- Model/QuoteUpdater.php | 3 - 6 files changed, 72 insertions(+), 32 deletions(-) create mode 100644 Gateway/Exception/WrongAmountException.php diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 3051bdb6..2792f6e0 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -27,6 +27,7 @@ Model\OrderPlace, Gateway\Transaction\TransactionBuilder }; +use Vipps\Payment\Gateway\Exception\WrongAmountException; use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; use Magento\Framework\Exception\LocalizedException; @@ -130,26 +131,18 @@ public function execute() try { // set quote store as current store $this->storeManager->setCurrentStore($quote->getStore()->getId()); - // fetch order status from vipps $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); - if ($transaction->isTransactionAborted()) { $this->cancelQuote($quote); - } elseif ($transaction->getTransactionInfo()->getStatus() - == Transaction::TRANSACTION_STATUS_INITIATE) { - // we process quotes that have last update 15 min passed - // vipps cancel if order not approved within 5 min - // so if quote is still initiated we can cancel it - $this->cancelQuote( - $quote, - [ - 'cancel_reason_phrase' => 'expired' - ] - ); - } else { - $this->processQuote($quote, $transaction); + continue; } + if ($this->shouldCancelExpiredQuote($quote, $transaction)) { + $this->cancelQuote($quote, 'expired'); + continue; + } + // process quote + $this->processQuote($quote, $transaction); } catch (VippsException $e) { $this->processVippsException($quote, $e); $this->logger->critical($e->getMessage()); @@ -166,6 +159,23 @@ public function execute() } } + /** + * @param Quote $quote + * @param Transaction $transaction + * + * @return bool + * @throws \Exception + */ + private function shouldCancelExpiredQuote(Quote $quote, Transaction $transaction) + { + $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add(new \DateInterval('PT5M')); //@codingStandardsIgnoreLine + $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine + + return $isQuoteExpired + && ($transaction->getTransactionInfo()->getStatus() == Transaction::TRANSACTION_STATUS_INITIATE); + + } + /** * @param $orderId * @@ -189,6 +199,7 @@ private function fetchOrderStatus($orderId) * @throws NoSuchEntityException * @throws VippsException * @throws LocalizedException + * @throws WrongAmountException */ private function processQuote(CartInterface $quote, Transaction $transaction) { @@ -213,13 +224,7 @@ private function processVippsException(CartInterface $quote, VippsException $e) { if ($e->getCode() < ZendResponse::STATUS_CODE_500) { /** @var Payment $payment */ - $this->cancelQuote( - $quote, - [ - 'cancel_reason_code' => $e->getCode(), - 'cancel_reason_phrase' => $e->getMessage() - ] - ); + $this->cancelQuote($quote, $e); } } @@ -227,13 +232,23 @@ private function processVippsException(CartInterface $quote, VippsException $e) * Cancel quote by setting reserved_order_id to null * * @param CartInterface $quote - * @param array $additionalInformation + * @param \Exception|string $info */ - private function cancelQuote(CartInterface $quote, $additionalInformation = []) + private function cancelQuote(CartInterface $quote, $info = null) { $reservedOrderId = $quote->getReservedOrderId(); $quote->setReservedOrderId(null); + $additionalInformation = []; + if ($info instanceof \Exception) { + $additionalInformation = [ + 'cancel_reason_code' => $info->getCode(), + 'cancel_reason_phrase' => $info->getMessage() + ]; + } elseif (\is_string($info)) { + $additionalInformation['cancel_reason_phrase'] = $info; + } + $additionalInformation = array_merge( $additionalInformation, [ @@ -264,7 +279,7 @@ private function createCollection($currentPage) $collection->setPageSize(self::COLLECTION_PAGE_SIZE); $collection->setCurPage($currentPage); - $collection->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id']); //@codingStandardsIgnoreLine + $collection->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']); //@codingStandardsIgnoreLine $collection->join( ['p' => $collection->getTable('quote_payment')], 'main_table.entity_id = p.quote_id', @@ -272,7 +287,7 @@ private function createCollection($currentPage) ); $collection->addFieldToFilter('p.method', ['eq' => 'vipps']); $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); - $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 900)]); + $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); return $collection; } diff --git a/Gateway/Exception/ExceptionFactory.php b/Gateway/Exception/ExceptionFactory.php index 3be43f24..de06ed0a 100644 --- a/Gateway/Exception/ExceptionFactory.php +++ b/Gateway/Exception/ExceptionFactory.php @@ -73,7 +73,7 @@ public function create($errorCode, $errorMessage) 'code' => $errorCode ]); } - $errorMessage = $this->getMessageByErrorCode($errorCode, $errorMessage); +// $errorMessage = $this->getMessageByErrorCode($errorCode, $errorMessage); $exceptionObject = new $groupName(__($errorMessage), null, (int)$errorCode); //@codingStandardsIgnoreLine return $exceptionObject; } diff --git a/Gateway/Exception/WrongAmountException.php b/Gateway/Exception/WrongAmountException.php new file mode 100644 index 00000000..425341b3 --- /dev/null +++ b/Gateway/Exception/WrongAmountException.php @@ -0,0 +1,24 @@ +setCheckoutMethod(Onepage::METHOD_REGISTER); } } + $payment->setMethod('vipps'); $quote->setIsActive(false); $this->cartRepository->save($quote); diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index f07a3e82..97384757 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -33,6 +33,7 @@ }; use Magento\Quote\Model\Quote; use Magento\Store\Model\ScopeInterface; +use Vipps\Payment\Gateway\Exception\WrongAmountException; use Vipps\Payment\Gateway\{ Transaction\Transaction, Exception\VippsException }; @@ -148,6 +149,7 @@ public function __construct( * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException + * @throws WrongAmountException */ public function execute(CartInterface $quote, Transaction $transaction) { @@ -240,6 +242,7 @@ private function canPlaceOrder(Transaction $transaction) * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException + * @throws WrongAmountException */ private function placeOrder(CartInterface $quote, Transaction $transaction) { @@ -364,7 +367,7 @@ private function notify($order) * @param CartInterface $quote * @param Transaction $transaction * - * @throws LocalizedException + * @throws WrongAmountException */ private function validateAmount(CartInterface $quote, Transaction $transaction) { @@ -372,7 +375,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); if ($quoteAmount !== $vippsAmount) { - throw new LocalizedException( + throw new WrongAmountException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $vippsAmount, $quoteAmount) ); } diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index a751d985..37dff1f6 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -76,9 +76,6 @@ public function execute(CartInterface $quote) if (!$transaction->isExpressCheckout()) { return false; } - $payment = $quote->getPayment(); - $payment->setMethod('vipps'); - $quote->setMayEditShippingAddress(false); $quote->setMayEditShippingMethod(true); From 41963c1e86ac8c392d2e50d228f630bda700f9ff Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Fri, 23 Nov 2018 23:02:19 +0100 Subject: [PATCH 052/121] VIPPS-133: Fix phpmd --- Controller/Payment/Fallback.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index d5c79952..61803929 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -109,6 +109,8 @@ class Fallback extends Action * @param OrderLocator $orderLocator * @param Compliance $compliance * @param LoggerInterface $logger + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, From f2c7194d9f1dd3cc4c42bdd35ee8a37db41e42cb Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 27 Nov 2018 12:52:44 +0100 Subject: [PATCH 053/121] ENKL-276: VIPPS shipments fail --- Cron/FetchOrderFromVipps.php | 3 ++- Model/OrderPlace.php | 43 +++++++++++++++++++++++++----------- Model/QuoteUpdater.php | 5 ++++- 3 files changed, 36 insertions(+), 15 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 2792f6e0..d610448f 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -131,6 +131,7 @@ public function execute() try { // set quote store as current store $this->storeManager->setCurrentStore($quote->getStore()->getId()); + // fetch order status from vipps $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); if ($transaction->isTransactionAborted()) { @@ -145,7 +146,7 @@ public function execute() $this->processQuote($quote, $transaction); } catch (VippsException $e) { $this->processVippsException($quote, $e); - $this->logger->critical($e->getMessage()); + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); } catch (\Throwable $e) { $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); } finally { diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 97384757..09906c3e 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -246,7 +246,9 @@ private function canPlaceOrder(Transaction $transaction) */ private function placeOrder(CartInterface $quote, Transaction $transaction) { - $reservedOrderId = $quote->getReservedOrderId(); + $clonedQuote = clone $quote; + + $reservedOrderId = $clonedQuote->getReservedOrderId(); if (!$reservedOrderId) { return null; } @@ -257,26 +259,41 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) } //this is used only for express checkout - $this->quoteUpdater->execute($quote); + $this->quoteUpdater->execute($clonedQuote); - /** @var Quote $quote */ - $quote = $this->cartRepository->get($quote->getId()); - if ($quote->getReservedOrderId() !== $reservedOrderId) { + /** @var Quote $clonedQuote */ + $clonedQuote = $this->cartRepository->get($clonedQuote->getId()); + if ($clonedQuote->getReservedOrderId() !== $reservedOrderId) { return null; } + $this->prepareQuote($clonedQuote); + // set quote active, collect totals and place order - $quote->setIsActive(true); - $quote->collectTotals(); - $this->validateAmount($quote, $transaction); - $orderId = $this->cartManagement->placeOrder($quote->getId()); + $clonedQuote->collectTotals(); + $this->validateAmount($clonedQuote, $transaction); - $quote->setReservedOrderId(null); - $this->cartRepository->save($quote); + $clonedQuote->setIsActive(true); + $orderId = $this->cartManagement->placeOrder($clonedQuote->getId()); + + $clonedQuote->setReservedOrderId(null); + $this->cartRepository->save($clonedQuote); return $this->orderRepository->get($orderId); } + /** + * @param CartInterface|Quote $quote + */ + private function prepareQuote($quote) + { + $websiteId = $quote->getStore()->getWebsiteId(); + foreach ($quote->getAllItems() as $item) { + /** @var Quote\Item $item */ + $item->getProduct()->setWebsiteId($websiteId); + } + } + /** * Authorize action * @@ -367,7 +384,7 @@ private function notify($order) * @param CartInterface $quote * @param Transaction $transaction * - * @throws WrongAmountException + * @throws LocalizedException */ private function validateAmount(CartInterface $quote, Transaction $transaction) { @@ -375,7 +392,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); if ($quoteAmount !== $vippsAmount) { - throw new WrongAmountException( + throw new LocalizedException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $vippsAmount, $quoteAmount) ); } diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index 37dff1f6..e44c8dbf 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -71,6 +71,7 @@ public function __construct( */ public function execute(CartInterface $quote) { + /** @var Quote $quote */ $response = $this->paymentDetailsProvider->get(['orderId' => $quote->getReservedOrderId()]); $transaction = $this->transactionBuilder->setData($response)->build(); if (!$transaction->isExpressCheckout()) { @@ -119,7 +120,6 @@ private function updateShippingAddress(Quote $quote, Transaction $transaction) $shippingAddress->setFirstname($userDetails->getFirstName()); $shippingAddress->setEmail($userDetails->getEmail()); $shippingAddress->setTelephone($userDetails->getMobileNumber()); - $shippingAddress->setCollectShippingRates(true); $shippingAddress->setShippingMethod($shippingDetails->getShippingMethodId()); $shippingAddress->setShippingAmount($shippingDetails->getShippingCost()); @@ -127,6 +127,9 @@ private function updateShippingAddress(Quote $quote, Transaction $transaction) $shippingAddress->setSaveInAddressBook(false); $shippingAddress->setSameAsBilling(true); $shippingAddress->unsCustomerAddressId(); + + // initiate collect totals again because we have made changes + $shippingAddress->setCollectShippingRates(true); } /** From ec516febab1ca0d89a217e3cb3b4f9d9585a647b Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 27 Nov 2018 16:36:10 +0100 Subject: [PATCH 054/121] ENKL-276: VIPPS shipments fail --- Model/OrderPlace.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 09906c3e..9a188acd 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -384,7 +384,7 @@ private function notify($order) * @param CartInterface $quote * @param Transaction $transaction * - * @throws LocalizedException + * @throws WrongAmountException */ private function validateAmount(CartInterface $quote, Transaction $transaction) { @@ -392,7 +392,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); if ($quoteAmount !== $vippsAmount) { - throw new LocalizedException( + throw new WrongAmountException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $vippsAmount, $quoteAmount) ); } From 17aca5579c8617554a9fbd28e44a0cd03697e07b Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 27 Nov 2018 17:13:59 +0100 Subject: [PATCH 055/121] VIPPS-172: Publication v.1.0.12 --- Controller/Payment/Callback.php | 13 ++- Controller/Payment/Fallback.php | 16 ++- Controller/Payment/ShippingDetails.php | 25 ++++- Cron/FetchOrderFromVipps.php | 75 ++++++++++---- Gateway/Exception/ExceptionFactory.php | 2 +- Gateway/Exception/WrongAmountException.php | 24 +++++ Gateway/Response/InitiateHandler.php | 1 + Model/Adminhtml/Source/PaymentAction.php | 12 +-- Model/Gdpr/Compliance.php | 108 ++++++++++++++++++++ Model/OrderPlace.php | 111 ++++++++++++++++++--- Model/Profiling/Profiler.php | 21 +++- Model/Quote/AddressUpdater.php | 105 +++++++++++++++++++ Model/QuoteUpdater.php | 26 +---- composer.json | 2 +- etc/adminhtml/system.xml | 6 ++ etc/di.xml | 6 ++ 16 files changed, 478 insertions(+), 75 deletions(-) create mode 100644 Gateway/Exception/WrongAmountException.php create mode 100644 Model/Gdpr/Compliance.php create mode 100644 Model/Quote/AddressUpdater.php diff --git a/Controller/Payment/Callback.php b/Controller/Payment/Callback.php index 5aead60e..9233e6c2 100755 --- a/Controller/Payment/Callback.php +++ b/Controller/Payment/Callback.php @@ -27,7 +27,8 @@ Gateway\Request\Initiate\MerchantDataBuilder, Gateway\Transaction\TransactionBuilder, Model\OrderPlace, - Model\QuoteLocator + Model\QuoteLocator, + Model\Gdpr\Compliance }; use Magento\Quote\{ Api\Data\CartInterface, Model\Quote @@ -72,6 +73,11 @@ class Callback extends Action */ private $quote; + /** + * @var Compliance + */ + private $gdprCompliance; + /** * Callback constructor. * @@ -88,6 +94,7 @@ public function __construct( QuoteLocator $quoteLocator, Json $jsonDecoder, TransactionBuilder $transactionBuilder, + Compliance $compliance, LoggerInterface $logger ) { parent::__construct($context); @@ -96,6 +103,7 @@ public function __construct( $this->jsonDecoder = $jsonDecoder; $this->transactionBuilder = $transactionBuilder; $this->logger = $logger; + $this->gdprCompliance = $compliance; } /** @@ -125,7 +133,8 @@ public function execute() 'message' => __('An error occurred during callback processing.') ]); } finally { - $this->logger->debug($this->getRequest()->getContent()); + $compliant = $this->gdprCompliance->process($this->getRequest()->getContent()); + $this->logger->debug($compliant); } return $result; } diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index a9142cd0..61803929 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -23,7 +23,8 @@ }; use Vipps\Payment\{ Api\CommandManagerInterface, Gateway\Exception\MerchantException, Gateway\Request\Initiate\MerchantDataBuilder, - Model\OrderLocator, Model\OrderPlace, Gateway\Transaction\TransactionBuilder, Model\QuoteLocator + Model\OrderLocator, Model\OrderPlace, Gateway\Transaction\TransactionBuilder, Model\QuoteLocator, + Model\Gdpr\Compliance }; use Magento\Quote\{ Api\Data\CartInterface, Api\CartRepositoryInterface, Model\Quote @@ -90,6 +91,11 @@ class Fallback extends Action */ private $logger; + /** + * @var Compliance + */ + private $gdprCompliance; + /** * Fallback constructor. * @@ -101,7 +107,10 @@ class Fallback extends Action * @param CartRepositoryInterface $cartRepository * @param QuoteLocator $quoteLocator * @param OrderLocator $orderLocator + * @param Compliance $compliance * @param LoggerInterface $logger + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( Context $context, @@ -112,6 +121,7 @@ public function __construct( CartRepositoryInterface $cartRepository, QuoteLocator $quoteLocator, OrderLocator $orderLocator, + Compliance $compliance, LoggerInterface $logger ) { parent::__construct($context); @@ -123,6 +133,7 @@ public function __construct( $this->quoteLocator = $quoteLocator; $this->orderLocator = $orderLocator; $this->logger = $logger; + $this->gdprCompliance = $compliance; } /** @@ -154,7 +165,8 @@ public function execute() $this->messageManager->addErrorMessage(__('An error occurred during payment status update.')); $resultRedirect->setPath('checkout/onepage/failure', ['_secure' => true]); } finally { - $this->logger->debug($this->getRequest()->getRequestString()); + $compliant = $this->gdprCompliance->process($this->getRequest()->getRequestString()); + $this->logger->debug($compliant); } return $resultRedirect; } diff --git a/Controller/Payment/ShippingDetails.php b/Controller/Payment/ShippingDetails.php index ba746cf2..25b59002 100644 --- a/Controller/Payment/ShippingDetails.php +++ b/Controller/Payment/ShippingDetails.php @@ -23,8 +23,11 @@ CartRepositoryInterface, Data\CartInterface, ShipmentEstimationInterface, Data\AddressInterfaceFactory }; use Magento\Quote\Model\Quote; +use Vipps\Payment\Model\Gdpr\Compliance; use Vipps\Payment\Gateway\Transaction\ShippingDetails as TransactionShippingDetails; -use Vipps\Payment\Model\QuoteLocator; +use Vipps\Payment\Model\{ + QuoteLocator, Quote\AddressUpdater +}; use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; @@ -65,6 +68,16 @@ class ShippingDetails extends Action */ private $logger; + /** + * @var Compliance + */ + private $gdprCompliance; + + /** + * @var AddressUpdater + */ + private $addressUpdater; + /** * ShippingDetails constructor. * @@ -73,6 +86,8 @@ class ShippingDetails extends Action * @param QuoteLocator $quoteLocator * @param ShipmentEstimationInterface $shipmentEstimation * @param AddressInterfaceFactory $addressFactory + * @param AddressUpdater $addressUpdater + * @param Compliance $compliance * @param Json $serializer * @param LoggerInterface $logger */ @@ -82,6 +97,8 @@ public function __construct( QuoteLocator $quoteLocator, ShipmentEstimationInterface $shipmentEstimation, AddressInterfaceFactory $addressFactory, + AddressUpdater $addressUpdater, + Compliance $compliance, Json $serializer, LoggerInterface $logger ) { @@ -92,6 +109,8 @@ public function __construct( $this->shipmentEstimation = $shipmentEstimation; $this->addressFactory = $addressFactory; $this->logger = $logger; + $this->addressUpdater = $addressUpdater; + $this->gdprCompliance = $compliance; } /** @@ -119,6 +138,7 @@ public function execute() * As Quote is deactivated, so we need to activate it for estimating shipping methods */ $quote = $this->cartRepository->get($quote->getId()); + $this->addressUpdater->fromSourceAddress($quote, $address); $quote->setIsActive(true); $shippingMethods = $this->shipmentEstimation->estimateByExtendedAddress($quote->getId(), $address); $responseData = [ @@ -151,7 +171,8 @@ public function execute() 'message' => __('An error occurred during Shipping Details processing.') ]); } finally { - $this->logger->debug($this->getRequest()->getContent()); + $compliantString = $this->gdprCompliance->process($this->getRequest()->getContent()); + $this->logger->debug($compliantString); } return $result; } diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 7c2d0a3e..d610448f 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -27,8 +27,10 @@ Model\OrderPlace, Gateway\Transaction\TransactionBuilder }; +use Vipps\Payment\Gateway\Exception\WrongAmountException; use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; +use Magento\Framework\Exception\LocalizedException; /** * Class FetchOrderStatus @@ -110,7 +112,7 @@ public function __construct( * Create orders from Vipps that are not created in Magento yet * * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\LocalizedException + * @throws LocalizedException */ public function execute() { @@ -132,15 +134,19 @@ public function execute() // fetch order status from vipps $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); - if ($transaction->isTransactionAborted()) { $this->cancelQuote($quote); - } else { - $this->processQuote($quote, $transaction); + continue; + } + if ($this->shouldCancelExpiredQuote($quote, $transaction)) { + $this->cancelQuote($quote, 'expired'); + continue; } + // process quote + $this->processQuote($quote, $transaction); } catch (VippsException $e) { $this->processVippsException($quote, $e); - $this->logger->critical($e->getMessage()); + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); } catch (\Throwable $e) { $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); } finally { @@ -154,6 +160,23 @@ public function execute() } } + /** + * @param Quote $quote + * @param Transaction $transaction + * + * @return bool + * @throws \Exception + */ + private function shouldCancelExpiredQuote(Quote $quote, Transaction $transaction) + { + $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add(new \DateInterval('PT5M')); //@codingStandardsIgnoreLine + $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine + + return $isQuoteExpired + && ($transaction->getTransactionInfo()->getStatus() == Transaction::TRANSACTION_STATUS_INITIATE); + + } + /** * @param $orderId * @@ -171,11 +194,13 @@ private function fetchOrderStatus($orderId) * @param Transaction $transaction * * @return OrderInterface|null + * @throws AlreadyExistsException * @throws CouldNotSaveException + * @throws InputException * @throws NoSuchEntityException * @throws VippsException - * @throws AlreadyExistsException - * @throws InputException + * @throws LocalizedException + * @throws WrongAmountException */ private function processQuote(CartInterface $quote, Transaction $transaction) { @@ -195,18 +220,12 @@ private function processQuote(CartInterface $quote, Transaction $transaction) /** * @param CartInterface $quote * @param VippsException $e - * - * @throws \Magento\Framework\Exception\LocalizedException */ private function processVippsException(CartInterface $quote, VippsException $e) { if ($e->getCode() < ZendResponse::STATUS_CODE_500) { /** @var Payment $payment */ - $payment = $quote->getPayment(); - $payment->setAdditionalInformation('reserved_order_id', $quote->getReservedOrderId()); - $payment->setAdditionalInformation('cancel_reason_code', $e->getCode()); - $payment->setAdditionalInformation('cancel_reason_phrase', $e->getMessage()); - $this->cancelQuote($quote); + $this->cancelQuote($quote, $e); } } @@ -214,12 +233,32 @@ private function processVippsException(CartInterface $quote, VippsException $e) * Cancel quote by setting reserved_order_id to null * * @param CartInterface $quote + * @param \Exception|string $info */ - private function cancelQuote(CartInterface $quote) + private function cancelQuote(CartInterface $quote, $info = null) { $reservedOrderId = $quote->getReservedOrderId(); - $quote->setReservedOrderId(null); + + $additionalInformation = []; + if ($info instanceof \Exception) { + $additionalInformation = [ + 'cancel_reason_code' => $info->getCode(), + 'cancel_reason_phrase' => $info->getMessage() + ]; + } elseif (\is_string($info)) { + $additionalInformation['cancel_reason_phrase'] = $info; + } + + $additionalInformation = array_merge( + $additionalInformation, + [ + 'reserved_order_id' => $reservedOrderId + ] + ); + $payment = $quote->getPayment(); + $payment->setAdditionalInformation('vipps', $additionalInformation); + $this->cartRepository->save($quote); $this->logger->debug(sprintf( @@ -241,7 +280,7 @@ private function createCollection($currentPage) $collection->setPageSize(self::COLLECTION_PAGE_SIZE); $collection->setCurPage($currentPage); - $collection->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id']); //@codingStandardsIgnoreLine + $collection->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']); //@codingStandardsIgnoreLine $collection->join( ['p' => $collection->getTable('quote_payment')], 'main_table.entity_id = p.quote_id', @@ -249,7 +288,7 @@ private function createCollection($currentPage) ); $collection->addFieldToFilter('p.method', ['eq' => 'vipps']); $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); - $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 1800)]); + $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); return $collection; } diff --git a/Gateway/Exception/ExceptionFactory.php b/Gateway/Exception/ExceptionFactory.php index 3be43f24..de06ed0a 100644 --- a/Gateway/Exception/ExceptionFactory.php +++ b/Gateway/Exception/ExceptionFactory.php @@ -73,7 +73,7 @@ public function create($errorCode, $errorMessage) 'code' => $errorCode ]); } - $errorMessage = $this->getMessageByErrorCode($errorCode, $errorMessage); +// $errorMessage = $this->getMessageByErrorCode($errorCode, $errorMessage); $exceptionObject = new $groupName(__($errorMessage), null, (int)$errorCode); //@codingStandardsIgnoreLine return $exceptionObject; } diff --git a/Gateway/Exception/WrongAmountException.php b/Gateway/Exception/WrongAmountException.php new file mode 100644 index 00000000..425341b3 --- /dev/null +++ b/Gateway/Exception/WrongAmountException.php @@ -0,0 +1,24 @@ +setCheckoutMethod(Onepage::METHOD_REGISTER); } } + $payment->setMethod('vipps'); $quote->setIsActive(false); $this->cartRepository->save($quote); diff --git a/Model/Adminhtml/Source/PaymentAction.php b/Model/Adminhtml/Source/PaymentAction.php index 571b5a5f..8a385f92 100644 --- a/Model/Adminhtml/Source/PaymentAction.php +++ b/Model/Adminhtml/Source/PaymentAction.php @@ -25,12 +25,12 @@ class PaymentAction implements ArrayInterface /** * @var string */ - const ACTION_CAPTURE = 'capture'; + const ACTION_AUTHORIZE = 'authorize'; /** * @var string */ - const ACTION_DIRECT_CAPTURE = 'direct_capture'; + const ACTION_AUTHORIZE_CAPTURE = 'authorize_capture'; /** * Possible actions on order place @@ -41,12 +41,12 @@ public function toOptionArray() { return [ [ - 'value' => self::ACTION_CAPTURE, - 'label' => __('Capture'), + 'value' => self::ACTION_AUTHORIZE, + 'label' => __('Authorize'), ], [ - 'value' => self::ACTION_DIRECT_CAPTURE, - 'label' => __('Direct Capture'), + 'value' => self::ACTION_AUTHORIZE_CAPTURE, + 'label' => __('Authorize and Capture'), ] ]; } diff --git a/Model/Gdpr/Compliance.php b/Model/Gdpr/Compliance.php new file mode 100644 index 00000000..fd6e429b --- /dev/null +++ b/Model/Gdpr/Compliance.php @@ -0,0 +1,108 @@ +serializer = $serializer; + $this->logger = $logger; + } + + /** + * Fields that require masking. + * + * @return array + */ + private function getReplacementSchema(): array + { + $schema = [ + 'addressLine1' => 1, + 'addressLine2' => 1, + 'email' => 1, + 'firstName' => 1, + 'lastName' => 1, + 'mobileNumber' => 1, + ]; + + return $schema; + } + + /** + * Mask response fields. + * + * @param array|string $responseData + * + * @return array|string + */ + public function process($responseData) + { + $wasPacked = false; + + try { + if (\is_string($responseData)) { + $responseData = $this->serializer->unserialize($responseData); + $wasPacked = true; + } + + if (!\is_array($responseData)) { + throw new SerializationException(__('Unserialization result is not an array')); + } + + array_walk_recursive($responseData, function (&$item, $key, $schema) { + if (isset($schema[$key])) { + $item = str_repeat('x', \strlen($item)); + } + }, $this->getReplacementSchema()); + + if ($wasPacked) { + $responseData = $this->serializer->serialize($responseData); + } + } catch (\Exception $e) { + $this->logger->critical('Gdpr compliance failed'); + $this->logger->critical($e->getMessage()); + } + + return $responseData; + } +} diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 20e3971e..9a188acd 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -20,6 +20,7 @@ }; use Magento\Framework\Exception\LocalizedException; use Magento\Payment\Helper\Formatter; +use Magento\Payment\Gateway\ConfigInterface; use Magento\Sales\Api\{ OrderManagementInterface, Data\OrderInterface, OrderRepositoryInterface }; @@ -31,9 +32,12 @@ CartRepositoryInterface, CartManagementInterface, Data\CartInterface }; use Magento\Quote\Model\Quote; +use Magento\Store\Model\ScopeInterface; +use Vipps\Payment\Gateway\Exception\WrongAmountException; use Vipps\Payment\Gateway\{ Transaction\Transaction, Exception\VippsException }; +use Vipps\Payment\Model\Adminhtml\Source\PaymentAction; /** * Class OrderManagement @@ -89,6 +93,11 @@ class OrderPlace */ private $lockManager; + /** + * @var ConfigInterface + */ + private $config; + /** * OrderPlace constructor. * @@ -101,6 +110,9 @@ class OrderPlace * @param Processor $processor * @param QuoteUpdater $quoteUpdater * @param LockManager $lockManager + * @param ConfigInterface $config + * + * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( OrderRepositoryInterface $orderRepository, @@ -111,7 +123,8 @@ public function __construct( QuoteLocator $quoteLocator, Processor $processor, QuoteUpdater $quoteUpdater, - LockManager $lockManager + LockManager $lockManager, + ConfigInterface $config ) { $this->orderRepository = $orderRepository; $this->cartRepository = $cartRepository; @@ -122,6 +135,7 @@ public function __construct( $this->processor = $processor; $this->quoteUpdater = $quoteUpdater; $this->lockManager = $lockManager; + $this->config = $config; } /** @@ -132,8 +146,10 @@ public function __construct( * @throws AlreadyExistsException * @throws CouldNotSaveException * @throws InputException + * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException + * @throws WrongAmountException */ public function execute(CartInterface $quote, Transaction $transaction) { @@ -149,7 +165,14 @@ public function execute(CartInterface $quote, Transaction $transaction) try { $order = $this->placeOrder($quote, $transaction); if ($order) { - $this->authorize($order, $transaction); + $paymentAction = $this->config->getValue('vipps_payment_action'); + switch ($paymentAction) { + case PaymentAction::ACTION_AUTHORIZE_CAPTURE: + $this->capture($order, $transaction); + break; + default: + $this->authorize($order, $transaction); + } } return $order; @@ -211,17 +234,21 @@ private function canPlaceOrder(Transaction $transaction) } /** - * @param CartInterface|Quote $quote + * @param CartInterface $quote * @param Transaction $transaction * * @return OrderInterface|null * @throws CouldNotSaveException + * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException + * @throws WrongAmountException */ private function placeOrder(CartInterface $quote, Transaction $transaction) { - $reservedOrderId = $quote->getReservedOrderId(); + $clonedQuote = clone $quote; + + $reservedOrderId = $clonedQuote->getReservedOrderId(); if (!$reservedOrderId) { return null; } @@ -232,26 +259,41 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) } //this is used only for express checkout - $this->quoteUpdater->execute($quote); + $this->quoteUpdater->execute($clonedQuote); - /** @var Quote $quote */ - $quote = $this->cartRepository->get($quote->getId()); - if ($quote->getReservedOrderId() !== $reservedOrderId) { + /** @var Quote $clonedQuote */ + $clonedQuote = $this->cartRepository->get($clonedQuote->getId()); + if ($clonedQuote->getReservedOrderId() !== $reservedOrderId) { return null; } + $this->prepareQuote($clonedQuote); + // set quote active, collect totals and place order - $quote->setIsActive(true); - $quote->collectTotals(); - $this->validateAmount($quote, $transaction); - $orderId = $this->cartManagement->placeOrder($quote->getId()); + $clonedQuote->collectTotals(); + $this->validateAmount($clonedQuote, $transaction); + + $clonedQuote->setIsActive(true); + $orderId = $this->cartManagement->placeOrder($clonedQuote->getId()); - $quote->setReservedOrderId(null); - $this->cartRepository->save($quote); + $clonedQuote->setReservedOrderId(null); + $this->cartRepository->save($clonedQuote); return $this->orderRepository->get($orderId); } + /** + * @param CartInterface|Quote $quote + */ + private function prepareQuote($quote) + { + $websiteId = $quote->getStore()->getWebsiteId(); + foreach ($quote->getAllItems() as $item) { + /** @var Quote\Item $item */ + $item->getProduct()->setWebsiteId($websiteId); + } + } + /** * Authorize action * @@ -287,6 +329,43 @@ private function authorize(OrderInterface $order, Transaction $transaction) $this->notify($order); } + /** + * Capture + * + * @param OrderInterface $order + * @param Transaction $transaction + * + * @throws LocalizedException + */ + private function capture(OrderInterface $order, Transaction $transaction) + { + if ($order->getState() !== Order::STATE_NEW) { + return; + } + + // preconditions + $totalDue = $order->getTotalDue(); + $baseTotalDue = $order->getBaseTotalDue(); + + /** @var Payment $payment */ + $payment = $order->getPayment(); + $payment->setAmountAuthorized($totalDue); + $payment->setBaseAmountAuthorized($baseTotalDue); + + $transactionId = $transaction->getTransactionId(); + $payment->setTransactionId($transactionId); + $payment->setTransactionAdditionalInfo( + PaymentTransaction::RAW_DETAILS, + $transaction->getTransactionInfo()->getData() + ); + + // do capture + $this->processor->capture($payment, null); + $this->orderRepository->save($order); + + $this->notify($order); + } + /** * Send order conformation email if not sent * @@ -305,7 +384,7 @@ private function notify($order) * @param CartInterface $quote * @param Transaction $transaction * - * @throws LocalizedException + * @throws WrongAmountException */ private function validateAmount(CartInterface $quote, Transaction $transaction) { @@ -313,7 +392,7 @@ private function validateAmount(CartInterface $quote, Transaction $transaction) $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); if ($quoteAmount !== $vippsAmount) { - throw new LocalizedException( + throw new WrongAmountException( __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $vippsAmount, $quoteAmount) ); } diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index 24fada09..95bdfed0 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -18,11 +18,13 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Payment\Gateway\Http\TransferInterface; use Magento\Store\Model\ScopeInterface; -use Vipps\Payment\Api\Profiling\Data\ItemInterface; -use Vipps\Payment\Api\Profiling\Data\ItemInterfaceFactory; -use Vipps\Payment\Api\Profiling\ItemRepositoryInterface; +use Vipps\Payment\Api\Profiling\ { + Data\ItemInterface, Data\ItemInterfaceFactory, ItemRepositoryInterface +}; + use Zend\Http\Response; use Magento\Framework\Json\DecoderInterface; +use Vipps\Payment\Model\Gdpr\Compliance; class Profiler implements ProfilerInterface { @@ -46,6 +48,11 @@ class Profiler implements ProfilerInterface */ private $jsonDecoder; + /** + * @var Compliance + */ + private $gdprCompliance; + /** * Profiler constructor. * @@ -53,17 +60,20 @@ class Profiler implements ProfilerInterface * @param ItemInterfaceFactory $dataItemFactory * @param ItemRepositoryInterface $itemRepository * @param DecoderInterface $jsonDecoder + * @param Compliance $gdprCompliance */ public function __construct( ScopeConfigInterface $config, ItemInterfaceFactory $dataItemFactory, ItemRepositoryInterface $itemRepository, - DecoderInterface $jsonDecoder + DecoderInterface $jsonDecoder, + Compliance $gdprCompliance ) { $this->config = $config; $this->dataItemFactory = $dataItemFactory; $this->itemRepository = $itemRepository; $this->jsonDecoder = $jsonDecoder; + $this->gdprCompliance = $gdprCompliance; } /** @@ -150,7 +160,8 @@ private function parseResponse(Response $response) private function depersonalizedResponse($response) { unset($response['url']); - return $response; + + return $this->gdprCompliance->process($response); } /** diff --git a/Model/Quote/AddressUpdater.php b/Model/Quote/AddressUpdater.php new file mode 100644 index 00000000..ac2a1f05 --- /dev/null +++ b/Model/Quote/AddressUpdater.php @@ -0,0 +1,105 @@ +cartRepository = $cartRepository; + } + + /** + * Update quote addresses from source address. + * + * @param Quote $quote + * @param Address $sourceAddress + */ + public function fromSourceAddress(Quote $quote, Address $sourceAddress) + { + $quote->setMayEditShippingAddress(false); + + $this->updateQuoteAddresses($quote, $sourceAddress); + $this->disabledQuoteAddressValidation($quote); + + /** + * Unset shipping assignment to prevent from saving / applying outdated data + * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment + */ + if ($quote->getExtensionAttributes()) { + $quote->getExtensionAttributes()->setShippingAssignments(null); + } + $this->cartRepository->save($quote); + } + + /** + * Update quote addresses from source address. + * + * @param Quote $quote + * @param Address $sourceAddress + */ + private function updateQuoteAddresses(Quote $quote, Address $sourceAddress) + { + if (!$quote->getIsVirtual()) { + $shippingAddress = $quote->getShippingAddress(); + $this->updateAddress($shippingAddress, $sourceAddress); + } + + $billingAddress = $quote->getBillingAddress(); + $this->updateAddress($billingAddress, $sourceAddress); + $billingAddress->setSameAsBilling(false); + } + + /** + * Update destination address from source. + * + * @param Address $destAddress + * @param Address $sourceAddress + */ + private function updateAddress(Address $destAddress, Address $sourceAddress) + { + $destAddress + ->setStreet($sourceAddress->getStreet()) + ->setCity($sourceAddress->getCity()) + ->setCountryId(ShippingDetails::NORWEGIAN_COUNTRY_ID) + ->setPostcode($sourceAddress->getPostcode()) + ->setSaveInAddressBook(false) + ->setSameAsBilling(true) + ->setCustomerAddressId(null); + } +} \ No newline at end of file diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index a93dbc33..e44c8dbf 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -71,14 +71,12 @@ public function __construct( */ public function execute(CartInterface $quote) { + /** @var Quote $quote */ $response = $this->paymentDetailsProvider->get(['orderId' => $quote->getReservedOrderId()]); $transaction = $this->transactionBuilder->setData($response)->build(); if (!$transaction->isExpressCheckout()) { return false; } - $payment = $quote->getPayment(); - $payment->setMethod('vipps'); - $quote->setMayEditShippingAddress(false); $quote->setMayEditShippingMethod(true); @@ -122,15 +120,16 @@ private function updateShippingAddress(Quote $quote, Transaction $transaction) $shippingAddress->setFirstname($userDetails->getFirstName()); $shippingAddress->setEmail($userDetails->getEmail()); $shippingAddress->setTelephone($userDetails->getMobileNumber()); - $shippingAddress->setCollectShippingRates(true); $shippingAddress->setShippingMethod($shippingDetails->getShippingMethodId()); $shippingAddress->setShippingAmount($shippingDetails->getShippingCost()); - $this->updateAddressData($shippingAddress, $shippingDetails); //We do not save user address from vipps in Magento $shippingAddress->setSaveInAddressBook(false); $shippingAddress->setSameAsBilling(true); $shippingAddress->unsCustomerAddressId(); + + // initiate collect totals again because we have made changes + $shippingAddress->setCollectShippingRates(true); } /** @@ -141,7 +140,6 @@ private function updateBillingAddress(Quote $quote, Transaction $transaction) { $userDetails = $transaction->getUserDetails(); $billingAddress = $quote->getBillingAddress(); - $this->updateAddressData($billingAddress, $transaction->getShippingDetails()); $billingAddress->setLastname($userDetails->getLastName()); $billingAddress->setFirstname($userDetails->getFirstName()); @@ -152,20 +150,4 @@ private function updateBillingAddress(Quote $quote, Transaction $transaction) $billingAddress->setSameAsBilling(false); $billingAddress->unsCustomerAddressId(); } - - /** - * @param Address $address - * @param ShippingDetails $shippingDetails - */ - private function updateAddressData(Address $address, ShippingDetails $shippingDetails) - { - $address->setStreet($shippingDetails->getStreet()); - $address->setCity($shippingDetails->getCity()); - $address->setCountryId(ShippingDetails::NORWEGIAN_COUNTRY_ID); - $address->setPostcode($shippingDetails->getPostCode()); - - $address->setSaveInAddressBook(false); - $address->setSameAsBilling(true); - $address->setCustomerAddressId(null); - } } diff --git a/composer.json b/composer.json index 402733cb..faecd374 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "magento2-module", "description": "Vipps Payment Method", "license": "proprietary", - "version": "1.0.11", + "version": "1.0.12", "require": { "magento/framework": "101.0.*", "magento/module-sales": "101.0.*", diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index cafe0185..a3a2eea0 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -57,6 +57,12 @@ Magento\Config\Model\Config\Source\Yesno payment/vipps/profiling + + + payment/vipps/vipps_payment_action + Vipps\Payment\Model\Adminhtml\Source\PaymentAction + 1 + payment/vipps/merchant_serial_number diff --git a/etc/di.xml b/etc/di.xml index e7f7c61e..0ddb17d8 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -45,6 +45,12 @@ + + + Vipps\Payment\Gateway\Config\Config + + + Vipps\Payment\Gateway\Config\Config From db37eb6c8542db73ef0ff48886570fc1d9d81bfb Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 27 Nov 2018 17:39:07 +0100 Subject: [PATCH 056/121] ENKL-276: VIPPS shipments fail --- Model/OrderPlace.php | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 9a188acd..47f0a51e 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -254,32 +254,31 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) } $order = $this->orderLocator->get($reservedOrderId); - if ($order) { - return $order; - } + if (!$order) { + //this is used only for express checkout + $this->quoteUpdater->execute($clonedQuote); + /** @var Quote $clonedQuote */ + $clonedQuote = $this->cartRepository->get($clonedQuote->getId()); + if ($clonedQuote->getReservedOrderId() !== $reservedOrderId) { + return null; + } - //this is used only for express checkout - $this->quoteUpdater->execute($clonedQuote); + $this->prepareQuote($clonedQuote); + $clonedQuote->collectTotals(); - /** @var Quote $clonedQuote */ - $clonedQuote = $this->cartRepository->get($clonedQuote->getId()); - if ($clonedQuote->getReservedOrderId() !== $reservedOrderId) { - return null; - } - - $this->prepareQuote($clonedQuote); + $this->validateAmount($clonedQuote, $transaction); - // set quote active, collect totals and place order - $clonedQuote->collectTotals(); - $this->validateAmount($clonedQuote, $transaction); + // set quote active, collect totals and place order + $clonedQuote->setIsActive(true); + $orderId = $this->cartManagement->placeOrder($clonedQuote->getId()); - $clonedQuote->setIsActive(true); - $orderId = $this->cartManagement->placeOrder($clonedQuote->getId()); + $order = $this->orderRepository->get($orderId); + } $clonedQuote->setReservedOrderId(null); $this->cartRepository->save($clonedQuote); - return $this->orderRepository->get($orderId); + return $order; } /** From 4d9f188d41a31f468eafc45be151b77bab65d31a Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 4 Dec 2018 10:11:58 +0100 Subject: [PATCH 057/121] VIPPS-156: Vipps support ticket - fixed issue with different scopeResolved when running a cron --- Cron/FetchOrderFromVipps.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index d610448f..e2471f8e 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -16,6 +16,7 @@ namespace Vipps\Payment\Cron; use Magento\Store\Model\StoreManagerInterface; +use Magento\Framework\App\Config\ScopeCodeResolver; use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory, Quote, Quote\Payment}; @@ -78,6 +79,10 @@ class FetchOrderFromVipps * @var StoreManagerInterface */ private $storeManager; + /** + * @var ScopeCodeResolver + */ + private $scopeCodeResolver; /** * FetchOrderFromVipps constructor. @@ -89,6 +94,7 @@ class FetchOrderFromVipps * @param CartRepositoryInterface $cartRepository * @param LoggerInterface $logger * @param StoreManagerInterface $storeManager + * @param ScopeCodeResolver $scopeCodeResolver */ public function __construct( CollectionFactory $quoteCollectionFactory, @@ -97,7 +103,8 @@ public function __construct( OrderPlace $orderManagement, CartRepositoryInterface $cartRepository, LoggerInterface $logger, - StoreManagerInterface $storeManager + StoreManagerInterface $storeManager, + ScopeCodeResolver $scopeCodeResolver ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; @@ -106,6 +113,7 @@ public function __construct( $this->cartRepository = $cartRepository; $this->logger = $logger; $this->storeManager = $storeManager; + $this->scopeCodeResolver = $scopeCodeResolver; } /** @@ -130,6 +138,7 @@ public function execute() /** @var Quote $quote */ try { // set quote store as current store + $this->scopeCodeResolver->clean(); $this->storeManager->setCurrentStore($quote->getStore()->getId()); // fetch order status from vipps From 8596a9ca24df3f321686a852260057657448778e Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 4 Dec 2018 10:11:58 +0100 Subject: [PATCH 058/121] VIPPS-156: Vipps support ticket - fixed issue with different scopeResolved when running a cron --- Cron/FetchOrderFromVipps.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index e2471f8e..eb75e66e 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -79,6 +79,7 @@ class FetchOrderFromVipps * @var StoreManagerInterface */ private $storeManager; + /** * @var ScopeCodeResolver */ From ddeec77893e3b57d2ccace1e164aa48342ba17f3 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Tue, 4 Dec 2018 13:35:10 +0100 Subject: [PATCH 059/121] VIPPS-156: Vipps support ticket - code review fixes --- Model/OrderPlace.php | 2 ++ Model/QuoteUpdater.php | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 47f0a51e..6e7844a3 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -264,6 +264,8 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) } $this->prepareQuote($clonedQuote); + + $clonedQuote->getShippingAddress()->setCollectShippingRates(true); $clonedQuote->collectTotals(); $this->validateAmount($clonedQuote, $transaction); diff --git a/Model/QuoteUpdater.php b/Model/QuoteUpdater.php index e44c8dbf..0db464ac 100644 --- a/Model/QuoteUpdater.php +++ b/Model/QuoteUpdater.php @@ -127,9 +127,6 @@ private function updateShippingAddress(Quote $quote, Transaction $transaction) $shippingAddress->setSaveInAddressBook(false); $shippingAddress->setSameAsBilling(true); $shippingAddress->unsCustomerAddressId(); - - // initiate collect totals again because we have made changes - $shippingAddress->setCollectShippingRates(true); } /** From 1eddd5335134ffa68a1b79cf8e97f2f6e3c4fed4 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 6 Dec 2018 11:10:01 +0100 Subject: [PATCH 060/121] VIPPS-156: Vipps support ticket - fix background processing --- Controller/Payment/Callback.php | 2 +- Cron/FetchOrderFromVipps.php | 64 +++++++++++++++++++++++++-------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/Controller/Payment/Callback.php b/Controller/Payment/Callback.php index 9233e6c2..4196f8d9 100755 --- a/Controller/Payment/Callback.php +++ b/Controller/Payment/Callback.php @@ -130,7 +130,7 @@ public function execute() $result->setHttpResponseCode(ZendResponse::STATUS_CODE_500); $result->setData([ 'status' => ZendResponse::STATUS_CODE_500, - 'message' => __('An error occurred during callback processing.') + 'message' => 'An error occurred during callback processing. ' . $e->getMessage() ]); } finally { $compliant = $this->gdprCompliance->process($this->getRequest()->getContent()); diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index eb75e66e..bbac7b2c 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -138,22 +138,19 @@ public function execute() foreach ($quoteCollection as $quote) { /** @var Quote $quote */ try { - // set quote store as current store - $this->scopeCodeResolver->clean(); - $this->storeManager->setCurrentStore($quote->getStore()->getId()); + // prepare environment before processing + $this->prepareEnv($quote); // fetch order status from vipps $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); - if ($transaction->isTransactionAborted()) { - $this->cancelQuote($quote); - continue; - } - if ($this->shouldCancelExpiredQuote($quote, $transaction)) { - $this->cancelQuote($quote, 'expired'); - continue; + + // check if quote should be canceled + if ($this->shouldCancelQuote($quote, $transaction, $reason)) { + $this->cancelQuote($quote, $reason); + } else { + // process quote + $this->processQuote($quote, $transaction); } - // process quote - $this->processQuote($quote, $transaction); } catch (VippsException $e) { $this->processVippsException($quote, $e); $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); @@ -170,6 +167,37 @@ public function execute() } } + /** + * @param Quote $quote + */ + private function prepareEnv(Quote $quote) + { + // set quote store as current store + $this->scopeCodeResolver->clean(); + $this->storeManager->setCurrentStore($quote->getStore()->getId()); + } + + /** + * @param Quote $quote + * @param Transaction $transaction + * @param null $reason + * + * @return bool + * @throws \Exception + */ + private function shouldCancelQuote(Quote $quote, Transaction $transaction, &$reason = null) + { + if ($transaction->isTransactionAborted()) { + $reason = 'aborted'; + return true; + } + if ($this->shouldCancelExpiredQuote($quote, $transaction)) { + $reason = 'expired after 5 min'; + return true; + } + return false; + } + /** * @param Quote $quote * @param Transaction $transaction @@ -180,10 +208,16 @@ public function execute() private function shouldCancelExpiredQuote(Quote $quote, Transaction $transaction) { $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add(new \DateInterval('PT5M')); //@codingStandardsIgnoreLine - $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine + $isQuoteExpired5M = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine - return $isQuoteExpired - && ($transaction->getTransactionInfo()->getStatus() == Transaction::TRANSACTION_STATUS_INITIATE); + return $isQuoteExpired5M + && (in_array( + $transaction->getTransactionInfo()->getStatus(), + [ + Transaction::TRANSACTION_STATUS_REGISTER, + Transaction::TRANSACTION_STATUS_INITIATE + ] + )); } From f7aca4a9a40c0e516441b2b38a141d7074706219 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Fri, 7 Dec 2018 11:54:26 +0200 Subject: [PATCH 061/121] VB2C-343: Fix express checkout. Replace quote saving by address saving. --- Controller/Payment/ShippingDetails.php | 1 + Model/Quote/AddressUpdater.php | 37 +++++++------------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/Controller/Payment/ShippingDetails.php b/Controller/Payment/ShippingDetails.php index 25b59002..08895d4e 100644 --- a/Controller/Payment/ShippingDetails.php +++ b/Controller/Payment/ShippingDetails.php @@ -158,6 +158,7 @@ public function execute() $result->setHttpResponseCode(ZendResponse::STATUS_CODE_200); $result->setData($responseData); } catch (LocalizedException $e) { + $this->logger->critical($e->getMessage()); $result->setHttpResponseCode(ZendResponse::STATUS_CODE_500); $result->setData([ 'status' => ZendResponse::STATUS_CODE_500, diff --git a/Model/Quote/AddressUpdater.php b/Model/Quote/AddressUpdater.php index ac2a1f05..eb04a7e4 100644 --- a/Model/Quote/AddressUpdater.php +++ b/Model/Quote/AddressUpdater.php @@ -19,7 +19,7 @@ use \Magento\Braintree\Model\Paypal\Helper\AbstractHelper; use Magento\Quote\{ - Api\CartRepositoryInterface, Model\Quote, Model\Quote\Address + Model\Quote, Model\Quote\Address }; use Vipps\Payment\Gateway\Transaction\ShippingDetails; @@ -30,41 +30,19 @@ */ class AddressUpdater extends AbstractHelper { - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - - /** - * AddressUpdater constructor. - * @param CartRepositoryInterface $cartRepository - */ - public function __construct(CartRepositoryInterface $cartRepository) - { - $this->cartRepository = $cartRepository; - } - /** * Update quote addresses from source address. * * @param Quote $quote * @param Address $sourceAddress + * @throws \Exception */ public function fromSourceAddress(Quote $quote, Address $sourceAddress) { $quote->setMayEditShippingAddress(false); - - $this->updateQuoteAddresses($quote, $sourceAddress); $this->disabledQuoteAddressValidation($quote); - /** - * Unset shipping assignment to prevent from saving / applying outdated data - * @see \Magento\Quote\Model\QuoteRepository\SaveHandler::processShippingAssignment - */ - if ($quote->getExtensionAttributes()) { - $quote->getExtensionAttributes()->setShippingAssignments(null); - } - $this->cartRepository->save($quote); + $this->updateQuoteAddresses($quote, $sourceAddress); } /** @@ -72,6 +50,7 @@ public function fromSourceAddress(Quote $quote, Address $sourceAddress) * * @param Quote $quote * @param Address $sourceAddress + * @throws \Exception */ private function updateQuoteAddresses(Quote $quote, Address $sourceAddress) { @@ -81,8 +60,8 @@ private function updateQuoteAddresses(Quote $quote, Address $sourceAddress) } $billingAddress = $quote->getBillingAddress(); - $this->updateAddress($billingAddress, $sourceAddress); $billingAddress->setSameAsBilling(false); + $this->updateAddress($billingAddress, $sourceAddress); } /** @@ -90,6 +69,7 @@ private function updateQuoteAddresses(Quote $quote, Address $sourceAddress) * * @param Address $destAddress * @param Address $sourceAddress + * @throws \Exception */ private function updateAddress(Address $destAddress, Address $sourceAddress) { @@ -100,6 +80,7 @@ private function updateAddress(Address $destAddress, Address $sourceAddress) ->setPostcode($sourceAddress->getPostcode()) ->setSaveInAddressBook(false) ->setSameAsBilling(true) - ->setCustomerAddressId(null); + ->setCustomerAddressId(null) + ->save(); } -} \ No newline at end of file +} From e578f9e9034ff8da13f1a2e0ab741bfebd1e59bb Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Mon, 10 Dec 2018 14:07:54 +0200 Subject: [PATCH 062/121] VIPPS-156: Improve background processing - added function of cancelling order in case when error occurred --- Api/CommandManagerInterface.php | 10 ++ Cron/FetchOrderFromVipps.php | 147 ++++++++++++++-------------- Gateway/Command/CommandManager.php | 15 +++ Gateway/Transaction/Transaction.php | 18 ++++ Model/OrderPlace.php | 59 +++++------ 5 files changed, 147 insertions(+), 102 deletions(-) diff --git a/Api/CommandManagerInterface.php b/Api/CommandManagerInterface.php index 323c4599..ab479e17 100644 --- a/Api/CommandManagerInterface.php +++ b/Api/CommandManagerInterface.php @@ -54,4 +54,14 @@ public function getPaymentDetails($arguments = []); * @throws VippsException */ public function getOrderStatus($orderId); + + /** + * Method to execute cancel Command. + * + * @param InfoInterface $payment + * @param array $arguments + * + * @return mixed + */ + public function cancel(InfoInterface $payment, $arguments = []); } diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index bbac7b2c..169ecc7c 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -121,7 +121,7 @@ public function __construct( * Create orders from Vipps that are not created in Magento yet * * @throws NoSuchEntityException - * @throws LocalizedException + * @throws \Exception */ public function execute() { @@ -136,29 +136,7 @@ public function execute() $quoteCollection->count() //@codingStandardsIgnoreLine )); foreach ($quoteCollection as $quote) { - /** @var Quote $quote */ - try { - // prepare environment before processing - $this->prepareEnv($quote); - - // fetch order status from vipps - $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); - - // check if quote should be canceled - if ($this->shouldCancelQuote($quote, $transaction, $reason)) { - $this->cancelQuote($quote, $reason); - } else { - // process quote - $this->processQuote($quote, $transaction); - } - } catch (VippsException $e) { - $this->processVippsException($quote, $e); - $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); - } catch (\Throwable $e) { - $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); - } finally { - usleep(1000000); //delay for 1 second - } + $this->processQuote($quote); } $currentPage++; } while ($currentPage <= $quoteCollection->getLastPageNumber()); @@ -168,57 +146,68 @@ public function execute() } /** + * Main process + * * @param Quote $quote + * + * @throws \Exception */ - private function prepareEnv(Quote $quote) + private function processQuote(Quote $quote) { - // set quote store as current store - $this->scopeCodeResolver->clean(); - $this->storeManager->setCurrentStore($quote->getStore()->getId()); + try { + $order = null; + + $this->prepareEnv($quote); + + // fetch order status from vipps + $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); + if ($transaction->isTransactionAborted()) { + $this->cancelQuote($quote, 'aborted on vipps side'); + return; + } + + if ($transaction->isTransactionNotReserved()) { + if ($this->isQuoteExpired($quote, new \DateInterval('PT5M'))) { + $this->cancelQuote($quote, 'expired after 5 min'); + } + return; + } + + $order = $this->placeOrder(clone $quote, $transaction); + } catch (VippsException $e) { + if ($e->getCode() < ZendResponse::STATUS_CODE_500) { + $this->cancelQuote($quote, $e); + } + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); + } catch (\Throwable $e) { + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); + } finally { + $this->postProcessing($quote, $order); + usleep(1000000); //delay for 1 second + } } /** * @param Quote $quote - * @param Transaction $transaction - * @param null $reason - * - * @return bool - * @throws \Exception */ - private function shouldCancelQuote(Quote $quote, Transaction $transaction, &$reason = null) + private function prepareEnv(Quote $quote) { - if ($transaction->isTransactionAborted()) { - $reason = 'aborted'; - return true; - } - if ($this->shouldCancelExpiredQuote($quote, $transaction)) { - $reason = 'expired after 5 min'; - return true; - } - return false; + // set quote store as current store + $this->scopeCodeResolver->clean(); + $this->storeManager->setCurrentStore($quote->getStore()->getId()); } /** * @param Quote $quote - * @param Transaction $transaction + * @param \DateInterval $interval * * @return bool - * @throws \Exception */ - private function shouldCancelExpiredQuote(Quote $quote, Transaction $transaction) + private function isQuoteExpired(Quote $quote, \DateInterval $interval) { - $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add(new \DateInterval('PT5M')); //@codingStandardsIgnoreLine - $isQuoteExpired5M = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine - - return $isQuoteExpired5M - && (in_array( - $transaction->getTransactionInfo()->getStatus(), - [ - Transaction::TRANSACTION_STATUS_REGISTER, - Transaction::TRANSACTION_STATUS_INITIATE - ] - )); - + $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine + $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine + return $isQuoteExpired; } /** @@ -246,33 +235,21 @@ private function fetchOrderStatus($orderId) * @throws LocalizedException * @throws WrongAmountException */ - private function processQuote(CartInterface $quote, Transaction $transaction) + private function placeOrder(CartInterface $quote, Transaction $transaction) { $order = $this->orderPlace->execute($quote, $transaction); - if (!$order) { + if ($order) { + $this->logger->debug(sprintf('Order placed: "%s"', $order->getIncrementId())); + } else { $this->logger->critical(sprintf( 'Order has not been placed, quote id: "%s", reserved_order_id: "%s"', $quote->getId(), $quote->getReservedOrderId() )); - } else { - $this->logger->debug(sprintf('Order placed: "%s"', $order->getIncrementId())); } return $order; } - /** - * @param CartInterface $quote - * @param VippsException $e - */ - private function processVippsException(CartInterface $quote, VippsException $e) - { - if ($e->getCode() < ZendResponse::STATUS_CODE_500) { - /** @var Payment $payment */ - $this->cancelQuote($quote, $e); - } - } - /** * Cancel quote by setting reserved_order_id to null * @@ -312,6 +289,28 @@ private function cancelQuote(CartInterface $quote, $info = null) )); } + /** + * @param OrderInterface|null $order + * @param Quote $quote + * + * @throws NoSuchEntityException + * @throws \Exception + */ + private function postProcessing(Quote $quote, OrderInterface $order = null) + { + if (!$order) { + /** @var Quote $updatedQuote */ + $updatedQuote = $this->cartRepository->get($quote->getEntityId()); + if ($updatedQuote->getReservedOrderId()) { + // more then 1 day + if ($this->isQuoteExpired($updatedQuote, new \DateInterval('P1D'))) { + $this->commandManager->cancel($updatedQuote->getPayment()); + $this->cancelQuote($updatedQuote, 'expired after 1 day'); + } + } + } + } + /** * @param $currentPage * diff --git a/Gateway/Command/CommandManager.php b/Gateway/Command/CommandManager.php index 64a90c96..c383e6d5 100644 --- a/Gateway/Command/CommandManager.php +++ b/Gateway/Command/CommandManager.php @@ -88,6 +88,21 @@ public function getOrderStatus($orderId) return $this->executeByCode('getOrderStatus', null, ['orderId' => $orderId]); } + /** + * {@inheritdoc} + * + * @param InfoInterface $payment + * @param array $arguments + * + * @return ResultInterface|mixed|null + * @throws CommandException + * @throws NotFoundException + */ + public function cancel(InfoInterface $payment, $arguments = []) + { + return $this->executeByCode('cancel', $payment, $arguments); + } + /** * {@inheritdoc} * diff --git a/Gateway/Transaction/Transaction.php b/Gateway/Transaction/Transaction.php index 2dd5b186..f090764c 100644 --- a/Gateway/Transaction/Transaction.php +++ b/Gateway/Transaction/Transaction.php @@ -236,6 +236,24 @@ public function isTransactionAborted() return false; } + /** + * Check that transaction has not been reserved yet + * + * @return bool + */ + public function isTransactionNotReserved() + { + $statuses = [ + Transaction::TRANSACTION_STATUS_REGISTER, + Transaction::TRANSACTION_STATUS_INITIATE + ]; + if (in_array($this->getTransactionInfo()->getStatus(), $statuses)) { + return true; + } + + return false; + } + /** * Method to retrieve Transaction Id. * diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 6e7844a3..3445528d 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -32,7 +32,7 @@ CartRepositoryInterface, CartManagementInterface, Data\CartInterface }; use Magento\Quote\Model\Quote; -use Magento\Store\Model\ScopeInterface; +use Vipps\Payment\Api\CommandManagerInterface; use Vipps\Payment\Gateway\Exception\WrongAmountException; use Vipps\Payment\Gateway\{ Transaction\Transaction, Exception\VippsException @@ -98,6 +98,11 @@ class OrderPlace */ private $config; + /** + * @var CommandManagerInterface + */ + private $commandManager; + /** * OrderPlace constructor. * @@ -111,7 +116,7 @@ class OrderPlace * @param QuoteUpdater $quoteUpdater * @param LockManager $lockManager * @param ConfigInterface $config - * + * @param CommandManagerInterface $commandManager * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -124,7 +129,8 @@ public function __construct( Processor $processor, QuoteUpdater $quoteUpdater, LockManager $lockManager, - ConfigInterface $config + ConfigInterface $config, + CommandManagerInterface $commandManager ) { $this->orderRepository = $orderRepository; $this->cartRepository = $cartRepository; @@ -136,6 +142,7 @@ public function __construct( $this->quoteUpdater = $quoteUpdater; $this->lockManager = $lockManager; $this->config = $config; + $this->commandManager = $commandManager; } /** @@ -246,9 +253,7 @@ private function canPlaceOrder(Transaction $transaction) */ private function placeOrder(CartInterface $quote, Transaction $transaction) { - $clonedQuote = clone $quote; - - $reservedOrderId = $clonedQuote->getReservedOrderId(); + $reservedOrderId = $quote->getReservedOrderId(); if (!$reservedOrderId) { return null; } @@ -256,29 +261,31 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) $order = $this->orderLocator->get($reservedOrderId); if (!$order) { //this is used only for express checkout - $this->quoteUpdater->execute($clonedQuote); - /** @var Quote $clonedQuote */ - $clonedQuote = $this->cartRepository->get($clonedQuote->getId()); - if ($clonedQuote->getReservedOrderId() !== $reservedOrderId) { + $this->quoteUpdater->execute($quote); + /** @var Quote $quote */ + $quote = $this->cartRepository->get($quote->getId()); + if ($quote->getReservedOrderId() !== $reservedOrderId) { return null; } - $this->prepareQuote($clonedQuote); + $this->prepareQuote($quote); - $clonedQuote->getShippingAddress()->setCollectShippingRates(true); - $clonedQuote->collectTotals(); + $quote->getShippingAddress()->setCollectShippingRates(true); + $quote->collectTotals(); - $this->validateAmount($clonedQuote, $transaction); - - // set quote active, collect totals and place order - $clonedQuote->setIsActive(true); - $orderId = $this->cartManagement->placeOrder($clonedQuote->getId()); - - $order = $this->orderRepository->get($orderId); + if ($this->validateAmount($quote, $transaction)) { + // set quote active, collect totals and place order + $quote->setIsActive(true); + $orderId = $this->cartManagement->placeOrder($quote->getId()); + $order = $this->orderRepository->get($orderId); + } else { + // cancel order on vipps side + $this->commandManager->cancel($quote->getPayment()); + } } - $clonedQuote->setReservedOrderId(null); - $this->cartRepository->save($clonedQuote); + $quote->setReservedOrderId(null); + $this->cartRepository->save($quote); return $order; } @@ -385,17 +392,13 @@ private function notify($order) * @param CartInterface $quote * @param Transaction $transaction * - * @throws WrongAmountException + * @return bool */ private function validateAmount(CartInterface $quote, Transaction $transaction) { $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); - if ($quoteAmount !== $vippsAmount) { - throw new WrongAmountException( - __('Reserved amount in Vipps "%1" is not equal to order amount "%2".', $vippsAmount, $quoteAmount) - ); - } + return $quoteAmount == $vippsAmount; } } From 113a05b1b33e0740a00f41d4cb6b9371c3bc9ec0 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Mon, 10 Dec 2018 19:19:40 +0200 Subject: [PATCH 063/121] VIPPS-156: Improve background processing - added function of cancelling order in case when error occurred --- Cron/FetchOrderFromVipps.php | 128 ++++++++++++++++++---------- Gateway/Transaction/Transaction.php | 6 +- Model/OrderPlace.php | 12 +-- 3 files changed, 86 insertions(+), 60 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 169ecc7c..6c0bc0da 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -45,6 +45,11 @@ class FetchOrderFromVipps */ const COLLECTION_PAGE_SIZE = 100; + /** + * @var string + */ + const MAX_NUMBER_OF_ATTEMPTS = 3; + /** * @var CollectionFactory */ @@ -137,6 +142,7 @@ public function execute() )); foreach ($quoteCollection as $quote) { $this->processQuote($quote); + usleep(1000000); //delay for 1 second } $currentPage++; } while ($currentPage <= $quoteCollection->getLastPageNumber()); @@ -150,43 +156,85 @@ public function execute() * * @param Quote $quote * + * @throws NoSuchEntityException * @throws \Exception */ private function processQuote(Quote $quote) { try { $order = null; + $transaction = null; + $currentException = null; $this->prepareEnv($quote); - // fetch order status from vipps $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); + if ($transaction->isTransactionAborted()) { - $this->cancelQuote($quote, 'aborted on vipps side'); + $this->cancelQuote($quote, $transaction, 'canceled on vipps side'); + } else { + $order = $this->placeOrder(clone $quote, $transaction); + } + } catch (\Throwable $e) { + $currentException = $e; //@codingStandardsIgnoreLine + $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); + } finally { + if ($order) { + // if order exists - nothing to do, all good return; } - - if ($transaction->isTransactionNotReserved()) { - if ($this->isQuoteExpired($quote, new \DateInterval('PT5M'))) { - $this->cancelQuote($quote, 'expired after 5 min'); - } + /** @var Quote $quote */ + $quote = $this->cartRepository->get($quote->getEntityId()); + if (!$quote->getReservedOrderId()) { + // if quote does not have reserved order id - such quote will not be processed next time return; } - $order = $this->placeOrder(clone $quote, $transaction); - } catch (VippsException $e) { - if ($e->getCode() < ZendResponse::STATUS_CODE_500) { - $this->cancelQuote($quote, $e); + // count not success (order not created) attempts of this process + if ($this->countAttempts($quote) >= $this->getMaxNumberOfAttempts()) { + $this->cancelQuote( + $quote, + $transaction, + sprintf( + 'canceled after "%s" attempts, last error "%s"', + $this->getMaxNumberOfAttempts(), + $currentException ? $currentException->getMessage() : 'n/a' + ) + ); + return; } - $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); - } catch (\Throwable $e) { - $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); - } finally { - $this->postProcessing($quote, $order); - usleep(1000000); //delay for 1 second } } + /** + * @param Quote|CartInterface $quote + * + * @return int + * @throws LocalizedException + */ + private function countAttempts($quote) + { + $additionalInfo = $quote->getPayment()->getAdditionalInformation(); + $attempts = (int)($additionalInfo['vipps']['attempts'] ?? 0); + + $additionalInfo['vipps']['attempts'] = ++$attempts; + $quote->getPayment()->setAdditionalInformation($additionalInfo); + + if ($attempts < $this->getMaxNumberOfAttempts()) { + $this->cartRepository->save($quote); + } + + return $attempts; + } + + /** + * @return int + */ + private function getMaxNumberOfAttempts() + { + return (int)self::MAX_NUMBER_OF_ATTEMPTS; + } + /** * @param Quote $quote */ @@ -203,7 +251,7 @@ private function prepareEnv(Quote $quote) * * @return bool */ - private function isQuoteExpired(Quote $quote, \DateInterval $interval) + private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine { $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine @@ -253,12 +301,15 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) /** * Cancel quote by setting reserved_order_id to null * - * @param CartInterface $quote - * @param \Exception|string $info + * @param CartInterface|Quote $quote + * @param Transaction|null $transaction + * @param null $info + * + * @throws LocalizedException */ - private function cancelQuote(CartInterface $quote, $info = null) + private function cancelQuote(CartInterface $quote, Transaction $transaction = null, $info = null) { - $reservedOrderId = $quote->getReservedOrderId(); + $savedQuote = clone $quote; $quote->setReservedOrderId(null); $additionalInformation = []; @@ -274,40 +325,25 @@ private function cancelQuote(CartInterface $quote, $info = null) $additionalInformation = array_merge( $additionalInformation, [ - 'reserved_order_id' => $reservedOrderId + 'reserved_order_id' => $savedQuote->getReservedOrderId() ] ); $payment = $quote->getPayment(); - $payment->setAdditionalInformation('vipps', $additionalInformation); + $existingAdditionalInfo = $payment->getAdditionalInformation()['vipps'] ?? []; + $payment->setAdditionalInformation('vipps', array_merge($existingAdditionalInfo, $additionalInformation)); $this->cartRepository->save($quote); $this->logger->debug(sprintf( - 'Quote was canceled, id: "%s", reserved_order_id: "%s"', + 'Quote was canceled, id: "%s", reserved_order_id: "%s", cancel reason "%s"', $quote->getId(), - $reservedOrderId + $savedQuote->getReservedOrderId(), + $additionalInformation['cancel_reason_phrase'] )); - } - /** - * @param OrderInterface|null $order - * @param Quote $quote - * - * @throws NoSuchEntityException - * @throws \Exception - */ - private function postProcessing(Quote $quote, OrderInterface $order = null) - { - if (!$order) { - /** @var Quote $updatedQuote */ - $updatedQuote = $this->cartRepository->get($quote->getEntityId()); - if ($updatedQuote->getReservedOrderId()) { - // more then 1 day - if ($this->isQuoteExpired($updatedQuote, new \DateInterval('P1D'))) { - $this->commandManager->cancel($updatedQuote->getPayment()); - $this->cancelQuote($updatedQuote, 'expired after 1 day'); - } - } + // cancel order on vipps side + if ($transaction && $transaction->isTransactionReserved()) { + $this->commandManager->cancel($savedQuote->getPayment()); } } diff --git a/Gateway/Transaction/Transaction.php b/Gateway/Transaction/Transaction.php index f090764c..b783838d 100644 --- a/Gateway/Transaction/Transaction.php +++ b/Gateway/Transaction/Transaction.php @@ -241,11 +241,11 @@ public function isTransactionAborted() * * @return bool */ - public function isTransactionNotReserved() + public function isTransactionReserved() { $statuses = [ - Transaction::TRANSACTION_STATUS_REGISTER, - Transaction::TRANSACTION_STATUS_INITIATE + Transaction::TRANSACTION_STATUS_RESERVE, + Transaction::TRANSACTION_STATUS_RESERVED ]; if (in_array($this->getTransactionInfo()->getStatus(), $statuses)) { return true; diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 3445528d..bb80069c 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -227,17 +227,7 @@ private function releaseLock($lockName) */ private function canPlaceOrder(Transaction $transaction) { - if (in_array( - $transaction->getTransactionInfo()->getStatus(), - [ - Transaction::TRANSACTION_STATUS_RESERVE, - Transaction::TRANSACTION_STATUS_RESERVED - ] - )) { - return true; - } - - return false; + return $transaction->isTransactionReserved(); } /** From 91e1ec3253324e6436cc68d1d2cb2af52b970c8f Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Mon, 10 Dec 2018 19:21:35 +0200 Subject: [PATCH 064/121] VIPPS-156: Improve background processing - added function of cancelling order in case when error occurred --- Cron/FetchOrderFromVipps.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 6c0bc0da..dd56f4fb 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -19,7 +19,7 @@ use Magento\Framework\App\Config\ScopeCodeResolver; use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; -use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory, Quote, Quote\Payment}; +use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory, Quote}; use Magento\Sales\Api\Data\OrderInterface; use Vipps\Payment\{ Api\CommandManagerInterface, @@ -29,7 +29,6 @@ Gateway\Transaction\TransactionBuilder }; use Vipps\Payment\Gateway\Exception\WrongAmountException; -use Zend\Http\Response as ZendResponse; use Psr\Log\LoggerInterface; use Magento\Framework\Exception\LocalizedException; From b8119c9b0a691819bfbc8733152b8363e886ff30 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Mon, 10 Dec 2018 19:58:38 +0200 Subject: [PATCH 065/121] VIPPS-173: Publication v.1.0.13 --- Cron/FetchOrderFromVipps.php | 2 +- Model/OrderPlace.php | 29 +++++++++++++++-------------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index dd56f4fb..53c249dd 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -172,7 +172,7 @@ private function processQuote(Quote $quote) if ($transaction->isTransactionAborted()) { $this->cancelQuote($quote, $transaction, 'canceled on vipps side'); } else { - $order = $this->placeOrder(clone $quote, $transaction); + $order = $this->placeOrder($quote, $transaction); } } catch (\Throwable $e) { $currentException = $e; //@codingStandardsIgnoreLine diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index bb80069c..cf6f33e1 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -243,7 +243,8 @@ private function canPlaceOrder(Transaction $transaction) */ private function placeOrder(CartInterface $quote, Transaction $transaction) { - $reservedOrderId = $quote->getReservedOrderId(); + $clonedQuote = clone $quote; + $reservedOrderId = $clonedQuote->getReservedOrderId(); if (!$reservedOrderId) { return null; } @@ -251,31 +252,31 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) $order = $this->orderLocator->get($reservedOrderId); if (!$order) { //this is used only for express checkout - $this->quoteUpdater->execute($quote); - /** @var Quote $quote */ - $quote = $this->cartRepository->get($quote->getId()); - if ($quote->getReservedOrderId() !== $reservedOrderId) { + $this->quoteUpdater->execute($clonedQuote); + /** @var Quote $clonedQuote */ + $clonedQuote = $this->cartRepository->get($clonedQuote->getId()); + if ($clonedQuote->getReservedOrderId() !== $reservedOrderId) { return null; } - $this->prepareQuote($quote); + $this->prepareQuote($clonedQuote); - $quote->getShippingAddress()->setCollectShippingRates(true); - $quote->collectTotals(); + $clonedQuote->getShippingAddress()->setCollectShippingRates(true); + $clonedQuote->collectTotals(); - if ($this->validateAmount($quote, $transaction)) { + if ($this->validateAmount($clonedQuote, $transaction)) { // set quote active, collect totals and place order - $quote->setIsActive(true); - $orderId = $this->cartManagement->placeOrder($quote->getId()); + $clonedQuote->setIsActive(true); + $orderId = $this->cartManagement->placeOrder($clonedQuote->getId()); $order = $this->orderRepository->get($orderId); } else { // cancel order on vipps side - $this->commandManager->cancel($quote->getPayment()); + $this->commandManager->cancel($clonedQuote->getPayment()); } } - $quote->setReservedOrderId(null); - $this->cartRepository->save($quote); + $clonedQuote->setReservedOrderId(null); + $this->cartRepository->save($clonedQuote); return $order; } From 9d5d4d71eb8177dbdd73958f1a88a7858b0a8688 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 24 Dec 2018 12:30:00 +0200 Subject: [PATCH 066/121] VIPPS-176: Create monitoring tables. Vipps quote, quote submit attempts. --- Setup/UpgradeSchema.php | 141 +++++++++++++++++++++++++++++++++++----- etc/module.xml | 2 +- 2 files changed, 126 insertions(+), 17 deletions(-) diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 80472d72..1084fd97 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -22,8 +22,11 @@ class UpgradeSchema implements UpgradeSchemaInterface // @codingStandardsIgnoreLine { /** - * @param SchemaSetupInterface $setup - * @param ModuleContextInterface $context + * Schema changes on the module upgrade. + * + * @param SchemaSetupInterface $setup Setup interface. + * @param ModuleContextInterface $context Module context. + * @throws \Zend_Db_Exception */ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) // @codingStandardsIgnoreLine { @@ -31,22 +34,128 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con $installer->startSetup(); if (version_compare($context->getVersion(), '1.1.0', '<')) { - $tableName = $installer->getTable('vipps_payment_jwt'); - $installer->getConnection()->addColumn( - $tableName, - 'scope', - [ - 'type' => Table::TYPE_TEXT, - 'length' => 8, - 'after' => 'token_id', - 'nullable' => false, - 'default' => 'default', - 'comment' => 'Scope' - ] - ); - $installer->getConnection()->truncateTable($tableName); + $this->addPaymentJwtScope($installer); + } + + if (version_compare($context->getVersion(), '1.1.1', '<')) { + $this->createVippsQuoteTable($installer); + $this->createVippsAttemptsTable($installer); } $installer->endSetup(); } + + /** + * @param SchemaSetupInterface $installer + */ + private function addPaymentJwtScope(SchemaSetupInterface $installer) + { + $tableName = $installer->getTable('vipps_payment_jwt'); + $installer->getConnection()->addColumn( + $tableName, + 'scope', + [ + 'type' => Table::TYPE_TEXT, + 'length' => 8, + 'after' => 'token_id', + 'nullable' => false, + 'default' => 'default', + 'comment' => 'Scope' + ] + ); + $installer->getConnection()->truncateTable($tableName); + } + + /** + * Install Vipps quote monitoring table. + * + * @param SchemaSetupInterface $installer + * @throws \Zend_Db_Exception + */ + private function createVippsQuoteTable(SchemaSetupInterface $installer): void + { + $connection = $installer->getConnection(); + + $table = $connection->newTable($connection->getTableName('vipps_quote')) + ->addColumn( + 'entity_id', + Table::TYPE_INTEGER, + null, + ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], + 'Entity Id' + )->addColumn( + 'quote_id', + Table::TYPE_INTEGER, + null, + ['nullable' => false, 'unsigned' => true], + 'Quote Id' + )->addColumn( + 'reserved_order_id', + Table::TYPE_TEXT, + 32, + ['nullable' => false, 'default' => ''], + 'Order Increment Id' + )->addColumn( + 'created_at', + Table::TYPE_DATETIME, + null, + [], + 'Created at' + )->addColumn( + 'updated_at', + Table::TYPE_DATETIME, + null, + [], + 'Updated at'); + + $installer->getConnection()->createTable($table); + } + + /** + * Install Quote submitting attempts table. + * + * @param SchemaSetupInterface $installer Schema installer. + * @throws \Zend_Db_Exception + */ + private function createVippsAttemptsTable(SchemaSetupInterface $installer): void + { + $connection = $installer->getConnection(); + + $table = $connection->newTable($connection->getTableName('vipps_quote_attempt')) + ->addColumn( + 'entity_id', + Table::TYPE_INTEGER, + null, + ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], + 'Entity Id' + )->addColumn( + 'parent_id', + Table::TYPE_INTEGER, + null, + ['nullable' => false, 'unsigned' => true], + 'Vipps Quote Id' + )->addColumn( + 'message', + Table::TYPE_TEXT, + null, + [], + 'Message' + )->addColumn( + 'created_at', + Table::TYPE_DATETIME, + null, + [], + 'Created at' + ) + ->addIndex($installer->getIdxName('vipps_quote_attempts', 'parent_id'), 'parent_id') + ->addForeignKey( + $installer->getFkName('vipps_quote_attempts', 'parent_id', 'vipps_quote', 'entity_id'), + 'parent_id', + 'vipps_quote', + 'entity_id', + Table::ACTION_CASCADE + ); + + $installer->getConnection()->createTable($table); + } } diff --git a/etc/module.xml b/etc/module.xml index 7087d9a5..4dd5d791 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + From 8abec57688b9c6e640a8289c124971c7e58ac05d Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 24 Dec 2018 13:09:49 +0200 Subject: [PATCH 067/121] VIPPS-176: Update module version. --- Setup/UpgradeSchema.php | 2 +- etc/module.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 1084fd97..122a0c25 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -37,7 +37,7 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con $this->addPaymentJwtScope($installer); } - if (version_compare($context->getVersion(), '1.1.1', '<')) { + if (version_compare($context->getVersion(), '1.2.0', '<')) { $this->createVippsQuoteTable($installer); $this->createVippsAttemptsTable($installer); } diff --git a/etc/module.xml b/etc/module.xml index 4dd5d791..08d262a4 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + From f9339c6c3aa9493f5d0df58589469741829a6d3c Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 31 Dec 2018 14:14:09 +0100 Subject: [PATCH 068/121] VIPPS-177: Save quote monitoring entity when redirect customer to Vipps. --- Api/Monitoring/Data/QuoteInterface.php | 56 +++++++++++++++ .../Quote/AttemptRepositoryInterface.php | 26 +++++++ Api/Monitoring/QuoteManagementInterface.php | 33 +++++++++ Api/Monitoring/QuoteRepositoryInterface.php | 34 +++++++++ Gateway/Response/InitiateHandler.php | 49 ++++++++++--- Model/Monitoring/Quote.php | 69 +++++++++++++++++++ Model/Monitoring/QuoteManagement.php | 68 ++++++++++++++++++ Model/Monitoring/QuoteRepository.php | 67 ++++++++++++++++++ Model/ResourceModel/Monitoring/Quote.php | 46 +++++++++++++ .../Monitoring/Quote/Attempt.php | 44 ++++++++++++ .../Monitoring/Quote/Collection.php | 60 ++++++++++++++++ 11 files changed, 541 insertions(+), 11 deletions(-) create mode 100644 Api/Monitoring/Data/QuoteInterface.php create mode 100644 Api/Monitoring/Quote/AttemptRepositoryInterface.php create mode 100644 Api/Monitoring/QuoteManagementInterface.php create mode 100644 Api/Monitoring/QuoteRepositoryInterface.php create mode 100644 Model/Monitoring/Quote.php create mode 100644 Model/Monitoring/QuoteManagement.php create mode 100644 Model/Monitoring/QuoteRepository.php create mode 100644 Model/ResourceModel/Monitoring/Quote.php create mode 100644 Model/ResourceModel/Monitoring/Quote/Attempt.php create mode 100644 Model/ResourceModel/Monitoring/Quote/Collection.php diff --git a/Api/Monitoring/Data/QuoteInterface.php b/Api/Monitoring/Data/QuoteInterface.php new file mode 100644 index 00000000..1a502174 --- /dev/null +++ b/Api/Monitoring/Data/QuoteInterface.php @@ -0,0 +1,56 @@ +cartRepository = $cartRepository; $this->subjectReader = $subjectReader; $this->checkoutHelper = $checkoutHelper; $this->customerSession = $customerSession; + $this->resourceConnection = $resourceConnection; + $this->quoteMonitoringManagement = $quoteMonitoringManagement; } /** - * {@inheritdoc} + * Save quote payment method. * * @param array $handlingSubject * @param array $responseBody @@ -100,6 +114,19 @@ public function handle(array $handlingSubject, array $responseBody) //@codingSta $payment->setMethod('vipps'); $quote->setIsActive(false); - $this->cartRepository->save($quote); + $connection = $this->resourceConnection->getConnection(); + + try { + $connection->beginTransaction(); + + $this->cartRepository->save($quote); + $this->quoteMonitoringManagement->create($quote); + + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw $e; + } + } } diff --git a/Model/Monitoring/Quote.php b/Model/Monitoring/Quote.php new file mode 100644 index 00000000..10d1fb29 --- /dev/null +++ b/Model/Monitoring/Quote.php @@ -0,0 +1,69 @@ +_init(QuoteResource::class); + } + + /** + * @param int $quoteId + * @return self + */ + public function setQuoteId(int $quoteId) + { + return $this->setData(self::QUOTE_ID, $quoteId); + } + + /** + * @param string $reservedOrderId + * @return self + */ + public function setReservedOrderId(string $reservedOrderId) + { + return $this->setData(self::RESERVED_ORDER_ID, $reservedOrderId); + } + + /** + * @return int + */ + public function getQuoteId() + { + return $this->getData(self::QUOTE_ID); + } + + /** + * @return string + */ + public function getReservedOrderId() + { + return $this->getData(self::RESERVED_ORDER_ID); + } +} diff --git a/Model/Monitoring/QuoteManagement.php b/Model/Monitoring/QuoteManagement.php new file mode 100644 index 00000000..a474e354 --- /dev/null +++ b/Model/Monitoring/QuoteManagement.php @@ -0,0 +1,68 @@ +quoteFactory = $quoteFactory; + $this->quoteRepository = $quoteRepository; + } + + /** + * @param CartInterface $cart + * @return QuoteInterface + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function create(CartInterface $cart) + { + /** @var Quote $monitoringQuote */ + $monitoringQuote = $this->quoteFactory->create(); + + $monitoringQuote + ->setQuoteId($cart->getId()) + ->setReservedOrderId($cart->getReservedOrderId()); + + return $this->quoteRepository->save($monitoringQuote); + + } +} diff --git a/Model/Monitoring/QuoteRepository.php b/Model/Monitoring/QuoteRepository.php new file mode 100644 index 00000000..dc79b290 --- /dev/null +++ b/Model/Monitoring/QuoteRepository.php @@ -0,0 +1,67 @@ +quoteResource = $quoteResource; + } + + /** + * Save monitoring record + * + * @param QuoteInterface $quote + * @return QuoteInterface + * @throws CouldNotSaveException + */ + public function save(QuoteInterface $quote) + { + try { + $this->quoteResource->save($quote); + + return $quote; + } catch (\Exception $e) { + throw new CouldNotSaveException( + __( + 'Could not save Vipps Quote: %1', + $e->getMessage() + ), + $e + ); + } + } +} diff --git a/Model/ResourceModel/Monitoring/Quote.php b/Model/ResourceModel/Monitoring/Quote.php new file mode 100644 index 00000000..fa9ca8ef --- /dev/null +++ b/Model/ResourceModel/Monitoring/Quote.php @@ -0,0 +1,46 @@ +_init(self::TABLE_NAME, self::INDEX_FIELD); + } +} diff --git a/Model/ResourceModel/Monitoring/Quote/Attempt.php b/Model/ResourceModel/Monitoring/Quote/Attempt.php new file mode 100644 index 00000000..552f8e1b --- /dev/null +++ b/Model/ResourceModel/Monitoring/Quote/Attempt.php @@ -0,0 +1,44 @@ +_init(self::TABLE_NAME, self::INDEX_FIELD); + } +} diff --git a/Model/ResourceModel/Monitoring/Quote/Collection.php b/Model/ResourceModel/Monitoring/Quote/Collection.php new file mode 100644 index 00000000..592fb3bc --- /dev/null +++ b/Model/ResourceModel/Monitoring/Quote/Collection.php @@ -0,0 +1,60 @@ + Date: Wed, 2 Jan 2019 12:41:42 +0200 Subject: [PATCH 069/121] VIPPS-177: Code review. --- Api/Monitoring/Data/QuoteInterface.php | 33 +++++++++++++++++++ Gateway/Response/InitiateHandler.php | 9 ++--- .../Monitoring/Quote/Collection.php | 3 +- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/Api/Monitoring/Data/QuoteInterface.php b/Api/Monitoring/Data/QuoteInterface.php index 1a502174..d45faf4a 100644 --- a/Api/Monitoring/Data/QuoteInterface.php +++ b/Api/Monitoring/Data/QuoteInterface.php @@ -32,6 +32,16 @@ interface QuoteInterface */ const RESERVED_ORDER_ID = 'reserved_order_id'; + /** + * @const string + */ + const CREATED_AT = 'created_at'; + + /** + * @const string + */ + const UPDATED_AT = 'updated_at'; + /** * @param int $quoteId * @return self @@ -44,6 +54,18 @@ public function setQuoteId(int $quoteId); */ public function setReservedOrderId(string $reservedOrderId); + /** + * @param string $createdAt + * @return self + */ + public function setCreatedAt(string $createdAt); + + /** + * @param string $updatedAt + * @return self + */ + public function setUpdatedAt(string $updatedAt); + /** * @return int */ @@ -53,4 +75,15 @@ public function getQuoteId(); * @return string */ public function getReservedOrderId(); + + /** + * @return string + */ + public function getCreatedAt(int $createdAt); + + /** + * @return string + */ + public function getUpdatedAt(string $updatedAt); + } diff --git a/Gateway/Response/InitiateHandler.php b/Gateway/Response/InitiateHandler.php index f7e98047..967fc019 100644 --- a/Gateway/Response/InitiateHandler.php +++ b/Gateway/Response/InitiateHandler.php @@ -68,6 +68,8 @@ class InitiateHandler implements HandlerInterface * @param SubjectReader $subjectReader * @param CheckoutHelper $checkoutHelper * @param SessionManagerInterface $customerSession + * @param ResourceConnection $resourceConnection + * @param QuoteManagement $monitoringManagement */ public function __construct( CartRepositoryInterface $cartRepository, @@ -75,15 +77,14 @@ public function __construct( CheckoutHelper $checkoutHelper, SessionManagerInterface $customerSession, ResourceConnection $resourceConnection, - QuoteManagement $quoteMonitoringManagement - ) - { + QuoteManagement $monitoringManagement + ) { $this->cartRepository = $cartRepository; $this->subjectReader = $subjectReader; $this->checkoutHelper = $checkoutHelper; $this->customerSession = $customerSession; $this->resourceConnection = $resourceConnection; - $this->quoteMonitoringManagement = $quoteMonitoringManagement; + $this->quoteMonitoringManagement = $monitoringManagement; } /** diff --git a/Model/ResourceModel/Monitoring/Quote/Collection.php b/Model/ResourceModel/Monitoring/Quote/Collection.php index 592fb3bc..1e590bc0 100644 --- a/Model/ResourceModel/Monitoring/Quote/Collection.php +++ b/Model/ResourceModel/Monitoring/Quote/Collection.php @@ -53,8 +53,7 @@ public function __construct( EventManager $eventManager, $mainTable = 'vipps_quote', $resourceModel = Quote::class - ) - { + ) { parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel); } } From 1a265fb467747c52c7eb04c79267ef084f45d4b0 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Wed, 2 Jan 2019 17:13:33 +0200 Subject: [PATCH 070/121] VIPPS-177: Code review. --- Api/Monitoring/Data/QuoteInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Api/Monitoring/Data/QuoteInterface.php b/Api/Monitoring/Data/QuoteInterface.php index d45faf4a..3c170ac9 100644 --- a/Api/Monitoring/Data/QuoteInterface.php +++ b/Api/Monitoring/Data/QuoteInterface.php @@ -79,11 +79,11 @@ public function getReservedOrderId(); /** * @return string */ - public function getCreatedAt(int $createdAt); + public function getCreatedAt(); /** * @return string */ - public function getUpdatedAt(string $updatedAt); + public function getUpdatedAt(); } From 1e02f6d2802b38e375eb8066d4e1dfa81d61112a Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 3 Jan 2019 13:09:26 +0200 Subject: [PATCH 071/121] VIPPS-182: Add order cancellation configuration Add paypal module dependency. --- Model/Adminhtml/Source/Cancellation/Type.php | 55 +++++++++++ Model/Order/Cancellation/Config.php | 96 ++++++++++++++++++++ composer.json | 1 + etc/adminhtml/system.xml | 1 + etc/adminhtml/system/order_cancellation.xml | 53 +++++++++++ etc/config.xml | 20 ++-- etc/module.xml | 1 + 7 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 Model/Adminhtml/Source/Cancellation/Type.php create mode 100644 Model/Order/Cancellation/Config.php create mode 100644 etc/adminhtml/system/order_cancellation.xml diff --git a/Model/Adminhtml/Source/Cancellation/Type.php b/Model/Adminhtml/Source/Cancellation/Type.php new file mode 100644 index 00000000..3d4ed8c7 --- /dev/null +++ b/Model/Adminhtml/Source/Cancellation/Type.php @@ -0,0 +1,55 @@ + self::AUTOMATIC, + 'label' => __('Automatic'), + ], + [ + 'value' => self::MANUAL, + 'label' => __('Manual'), + ] + ]; + } +} diff --git a/Model/Order/Cancellation/Config.php b/Model/Order/Cancellation/Config.php new file mode 100644 index 00000000..50cbcc65 --- /dev/null +++ b/Model/Order/Cancellation/Config.php @@ -0,0 +1,96 @@ +scopeConfig = $scopeConfig; + } + + /** + * Cancellation type code. + * + * @return string + */ + public function getType() + { + return $this->getStoreConfig(self::XML_PATH_TYPE); + } + + /** + * Common method to return store config value. + * + * @param $path + * @return mixed + */ + private function getStoreConfig($path) + { + return $this->scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE); + } + + /** + * Number of failed attempts. + * + * @return int + */ + public function getAttemptsCount() + { + return $this->getStoreConfig(self::XML_PATH_ATTEMPT_COUNT); + } + + /** + * Return inactivity time in minutes. + * + * @return int + */ + public function getInactivityTime() + { + return $this->getStoreConfig(self::XML_PATH_INACTIVITY_TIME); + } +} diff --git a/composer.json b/composer.json index 3bb74cf8..221705df 100644 --- a/composer.json +++ b/composer.json @@ -9,6 +9,7 @@ "magento/module-sales": "101.0.*", "magento/module-payment": "100.2.*", "magento/module-checkout": "100.2.*", + "magento/module-paypal": "~100.2.0||~100.3.0", "psr/log": "~1.0" }, "autoload": { diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index a3a2eea0..0d5b393c 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -98,6 +98,7 @@ +
diff --git a/etc/adminhtml/system/order_cancellation.xml b/etc/adminhtml/system/order_cancellation.xml new file mode 100644 index 00000000..1d6b4c76 --- /dev/null +++ b/etc/adminhtml/system/order_cancellation.xml @@ -0,0 +1,53 @@ + + + + + + + + validate-number required-entry + + + + vipps/order_cancellation/attempts_count + + + + + Vipps\Payment\Model\Adminhtml\Source\Cancellation\Type + + + + vipps/order_cancellation/type + + + + + validate-number required-entry + + + + vipps/order_cancellation/customer_inactivity_time + + + diff --git a/etc/config.xml b/etc/config.xml index 0f551c8f..0e0e6bbb 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -15,7 +15,8 @@ * IN THE SOFTWARE. */ --> - + @@ -35,12 +36,19 @@ 1 1 develop - - - - - + + + + + + + + manual + 1 + 10 + + diff --git a/etc/module.xml b/etc/module.xml index 08d262a4..e8f6531a 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -21,6 +21,7 @@ + From 16ca0f77e0be22a9843ec0b2a15e6f90ddd8129b Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 3 Jan 2019 15:49:24 +0200 Subject: [PATCH 072/121] VIPPS-183: Create cancellation table. --- Setup/UpgradeSchema.php | 61 ++++++++++++++++++++++++++++++++++++++++- etc/module.xml | 2 +- 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 122a0c25..d578c404 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -16,8 +16,8 @@ namespace Vipps\Payment\Setup; -use Magento\Framework\Setup\{SchemaSetupInterface, UpgradeSchemaInterface, ModuleContextInterface}; use Magento\Framework\DB\Ddl\Table; +use Magento\Framework\Setup\{ModuleContextInterface, SchemaSetupInterface, UpgradeSchemaInterface}; class UpgradeSchema implements UpgradeSchemaInterface // @codingStandardsIgnoreLine { @@ -42,6 +42,9 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con $this->createVippsAttemptsTable($installer); } + if (version_compare($context->getVersion(), '1.2.1', '<')) { + $this->createCancellationTable($installer); + } $installer->endSetup(); } @@ -158,4 +161,60 @@ private function createVippsAttemptsTable(SchemaSetupInterface $installer): void $installer->getConnection()->createTable($table); } + + /** + * Create cancellation table. + * + * @param SchemaSetupInterface $installer + * @throws \Zend_Db_Exception + */ + private function createCancellationTable(SchemaSetupInterface $installer): void + { + $connection = $installer->getConnection(); + + $table = $connection->newTable($connection->getTableName('vipps_quote_cancellation')) + ->addColumn( + 'entity_id', + Table::TYPE_INTEGER, + null, + ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], + 'Entity Id' + )->addColumn( + 'parent_id', + Table::TYPE_INTEGER, + null, + ['nullable' => false, 'unsigned' => true], + 'Vipps Quote Id' + ) + ->addColumn( + 'type', + Table::TYPE_INTEGER, + 1, + [], + 'Type' + ) + ->addColumn( + 'reason', + Table::TYPE_TEXT, + null, + [], + 'Reason' + )->addColumn( + 'created_at', + Table::TYPE_DATETIME, + null, + [], + 'Created at' + ) + ->addIndex($installer->getIdxName('vipps_quote_cancellation', 'parent_id'), 'parent_id') + ->addForeignKey( + $installer->getFkName('vipps_quote_cancellation', 'parent_id', 'vipps_quote', 'entity_id'), + 'parent_id', + 'vipps_quote', + 'entity_id', + Table::ACTION_CASCADE + ); + + $installer->getConnection()->createTable($table); + } } diff --git a/etc/module.xml b/etc/module.xml index e8f6531a..76df3b9f 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + From 2644f08f2b77679dedee9c6aab474d3b06f598c4 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 3 Jan 2019 16:48:28 +0200 Subject: [PATCH 073/121] VIPPS-183: Update table structure. --- Setup/UpgradeSchema.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index d578c404..2cef236c 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -188,17 +188,24 @@ private function createCancellationTable(SchemaSetupInterface $installer): void ) ->addColumn( 'type', - Table::TYPE_INTEGER, - 1, + Table::TYPE_TEXT, + 10, [], 'Type' ) ->addColumn( - 'reason', + 'code', + Table::TYPE_TEXT, + 255, + [], + 'Reason Code' + ) + ->addColumn( + 'phrase', Table::TYPE_TEXT, null, [], - 'Reason' + 'Reason Phrase' )->addColumn( 'created_at', Table::TYPE_DATETIME, From 6f1b52f34c2bc008e9b3c6a0da74f841fd1ac6f0 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Tue, 8 Jan 2019 19:00:13 +0200 Subject: [PATCH 074/121] VIPPS-183: Fix tableschema updates. --- Setup/UpgradeSchema.php | 54 +++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 2cef236c..3340ace1 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -58,12 +58,12 @@ private function addPaymentJwtScope(SchemaSetupInterface $installer) $tableName, 'scope', [ - 'type' => Table::TYPE_TEXT, - 'length' => 8, - 'after' => 'token_id', + 'type' => Table::TYPE_TEXT, + 'length' => 8, + 'after' => 'token_id', 'nullable' => false, - 'default' => 'default', - 'comment' => 'Scope' + 'default' => 'default', + 'comment' => 'Scope' ] ); $installer->getConnection()->truncateTable($tableName); @@ -90,7 +90,7 @@ private function createVippsQuoteTable(SchemaSetupInterface $installer): void 'quote_id', Table::TYPE_INTEGER, null, - ['nullable' => false, 'unsigned' => true], + ['nullable' => true, 'unsigned' => true], 'Quote Id' )->addColumn( 'reserved_order_id', @@ -99,17 +99,32 @@ private function createVippsQuoteTable(SchemaSetupInterface $installer): void ['nullable' => false, 'default' => ''], 'Order Increment Id' )->addColumn( + 'attempts', + Table::TYPE_INTEGER, + 3, + ['nullable' => false, 'default' => '0'], + 'Attempts Number' + ) + ->addColumn( 'created_at', - Table::TYPE_DATETIME, + Table::TYPE_TIMESTAMP, null, - [], + [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT, Table::OPTION_NULLABLE => false], 'Created at' )->addColumn( 'updated_at', - Table::TYPE_DATETIME, + Table::TYPE_TIMESTAMP, null, - [], - 'Updated at'); + [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT_UPDATE, Table::OPTION_NULLABLE => false], + 'Updated at') + ->addIndex($installer->getIdxName('vipps_quote', 'quote_id'), 'quote_id') + ->addForeignKey( + $installer->getFkName('vipps_quote', 'quote_id', 'quote', 'entity_id'), + 'quote_id', + 'quote', + 'entity_id', + Table::ACTION_SET_NULL + ); $installer->getConnection()->createTable($table); } @@ -145,9 +160,9 @@ private function createVippsAttemptsTable(SchemaSetupInterface $installer): void 'Message' )->addColumn( 'created_at', - Table::TYPE_DATETIME, + Table::TYPE_TIMESTAMP, null, - [], + [Table::OPTION_NULLABLE => false, Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT], 'Created at' ) ->addIndex($installer->getIdxName('vipps_quote_attempts', 'parent_id'), 'parent_id') @@ -193,24 +208,17 @@ private function createCancellationTable(SchemaSetupInterface $installer): void [], 'Type' ) - ->addColumn( - 'code', - Table::TYPE_TEXT, - 255, - [], - 'Reason Code' - ) ->addColumn( 'phrase', Table::TYPE_TEXT, null, - [], + [Table::OPTION_NULLABLE => true], 'Reason Phrase' )->addColumn( 'created_at', - Table::TYPE_DATETIME, + Table::TYPE_TIMESTAMP, null, - [], + [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT, Table::OPTION_NULLABLE => false], 'Created at' ) ->addIndex($installer->getIdxName('vipps_quote_cancellation', 'parent_id'), 'parent_id') From e7a70328bd12bfa3f76d8008a8f416180fd1e425 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Tue, 8 Jan 2019 19:00:43 +0200 Subject: [PATCH 075/121] VIPPS-183: Commit interfaces. --- Api/Monitoring/Data/QuoteAttemptInterface.php | 82 +++++++++++++++++++ .../Data/QuoteCancellationInterface.php | 72 ++++++++++++++++ Api/Monitoring/Data/QuoteInterface.php | 29 ++++++- .../Quote/AttemptRepositoryInterface.php | 10 ++- .../Quote/CancellationFacadeInterface.php | 49 +++++++++++ Api/Monitoring/QuoteManagementInterface.php | 18 ++++ 6 files changed, 257 insertions(+), 3 deletions(-) create mode 100644 Api/Monitoring/Data/QuoteAttemptInterface.php create mode 100644 Api/Monitoring/Data/QuoteCancellationInterface.php create mode 100644 Api/Monitoring/Quote/CancellationFacadeInterface.php diff --git a/Api/Monitoring/Data/QuoteAttemptInterface.php b/Api/Monitoring/Data/QuoteAttemptInterface.php new file mode 100644 index 00000000..73e5bb5a --- /dev/null +++ b/Api/Monitoring/Data/QuoteAttemptInterface.php @@ -0,0 +1,82 @@ + Date: Tue, 8 Jan 2019 19:10:58 +0200 Subject: [PATCH 076/121] VIPPS-184: Employ quote monitoring with payment processing. Split cron tasks. Create attempts on cron processing. Create cancellation when limit is reached or transaction was aborted in vipps. Provide backward compatibility with already created quotes. --- Cron/CancelQuoteByAttempts.php | 311 +++++++++++++++++ Cron/FetchOrderFromVipps.php | 319 +++++++++--------- Model/Monitoring/Quote.php | 98 +++++- Model/Monitoring/Quote/Attempt.php | 87 +++++ Model/Monitoring/Quote/AttemptManagement.php | 100 ++++++ Model/Monitoring/Quote/AttemptRepository.php | 64 ++++ Model/Monitoring/Quote/Cancellation.php | 87 +++++ Model/Monitoring/Quote/CancellationFacade.php | 135 ++++++++ .../Monitoring/Quote/CancellationFactory.php | 62 ++++ .../Quote/CancellationRepository.php | 62 ++++ Model/Monitoring/QuoteManagement.php | 41 +++ Model/Monitoring/QuoteRepository.php | 28 +- Model/Order/Cancellation/Config.php | 22 +- .../Monitoring/Quote/Cancellation.php | 44 +++ .../Monitoring/Quote/Cancellation/Type.php | 39 +++ etc/crontab.xml | 3 + etc/extension_attributes.xml | 23 ++ 17 files changed, 1346 insertions(+), 179 deletions(-) create mode 100644 Cron/CancelQuoteByAttempts.php create mode 100644 Model/Monitoring/Quote/Attempt.php create mode 100644 Model/Monitoring/Quote/AttemptManagement.php create mode 100644 Model/Monitoring/Quote/AttemptRepository.php create mode 100644 Model/Monitoring/Quote/Cancellation.php create mode 100644 Model/Monitoring/Quote/CancellationFacade.php create mode 100644 Model/Monitoring/Quote/CancellationFactory.php create mode 100644 Model/Monitoring/Quote/CancellationRepository.php create mode 100644 Model/ResourceModel/Monitoring/Quote/Cancellation.php create mode 100644 Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php create mode 100644 etc/extension_attributes.xml diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php new file mode 100644 index 00000000..fffd665a --- /dev/null +++ b/Cron/CancelQuoteByAttempts.php @@ -0,0 +1,311 @@ +quoteCollectionFactory = $quoteCollectionFactory; + $this->commandManager = $commandManager; + $this->transactionBuilder = $transactionBuilder; + $this->orderPlace = $orderManagement; + $this->cartRepository = $cartRepository; + $this->logger = $logger; + $this->storeManager = $storeManager; + $this->scopeCodeResolver = $scopeCodeResolver; + $this->quoteManagement = $quoteManagement; + $this->cancellationConfig = $cancellationConfig; + $this->cancellationFactory = $cancellationFactory; + $this->cancellationRepository = $cancellationRepository; + $this->quoteMonitorRepository = $quoteMonitorRepository; + $this->attemptManagement = $attemptManagement; + $this->cancellationFacade = $cancellationFacade; + } + + /** + * Create orders from Vipps that are not created in Magento yet + * + * @throws NoSuchEntityException + * @throws \Exception + */ + public function execute() + { + try { + $currentStore = $this->storeManager->getStore()->getId(); + $currentPage = 1; + do { + $quoteCollection = $this->createCollection($currentPage); + $this->logger->debug( + 'Fetched quote collection to cancel', + ['current page' => $currentPage], + ['collection count' => $quoteCollection->count()] + ); + foreach ($quoteCollection as $quote) { + $this->processQuote($quote); + usleep(1000000); //delay for 1 second + } + $currentPage++; + } while ($currentPage <= $quoteCollection->getLastPageNumber()); + } finally { + $this->storeManager->setCurrentStore($currentStore); + } + } + + /** + * Get quote collection to cancel. + * + * @param $currentPage + * + * @return Collection + */ + private function createCollection($currentPage) + { + /** @var Collection $collection */ + $collection = $this->quoteCollectionFactory->create(); + + $collection + ->setPageSize(self::COLLECTION_PAGE_SIZE) + ->setCurPage($currentPage) + ->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']) + ->join( + ['p' => $collection->getTable('quote_payment')], + 'main_table.entity_id = p.quote_id', + ['p.method'] + ) + ->addFieldToFilter('p.method', ['eq' => 'vipps']) + ->join( + ['vq' => $collection->getTable('vipps_quote')], + 'main_table.entity_id = vq.quote_id', + ['vq.attempts'] + ) + ->addFieldToFilter( + 'vq.attempts', + ['gteq' => $this->cancellationConfig->getAttemptsMaxCount()] + ); + + // Filter not cancelled quotes. + $collection + ->getSelect() + ->joinLeft( + ['vqc' => $collection->getTable('vipps_quote_cancellation')], + 'vq.entity_id = vqc.parent_id', + [] + ); + $collection->addFieldToFilter('vqc.entity_id', ['null' => 1]); + + + return $collection; + } + + /** + * Main process + * + * @param Quote $quote + * + * @throws NoSuchEntityException + * @throws \Exception + */ + private function processQuote(Quote $quote) + { + $transaction = null; + $this->logger->info('Start quote cancelling', ['quote_id' => $quote->getId()]); + + try { + // Load vipps quote monitoring as extension attribute. + $this->quoteManagement->loadExtensionAttribute($quote); + + $this->prepareEnv($quote); + + $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); + + } catch (\Throwable $e) { + $this->logger->critical($e->getMessage(), ['quote_id' => $quote->getId()]); + } finally { + $this + ->cancellationFacade + ->cancelMagento( + $quote, + CancellationTypeResource::MAGENTO, + __('Number of attempts reached: %1', $this->cancellationConfig->getAttemptsMaxCount()), + $transaction + ); + } + } + + /** + * Prepare environment. + * + * @param Quote $quote + */ + private function prepareEnv(Quote $quote) + { + // set quote store as current store + $this->scopeCodeResolver->clean(); + $this->storeManager->setCurrentStore($quote->getStore()->getId()); + } + + /** + * @param $orderId + * + * @return Transaction + * @throws VippsException + */ + private function fetchOrderStatus($orderId) + { + $response = $this->commandManager->getOrderStatus($orderId); + return $this->transactionBuilder->setData($response)->build(); + } + + /** + * @param Quote $quote + * @param \DateInterval $interval + * + * @return bool + */ +// private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine +// { +// $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine +// $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine +// return $isQuoteExpired; +// } +} diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 53c249dd..24353f57 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -13,24 +13,31 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ + namespace Vipps\Payment\Cron; -use Magento\Store\Model\StoreManagerInterface; use Magento\Framework\App\Config\ScopeCodeResolver; -use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException}; +use Magento\Framework\Exception\{AlreadyExistsException, CouldNotSaveException, InputException, NoSuchEntityException}; +use Magento\Framework\Exception\LocalizedException; use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; -use Magento\Quote\Model\{ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory, Quote}; +use Magento\Quote\Model\{Quote, ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; use Magento\Sales\Api\Data\OrderInterface; -use Vipps\Payment\{ - Api\CommandManagerInterface, +use Magento\Store\Model\StoreManagerInterface; +use Psr\Log\LoggerInterface; +use Vipps\Payment\{Api\CommandManagerInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, - Model\OrderPlace, - Gateway\Transaction\TransactionBuilder -}; + Gateway\Transaction\TransactionBuilder, + Model\Order\Cancellation\Config, + Model\OrderPlace}; use Vipps\Payment\Gateway\Exception\WrongAmountException; -use Psr\Log\LoggerInterface; -use Magento\Framework\Exception\LocalizedException; +use Vipps\Payment\Model\Monitoring\{Quote\AttemptManagement, + Quote\CancellationFacade, + Quote\CancellationFactory, + Quote\CancellationRepository, + QuoteManagement as QuoteMonitorManagement, + QuoteRepository as QuoteMonitorRepository}; +use Vipps\Payment\Model\ResourceModel\Monitoring\Quote\Cancellation\Type as CancellationTypeResource; /** * Class FetchOrderStatus @@ -44,11 +51,6 @@ class FetchOrderFromVipps */ const COLLECTION_PAGE_SIZE = 100; - /** - * @var string - */ - const MAX_NUMBER_OF_ATTEMPTS = 3; - /** * @var CollectionFactory */ @@ -88,37 +90,73 @@ class FetchOrderFromVipps * @var ScopeCodeResolver */ private $scopeCodeResolver; + /** + * @var QuoteMonitorManagement + */ + private $quoteManagement; + /** + * @var Config + */ + private $cancellationConfig; + /** + * @var CancellationFactory + */ + private $cancellationFactory; + /** + * @var CancellationRepository + */ + private $cancellationRepository; + /** + * @var QuoteMonitorRepository + */ + private $quoteMonitorRepository; + /** + * @var AttemptManagement + */ + private $attemptManagement; + /** + * @var CancellationFacade + */ + private $cancellationFacade; /** * FetchOrderFromVipps constructor. - * * @param CollectionFactory $quoteCollectionFactory * @param CommandManagerInterface $commandManager * @param TransactionBuilder $transactionBuilder * @param OrderPlace $orderManagement - * @param CartRepositoryInterface $cartRepository * @param LoggerInterface $logger * @param StoreManagerInterface $storeManager * @param ScopeCodeResolver $scopeCodeResolver + * @param QuoteMonitorManagement $quoteManagement + * @param Config $cancellationConfig + * @param AttemptManagement $attemptManagement + * @param CancellationFacade $cancellationFacade */ public function __construct( CollectionFactory $quoteCollectionFactory, CommandManagerInterface $commandManager, TransactionBuilder $transactionBuilder, OrderPlace $orderManagement, - CartRepositoryInterface $cartRepository, LoggerInterface $logger, StoreManagerInterface $storeManager, - ScopeCodeResolver $scopeCodeResolver + ScopeCodeResolver $scopeCodeResolver, + QuoteMonitorManagement $quoteManagement, + Config $cancellationConfig, + AttemptManagement $attemptManagement, + CancellationFacade $cancellationFacade ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; $this->transactionBuilder = $transactionBuilder; $this->orderPlace = $orderManagement; - $this->cartRepository = $cartRepository; $this->logger = $logger; $this->storeManager = $storeManager; $this->scopeCodeResolver = $scopeCodeResolver; + $this->quoteManagement = $quoteManagement; + $this->cancellationConfig = $cancellationConfig; + $this->attemptManagement = $attemptManagement; + $this->cancellationFacade = $cancellationFacade; } /** @@ -134,11 +172,10 @@ public function execute() $currentPage = 1; do { $quoteCollection = $this->createCollection($currentPage); - $this->logger->debug(sprintf( - 'Fetched payment details, page: "%s", quotes: "%s"', - $currentPage, - $quoteCollection->count() //@codingStandardsIgnoreLine - )); + $this->logger->debug( + 'Fetched payment details', + ['page' => $currentPage, 'count' => $quoteCollection->count()] + ); foreach ($quoteCollection as $quote) { $this->processQuote($quote); usleep(1000000); //delay for 1 second @@ -151,90 +188,121 @@ public function execute() } /** - * Main process + * @param $currentPage * + * @return Collection + */ + private function createCollection($currentPage) + { + /** @var Collection $collection */ + $collection = $this->quoteCollectionFactory->create(); + + $collection + ->setPageSize(self::COLLECTION_PAGE_SIZE) + ->setCurPage($currentPage) + ->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']) + ->join( + ['p' => $collection->getTable('quote_payment')], + 'main_table.entity_id = p.quote_id', + ['p.method'] + ) + ->addFieldToFilter('p.method', ['eq' => 'vipps']); + + // Filter quotes that reached max count of queries. They will be cancelled by another job. + $collection + ->getSelect() + ->joinLeft( + ['vq' => $collection->getTable('vipps_quote')], + 'main_table.entity_id = vq.quote_id', + ['attempts'] + ); + + $collection->addFieldToFilter('vq.attempts', [ + ['lt' => $this->cancellationConfig->getAttemptsMaxCount()], + ['null' => 1] + ] + ); + + // Filter not cancelled quotes. + $collection + ->getSelect() + ->joinLeft( + ['vqc' => $collection->getTable('vipps_quote_cancellation')], + 'vq.entity_id = vqc.parent_id', + [] + ); + $collection->addFieldToFilter('vqc.entity_id', ['null' => 1]); + + // @todo: uncomment this. +// $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); +// $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min +// $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); + return $collection; + } + + /** * @param Quote $quote - * - * @throws NoSuchEntityException - * @throws \Exception + * @throws CouldNotSaveException */ private function processQuote(Quote $quote) { try { - $order = null; - $transaction = null; - $currentException = null; + $this->quoteManagement->loadExtensionAttribute($quote); + $attempt = $this->createMonitoringAttempt($quote); + // Load vipps quote monitoring as extension attribute. $this->prepareEnv($quote); $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); if ($transaction->isTransactionAborted()) { - $this->cancelQuote($quote, $transaction, 'canceled on vipps side'); + $attempt->setMessage('Transaction was cancelled on Vipps side'); + // Create cancellation if transaction was aborted on Vipps side. + $this + ->cancellationFacade + ->cancelMagento( + $quote, + CancellationTypeResource::VIPPS, + 'Transaction was cancelled on Vipps side', + $transaction + ); } else { - $order = $this->placeOrder($quote, $transaction); + $this->placeOrder($quote, $transaction); } } catch (\Throwable $e) { - $currentException = $e; //@codingStandardsIgnoreLine - $this->logger->critical($e->getMessage() . ', quote id = ' . $quote->getId()); - } finally { - if ($order) { - // if order exists - nothing to do, all good - return; - } - /** @var Quote $quote */ - $quote = $this->cartRepository->get($quote->getEntityId()); - if (!$quote->getReservedOrderId()) { - // if quote does not have reserved order id - such quote will not be processed next time - return; + $this->logger->critical($e->getMessage(), ['quote_id' => $quote->getId()]); + if (isset($attempt)) { + $attempt->setMessage($e->getMessage()); } - - // count not success (order not created) attempts of this process - if ($this->countAttempts($quote) >= $this->getMaxNumberOfAttempts()) { - $this->cancelQuote( - $quote, - $transaction, - sprintf( - 'canceled after "%s" attempts, last error "%s"', - $this->getMaxNumberOfAttempts(), - $currentException ? $currentException->getMessage() : 'n/a' - ) - ); - return; + } finally { + if (isset($attempt)) { + // Simply save the attempt. + $this->attemptManagement->save($attempt); } } } /** - * @param Quote|CartInterface $quote + * Create monitoring attempt. * - * @return int - * @throws LocalizedException + * @param CartInterface $quote + * @return \Vipps\Payment\Model\Monitoring\Quote\Attempt + * @throws AlreadyExistsException + * @throws CouldNotSaveException */ - private function countAttempts($quote) + private function createMonitoringAttempt(CartInterface $quote) { - $additionalInfo = $quote->getPayment()->getAdditionalInformation(); - $attempts = (int)($additionalInfo['vipps']['attempts'] ?? 0); + /** @var \Vipps\Payment\Model\Monitoring\Quote $monitoringQuote */ + $monitoringQuote = $quote + ->getExtensionAttributes() + ->getVippsMonitoring(); - $additionalInfo['vipps']['attempts'] = ++$attempts; - $quote->getPayment()->setAdditionalInformation($additionalInfo); - - if ($attempts < $this->getMaxNumberOfAttempts()) { - $this->cartRepository->save($quote); - } - - return $attempts; - } - - /** - * @return int - */ - private function getMaxNumberOfAttempts() - { - return (int)self::MAX_NUMBER_OF_ATTEMPTS; + return $this->attemptManagement->createAttempt($monitoringQuote); } /** + * Prepare environment. + * * @param Quote $quote */ private function prepareEnv(Quote $quote) @@ -244,19 +312,6 @@ private function prepareEnv(Quote $quote) $this->storeManager->setCurrentStore($quote->getStore()->getId()); } - /** - * @param Quote $quote - * @param \DateInterval $interval - * - * @return bool - */ - private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine - { - $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine - $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine - return $isQuoteExpired; - } - /** * @param $orderId * @@ -298,76 +353,16 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) } /** - * Cancel quote by setting reserved_order_id to null - * - * @param CartInterface|Quote $quote - * @param Transaction|null $transaction - * @param null $info - * - * @throws LocalizedException - */ - private function cancelQuote(CartInterface $quote, Transaction $transaction = null, $info = null) - { - $savedQuote = clone $quote; - $quote->setReservedOrderId(null); - - $additionalInformation = []; - if ($info instanceof \Exception) { - $additionalInformation = [ - 'cancel_reason_code' => $info->getCode(), - 'cancel_reason_phrase' => $info->getMessage() - ]; - } elseif (\is_string($info)) { - $additionalInformation['cancel_reason_phrase'] = $info; - } - - $additionalInformation = array_merge( - $additionalInformation, - [ - 'reserved_order_id' => $savedQuote->getReservedOrderId() - ] - ); - $payment = $quote->getPayment(); - $existingAdditionalInfo = $payment->getAdditionalInformation()['vipps'] ?? []; - $payment->setAdditionalInformation('vipps', array_merge($existingAdditionalInfo, $additionalInformation)); - - $this->cartRepository->save($quote); - - $this->logger->debug(sprintf( - 'Quote was canceled, id: "%s", reserved_order_id: "%s", cancel reason "%s"', - $quote->getId(), - $savedQuote->getReservedOrderId(), - $additionalInformation['cancel_reason_phrase'] - )); - - // cancel order on vipps side - if ($transaction && $transaction->isTransactionReserved()) { - $this->commandManager->cancel($savedQuote->getPayment()); - } - } - - /** - * @param $currentPage + * @deprecated + * @param Quote $quote + * @param \DateInterval $interval * - * @return Collection + * @return bool */ - private function createCollection($currentPage) + private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine { - /** @var Collection $collection */ - $collection = $this->quoteCollectionFactory->create(); - - $collection->setPageSize(self::COLLECTION_PAGE_SIZE); - $collection->setCurPage($currentPage); - $collection->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']); //@codingStandardsIgnoreLine - $collection->join( - ['p' => $collection->getTable('quote_payment')], - 'main_table.entity_id = p.quote_id', - ['p.method'] - ); - $collection->addFieldToFilter('p.method', ['eq' => 'vipps']); - $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); - $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min - $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); - return $collection; + $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine + $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine + return $isQuoteExpired; } } diff --git a/Model/Monitoring/Quote.php b/Model/Monitoring/Quote.php index 10d1fb29..bad18f3b 100644 --- a/Model/Monitoring/Quote.php +++ b/Model/Monitoring/Quote.php @@ -14,6 +14,7 @@ * IN THE SOFTWARE. * */ + namespace Vipps\Payment\Model\Monitoring; use Magento\Framework\Model\AbstractModel; @@ -25,14 +26,6 @@ */ class Quote extends AbstractModel implements QuoteInterface { - /** - * Constructor. - */ - protected function _construct() - { - $this->_init(QuoteResource::class); - } - /** * @param int $quoteId * @return self @@ -43,10 +36,10 @@ public function setQuoteId(int $quoteId) } /** - * @param string $reservedOrderId + * @param string|null $reservedOrderId Null for backward compatibility. * @return self */ - public function setReservedOrderId(string $reservedOrderId) + public function setReservedOrderId($reservedOrderId = '') { return $this->setData(self::RESERVED_ORDER_ID, $reservedOrderId); } @@ -66,4 +59,89 @@ public function getReservedOrderId() { return $this->getData(self::RESERVED_ORDER_ID); } + + /** + * @param string $createdAt + * @return self + */ + public function setCreatedAt(string $createdAt) + { + return $this->setData(self::CREATED_AT, $createdAt); + } + + /** + * @param string $updatedAt + * @return self + */ + public function setUpdatedAt(string $updatedAt) + { + return $this->setData(self::UPDATED_AT, $updatedAt); + } + + /** + * @return string + */ + public function getCreatedAt() + { + return $this->getData(self::CREATED_AT); + } + + /** + * @return string + */ + public function getUpdatedAt() + { + return $this->getData(self::UPDATED_AT); + } + + /** + * @return int + */ + public function getEntityId() + { + return $this->getData(self::ENTITY_ID); + } + + /** + * Increment attempts. + * + * @return Quote + */ + public function incrementAttempt() + { + return $this->setAttempts($this->getAttempts() + 1); + } + + /** + * @param int $attempts + * @return self + */ + public function setAttempts(int $attempts) + { + return $this->setData(self::ATTEMPTS, $attempts); + } + + /** + * @return int + */ + public function getAttempts() + { + return $this->getData(self::ATTEMPTS); + } + + /** + * Clear attempts. + */ + public function clearAttempts() + { + $this->setAttempts(0); + } + + /** + * Constructor. + */ + protected function _construct() + { + $this->_init(QuoteResource::class); + } } diff --git a/Model/Monitoring/Quote/Attempt.php b/Model/Monitoring/Quote/Attempt.php new file mode 100644 index 00000000..5c122abe --- /dev/null +++ b/Model/Monitoring/Quote/Attempt.php @@ -0,0 +1,87 @@ +setData(self::PARENT_ID, $parentId); + } + + /** + * @param string $message + * @return QuoteAttemptInterface + */ + public function setMessage(string $message) + { + return $this->setData(self::MESSAGE, $message); + } + + /** + * @param string $createdAt + * @return QuoteAttemptInterface + */ + public function setCreatedAt(string $createdAt) + { + return $this->setData(self::CREATED_AT, $createdAt); + } + + /** + * @return int + */ + public function getParentId() + { + return $this->getData(self::PARENT_ID); + } + + /** + * @return string + */ + public function getMessage() + { + return $this->getData(self::MESSAGE); + } + + /** + * @return string + */ + public function getCreatedAt() + { + return $this->getData(self::CREATED_AT); + } + + /** + * Constructor. + */ + protected function _construct() + { + $this->_init(AttemptResource::class); + } +} diff --git a/Model/Monitoring/Quote/AttemptManagement.php b/Model/Monitoring/Quote/AttemptManagement.php new file mode 100644 index 00000000..dfcd97c6 --- /dev/null +++ b/Model/Monitoring/Quote/AttemptManagement.php @@ -0,0 +1,100 @@ +attemptFactory = $attemptFactory; + $this->attemptResource = $resource; + $this->quoteMonitorRepository = $quoteRepository; + $this->attemptRepository = $attemptRepository; + } + + /** + * Create new saved attempt. Increment attempt count. Fill it with message later. + * + * @param MonitoringQuote $quote + * @return Attempt + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function createAttempt(MonitoringQuote $quote) + { + $attempt = $this + ->attemptFactory + ->create(['data' => ['parent_id' => $quote->getId()]]) + ->setDataChanges(true); + + // Saving attempt right immediately after creation cause it's already happened. + $this->attemptRepository->save($attempt); + + // Increase attempt counter. + $quote->incrementAttempt(); + $this->quoteMonitorRepository->save($quote); + + return $attempt; + } + + /** + * @param Attempt $attempt + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function save(Attempt $attempt) + { + $this->attemptRepository->save($attempt); + } +} diff --git a/Model/Monitoring/Quote/AttemptRepository.php b/Model/Monitoring/Quote/AttemptRepository.php new file mode 100644 index 00000000..7425c648 --- /dev/null +++ b/Model/Monitoring/Quote/AttemptRepository.php @@ -0,0 +1,64 @@ +resource = $resource; + } + + /** + * @param QuoteAttemptInterface $attempt + * @return QuoteAttemptInterface + * @throws CouldNotSaveException + */ + public function save(QuoteAttemptInterface $attempt) + { + try { + $this->resource->save($attempt); + + return $attempt; + } catch (\Exception $e) { + throw new CouldNotSaveException( + __( + 'Could not save Vipps Quote Attempt: %1', + $e->getMessage() + ), + $e + ); + } + } +} diff --git a/Model/Monitoring/Quote/Cancellation.php b/Model/Monitoring/Quote/Cancellation.php new file mode 100644 index 00000000..33a1ffb3 --- /dev/null +++ b/Model/Monitoring/Quote/Cancellation.php @@ -0,0 +1,87 @@ +getData(self::PARENT_ID); + } + + /** + * @return string + */ + public function getType() + { + return $this->getData(self::TYPE); + } + + /** + * @return string + */ + public function getPhrase() + { + return $this->getData(self::PHRASE); + } + + /** + * @param int $parentId + * @return self + */ + public function setParentId(int $parentId) + { + return $this->setData($parentId, $parentId); + } + + /** + * @param string $type + * @return self + */ + public function setType(string $type) + { + $this->setData(self::TYPE, $type); + } + + /** + * @param string $phrase + * @return self + */ + public function setPhrase(string $phrase) + { + return $this->setData(self::PHRASE, $phrase); + } + + /** + * Constructor. + */ + protected function _construct() + { + $this->_init(CancellationResource::class); + } +} diff --git a/Model/Monitoring/Quote/CancellationFacade.php b/Model/Monitoring/Quote/CancellationFacade.php new file mode 100644 index 00000000..caf43d16 --- /dev/null +++ b/Model/Monitoring/Quote/CancellationFacade.php @@ -0,0 +1,135 @@ +commandManager = $commandManager; + $this->cancellationFactory = $cancellationFactory; + $this->cancellationRepository = $cancelRepository; + $this->config = $config; + } + + /** + * vipps_monitoring extension attribute requires to be loaded in the quote. + * + * @param CartInterface $quote + * @param string $type + * @param string $reason + * @param Transaction|null $transaction + * @throws NoSuchEntityException + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function cancelMagento( + CartInterface $quote, + string $type, + string $reason, + Transaction $transaction = null + ) { + /** @var \Vipps\Payment\Api\Monitoring\Data\QuoteInterface $quoteMonitoring */ + $quoteMonitoring = $quote->getExtensionAttributes()->getVippsMonitoring(); + + if (!$quoteMonitoring) { + throw new NoSuchEntityException(__('Vipps Monitoring model is not loaded in Quote extension attributes')); + } + + // Creating magento cancellation. + $cancellation = $this + ->cancellationFactory + ->create($quoteMonitoring, $type, $reason); + + $this->cancellationRepository->save($cancellation); + + // Nothing to cancel if cancellation initialed by Vipps. + if ($type === CancellationTypeResource::MAGENTO + && $this->isAutomaticVippsCancellation($quote->getStoreId()) + ) { + $this->cancelVipps($quote, $cancellation, $transaction); + } + } + + /** + * @param int|null $storeId + * @return bool + */ + private function isAutomaticVippsCancellation($storeId = null) + { + return $this->config->isTypeAutomatic($storeId); + } + + /** + * @param CartInterface $quote + * @param Cancellation $cancellation + * @param Transaction|null $transaction + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function cancelVipps( + CartInterface $quote, + Cancellation $cancellation, + Transaction $transaction = null + ) { + // cancel order on vipps side + if ($transaction && $transaction->isTransactionReserved()) { + $this->commandManager->cancel($quote->getPayment()); + + $cancellation->setType(CancellationTypeResource::ALL); + $this->cancellationRepository->save($cancellation); + } + } +} diff --git a/Model/Monitoring/Quote/CancellationFactory.php b/Model/Monitoring/Quote/CancellationFactory.php new file mode 100644 index 00000000..172ce348 --- /dev/null +++ b/Model/Monitoring/Quote/CancellationFactory.php @@ -0,0 +1,62 @@ +objectManager = $objectManager; + } + + /** + * Create cancellation entity. + * + * @param QuoteInterface $monitoringQuote + * @param string|null $type + * @param string|null $code + * @param string|null $reason + * @return Cancellation + */ + public function create(QuoteInterface $monitoringQuote, string $type = null, string $reason = null) + { + $data = [ + Cancellation::PARENT_ID => $monitoringQuote->getEntityId(), + Cancellation::TYPE => $type, + Cancellation::PHRASE => $reason + ]; + + return $this + ->objectManager + ->create(Cancellation::class, ['data' => $data]); + } +} diff --git a/Model/Monitoring/Quote/CancellationRepository.php b/Model/Monitoring/Quote/CancellationRepository.php new file mode 100644 index 00000000..1ced7154 --- /dev/null +++ b/Model/Monitoring/Quote/CancellationRepository.php @@ -0,0 +1,62 @@ +resource = $resource; + } + + /** + * @param QuoteCancellationInterface $cancellation + * @return QuoteCancellationInterface + * @throws CouldNotSaveException + */ + public function save(QuoteCancellationInterface $cancellation) + { + try { + $cancellation->setDataChanges(true); + $this->resource->save($cancellation); + + return $cancellation; + } catch (\Exception $e) { + throw new CouldNotSaveException( + __( + 'Could not save Vipps Quote Cancellation: %1', + $e->getMessage() + ), + $e + ); + } + } +} diff --git a/Model/Monitoring/QuoteManagement.php b/Model/Monitoring/QuoteManagement.php index a474e354..3c424747 100644 --- a/Model/Monitoring/QuoteManagement.php +++ b/Model/Monitoring/QuoteManagement.php @@ -17,6 +17,7 @@ namespace Vipps\Payment\Model\Monitoring; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\Api\Monitoring\{Data\QuoteInterface, QuoteManagementInterface}; @@ -63,6 +64,46 @@ public function create(CartInterface $cart) ->setReservedOrderId($cart->getReservedOrderId()); return $this->quoteRepository->save($monitoringQuote); + } + + /** + * Loads Vipps monitoring as extension attribute. + * + * @param CartInterface $quote + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function loadExtensionAttribute(CartInterface $quote) + { + if ($extensionAttributes = $quote->getExtensionAttributes()) { + if (!$extensionAttributes->getVippsMonitoring()) { + + $monitoringQuote = $this->getByQuote($quote); + + $extensionAttributes->setVippsMonitoring($monitoringQuote); + } + } + } + + /** + * @param CartInterface $cart + * @return Quote + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function getByQuote(CartInterface $cart) + { + /** @var Quote $monitoringQuote */ + try { + $monitoringQuote = $this->quoteRepository->loadByQuote($cart->getId()); + } catch (NoSuchEntityException $exception) { + // Setup default values for backward compatibility with current quotes. + $monitoringQuote = $this->quoteFactory->create() + ->setQuoteId($cart->getId()) + ->setReservedOrderId($cart->getReservedOrderId()); + + // Backward compatibility for old quotes paid with vipps. + $this->quoteRepository->save($monitoringQuote); + } + return $monitoringQuote; } } diff --git a/Model/Monitoring/QuoteRepository.php b/Model/Monitoring/QuoteRepository.php index dc79b290..8efcecae 100644 --- a/Model/Monitoring/QuoteRepository.php +++ b/Model/Monitoring/QuoteRepository.php @@ -14,9 +14,11 @@ * IN THE SOFTWARE. * */ + namespace Vipps\Payment\Model\Monitoring; use Magento\Framework\Exception\CouldNotSaveException; +use Magento\Framework\Exception\NoSuchEntityException; use Vipps\Payment\Api\Monitoring\Data\QuoteInterface; use Vipps\Payment\Api\Monitoring\QuoteRepositoryInterface; use Vipps\Payment\Model\ResourceModel\Monitoring\Quote as QuoteResource; @@ -30,15 +32,20 @@ class QuoteRepository implements QuoteRepositoryInterface * @var QuoteResource */ private $quoteResource; + /** + * @var QuoteFactory + */ + private $quoteFactory; /** * QuoteRepository constructor. * - * @param QuoteResource $quoteResource. + * @param QuoteResource $quoteResource . */ - public function __construct(QuoteResource $quoteResource) + public function __construct(QuoteResource $quoteResource, QuoteFactory $quoteFactory) { $this->quoteResource = $quoteResource; + $this->quoteFactory = $quoteFactory; } /** @@ -64,4 +71,21 @@ public function save(QuoteInterface $quote) ); } } + + /** + * @param $quoteId + * @throws NoSuchEntityException + */ + public function loadByQuote($quoteId) + { + $monitoringQuote = $this->quoteFactory->create([]); + + $this->quoteResource->load($monitoringQuote, $quoteId, 'quote_id'); + + if (!$monitoringQuote->getId()) { + throw NoSuchEntityException::singleField('quote_id', $quoteId); + } + + return $monitoringQuote; + } } diff --git a/Model/Order/Cancellation/Config.php b/Model/Order/Cancellation/Config.php index 50cbcc65..13256310 100644 --- a/Model/Order/Cancellation/Config.php +++ b/Model/Order/Cancellation/Config.php @@ -19,6 +19,7 @@ use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Store\Model\ScopeInterface; +use Vipps\Payment\Model\Adminhtml\Source\Cancellation\Type; /** * Class Config @@ -53,25 +54,36 @@ public function __construct(ScopeConfigInterface $scopeConfig) $this->scopeConfig = $scopeConfig; } + /** + * @param int|null $storeId + * @return bool + */ + public function isTypeAutomatic($storeId = null) + { + return $this->getType($storeId) === Type::AUTOMATIC; + } + /** * Cancellation type code. * + * @param int|null $storeId * @return string */ - public function getType() + public function getType($storeId = null) { - return $this->getStoreConfig(self::XML_PATH_TYPE); + return $this->getStoreConfig(self::XML_PATH_TYPE, $storeId); } /** * Common method to return store config value. * * @param $path + * @param int|null $storeId * @return mixed */ - private function getStoreConfig($path) + private function getStoreConfig($path, $storeId = null) { - return $this->scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE); + return $this->scopeConfig->getValue($path, ScopeInterface::SCOPE_STORE, $storeId); } /** @@ -79,7 +91,7 @@ private function getStoreConfig($path) * * @return int */ - public function getAttemptsCount() + public function getAttemptsMaxCount() { return $this->getStoreConfig(self::XML_PATH_ATTEMPT_COUNT); } diff --git a/Model/ResourceModel/Monitoring/Quote/Cancellation.php b/Model/ResourceModel/Monitoring/Quote/Cancellation.php new file mode 100644 index 00000000..a2e4da30 --- /dev/null +++ b/Model/ResourceModel/Monitoring/Quote/Cancellation.php @@ -0,0 +1,44 @@ +_init(self::TABLE_NAME, self::INDEX_FIELD); + } +} diff --git a/Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php b/Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php new file mode 100644 index 00000000..f8483a61 --- /dev/null +++ b/Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php @@ -0,0 +1,39 @@ + */10 * * * * + + */10 * * * * + diff --git a/etc/extension_attributes.xml b/etc/extension_attributes.xml new file mode 100644 index 00000000..73ee8c42 --- /dev/null +++ b/etc/extension_attributes.xml @@ -0,0 +1,23 @@ + + + + + + + + From def70217d8cc0a2b1421df52bbf7e719ac188a17 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Fri, 11 Jan 2019 15:43:11 +0200 Subject: [PATCH 077/121] VIPPS-179: Monitoring quote grid and details page. --- Api/Monitoring/Data/QuoteInterface.php | 23 ++++ .../Quote/CancellationFacadeInterface.php | 3 +- Block/Monitoring/View.php | 108 +++++++++++++++ Controller/Adminhtml/Monitoring/Index.php | 59 ++++++++ Controller/Adminhtml/Monitoring/View.php | 56 ++++++++ Cron/CancelQuoteByAttempts.php | 1 - Model/Monitoring/Quote.php | 23 +++- Model/Monitoring/Quote/CancellationFacade.php | 42 +++++- Model/Monitoring/QuoteRepository.php | 24 +++- .../{Collection.php => GridCollection.php} | 4 +- Setup/UpgradeSchema.php | 26 ++++ etc/adminhtml/menu.xml | 43 +++--- etc/di.xml | 8 ++ etc/module.xml | 2 +- .../layout/vipps_monitoring_index.xml | 24 ++++ .../layout/vipps_monitoring_view.xml | 32 +++++ .../templates/monitoring/back_button.phtml | 34 +++++ .../adminhtml/templates/monitoring/view.phtml | 103 ++++++++++++++ .../ui_component/vipps_monitoring.xml | 127 ++++++++++++++++++ 19 files changed, 715 insertions(+), 27 deletions(-) create mode 100644 Block/Monitoring/View.php create mode 100644 Controller/Adminhtml/Monitoring/Index.php create mode 100644 Controller/Adminhtml/Monitoring/View.php rename Model/ResourceModel/Monitoring/Quote/{Collection.php => GridCollection.php} (96%) create mode 100644 view/adminhtml/layout/vipps_monitoring_index.xml create mode 100644 view/adminhtml/layout/vipps_monitoring_view.xml create mode 100644 view/adminhtml/templates/monitoring/back_button.phtml create mode 100644 view/adminhtml/templates/monitoring/view.phtml create mode 100644 view/adminhtml/ui_component/vipps_monitoring.xml diff --git a/Api/Monitoring/Data/QuoteInterface.php b/Api/Monitoring/Data/QuoteInterface.php index 5659b31a..a8e9e326 100644 --- a/Api/Monitoring/Data/QuoteInterface.php +++ b/Api/Monitoring/Data/QuoteInterface.php @@ -22,6 +22,12 @@ */ interface QuoteInterface { + /** + * @const Canceled statuses. + */ + const IS_CANCELED_YES = 1; + const IS_CANCELED_NO = 0; + /** * @const string */ @@ -52,6 +58,11 @@ interface QuoteInterface */ const ATTEMPTS = 'attempts'; + /** + * @const string + */ + const IS_CANCELED = 'is_canceled'; + /** * @param int $quoteId * @return self @@ -82,6 +93,12 @@ public function setUpdatedAt(string $updatedAt); */ public function setAttempts(int $attempts); + /** + * @param int $isCanceled + * @return self + */ + public function setIsCanceled(int $isCanceled); + /** * @return int */ @@ -111,4 +128,10 @@ public function getAttempts(); * @return int */ public function getEntityId(); + + /** + * @return int + */ + public function getIsCanceled(); + } diff --git a/Api/Monitoring/Quote/CancellationFacadeInterface.php b/Api/Monitoring/Quote/CancellationFacadeInterface.php index 71dd912f..6fbe351c 100644 --- a/Api/Monitoring/Quote/CancellationFacadeInterface.php +++ b/Api/Monitoring/Quote/CancellationFacadeInterface.php @@ -15,11 +15,12 @@ * */ -namespace Vipps\Payment\Model\Monitoring\Quote; +namespace Vipps\Payment\Api\Monitoring\Quote; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\Gateway\Transaction\Transaction; +use Vipps\Payment\Model\Monitoring\Quote\Cancellation; /** * Quote Cancellation Facade. diff --git a/Block/Monitoring/View.php b/Block/Monitoring/View.php new file mode 100644 index 00000000..2bd76f15 --- /dev/null +++ b/Block/Monitoring/View.php @@ -0,0 +1,108 @@ +monitoringQuoteRepository = $monitoringQuoteRepository; + $this->quoteRepository = $quoteRepository; + } + + /** + * @return \Magento\Quote\Api\Data\CartInterface + */ + public function getQuote() + { + if (!$this->quote) { + try { + $this->quote = $this->quoteRepository->get($this->getMonitoringQuote()->getQuoteId()); + } catch (\Exception $e) { + $this->quoteLoadingError = $e->getMessage(); + } + } + + return $this->quote; + } + + /** + * @return \Vipps\Payment\Api\Monitoring\Data\QuoteInterface + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + public function getMonitoringQuote() + { + if (!$this->monitoringQuote) { + try { + $this->monitoringQuote = $this->monitoringQuoteRepository->load($this->getRequest()->getParam('id')); + } catch (\Exception $e) { + // Display this error in template. + } + } + + return $this->monitoringQuote; + } + + /** + * Quote loading error. + * + * @return string|null + */ + public function getQuoteLoadingError() + { + return $this->quoteLoadingError; + } +} diff --git a/Controller/Adminhtml/Monitoring/Index.php b/Controller/Adminhtml/Monitoring/Index.php new file mode 100644 index 00000000..905139a9 --- /dev/null +++ b/Controller/Adminhtml/Monitoring/Index.php @@ -0,0 +1,59 @@ +resultPageFactory = $resultPageFactory; + } + + /** + * Display grid. + * + * @return Page + */ + public function execute() + { + /** @var Page $resultPage */ + $resultPage = $this->resultPageFactory->create(); + $resultPage->setActiveMenu('Vipps_Payment::vipps_monitoring'); + $resultPage->getConfig()->getTitle()->prepend(__('Quote Monitoring')); + return $resultPage; + } +} diff --git a/Controller/Adminhtml/Monitoring/View.php b/Controller/Adminhtml/Monitoring/View.php new file mode 100644 index 00000000..31993e1a --- /dev/null +++ b/Controller/Adminhtml/Monitoring/View.php @@ -0,0 +1,56 @@ +resultPageFactory = $resultPageFactory; + } + + /** + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + $resultPage = $this->resultPageFactory->create(); + $resultPage->setActiveMenu('Vipps_Payment::vipps_monitoring'); + return $resultPage; + } +} diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index fffd665a..d0e017d5 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -257,7 +257,6 @@ private function processQuote(Quote $quote) $this->prepareEnv($quote); $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); - } catch (\Throwable $e) { $this->logger->critical($e->getMessage(), ['quote_id' => $quote->getId()]); } finally { diff --git a/Model/Monitoring/Quote.php b/Model/Monitoring/Quote.php index bad18f3b..f2e6b437 100644 --- a/Model/Monitoring/Quote.php +++ b/Model/Monitoring/Quote.php @@ -130,11 +130,30 @@ public function getAttempts() } /** - * Clear attempts. + * Clear attempts. Can be used to restart pushing to vipps. + * + * @return QuoteInterface */ public function clearAttempts() { - $this->setAttempts(0); + return $this->setAttempts(0); + } + + /** + * @param int $isCanceled + * @return QuoteInterface + */ + public function setIsCanceled(int $isCanceled) + { + return $this->setData(self::IS_CANCELED, $isCanceled); + } + + /** + * @return int + */ + public function getIsCanceled() + { + return $this->getData(self::IS_CANCELED); } /** diff --git a/Model/Monitoring/Quote/CancellationFacade.php b/Model/Monitoring/Quote/CancellationFacade.php index caf43d16..d031ad4b 100644 --- a/Model/Monitoring/Quote/CancellationFacade.php +++ b/Model/Monitoring/Quote/CancellationFacade.php @@ -17,10 +17,15 @@ namespace Vipps\Payment\Model\Monitoring\Quote; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; -use Vipps\Payment\{Api\CommandManagerInterface, +use Vipps\Payment\{ + Api\CommandManagerInterface, + Api\Monitoring\Data\QuoteInterface, + Api\Monitoring\Quote\CancellationFacadeInterface, Gateway\Transaction\Transaction, + Model\Monitoring\QuoteRepository, Model\Order\Cancellation\Config, Model\ResourceModel\Monitoring\Quote\Cancellation\Type as CancellationTypeResource}; @@ -46,24 +51,42 @@ class CancellationFacade implements CancellationFacadeInterface * @var Config */ private $config; + /** + * @var QuoteRepository + */ + private $quoteRepository; + /** + * @var \Magento\Framework\DB\Adapter\AdapterInterface + */ + private $adapter; + /** + * @var ResourceConnection + */ + private $resourceConnection; /** * CancellationFacade constructor. * @param CommandManagerInterface $commandManager * @param CancellationFactory $cancellationFactory * @param CancellationRepository $cancelRepository + * @param QuoteRepository $quoteRepository + * @param ResourceConnection $resourceConnection * @param Config $config */ public function __construct( CommandManagerInterface $commandManager, CancellationFactory $cancellationFactory, CancellationRepository $cancelRepository, + QuoteRepository $quoteRepository, + ResourceConnection $resourceConnection, Config $config ) { $this->commandManager = $commandManager; $this->cancellationFactory = $cancellationFactory; $this->cancellationRepository = $cancelRepository; $this->config = $config; + $this->quoteRepository = $quoteRepository; + $this->resourceConnection = $resourceConnection; } /** @@ -75,6 +98,7 @@ public function __construct( * @param Transaction|null $transaction * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Exception */ public function cancelMagento( CartInterface $quote, @@ -89,12 +113,24 @@ public function cancelMagento( throw new NoSuchEntityException(__('Vipps Monitoring model is not loaded in Quote extension attributes')); } - // Creating magento cancellation. + $connection = $this->resourceConnection->getConnection(); + + // Creating quote cancellation. $cancellation = $this ->cancellationFactory ->create($quoteMonitoring, $type, $reason); - $this->cancellationRepository->save($cancellation); + $quoteMonitoring->setIsCanceled(QuoteInterface::IS_CANCELED_YES); + + $connection->beginTransaction(); + try { + $this->quoteRepository->save($quoteMonitoring); + $this->cancellationRepository->save($cancellation); + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw $e; + } // Nothing to cancel if cancellation initialed by Vipps. if ($type === CancellationTypeResource::MAGENTO diff --git a/Model/Monitoring/QuoteRepository.php b/Model/Monitoring/QuoteRepository.php index 8efcecae..340eeeaf 100644 --- a/Model/Monitoring/QuoteRepository.php +++ b/Model/Monitoring/QuoteRepository.php @@ -41,6 +41,7 @@ class QuoteRepository implements QuoteRepositoryInterface * QuoteRepository constructor. * * @param QuoteResource $quoteResource . + * @param QuoteFactory $quoteFactory */ public function __construct(QuoteResource $quoteResource, QuoteFactory $quoteFactory) { @@ -73,12 +74,15 @@ public function save(QuoteInterface $quote) } /** + * Load monitoring quote by quote. + * * @param $quoteId + * @return Quote * @throws NoSuchEntityException */ public function loadByQuote($quoteId) { - $monitoringQuote = $this->quoteFactory->create([]); + $monitoringQuote = $this->quoteFactory->create(); $this->quoteResource->load($monitoringQuote, $quoteId, 'quote_id'); @@ -88,4 +92,22 @@ public function loadByQuote($quoteId) return $monitoringQuote; } + + /** + * @param int $monitoringQuoteId + * @return Quote + * @throws NoSuchEntityException + */ + public function load(int $monitoringQuoteId) + { + $monitoringQuote = $this->quoteFactory->create(); + + $this->quoteResource->load($monitoringQuote, $monitoringQuoteId); + + if (!$monitoringQuote->getId()) { + throw NoSuchEntityException::singleField('entity_id', $monitoringQuoteId); + } + + return $monitoringQuote; + } } diff --git a/Model/ResourceModel/Monitoring/Quote/Collection.php b/Model/ResourceModel/Monitoring/Quote/GridCollection.php similarity index 96% rename from Model/ResourceModel/Monitoring/Quote/Collection.php rename to Model/ResourceModel/Monitoring/Quote/GridCollection.php index 1e590bc0..69c62b64 100644 --- a/Model/ResourceModel/Monitoring/Quote/Collection.php +++ b/Model/ResourceModel/Monitoring/Quote/GridCollection.php @@ -29,7 +29,7 @@ /** * Class Collection */ -class Collection extends SearchResult +class GridCollection extends SearchResult { /** * @var string @@ -51,7 +51,7 @@ public function __construct( Logger $logger, FetchStrategy $fetchStrategy, EventManager $eventManager, - $mainTable = 'vipps_quote', + $mainTable = Quote::TABLE_NAME, $resourceModel = Quote::class ) { parent::__construct($entityFactory, $logger, $fetchStrategy, $eventManager, $mainTable, $resourceModel); diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 3340ace1..7a595e18 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -45,6 +45,11 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con if (version_compare($context->getVersion(), '1.2.1', '<')) { $this->createCancellationTable($installer); } + + if (version_compare($context->getVersion(), '1.3.0', '<')) { + $this->addCancelToVippsQuote($installer); + } + $installer->endSetup(); } @@ -232,4 +237,25 @@ private function createCancellationTable(SchemaSetupInterface $installer): void $installer->getConnection()->createTable($table); } + + /** + * @param SchemaSetupInterface $installer + */ + private function addCancelToVippsQuote(SchemaSetupInterface $installer) + { + $connection = $installer->getConnection(); + + $connection + ->addColumn( + $connection->getTableName('vipps_quote'), + 'is_canceled', + [ + Table::OPTION_TYPE => Table::TYPE_BOOLEAN, + Table::OPTION_DEFAULT => 0, + Table::OPTION_NULLABLE => false, + 'comment' => 'Is Canceled', + 'after' => 'reserved_order_id' + ] + ); + } } diff --git a/etc/adminhtml/menu.xml b/etc/adminhtml/menu.xml index 34b9633e..3bdcc080 100644 --- a/etc/adminhtml/menu.xml +++ b/etc/adminhtml/menu.xml @@ -15,24 +15,35 @@ * IN THE SOFTWARE. */ --> - + + id="Vipps_Payment::vipps" + title="Vipps Payment" + translate="title" + module="Vipps_Payment" + sortOrder="100" + parent="Magento_Backend::system" + resource="Vipps_Payment::vipps"/> + id="Vipps_Payment::vipps_monitoring" + title="Quote Monitoring" + translate="title" + module="Vipps_Payment" + sortOrder="10" + parent="Vipps_Payment::vipps" + action="vipps/monitoring/index" + resource="Vipps_Payment::vipps"/> + + diff --git a/etc/di.xml b/etc/di.xml index 0ddb17d8..63c9cc4d 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -382,6 +382,14 @@ + + + + Vipps\Payment\Model\ResourceModel\Monitoring\Quote\GridCollection + + + + Vipps\Payment\Model\Logger diff --git a/etc/module.xml b/etc/module.xml index 76df3b9f..cd943f90 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + diff --git a/view/adminhtml/layout/vipps_monitoring_index.xml b/view/adminhtml/layout/vipps_monitoring_index.xml new file mode 100644 index 00000000..20d6f914 --- /dev/null +++ b/view/adminhtml/layout/vipps_monitoring_index.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/view/adminhtml/layout/vipps_monitoring_view.xml b/view/adminhtml/layout/vipps_monitoring_view.xml new file mode 100644 index 00000000..f66ba7f5 --- /dev/null +++ b/view/adminhtml/layout/vipps_monitoring_view.xml @@ -0,0 +1,32 @@ + + + + + + + + + + diff --git a/view/adminhtml/templates/monitoring/back_button.phtml b/view/adminhtml/templates/monitoring/back_button.phtml new file mode 100644 index 00000000..67e0d612 --- /dev/null +++ b/view/adminhtml/templates/monitoring/back_button.phtml @@ -0,0 +1,34 @@ + +
+
+
+ +
+
+
diff --git a/view/adminhtml/templates/monitoring/view.phtml b/view/adminhtml/templates/monitoring/view.phtml new file mode 100644 index 00000000..55dc805b --- /dev/null +++ b/view/adminhtml/templates/monitoring/view.phtml @@ -0,0 +1,103 @@ +getMonitoringQuote(); +?> +
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
getQuoteId(); ?>
getReservedOrderId(); ?>
getAttempts(); ?>
getIsCanceled() ? 'Yes' : 'No'; ?>
getCreatedAt(); ?>
getUpdatedAt(); ?>
+ + +
+ +
+ getQuoteLoadingError()): ?> + + + getQuote(); ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
getStoreId(); ?>
getReservedOrderId(); ?>
getCustomerEmail(); ?>
getCustomerFirstname() . ' ' . $quote->getCustomerLastname(); ?>
getGrandTotal(); ?>
getIsActive() ? 'Yes' : 'No'; ?>
getCreatedAt(); ?>
getUpdatedAt(); ?>
+ +
diff --git a/view/adminhtml/ui_component/vipps_monitoring.xml b/view/adminhtml/ui_component/vipps_monitoring.xml new file mode 100644 index 00000000..f2c2050b --- /dev/null +++ b/view/adminhtml/ui_component/vipps_monitoring.xml @@ -0,0 +1,127 @@ + + ++ + + vipps_monitoring.vipps_monitoring_data_source + vipps_monitoring.vipps_monitoring_data_source + + monitoring_columns + Vipps_Payment::vipps + + + + + Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider + + vipps_monitoring_data_source + entity_id + id + + + Magento_Ui/js/grid/provider + + + + + + + + + + + + + + + + entity_id + 10 + + + + + + + textRange + desc + Quote ID + 20 + + + + + + + textRange + Reserved Order ID + 30 + + + + + + + textRange + Attempts Count + 40 + + + + + + + select + select + + + + + + + dateRange + date + Magento_Ui/js/grid/columns/date + Created At + 100 + + + + + + + dateRange + date + Magento_Ui/js/grid/columns/date + Last Updated At + 110 + + + + + + + entity_id + 200 + vipps/monitoring/view + + + + + From 758ab1c8437eae520a37f17ed887045f78c4aa28 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Sun, 13 Jan 2019 12:09:23 +0200 Subject: [PATCH 078/121] VIPPS-184: Code review. Take out cancellation entity. Vipps quote implements cancellation interface. --- Api/Monitoring/Data/QuoteAttemptInterface.php | 1 + .../Data/QuoteCancellationInterface.php | 63 ++++++++++---- Api/Monitoring/Data/QuoteInterface.php | 1 + .../Quote/AttemptRepositoryInterface.php | 1 + .../Quote/CancellationFacadeInterface.php | 7 +- Api/Monitoring/QuoteManagementInterface.php | 1 + Api/Monitoring/QuoteRepositoryInterface.php | 1 + Cron/CancelQuoteByAttempts.php | 86 ++++-------------- Cron/FetchOrderFromVipps.php | 21 ++--- Model/Monitoring/Quote.php | 53 ++++++++++- Model/Monitoring/Quote/Cancellation.php | 87 ------------------- Model/Monitoring/Quote/CancellationFacade.php | 75 +++++++++------- .../Monitoring/Quote/CancellationFactory.php | 62 ------------- .../Quote/CancellationRepository.php | 62 ------------- Model/Monitoring/QuoteManagement.php | 1 - Model/Monitoring/QuoteRepository.php | 5 +- .../Monitoring/Quote/Cancellation.php | 44 ---------- .../Monitoring/Quote/Cancellation/Type.php | 39 --------- Setup/UpgradeSchema.php | 81 ++++++++--------- 19 files changed, 212 insertions(+), 479 deletions(-) delete mode 100644 Model/Monitoring/Quote/Cancellation.php delete mode 100644 Model/Monitoring/Quote/CancellationFactory.php delete mode 100644 Model/Monitoring/Quote/CancellationRepository.php delete mode 100644 Model/ResourceModel/Monitoring/Quote/Cancellation.php delete mode 100644 Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php diff --git a/Api/Monitoring/Data/QuoteAttemptInterface.php b/Api/Monitoring/Data/QuoteAttemptInterface.php index 73e5bb5a..b1ebda11 100644 --- a/Api/Monitoring/Data/QuoteAttemptInterface.php +++ b/Api/Monitoring/Data/QuoteAttemptInterface.php @@ -19,6 +19,7 @@ /** * Interface QuoteInterface + * @api */ interface QuoteAttemptInterface { diff --git a/Api/Monitoring/Data/QuoteCancellationInterface.php b/Api/Monitoring/Data/QuoteCancellationInterface.php index 6a0248fa..7446f7fe 100644 --- a/Api/Monitoring/Data/QuoteCancellationInterface.php +++ b/Api/Monitoring/Data/QuoteCancellationInterface.php @@ -18,55 +18,80 @@ namespace Vipps\Payment\Api\Monitoring\Data; /** - * Interface QuoteCancellationInterface + * Interface QuoteInterface + * @api */ interface QuoteCancellationInterface { /** - * @const Vipps quote id + * @const string */ - const PARENT_ID = 'parent_id'; + const IS_CANCELED = 'is_canceled'; /** - * @const Cancellation type (magento, vipps, both) + * @const string */ - const TYPE = 'type'; + const IS_CANCELED_YES = 1; /** - * @const Reason. + * @const string */ - const PHRASE = 'phrase'; + const IS_CANCELED_NO = 0; /** - * @return int + * @const string */ - public function getParentId(); + const CANCEL_TYPE = 'cancel_type'; /** - * @return string + * @const string */ - public function getType(); + const CANCEL_REASON = 'cancel_reason'; /** - * @return string + * @const string Canceled in vipps. */ - public function getPhrase(); + const CANCEL_TYPE_VIPPS = 'vipps'; /** - * @param int $parentId + * @const string Canceled in magento. + */ + const CANCEL_TYPE_MAGENTO = 'magento'; + + /** + * @const string Canceled everywhere. + */ + const CANCEL_TYPE_ALL = 'all'; + + /** + * @return string|null + */ + public function getCancelType(); + + /** + * @return string|null + */ + public function getCancelReason(); + + /** + * @param bool $isCanceled * @return self */ - public function setParentId(int $parentId); + public function setIsCanceled(bool $isCanceled); + + /** + * @param string $reason + */ + public function setCancelReason(string $reason); /** * @param string $type * @return self */ - public function setType(string $type); + public function setCancelType($type); /** - * @param string $phrase - * @return self + * @return bool */ - public function setPhrase(string $phrase); + public function isCanceled(); } diff --git a/Api/Monitoring/Data/QuoteInterface.php b/Api/Monitoring/Data/QuoteInterface.php index 5659b31a..bef87e85 100644 --- a/Api/Monitoring/Data/QuoteInterface.php +++ b/Api/Monitoring/Data/QuoteInterface.php @@ -19,6 +19,7 @@ /** * Interface QuoteInterface + * @api */ interface QuoteInterface { diff --git a/Api/Monitoring/Quote/AttemptRepositoryInterface.php b/Api/Monitoring/Quote/AttemptRepositoryInterface.php index a63915e6..00566ffd 100644 --- a/Api/Monitoring/Quote/AttemptRepositoryInterface.php +++ b/Api/Monitoring/Quote/AttemptRepositoryInterface.php @@ -22,6 +22,7 @@ /** * Interface AttemptRepositoryInterface + * @api */ interface AttemptRepositoryInterface { diff --git a/Api/Monitoring/Quote/CancellationFacadeInterface.php b/Api/Monitoring/Quote/CancellationFacadeInterface.php index 71dd912f..dc4fb781 100644 --- a/Api/Monitoring/Quote/CancellationFacadeInterface.php +++ b/Api/Monitoring/Quote/CancellationFacadeInterface.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Model\Monitoring\Quote; +namespace Vipps\Payment\Api\Monitoring\Quote; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; @@ -24,6 +24,7 @@ /** * Quote Cancellation Facade. * It cancels the quote. Provides an ability to send cancellation request to Vipps. + * @api */ interface CancellationFacadeInterface { @@ -41,9 +42,9 @@ public function cancelMagento(CartInterface $quote, string $type, string $reason /** * @param CartInterface $quote - * @param Cancellation $cancellation * @param Transaction|null $transaction * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws NoSuchEntityException */ - public function cancelVipps(CartInterface $quote, Cancellation $cancellation, Transaction $transaction = null); + public function cancelVipps(CartInterface $quote, Transaction $transaction = null); } diff --git a/Api/Monitoring/QuoteManagementInterface.php b/Api/Monitoring/QuoteManagementInterface.php index ab109ed4..4550c688 100644 --- a/Api/Monitoring/QuoteManagementInterface.php +++ b/Api/Monitoring/QuoteManagementInterface.php @@ -25,6 +25,7 @@ /** * Interface QuoteManagementInterface + * @api */ interface QuoteManagementInterface { diff --git a/Api/Monitoring/QuoteRepositoryInterface.php b/Api/Monitoring/QuoteRepositoryInterface.php index 651ee4e6..38c27f4e 100644 --- a/Api/Monitoring/QuoteRepositoryInterface.php +++ b/Api/Monitoring/QuoteRepositoryInterface.php @@ -22,6 +22,7 @@ /** * Interface QuoteRepositoryInterface + * @api */ interface QuoteRepositoryInterface { diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index fffd665a..c0cac267 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -19,22 +19,19 @@ use Magento\Framework\App\Config\ScopeCodeResolver; use Magento\Framework\Exception\{NoSuchEntityException}; -use Magento\Quote\Api\{CartRepositoryInterface}; use Magento\Quote\Model\{Quote, ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, + Api\Monitoring\Data\QuoteCancellationInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, + Model\Monitoring\Quote\CancellationFacade, Model\Monitoring\Quote\CancellationRepository, - Model\Order\Cancellation\Config, - Model\OrderPlace}; -use Vipps\Payment\Model\Monitoring\Quote\AttemptManagement; + Model\Order\Cancellation\Config}; use Vipps\Payment\Model\Monitoring\Quote\CancellationFactory; use Vipps\Payment\Model\Monitoring\QuoteManagement as QuoteMonitorManagement; -use Vipps\Payment\Model\Monitoring\QuoteRepository as QuoteMonitorRepository; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote\Cancellation\Type as CancellationTypeResource; /** * Class FetchOrderStatus @@ -63,21 +60,11 @@ class CancelQuoteByAttempts */ private $transactionBuilder; - /** - * @var OrderPlace - */ - private $orderPlace; - /** * @var LoggerInterface */ private $logger; - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - /** * @var StoreManagerInterface */ @@ -87,32 +74,19 @@ class CancelQuoteByAttempts * @var ScopeCodeResolver */ private $scopeCodeResolver; + /** * @var QuoteMonitorManagement */ private $quoteManagement; + /** * @var Config */ private $cancellationConfig; + /** - * @var CancellationFactory - */ - private $cancellationFactory; - /** - * @var CancellationRepository - */ - private $cancellationRepository; - /** - * @var QuoteMonitorRepository - */ - private $quoteMonitorRepository; - /** - * @var AttemptManagement - */ - private $attemptManagement; - /** - * @var \Vipps\Payment\Model\Monitoring\Quote\CancellationFacade + * @var CancellationFacade */ private $cancellationFacade; @@ -122,43 +96,32 @@ class CancelQuoteByAttempts * @param CollectionFactory $quoteCollectionFactory * @param CommandManagerInterface $commandManager * @param TransactionBuilder $transactionBuilder - * @param OrderPlace $orderManagement - * @param CartRepositoryInterface $cartRepository * @param LoggerInterface $logger * @param StoreManagerInterface $storeManager * @param ScopeCodeResolver $scopeCodeResolver + * @param QuoteMonitorManagement $quoteManagement + * @param Config $cancellationConfig + * @param CancellationFacade $cancellationFacade */ public function __construct( CollectionFactory $quoteCollectionFactory, CommandManagerInterface $commandManager, TransactionBuilder $transactionBuilder, - OrderPlace $orderManagement, - CartRepositoryInterface $cartRepository, LoggerInterface $logger, StoreManagerInterface $storeManager, ScopeCodeResolver $scopeCodeResolver, QuoteMonitorManagement $quoteManagement, Config $cancellationConfig, - CancellationFactory $cancellationFactory, - CancellationRepository $cancellationRepository, - QuoteMonitorRepository $quoteMonitorRepository, - AttemptManagement $attemptManagement, - \Vipps\Payment\Model\Monitoring\Quote\CancellationFacade $cancellationFacade + CancellationFacade $cancellationFacade ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; $this->transactionBuilder = $transactionBuilder; - $this->orderPlace = $orderManagement; - $this->cartRepository = $cartRepository; $this->logger = $logger; $this->storeManager = $storeManager; $this->scopeCodeResolver = $scopeCodeResolver; $this->quoteManagement = $quoteManagement; $this->cancellationConfig = $cancellationConfig; - $this->cancellationFactory = $cancellationFactory; - $this->cancellationRepository = $cancellationRepository; - $this->quoteMonitorRepository = $quoteMonitorRepository; - $this->attemptManagement = $attemptManagement; $this->cancellationFacade = $cancellationFacade; } @@ -193,6 +156,8 @@ public function execute() /** * Get quote collection to cancel. + * Conditions are: + * number of attempts greater than allowed * * @param $currentPage * @@ -224,15 +189,7 @@ private function createCollection($currentPage) ); // Filter not cancelled quotes. - $collection - ->getSelect() - ->joinLeft( - ['vqc' => $collection->getTable('vipps_quote_cancellation')], - 'vq.entity_id = vqc.parent_id', - [] - ); - $collection->addFieldToFilter('vqc.entity_id', ['null' => 1]); - + $collection->addFieldToFilter('vq.is_canceled', ['neq' => 1]); return $collection; } @@ -265,7 +222,7 @@ private function processQuote(Quote $quote) ->cancellationFacade ->cancelMagento( $quote, - CancellationTypeResource::MAGENTO, + QuoteCancellationInterface::CANCEL_TYPE_MAGENTO, __('Number of attempts reached: %1', $this->cancellationConfig->getAttemptsMaxCount()), $transaction ); @@ -295,17 +252,4 @@ private function fetchOrderStatus($orderId) $response = $this->commandManager->getOrderStatus($orderId); return $this->transactionBuilder->setData($response)->build(); } - - /** - * @param Quote $quote - * @param \DateInterval $interval - * - * @return bool - */ -// private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine -// { -// $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine -// $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine -// return $isQuoteExpired; -// } } diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 24353f57..10c242d1 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -25,6 +25,8 @@ use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, + Api\Monitoring\Data\QuoteCancellationInterface, + Api\Monitoring\Data\QuoteInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, @@ -224,19 +226,12 @@ private function createCollection($currentPage) ); // Filter not cancelled quotes. - $collection - ->getSelect() - ->joinLeft( - ['vqc' => $collection->getTable('vipps_quote_cancellation')], - 'vq.entity_id = vqc.parent_id', - [] - ); - $collection->addFieldToFilter('vqc.entity_id', ['null' => 1]); + $collection->addFieldToFilter('vq.is_canceled', ['neq' => 1]); - // @todo: uncomment this. -// $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); -// $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min -// $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); + // @todo discuss if this legacy should be removed. + $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); + $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min + $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); return $collection; } @@ -262,7 +257,7 @@ private function processQuote(Quote $quote) ->cancellationFacade ->cancelMagento( $quote, - CancellationTypeResource::VIPPS, + QuoteCancellationInterface::CANCEL_TYPE_VIPPS, 'Transaction was cancelled on Vipps side', $transaction ); diff --git a/Model/Monitoring/Quote.php b/Model/Monitoring/Quote.php index bad18f3b..bac6244c 100644 --- a/Model/Monitoring/Quote.php +++ b/Model/Monitoring/Quote.php @@ -19,12 +19,13 @@ use Magento\Framework\Model\AbstractModel; use Vipps\Payment\Api\Monitoring\Data\QuoteInterface; +use Vipps\Payment\Api\Monitoring\Data\QuoteCancellationInterface; use Vipps\Payment\Model\ResourceModel\Monitoring\Quote as QuoteResource; /** * Quote monitoring model. */ -class Quote extends AbstractModel implements QuoteInterface +class Quote extends AbstractModel implements QuoteInterface, QuoteCancellationInterface { /** * @param int $quoteId @@ -129,6 +130,56 @@ public function getAttempts() return $this->getData(self::ATTEMPTS); } + /** + * @return bool + */ + public function isCanceled() + { + return $this->getData(self::IS_CANCELED); + } + + /** + * @param bool $isCanceled + * @return Quote + */ + public function setIsCanceled(bool $isCanceled) + { + return $this->setData(self::IS_CANCELED, $isCanceled); + } + + /** + * @return string|null + */ + public function getCancelType() + { + return $this->getData(self::CANCEL_TYPE); + } + + /** + * @return string|null + */ + public function getCancelReason() + { + return $this->getData(self::CANCEL_REASON); + } + + /** + * @param string $reason + */ + public function setCancelReason(string $reason) + { + $this->setData(self::CANCEL_REASON, $reason); + } + + /** + * @param string $type + * @return Quote + */ + public function setCancelType($type) + { + return $this->setData(self::CANCEL_TYPE, $type); + } + /** * Clear attempts. */ diff --git a/Model/Monitoring/Quote/Cancellation.php b/Model/Monitoring/Quote/Cancellation.php deleted file mode 100644 index 33a1ffb3..00000000 --- a/Model/Monitoring/Quote/Cancellation.php +++ /dev/null @@ -1,87 +0,0 @@ -getData(self::PARENT_ID); - } - - /** - * @return string - */ - public function getType() - { - return $this->getData(self::TYPE); - } - - /** - * @return string - */ - public function getPhrase() - { - return $this->getData(self::PHRASE); - } - - /** - * @param int $parentId - * @return self - */ - public function setParentId(int $parentId) - { - return $this->setData($parentId, $parentId); - } - - /** - * @param string $type - * @return self - */ - public function setType(string $type) - { - $this->setData(self::TYPE, $type); - } - - /** - * @param string $phrase - * @return self - */ - public function setPhrase(string $phrase) - { - return $this->setData(self::PHRASE, $phrase); - } - - /** - * Constructor. - */ - protected function _construct() - { - $this->_init(CancellationResource::class); - } -} diff --git a/Model/Monitoring/Quote/CancellationFacade.php b/Model/Monitoring/Quote/CancellationFacade.php index caf43d16..ad85b134 100644 --- a/Model/Monitoring/Quote/CancellationFacade.php +++ b/Model/Monitoring/Quote/CancellationFacade.php @@ -20,7 +20,11 @@ use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\{Api\CommandManagerInterface, + Api\Monitoring\Data\QuoteCancellationInterface, + Api\Monitoring\Data\QuoteInterface, + Api\Monitoring\Quote\CancellationFacadeInterface, Gateway\Transaction\Transaction, + Model\Monitoring\QuoteRepository, Model\Order\Cancellation\Config, Model\ResourceModel\Monitoring\Quote\Cancellation\Type as CancellationTypeResource}; @@ -34,36 +38,30 @@ class CancellationFacade implements CancellationFacadeInterface * @var CommandManagerInterface */ private $commandManager; - /** - * @var CancellationFactory - */ - private $cancellationFactory; - /** - * @var CancellationRepository - */ - private $cancellationRepository; + /** * @var Config */ private $config; + /** + * @var QuoteRepository + */ + private $quoteRepository; /** * CancellationFacade constructor. * @param CommandManagerInterface $commandManager - * @param CancellationFactory $cancellationFactory - * @param CancellationRepository $cancelRepository + * @param QuoteRepository $quoteRepository * @param Config $config */ public function __construct( CommandManagerInterface $commandManager, - CancellationFactory $cancellationFactory, - CancellationRepository $cancelRepository, + QuoteRepository $quoteRepository, Config $config ) { $this->commandManager = $commandManager; - $this->cancellationFactory = $cancellationFactory; - $this->cancellationRepository = $cancelRepository; $this->config = $config; + $this->quoteRepository = $quoteRepository; } /** @@ -82,28 +80,42 @@ public function cancelMagento( string $reason, Transaction $transaction = null ) { - /** @var \Vipps\Payment\Api\Monitoring\Data\QuoteInterface $quoteMonitoring */ - $quoteMonitoring = $quote->getExtensionAttributes()->getVippsMonitoring(); - - if (!$quoteMonitoring) { - throw new NoSuchEntityException(__('Vipps Monitoring model is not loaded in Quote extension attributes')); - } + /** @var \Vipps\Payment\Api\Monitoring\Data\QuoteInterface $monitoring */ + $monitoring = $this->getMonitoring($quote); - // Creating magento cancellation. - $cancellation = $this - ->cancellationFactory - ->create($quoteMonitoring, $type, $reason); + $monitoring + ->setIsCanceled(QuoteInterface::IS_CANCELED_YES) + ->setCancelType($type) + ->setCancelReason($reason); - $this->cancellationRepository->save($cancellation); + $this->quoteRepository->save($monitoring); // Nothing to cancel if cancellation initialed by Vipps. - if ($type === CancellationTypeResource::MAGENTO + if ($type === QuoteCancellationInterface::CANCEL_TYPE_MAGENTO && $this->isAutomaticVippsCancellation($quote->getStoreId()) ) { - $this->cancelVipps($quote, $cancellation, $transaction); + $this->cancelVipps($quote, $transaction); } } + /** + * Get Vipps Quote Monitoring entity. + * + * @param $quote + * @return mixed + * @throws NoSuchEntityException + */ + private function getMonitoring($quote) + { + $monitoring = $quote->getExtensionAttributes()->getVippsMonitoring(); + + if (!$monitoring) { + throw new NoSuchEntityException(__('Vipps Monitoring model is not loaded in Quote extension attributes')); + } + + return $monitoring; + } + /** * @param int|null $storeId * @return bool @@ -115,21 +127,22 @@ private function isAutomaticVippsCancellation($storeId = null) /** * @param CartInterface $quote - * @param Cancellation $cancellation * @param Transaction|null $transaction * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws NoSuchEntityException */ public function cancelVipps( CartInterface $quote, - Cancellation $cancellation, Transaction $transaction = null ) { + $monitoring = $this->getMonitoring($quote); + // cancel order on vipps side if ($transaction && $transaction->isTransactionReserved()) { $this->commandManager->cancel($quote->getPayment()); - $cancellation->setType(CancellationTypeResource::ALL); - $this->cancellationRepository->save($cancellation); + $monitoring->setType(QuoteCancellationInterface::CANCEL_TYPE_ALL); + $this->quoteRepository->save($monitoring); } } } diff --git a/Model/Monitoring/Quote/CancellationFactory.php b/Model/Monitoring/Quote/CancellationFactory.php deleted file mode 100644 index 172ce348..00000000 --- a/Model/Monitoring/Quote/CancellationFactory.php +++ /dev/null @@ -1,62 +0,0 @@ -objectManager = $objectManager; - } - - /** - * Create cancellation entity. - * - * @param QuoteInterface $monitoringQuote - * @param string|null $type - * @param string|null $code - * @param string|null $reason - * @return Cancellation - */ - public function create(QuoteInterface $monitoringQuote, string $type = null, string $reason = null) - { - $data = [ - Cancellation::PARENT_ID => $monitoringQuote->getEntityId(), - Cancellation::TYPE => $type, - Cancellation::PHRASE => $reason - ]; - - return $this - ->objectManager - ->create(Cancellation::class, ['data' => $data]); - } -} diff --git a/Model/Monitoring/Quote/CancellationRepository.php b/Model/Monitoring/Quote/CancellationRepository.php deleted file mode 100644 index 1ced7154..00000000 --- a/Model/Monitoring/Quote/CancellationRepository.php +++ /dev/null @@ -1,62 +0,0 @@ -resource = $resource; - } - - /** - * @param QuoteCancellationInterface $cancellation - * @return QuoteCancellationInterface - * @throws CouldNotSaveException - */ - public function save(QuoteCancellationInterface $cancellation) - { - try { - $cancellation->setDataChanges(true); - $this->resource->save($cancellation); - - return $cancellation; - } catch (\Exception $e) { - throw new CouldNotSaveException( - __( - 'Could not save Vipps Quote Cancellation: %1', - $e->getMessage() - ), - $e - ); - } - } -} diff --git a/Model/Monitoring/QuoteManagement.php b/Model/Monitoring/QuoteManagement.php index 3c424747..95de6704 100644 --- a/Model/Monitoring/QuoteManagement.php +++ b/Model/Monitoring/QuoteManagement.php @@ -76,7 +76,6 @@ public function loadExtensionAttribute(CartInterface $quote) { if ($extensionAttributes = $quote->getExtensionAttributes()) { if (!$extensionAttributes->getVippsMonitoring()) { - $monitoringQuote = $this->getByQuote($quote); $extensionAttributes->setVippsMonitoring($monitoringQuote); diff --git a/Model/Monitoring/QuoteRepository.php b/Model/Monitoring/QuoteRepository.php index 8efcecae..cc6ee40d 100644 --- a/Model/Monitoring/QuoteRepository.php +++ b/Model/Monitoring/QuoteRepository.php @@ -74,14 +74,13 @@ public function save(QuoteInterface $quote) /** * @param $quoteId + * @return Quote * @throws NoSuchEntityException */ public function loadByQuote($quoteId) { - $monitoringQuote = $this->quoteFactory->create([]); - + $monitoringQuote = $this->quoteFactory->create(); $this->quoteResource->load($monitoringQuote, $quoteId, 'quote_id'); - if (!$monitoringQuote->getId()) { throw NoSuchEntityException::singleField('quote_id', $quoteId); } diff --git a/Model/ResourceModel/Monitoring/Quote/Cancellation.php b/Model/ResourceModel/Monitoring/Quote/Cancellation.php deleted file mode 100644 index a2e4da30..00000000 --- a/Model/ResourceModel/Monitoring/Quote/Cancellation.php +++ /dev/null @@ -1,44 +0,0 @@ -_init(self::TABLE_NAME, self::INDEX_FIELD); - } -} diff --git a/Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php b/Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php deleted file mode 100644 index f8483a61..00000000 --- a/Model/ResourceModel/Monitoring/Quote/Cancellation/Type.php +++ /dev/null @@ -1,39 +0,0 @@ -getVersion(), '1.2.1', '<')) { - $this->createCancellationTable($installer); + $this->addCancelationToQuote($installer); } $installer->endSetup(); } @@ -116,7 +116,8 @@ private function createVippsQuoteTable(SchemaSetupInterface $installer): void Table::TYPE_TIMESTAMP, null, [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT_UPDATE, Table::OPTION_NULLABLE => false], - 'Updated at') + 'Updated at' + ) ->addIndex($installer->getIdxName('vipps_quote', 'quote_id'), 'quote_id') ->addForeignKey( $installer->getFkName('vipps_quote', 'quote_id', 'quote', 'entity_id'), @@ -183,53 +184,47 @@ private function createVippsAttemptsTable(SchemaSetupInterface $installer): void * @param SchemaSetupInterface $installer * @throws \Zend_Db_Exception */ - private function createCancellationTable(SchemaSetupInterface $installer): void + private function addCancelationToQuote(SchemaSetupInterface $installer): void { $connection = $installer->getConnection(); + $tableName = $connection->getTableName('vipps_quote'); - $table = $connection->newTable($connection->getTableName('vipps_quote_cancellation')) - ->addColumn( - 'entity_id', - Table::TYPE_INTEGER, - null, - ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], - 'Entity Id' - )->addColumn( - 'parent_id', - Table::TYPE_INTEGER, - null, - ['nullable' => false, 'unsigned' => true], - 'Vipps Quote Id' - ) + $connection ->addColumn( - 'type', - Table::TYPE_TEXT, - 10, - [], - 'Type' - ) + $tableName, + 'is_canceled', + [ + Table::OPTION_TYPE => Table::TYPE_BOOLEAN, + Table::OPTION_NULLABLE => true, + Table::OPTION_DEFAULT => 0, + 'comment' => 'Is canceled', + 'after' => 'attempts' + ] + ); + + $connection ->addColumn( - 'phrase', - Table::TYPE_TEXT, - null, - [Table::OPTION_NULLABLE => true], - 'Reason Phrase' - )->addColumn( - 'created_at', - Table::TYPE_TIMESTAMP, - null, - [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT, Table::OPTION_NULLABLE => false], - 'Created at' - ) - ->addIndex($installer->getIdxName('vipps_quote_cancellation', 'parent_id'), 'parent_id') - ->addForeignKey( - $installer->getFkName('vipps_quote_cancellation', 'parent_id', 'vipps_quote', 'entity_id'), - 'parent_id', - 'vipps_quote', - 'entity_id', - Table::ACTION_CASCADE + $tableName, + 'cancel_type', + [ + Table::OPTION_TYPE => Table::TYPE_TEXT, + Table::OPTION_LENGTH => 10, + Table::OPTION_NULLABLE => true, + 'comment' => 'Cancellation Type', + 'after' => 'is_canceled' + ] ); - $installer->getConnection()->createTable($table); + $connection + ->addColumn( + $tableName, + 'cancel_reason', + [ + Table::OPTION_TYPE => Table::TYPE_TEXT, + Table::OPTION_NULLABLE => true, + 'comment' => 'Cancellation Reason', + 'after' => 'cancel_type' + ] + ); } } From 5fa7e44a5d4879038ae9cf8f9d8cf90dfc3418dc Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Sun, 13 Jan 2019 12:43:27 +0200 Subject: [PATCH 079/121] VIPPS-179: Remove unnecessary module upgrade. --- Setup/UpgradeSchema.php | 25 ------------------------- etc/module.xml | 2 +- 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index e2067ef0..58ead821 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -46,10 +46,6 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con $this->addCancelationToQuote($installer); } - if (version_compare($context->getVersion(), '1.3.0', '<')) { - $this->addCancelToVippsQuote($installer); - } - $installer->endSetup(); } @@ -232,25 +228,4 @@ private function addCancelationToQuote(SchemaSetupInterface $installer): void ] ); } - - /** - * @param SchemaSetupInterface $installer - */ - private function addCancelToVippsQuote(SchemaSetupInterface $installer) - { - $connection = $installer->getConnection(); - - $connection - ->addColumn( - $connection->getTableName('vipps_quote'), - 'is_canceled', - [ - Table::OPTION_TYPE => Table::TYPE_BOOLEAN, - Table::OPTION_DEFAULT => 0, - Table::OPTION_NULLABLE => false, - 'comment' => 'Is Canceled', - 'after' => 'reserved_order_id' - ] - ); - } } diff --git a/etc/module.xml b/etc/module.xml index cd943f90..76df3b9f 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + From a122bc5779e0c093e81819950dce7e92363ed271 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 28 Jan 2019 14:37:17 +0200 Subject: [PATCH 080/121] VIPPS-189: Adjustments Remove 2.3 features. Remove "Monitoring" word from directory/namespace. Remove module extra version. --- .../Data/QuoteAttemptInterface.php | 2 +- .../Data/QuoteCancellationInterface.php | 2 +- Api/{Monitoring => }/Data/QuoteInterface.php | 2 +- .../Quote/AttemptRepositoryInterface.php | 4 +- .../Quote/CancellationFacadeInterface.php | 3 +- .../QuoteManagementInterface.php | 8 ++-- .../QuoteRepositoryInterface.php | 4 +- Block/Monitoring/View.php | 4 +- Cron/CancelQuoteByAttempts.php | 10 ++--- Cron/FetchOrderFromVipps.php | 20 ++++----- Gateway/Response/InitiateHandler.php | 2 +- Model/{Monitoring => }/Quote.php | 18 ++++++-- Model/{Monitoring => }/Quote/Attempt.php | 6 +-- .../Quote/AttemptManagement.php | 9 ++-- .../Quote/AttemptRepository.php | 8 ++-- .../Quote/CancellationFacade.php | 13 +++--- Model/{Monitoring => }/QuoteManagement.php | 8 ++-- Model/{Monitoring => }/QuoteRepository.php | 9 ++-- .../ResourceModel/{Monitoring => }/Quote.php | 2 +- .../{Monitoring => }/Quote/Attempt.php | 2 +- .../{Monitoring => }/Quote/GridCollection.php | 4 +- Setup/UpgradeSchema.php | 43 +++++++++---------- etc/module.xml | 2 +- 23 files changed, 93 insertions(+), 92 deletions(-) rename Api/{Monitoring => }/Data/QuoteAttemptInterface.php (97%) rename Api/{Monitoring => }/Data/QuoteCancellationInterface.php (98%) rename Api/{Monitoring => }/Data/QuoteInterface.php (98%) rename Api/{Monitoring => }/Quote/AttemptRepositoryInterface.php (92%) rename Api/{Monitoring => }/Quote/CancellationFacadeInterface.php (95%) rename Api/{Monitoring => }/QuoteManagementInterface.php (87%) rename Api/{Monitoring => }/QuoteRepositoryInterface.php (93%) rename Model/{Monitoring => }/Quote.php (91%) rename Model/{Monitoring => }/Quote/Attempt.php (92%) rename Model/{Monitoring => }/Quote/AttemptManagement.php (92%) rename Model/{Monitoring => }/Quote/AttemptRepository.php (87%) rename Model/{Monitoring => }/Quote/CancellationFacade.php (91%) rename Model/{Monitoring => }/QuoteManagement.php (94%) rename Model/{Monitoring => }/QuoteRepository.php (93%) rename Model/ResourceModel/{Monitoring => }/Quote.php (96%) rename Model/ResourceModel/{Monitoring => }/Quote/Attempt.php (95%) rename Model/ResourceModel/{Monitoring => }/Quote/GridCollection.php (95%) diff --git a/Api/Monitoring/Data/QuoteAttemptInterface.php b/Api/Data/QuoteAttemptInterface.php similarity index 97% rename from Api/Monitoring/Data/QuoteAttemptInterface.php rename to Api/Data/QuoteAttemptInterface.php index b1ebda11..dd820a9d 100644 --- a/Api/Monitoring/Data/QuoteAttemptInterface.php +++ b/Api/Data/QuoteAttemptInterface.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Api\Monitoring\Data; +namespace Vipps\Payment\Api\Data; /** * Interface QuoteInterface diff --git a/Api/Monitoring/Data/QuoteCancellationInterface.php b/Api/Data/QuoteCancellationInterface.php similarity index 98% rename from Api/Monitoring/Data/QuoteCancellationInterface.php rename to Api/Data/QuoteCancellationInterface.php index 7446f7fe..8b74474f 100644 --- a/Api/Monitoring/Data/QuoteCancellationInterface.php +++ b/Api/Data/QuoteCancellationInterface.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Api\Monitoring\Data; +namespace Vipps\Payment\Api\Data; /** * Interface QuoteInterface diff --git a/Api/Monitoring/Data/QuoteInterface.php b/Api/Data/QuoteInterface.php similarity index 98% rename from Api/Monitoring/Data/QuoteInterface.php rename to Api/Data/QuoteInterface.php index 46682619..1eba6a6f 100644 --- a/Api/Monitoring/Data/QuoteInterface.php +++ b/Api/Data/QuoteInterface.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Api\Monitoring\Data; +namespace Vipps\Payment\Api\Data; /** * Interface QuoteInterface diff --git a/Api/Monitoring/Quote/AttemptRepositoryInterface.php b/Api/Quote/AttemptRepositoryInterface.php similarity index 92% rename from Api/Monitoring/Quote/AttemptRepositoryInterface.php rename to Api/Quote/AttemptRepositoryInterface.php index 00566ffd..6dd58479 100644 --- a/Api/Monitoring/Quote/AttemptRepositoryInterface.php +++ b/Api/Quote/AttemptRepositoryInterface.php @@ -15,10 +15,10 @@ * */ -namespace Vipps\Payment\Api\Monitoring\Quote; +namespace Vipps\Payment\Api\Quote; use Magento\Framework\Exception\CouldNotSaveException; -use Vipps\Payment\Api\Monitoring\Data\QuoteAttemptInterface; +use Vipps\Payment\Api\Data\QuoteAttemptInterface; /** * Interface AttemptRepositoryInterface diff --git a/Api/Monitoring/Quote/CancellationFacadeInterface.php b/Api/Quote/CancellationFacadeInterface.php similarity index 95% rename from Api/Monitoring/Quote/CancellationFacadeInterface.php rename to Api/Quote/CancellationFacadeInterface.php index 935c23bf..da582843 100644 --- a/Api/Monitoring/Quote/CancellationFacadeInterface.php +++ b/Api/Quote/CancellationFacadeInterface.php @@ -15,12 +15,11 @@ * */ -namespace Vipps\Payment\Api\Monitoring\Quote; +namespace Vipps\Payment\Api\Quote; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\Gateway\Transaction\Transaction; -use Vipps\Payment\Model\Monitoring\Quote\Cancellation; /** * Quote Cancellation Facade. diff --git a/Api/Monitoring/QuoteManagementInterface.php b/Api/QuoteManagementInterface.php similarity index 87% rename from Api/Monitoring/QuoteManagementInterface.php rename to Api/QuoteManagementInterface.php index 4550c688..9f29f293 100644 --- a/Api/Monitoring/QuoteManagementInterface.php +++ b/Api/QuoteManagementInterface.php @@ -15,13 +15,11 @@ * */ -namespace Vipps\Payment\Api\Monitoring; +namespace Vipps\Payment\Api; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; -use Vipps\Payment\Api\Monitoring\Data\QuoteInterface; -use Vipps\Payment\Model\Monitoring\Quote; -use Vipps\Payment\Model\Monitoring\QuoteManagement; +use Vipps\Payment\Api\Data\QuoteInterface; +use Vipps\Payment\Model\Quote; /** * Interface QuoteManagementInterface diff --git a/Api/Monitoring/QuoteRepositoryInterface.php b/Api/QuoteRepositoryInterface.php similarity index 93% rename from Api/Monitoring/QuoteRepositoryInterface.php rename to Api/QuoteRepositoryInterface.php index 38c27f4e..a4c0121e 100644 --- a/Api/Monitoring/QuoteRepositoryInterface.php +++ b/Api/QuoteRepositoryInterface.php @@ -15,10 +15,10 @@ * */ -namespace Vipps\Payment\Api\Monitoring; +namespace Vipps\Payment\Api; use Magento\Framework\Exception\CouldNotSaveException; -use Vipps\Payment\Api\Monitoring\Data\QuoteInterface; +use Vipps\Payment\Api\Data\QuoteInterface; /** * Interface QuoteRepositoryInterface diff --git a/Block/Monitoring/View.php b/Block/Monitoring/View.php index 2bd76f15..da954d7f 100644 --- a/Block/Monitoring/View.php +++ b/Block/Monitoring/View.php @@ -19,7 +19,7 @@ use Magento\Framework\View\Element\Template; use Magento\Quote\Api\CartRepositoryInterface; -use Vipps\Payment\Model\Monitoring\QuoteRepository as MonitoringQuoteRepository; +use Vipps\Payment\Model\QuoteRepository as MonitoringQuoteRepository; /** * View Quote Monitoring entity. @@ -80,7 +80,7 @@ public function getQuote() } /** - * @return \Vipps\Payment\Api\Monitoring\Data\QuoteInterface + * @return \Vipps\Payment\Api\Data\QuoteInterface * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getMonitoringQuote() diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index 677ba9e9..10d51339 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -23,15 +23,13 @@ use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, - Api\Monitoring\Data\QuoteCancellationInterface, + Api\Data\QuoteCancellationInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, - Model\Monitoring\Quote\CancellationFacade, - Model\Monitoring\Quote\CancellationRepository, - Model\Order\Cancellation\Config}; -use Vipps\Payment\Model\Monitoring\Quote\CancellationFactory; -use Vipps\Payment\Model\Monitoring\QuoteManagement as QuoteMonitorManagement; + Model\Order\Cancellation\Config, + Model\Quote\CancellationFacade}; +use Vipps\Payment\Model\QuoteManagement as QuoteMonitorManagement; /** * Class FetchOrderStatus diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 10c242d1..15630ba7 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -25,21 +25,17 @@ use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, - Api\Monitoring\Data\QuoteCancellationInterface, - Api\Monitoring\Data\QuoteInterface, + Api\Data\QuoteCancellationInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, Model\Order\Cancellation\Config, - Model\OrderPlace}; + Model\OrderPlace, + Model\Quote\AttemptManagement, + Model\Quote\CancellationFacade, + Model\QuoteManagement as QuoteMonitorManagement, + Model\QuoteRepository as QuoteMonitorRepository}; use Vipps\Payment\Gateway\Exception\WrongAmountException; -use Vipps\Payment\Model\Monitoring\{Quote\AttemptManagement, - Quote\CancellationFacade, - Quote\CancellationFactory, - Quote\CancellationRepository, - QuoteManagement as QuoteMonitorManagement, - QuoteRepository as QuoteMonitorRepository}; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote\Cancellation\Type as CancellationTypeResource; /** * Class FetchOrderStatus @@ -281,13 +277,13 @@ private function processQuote(Quote $quote) * Create monitoring attempt. * * @param CartInterface $quote - * @return \Vipps\Payment\Model\Monitoring\Quote\Attempt + * @return \Vipps\Payment\Model\Quote\Attempt * @throws AlreadyExistsException * @throws CouldNotSaveException */ private function createMonitoringAttempt(CartInterface $quote) { - /** @var \Vipps\Payment\Model\Monitoring\Quote $monitoringQuote */ + /** @var \Vipps\Payment\Model\Quote $monitoringQuote */ $monitoringQuote = $quote ->getExtensionAttributes() ->getVippsMonitoring(); diff --git a/Gateway/Response/InitiateHandler.php b/Gateway/Response/InitiateHandler.php index 967fc019..a0710069 100644 --- a/Gateway/Response/InitiateHandler.php +++ b/Gateway/Response/InitiateHandler.php @@ -22,7 +22,7 @@ use Magento\Framework\{App\ResourceConnection, Session\SessionManagerInterface}; use Magento\Payment\Gateway\{Data\PaymentDataObjectInterface, Response\HandlerInterface}; use Magento\Quote\{Api\CartRepositoryInterface, Model\Quote\Payment}; -use Vipps\Payment\{Gateway\Request\SubjectReader, Model\Monitoring\QuoteManagement}; +use Vipps\Payment\{Gateway\Request\SubjectReader, Model\QuoteManagement}; /** * Class InitiateHandler diff --git a/Model/Monitoring/Quote.php b/Model/Quote.php similarity index 91% rename from Model/Monitoring/Quote.php rename to Model/Quote.php index bac6244c..80d3ff71 100644 --- a/Model/Monitoring/Quote.php +++ b/Model/Quote.php @@ -15,12 +15,12 @@ * */ -namespace Vipps\Payment\Model\Monitoring; +namespace Vipps\Payment\Model; use Magento\Framework\Model\AbstractModel; -use Vipps\Payment\Api\Monitoring\Data\QuoteInterface; -use Vipps\Payment\Api\Monitoring\Data\QuoteCancellationInterface; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote as QuoteResource; +use Vipps\Payment\Api\Data\QuoteCancellationInterface; +use Vipps\Payment\Api\Data\QuoteInterface; +use Vipps\Payment\Model\ResourceModel\Quote as QuoteResource; /** * Quote monitoring model. @@ -188,6 +188,16 @@ public function clearAttempts() $this->setAttempts(0); } + /** + * Get is canceled. + * + * @return bool + */ + public function getIsCanceled() + { + return $this->getData(self::IS_CANCELED) === self::IS_CANCELED_YES; + } + /** * Constructor. */ diff --git a/Model/Monitoring/Quote/Attempt.php b/Model/Quote/Attempt.php similarity index 92% rename from Model/Monitoring/Quote/Attempt.php rename to Model/Quote/Attempt.php index 5c122abe..ac5a00ec 100644 --- a/Model/Monitoring/Quote/Attempt.php +++ b/Model/Quote/Attempt.php @@ -15,11 +15,11 @@ * */ -namespace Vipps\Payment\Model\Monitoring\Quote; +namespace Vipps\Payment\Model\Quote; use Magento\Framework\Model\AbstractModel; -use Vipps\Payment\Api\Monitoring\Data\QuoteAttemptInterface; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote\Attempt as AttemptResource; +use Vipps\Payment\Api\Data\QuoteAttemptInterface; +use Vipps\Payment\Model\ResourceModel\Quote\Attempt as AttemptResource; /** * Quote cancellation model. diff --git a/Model/Monitoring/Quote/AttemptManagement.php b/Model/Quote/AttemptManagement.php similarity index 92% rename from Model/Monitoring/Quote/AttemptManagement.php rename to Model/Quote/AttemptManagement.php index dfcd97c6..cfcb2047 100644 --- a/Model/Monitoring/Quote/AttemptManagement.php +++ b/Model/Quote/AttemptManagement.php @@ -15,11 +15,12 @@ * */ -namespace Vipps\Payment\Model\Monitoring\Quote; +namespace Vipps\Payment\Model\Quote; -use Vipps\Payment\Model\{Monitoring\Quote as MonitoringQuote, - Monitoring\QuoteRepository as QuoteMonitorRepository, - ResourceModel\Monitoring\Quote\Attempt as Resource}; +use Vipps\Payment\Model\{Quote as MonitoringQuote, + Quote\AttemptFactory, + QuoteRepository as QuoteMonitorRepository, + ResourceModel\Quote\Attempt as Resource}; /** * Attempt Management. diff --git a/Model/Monitoring/Quote/AttemptRepository.php b/Model/Quote/AttemptRepository.php similarity index 87% rename from Model/Monitoring/Quote/AttemptRepository.php rename to Model/Quote/AttemptRepository.php index 7425c648..57125292 100644 --- a/Model/Monitoring/Quote/AttemptRepository.php +++ b/Model/Quote/AttemptRepository.php @@ -15,15 +15,15 @@ * */ -namespace Vipps\Payment\Model\Monitoring\Quote; +namespace Vipps\Payment\Model\Quote; use Magento\Framework\Exception\CouldNotSaveException; -use Vipps\Payment\Api\Monitoring\{Data\QuoteAttemptInterface, Quote\AttemptRepositoryInterface}; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote\Attempt as AttemptResource; +use Vipps\Payment\Api\{Data\QuoteAttemptInterface}; +use Vipps\Payment\Api\Quote\AttemptRepositoryInterface; +use Vipps\Payment\Model\ResourceModel\Quote\Attempt as AttemptResource; /** * Class AttemptRepository - * @package Vipps\Payment\Model\Monitoring\Quote */ class AttemptRepository implements AttemptRepositoryInterface { diff --git a/Model/Monitoring/Quote/CancellationFacade.php b/Model/Quote/CancellationFacade.php similarity index 91% rename from Model/Monitoring/Quote/CancellationFacade.php rename to Model/Quote/CancellationFacade.php index ad85b134..bb350507 100644 --- a/Model/Monitoring/Quote/CancellationFacade.php +++ b/Model/Quote/CancellationFacade.php @@ -15,18 +15,17 @@ * */ -namespace Vipps\Payment\Model\Monitoring\Quote; +namespace Vipps\Payment\Model\Quote; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\{Api\CommandManagerInterface, - Api\Monitoring\Data\QuoteCancellationInterface, - Api\Monitoring\Data\QuoteInterface, - Api\Monitoring\Quote\CancellationFacadeInterface, + Api\Data\QuoteCancellationInterface, + Api\Data\QuoteInterface, + Api\Quote\CancellationFacadeInterface, Gateway\Transaction\Transaction, - Model\Monitoring\QuoteRepository, Model\Order\Cancellation\Config, - Model\ResourceModel\Monitoring\Quote\Cancellation\Type as CancellationTypeResource}; + Model\QuoteRepository}; /** * Quote Cancellation Facade. @@ -80,7 +79,7 @@ public function cancelMagento( string $reason, Transaction $transaction = null ) { - /** @var \Vipps\Payment\Api\Monitoring\Data\QuoteInterface $monitoring */ + /** @var \Vipps\Payment\Api\Data\QuoteInterface $monitoring */ $monitoring = $this->getMonitoring($quote); $monitoring diff --git a/Model/Monitoring/QuoteManagement.php b/Model/QuoteManagement.php similarity index 94% rename from Model/Monitoring/QuoteManagement.php rename to Model/QuoteManagement.php index 95de6704..68459f3b 100644 --- a/Model/Monitoring/QuoteManagement.php +++ b/Model/QuoteManagement.php @@ -15,11 +15,13 @@ * */ -namespace Vipps\Payment\Model\Monitoring; +namespace Vipps\Payment\Model; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; -use Vipps\Payment\Api\Monitoring\{Data\QuoteInterface, QuoteManagementInterface}; +use Vipps\Payment\Api\Data\QuoteInterface; +use Vipps\Payment\Api\QuoteManagementInterface; +use Vipps\Payment\Model\QuoteFactory; /** * Class QuoteRepository @@ -32,7 +34,7 @@ class QuoteManagement implements QuoteManagementInterface private $quoteFactory; /** - * @var \Vipps\Payment\Model\Monitoring\QuoteRepository + * @var \Vipps\Payment\Model\QuoteRepository */ private $quoteRepository; diff --git a/Model/Monitoring/QuoteRepository.php b/Model/QuoteRepository.php similarity index 93% rename from Model/Monitoring/QuoteRepository.php rename to Model/QuoteRepository.php index d23e5ac8..74781e40 100644 --- a/Model/Monitoring/QuoteRepository.php +++ b/Model/QuoteRepository.php @@ -15,13 +15,14 @@ * */ -namespace Vipps\Payment\Model\Monitoring; +namespace Vipps\Payment\Model; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\NoSuchEntityException; -use Vipps\Payment\Api\Monitoring\Data\QuoteInterface; -use Vipps\Payment\Api\Monitoring\QuoteRepositoryInterface; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote as QuoteResource; +use Vipps\Payment\Api\Data\QuoteInterface; +use Vipps\Payment\Api\QuoteRepositoryInterface; +use Vipps\Payment\Model\QuoteFactory; +use Vipps\Payment\Model\ResourceModel\Quote as QuoteResource; /** * Class QuoteRepository diff --git a/Model/ResourceModel/Monitoring/Quote.php b/Model/ResourceModel/Quote.php similarity index 96% rename from Model/ResourceModel/Monitoring/Quote.php rename to Model/ResourceModel/Quote.php index fa9ca8ef..8396569f 100644 --- a/Model/ResourceModel/Monitoring/Quote.php +++ b/Model/ResourceModel/Quote.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Model\ResourceModel\Monitoring; +namespace Vipps\Payment\Model\ResourceModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; diff --git a/Model/ResourceModel/Monitoring/Quote/Attempt.php b/Model/ResourceModel/Quote/Attempt.php similarity index 95% rename from Model/ResourceModel/Monitoring/Quote/Attempt.php rename to Model/ResourceModel/Quote/Attempt.php index 552f8e1b..4f4e7097 100644 --- a/Model/ResourceModel/Monitoring/Quote/Attempt.php +++ b/Model/ResourceModel/Quote/Attempt.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Model\ResourceModel\Monitoring\Quote; +namespace Vipps\Payment\Model\ResourceModel\Quote; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; diff --git a/Model/ResourceModel/Monitoring/Quote/GridCollection.php b/Model/ResourceModel/Quote/GridCollection.php similarity index 95% rename from Model/ResourceModel/Monitoring/Quote/GridCollection.php rename to Model/ResourceModel/Quote/GridCollection.php index 69c62b64..b88d01e7 100644 --- a/Model/ResourceModel/Monitoring/Quote/GridCollection.php +++ b/Model/ResourceModel/Quote/GridCollection.php @@ -15,7 +15,7 @@ * */ -namespace Vipps\Payment\Model\ResourceModel\Monitoring\Quote; +namespace Vipps\Payment\Model\ResourceModel\Quote; use Magento\Customer\Ui\Component\DataProvider\Document; use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as FetchStrategy; @@ -24,7 +24,7 @@ use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult; use Psr\Log\LoggerInterface as Logger; -use Vipps\Payment\Model\ResourceModel\Monitoring\Quote; +use Vipps\Payment\Model\ResourceModel\Quote; /** * Class Collection diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 58ead821..6b7ee54f 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -40,9 +40,6 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con if (version_compare($context->getVersion(), '1.2.0', '<')) { $this->createVippsQuoteTable($installer); $this->createVippsAttemptsTable($installer); - } - - if (version_compare($context->getVersion(), '1.2.1', '<')) { $this->addCancelationToQuote($installer); } @@ -76,7 +73,7 @@ private function addPaymentJwtScope(SchemaSetupInterface $installer) * @param SchemaSetupInterface $installer * @throws \Zend_Db_Exception */ - private function createVippsQuoteTable(SchemaSetupInterface $installer): void + private function createVippsQuoteTable(SchemaSetupInterface $installer) { $connection = $installer->getConnection(); @@ -110,13 +107,13 @@ private function createVippsQuoteTable(SchemaSetupInterface $installer): void 'created_at', Table::TYPE_TIMESTAMP, null, - [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT, Table::OPTION_NULLABLE => false], + ['default' => Table::TIMESTAMP_INIT, 'nullable' => false], 'Created at' )->addColumn( 'updated_at', Table::TYPE_TIMESTAMP, null, - [Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT_UPDATE, Table::OPTION_NULLABLE => false], + ['default' => Table::TIMESTAMP_INIT_UPDATE, 'nullable' => false], 'Updated at' ) ->addIndex($installer->getIdxName('vipps_quote', 'quote_id'), 'quote_id') @@ -137,7 +134,7 @@ private function createVippsQuoteTable(SchemaSetupInterface $installer): void * @param SchemaSetupInterface $installer Schema installer. * @throws \Zend_Db_Exception */ - private function createVippsAttemptsTable(SchemaSetupInterface $installer): void + private function createVippsAttemptsTable(SchemaSetupInterface $installer) { $connection = $installer->getConnection(); @@ -164,7 +161,7 @@ private function createVippsAttemptsTable(SchemaSetupInterface $installer): void 'created_at', Table::TYPE_TIMESTAMP, null, - [Table::OPTION_NULLABLE => false, Table::OPTION_DEFAULT => Table::TIMESTAMP_INIT], + ['nullable' => false, 'default' => Table::TIMESTAMP_INIT], 'Created at' ) ->addIndex($installer->getIdxName('vipps_quote_attempts', 'parent_id'), 'parent_id') @@ -185,7 +182,7 @@ private function createVippsAttemptsTable(SchemaSetupInterface $installer): void * @param SchemaSetupInterface $installer * @throws \Zend_Db_Exception */ - private function addCancelationToQuote(SchemaSetupInterface $installer): void + private function addCancelationToQuote(SchemaSetupInterface $installer) { $connection = $installer->getConnection(); $tableName = $connection->getTableName('vipps_quote'); @@ -195,11 +192,11 @@ private function addCancelationToQuote(SchemaSetupInterface $installer): void $tableName, 'is_canceled', [ - Table::OPTION_TYPE => Table::TYPE_BOOLEAN, - Table::OPTION_NULLABLE => true, - Table::OPTION_DEFAULT => 0, - 'comment' => 'Is canceled', - 'after' => 'attempts' + 'type' => Table::TYPE_BOOLEAN, + 'nullable' => true, + 'default' => 0, + 'comment' => 'Is canceled', + 'after' => 'attempts' ] ); @@ -208,11 +205,11 @@ private function addCancelationToQuote(SchemaSetupInterface $installer): void $tableName, 'cancel_type', [ - Table::OPTION_TYPE => Table::TYPE_TEXT, - Table::OPTION_LENGTH => 10, - Table::OPTION_NULLABLE => true, - 'comment' => 'Cancellation Type', - 'after' => 'is_canceled' + 'type' => Table::TYPE_TEXT, + 'length' => 10, + 'nullable' => true, + 'comment' => 'Cancellation Type', + 'after' => 'is_canceled' ] ); @@ -221,10 +218,10 @@ private function addCancelationToQuote(SchemaSetupInterface $installer): void $tableName, 'cancel_reason', [ - Table::OPTION_TYPE => Table::TYPE_TEXT, - Table::OPTION_NULLABLE => true, - 'comment' => 'Cancellation Reason', - 'after' => 'cancel_type' + 'type' => Table::TYPE_TEXT, + 'nullable' => true, + 'comment' => 'Cancellation Reason', + 'after' => 'cancel_type' ] ); } diff --git a/etc/module.xml b/etc/module.xml index 76df3b9f..e8f6531a 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -16,7 +16,7 @@ */ --> - + From 069e2c2c6e3e97a9a3364e3ae7c50c56a9917a6d Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 28 Jan 2019 15:26:06 +0200 Subject: [PATCH 081/121] VIPPS-189: Adjustments. Reorganize Cancellation interface. --- Api/Data/QuoteCancellationInterface.php | 4 +- Api/Data/QuoteInterface.php | 25 +----- Api/Quote/CancellationFacadeInterface.php | 13 +-- Cron/CancelQuoteByAttempts.php | 2 +- Cron/FetchOrderFromVipps.php | 9 +- Model/Quote.php | 17 +--- Model/Quote/AttemptManagement.php | 14 +--- Model/Quote/CancellationFacade.php | 84 +++++++++++++------ .../adminhtml/templates/monitoring/view.phtml | 2 +- 9 files changed, 73 insertions(+), 97 deletions(-) diff --git a/Api/Data/QuoteCancellationInterface.php b/Api/Data/QuoteCancellationInterface.php index 8b74474f..b59ae56c 100644 --- a/Api/Data/QuoteCancellationInterface.php +++ b/Api/Data/QuoteCancellationInterface.php @@ -74,10 +74,10 @@ public function getCancelType(); public function getCancelReason(); /** - * @param bool $isCanceled + * @param int $isCanceled * @return self */ - public function setIsCanceled(bool $isCanceled); + public function setIsCanceled(int $isCanceled); /** * @param string $reason diff --git a/Api/Data/QuoteInterface.php b/Api/Data/QuoteInterface.php index 1eba6a6f..0400e9df 100644 --- a/Api/Data/QuoteInterface.php +++ b/Api/Data/QuoteInterface.php @@ -21,14 +21,8 @@ * Interface QuoteInterface * @api */ -interface QuoteInterface +interface QuoteInterface extends QuoteCancellationInterface { - /** - * @const Canceled statuses. - */ - const IS_CANCELED_YES = 1; - const IS_CANCELED_NO = 0; - /** * @const string */ @@ -59,11 +53,6 @@ interface QuoteInterface */ const ATTEMPTS = 'attempts'; - /** - * @const string - */ - const IS_CANCELED = 'is_canceled'; - /** * @param int $quoteId * @return self @@ -94,12 +83,6 @@ public function setUpdatedAt(string $updatedAt); */ public function setAttempts(int $attempts); - /** - * @param int $isCanceled - * @return self - */ - public function setIsCanceled(int $isCanceled); - /** * @return int */ @@ -129,10 +112,4 @@ public function getAttempts(); * @return int */ public function getEntityId(); - - /** - * @return int - */ - public function getIsCanceled(); - } diff --git a/Api/Quote/CancellationFacadeInterface.php b/Api/Quote/CancellationFacadeInterface.php index da582843..97498997 100644 --- a/Api/Quote/CancellationFacadeInterface.php +++ b/Api/Quote/CancellationFacadeInterface.php @@ -23,13 +23,12 @@ /** * Quote Cancellation Facade. - * It cancels the quote. Provides an ability to send cancellation request to Vipps. * @api */ interface CancellationFacadeInterface { /** - * vipps_monitoring extension attribute requires to be loaded in the quote. + * Cancel Vipps payment transaction both, Magento and Vipps. * * @param CartInterface $quote * @param string $type @@ -38,13 +37,5 @@ interface CancellationFacadeInterface * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function cancelMagento(CartInterface $quote, string $type, string $reason, Transaction $transaction = null); - - /** - * @param CartInterface $quote - * @param Transaction|null $transaction - * @throws \Magento\Framework\Exception\CouldNotSaveException - * @throws NoSuchEntityException - */ - public function cancelVipps(CartInterface $quote, Transaction $transaction = null); + public function cancel(CartInterface $quote, string $type, string $reason, Transaction $transaction = null); } diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index 10d51339..a6907e1d 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -217,7 +217,7 @@ private function processQuote(Quote $quote) } finally { $this ->cancellationFacade - ->cancelMagento( + ->cancel( $quote, QuoteCancellationInterface::CANCEL_TYPE_MAGENTO, __('Number of attempts reached: %1', $this->cancellationConfig->getAttemptsMaxCount()), diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 15630ba7..472e9d97 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -238,10 +238,10 @@ private function createCollection($currentPage) private function processQuote(Quote $quote) { try { + // Load vipps quote monitoring as extension attribute. $this->quoteManagement->loadExtensionAttribute($quote); - $attempt = $this->createMonitoringAttempt($quote); + $attempt = $this->createAttempt($quote); - // Load vipps quote monitoring as extension attribute. $this->prepareEnv($quote); $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); @@ -251,7 +251,7 @@ private function processQuote(Quote $quote) // Create cancellation if transaction was aborted on Vipps side. $this ->cancellationFacade - ->cancelMagento( + ->cancel( $quote, QuoteCancellationInterface::CANCEL_TYPE_VIPPS, 'Transaction was cancelled on Vipps side', @@ -278,10 +278,9 @@ private function processQuote(Quote $quote) * * @param CartInterface $quote * @return \Vipps\Payment\Model\Quote\Attempt - * @throws AlreadyExistsException * @throws CouldNotSaveException */ - private function createMonitoringAttempt(CartInterface $quote) + private function createAttempt(CartInterface $quote) { /** @var \Vipps\Payment\Model\Quote $monitoringQuote */ $monitoringQuote = $quote diff --git a/Model/Quote.php b/Model/Quote.php index 80d3ff71..1dde76f8 100644 --- a/Model/Quote.php +++ b/Model/Quote.php @@ -18,14 +18,13 @@ namespace Vipps\Payment\Model; use Magento\Framework\Model\AbstractModel; -use Vipps\Payment\Api\Data\QuoteCancellationInterface; use Vipps\Payment\Api\Data\QuoteInterface; use Vipps\Payment\Model\ResourceModel\Quote as QuoteResource; /** * Quote monitoring model. */ -class Quote extends AbstractModel implements QuoteInterface, QuoteCancellationInterface +class Quote extends AbstractModel implements QuoteInterface { /** * @param int $quoteId @@ -139,10 +138,10 @@ public function isCanceled() } /** - * @param bool $isCanceled + * @param int $isCanceled * @return Quote */ - public function setIsCanceled(bool $isCanceled) + public function setIsCanceled(int $isCanceled) { return $this->setData(self::IS_CANCELED, $isCanceled); } @@ -188,16 +187,6 @@ public function clearAttempts() $this->setAttempts(0); } - /** - * Get is canceled. - * - * @return bool - */ - public function getIsCanceled() - { - return $this->getData(self::IS_CANCELED) === self::IS_CANCELED_YES; - } - /** * Constructor. */ diff --git a/Model/Quote/AttemptManagement.php b/Model/Quote/AttemptManagement.php index cfcb2047..3deb1c51 100644 --- a/Model/Quote/AttemptManagement.php +++ b/Model/Quote/AttemptManagement.php @@ -17,10 +17,7 @@ namespace Vipps\Payment\Model\Quote; -use Vipps\Payment\Model\{Quote as MonitoringQuote, - Quote\AttemptFactory, - QuoteRepository as QuoteMonitorRepository, - ResourceModel\Quote\Attempt as Resource}; +use Vipps\Payment\Model\{Quote as MonitoringQuote, Quote\AttemptFactory, QuoteRepository as QuoteMonitorRepository}; /** * Attempt Management. @@ -32,11 +29,6 @@ class AttemptManagement */ private $attemptFactory; - /** - * @var Resource - */ - private $attemptResource; - /** * @var QuoteMonitorRepository */ @@ -51,17 +43,15 @@ class AttemptManagement * AttemptManagement constructor. * * @param AttemptFactory $attemptFactory - * @param Resource $resource * @param QuoteMonitorRepository $quoteRepository + * @param AttemptRepository $attemptRepository */ public function __construct( AttemptFactory $attemptFactory, - Resource $resource, QuoteMonitorRepository $quoteRepository, AttemptRepository $attemptRepository ) { $this->attemptFactory = $attemptFactory; - $this->attemptResource = $resource; $this->quoteMonitorRepository = $quoteRepository; $this->attemptRepository = $attemptRepository; } diff --git a/Model/Quote/CancellationFacade.php b/Model/Quote/CancellationFacade.php index bb350507..d95b816e 100644 --- a/Model/Quote/CancellationFacade.php +++ b/Model/Quote/CancellationFacade.php @@ -73,27 +73,21 @@ public function __construct( * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function cancelMagento( + public function cancel( CartInterface $quote, string $type, string $reason, Transaction $transaction = null ) { - /** @var \Vipps\Payment\Api\Data\QuoteInterface $monitoring */ - $monitoring = $this->getMonitoring($quote); + /** @var \Vipps\Payment\Api\Data\QuoteInterface $vippsQuote */ + $vippsQuote = $this->getVippsQuote($quote); - $monitoring - ->setIsCanceled(QuoteInterface::IS_CANCELED_YES) - ->setCancelType($type) - ->setCancelReason($reason); - - $this->quoteRepository->save($monitoring); + if ($this->isAllowedToCancelMagento($vippsQuote)) { + $this->cancelMagento($vippsQuote, $type, $reason); + } - // Nothing to cancel if cancellation initialed by Vipps. - if ($type === QuoteCancellationInterface::CANCEL_TYPE_MAGENTO - && $this->isAutomaticVippsCancellation($quote->getStoreId()) - ) { - $this->cancelVipps($quote, $transaction); + if ($this->isAllowedToCancelVipps($type, $quote->getStoreId(), $transaction)) { + $this->cancelVipps($quote, $vippsQuote); } } @@ -104,17 +98,58 @@ public function cancelMagento( * @return mixed * @throws NoSuchEntityException */ - private function getMonitoring($quote) + private function getVippsQuote($quote) { $monitoring = $quote->getExtensionAttributes()->getVippsMonitoring(); if (!$monitoring) { - throw new NoSuchEntityException(__('Vipps Monitoring model is not loaded in Quote extension attributes')); + throw new NoSuchEntityException(__('Vipps Quote model is not loaded in Quote extension attributes')); } return $monitoring; } + /** + * @param QuoteInterface $vippsQuote + * @return bool + */ + private function isAllowedToCancelMagento(QuoteInterface $vippsQuote) + { + return !$vippsQuote->isCanceled(); + } + + /** + * vipps_monitoring extension attribute requires to be loaded in the quote. + * + * @param QuoteInterface $vippsQuote + * @param string $type + * @param string $reason + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + private function cancelMagento(QuoteInterface $vippsQuote, string $type, string $reason) + { + $vippsQuote + ->setIsCanceled(QuoteInterface::IS_CANCELED_YES) + ->setCancelType($type) + ->setCancelReason($reason); + + $this->quoteRepository->save($vippsQuote); + } + + /** + * @param $type + * @param int $storeId + * @param Transaction|null $transaction + * @return bool + */ + private function isAllowedToCancelVipps($type, int $storeId, Transaction $transaction = null) + { + return $type === QuoteCancellationInterface::CANCEL_TYPE_MAGENTO // Initiated by Magento + && $this->isAutomaticVippsCancellation($storeId) // Automatic cancel is allowed by configuration + && $transaction // There is transaction in Vipps that can be canceled + && $transaction->isTransactionReserved(); + } + /** * @param int|null $storeId * @return bool @@ -126,22 +161,17 @@ private function isAutomaticVippsCancellation($storeId = null) /** * @param CartInterface $quote - * @param Transaction|null $transaction + * @param QuoteInterface $vippsQuote * @throws \Magento\Framework\Exception\CouldNotSaveException - * @throws NoSuchEntityException */ - public function cancelVipps( + private function cancelVipps( CartInterface $quote, - Transaction $transaction = null + QuoteInterface $vippsQuote ) { - $monitoring = $this->getMonitoring($quote); - // cancel order on vipps side - if ($transaction && $transaction->isTransactionReserved()) { - $this->commandManager->cancel($quote->getPayment()); + $this->commandManager->cancel($quote->getPayment()); - $monitoring->setType(QuoteCancellationInterface::CANCEL_TYPE_ALL); - $this->quoteRepository->save($monitoring); - } + $vippsQuote->setCancelType(QuoteCancellationInterface::CANCEL_TYPE_ALL); + $this->quoteRepository->save($vippsQuote); } } diff --git a/view/adminhtml/templates/monitoring/view.phtml b/view/adminhtml/templates/monitoring/view.phtml index 55dc805b..215c75bc 100644 --- a/view/adminhtml/templates/monitoring/view.phtml +++ b/view/adminhtml/templates/monitoring/view.phtml @@ -40,7 +40,7 @@ $monitoringQuote = $block->getMonitoringQuote(); getIsCanceled() ? 'Yes' : 'No'; ?> + echo $monitoringQuote->isCanceled() ? 'Yes' : 'No'; ?> From 6f7a5e2568fefda13a9f9154c8e2b8691be262bc Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Tue, 29 Jan 2019 12:32:49 +0200 Subject: [PATCH 082/121] VIPPS-189: Adjustments. Rename monitoring quote to vipps quote. Add backward compatibility with old quotes processing. --- ...nterface.php => CancelFacadeInterface.php} | 19 ++- Block/Monitoring/View.php | 24 ++-- Cron/CancelQuoteByAttempts.php | 71 +++++----- Cron/FetchOrderFromVipps.php | 131 +++++++----------- Gateway/Response/InitiateHandler.php | 6 +- Model/Quote/AttemptManagement.php | 6 +- ...ancellationFacade.php => CancelFacade.php} | 69 ++++----- Model/QuoteManagement.php | 4 +- Model/ResourceModel/Quote/Collection.php | 42 ++++++ Setup/UpgradeData.php | 92 ++++++++++++ etc/di.xml | 2 +- etc/extension_attributes.xml | 2 +- .../adminhtml/templates/monitoring/view.phtml | 2 +- 13 files changed, 291 insertions(+), 179 deletions(-) rename Api/Quote/{CancellationFacadeInterface.php => CancelFacadeInterface.php} (77%) rename Model/Quote/{CancellationFacade.php => CancelFacade.php} (77%) create mode 100644 Model/ResourceModel/Quote/Collection.php create mode 100644 Setup/UpgradeData.php diff --git a/Api/Quote/CancellationFacadeInterface.php b/Api/Quote/CancelFacadeInterface.php similarity index 77% rename from Api/Quote/CancellationFacadeInterface.php rename to Api/Quote/CancelFacadeInterface.php index 97498997..8aa186ff 100644 --- a/Api/Quote/CancellationFacadeInterface.php +++ b/Api/Quote/CancelFacadeInterface.php @@ -17,25 +17,30 @@ namespace Vipps\Payment\Api\Quote; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; +use Vipps\Payment\Api\Data\QuoteInterface; use Vipps\Payment\Gateway\Transaction\Transaction; /** - * Quote Cancellation Facade. + * Cancels Vipps payment everywhere. * @api */ -interface CancellationFacadeInterface +interface CancelFacadeInterface { /** * Cancel Vipps payment transaction both, Magento and Vipps. * - * @param CartInterface $quote + * @param QuoteInterface $vippsQuote * @param string $type * @param string $reason + * @param CartInterface|null $quote * @param Transaction|null $transaction - * @throws NoSuchEntityException - * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function cancel(CartInterface $quote, string $type, string $reason, Transaction $transaction = null); + public function cancel( + QuoteInterface $vippsQuote, + string $type, + string $reason, + CartInterface $quote = null, + Transaction $transaction = null + ); } diff --git a/Block/Monitoring/View.php b/Block/Monitoring/View.php index da954d7f..48ba9c78 100644 --- a/Block/Monitoring/View.php +++ b/Block/Monitoring/View.php @@ -19,7 +19,7 @@ use Magento\Framework\View\Element\Template; use Magento\Quote\Api\CartRepositoryInterface; -use Vipps\Payment\Model\QuoteRepository as MonitoringQuoteRepository; +use Vipps\Payment\Model\QuoteRepository as vippsQuoteRepository; /** * View Quote Monitoring entity. @@ -27,13 +27,13 @@ class View extends Template { /** - * @var MonitoringQuoteRepository + * @var vippsQuoteRepository */ - private $monitoringQuoteRepository; + private $vippsQuoteRepository; /** * @var */ - private $monitoringQuote; + private $vippsQuote; /** * @var CartRepositoryInterface */ @@ -47,19 +47,19 @@ class View extends Template /** * View constructor. - * @param MonitoringQuoteRepository $monitoringQuoteRepository + * @param vippsQuoteRepository $monitoringQuoteRepository * @param CartRepositoryInterface $quoteRepository * @param Template\Context $context * @param array $data */ public function __construct( - MonitoringQuoteRepository $monitoringQuoteRepository, + vippsQuoteRepository $monitoringQuoteRepository, CartRepositoryInterface $quoteRepository, Template\Context $context, array $data = [] ) { parent::__construct($context, $data); - $this->monitoringQuoteRepository = $monitoringQuoteRepository; + $this->vippsQuoteRepository = $monitoringQuoteRepository; $this->quoteRepository = $quoteRepository; } @@ -70,7 +70,7 @@ public function getQuote() { if (!$this->quote) { try { - $this->quote = $this->quoteRepository->get($this->getMonitoringQuote()->getQuoteId()); + $this->quote = $this->quoteRepository->get($this->getVippsQuote()->getQuoteId()); } catch (\Exception $e) { $this->quoteLoadingError = $e->getMessage(); } @@ -83,17 +83,17 @@ public function getQuote() * @return \Vipps\Payment\Api\Data\QuoteInterface * @throws \Magento\Framework\Exception\NoSuchEntityException */ - public function getMonitoringQuote() + public function getVippsQuote() { - if (!$this->monitoringQuote) { + if (!$this->vippsQuote) { try { - $this->monitoringQuote = $this->monitoringQuoteRepository->load($this->getRequest()->getParam('id')); + $this->vippsQuote = $this->vippsQuoteRepository->load($this->getRequest()->getParam('id')); } catch (\Exception $e) { // Display this error in template. } } - return $this->monitoringQuote; + return $this->vippsQuote; } /** diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index a6907e1d..3b177491 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -18,17 +18,21 @@ namespace Vipps\Payment\Cron; use Magento\Framework\App\Config\ScopeCodeResolver; -use Magento\Framework\Exception\{NoSuchEntityException}; -use Magento\Quote\Model\{Quote, ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; +use Magento\Framework\Exception\{CouldNotSaveException, NoSuchEntityException}; +use Magento\Quote\Api\{CartRepositoryInterface}; +use Magento\Quote\Model\{Quote, ResourceModel\Quote\CollectionFactory}; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, Api\Data\QuoteCancellationInterface, + Api\Data\QuoteInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, Model\Order\Cancellation\Config, - Model\Quote\CancellationFacade}; + Model\Quote\CancelFacade, + Model\ResourceModel\Quote\Collection as VippsQuoteCollection, + Model\ResourceModel\Quote\CollectionFactory as VippsQuoteCollectionFactory}; use Vipps\Payment\Model\QuoteManagement as QuoteMonitorManagement; /** @@ -84,9 +88,17 @@ class CancelQuoteByAttempts private $cancellationConfig; /** - * @var CancellationFacade + * @var CancelFacade */ private $cancellationFacade; + /** + * @var VippsQuoteCollectionFactory + */ + private $vippsQuoteCollectionFactory; + /** + * @var CartRepositoryInterface + */ + private $cartRepository; /** * FetchOrderFromVipps constructor. @@ -99,7 +111,9 @@ class CancelQuoteByAttempts * @param ScopeCodeResolver $scopeCodeResolver * @param QuoteMonitorManagement $quoteManagement * @param Config $cancellationConfig - * @param CancellationFacade $cancellationFacade + * @param CancelFacade $cancellationFacade + * @param VippsQuoteCollectionFactory $vippsQuoteCollectionFactory + * @param CartRepositoryInterface $cartRepository */ public function __construct( CollectionFactory $quoteCollectionFactory, @@ -110,7 +124,9 @@ public function __construct( ScopeCodeResolver $scopeCodeResolver, QuoteMonitorManagement $quoteManagement, Config $cancellationConfig, - CancellationFacade $cancellationFacade + CancelFacade $cancellationFacade, + VippsQuoteCollectionFactory $vippsQuoteCollectionFactory, + CartRepositoryInterface $cartRepository ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; @@ -121,6 +137,8 @@ public function __construct( $this->quoteManagement = $quoteManagement; $this->cancellationConfig = $cancellationConfig; $this->cancellationFacade = $cancellationFacade; + $this->vippsQuoteCollectionFactory = $vippsQuoteCollectionFactory; + $this->cartRepository = $cartRepository; } /** @@ -153,41 +171,29 @@ public function execute() } /** - * Get quote collection to cancel. + * Get vipps quote collection to cancel. * Conditions are: * number of attempts greater than allowed * * @param $currentPage * - * @return Collection + * @return VippsQuoteCollection */ private function createCollection($currentPage) { - /** @var Collection $collection */ - $collection = $this->quoteCollectionFactory->create(); + /** @var VippsQuoteCollection $collection */ + $collection = $this->vippsQuoteCollectionFactory->create(); $collection ->setPageSize(self::COLLECTION_PAGE_SIZE) ->setCurPage($currentPage) - ->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']) - ->join( - ['p' => $collection->getTable('quote_payment')], - 'main_table.entity_id = p.quote_id', - ['p.method'] - ) - ->addFieldToFilter('p.method', ['eq' => 'vipps']) - ->join( - ['vq' => $collection->getTable('vipps_quote')], - 'main_table.entity_id = vq.quote_id', - ['vq.attempts'] - ) ->addFieldToFilter( - 'vq.attempts', + 'attempts', ['gteq' => $this->cancellationConfig->getAttemptsMaxCount()] ); // Filter not cancelled quotes. - $collection->addFieldToFilter('vq.is_canceled', ['neq' => 1]); + $collection->addFieldToFilter('is_canceled', ['neq' => 1]); return $collection; } @@ -195,32 +201,31 @@ private function createCollection($currentPage) /** * Main process * - * @param Quote $quote + * @param QuoteInterface $vippsQuote * - * @throws NoSuchEntityException - * @throws \Exception + * @throws CouldNotSaveException */ - private function processQuote(Quote $quote) + private function processQuote(QuoteInterface $vippsQuote) { $transaction = null; - $this->logger->info('Start quote cancelling', ['quote_id' => $quote->getId()]); + $this->logger->info('Start quote cancelling', ['vipps_quote_id' => $vippsQuote->getId()]); try { - // Load vipps quote monitoring as extension attribute. - $this->quoteManagement->loadExtensionAttribute($quote); + $quote = $this->cartRepository->get($vippsQuote->getQuoteId()); $this->prepareEnv($quote); $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); } catch (\Throwable $e) { - $this->logger->critical($e->getMessage(), ['quote_id' => $quote->getId()]); + $this->logger->critical($e->getMessage(), ['quote_id' => $vippsQuote->getId()]); } finally { $this ->cancellationFacade ->cancel( - $quote, + $vippsQuote, QuoteCancellationInterface::CANCEL_TYPE_MAGENTO, __('Number of attempts reached: %1', $this->cancellationConfig->getAttemptsMaxCount()), + $quote, $transaction ); } diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 472e9d97..9f0f013b 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -20,7 +20,7 @@ use Magento\Framework\Exception\{AlreadyExistsException, CouldNotSaveException, InputException, NoSuchEntityException}; use Magento\Framework\Exception\LocalizedException; use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; -use Magento\Quote\Model\{Quote, ResourceModel\Quote\Collection, ResourceModel\Quote\CollectionFactory}; +use Magento\Quote\Model\{Quote, QuoteRepository, ResourceModel\Quote\CollectionFactory}; use Magento\Sales\Api\Data\OrderInterface; use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; @@ -31,10 +31,12 @@ Gateway\Transaction\TransactionBuilder, Model\Order\Cancellation\Config, Model\OrderPlace, + Model\Quote as VippsQuote, Model\Quote\AttemptManagement, - Model\Quote\CancellationFacade, + Model\Quote\CancelFacade, Model\QuoteManagement as QuoteMonitorManagement, - Model\QuoteRepository as QuoteMonitorRepository}; + Model\ResourceModel\Quote\Collection as VippsQuoteCollection, + Model\ResourceModel\Quote\CollectionFactory as VippsQuoteCollectionFactory}; use Vipps\Payment\Gateway\Exception\WrongAmountException; /** @@ -88,38 +90,34 @@ class FetchOrderFromVipps * @var ScopeCodeResolver */ private $scopeCodeResolver; - /** - * @var QuoteMonitorManagement - */ - private $quoteManagement; + /** * @var Config */ private $cancellationConfig; + /** - * @var CancellationFactory - */ - private $cancellationFactory; - /** - * @var CancellationRepository + * @var AttemptManagement */ - private $cancellationRepository; + private $attemptManagement; /** - * @var QuoteMonitorRepository + * @var CancelFacade */ - private $quoteMonitorRepository; + private $cancellationFacade; /** - * @var AttemptManagement + * @var VippsQuoteCollectionFactory */ - private $attemptManagement; + private $vippsQuoteCollectionFactory; /** - * @var CancellationFacade + * @var QuoteRepository */ - private $cancellationFacade; + private $quoteRepository; /** * FetchOrderFromVipps constructor. * @param CollectionFactory $quoteCollectionFactory + * @param VippsQuoteCollectionFactory $vippsQuoteCollectionFactory + * @param QuoteRepository $quoteRepository * @param CommandManagerInterface $commandManager * @param TransactionBuilder $transactionBuilder * @param OrderPlace $orderManagement @@ -129,20 +127,21 @@ class FetchOrderFromVipps * @param QuoteMonitorManagement $quoteManagement * @param Config $cancellationConfig * @param AttemptManagement $attemptManagement - * @param CancellationFacade $cancellationFacade + * @param CancelFacade $cancellationFacade */ public function __construct( CollectionFactory $quoteCollectionFactory, + VippsQuoteCollectionFactory $vippsQuoteCollectionFactory, + QuoteRepository $quoteRepository, CommandManagerInterface $commandManager, TransactionBuilder $transactionBuilder, OrderPlace $orderManagement, LoggerInterface $logger, StoreManagerInterface $storeManager, ScopeCodeResolver $scopeCodeResolver, - QuoteMonitorManagement $quoteManagement, Config $cancellationConfig, AttemptManagement $attemptManagement, - CancellationFacade $cancellationFacade + CancelFacade $cancellationFacade ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; @@ -151,10 +150,11 @@ public function __construct( $this->logger = $logger; $this->storeManager = $storeManager; $this->scopeCodeResolver = $scopeCodeResolver; - $this->quoteManagement = $quoteManagement; $this->cancellationConfig = $cancellationConfig; $this->attemptManagement = $attemptManagement; $this->cancellationFacade = $cancellationFacade; + $this->vippsQuoteCollectionFactory = $vippsQuoteCollectionFactory; + $this->quoteRepository = $quoteRepository; } /** @@ -169,17 +169,18 @@ public function execute() $currentStore = $this->storeManager->getStore()->getId(); $currentPage = 1; do { - $quoteCollection = $this->createCollection($currentPage); + $vippsQuoteCollection = $this->createCollection($currentPage); $this->logger->debug( 'Fetched payment details', - ['page' => $currentPage, 'count' => $quoteCollection->count()] + ['page' => $currentPage, 'count' => $vippsQuoteCollection->count()] ); - foreach ($quoteCollection as $quote) { - $this->processQuote($quote); + /** @var VippsQuote $vippsQuote */ + foreach ($vippsQuoteCollection as $vippsQuote) { + $this->processQuote($vippsQuote); usleep(1000000); //delay for 1 second } $currentPage++; - } while ($currentPage <= $quoteCollection->getLastPageNumber()); + } while ($currentPage <= $vippsQuoteCollection->getLastPageNumber()); } finally { $this->storeManager->setCurrentStore($currentStore); } @@ -188,60 +189,40 @@ public function execute() /** * @param $currentPage * - * @return Collection + * @return VippsQuoteCollection */ private function createCollection($currentPage) { - /** @var Collection $collection */ - $collection = $this->quoteCollectionFactory->create(); + /** @var VippsQuoteCollection $collection */ + $collection = $this->vippsQuoteCollectionFactory->create(); $collection ->setPageSize(self::COLLECTION_PAGE_SIZE) ->setCurPage($currentPage) - ->addFieldToSelect(['entity_id', 'reserved_order_id', 'store_id', 'updated_at']) - ->join( - ['p' => $collection->getTable('quote_payment')], - 'main_table.entity_id = p.quote_id', - ['p.method'] + ->addFieldToFilter( + 'attempts', + [ + ['lt' => $this->cancellationConfig->getAttemptsMaxCount()], + ['null' => 1] + ] ) - ->addFieldToFilter('p.method', ['eq' => 'vipps']); - - // Filter quotes that reached max count of queries. They will be cancelled by another job. - $collection - ->getSelect() - ->joinLeft( - ['vq' => $collection->getTable('vipps_quote')], - 'main_table.entity_id = vq.quote_id', - ['attempts'] - ); + ->addFieldToFilter('is_canceled', ['neq' => 1]); // Filter not cancelled quotes. - $collection->addFieldToFilter('vq.attempts', [ - ['lt' => $this->cancellationConfig->getAttemptsMaxCount()], - ['null' => 1] - ] - ); - - // Filter not cancelled quotes. - $collection->addFieldToFilter('vq.is_canceled', ['neq' => 1]); - - // @todo discuss if this legacy should be removed. - $collection->addFieldToFilter('main_table.is_active', ['in' => ['0']]); - $collection->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)]); // 5min - $collection->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); return $collection; } /** - * @param Quote $quote + * @param VippsQuote $vippsQuote * @throws CouldNotSaveException */ - private function processQuote(Quote $quote) + private function processQuote(VippsQuote $vippsQuote) { try { - // Load vipps quote monitoring as extension attribute. - $this->quoteManagement->loadExtensionAttribute($quote); - $attempt = $this->createAttempt($quote); + // Register empty attempt. + $attempt = $this->attemptManagement->createAttempt($vippsQuote); + // Get Magento Quote for processing. + $quote = $this->quoteRepository->get($vippsQuote->getQuoteId()); $this->prepareEnv($quote); $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); @@ -252,16 +233,17 @@ private function processQuote(Quote $quote) $this ->cancellationFacade ->cancel( - $quote, + $vippsQuote, QuoteCancellationInterface::CANCEL_TYPE_VIPPS, 'Transaction was cancelled on Vipps side', + $quote, $transaction ); } else { $this->placeOrder($quote, $transaction); } } catch (\Throwable $e) { - $this->logger->critical($e->getMessage(), ['quote_id' => $quote->getId()]); + $this->logger->critical($e->getMessage(), ['vipps_quote_id' => $vippsQuote->getId()]); if (isset($attempt)) { $attempt->setMessage($e->getMessage()); } @@ -273,23 +255,6 @@ private function processQuote(Quote $quote) } } - /** - * Create monitoring attempt. - * - * @param CartInterface $quote - * @return \Vipps\Payment\Model\Quote\Attempt - * @throws CouldNotSaveException - */ - private function createAttempt(CartInterface $quote) - { - /** @var \Vipps\Payment\Model\Quote $monitoringQuote */ - $monitoringQuote = $quote - ->getExtensionAttributes() - ->getVippsMonitoring(); - - return $this->attemptManagement->createAttempt($monitoringQuote); - } - /** * Prepare environment. * diff --git a/Gateway/Response/InitiateHandler.php b/Gateway/Response/InitiateHandler.php index a0710069..401b8525 100644 --- a/Gateway/Response/InitiateHandler.php +++ b/Gateway/Response/InitiateHandler.php @@ -59,7 +59,7 @@ class InitiateHandler implements HandlerInterface /** * @var QuoteManagement */ - private $quoteMonitoringManagement; + private $vippsQuoteManagement; /** * InitiateHandler constructor. @@ -84,7 +84,7 @@ public function __construct( $this->checkoutHelper = $checkoutHelper; $this->customerSession = $customerSession; $this->resourceConnection = $resourceConnection; - $this->quoteMonitoringManagement = $monitoringManagement; + $this->vippsQuoteManagement = $monitoringManagement; } /** @@ -121,7 +121,7 @@ public function handle(array $handlingSubject, array $responseBody) //@codingSta $connection->beginTransaction(); $this->cartRepository->save($quote); - $this->quoteMonitoringManagement->create($quote); + $this->vippsQuoteManagement->create($quote); $connection->commit(); } catch (\Exception $e) { diff --git a/Model/Quote/AttemptManagement.php b/Model/Quote/AttemptManagement.php index 3deb1c51..2b72e68d 100644 --- a/Model/Quote/AttemptManagement.php +++ b/Model/Quote/AttemptManagement.php @@ -17,7 +17,7 @@ namespace Vipps\Payment\Model\Quote; -use Vipps\Payment\Model\{Quote as MonitoringQuote, Quote\AttemptFactory, QuoteRepository as QuoteMonitorRepository}; +use Vipps\Payment\Model\{Quote as VippsQuote, Quote\AttemptFactory, QuoteRepository as QuoteMonitorRepository}; /** * Attempt Management. @@ -59,11 +59,11 @@ public function __construct( /** * Create new saved attempt. Increment attempt count. Fill it with message later. * - * @param MonitoringQuote $quote + * @param VippsQuote $quote * @return Attempt * @throws \Magento\Framework\Exception\CouldNotSaveException */ - public function createAttempt(MonitoringQuote $quote) + public function createAttempt(VippsQuote $quote) { $attempt = $this ->attemptFactory diff --git a/Model/Quote/CancellationFacade.php b/Model/Quote/CancelFacade.php similarity index 77% rename from Model/Quote/CancellationFacade.php rename to Model/Quote/CancelFacade.php index d95b816e..6cfe717b 100644 --- a/Model/Quote/CancellationFacade.php +++ b/Model/Quote/CancelFacade.php @@ -17,12 +17,11 @@ namespace Vipps\Payment\Model\Quote; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\{Api\CommandManagerInterface, Api\Data\QuoteCancellationInterface, Api\Data\QuoteInterface, - Api\Quote\CancellationFacadeInterface, + Api\Quote\CancelFacadeInterface, Gateway\Transaction\Transaction, Model\Order\Cancellation\Config, Model\QuoteRepository}; @@ -31,7 +30,7 @@ * Quote Cancellation Facade. * It cancels the quote. Provides an ability to send cancellation request to Vipps. */ -class CancellationFacade implements CancellationFacadeInterface +class CancelFacade implements CancelFacadeInterface { /** * @var CommandManagerInterface @@ -42,11 +41,17 @@ class CancellationFacade implements CancellationFacadeInterface * @var Config */ private $config; + /** * @var QuoteRepository */ private $quoteRepository; + /** + * @var bool + */ + private $forceVippsCancel = false; + /** * CancellationFacade constructor. * @param CommandManagerInterface $commandManager @@ -67,48 +72,28 @@ public function __construct( * vipps_monitoring extension attribute requires to be loaded in the quote. * * @param CartInterface $quote + * @param QuoteInterface $vippsQuote * @param string $type * @param string $reason * @param Transaction|null $transaction - * @throws NoSuchEntityException * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function cancel( - CartInterface $quote, + QuoteInterface $vippsQuote, string $type, string $reason, + CartInterface $quote = null, Transaction $transaction = null ) { - /** @var \Vipps\Payment\Api\Data\QuoteInterface $vippsQuote */ - $vippsQuote = $this->getVippsQuote($quote); - if ($this->isAllowedToCancelMagento($vippsQuote)) { $this->cancelMagento($vippsQuote, $type, $reason); } - if ($this->isAllowedToCancelVipps($type, $quote->getStoreId(), $transaction)) { - $this->cancelVipps($quote, $vippsQuote); + if ($this->isAllowedToCancelVipps($type, $quote, $transaction)) { + $this->cancelVipps(QuoteCancellationInterface::CANCEL_TYPE_ALL, $quote, $vippsQuote); } } - /** - * Get Vipps Quote Monitoring entity. - * - * @param $quote - * @return mixed - * @throws NoSuchEntityException - */ - private function getVippsQuote($quote) - { - $monitoring = $quote->getExtensionAttributes()->getVippsMonitoring(); - - if (!$monitoring) { - throw new NoSuchEntityException(__('Vipps Quote model is not loaded in Quote extension attributes')); - } - - return $monitoring; - } - /** * @param QuoteInterface $vippsQuote * @return bool @@ -138,14 +123,15 @@ private function cancelMagento(QuoteInterface $vippsQuote, string $type, string /** * @param $type - * @param int $storeId + * @param CartInterface|null $quote * @param Transaction|null $transaction * @return bool */ - private function isAllowedToCancelVipps($type, int $storeId, Transaction $transaction = null) + private function isAllowedToCancelVipps($type, CartInterface $quote = null, Transaction $transaction = null) { return $type === QuoteCancellationInterface::CANCEL_TYPE_MAGENTO // Initiated by Magento - && $this->isAutomaticVippsCancellation($storeId) // Automatic cancel is allowed by configuration + && $quote // Quote is required for Vipps cancellation + && $this->isAutomaticVippsCancellation($quote->getStoreId()) // Automatic cancel is allowed by configuration && $transaction // There is transaction in Vipps that can be canceled && $transaction->isTransactionReserved(); } @@ -156,7 +142,15 @@ private function isAllowedToCancelVipps($type, int $storeId, Transaction $transa */ private function isAutomaticVippsCancellation($storeId = null) { - return $this->config->isTypeAutomatic($storeId); + return $this->isForceVippsCancel() || $this->config->isTypeAutomatic($storeId); + } + + /** + * @return bool + */ + public function isForceVippsCancel(): bool + { + return $this->forceVippsCancel; } /** @@ -165,13 +159,22 @@ private function isAutomaticVippsCancellation($storeId = null) * @throws \Magento\Framework\Exception\CouldNotSaveException */ private function cancelVipps( + $type, CartInterface $quote, QuoteInterface $vippsQuote ) { // cancel order on vipps side $this->commandManager->cancel($quote->getPayment()); - $vippsQuote->setCancelType(QuoteCancellationInterface::CANCEL_TYPE_ALL); + $vippsQuote->setCancelType($type); $this->quoteRepository->save($vippsQuote); } + + /** + * @param bool $automaticVippsCancel + */ + public function setForceAutomaticVippsCancel(bool $automaticVippsCancel) + { + $this->forceVippsCancel = $automaticVippsCancel; + } } diff --git a/Model/QuoteManagement.php b/Model/QuoteManagement.php index 68459f3b..992cdc66 100644 --- a/Model/QuoteManagement.php +++ b/Model/QuoteManagement.php @@ -77,10 +77,10 @@ public function create(CartInterface $cart) public function loadExtensionAttribute(CartInterface $quote) { if ($extensionAttributes = $quote->getExtensionAttributes()) { - if (!$extensionAttributes->getVippsMonitoring()) { + if (!$extensionAttributes->getVippsQuote()) { $monitoringQuote = $this->getByQuote($quote); - $extensionAttributes->setVippsMonitoring($monitoringQuote); + $extensionAttributes->setVippsQuote($monitoringQuote); } } } diff --git a/Model/ResourceModel/Quote/Collection.php b/Model/ResourceModel/Quote/Collection.php new file mode 100644 index 00000000..b28f85b8 --- /dev/null +++ b/Model/ResourceModel/Quote/Collection.php @@ -0,0 +1,42 @@ +_init(QuoteModel::class, QuoteResource::class); + } +} diff --git a/Setup/UpgradeData.php b/Setup/UpgradeData.php new file mode 100644 index 00000000..99069422 --- /dev/null +++ b/Setup/UpgradeData.php @@ -0,0 +1,92 @@ +quoteCollectionFactory = $collectionFactory; + } + + /** + * Data updates on the module upgrade. + * + * @param ModuleDataSetupInterface $setup Setup interface. + * @param ModuleContextInterface $context Module context. + * @throws \Zend_Db_Exception + */ + public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context) // @codingStandardsIgnoreLine + { + $installer = $setup; + $installer->startSetup(); + + if (version_compare($context->getVersion(), '1.2.0', '<')) { + $this->fillVippsQuotes($setup); + } + + $installer->endSetup(); + } + + /** + * Fill Vipps quote tables for currently unprocessed quotes. + * + * @param ModuleDataSetupInterface $installer + * @return void + */ + private function fillVippsQuotes(ModuleDataSetupInterface $installer) + { + $connection = $installer->getConnection(); + $tableName = $connection->getTableName('vipps_quote'); + + /** @var Collection $collection */ + $collection = $this->quoteCollectionFactory->create(); + + $collection + ->addFieldToSelect('entity_id', 'quote_id') + ->addFieldToSelect('reserved_order_id') + ->join( + ['p' => $collection->getTable('quote_payment')], + 'main_table.entity_id = p.quote_id and p.method = "vipps"', + [] + ) + // Taking all old-style quotes that were valid for processing. Adding Vipps quote monitoring records for them. + ->addFieldToFilter('main_table.is_active', ['in' => ['0']]) + ->addFieldToFilter('main_table.updated_at', ['to' => date("Y-m-d H:i:s", time() - 300)])// 5 min + ->addFieldToFilter('main_table.reserved_order_id', ['neq' => '']); + + $updateSql = $connection + ->insertFromSelect( + $collection->getSelect(), + $tableName, + ['quote_id', 'reserved_order_id'] + ); + $connection->query($updateSql); + } +} diff --git a/etc/di.xml b/etc/di.xml index 63c9cc4d..c29a0660 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -385,7 +385,7 @@ - Vipps\Payment\Model\ResourceModel\Monitoring\Quote\GridCollection + Vipps\Payment\Model\ResourceModel\Quote\GridCollection diff --git a/etc/extension_attributes.xml b/etc/extension_attributes.xml index 73ee8c42..962b894e 100644 --- a/etc/extension_attributes.xml +++ b/etc/extension_attributes.xml @@ -17,7 +17,7 @@ - + diff --git a/view/adminhtml/templates/monitoring/view.phtml b/view/adminhtml/templates/monitoring/view.phtml index 215c75bc..43a29615 100644 --- a/view/adminhtml/templates/monitoring/view.phtml +++ b/view/adminhtml/templates/monitoring/view.phtml @@ -16,7 +16,7 @@ */ /** @var \Vipps\Payment\Block\Monitoring\View $block */ -$monitoringQuote = $block->getMonitoringQuote(); +$monitoringQuote = $block->getVippsQuote(); ?>
From 23d3d504c8e5de6eda670974018462845ab25bd3 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Tue, 29 Jan 2019 12:46:12 +0200 Subject: [PATCH 083/121] VIPPS-189: Adjustments. Remove unused code. --- Cron/CancelQuoteByAttempts.php | 3 --- Model/Quote/CancelFacade.php | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index 3b177491..12c27c9d 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -103,7 +103,6 @@ class CancelQuoteByAttempts /** * FetchOrderFromVipps constructor. * - * @param CollectionFactory $quoteCollectionFactory * @param CommandManagerInterface $commandManager * @param TransactionBuilder $transactionBuilder * @param LoggerInterface $logger @@ -116,7 +115,6 @@ class CancelQuoteByAttempts * @param CartRepositoryInterface $cartRepository */ public function __construct( - CollectionFactory $quoteCollectionFactory, CommandManagerInterface $commandManager, TransactionBuilder $transactionBuilder, LoggerInterface $logger, @@ -128,7 +126,6 @@ public function __construct( VippsQuoteCollectionFactory $vippsQuoteCollectionFactory, CartRepositoryInterface $cartRepository ) { - $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; $this->transactionBuilder = $transactionBuilder; $this->logger = $logger; diff --git a/Model/Quote/CancelFacade.php b/Model/Quote/CancelFacade.php index 6cfe717b..f9c298b6 100644 --- a/Model/Quote/CancelFacade.php +++ b/Model/Quote/CancelFacade.php @@ -75,7 +75,7 @@ public function __construct( * @param QuoteInterface $vippsQuote * @param string $type * @param string $reason - * @param Transaction|null $transaction + * @param Transaction|null $transaction Order status transaction. * @throws \Magento\Framework\Exception\CouldNotSaveException */ public function cancel( From 784a2f3c66c195b5ee911c04123277b18b6695f3 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Tue, 29 Jan 2019 14:00:03 +0200 Subject: [PATCH 084/121] VIPPS-179. Render cancellation type, reason. Create view ui component. --- Block/Monitoring/View.php | 2 +- Cron/CancelQuoteByAttempts.php | 2 +- .../{Profiling => }/Column/ShowAction.php | 10 ++-- .../adminhtml/templates/monitoring/view.phtml | 47 +++++++++++++------ .../ui_component/vipps_monitoring.xml | 12 ++--- .../ui_component/vipps_profiling.xml | 3 +- 6 files changed, 50 insertions(+), 26 deletions(-) rename Ui/Component/{Profiling => }/Column/ShowAction.php (90%) diff --git a/Block/Monitoring/View.php b/Block/Monitoring/View.php index 48ba9c78..df5f919c 100644 --- a/Block/Monitoring/View.php +++ b/Block/Monitoring/View.php @@ -87,7 +87,7 @@ public function getVippsQuote() { if (!$this->vippsQuote) { try { - $this->vippsQuote = $this->vippsQuoteRepository->load($this->getRequest()->getParam('id')); + $this->vippsQuote = $this->vippsQuoteRepository->load($this->getRequest()->getParam('entity_id')); } catch (\Exception $e) { // Display this error in template. } diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index 12c27c9d..44c997a4 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -221,7 +221,7 @@ private function processQuote(QuoteInterface $vippsQuote) ->cancel( $vippsQuote, QuoteCancellationInterface::CANCEL_TYPE_MAGENTO, - __('Number of attempts reached: %1', $this->cancellationConfig->getAttemptsMaxCount()), + __('Max number of attempts reached (%1)', $this->cancellationConfig->getAttemptsMaxCount()), $quote, $transaction ); diff --git a/Ui/Component/Profiling/Column/ShowAction.php b/Ui/Component/Column/ShowAction.php similarity index 90% rename from Ui/Component/Profiling/Column/ShowAction.php rename to Ui/Component/Column/ShowAction.php index a7c61456..e55418ee 100644 --- a/Ui/Component/Profiling/Column/ShowAction.php +++ b/Ui/Component/Column/ShowAction.php @@ -13,7 +13,8 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -namespace Vipps\Payment\Ui\Component\Profiling\Column; + +namespace Vipps\Payment\Ui\Component\Column; use Magento\Framework\UrlInterface; use Magento\Framework\View\Element\UiComponent\ContextInterface; @@ -60,12 +61,15 @@ public function __construct( public function prepareDataSource(array $dataSource) { if (isset($dataSource['data']['items'])) { + + $path = $this->getData('config/urlPath') ?? '#'; + foreach ($dataSource['data']['items'] as & $item) { if (isset($item['entity_id'])) { $item[$this->getData('name')] = [ 'edit' => [ - 'href' => $this->urlBuilder - ->getUrl('vipps/profiling/view', ['entity_id' => $item['entity_id']]), + 'href' => $this->urlBuilder + ->getUrl($path, ['entity_id' => $item['entity_id']]), 'label' => __('Show') ] ]; diff --git a/view/adminhtml/templates/monitoring/view.phtml b/view/adminhtml/templates/monitoring/view.phtml index 43a29615..5af8f5e6 100644 --- a/view/adminhtml/templates/monitoring/view.phtml +++ b/view/adminhtml/templates/monitoring/view.phtml @@ -16,39 +16,54 @@ */ /** @var \Vipps\Payment\Block\Monitoring\View $block */ -$monitoringQuote = $block->getVippsQuote(); +$vippsQuote = $block->getVippsQuote(); ?>
- +
- - + + - - + + - + + echo $vippsQuote->getAttempts(); ?> - - + + + isCanceled()): ?> + + + + + + + + + - + - +
getQuoteId(); ?>getEntityId(); ?>
getReservedOrderId(); ?>getReservedOrderId(); ?>
getAttempts(); ?>
isCanceled() ? 'Yes' : 'No'; ?>isCanceled() ? 'Yes' : 'No'; ?> +
getCancelType(); ?> +
getCancelReason(); ?> +
getCreatedAt(); ?>getCreatedAt(); ?>
getUpdatedAt(); ?>getUpdatedAt(); ?>
@@ -63,6 +78,10 @@ $monitoringQuote = $block->getVippsQuote(); getQuote(); ?> + + + + @@ -72,7 +91,7 @@ $monitoringQuote = $block->getVippsQuote(); - + diff --git a/view/adminhtml/ui_component/vipps_monitoring.xml b/view/adminhtml/ui_component/vipps_monitoring.xml index f2c2050b..ad9f100d 100644 --- a/view/adminhtml/ui_component/vipps_monitoring.xml +++ b/view/adminhtml/ui_component/vipps_monitoring.xml @@ -56,12 +56,12 @@ - + textRange desc - Quote ID + Vipps Quote ID 20 @@ -78,7 +78,7 @@ - textRange + Attempts Count 40 @@ -109,17 +109,17 @@ dateRange date Magento_Ui/js/grid/columns/date - Last Updated At + Updated At 110 - + entity_id 200 - vipps/monitoring/view + vipps/monitoring/view diff --git a/view/adminhtml/ui_component/vipps_profiling.xml b/view/adminhtml/ui_component/vipps_profiling.xml index 2ad0a486..e06b82b1 100644 --- a/view/adminhtml/ui_component/vipps_profiling.xml +++ b/view/adminhtml/ui_component/vipps_profiling.xml @@ -124,10 +124,11 @@ - + entity_id + vipps/profiling/view 200 From 0173db1502c62b8ce32312c8177fbb2e67058792 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 31 Jan 2019 13:07:39 +0200 Subject: [PATCH 085/121] VIPPS-180: Vipps actions to restart transaction. Add Quote statuses. Show attempts list. Reorganize cancelation. --- Api/Data/QuoteCancellationInterface.php | 97 ------------- Api/Data/QuoteInterface.php | 2 +- .../Data/QuoteStatusInterface.php | 47 ++++--- Api/Quote/CancelFacadeInterface.php | 8 +- Block/Monitoring/Buttons.php | 92 +++++++++++++ Block/Monitoring/View.php | 107 +++++++++----- Controller/Adminhtml/Monitoring/Cancel.php | 87 ++++++++++++ Controller/Adminhtml/Monitoring/Restart.php | 88 ++++++++++++ Controller/Adminhtml/Monitoring/View.php | 33 ++++- Cron/CancelQuoteByAttempts.php | 48 +++++-- Cron/FetchOrderFromVipps.php | 49 +++---- Model/Order/Cancellation/Config.php | 11 +- Model/Quote.php | 50 ++----- Model/Quote/AttemptRepository.php | 26 +++- Model/Quote/CancelFacade.php | 130 ++---------------- Model/Quote/Command/ManualCancel.php | 85 ++++++++++++ Model/Quote/Command/ManualCancelFactory.php | 35 +++++ Model/Quote/Command/Restart.php | 59 ++++++++ Model/Quote/Command/RestartFactory.php | 35 +++++ .../Quote/Attempt/Collection.php | 42 ++++++ Setup/UpgradeSchema.php | 41 ++---- Ui/Component/Column/Status.php | 70 ++++++++++ etc/config.xml | 2 +- etc/crontab.xml | 2 +- .../layout/vipps_monitoring_view.xml | 4 +- .../templates/monitoring/buttons.phtml | 78 +++++++++++ .../adminhtml/templates/monitoring/view.phtml | 88 ++++++------ .../ui_component/vipps_monitoring.xml | 20 +-- 28 files changed, 998 insertions(+), 438 deletions(-) delete mode 100644 Api/Data/QuoteCancellationInterface.php rename view/adminhtml/templates/monitoring/back_button.phtml => Api/Data/QuoteStatusInterface.php (61%) create mode 100644 Block/Monitoring/Buttons.php create mode 100644 Controller/Adminhtml/Monitoring/Cancel.php create mode 100644 Controller/Adminhtml/Monitoring/Restart.php create mode 100644 Model/Quote/Command/ManualCancel.php create mode 100644 Model/Quote/Command/ManualCancelFactory.php create mode 100644 Model/Quote/Command/Restart.php create mode 100644 Model/Quote/Command/RestartFactory.php create mode 100644 Model/ResourceModel/Quote/Attempt/Collection.php create mode 100644 Ui/Component/Column/Status.php create mode 100644 view/adminhtml/templates/monitoring/buttons.phtml diff --git a/Api/Data/QuoteCancellationInterface.php b/Api/Data/QuoteCancellationInterface.php deleted file mode 100644 index b59ae56c..00000000 --- a/Api/Data/QuoteCancellationInterface.php +++ /dev/null @@ -1,97 +0,0 @@ - -
-
-
- -
-
-
+namespace Vipps\Payment\Api\Data; + +/** + * Interface QuoteInterface + * @api + */ +interface QuoteStatusInterface +{ + /** + * @const string(10) + */ + const FIELD_STATUS = 'status'; + + const STATUS_NEW = 'new'; + const STATUS_PLACED = 'placed'; + const STATUS_PLACE_FAILED = 'place_failed'; + const STATUS_CANCELED = 'canceled'; + const STATUS_CANCEL_FAILED = 'cancel_failed'; + + /** + * @return string + */ + public function getStatus(); + + /** + * @param string $status + * @return self + */ + public function setStatus(string $status); +} diff --git a/Api/Quote/CancelFacadeInterface.php b/Api/Quote/CancelFacadeInterface.php index 8aa186ff..e20a8545 100644 --- a/Api/Quote/CancelFacadeInterface.php +++ b/Api/Quote/CancelFacadeInterface.php @@ -31,16 +31,10 @@ interface CancelFacadeInterface * Cancel Vipps payment transaction both, Magento and Vipps. * * @param QuoteInterface $vippsQuote - * @param string $type - * @param string $reason * @param CartInterface|null $quote - * @param Transaction|null $transaction */ public function cancel( QuoteInterface $vippsQuote, - string $type, - string $reason, - CartInterface $quote = null, - Transaction $transaction = null + CartInterface $quote ); } diff --git a/Block/Monitoring/Buttons.php b/Block/Monitoring/Buttons.php new file mode 100644 index 00000000..63dc79e4 --- /dev/null +++ b/Block/Monitoring/Buttons.php @@ -0,0 +1,92 @@ +restartFactory = $restartFactory; + $this->registry = $registry; + $this->cancelFactory = $cancelFactory; + } + + /** + * @return bool + */ + public function isRestartVisible() + { + $restart = $this->restartFactory->create($this->getVippsQuote()); + + return $restart->isAllowed(); + } + + /** + * @return \Vipps\Payment\Api\Data\QuoteInterface + */ + public function getVippsQuote() + { + return $this->registry->registry('vipps_quote'); + } + + /** + * @return bool + */ + public function isCancelVisible() + { + $cancel = $this->cancelFactory->create($this->getVippsQuote()); + + return $cancel->isAllowed(); + } +} diff --git a/Block/Monitoring/View.php b/Block/Monitoring/View.php index df5f919c..c6a4a048 100644 --- a/Block/Monitoring/View.php +++ b/Block/Monitoring/View.php @@ -17,9 +17,13 @@ namespace Vipps\Payment\Block\Monitoring; +use Magento\Framework\Pricing\Helper\Data; +use Magento\Framework\Registry; use Magento\Framework\View\Element\Template; use Magento\Quote\Api\CartRepositoryInterface; -use Vipps\Payment\Model\QuoteRepository as vippsQuoteRepository; +use Vipps\Payment\Model\Quote\AttemptRepository; +use Vipps\Payment\Model\QuoteRepository as VippsQuoteRepository; +use Vipps\Payment\Ui\Component\Column\Status; /** * View Quote Monitoring entity. @@ -27,40 +31,74 @@ class View extends Template { /** - * @var vippsQuoteRepository + * @var VippsQuoteRepository */ private $vippsQuoteRepository; - /** - * @var - */ - private $vippsQuote; + /** * @var CartRepositoryInterface */ private $quoteRepository; - private $quote; /** - * @var string|null + * @var string + */ + private $quoteLoadingError = ''; + + /** + * @var Registry + */ + private $registry; + + /** + * @var Data + */ + private $priceHelper; + + /** + * @var AttemptRepository */ - private $quoteLoadingError = null; + private $attemptRepository; + /** + * @var Status + */ + private $status; /** * View constructor. - * @param vippsQuoteRepository $monitoringQuoteRepository + * @param VippsQuoteRepository $vippsQuoteRepository * @param CartRepositoryInterface $quoteRepository + * @param AttemptRepository $attemptRepository + * @param Registry $registry + * @param Data $priceHelper * @param Template\Context $context * @param array $data */ public function __construct( - vippsQuoteRepository $monitoringQuoteRepository, + VippsQuoteRepository $vippsQuoteRepository, CartRepositoryInterface $quoteRepository, + AttemptRepository $attemptRepository, + Status $status, + Registry $registry, + Data $priceHelper, Template\Context $context, array $data = [] ) { parent::__construct($context, $data); - $this->vippsQuoteRepository = $monitoringQuoteRepository; + $this->vippsQuoteRepository = $vippsQuoteRepository; $this->quoteRepository = $quoteRepository; + $this->registry = $registry; + $this->priceHelper = $priceHelper; + $this->attemptRepository = $attemptRepository; + $this->status = $status; + } + + /** + * @return Data + */ + public function getPriceHelper() + { + return $this->priceHelper; } /** @@ -68,41 +106,48 @@ public function __construct( */ public function getQuote() { - if (!$this->quote) { - try { - $this->quote = $this->quoteRepository->get($this->getVippsQuote()->getQuoteId()); - } catch (\Exception $e) { - $this->quoteLoadingError = $e->getMessage(); - } + try { + return $this->quoteRepository->get($this->getVippsQuote()->getQuoteId()); + } catch (\Exception $e) { + $this->quoteLoadingError = $e->getMessage(); } - - return $this->quote; } /** * @return \Vipps\Payment\Api\Data\QuoteInterface - * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function getVippsQuote() { - if (!$this->vippsQuote) { - try { - $this->vippsQuote = $this->vippsQuoteRepository->load($this->getRequest()->getParam('entity_id')); - } catch (\Exception $e) { - // Display this error in template. - } - } - - return $this->vippsQuote; + return $this->registry->registry('vipps_quote'); } /** * Quote loading error. * - * @return string|null + * @return string */ public function getQuoteLoadingError() { return $this->quoteLoadingError; } + + /** + * @return \Vipps\Payment\Model\ResourceModel\Quote\Attempt\Collection + */ + public function getAttempts() + { + return $this + ->attemptRepository + ->getByVippsQuote($this->getVippsQuote()) + ->load(); + } + + /** + * @param string $code + * @return string + */ + public function getStatusLabel($code) + { + return $this->status->getLabel($code); + } } diff --git a/Controller/Adminhtml/Monitoring/Cancel.php b/Controller/Adminhtml/Monitoring/Cancel.php new file mode 100644 index 00000000..8e254067 --- /dev/null +++ b/Controller/Adminhtml/Monitoring/Cancel.php @@ -0,0 +1,87 @@ +quoteRepository = $quoteRepository; + $this->manualCancelFactory = $manualCancelFactory; + } + + /** + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + try { + $this + ->getManualCancelCommand() + ->execute(); + } catch (\Throwable $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } + + return $this + ->resultRedirectFactory + ->create() + ->setUrl($this->_redirect->getRefererUrl()); + } + + /** + * @return \Vipps\Payment\Model\Quote\Command\ManualCancel + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getManualCancelCommand() + { + $vippsQuote = $this + ->quoteRepository + ->load($this->getRequest()->getParam('entity_id')); + + return $this->manualCancelFactory->create($vippsQuote); + } +} diff --git a/Controller/Adminhtml/Monitoring/Restart.php b/Controller/Adminhtml/Monitoring/Restart.php new file mode 100644 index 00000000..00936d40 --- /dev/null +++ b/Controller/Adminhtml/Monitoring/Restart.php @@ -0,0 +1,88 @@ +quoteRepository = $quoteRepository; + $this->restartFactory = $restartFactory; + } + + /** + * @return ResponseInterface|ResultInterface|Page + */ + public function execute() + { + try { + $this + ->getRestart() + ->execute(); + } catch (\Throwable $e) { + $this->messageManager->addErrorMessage($e->getMessage()); + } + + return $this + ->resultRedirectFactory + ->create() + ->setUrl($this->_redirect->getRefererUrl()); + } + + /** + * @return \Vipps\Payment\Model\Quote\Command\Restart + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getRestart() + { + $vippsQuote = $this + ->quoteRepository + ->load($this->getRequest()->getParam('entity_id')); + + return $this->restartFactory->create($vippsQuote); + } +} diff --git a/Controller/Adminhtml/Monitoring/View.php b/Controller/Adminhtml/Monitoring/View.php index 31993e1a..becceea4 100644 --- a/Controller/Adminhtml/Monitoring/View.php +++ b/Controller/Adminhtml/Monitoring/View.php @@ -18,7 +18,12 @@ namespace Vipps\Payment\Controller\Adminhtml\Monitoring; use Magento\Backend\{App\Action, App\Action\Context}; -use Magento\Framework\{App\ResponseInterface, Controller\ResultInterface, View\Result\Page, View\Result\PageFactory}; +use Magento\Framework\{App\ResponseInterface, + Controller\ResultInterface, + Registry, + View\Result\Page, + View\Result\PageFactory}; +use Vipps\Payment\Model\QuoteRepository as VippsQuoteRepository; /** * Class View @@ -29,19 +34,33 @@ class View extends Action * @var PageFactory */ private $resultPageFactory; + /** + * @var VippsQuoteRepository + */ + private $quoteRepository; + /** + * @var Registry + */ + private $registry; /** * View constructor. * + * @param VippsQuoteRepository $quoteRepository + * @param Registry $registry * @param Context $context * @param PageFactory $resultPageFactory */ public function __construct( + VippsQuoteRepository $quoteRepository, + Registry $registry, Context $context, PageFactory $resultPageFactory ) { parent::__construct($context); $this->resultPageFactory = $resultPageFactory; + $this->quoteRepository = $quoteRepository; + $this->registry = $registry; } /** @@ -49,6 +68,18 @@ public function __construct( */ public function execute() { + try { + $vippsQuote = $this->quoteRepository->load($this->getRequest()->getParam('entity_id')); + $this->registry->register('vipps_quote', $vippsQuote); + } catch (\Throwable $e) { + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $this->messageManager->addErrorMessage($e->getMessage()); + $resultRedirect = $this->resultRedirectFactory->create(); + $resultRedirect->setPath('*/*'); + + return $resultRedirect; + } + $resultPage = $this->resultPageFactory->create(); $resultPage->setActiveMenu('Vipps_Payment::vipps_monitoring'); return $resultPage; diff --git a/Cron/CancelQuoteByAttempts.php b/Cron/CancelQuoteByAttempts.php index 44c997a4..61e1ead5 100644 --- a/Cron/CancelQuoteByAttempts.php +++ b/Cron/CancelQuoteByAttempts.php @@ -24,8 +24,8 @@ use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, - Api\Data\QuoteCancellationInterface, Api\Data\QuoteInterface, + Api\Data\QuoteStatusInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, @@ -33,6 +33,7 @@ Model\Quote\CancelFacade, Model\ResourceModel\Quote\Collection as VippsQuoteCollection, Model\ResourceModel\Quote\CollectionFactory as VippsQuoteCollectionFactory}; +use Vipps\Payment\Model\Quote\AttemptManagement; use Vipps\Payment\Model\QuoteManagement as QuoteMonitorManagement; /** @@ -99,6 +100,14 @@ class CancelQuoteByAttempts * @var CartRepositoryInterface */ private $cartRepository; + /** + * @var AttemptManagement + */ + private $attemptManagement; + /** + * @var AttemptRepository + */ + private $attemptRepository; /** * FetchOrderFromVipps constructor. @@ -124,7 +133,8 @@ public function __construct( Config $cancellationConfig, CancelFacade $cancellationFacade, VippsQuoteCollectionFactory $vippsQuoteCollectionFactory, - CartRepositoryInterface $cartRepository + CartRepositoryInterface $cartRepository, + AttemptManagement $attemptManagement ) { $this->commandManager = $commandManager; $this->transactionBuilder = $transactionBuilder; @@ -136,6 +146,7 @@ public function __construct( $this->cancellationFacade = $cancellationFacade; $this->vippsQuoteCollectionFactory = $vippsQuoteCollectionFactory; $this->cartRepository = $cartRepository; + $this->attemptManagement = $attemptManagement; } /** @@ -189,8 +200,8 @@ private function createCollection($currentPage) ['gteq' => $this->cancellationConfig->getAttemptsMaxCount()] ); - // Filter not cancelled quotes. - $collection->addFieldToFilter('is_canceled', ['neq' => 1]); + // Filter processing cancelled quotes. + $collection->addFieldToFilter(QuoteStatusInterface::FIELD_STATUS, ['eq' => QuoteStatusInterface::STATUS_NEW]); return $collection; } @@ -210,21 +221,28 @@ private function processQuote(QuoteInterface $vippsQuote) try { $quote = $this->cartRepository->get($vippsQuote->getQuoteId()); + $attempt = $this->attemptManagement->createAttempt($vippsQuote); + $this->prepareEnv($quote); - $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); + $attempt + ->setMessage(__( + 'Max number of attempts reached (%1)', + $this->cancellationConfig->getAttemptsMaxCount() + )); + + if ($this->cancellationConfig->isAutomatic($quote->getStoreId())) { + $this + ->cancellationFacade + ->cancel($vippsQuote, $quote); + } } catch (\Throwable $e) { $this->logger->critical($e->getMessage(), ['quote_id' => $vippsQuote->getId()]); - } finally { - $this - ->cancellationFacade - ->cancel( - $vippsQuote, - QuoteCancellationInterface::CANCEL_TYPE_MAGENTO, - __('Max number of attempts reached (%1)', $this->cancellationConfig->getAttemptsMaxCount()), - $quote, - $transaction - ); + + if (isset($attempt)) { + $attempt->setMessage($e->getMessage()); + $this->attemptManagement->save($attempt); + } } } diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 9f0f013b..398d4cd3 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -25,7 +25,7 @@ use Magento\Store\Model\StoreManagerInterface; use Psr\Log\LoggerInterface; use Vipps\Payment\{Api\CommandManagerInterface, - Api\Data\QuoteCancellationInterface, + Api\Data\QuoteStatusInterface, Gateway\Exception\VippsException, Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, @@ -33,8 +33,7 @@ Model\OrderPlace, Model\Quote as VippsQuote, Model\Quote\AttemptManagement, - Model\Quote\CancelFacade, - Model\QuoteManagement as QuoteMonitorManagement, + Model\QuoteRepository as VippsQuoteRepository, Model\ResourceModel\Quote\Collection as VippsQuoteCollection, Model\ResourceModel\Quote\CollectionFactory as VippsQuoteCollectionFactory}; use Vipps\Payment\Gateway\Exception\WrongAmountException; @@ -100,23 +99,27 @@ class FetchOrderFromVipps * @var AttemptManagement */ private $attemptManagement; - /** - * @var CancelFacade - */ - private $cancellationFacade; + /** * @var VippsQuoteCollectionFactory */ private $vippsQuoteCollectionFactory; + /** * @var QuoteRepository */ private $quoteRepository; + /** + * @var VippsQuoteRepository + */ + private $vippsQuoteRepository; + /** * FetchOrderFromVipps constructor. * @param CollectionFactory $quoteCollectionFactory * @param VippsQuoteCollectionFactory $vippsQuoteCollectionFactory + * @param VippsQuoteRepository $vippsQuoteRepository * @param QuoteRepository $quoteRepository * @param CommandManagerInterface $commandManager * @param TransactionBuilder $transactionBuilder @@ -124,14 +127,13 @@ class FetchOrderFromVipps * @param LoggerInterface $logger * @param StoreManagerInterface $storeManager * @param ScopeCodeResolver $scopeCodeResolver - * @param QuoteMonitorManagement $quoteManagement * @param Config $cancellationConfig * @param AttemptManagement $attemptManagement - * @param CancelFacade $cancellationFacade */ public function __construct( CollectionFactory $quoteCollectionFactory, VippsQuoteCollectionFactory $vippsQuoteCollectionFactory, + VippsQuoteRepository $vippsQuoteRepository, QuoteRepository $quoteRepository, CommandManagerInterface $commandManager, TransactionBuilder $transactionBuilder, @@ -140,8 +142,7 @@ public function __construct( StoreManagerInterface $storeManager, ScopeCodeResolver $scopeCodeResolver, Config $cancellationConfig, - AttemptManagement $attemptManagement, - CancelFacade $cancellationFacade + AttemptManagement $attemptManagement ) { $this->quoteCollectionFactory = $quoteCollectionFactory; $this->commandManager = $commandManager; @@ -152,9 +153,9 @@ public function __construct( $this->scopeCodeResolver = $scopeCodeResolver; $this->cancellationConfig = $cancellationConfig; $this->attemptManagement = $attemptManagement; - $this->cancellationFacade = $cancellationFacade; $this->vippsQuoteCollectionFactory = $vippsQuoteCollectionFactory; $this->quoteRepository = $quoteRepository; + $this->vippsQuoteRepository = $vippsQuoteRepository; } /** @@ -206,7 +207,10 @@ private function createCollection($currentPage) ['null' => 1] ] ) - ->addFieldToFilter('is_canceled', ['neq' => 1]); // Filter not cancelled quotes. + ->addFieldToFilter( + QuoteStatusInterface::FIELD_STATUS, + ['in' => [QuoteStatusInterface::STATUS_NEW, QuoteStatusInterface::STATUS_PLACE_FAILED]] + ); // Filter new and place failed quotes. return $collection; } @@ -228,17 +232,10 @@ private function processQuote(VippsQuote $vippsQuote) $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); if ($transaction->isTransactionAborted()) { - $attempt->setMessage('Transaction was cancelled on Vipps side'); - // Create cancellation if transaction was aborted on Vipps side. - $this - ->cancellationFacade - ->cancel( - $vippsQuote, - QuoteCancellationInterface::CANCEL_TYPE_VIPPS, - 'Transaction was cancelled on Vipps side', - $quote, - $transaction - ); + $transactionMessage = 'Transaction was cancelled on Vipps side'; + $attempt->setMessage($transactionMessage); + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCELED); + $this->vippsQuoteRepository->save($vippsQuote); } else { $this->placeOrder($quote, $transaction); } @@ -248,6 +245,9 @@ private function processQuote(VippsQuote $vippsQuote) $attempt->setMessage($e->getMessage()); } } finally { + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACE_FAILED); + $this->vippsQuoteRepository->save($vippsQuote); + if (isset($attempt)) { // Simply save the attempt. $this->attemptManagement->save($attempt); @@ -313,6 +313,7 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) * @param \DateInterval $interval * * @return bool + * @throws \Exception */ private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine { diff --git a/Model/Order/Cancellation/Config.php b/Model/Order/Cancellation/Config.php index 13256310..c65433bf 100644 --- a/Model/Order/Cancellation/Config.php +++ b/Model/Order/Cancellation/Config.php @@ -58,11 +58,20 @@ public function __construct(ScopeConfigInterface $scopeConfig) * @param int|null $storeId * @return bool */ - public function isTypeAutomatic($storeId = null) + public function isAutomatic($storeId = null) { return $this->getType($storeId) === Type::AUTOMATIC; } + /** + * @param int|null $storeId + * @return bool + */ + public function isManual($storeId = null) + { + return $this->getType($storeId) === Type::MANUAL; + } + /** * Cancellation type code. * diff --git a/Model/Quote.php b/Model/Quote.php index 1dde76f8..29e0d9c3 100644 --- a/Model/Quote.php +++ b/Model/Quote.php @@ -130,61 +130,29 @@ public function getAttempts() } /** - * @return bool - */ - public function isCanceled() - { - return $this->getData(self::IS_CANCELED); - } - - /** - * @param int $isCanceled + * Clear attempts. * @return Quote */ - public function setIsCanceled(int $isCanceled) - { - return $this->setData(self::IS_CANCELED, $isCanceled); - } - - /** - * @return string|null - */ - public function getCancelType() - { - return $this->getData(self::CANCEL_TYPE); - } - - /** - * @return string|null - */ - public function getCancelReason() - { - return $this->getData(self::CANCEL_REASON); - } - - /** - * @param string $reason - */ - public function setCancelReason(string $reason) + public function clearAttempts() { - $this->setData(self::CANCEL_REASON, $reason); + return $this->setAttempts(0); } /** - * @param string $type + * @param string $status * @return Quote */ - public function setCancelType($type) + public function setStatus(string $status) { - return $this->setData(self::CANCEL_TYPE, $type); + return $this->setData(self::FIELD_STATUS, $status); } /** - * Clear attempts. + * @return string */ - public function clearAttempts() + public function getStatus() { - $this->setAttempts(0); + return $this->getData(self::FIELD_STATUS); } /** diff --git a/Model/Quote/AttemptRepository.php b/Model/Quote/AttemptRepository.php index 57125292..b15f170d 100644 --- a/Model/Quote/AttemptRepository.php +++ b/Model/Quote/AttemptRepository.php @@ -18,9 +18,10 @@ namespace Vipps\Payment\Model\Quote; use Magento\Framework\Exception\CouldNotSaveException; -use Vipps\Payment\Api\{Data\QuoteAttemptInterface}; +use Vipps\Payment\Api\{Data\QuoteAttemptInterface, Data\QuoteInterface}; use Vipps\Payment\Api\Quote\AttemptRepositoryInterface; use Vipps\Payment\Model\ResourceModel\Quote\Attempt as AttemptResource; +use Vipps\Payment\Model\ResourceModel\Quote\Attempt\CollectionFactory; /** * Class AttemptRepository @@ -31,13 +32,19 @@ class AttemptRepository implements AttemptRepositoryInterface * @var AttemptResource */ private $resource; + /** + * @var CollectionFactory + */ + private $collectionFactory; /** * @param AttemptResource $resource + * @param CollectionFactory $collectionFactory */ - public function __construct(AttemptResource $resource) + public function __construct(AttemptResource $resource, CollectionFactory $collectionFactory) { $this->resource = $resource; + $this->collectionFactory = $collectionFactory; } /** @@ -61,4 +68,19 @@ public function save(QuoteAttemptInterface $attempt) ); } } + + /** + * @param QuoteInterface $quote + * @return AttemptResource\Collection + */ + public function getByVippsQuote(QuoteInterface $quote) + { + /** @var \Vipps\Payment\Model\ResourceModel\Quote\Attempt\Collection $collection */ + $collection = $this->collectionFactory->create(); + $collection + ->addFieldToFilter('parent_id', ['eq' => $quote->getEntityId()]) + ->setOrder('created_at'); + + return $collection; + } } diff --git a/Model/Quote/CancelFacade.php b/Model/Quote/CancelFacade.php index f9c298b6..dcbf097b 100644 --- a/Model/Quote/CancelFacade.php +++ b/Model/Quote/CancelFacade.php @@ -19,11 +19,9 @@ use Magento\Quote\Api\Data\CartInterface; use Vipps\Payment\{Api\CommandManagerInterface, - Api\Data\QuoteCancellationInterface, Api\Data\QuoteInterface, + Api\Data\QuoteStatusInterface, Api\Quote\CancelFacadeInterface, - Gateway\Transaction\Transaction, - Model\Order\Cancellation\Config, Model\QuoteRepository}; /** @@ -37,144 +35,46 @@ class CancelFacade implements CancelFacadeInterface */ private $commandManager; - /** - * @var Config - */ - private $config; - /** * @var QuoteRepository */ private $quoteRepository; - /** - * @var bool - */ - private $forceVippsCancel = false; - /** * CancellationFacade constructor. * @param CommandManagerInterface $commandManager * @param QuoteRepository $quoteRepository - * @param Config $config */ public function __construct( CommandManagerInterface $commandManager, - QuoteRepository $quoteRepository, - Config $config + QuoteRepository $quoteRepository ) { $this->commandManager = $commandManager; - $this->config = $config; $this->quoteRepository = $quoteRepository; } /** * vipps_monitoring extension attribute requires to be loaded in the quote. * - * @param CartInterface $quote * @param QuoteInterface $vippsQuote - * @param string $type - * @param string $reason - * @param Transaction|null $transaction Order status transaction. + * @param CartInterface $quote * @throws \Magento\Framework\Exception\CouldNotSaveException + * @throws \Throwable */ public function cancel( QuoteInterface $vippsQuote, - string $type, - string $reason, - CartInterface $quote = null, - Transaction $transaction = null + CartInterface $quote ) { - if ($this->isAllowedToCancelMagento($vippsQuote)) { - $this->cancelMagento($vippsQuote, $type, $reason); - } - - if ($this->isAllowedToCancelVipps($type, $quote, $transaction)) { - $this->cancelVipps(QuoteCancellationInterface::CANCEL_TYPE_ALL, $quote, $vippsQuote); + try { + // cancel order on vipps side + $this->commandManager->cancel($quote->getPayment()); + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCELED); + } catch (\Throwable $exception) { + // Log the exception + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCEL_FAILED); + throw $exception; + } finally { + $this->quoteRepository->save($vippsQuote); } } - - /** - * @param QuoteInterface $vippsQuote - * @return bool - */ - private function isAllowedToCancelMagento(QuoteInterface $vippsQuote) - { - return !$vippsQuote->isCanceled(); - } - - /** - * vipps_monitoring extension attribute requires to be loaded in the quote. - * - * @param QuoteInterface $vippsQuote - * @param string $type - * @param string $reason - * @throws \Magento\Framework\Exception\CouldNotSaveException - */ - private function cancelMagento(QuoteInterface $vippsQuote, string $type, string $reason) - { - $vippsQuote - ->setIsCanceled(QuoteInterface::IS_CANCELED_YES) - ->setCancelType($type) - ->setCancelReason($reason); - - $this->quoteRepository->save($vippsQuote); - } - - /** - * @param $type - * @param CartInterface|null $quote - * @param Transaction|null $transaction - * @return bool - */ - private function isAllowedToCancelVipps($type, CartInterface $quote = null, Transaction $transaction = null) - { - return $type === QuoteCancellationInterface::CANCEL_TYPE_MAGENTO // Initiated by Magento - && $quote // Quote is required for Vipps cancellation - && $this->isAutomaticVippsCancellation($quote->getStoreId()) // Automatic cancel is allowed by configuration - && $transaction // There is transaction in Vipps that can be canceled - && $transaction->isTransactionReserved(); - } - - /** - * @param int|null $storeId - * @return bool - */ - private function isAutomaticVippsCancellation($storeId = null) - { - return $this->isForceVippsCancel() || $this->config->isTypeAutomatic($storeId); - } - - /** - * @return bool - */ - public function isForceVippsCancel(): bool - { - return $this->forceVippsCancel; - } - - /** - * @param CartInterface $quote - * @param QuoteInterface $vippsQuote - * @throws \Magento\Framework\Exception\CouldNotSaveException - */ - private function cancelVipps( - $type, - CartInterface $quote, - QuoteInterface $vippsQuote - ) { - // cancel order on vipps side - $this->commandManager->cancel($quote->getPayment()); - - $vippsQuote->setCancelType($type); - $this->quoteRepository->save($vippsQuote); - } - - /** - * @param bool $automaticVippsCancel - */ - public function setForceAutomaticVippsCancel(bool $automaticVippsCancel) - { - $this->forceVippsCancel = $automaticVippsCancel; - } } diff --git a/Model/Quote/Command/ManualCancel.php b/Model/Quote/Command/ManualCancel.php new file mode 100644 index 00000000..c4c1bccd --- /dev/null +++ b/Model/Quote/Command/ManualCancel.php @@ -0,0 +1,85 @@ +vippsQuote = $vippsQuote; + $this->config = $config; + $this->cartRepository = $cartRepository; + $this->cancelFacade = $cancelFacade; + } + + /** + * Verify is Quote Processing allowed for restart. + * + * @return bool + */ + public function isAllowed() + { + return in_array( + $this->vippsQuote->getStatus(), + [QuoteStatusInterface::STATUS_PLACE_FAILED, QuoteStatusInterface::STATUS_CANCEL_FAILED], + true + ); + } + + /** + * @throws LocalizedException + */ + public function execute() + { + try { + $quote = $this->cartRepository->get($this->vippsQuote->getQuoteId()); + + $this + ->cancelFacade + ->cancel($this->vippsQuote, $quote); + } catch (\Throwable $exception) { + throw new LocalizedException(__('Failed to cancel the order. Please contact support team.')); + } + } +} diff --git a/Model/Quote/Command/ManualCancelFactory.php b/Model/Quote/Command/ManualCancelFactory.php new file mode 100644 index 00000000..a207c32a --- /dev/null +++ b/Model/Quote/Command/ManualCancelFactory.php @@ -0,0 +1,35 @@ +objectManager = $objectManager; + } + + /** + * @param QuoteInterface $vippsQuote + * @return ManualCancel + */ + public function create(QuoteInterface $vippsQuote) + { + return $this->objectManager->create(ManualCancel::class, ['vippsQuote' => $vippsQuote]); + } +} diff --git a/Model/Quote/Command/Restart.php b/Model/Quote/Command/Restart.php new file mode 100644 index 00000000..a549bece --- /dev/null +++ b/Model/Quote/Command/Restart.php @@ -0,0 +1,59 @@ +vippsQuote = $vippsQuote; + $this->quoteRepository = $quoteRepository; + } + + /** + * Verify is Quote Processing allowed for restart. + * + * @return bool + */ + public function isAllowed() + { + return $this->vippsQuote->getStatus() === QuoteStatusInterface::STATUS_PLACE_FAILED; + } + + /** + * Mark Vipps Quote as ready for restart. + * + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function execute() + { + $this + ->vippsQuote + ->clearAttempts() + ->setStatus(QuoteStatusInterface::STATUS_NEW); + + $this->quoteRepository->save($this->vippsQuote); + } +} diff --git a/Model/Quote/Command/RestartFactory.php b/Model/Quote/Command/RestartFactory.php new file mode 100644 index 00000000..5af744ac --- /dev/null +++ b/Model/Quote/Command/RestartFactory.php @@ -0,0 +1,35 @@ +objectManager = $objectManager; + } + + /** + * @param QuoteInterface $vippsQuote + * @return Restart + */ + public function create(QuoteInterface $vippsQuote) + { + return $this->objectManager->create(Restart::class, ['vippsQuote' => $vippsQuote]); + } +} diff --git a/Model/ResourceModel/Quote/Attempt/Collection.php b/Model/ResourceModel/Quote/Attempt/Collection.php new file mode 100644 index 00000000..983d19ca --- /dev/null +++ b/Model/ResourceModel/Quote/Attempt/Collection.php @@ -0,0 +1,42 @@ +_init(AttemptModel::class, AttemptResource::class); + } +} diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index 6b7ee54f..11b17106 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -40,7 +40,7 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con if (version_compare($context->getVersion(), '1.2.0', '<')) { $this->createVippsQuoteTable($installer); $this->createVippsAttemptsTable($installer); - $this->addCancelationToQuote($installer); + $this->addStatusToQuote($installer); } $installer->endSetup(); @@ -180,9 +180,8 @@ private function createVippsAttemptsTable(SchemaSetupInterface $installer) * Create cancellation table. * * @param SchemaSetupInterface $installer - * @throws \Zend_Db_Exception */ - private function addCancelationToQuote(SchemaSetupInterface $installer) + private function addStatusToQuote(SchemaSetupInterface $installer) { $connection = $installer->getConnection(); $tableName = $connection->getTableName('vipps_quote'); @@ -190,38 +189,14 @@ private function addCancelationToQuote(SchemaSetupInterface $installer) $connection ->addColumn( $tableName, - 'is_canceled', - [ - 'type' => Table::TYPE_BOOLEAN, - 'nullable' => true, - 'default' => 0, - 'comment' => 'Is canceled', - 'after' => 'attempts' - ] - ); - - $connection - ->addColumn( - $tableName, - 'cancel_type', - [ - 'type' => Table::TYPE_TEXT, - 'length' => 10, - 'nullable' => true, - 'comment' => 'Cancellation Type', - 'after' => 'is_canceled' - ] - ); - - $connection - ->addColumn( - $tableName, - 'cancel_reason', + 'status', [ 'type' => Table::TYPE_TEXT, - 'nullable' => true, - 'comment' => 'Cancellation Reason', - 'after' => 'cancel_type' + 'length' => 20, + 'nullable' => false, + 'comment' => 'Status', + 'after' => 'reserved_order_id', + 'default' => 'new' ] ); } diff --git a/Ui/Component/Column/Status.php b/Ui/Component/Column/Status.php new file mode 100644 index 00000000..f87961bd --- /dev/null +++ b/Ui/Component/Column/Status.php @@ -0,0 +1,70 @@ + '', 'label' => '
- + - + @@ -38,39 +39,25 @@ $vippsQuote = $block->getVippsQuote(); echo $vippsQuote->getAttempts(); ?> - + - isCanceled()): ?> - - - - - - - - - - - + + - - + +
getId(); ?>
getStoreId(); ?>getReservedOrderId(); ?>
getCustomerEmail(); ?>
getEntityId(); ?>getEntityId(); ?>
getReservedOrderId(); ?>getReservedOrderId(); ?>
isCanceled() ? 'Yes' : 'No'; ?> + $block->getStatusLabel($vippsQuote->getStatus()) ?>
getCancelType(); ?> -
getCancelReason(); ?> -
getCreatedAt(); ?>getCreatedAt(); ?>
getUpdatedAt(); ?>getUpdatedAt(); ?>
- +
getQuoteLoadingError()): ?> @@ -79,44 +66,63 @@ $vippsQuote = $block->getVippsQuote(); - - + + - - + + - - + + - + - + - - + + - - + + - - + + - - + +
getId(); ?>getId(); ?>
getStoreId(); ?>getStoreId(); ?>
getReservedOrderId(); ?>getReservedOrderId(); ?>
getCustomerEmail(); ?>
getCustomerFirstname() . ' ' . $quote->getCustomerLastname(); ?>
getGrandTotal(); ?>getPriceHelper()->currencyByStore($quote->getGrandTotal(), $quote->getStoreId()); ?>
getIsActive() ? 'Yes' : 'No'; ?>getIsActive() ? 'Yes' : 'No'; ?>
getCreatedAt(); ?>getCreatedAt(); ?>
getUpdatedAt(); ?>getUpdatedAt(); ?>
+ + count()): ?> +
+ +
+ + + + + + + + + +
formatDate($attempt->getCreatedAt(), \IntlDateFormatter::LONG, true); ?>getMessage(); ?>
+ +
diff --git a/view/adminhtml/ui_component/vipps_monitoring.xml b/view/adminhtml/ui_component/vipps_monitoring.xml index ad9f100d..890ffa3d 100644 --- a/view/adminhtml/ui_component/vipps_monitoring.xml +++ b/view/adminhtml/ui_component/vipps_monitoring.xml @@ -78,19 +78,23 @@ - + Attempts Count 40 - - - - select - select - - + + + Vipps\Payment\Ui\Component\Column\Status + + select + select + Status + Magento_Ui/js/grid/columns/select + 50 + + From 2a7699187ece774d05aef563cd0ab3f67c526f79 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Fri, 1 Feb 2019 12:13:31 +0200 Subject: [PATCH 086/121] VIPPS-180: Mark quote as placed. --- Controller/Payment/Callback.php | 1 - Cron/FetchOrderFromVipps.php | 7 +- Model/OrderPlace.php | 176 ++++++++++++++++++-------------- Model/QuoteManagement.php | 9 ++ etc/di.xml | 1 + 5 files changed, 117 insertions(+), 77 deletions(-) diff --git a/Controller/Payment/Callback.php b/Controller/Payment/Callback.php index 4196f8d9..dac35ec2 100755 --- a/Controller/Payment/Callback.php +++ b/Controller/Payment/Callback.php @@ -118,7 +118,6 @@ public function execute() $requestData = $this->jsonDecoder->unserialize($this->getRequest()->getContent()); $this->authorize($requestData); - $transaction = $this->transactionBuilder->setData($requestData)->build(); $this->orderPlace->execute($this->getQuote($requestData), $transaction); diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 398d4cd3..83b24b13 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -237,15 +237,18 @@ private function processQuote(VippsQuote $vippsQuote) $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCELED); $this->vippsQuoteRepository->save($vippsQuote); } else { - $this->placeOrder($quote, $transaction); + $order = $this->placeOrder($quote, $transaction); + if ($order) { + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACED); + } } } catch (\Throwable $e) { + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACE_FAILED); $this->logger->critical($e->getMessage(), ['vipps_quote_id' => $vippsQuote->getId()]); if (isset($attempt)) { $attempt->setMessage($e->getMessage()); } } finally { - $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACE_FAILED); $this->vippsQuoteRepository->save($vippsQuote); if (isset($attempt)) { diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index cf6f33e1..f1db4661 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -13,30 +13,25 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ + namespace Vipps\Payment\Model; -use Magento\Framework\Exception\{ - CouldNotSaveException, NoSuchEntityException, AlreadyExistsException, InputException -}; +use Magento\Framework\Exception\{AlreadyExistsException, CouldNotSaveException, InputException, NoSuchEntityException}; use Magento\Framework\Exception\LocalizedException; -use Magento\Payment\Helper\Formatter; use Magento\Payment\Gateway\ConfigInterface; -use Magento\Sales\Api\{ - OrderManagementInterface, Data\OrderInterface, OrderRepositoryInterface -}; -use Magento\Sales\Model\{ - Order, Order\Payment\Transaction as PaymentTransaction, - Order\Payment\Processor, Order\Payment -}; -use Magento\Quote\Api\{ - CartRepositoryInterface, CartManagementInterface, Data\CartInterface -}; +use Magento\Payment\Helper\Formatter; +use Magento\Quote\Api\{CartManagementInterface, CartRepositoryInterface, Data\CartInterface}; use Magento\Quote\Model\Quote; +use Magento\Sales\Api\{Data\OrderInterface, OrderManagementInterface, OrderRepositoryInterface}; +use Magento\Sales\Model\{Order, + Order\Payment, + Order\Payment\Processor, + Order\Payment\Transaction as PaymentTransaction}; +use Psr\Log\LoggerInterface; use Vipps\Payment\Api\CommandManagerInterface; +use Vipps\Payment\Api\Data\QuoteStatusInterface; +use Vipps\Payment\Gateway\{Exception\VippsException, Transaction\Transaction}; use Vipps\Payment\Gateway\Exception\WrongAmountException; -use Vipps\Payment\Gateway\{ - Transaction\Transaction, Exception\VippsException -}; use Vipps\Payment\Model\Adminhtml\Source\PaymentAction; /** @@ -102,6 +97,14 @@ class OrderPlace * @var CommandManagerInterface */ private $commandManager; + /** + * @var QuoteManagement + */ + private $quoteManagement; + /** + * @var LoggerInterface + */ + private $logger; /** * OrderPlace constructor. @@ -117,6 +120,8 @@ class OrderPlace * @param LockManager $lockManager * @param ConfigInterface $config * @param CommandManagerInterface $commandManager + * @param QuoteManagement $quoteManagement + * @param LoggerInterface $logger * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -130,7 +135,9 @@ public function __construct( QuoteUpdater $quoteUpdater, LockManager $lockManager, ConfigInterface $config, - CommandManagerInterface $commandManager + CommandManagerInterface $commandManager, + QuoteManagement $quoteManagement, + LoggerInterface $logger ) { $this->orderRepository = $orderRepository; $this->cartRepository = $cartRepository; @@ -143,6 +150,8 @@ public function __construct( $this->lockManager = $lockManager; $this->config = $config; $this->commandManager = $commandManager; + $this->quoteManagement = $quoteManagement; + $this->logger = $logger; } /** @@ -171,7 +180,9 @@ public function execute(CartInterface $quote, Transaction $transaction) try { $order = $this->placeOrder($quote, $transaction); + if ($order) { + $this->updateVippsQuote($quote); $paymentAction = $this->config->getValue('vipps_payment_action'); switch ($paymentAction) { case PaymentAction::ACTION_AUTHORIZE_CAPTURE: @@ -188,6 +199,18 @@ public function execute(CartInterface $quote, Transaction $transaction) } } + /** + * Check can we place order or not based on transaction object + * + * @param Transaction $transaction + * + * @return bool + */ + private function canPlaceOrder(Transaction $transaction) + { + return $transaction->isTransactionReserved(); + } + /** * @param CartInterface $quote * @@ -207,29 +230,6 @@ private function acquireLock(CartInterface $quote) return false; } - /** - * @param $lockName - * - * @return bool - * @throws InputException - */ - private function releaseLock($lockName) - { - return $this->lockManager->unlock($lockName); - } - - /** - * Check can we place order or not based on transaction object - * - * @param Transaction $transaction - * - * @return bool - */ - private function canPlaceOrder(Transaction $transaction) - { - return $transaction->isTransactionReserved(); - } - /** * @param CartInterface $quote * @param Transaction $transaction @@ -294,38 +294,36 @@ private function prepareQuote($quote) } /** - * Authorize action + * Check if reserved Order amount in vipps is the same as in Magento. * - * @param OrderInterface $order + * @param CartInterface $quote * @param Transaction $transaction + * + * @return bool */ - private function authorize(OrderInterface $order, Transaction $transaction) + private function validateAmount(CartInterface $quote, Transaction $transaction) { - if ($order->getState() !== Order::STATE_NEW) { - return; - } - - /** @var Payment $payment */ - $payment = $order->getPayment(); - $transactionId = $transaction->getTransactionId(); - $payment->setTransactionId($transactionId); - $payment->setIsTransactionClosed(false); - $payment->setTransactionAdditionalInfo( - PaymentTransaction::RAW_DETAILS, - $transaction->getTransactionInfo()->getData() - ); - - // preconditions - $totalDue = $order->getTotalDue(); - $baseTotalDue = $order->getBaseTotalDue(); + $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); + $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); - // do authorize - $this->processor->authorize($payment, false, $baseTotalDue); - // base amount will be set inside - $payment->setAmountAuthorized($totalDue); - $this->orderRepository->save($order); + return $quoteAmount == $vippsAmount; + } - $this->notify($order); + /** + * Update vipps quote with success. + * + * @param CartInterface $cart + */ + private function updateVippsQuote(CartInterface $cart) + { + try { + $vippsQuote = $this->quoteManagement->getByQuote($cart); + $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACED); + $this->quoteManagement->save($vippsQuote); + } catch (\Throwable $e) { + // Order is submitted but failed to update Vipps Quote. It should not affect order flow. + $this->logger->error($e->getMessage()); + } } /** @@ -378,18 +376,48 @@ private function notify($order) } /** - * Check if reserved Order amount in vipps is the same as in Magento. + * Authorize action * - * @param CartInterface $quote + * @param OrderInterface $order * @param Transaction $transaction + */ + private function authorize(OrderInterface $order, Transaction $transaction) + { + if ($order->getState() !== Order::STATE_NEW) { + return; + } + + /** @var Payment $payment */ + $payment = $order->getPayment(); + $transactionId = $transaction->getTransactionId(); + $payment->setTransactionId($transactionId); + $payment->setIsTransactionClosed(false); + $payment->setTransactionAdditionalInfo( + PaymentTransaction::RAW_DETAILS, + $transaction->getTransactionInfo()->getData() + ); + + // preconditions + $totalDue = $order->getTotalDue(); + $baseTotalDue = $order->getBaseTotalDue(); + + // do authorize + $this->processor->authorize($payment, false, $baseTotalDue); + // base amount will be set inside + $payment->setAmountAuthorized($totalDue); + $this->orderRepository->save($order); + + $this->notify($order); + } + + /** + * @param $lockName * * @return bool + * @throws InputException */ - private function validateAmount(CartInterface $quote, Transaction $transaction) + private function releaseLock($lockName) { - $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); - $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); - - return $quoteAmount == $vippsAmount; + return $this->lockManager->unlock($lockName); } } diff --git a/Model/QuoteManagement.php b/Model/QuoteManagement.php index 992cdc66..8d117ec0 100644 --- a/Model/QuoteManagement.php +++ b/Model/QuoteManagement.php @@ -107,4 +107,13 @@ public function getByQuote(CartInterface $cart) return $monitoringQuote; } + + /** + * @param Quote $quote + * @throws \Magento\Framework\Exception\CouldNotSaveException + */ + public function save(Quote $quote) + { + $this->quoteRepository->save($quote); + } } diff --git a/etc/di.xml b/etc/di.xml index c29a0660..d834a2ec 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -48,6 +48,7 @@ Vipps\Payment\Gateway\Config\Config + Vipps\Payment\Model\Logger From 07d6e0403eaf583c60da9818624be8d421a96d7e Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Fri, 1 Feb 2019 17:35:11 +0200 Subject: [PATCH 087/121] VIPPS-189: Add expiration time. --- Api/Data/QuoteStatusInterface.php | 1 + Cron/FetchOrderFromVipps.php | 61 +++++++++++++++++------------ Gateway/Transaction/Transaction.php | 50 +++++++++++++---------- 3 files changed, 66 insertions(+), 46 deletions(-) diff --git a/Api/Data/QuoteStatusInterface.php b/Api/Data/QuoteStatusInterface.php index 050137e0..f7055136 100644 --- a/Api/Data/QuoteStatusInterface.php +++ b/Api/Data/QuoteStatusInterface.php @@ -29,6 +29,7 @@ interface QuoteStatusInterface const FIELD_STATUS = 'status'; const STATUS_NEW = 'new'; + const STATUS_EXPIRED = 'expired'; const STATUS_PLACED = 'placed'; const STATUS_PLACE_FAILED = 'place_failed'; const STATUS_CANCELED = 'canceled'; diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 83b24b13..2ec4027e 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -19,7 +19,8 @@ use Magento\Framework\App\Config\ScopeCodeResolver; use Magento\Framework\Exception\{AlreadyExistsException, CouldNotSaveException, InputException, NoSuchEntityException}; use Magento\Framework\Exception\LocalizedException; -use Magento\Quote\Api\{CartRepositoryInterface, Data\CartInterface}; +use Magento\Framework\Intl\DateTimeFactory; +use Magento\Quote\Api\{Data\CartInterface}; use Magento\Quote\Model\{Quote, QuoteRepository, ResourceModel\Quote\CollectionFactory}; use Magento\Sales\Api\Data\OrderInterface; use Magento\Store\Model\StoreManagerInterface; @@ -75,11 +76,6 @@ class FetchOrderFromVipps */ private $logger; - /** - * @var CartRepositoryInterface - */ - private $cartRepository; - /** * @var StoreManagerInterface */ @@ -115,6 +111,11 @@ class FetchOrderFromVipps */ private $vippsQuoteRepository; + /** + * @var DateTimeFactory + */ + private $dateTimeFactory; + /** * FetchOrderFromVipps constructor. * @param CollectionFactory $quoteCollectionFactory @@ -128,6 +129,7 @@ class FetchOrderFromVipps * @param StoreManagerInterface $storeManager * @param ScopeCodeResolver $scopeCodeResolver * @param Config $cancellationConfig + * @param DateTimeFactory $dateTimeFactory * @param AttemptManagement $attemptManagement */ public function __construct( @@ -142,6 +144,7 @@ public function __construct( StoreManagerInterface $storeManager, ScopeCodeResolver $scopeCodeResolver, Config $cancellationConfig, + DateTimeFactory $dateTimeFactory, AttemptManagement $attemptManagement ) { $this->quoteCollectionFactory = $quoteCollectionFactory; @@ -156,6 +159,7 @@ public function __construct( $this->vippsQuoteCollectionFactory = $vippsQuoteCollectionFactory; $this->quoteRepository = $quoteRepository; $this->vippsQuoteRepository = $vippsQuoteRepository; + $this->dateTimeFactory = $dateTimeFactory; } /** @@ -221,6 +225,9 @@ private function createCollection($currentPage) */ private function processQuote(VippsQuote $vippsQuote) { + $vippsQuoteStatus = ''; + $attemptMessage = ''; + try { // Register empty attempt. $attempt = $this->attemptManagement->createAttempt($vippsQuote); @@ -230,29 +237,30 @@ private function processQuote(VippsQuote $vippsQuote) $this->prepareEnv($quote); $transaction = $this->fetchOrderStatus($quote->getReservedOrderId()); - if ($transaction->isTransactionAborted()) { - $transactionMessage = 'Transaction was cancelled on Vipps side'; - $attempt->setMessage($transactionMessage); - $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCELED); - $this->vippsQuoteRepository->save($vippsQuote); + $attemptMessage = 'Transaction was cancelled on Vipps side'; + $vippsQuoteStatus = QuoteStatusInterface::STATUS_CANCELED; } else { $order = $this->placeOrder($quote, $transaction); - if ($order) { - $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACED); + $vippsQuoteStatus = $order + ? QuoteStatusInterface::STATUS_PLACED + : QuoteStatusInterface::STATUS_PLACE_FAILED; + + if ($transaction->isInitiate() && $this->isQuoteExpired($vippsQuote)) { + $vippsQuoteStatus = QuoteStatusInterface::STATUS_EXPIRED; + $attemptMessage = 'Transaction has been expired'; } } } catch (\Throwable $e) { - $vippsQuote->setStatus(QuoteStatusInterface::STATUS_PLACE_FAILED); + $vippsQuoteStatus = QuoteStatusInterface::STATUS_PLACE_FAILED; $this->logger->critical($e->getMessage(), ['vipps_quote_id' => $vippsQuote->getId()]); - if (isset($attempt)) { - $attempt->setMessage($e->getMessage()); - } + $attemptMessage = $e->getMessage(); } finally { + $vippsQuote->setStatus($vippsQuoteStatus); $this->vippsQuoteRepository->save($vippsQuote); if (isset($attempt)) { - // Simply save the attempt. + $attempt->setMessage($attemptMessage); $this->attemptManagement->save($attempt); } } @@ -311,17 +319,20 @@ private function placeOrder(CartInterface $quote, Transaction $transaction) } /** - * @deprecated - * @param Quote $quote - * @param \DateInterval $interval + * Validate Vipps Quote expiration. * + * @param $vippsQuote * @return bool * @throws \Exception */ - private function isQuoteExpired(Quote $quote, \DateInterval $interval) //@codingStandardsIgnoreLine + private function isQuoteExpired($vippsQuote) { - $quoteExpiredAt = (new \DateTime($quote->getUpdatedAt()))->add($interval); //@codingStandardsIgnoreLine - $isQuoteExpired = !$quoteExpiredAt->diff(new \DateTime())->invert; //@codingStandardsIgnoreLine - return $isQuoteExpired; + $createdAt = $this->dateTimeFactory->create($vippsQuote->getCreatedAt()); + + $interval = new \DateInterval("PT{$this->cancellationConfig->getInactivityTime()}M"); + + $createdAt->add($interval); + + return !$createdAt->diff($this->dateTimeFactory->create())->invert; } } diff --git a/Gateway/Transaction/Transaction.php b/Gateway/Transaction/Transaction.php index b783838d..7eb03629 100644 --- a/Gateway/Transaction/Transaction.php +++ b/Gateway/Transaction/Transaction.php @@ -13,6 +13,7 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ + namespace Vipps\Payment\Gateway\Transaction; /** @@ -169,14 +170,6 @@ public function __construct( $this->shippingDetails = $shippingDetails; } - /** - * @return TransactionInfo - */ - public function getTransactionInfo() - { - return $this->transactionInfo; - } - /** * @return TransactionSummary */ @@ -185,14 +178,6 @@ public function getTransactionSummary() return $this->transactionSummary; } - /** - * @return TransactionLogHistory - */ - public function getTransactionLogHistory() - { - return $this->transactionLogHistory; - } - /** * @return null|UserDetails */ @@ -214,7 +199,25 @@ public function getShippingDetails() */ public function isExpressCheckout() { - return $this->userDetails === null ? false : true; + return $this->userDetails === null ? false : true; + } + + /** + * Is initiate transaction. + * + * @return bool + */ + public function isInitiate() + { + return $this->getTransactionInfo()->getStatus() === Transaction::TRANSACTION_STATUS_INITIATE; + } + + /** + * @return TransactionInfo + */ + public function getTransactionInfo() + { + return $this->transactionInfo; } /** @@ -229,11 +232,8 @@ public function isTransactionAborted() Transaction::TRANSACTION_STATUS_REJECTED, Transaction::TRANSACTION_STATUS_FAILED ]; - if (in_array($this->getTransactionInfo()->getStatus(), $abortedStatuses)) { - return true; - } - return false; + return in_array($this->getTransactionInfo()->getStatus(), $abortedStatuses); } /** @@ -264,4 +264,12 @@ public function getTransactionId() return $this->getTransactionInfo()->getTransactionId() ?: $this->getTransactionLogHistory()->getLastTransactionId(); } + + /** + * @return TransactionLogHistory + */ + public function getTransactionLogHistory() + { + return $this->transactionLogHistory; + } } From 58427283bbdc6240fa8b8c197c89cf1fae340bce Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 4 Feb 2019 12:55:59 +0200 Subject: [PATCH 088/121] VIPPS-189: Add expired status. --- Ui/Component/Column/Status.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Ui/Component/Column/Status.php b/Ui/Component/Column/Status.php index f87961bd..22f7abe1 100644 --- a/Ui/Component/Column/Status.php +++ b/Ui/Component/Column/Status.php @@ -50,6 +50,7 @@ public function getOptions() { return [ QuoteStatusInterface::STATUS_NEW => __('New'), + QuoteStatusInterface::STATUS_EXPIRED => __('Expired'), QuoteStatusInterface::STATUS_PLACED => __('Placed'), QuoteStatusInterface::STATUS_PLACE_FAILED => __('Placing Failed'), QuoteStatusInterface::STATUS_CANCELED => __('Canceled'), From bfde10d058c5a91d4081a4bbf91f82d6893de28c Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 4 Feb 2019 16:29:36 +0200 Subject: [PATCH 089/121] VIPPS-189: Add cancellation attempt. Fix date formatter. --- Block/Monitoring/View.php | 18 ++++++++++++++++++ Model/Quote/CancelFacade.php | 15 ++++++++++++++- view/adminhtml/templates/monitoring/view.phtml | 16 ++++++++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/Block/Monitoring/View.php b/Block/Monitoring/View.php index c6a4a048..12ecbd6e 100644 --- a/Block/Monitoring/View.php +++ b/Block/Monitoring/View.php @@ -150,4 +150,22 @@ public function getStatusLabel($code) { return $this->status->getLabel($code); } + + /** + * Retrieve formatting date + * + * @param null|string|\DateTimeInterface $date + * @param int $format + * @param bool $showTime + * @param null|string $timezone + * @return string + */ + public function formatDate( + $date = null, + $format = \IntlDateFormatter::MEDIUM, + $showTime = true, + $timezone = null + ) { + return parent::formatDate($date, $format, $showTime, $timezone); + } } diff --git a/Model/Quote/CancelFacade.php b/Model/Quote/CancelFacade.php index dcbf097b..90dbb3da 100644 --- a/Model/Quote/CancelFacade.php +++ b/Model/Quote/CancelFacade.php @@ -39,18 +39,25 @@ class CancelFacade implements CancelFacadeInterface * @var QuoteRepository */ private $quoteRepository; + /** + * @var AttemptManagement + */ + private $attemptManagement; /** * CancellationFacade constructor. * @param CommandManagerInterface $commandManager * @param QuoteRepository $quoteRepository + * @param AttemptManagement $attemptManagement */ public function __construct( CommandManagerInterface $commandManager, - QuoteRepository $quoteRepository + QuoteRepository $quoteRepository, + AttemptManagement $attemptManagement ) { $this->commandManager = $commandManager; $this->quoteRepository = $quoteRepository; + $this->attemptManagement = $attemptManagement; } /** @@ -66,14 +73,20 @@ public function cancel( CartInterface $quote ) { try { + $attempt = $this->attemptManagement->createAttempt($vippsQuote); // cancel order on vipps side $this->commandManager->cancel($quote->getPayment()); $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCELED); + $attempt->setMessage('The order has been canceled.'); } catch (\Throwable $exception) { // Log the exception $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCEL_FAILED); + $attempt->setMessage($exception->getMessage()); throw $exception; } finally { + if (isset($attempt)) { + $this->attemptManagement->save($attempt); + } $this->quoteRepository->save($vippsQuote); } } diff --git a/view/adminhtml/templates/monitoring/view.phtml b/view/adminhtml/templates/monitoring/view.phtml index aafa6bd7..9634ac21 100644 --- a/view/adminhtml/templates/monitoring/view.phtml +++ b/view/adminhtml/templates/monitoring/view.phtml @@ -46,11 +46,11 @@ $attempts = $block->getAttempts(); - getCreatedAt(); ?> + formatDate($vippsQuote->getCreatedAt()); ?> - getUpdatedAt(); ?> + formatDate($vippsQuote->getUpdatedAt()); ?> @@ -97,11 +97,11 @@ $attempts = $block->getAttempts(); - getCreatedAt(); ?> + formatDate($quote->getCreatedAt()); ?> - getUpdatedAt(); ?> + formatDate($quote->getUpdatedAt()); ?> @@ -117,12 +117,16 @@ $attempts = $block->getAttempts(); /** @var \Vipps\Payment\Model\Quote\Attempt $attempt */ foreach ($attempts as $attempt): ?> - formatDate($attempt->getCreatedAt(), \IntlDateFormatter::LONG, true); ?> - getMessage(); ?> + getMessage(); ?> + formatDate($attempt->getCreatedAt()); ?> + +
+ +
From a5f0a99331e39686eb7d83c291e3cdd88984dec6 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 4 Feb 2019 18:31:12 +0200 Subject: [PATCH 090/121] VIPPS-189: Add untranslated strings. --- i18n/nb_NO.csv | 122 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 36 deletions(-) diff --git a/i18n/nb_NO.csv b/i18n/nb_NO.csv index 9afd4860..b8226a78 100644 --- a/i18n/nb_NO.csv +++ b/i18n/nb_NO.csv @@ -1,68 +1,118 @@ "A total of %1 record(s) were deleted.","Totalt %1 rad(er) ble slettet." -"Invalid request parameters","Invalid request parameters" "Invalid request","Invalid forespørsel" -"An error occurred during callback processing.","En feil har oppstått under callback prosesseringen." -"Your order was not created. Please try again later.","Din ordre ble ikke opprettet. Vennligst prøv igjen senere." -"An error occurred during payment status update.","En feil oppstod under betalingens statusoppdatering." -"Your order was created, but was canceled.","Din ordre ble opprettet men ble kansellert." "An error occurred during request to Vipps. Please try again later.","En feil har oppstått i forespørselen mot Vipps. Vennligst prøv igjen senere." +"An error occurred during payment status update.","En feil oppstod under betalingens statusoppdatering." +"Your order was canceled in Vipps.","Bestillingen din ble kansellert i Vipps." +"An error occurred during Shipping Details processing.","En feil oppstod ved behandling av leveringsdetaljene." "Can't cancel captured transaction.","Kan ikke kansellere en trukket transaksjon." "Captured amount is higher then remaining amount to capture","Beløpet som du prøver å trekke er høyere enn resterede beløp som kan trekkes" "Transaction validation failed.","Transaksjonens validering feilet." "Refund amount is higher then remaining amount to refund","Beløpet du prøver å refundere er høyere enn resterede beløp som er tilgjengelig for refundering" "Couldn't process this request. Please try again later or contact a store administrator.","Kan ikke prosessere denne forespørselen. Vennligst prøv igjen senere eller kontakt administrator." +"Thank you for shopping at %1.","Takk for at du handler hos %1." +"Thank you for shopping.","Takk for at du handler." "Gateway response error. Incorrect transaction data.","Gateway response error. Ugyldig transaksjonsdata." "Gateway response error. Incorrect initiate payment parameters.","Gateway response error. Ugyldige parametere for initiering av betaling." "Gateway response error. Order Id is incorrect","Gateway response error. Order Id er feil" -Capture,Capture -"Direct Capture","Direct Capture" -"Profiling item with id %1 does not exist.","Profiling item with id %1 does not exist." "Can't retrieve access token from Vipps.","Kan ikke hente access token fra Vipps." "Refreshed Jwt data.","Oppdatert Jwt data." -"Can't save jwt data to database.","Kan ikke lagre jwt data i database." -"Can't get order details","Kan ikke hente ordre detaljer" -"An error occurred during status update","En feil har oppstått under statusoppdatering" +"Vipps payment method does not support Capture Offline","Vipps støtter ikke reservasjon uten nettilkobling." +"Vipps payment method does not support Refund Offline","Vipps støtter ikke refusjon uten nettilkobling." "Can't initiate payment","Kan ikke initiere betalingen" "Some error phrase here","Legg inn feilmelding her" Show,Vis +"Created At",Opprettet +"You will be redirected to the Vipps website.","Du vil bli videresendt til Vipps landingsside." +"Continue to Vipps","Fortsett til Vipps" +"Vipps Payment","Vipps Betaling" +"Enable this Solution","Aktiver denne løsningen" +"Basic Vipps Settings","Generelle Vipps innstillinger" +Environment,Miljø +"Express Checkout","Vipps Hurtigkasse" +"Enable Express Checkout","Aktiver Vipps Hurtigkasse" +"Advanced Settings","Avanserte innstillinger" +"Display on Shopping Cart","Vis i handlekurv" +Delete,Slett +"Delete items","Slettede elementer" +"Are you sure to delete selected records?","Er du sikker på at du vil slette de valgte radene?" +"Quote Monitoring","Quote Monitoring" +"Requests Profiling","Requests Profiling" +"Invalid request parameters","Invalid request parameters" +"Express Payment method is not available.","Express Payment method is not available." +"Couldn't get information about order status right now. Please contact a store administrator.","Couldn't get information about order status right now. Please contact a store administrator." +"Requested quote not found","Requested quote not found" +"Max number of attempts reached (%1)","Max number of attempts reached (%1)" +"An error occurred during capture info sync.","An error occurred during capture info sync." +"Captured amount is not the same as you are trying to capture.' . . ' Payment information was not synced correctly between Magento and Vipps.' . . ' It might be happened that previous operation was successfully completed in Vipps' . . ' but Magento did not receive a response.' . . ' To be in sync you have to capture the same amount that has been already captured' . . ' in Vipps: %1","Captured amount is not the same as you are trying to capture.' . . ' Payment information was not synced correctly between Magento and Vipps.' . . ' It might be happened that previous operation was successfully completed in Vipps' . . ' but Magento did not receive a response.' . . ' To be in sync you have to capture the same amount that has been already captured' . . ' in Vipps: %1" +"An error occurred during refund info sync.","An error occurred during refund info sync." +"Refunded amount is not the same as you are trying to refund.' . . ' Payment information was not synced correctly between Magento and Vipps.' . . ' It might be happened that previous operation was successfully completed in Vipps' . . ' but Magento did not receive a response.' . . ' To be in sync you have to refund the same amount that has been already refunded' . . ' in Vipps: %1","Refunded amount is not the same as you are trying to refund.' . . ' Payment information was not synced correctly between Magento and Vipps.' . . ' It might be happened that previous operation was successfully completed in Vipps' . . ' but Magento did not receive a response.' . . ' To be in sync you have to refund the same amount that has been already refunded' . . ' in Vipps: %1" +"Order Id: %1","Order Id: %1" +Automatic,Automatic +Manual,Manual +Authorize,Authorize +"Authorize and Capture","Authorize and Capture" +"Unserialization result is not an array","Unserialization result is not an array" +"Current connection is already holding lock for $1, only single lock allowed","Current connection is already holding lock for $1, only single lock allowed" +"Lock name too long: %1...","Lock name too long: %1..." +"Profiling item with id ""%1"" does not exist.","Profiling item with id ""%1"" does not exist." +"Could not save Vipps Quote Attempt: %1","Could not save Vipps Quote Attempt: %1" +"Failed to cancel the order. Please contact support team.","Failed to cancel the order. Please contact support team." +"Could not save Vipps Quote: %1","Could not save Vipps Quote: %1" +"Header (center)","Header (center)" +"Sidebar (right)","Sidebar (right)" +"Near Add to cart button","Near Add to cart button" +"After Proceed to checkout button","After Proceed to checkout button" +"'Can't save jwt data to database.' .","'Can't save jwt data to database.' ." +"Deleted JWT data from database.","Deleted JWT data from database." +"Can't invalidate Vipps Jwt Token. Please try again.","Can't invalidate Vipps Jwt Token. Please try again." +"Requested Quote does not exist","Requested Quote does not exist" +New,New +Expired,Expired +Placed,Placed +"Placing Failed","Placing Failed" +Canceled,Canceled +"Cancelling Failed","Cancelling Failed" +Back,Back +"Restart Processing","Restart Processing" +Cancel,Cancel +"Vipps Information","Vipps Information" +"Vipps Quote Id","Vipps Quote Id" +"Reserved Order Id","Reserved Order Id" +"Number Of Attempts","Number Of Attempts" +Status,Status +"Last Updated At","Last Updated At" +"Quote Information","Quote Information" +"Quote Id","Quote Id" +"Store Id","Store Id" +"Customer Email","Customer Email" +"Customer Full Name","Customer Full Name" +"Grand Total","Grand Total" +"Is Active","Is Active" +Attempts,Attempts +"No attempts found.","No attempts found." "Profiling Item Information","Profiling Item Information" "Increment Id","Increment Id" -"Created At","Opprettet" "Request Type","Request Type" "Response Code","Response Code" Request,Request Response,Response -"You will be redirected to the Vipps website.","Du vil bli videresendt til Vipps landingsside." -"Continue to Vipps","Fortsett til Vipps" -"Vipps Payment","Vipps Betaling" Vipps,Vipps -"Information message about Vipps payment method","Infomasjon om Vipps betalingsmetode" -"Enable this Solution","Aktiver denne løsningen" -"Basic Vipps Settings","Generelle Vipps innstillinger" -Environment,Miljø +"Vipps payment method","Vipps payment method" Debug,Debug "Request/Response Profiling","Request/Response Profiling" +"Payment Action","Payment Action" "Saleunit Serial Number","Saleunit Serial Number" "Client ID","Client ID" "Client Secret","Client Secret" "Subscription Key 1","Subscription Key 1" "Subscription Key 2","Subscription Key 2" -Delete,Slett -"Delete items","Slettede elementer" -"Are you sure to delete selected records?","Er du sikker på at du vil slette de valgte radene?" +"Order Cancellation","Order Cancellation" +"Number of Attempts","Number of Attempts" +"Processing Type","Processing Type" +"Inactivity Time","Inactivity Time" +"Vipps Quote ID","Vipps Quote ID" +"Reserved Order ID","Reserved Order ID" +"Attempts Count","Attempts Count" +"Updated At","Updated At" ID,ID "Status Code","Status Code" -"Bad Request","Forespørsel er på feil format" -"An error occurred during order creation.","En feil oppstod ved opprettelse av bestillingen." -"An error occurred during Shipping Details processing.","En feil oppstod ved behandling av leveringsdetaljene." -"Your order was canceled in Vipps.","Bestillingen din ble kansellert i Vipps." -"Thank you for shopping at %1.","Takk for at du handler hos %1." -"Thank you for shopping.","Takk for at du handler." -"Order conformation will be sent later!","Bestillingsbekreftelse sendes senere!" -"Can't place your order. Please try again later.","Bestillingen kan ikke gjennomføres. Vennligst prøv igjen senere." -"Display on Shopping Cart","Vis i handlekurv" -"Advanced Settings","Avanserte innstillinger" -"Express Checkout","Vipps Hurtigkasse" -"Enable Express Checkout","Aktiver Vipps Hurtigkasse" -"Vipps payment method does not support Capture Offline","Vipps støtter ikke reservasjon uten nettilkobling." -"Vipps payment method does not support Refund Offline","Vipps støtter ikke refusjon uten nettilkobling." From b52f082c4b7eb1f6dd75b8130ea9be961605c1ea Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 4 Feb 2019 18:43:45 +0200 Subject: [PATCH 091/121] VIPPS-189: Code review. --- Block/Monitoring/Buttons.php | 3 ++- Model/OrderPlace.php | 2 ++ i18n/nb_NO.csv | 2 ++ view/adminhtml/templates/monitoring/buttons.phtml | 7 ++++--- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Block/Monitoring/Buttons.php b/Block/Monitoring/Buttons.php index 63dc79e4..0b83d605 100644 --- a/Block/Monitoring/Buttons.php +++ b/Block/Monitoring/Buttons.php @@ -31,10 +31,12 @@ class Buttons extends Template * @var RestartFactory */ private $restartFactory; + /** * @var Registry */ private $registry; + /** * @var ManualCancelFactory */ @@ -54,7 +56,6 @@ public function __construct( Template\Context $context, array $data = [] ) { - parent::__construct($context, $data); $this->restartFactory = $restartFactory; diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index f1db4661..7dd3e4ce 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -97,10 +97,12 @@ class OrderPlace * @var CommandManagerInterface */ private $commandManager; + /** * @var QuoteManagement */ private $quoteManagement; + /** * @var LoggerInterface */ diff --git a/i18n/nb_NO.csv b/i18n/nb_NO.csv index b8226a78..dee265cc 100644 --- a/i18n/nb_NO.csv +++ b/i18n/nb_NO.csv @@ -116,3 +116,5 @@ Debug,Debug "Updated At","Updated At" ID,ID "Status Code","Status Code" +"This procedure can not be undone","This procedure can not be undone" +"Are you sure?","Are you sure?" diff --git a/view/adminhtml/templates/monitoring/buttons.phtml b/view/adminhtml/templates/monitoring/buttons.phtml index c7b0c38a..d1e75e4f 100644 --- a/view/adminhtml/templates/monitoring/buttons.phtml +++ b/view/adminhtml/templates/monitoring/buttons.phtml @@ -53,7 +53,8 @@ $vippsQuote = $block->getVippsQuote(); ?> diff --git a/view/adminhtml/templates/monitoring/view.phtml b/view/adminhtml/templates/monitoring/view.phtml index deea7bc8..78f0c8c1 100644 --- a/view/adminhtml/templates/monitoring/view.phtml +++ b/view/adminhtml/templates/monitoring/view.phtml @@ -14,7 +14,7 @@ * IN THE SOFTWARE. * */ - +//@codingStandardsIgnoreFile /** @var \Vipps\Payment\Block\Monitoring\View $block */ $vippsQuote = $block->getVippsQuote(); $attempts = $block->getAttempts(); @@ -39,13 +39,11 @@ $attempts = $block->getAttempts(); - getAttempts(); ?> + getAttempts(); ?> - getStatusLabel($vippsQuote->getStatus()) ?> + getStatusLabel($vippsQuote->getStatus()) ?> @@ -63,9 +61,9 @@ $attempts = $block->getAttempts();
- getQuoteLoadingError()): ?> + getQuoteLoadingError()) : ?> - + getQuote(); ?> @@ -91,8 +89,7 @@ $attempts = $block->getAttempts(); - + @@ -111,7 +108,7 @@ $attempts = $block->getAttempts();
getPriceHelper()->currencyByStore($quote->getGrandTotal(), $quote->getStoreId()); ?>getPriceHelper()->currencyByStore($quote->getGrandTotal(), $quote->getStoreId()); ?>
- count()): ?> + count()) : ?>
@@ -119,7 +116,7 @@ $attempts = $block->getAttempts(); + foreach ($attempts as $attempt) : ?> getMessage(); ?> formatDate($attempt->getCreatedAt()); ?> @@ -127,10 +124,9 @@ $attempts = $block->getAttempts(); - +
-
diff --git a/view/adminhtml/templates/profiling/back_button.phtml b/view/adminhtml/templates/profiling/back_button.phtml index 3d87d22d..4493cdf4 100644 --- a/view/adminhtml/templates/profiling/back_button.phtml +++ b/view/adminhtml/templates/profiling/back_button.phtml @@ -24,9 +24,9 @@ title="Back" type="button" class="action- scalable back" - onclick="location.href='getUrl('*/*'); ?>'" + onclick="location.href='getUrl('*/*'); //@codingStandardsIgnoreLine ?>'" data-ui-id="back-button"> - Back + diff --git a/view/adminhtml/templates/profiling/view.phtml b/view/adminhtml/templates/profiling/view.phtml index e899d013..97cbc5fe 100644 --- a/view/adminhtml/templates/profiling/view.phtml +++ b/view/adminhtml/templates/profiling/view.phtml @@ -13,40 +13,39 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ - +//@codingStandardsIgnoreFile /** @var \Vipps\Payment\Block\Profiling\View $block */ -$item = $block->getItem(); -?> +$item = $block->getItem(); ?>
- +
- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + +
getIncrementId(); ?>
getCreatedAt(); ?>
getRequestType(); ?>
getStatusCode(); ?>
getFormattedRequest(); ?>
getFormattedResponse(); ?>
getIncrementId(); ?>
getCreatedAt(); ?>
getRequestType(); ?>
getStatusCode(); ?>
getFormattedRequest(); ?>
getFormattedResponse(); ?>
diff --git a/view/frontend/templates/button.phtml b/view/frontend/templates/button.phtml index dc178ea0..53c55433 100644 --- a/view/frontend/templates/button.phtml +++ b/view/frontend/templates/button.phtml @@ -15,28 +15,40 @@ */ ?> getData('size'); - $isInCatalogProduct = false; - if ($block->getIsInCatalogProduct()) { - $isInCatalogProduct = $block->getIsInCatalogProduct(); - } - $shortcutHtmlId = $block->escapeHtml($block->getShortcutHtmlId()); +/** @var $block \Vipps\Payment\Block\Express\Button */ +$size = $block->getData('size'); +$isInCatalogProduct = false; +if ($block->getIsInCatalogProduct()) { + $isInCatalogProduct = $block->getIsInCatalogProduct(); +} +$shortcutHtmlId = $block->escapeHtml($block->getShortcutHtmlId()); ?> \ No newline at end of file + From ff52be9b968a673ad682bada21096782f6463dc8 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 7 Feb 2019 16:55:35 +0200 Subject: [PATCH 103/121] Update text. --- etc/adminhtml/system.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 2ff56e5e..66862dda 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -21,8 +21,8 @@ - - + + complex vipps-section Vipps\Payment\Block\Adminhtml\System\Config\Fieldset\Payment payment/vipps/active From d8c831c689e63baf2ca4aa2c038b7d7c3fab3f1e Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Thu, 7 Feb 2019 19:43:31 +0200 Subject: [PATCH 104/121] VB2C-346: error returned by Vipps was not logged. --- Gateway/Command/GatewayCommand.php | 40 ++++++++++++++++++--------- Model/Profiling/Profiler.php | 2 +- Model/Profiling/ProfilerInterface.php | 2 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Gateway/Command/GatewayCommand.php b/Gateway/Command/GatewayCommand.php index a8fa6a6d..9adb98b4 100755 --- a/Gateway/Command/GatewayCommand.php +++ b/Gateway/Command/GatewayCommand.php @@ -135,51 +135,50 @@ public function execute(array $commandSubject) $transfer = $this->transferFactory->create( $this->requestBuilder->build($commandSubject) ); - $result = $this->client->placeRequest($transfer); - /** @var ZendResponse $response */ $response = $result['response']; $responseBody = $this->jsonDecoder->decode($response->getContent()); - $this->profiler->save($transfer, $response); - if (!$response->isSuccess()) { $error = $this->extractError($responseBody); - $exception = $this->exceptionFactory->create( - $error['code'] ?: $response->getStatusCode(), - $error['message'] ?: $response->getReasonPhrase() + $orderId = $this->extractOrderId($transfer, $responseBody); + $errorCode = $error['code'] ?: $response->getStatusCode(); + $errorMessage = $error['message'] ?: $response->getReasonPhrase(); + $exception = $this->exceptionFactory->create($errorCode, $errorMessage); + $message = sprintf( + 'Request error. Code: "%s", message: "%s", order id: "%s"', + $errorCode, + $errorMessage, + $orderId ); + $this->logger->critical($message); throw $exception; } - /** Validating Success response body by specific command validators */ if ($this->validator !== null) { $validationResult = $this->validator->validate( array_merge($commandSubject, ['jsonData' => $responseBody]) ); if (!$validationResult->isValid()) { - $this->logExceptions($validationResult->getFailsDescription()); + $this->logValidationFails($validationResult->getFailsDescription()); throw new CommandException( __('Transaction validation failed.') ); } } - /** Handling response after validation is success */ if ($this->handler) { $this->handler->handle($commandSubject, $responseBody); } - return $responseBody; } - /** * @param Phrase[] $fails * * @return void */ - private function logExceptions(array $fails) + private function logValidationFails(array $fails) { foreach ($fails as $failPhrase) { $this->logger->critical((string) $failPhrase); @@ -200,4 +199,19 @@ private function extractError($responseBody) 'message' => isset($responseBody[0]['errorMessage']) ? $responseBody[0]['errorMessage'] : null, ]; } + + /** + * @param TransferInterface $transfer + * @param $responseBody + * + * @return string|null + */ + private function extractOrderId(TransferInterface $transfer, $responseBody) + { + $orderId = null; + if (preg_match('/payments(\/([^\/]+)\/([a-z]+))?$/', $transfer->getUri(), $matches)) { + $orderId = $matches[2] ?? null; + } + return $orderId ?? ($transfer->getBody()['transaction']['orderId'] ?? ($responseBody['orderId'] ?? null)); + } } diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index 95bdfed0..3bfa12f4 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -80,7 +80,7 @@ public function __construct( * @param TransferInterface $transfer * @param Response $response * - * @return bool + * @return string|null */ public function save(TransferInterface $transfer, Response $response) { diff --git a/Model/Profiling/ProfilerInterface.php b/Model/Profiling/ProfilerInterface.php index ab1bd10f..ef7797c0 100644 --- a/Model/Profiling/ProfilerInterface.php +++ b/Model/Profiling/ProfilerInterface.php @@ -25,7 +25,7 @@ interface ProfilerInterface * @param TransferInterface $transfer * @param Response $response * - * @return bool + * @return string|null */ public function save(TransferInterface $transfer, Response $response); } From 21ce3c7220a3c3cb3b9ea0cb452a830cad239f8d Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Thu, 7 Feb 2019 19:57:54 +0200 Subject: [PATCH 105/121] Code review. --- Cron/FetchOrderFromVipps.php | 2 +- Model/OrderPlace.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index ccb97f5a..75d422aa 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -215,7 +215,7 @@ private function createCollection($currentPage) private function processQuote(VippsQuote $vippsQuote) { $vippsQuoteStatus = QuoteStatusInterface::STATUS_PROCESSING; - $attemptMessage = __('Waiting for customer action'); + $attemptMessage = __('Waiting while customer accept payment'); try { // Register new attempt. diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 1e674aab..55230673 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -303,7 +303,7 @@ private function prepareQuote($quote) */ private function validateAmount(CartInterface $quote, Transaction $transaction) { - $quoteAmount = (int)$this->formatPrice($quote->getGrandTotal()) * 100; + $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); if ($quoteAmount != $vippsAmount) { From b4bec491f1f6ad9aa86273fa46989c3af2a28b6b Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Thu, 7 Feb 2019 20:41:09 +0200 Subject: [PATCH 106/121] VB2C-346: error returned by Vipps was not logged. minor fix for types --- Gateway/Command/GatewayCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gateway/Command/GatewayCommand.php b/Gateway/Command/GatewayCommand.php index 9adb98b4..b7a8ff88 100755 --- a/Gateway/Command/GatewayCommand.php +++ b/Gateway/Command/GatewayCommand.php @@ -202,11 +202,11 @@ private function extractError($responseBody) /** * @param TransferInterface $transfer - * @param $responseBody + * @param array $responseBody * * @return string|null */ - private function extractOrderId(TransferInterface $transfer, $responseBody) + private function extractOrderId($transfer, $responseBody) { $orderId = null; if (preg_match('/payments(\/([^\/]+)\/([a-z]+))?$/', $transfer->getUri(), $matches)) { From 8eed774f5ccdea45494d2c9992bb4512d2a22bdd Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Thu, 7 Feb 2019 20:54:16 +0200 Subject: [PATCH 107/121] VB2C-346: error returned by Vipps was not logged. minor CR fixes --- Gateway/Command/GatewayCommand.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Gateway/Command/GatewayCommand.php b/Gateway/Command/GatewayCommand.php index b7a8ff88..fcbad73e 100755 --- a/Gateway/Command/GatewayCommand.php +++ b/Gateway/Command/GatewayCommand.php @@ -135,16 +135,20 @@ public function execute(array $commandSubject) $transfer = $this->transferFactory->create( $this->requestBuilder->build($commandSubject) ); + $result = $this->client->placeRequest($transfer); + /** @var ZendResponse $response */ $response = $result['response']; $responseBody = $this->jsonDecoder->decode($response->getContent()); + $this->profiler->save($transfer, $response); + if (!$response->isSuccess()) { $error = $this->extractError($responseBody); $orderId = $this->extractOrderId($transfer, $responseBody); - $errorCode = $error['code'] ?: $response->getStatusCode(); - $errorMessage = $error['message'] ?: $response->getReasonPhrase(); + $errorCode = $error['code'] ?? $response->getStatusCode(); + $errorMessage = $error['message'] ?? $response->getReasonPhrase(); $exception = $this->exceptionFactory->create($errorCode, $errorMessage); $message = sprintf( 'Request error. Code: "%s", message: "%s", order id: "%s"', From 211a5fb71840132df2cfd36c388bd0132696207d Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Thu, 7 Feb 2019 20:56:08 +0200 Subject: [PATCH 108/121] VB2C-346: error returned by Vipps was not logged. minor CR fixes --- Gateway/Command/GatewayCommand.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Gateway/Command/GatewayCommand.php b/Gateway/Command/GatewayCommand.php index fcbad73e..668359e4 100755 --- a/Gateway/Command/GatewayCommand.php +++ b/Gateway/Command/GatewayCommand.php @@ -159,6 +159,7 @@ public function execute(array $commandSubject) $this->logger->critical($message); throw $exception; } + /** Validating Success response body by specific command validators */ if ($this->validator !== null) { $validationResult = $this->validator->validate( @@ -171,10 +172,12 @@ public function execute(array $commandSubject) ); } } + /** Handling response after validation is success */ if ($this->handler) { $this->handler->handle($commandSubject, $responseBody); } + return $responseBody; } /** From e2fcb5508a7f6416cdd3347a0a3fa67ffc8a865c Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Thu, 7 Feb 2019 21:01:50 +0200 Subject: [PATCH 109/121] VB2C-346: error returned by Vipps was not logged. minor CR fixes --- Gateway/Command/GatewayCommand.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Gateway/Command/GatewayCommand.php b/Gateway/Command/GatewayCommand.php index 668359e4..92fe1a31 100755 --- a/Gateway/Command/GatewayCommand.php +++ b/Gateway/Command/GatewayCommand.php @@ -180,6 +180,7 @@ public function execute(array $commandSubject) return $responseBody; } + /** * @param Phrase[] $fails * From bc6848bb920bbd75fc4d7932efa476dc993459bc Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 11 Feb 2019 13:30:11 +0200 Subject: [PATCH 110/121] VIPPS-181: Search for extistend order in background processing. --- Cron/FetchOrderFromVipps.php | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index 75d422aa..c2e48cd3 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -20,7 +20,6 @@ use Magento\Framework\Exception\{AlreadyExistsException, CouldNotSaveException, InputException, NoSuchEntityException}; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Intl\DateTimeFactory; -use Magento\Quote\Api\{Data\CartInterface}; use Magento\Quote\Model\{QuoteRepository, ResourceModel\Quote\CollectionFactory}; use Magento\Sales\Api\Data\OrderInterface; use Magento\Store\Model\StoreManagerInterface; @@ -31,6 +30,7 @@ Gateway\Transaction\Transaction, Gateway\Transaction\TransactionBuilder, Model\Order\Cancellation\Config, + Model\OrderLocator, Model\OrderPlace, Model\Quote as VippsQuote, Model\Quote\AttemptManagement, @@ -110,10 +110,13 @@ class FetchOrderFromVipps * @var DateTimeFactory */ private $dateTimeFactory; + /** + * @var OrderLocator + */ + private $orderLocator; /** * FetchOrderFromVipps constructor. - * @param CollectionFactory $quoteCollectionFactory * @param VippsQuoteCollectionFactory $vippsQuoteCollectionFactory * @param VippsQuoteRepository $vippsQuoteRepository * @param QuoteRepository $quoteRepository @@ -126,6 +129,7 @@ class FetchOrderFromVipps * @param Config $cancellationConfig * @param DateTimeFactory $dateTimeFactory * @param AttemptManagement $attemptManagement + * @param OrderLocator $orderLocator */ public function __construct( VippsQuoteCollectionFactory $vippsQuoteCollectionFactory, @@ -139,7 +143,8 @@ public function __construct( ScopeCodeResolver $scopeCodeResolver, Config $cancellationConfig, DateTimeFactory $dateTimeFactory, - AttemptManagement $attemptManagement + AttemptManagement $attemptManagement, + OrderLocator $orderLocator ) { $this->commandManager = $commandManager; $this->transactionBuilder = $transactionBuilder; @@ -153,6 +158,7 @@ public function __construct( $this->quoteRepository = $quoteRepository; $this->vippsQuoteRepository = $vippsQuoteRepository; $this->dateTimeFactory = $dateTimeFactory; + $this->orderLocator = $orderLocator; } /** @@ -223,13 +229,12 @@ private function processQuote(VippsQuote $vippsQuote) $this->prepareEnv($vippsQuote); // Get Magento Quote for processing. - $quote = $this->quoteRepository->get($vippsQuote->getQuoteId()); $transaction = $this->fetchOrderStatus($vippsQuote->getReservedOrderId()); if ($transaction->isTransactionAborted()) { $attemptMessage = __('Transaction was cancelled in Vipps'); $vippsQuoteStatus = QuoteStatusInterface::STATUS_CANCELED; } else { - $order = $this->placeOrder($quote, $transaction); + $order = $this->placeOrder($vippsQuote, $transaction); if ($order) { $vippsQuoteStatus = QuoteStatusInterface::STATUS_PLACED; $attemptMessage = __('Placed'); @@ -283,21 +288,25 @@ private function fetchOrderStatus($orderId) } /** - * @param CartInterface $quote + * @param VippsQuote $vippsQuote * @param Transaction $transaction * * @return OrderInterface|null * @throws AlreadyExistsException * @throws CouldNotSaveException * @throws InputException + * @throws LocalizedException * @throws NoSuchEntityException * @throws VippsException - * @throws LocalizedException * @throws WrongAmountException */ - private function placeOrder(CartInterface $quote, Transaction $transaction) + private function placeOrder(VippsQuote $vippsQuote, Transaction $transaction) { - $order = $this->orderPlace->execute($quote, $transaction); + $quote = $this->quoteRepository->get($vippsQuote->getQuoteId()); + + $existentOrder = $this->orderLocator->get($vippsQuote->getReservedOrderId()); + + $order = $existentOrder ?? $this->orderPlace->execute($quote, $transaction); if ($order) { $this->logger->debug(sprintf('Order placed: "%s"', $order->getIncrementId())); } else { From 3996f367cb374232ca81b0ab67aeb42ed0c3530c Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 11 Feb 2019 15:34:37 +0200 Subject: [PATCH 111/121] VIPPS-181: Add void status to canceled statuses list. --- Gateway/Transaction/Transaction.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gateway/Transaction/Transaction.php b/Gateway/Transaction/Transaction.php index 7eb03629..8f082358 100644 --- a/Gateway/Transaction/Transaction.php +++ b/Gateway/Transaction/Transaction.php @@ -230,7 +230,8 @@ public function isTransactionAborted() Transaction::TRANSACTION_STATUS_CANCELLED, Transaction::TRANSACTION_STATUS_AUTOCANCEL, Transaction::TRANSACTION_STATUS_REJECTED, - Transaction::TRANSACTION_STATUS_FAILED + Transaction::TRANSACTION_STATUS_FAILED, + Transaction::TRANSACTION_STATUS_VOID ]; return in_array($this->getTransactionInfo()->getStatus(), $abortedStatuses); From ede5c16751d5e6bc9d75c6c7139bfc6e90000270 Mon Sep 17 00:00:00 2001 From: Oleg Malichenko Date: Mon, 11 Feb 2019 15:44:54 +0200 Subject: [PATCH 112/121] Update FetchOrderFromVipps.php --- Cron/FetchOrderFromVipps.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Cron/FetchOrderFromVipps.php b/Cron/FetchOrderFromVipps.php index c2e48cd3..278ab93d 100644 --- a/Cron/FetchOrderFromVipps.php +++ b/Cron/FetchOrderFromVipps.php @@ -110,6 +110,7 @@ class FetchOrderFromVipps * @var DateTimeFactory */ private $dateTimeFactory; + /** * @var OrderLocator */ From c0351b7e73f235b258757e1899b990a3806abc21 Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Tue, 12 Feb 2019 10:29:52 +0200 Subject: [PATCH 113/121] VB2C-346: profile data is reverted with erroneous transaction added separate connection for profile recource model --- INSTALL.md | 23 +++++++++++++++++++++++ etc/di.xml | 5 +++++ 2 files changed, 28 insertions(+) diff --git a/INSTALL.md b/INSTALL.md index c2c76535..679310cc 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -37,6 +37,29 @@ From Magento Admin navigate to `Store` -> `Configuration` -> `Sales` -> `Payment By clicking the `Configure` button, all configuration module settings will be shown. Once you have finished with the configuration simply click `Close` and `Save` button for your convenience. +## Add a separate connection for Vipps resources +* Duplicate 'default' connection in app/etc/env.php and name it 'vipps'. It should look like: +``` + 'vipps' => + array ( + 'host' => 'your_DB_host', + 'dbname' => 'your_DB_name', + 'username' => 'your_user', + 'password' => 'your_password', + 'model' => 'mysql4', + 'engine' => 'innodb', + 'initStatements' => 'SET NAMES utf8;', + 'active' => '1', + ), +``` +* Add also the following configuration to 'resource' array in the same file: +``` + 'vipps' => + array ( + 'connection' => 'vipps', + ), +``` + # Settings Vipps Payments configuration is divided by sections. It helps to quickly find and manage settings of each module feature: diff --git a/etc/di.xml b/etc/di.xml index 20229070..3d0ce970 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -414,4 +414,9 @@ Magento\Customer\Model\Session\Proxy
+ + + vipps + +
From ece6898837646e25a580d0fe20e66c4ce48cea31 Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Tue, 12 Feb 2019 10:32:43 +0200 Subject: [PATCH 114/121] VB2C-346: profile data is reverted with erroneous transaction added reason --- INSTALL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/INSTALL.md b/INSTALL.md index 679310cc..e6333e87 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -59,6 +59,7 @@ By clicking the `Configure` button, all configuration module settings will be sh 'connection' => 'vipps', ), ``` +These settings are required to prevent profiles loss when Magento reverts invoice/refund transactions. # Settings From 495da860e7e6c454f10852a8ddec66cdf4ded3b6 Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Tue, 12 Feb 2019 16:56:13 +0200 Subject: [PATCH 115/121] VB2C-346: profile data is reverted with erroneous transaction added filtering of POST fields --- Gateway/Http/Client/Curl.php | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index 2e30338c..8afe7fac 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -57,6 +57,16 @@ class Curl implements ClientInterface */ private $logger; + /** + * @var array + */ + private $knownFields = [ + 'orderId', + 'customerInfo', + 'merchantInfo', + 'transaction', + ]; + /** * Curl constructor. * @@ -115,12 +125,13 @@ private function place(TransferInterface $transfer) /** @var MagentoCurl $adapter */ $adapter = $this->adapterFactory->create(); $options = $this->getBasicOptions(); + $requestBody = $this->preparePostFields($transfer->getBody()); if ($transfer->getMethod() === Request::METHOD_PUT) { $options = $options + [ CURLOPT_RETURNTRANSFER => true, CURLOPT_CUSTOMREQUEST => Request::METHOD_PUT, - CURLOPT_POSTFIELDS => $this->jsonEncoder->encode($transfer->getBody()) + CURLOPT_POSTFIELDS => $this->jsonEncoder->encode($requestBody) ]; } $adapter->setOptions($options); @@ -141,6 +152,22 @@ private function place(TransferInterface $transfer) } } + /** + * Remove all fields that are not marked as known. + * + * @param array $fields + * @return array + */ + private function preparePostFields($fields) + { + foreach ($fields as $name => $value) { + if (!in_array($name, $this->knownFields)) { + unset($fields[$name]); + } + } + return $fields; + } + /** * @param $headers * From 7acec76be88539dcdefe3e9d2505c42cb004fcba Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Tue, 12 Feb 2019 17:53:18 +0200 Subject: [PATCH 116/121] VB2C-346: profile data is reverted with erroneous transaction fix for absent created_at field --- Api/Profiling/Data/ItemInterface.php | 6 ++++++ Model/Profiling/Item.php | 9 +++++++++ Model/Profiling/Profiler.php | 1 + 3 files changed, 16 insertions(+) diff --git a/Api/Profiling/Data/ItemInterface.php b/Api/Profiling/Data/ItemInterface.php index 6be37b81..cdae9d7d 100644 --- a/Api/Profiling/Data/ItemInterface.php +++ b/Api/Profiling/Data/ItemInterface.php @@ -132,4 +132,10 @@ public function setResponse($value); * @return string */ public function getCreatedAt(); + + /** + * @param string $value + * @return $this + */ + public function setCreatedAt($value); } diff --git a/Model/Profiling/Item.php b/Model/Profiling/Item.php index e58da003..68629995 100644 --- a/Model/Profiling/Item.php +++ b/Model/Profiling/Item.php @@ -43,6 +43,15 @@ public function getCreatedAt(): string return (string)$this->getData(self::CREATED_AT); } + /** + * @param string $value + * @return $this + */ + public function setCreatedAt($value) + { + return $this->setData(self::CREATED_AT, $value); + } + /** * Return increment id value * diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index 3bfa12f4..9d297b71 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -103,6 +103,7 @@ public function save(TransferInterface $transfer, Response $response) $itemDO->setStatusCode($response->getStatusCode()); $itemDO->setIncrementId($orderId); $itemDO->setResponse($this->packArray($this->parseResponse($response))); + $itemDO->setCreatedAt(date('Y-m-d H:i:s')); $item = $this->itemRepository->save($itemDO); return $item->getEntityId(); From 0017d4c113f3bcfa0df46800d195faa99357eb9d Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Tue, 12 Feb 2019 19:56:18 +0200 Subject: [PATCH 117/121] VB2C-346: profile data is reverted with erroneous transaction CR fixes --- Gateway/Http/Client/Curl.php | 12 +++++++----- Model/Profiling/Profiler.php | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index 8afe7fac..a1164278 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -160,11 +160,13 @@ private function place(TransferInterface $transfer) */ private function preparePostFields($fields) { - foreach ($fields as $name => $value) { - if (!in_array($name, $this->knownFields)) { - unset($fields[$name]); - } - } + $knownFields = $this->knownFields; + $fields = array_filter( + $fields, + function ($key) use ($knownFields) { return in_array($key, $knownFields, false);}, + ARRAY_FILTER_USE_KEY + ); + return $fields; } diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index 9d297b71..e9a7f904 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -103,7 +103,7 @@ public function save(TransferInterface $transfer, Response $response) $itemDO->setStatusCode($response->getStatusCode()); $itemDO->setIncrementId($orderId); $itemDO->setResponse($this->packArray($this->parseResponse($response))); - $itemDO->setCreatedAt(date('Y-m-d H:i:s')); + $itemDO->setCreatedAt(date(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)); $item = $this->itemRepository->save($itemDO); return $item->getEntityId(); From 9dc761c2b1ae1182bed8e7bdebbad731c1cea112 Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Wed, 13 Feb 2019 11:34:09 +0200 Subject: [PATCH 118/121] VB2C-346: profile data is reverted with erroneous transaction CR fixes --- Gateway/Http/Client/Curl.php | 6 +++--- Model/Profiling/Profiler.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index a1164278..7d79f5f4 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -60,7 +60,7 @@ class Curl implements ClientInterface /** * @var array */ - private $knownFields = [ + private $allowedFields = [ 'orderId', 'customerInfo', 'merchantInfo', @@ -160,10 +160,10 @@ private function place(TransferInterface $transfer) */ private function preparePostFields($fields) { - $knownFields = $this->knownFields; + $allowedFields = $this->allowedFields; $fields = array_filter( $fields, - function ($key) use ($knownFields) { return in_array($key, $knownFields, false);}, + function ($key) use ($allowedFields) { return in_array($key, $allowedFields);}, ARRAY_FILTER_USE_KEY ); diff --git a/Model/Profiling/Profiler.php b/Model/Profiling/Profiler.php index e9a7f904..8b5ce4e4 100644 --- a/Model/Profiling/Profiler.php +++ b/Model/Profiling/Profiler.php @@ -103,7 +103,7 @@ public function save(TransferInterface $transfer, Response $response) $itemDO->setStatusCode($response->getStatusCode()); $itemDO->setIncrementId($orderId); $itemDO->setResponse($this->packArray($this->parseResponse($response))); - $itemDO->setCreatedAt(date(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)); + $itemDO->setCreatedAt(gmdate(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT)); $item = $this->itemRepository->save($itemDO); return $item->getEntityId(); From c791573358990216837dd33418fbdd952de0ece7 Mon Sep 17 00:00:00 2001 From: Serhiy Shkolyarenko Date: Mon, 18 Feb 2019 20:45:52 +0200 Subject: [PATCH 119/121] VB2C-346: error returned by Vipps was not logged fixed field logging --- Gateway/Http/Client/Curl.php | 30 +----------------------------- Gateway/Http/TransferFactory.php | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 32 deletions(-) diff --git a/Gateway/Http/Client/Curl.php b/Gateway/Http/Client/Curl.php index 7d79f5f4..b48e28db 100644 --- a/Gateway/Http/Client/Curl.php +++ b/Gateway/Http/Client/Curl.php @@ -57,16 +57,6 @@ class Curl implements ClientInterface */ private $logger; - /** - * @var array - */ - private $allowedFields = [ - 'orderId', - 'customerInfo', - 'merchantInfo', - 'transaction', - ]; - /** * Curl constructor. * @@ -125,7 +115,7 @@ private function place(TransferInterface $transfer) /** @var MagentoCurl $adapter */ $adapter = $this->adapterFactory->create(); $options = $this->getBasicOptions(); - $requestBody = $this->preparePostFields($transfer->getBody()); + $requestBody = $transfer->getBody(); if ($transfer->getMethod() === Request::METHOD_PUT) { $options = $options + [ @@ -152,24 +142,6 @@ private function place(TransferInterface $transfer) } } - /** - * Remove all fields that are not marked as known. - * - * @param array $fields - * @return array - */ - private function preparePostFields($fields) - { - $allowedFields = $this->allowedFields; - $fields = array_filter( - $fields, - function ($key) use ($allowedFields) { return in_array($key, $allowedFields);}, - ARRAY_FILTER_USE_KEY - ); - - return $fields; - } - /** * @param $headers * diff --git a/Gateway/Http/TransferFactory.php b/Gateway/Http/TransferFactory.php index 7664e4a4..09662011 100644 --- a/Gateway/Http/TransferFactory.php +++ b/Gateway/Http/TransferFactory.php @@ -54,6 +54,16 @@ class TransferFactory implements TransferFactoryInterface */ private $urlParams = []; + /** + * @var array + */ + private $allowedFields = [ + 'orderId', + 'customerInfo', + 'merchantInfo', + 'transaction', + ]; + /** * TransferFactory constructor. * @@ -91,9 +101,7 @@ public function create(array $request) ClientInterface::HEADER_PARAM_X_REQUEST_ID => $request['requestId'] ?? $this->generateRequestId() ]); - if (isset($request['requestId'])) { - unset($request['requestId']); - } + $request = $this->filterPostFields($request); $this->transferBuilder ->setBody($this->getBody($request)) @@ -103,6 +111,24 @@ public function create(array $request) return $this->transferBuilder->build(); } + /** + * Remove all fields that are not marked as allowed. + * + * @param array $fields + * @return array + */ + private function filterPostFields($fields) + { + $allowedFields = $this->allowedFields; + $fields = array_filter( + $fields, + function ($key) use ($allowedFields) { return in_array($key, $allowedFields);}, + ARRAY_FILTER_USE_KEY + ); + + return $fields; + } + /** * Generating Url. * From c6a4f0acd641dff75136f3bc43084e218966bcd4 Mon Sep 17 00:00:00 2001 From: Ostap Smolyar Date: Mon, 25 Feb 2019 12:00:36 +0200 Subject: [PATCH 120/121] VIPPS-196: Order amount in Vipps is not the same as in Magento --- Gateway/Command/CaptureCommand.php | 6 +++--- Gateway/Command/RefundCommand.php | 6 +++--- Gateway/Request/Initiate/TransactionDataBuilder.php | 4 ++-- Gateway/Request/TransactionDataBuilder.php | 2 +- Model/OrderPlace.php | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Gateway/Command/CaptureCommand.php b/Gateway/Command/CaptureCommand.php index bb6effe9..e8b68bae 100644 --- a/Gateway/Command/CaptureCommand.php +++ b/Gateway/Command/CaptureCommand.php @@ -183,7 +183,7 @@ public function __construct( public function execute(array $commandSubject) { $amount = $this->subjectReader->readAmount($commandSubject); - $amount = (int)($this->formatPrice($amount) * 100); + $amount = (int)round($this->formatPrice($amount) * 100); $response = $this->paymentDetailsProvider->get($commandSubject); $transaction = $this->transactionBuilder->setData($response)->build(); @@ -219,14 +219,14 @@ private function captureBasedOnPaymentDetails($commandSubject, Transaction $tran { $payment = $this->subjectReader->readPayment($commandSubject); $amount = $this->subjectReader->readAmount($commandSubject); - $amount = (int)($this->formatPrice($amount) * 100); + $amount = (int)round($this->formatPrice($amount) * 100); $orderAdapter = $payment->getOrder(); $orderIncrementId = $orderAdapter->getOrderIncrementId(); $order = $this->orderRepository->get($orderAdapter->getId()); - $magentoTotalDue = (int)($this->formatPrice($order->getTotalDue()) * 100); + $magentoTotalDue = (int)round($this->formatPrice($order->getTotalDue()) * 100); $vippsTotalDue = $transaction->getTransactionSummary()->getRemainingAmountToCapture(); $deltaTotalDue = $magentoTotalDue - $vippsTotalDue; diff --git a/Gateway/Command/RefundCommand.php b/Gateway/Command/RefundCommand.php index 2916e9a1..797fc7d2 100644 --- a/Gateway/Command/RefundCommand.php +++ b/Gateway/Command/RefundCommand.php @@ -184,7 +184,7 @@ public function __construct( public function execute(array $commandSubject) { $amount = $this->subjectReader->readAmount($commandSubject); - $amount = (int)($this->formatPrice($amount) * 100); + $amount = (int)round($this->formatPrice($amount) * 100); $response = $this->paymentDetailsProvider->get($commandSubject); $transaction = $this->transactionBuilder->setData($response)->build(); @@ -220,14 +220,14 @@ private function refundBasedOnPaymentDetails($commandSubject, Transaction $trans { $payment = $this->subjectReader->readPayment($commandSubject); $amount = $this->subjectReader->readAmount($commandSubject); - $amount = (int)($this->formatPrice($amount) * 100); + $amount = (int)round($this->formatPrice($amount) * 100); $orderAdapter = $payment->getOrder(); $orderIncrementId = $orderAdapter->getOrderIncrementId(); $order = $this->orderRepository->get($orderAdapter->getId()); - $magentoTotalRefunded = (int)($this->formatPrice($order->getTotalRefunded()) * 100); + $magentoTotalRefunded = (int)round($this->formatPrice($order->getTotalRefunded()) * 100); $vippsTotalRefunded = $transaction->getTransactionSummary()->getRefundedAmount(); $deltaTotalRefunded = $vippsTotalRefunded - $magentoTotalRefunded; diff --git a/Gateway/Request/Initiate/TransactionDataBuilder.php b/Gateway/Request/Initiate/TransactionDataBuilder.php index bc9a9b3d..09e4bfbc 100644 --- a/Gateway/Request/Initiate/TransactionDataBuilder.php +++ b/Gateway/Request/Initiate/TransactionDataBuilder.php @@ -79,14 +79,14 @@ public function build(array $buildSubject) $quote = $payment->getQuote(); $amount = $this->subjectReader->readAmount($buildSubject); - $amount = (int)($this->formatPrice($amount) * 100); + $amount = (int)round($this->formatPrice($amount) * 100); if ($buildSubject[self::PAYMENT_TYPE_KEY] == self::PAYMENT_TYPE_EXPRESS_CHECKOUT) { $shippingAddress = $quote->getShippingAddress(); $shippingAddress->setShippingMethod(null); $quote->collectTotals(); - $amount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); + $amount = (int)round($this->formatPrice($quote->getGrandTotal()) * 100); } return [ diff --git a/Gateway/Request/TransactionDataBuilder.php b/Gateway/Request/TransactionDataBuilder.php index a38ded17..6bedbeec 100644 --- a/Gateway/Request/TransactionDataBuilder.php +++ b/Gateway/Request/TransactionDataBuilder.php @@ -76,7 +76,7 @@ public function build(array $buildSubject) $amount = $this->subjectReader->readAmount($buildSubject); if ($amount) { - $transactionData[self::$transaction][self::$amount] = (int)($this->formatPrice($amount) * 100); + $transactionData[self::$transaction][self::$amount] = (int)round($this->formatPrice($amount) * 100); } return $transactionData; diff --git a/Model/OrderPlace.php b/Model/OrderPlace.php index 55230673..4a11a6c8 100644 --- a/Model/OrderPlace.php +++ b/Model/OrderPlace.php @@ -303,7 +303,7 @@ private function prepareQuote($quote) */ private function validateAmount(CartInterface $quote, Transaction $transaction) { - $quoteAmount = (int)($this->formatPrice($quote->getGrandTotal()) * 100); + $quoteAmount = (int)round($this->formatPrice($quote->getGrandTotal()) * 100); $vippsAmount = (int)$transaction->getTransactionInfo()->getAmount(); if ($quoteAmount != $vippsAmount) { From d60771115d376ea4d9bc1e6d3526a5f8aebf3eba Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Mon, 25 Feb 2019 15:27:49 +0100 Subject: [PATCH 121/121] Publication v.1.2.3 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 697e6444..7e6b0112 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "magento2-module", "description": "Vipps Payment Method", "license": "proprietary", - "version": "1.2.2", + "version": "1.2.3", "require": { "magento/framework": "101.0.*", "magento/module-sales": "101.0.*",