From 54037caab36b15e82c5adecb8922cba7bf22aada Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 6 May 2021 18:14:18 +0300 Subject: [PATCH 1/5] VIPPS-383: Backport fixes to 2.2 --- Controller/Payment/Fallback.php | 119 ++++++++++++++------ Gateway/Validator/AvailabilityValidator.php | 2 + Model/QuoteRepository.php | 35 +++--- Model/ResourceModel/Quote.php | 42 +++++++ Model/TransactionProcessor.php | 62 ++++++++-- Observer/CheckoutSubmitAllAfter.php | 98 ++++++++++++++++ Observer/CheckoutSubmitBefore.php | 96 ++++++++++++++++ Observer/OrderPaymentAfter.php | 6 +- etc/adminhtml/system/cancellation.xml | 10 ++ etc/config.xml | 1 + etc/di.xml | 14 ++- etc/events.xml | 6 + etc/frontend/di.xml | 9 ++ 13 files changed, 436 insertions(+), 64 deletions(-) create mode 100644 Observer/CheckoutSubmitAllAfter.php create mode 100644 Observer/CheckoutSubmitBefore.php diff --git a/Controller/Payment/Fallback.php b/Controller/Payment/Fallback.php index be671d28..f2907449 100755 --- a/Controller/Payment/Fallback.php +++ b/Controller/Payment/Fallback.php @@ -19,16 +19,17 @@ use Magento\Checkout\Model\Session; use Magento\Framework\App\Action\Action; use Magento\Framework\App\Action\Context; -use Magento\Framework\App\Request\InvalidRequestException; use Magento\Framework\App\RequestInterface; +use Magento\Framework\App\Request\InvalidRequestException; use Magento\Framework\App\ResponseInterface; -use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Controller\ResultFactory; use Magento\Framework\Controller\ResultInterface; +use Magento\Framework\Controller\Result\Redirect; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Session\SessionManagerInterface; +use Magento\Payment\Gateway\ConfigInterface; use Magento\Quote\Api\CartRepositoryInterface; use Magento\Quote\Model\Quote; use Magento\Sales\Api\OrderManagementInterface; @@ -36,11 +37,11 @@ use Vipps\Payment\Api\Data\QuoteInterface; use Vipps\Payment\Api\QuoteRepositoryInterface; use Vipps\Payment\Gateway\Command\PaymentDetailsProvider; +use Vipps\Payment\Gateway\Exception\VippsException; use Vipps\Payment\Gateway\Transaction\Transaction; use Vipps\Payment\Model\Exception\AcquireLockException; use Vipps\Payment\Model\Gdpr\Compliance; use Vipps\Payment\Model\TransactionProcessor; -use Vipps\Payment\Gateway\Exception\VippsException; /** * Class Fallback @@ -94,6 +95,11 @@ class Fallback extends Action */ private $vippsQuote; + /** + * @var ConfigInterface + */ + private $config; + /** * Fallback constructor. * @@ -106,6 +112,7 @@ class Fallback extends Action * @param OrderManagementInterface $orderManagement * @param Compliance $compliance * @param LoggerInterface $logger + * @param ConfigInterface $config * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -118,7 +125,8 @@ public function __construct( QuoteRepositoryInterface $vippsQuoteRepository, OrderManagementInterface $orderManagement, Compliance $compliance, - LoggerInterface $logger + LoggerInterface $logger, + ConfigInterface $config ) { parent::__construct($context); $this->paymentDetailsProvider = $paymentDetailsProvider; @@ -129,6 +137,7 @@ public function __construct( $this->gdprCompliance = $compliance; $this->orderManagement = $orderManagement; $this->logger = $logger; + $this->config = $config; } /** @@ -137,6 +146,7 @@ public function __construct( */ public function execute() { + $transaction = null; /** @var Redirect $resultRedirect */ $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); try { @@ -156,9 +166,17 @@ public function execute() . ' Please contact to store administrator.') ); } finally { - $this->storeLastOrderOrRestoreQuote(); + $vippsQuote = $this->getVippsQuote(true); + $cartPersistence = $this->config->getValue('cancellation_cart_persistence'); + $transactionWasCancelled = $transaction && $transaction->transactionWasCancelled(); + + if ($transactionWasCancelled && $cartPersistence) { + $this->restoreQuote($vippsQuote); + } elseif ($vippsQuote->getOrderId()) { + $this->storeLastOrder($vippsQuote); + } if (isset($e)) { - if ($this->getVippsQuote()->getOrderId()) { + if (!$cartPersistence && $this->getVippsQuote()->getOrderId()) { $resultRedirect->setPath('checkout/onepage/failure', ['_secure' => true]); } else { $resultRedirect->setPath('checkout/cart', ['_secure' => true]); @@ -210,9 +228,8 @@ private function getVippsQuote($forceReload = false) * Method to update Checkout session with Last Placed Order * Or restore quote if order was not placed (ex. Express Checkout) */ - private function storeLastOrderOrRestoreQuote() + private function storeLastOrder(QuoteInterface $vippsQuote) { - $vippsQuote = $this->getVippsQuote(true); if ($vippsQuote->getOrderId()) { $this->checkoutSession ->clearStorage() @@ -223,16 +240,24 @@ private function storeLastOrderOrRestoreQuote() ->setLastOrderStatus( $this->orderManagement->getStatus($vippsQuote->getOrderId()) ); - } else { - $quote = $this->cartRepository->get($vippsQuote->getQuoteId()); + } + } + + /** + * @param QuoteInterface $vippsQuote + * + * @throws NoSuchEntityException + */ + private function restoreQuote(QuoteInterface $vippsQuote) + { + $quote = $this->cartRepository->get($vippsQuote->getQuoteId()); - /** @var Quote $quote */ - $quote->setIsActive(true); - $quote->setReservedOrderId(null); + /** @var Quote $quote */ + $quote->setIsActive(true); + $quote->setReservedOrderId(null); - $this->cartRepository->save($quote); - $this->checkoutSession->replaceQuote($quote); - } + $this->cartRepository->save($quote); + $this->checkoutSession->replaceQuote($quote); } /** @@ -243,11 +268,41 @@ private function storeLastOrderOrRestoreQuote() * @throws \Exception */ private function prepareResponse(Redirect $resultRedirect, Transaction $transaction) + { + $this->defineMessage($transaction); + $this->defineRedirectPath($resultRedirect, $transaction); + + return $resultRedirect; + } + + /** + * @param $e \Exception + * + * @return string + */ + private function enlargeMessage($e) + { + $quoteId = $this->checkoutSession->getQuoteId(); + $trace = $e->getTraceAsString(); + $message = $e->getMessage(); + + return "QuoteID: $quoteId. Exception message: $message. Stack Trace $trace"; + } + + /** + * @return mixed + */ + private function isCartPersistent() + { + return $this->config->getValue('cancellation_cart_persistence'); + } + + private function defineMessage(Transaction $transaction): void { if ($transaction->transactionWasCancelled()) { $this->messageManager->addWarningMessage(__('Your order was cancelled in Vipps.')); } elseif ($transaction->isTransactionReserved() || $transaction->isTransactionCaptured()) { - return $resultRedirect->setPath('checkout/onepage/success', ['_secure' => true]); + //$this->messageManager->addWarningMessage(__('Your order was successfully placed.')); } elseif ($transaction->isTransactionExpired()) { $this->messageManager->addErrorMessage( __('Transaction was expired. Please, place your order again') @@ -257,27 +312,25 @@ private function prepareResponse(Redirect $resultRedirect, Transaction $transact __('We have not received a confirmation that order was reserved. It will be checked later again.') ); } - - if ($this->getVippsQuote()->getOrderId()) { - $resultRedirect->setPath('checkout/onepage/failure', ['_secure' => true]); - } else { - $resultRedirect->setPath('checkout/cart', ['_secure' => true]); - } - - return $resultRedirect; } /** - * @param $e \Exception + * @param Redirect $resultRedirect + * @param Transaction $transaction * - * @return string + * @throws NoSuchEntityException */ - private function enlargeMessage($e) + private function defineRedirectPath(Redirect $resultRedirect, Transaction $transaction): void { - $quoteId = $this->checkoutSession->getQuoteId(); - $trace = $e->getTraceAsString(); - $message = $e->getMessage(); - - return "QuoteID: $quoteId. Exception message: $message. Stack Trace $trace"; + if ($transaction->isTransactionReserved() || $transaction->isTransactionCaptured()) { + $resultRedirect->setPath('checkout/onepage/success', ['_secure' => true]); + } else { + $orderId = $this->getVippsQuote() ? $this->getVippsQuote()->getOrderId() : null; + if (!$this->isCartPersistent() && $orderId) { + $resultRedirect->setPath('checkout/onepage/failure', ['_secure' => true]); + } else { + $resultRedirect->setPath('checkout/cart', ['_secure' => true]); + } + } } } diff --git a/Gateway/Validator/AvailabilityValidator.php b/Gateway/Validator/AvailabilityValidator.php index 2114f79d..ce3db64b 100644 --- a/Gateway/Validator/AvailabilityValidator.php +++ b/Gateway/Validator/AvailabilityValidator.php @@ -53,6 +53,8 @@ public function __construct( * * @return ResultInterface * @throws NoSuchEntityException + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function validate(array $validationSubject) { diff --git a/Model/QuoteRepository.php b/Model/QuoteRepository.php index 1272d894..80d8dff8 100644 --- a/Model/QuoteRepository.php +++ b/Model/QuoteRepository.php @@ -123,6 +123,26 @@ public function loadByQuote($quoteId) return $vippsQuote; } + /** + * Load monitoring quote by quote. + * + * @param $quoteId + * + * @return Quote + * @throws NoSuchEntityException + */ + public function loadNewByQuote($quoteId): QuoteInterface + { + $vippsQuote = $this->quoteFactory->create(); + $this->quoteResource->loadNewByQuote($vippsQuote, $quoteId, 'quote_id'); + + if (!$vippsQuote->getId()) { + throw NoSuchEntityException::singleField('quote_id', $quoteId); + } + + return $vippsQuote; + } + /** * @param string $reservedOrderId * @@ -138,21 +158,6 @@ public function loadByOrderId($reservedOrderId) throw NoSuchEntityException::singleField('reserved_order_id', $reservedOrderId); } - if (!$vippsQuote->getOrderId()) { - $order = $this->tryLocateOrder($reservedOrderId); - if ($order) { - try { - $vippsQuote->setOrderId((int)$order->getEntityId()); - if ($vippsQuote->getStatus() == QuoteInterface::STATUS_NEW) { - $vippsQuote->setStatus(QuoteInterface::STATUS_PENDING); - } - $vippsQuote = $this->save($vippsQuote); - } catch (\Throwable $t) { - $this->logger->error($t); - } - } - } - return $vippsQuote; } diff --git a/Model/ResourceModel/Quote.php b/Model/ResourceModel/Quote.php index 28f2389b..55689155 100644 --- a/Model/ResourceModel/Quote.php +++ b/Model/ResourceModel/Quote.php @@ -16,7 +16,10 @@ namespace Vipps\Payment\Model\ResourceModel; +use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Model\AbstractModel; use Magento\Framework\Model\ResourceModel\Db\AbstractDb; +use Vipps\Payment\Api\Data\QuoteInterface; /** * Class Quote @@ -42,4 +45,43 @@ protected function _construct() //@codingStandardsIgnoreLine { $this->_init(self::TABLE_NAME, self::INDEX_FIELD); } + + /** + * @param AbstractModel $object + * @param $value + * @param null $field + * + * @return $this + * @throws LocalizedException + */ + public function loadNewByQuote(AbstractModel $object, $value, $field = null) + { + $object->beforeLoad($value, $field); + if ($field === null) { + $field = $this->getIdFieldName(); + } + + $connection = $this->getConnection(); + if ($connection && $value !== null) { + $connection = $this->getConnection(); + $select = $connection->select() + ->from($this->getMainTable()) + ->where("$field = ?", $value) + ->where('status = ?', QuoteInterface::STATUS_NEW) + ->limit(1); + $data = $connection->fetchRow($select); + + if ($data) { + $object->setData($data); + } + } + + $this->unserializeFields($object); + $this->_afterLoad($object); + $object->afterLoad(); + $object->setOrigData(); + $object->setHasDataChanges(false); + + return $this; + } } diff --git a/Model/TransactionProcessor.php b/Model/TransactionProcessor.php index dab2fe1f..7aade8f5 100644 --- a/Model/TransactionProcessor.php +++ b/Model/TransactionProcessor.php @@ -16,11 +16,12 @@ namespace Vipps\Payment\Model; +use Magento\Framework\App\ResourceConnection; use Magento\Framework\Exception\AlreadyExistsException; use Magento\Framework\Exception\CouldNotSaveException; use Magento\Framework\Exception\InputException; -use Magento\Framework\Exception\NoSuchEntityException; use Magento\Framework\Exception\LocalizedException; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Payment\Gateway\ConfigInterface; use Magento\Payment\Helper\Formatter; use Magento\Quote\Api\CartManagementInterface; @@ -39,8 +40,8 @@ use Vipps\Payment\Api\Data\QuoteStatusInterface; use Vipps\Payment\Gateway\Command\PaymentDetailsProvider; use Vipps\Payment\Gateway\Exception\VippsException; -use Vipps\Payment\Gateway\Transaction\Transaction; use Vipps\Payment\Gateway\Exception\WrongAmountException; +use Vipps\Payment\Gateway\Transaction\Transaction; use Vipps\Payment\Model\Adminhtml\Source\PaymentAction; use Vipps\Payment\Model\Exception\AcquireLockException; @@ -112,6 +113,11 @@ class TransactionProcessor */ private $logger; + /** + * @var ResourceConnection + */ + private $resourceConnection; + /** * TransactionProcessor constructor. * @@ -127,6 +133,7 @@ class TransactionProcessor * @param OrderManagementInterface $orderManagement * @param PaymentDetailsProvider $paymentDetailsProvider * @param LoggerInterface $logger + * @param ResourceConnection $resourceConnection * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -142,7 +149,8 @@ public function __construct( QuoteManagement $quoteManagement, OrderManagementInterface $orderManagement, PaymentDetailsProvider $paymentDetailsProvider, - LoggerInterface $logger + LoggerInterface $logger, + ResourceConnection $resourceConnection ) { $this->orderRepository = $orderRepository; $this->cartRepository = $cartRepository; @@ -156,6 +164,7 @@ public function __construct( $this->orderManagement = $orderManagement; $this->paymentDetailsProvider = $paymentDetailsProvider; $this->logger = $logger; + $this->resourceConnection = $resourceConnection; } /** @@ -202,12 +211,13 @@ public function process(QuoteInterface $vippsQuote) * @param Transaction $transaction * * @throws CouldNotSaveException + * @throw \Exception * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ private function processCancelledTransaction(QuoteInterface $vippsQuote, Transaction $transaction) //@codingStandardsIgnoreLine { if ($vippsQuote->getOrderId()) { - $this->orderManagement->cancel($vippsQuote->getOrderId()); + $this->cancelOrder($vippsQuote->getOrderId()); } $vippsQuote->setStatus(QuoteStatusInterface::STATUS_CANCELED); @@ -230,7 +240,7 @@ private function processReservedTransaction(QuoteInterface $vippsQuote, Transact if ($vippsQuote->getOrderId()) { $order = $this->orderRepository->get($vippsQuote->getOrderId()); } else { - $order = $this->placeOrder($transaction); + $order = $this->placeOrder($vippsQuote, $transaction); } $paymentAction = $this->config->getValue('vipps_payment_action'); @@ -307,7 +317,6 @@ private function acquireLock($reservedOrderId) throw new AcquireLockException( __('Can not acquire lock for order "%1"', $reservedOrderId) ); - } return $lockName; @@ -325,14 +334,22 @@ private function acquireLock($reservedOrderId) * @throws WrongAmountException * @throws \Exception */ - private function placeOrder(Transaction $transaction) + private function placeOrder(QuoteInterface $vippsQuote, Transaction $transaction) { - $quote = $this->quoteLocator->get($transaction->getOrderId()); + $quote = $this->cartRepository->get($vippsQuote->getQuoteId()); if (!$quote) { throw new \Exception( //@codingStandardsIgnoreLine - __('Could not place order. Could not find quote with such reserved order id.') + __('Could not place order. Could not find quote.') ); } + + if ($vippsQuote->getReservedOrderId() + && $quote->getReservedOrderId() !== $vippsQuote->getReservedOrderId() + ) { + $quote->setReservedOrderId($vippsQuote->getReservedOrderId()); + $this->cartRepository->save($quote); + } + if (!$quote->getReservedOrderId() || $quote->getReservedOrderId() !== $transaction->getOrderId()) { throw new \Exception( //@codingStandardsIgnoreLine __('Quote reserved order id does not match Vipps transaction order id.') @@ -482,4 +499,31 @@ private function notify(OrderInterface $order) $this->orderManagement->notify($order->getEntityId()); } } + + /** + * @param int $orderId + * + * @throws \Exception + */ + private function cancelOrder($orderId): void + { + $order = $this->orderRepository->get($orderId); + if ($order->getState() === Order::STATE_NEW) { + $this->orderManagement->cancel($orderId); + } else { + $connection = $this->resourceConnection->getConnection(); + try { + $connection->beginTransaction(); + + $order->setState(Order::STATE_NEW); + $this->orderRepository->save($order); + $this->orderManagement->cancel($orderId); + + $connection->commit(); + } catch (\Exception $e) { + $connection->rollBack(); + throw $e; + } + } + } } diff --git a/Observer/CheckoutSubmitAllAfter.php b/Observer/CheckoutSubmitAllAfter.php new file mode 100644 index 00000000..7f520f29 --- /dev/null +++ b/Observer/CheckoutSubmitAllAfter.php @@ -0,0 +1,98 @@ +orderManagement = $orderManagement; + $this->quoteRepository = $quoteRepository; + $this->logger = $logger; + } + + /** + * Marks vipps quote as placed after order placed + * + * @param Observer $observer + */ + public function execute(Observer $observer) + { + /** @var OrderInterface $order */ + $order = $observer->getData('order'); + if (!$order || !($order instanceof OrderInterface)) { + return; + } + + $payment = $order->getPayment(); + if (!$payment || !($payment instanceof OrderPaymentInterface)) { + return; + } + + if ('vipps' == $payment->getMethod()) { + try { + // updated vipps quote + $vippsQuote = $this->quoteRepository->loadByOrderId($order->getIncrementId()); + $vippsQuote->setOrderId((int)$order->getEntityId()); + $vippsQuote->setStatus(QuoteInterface::STATUS_PENDING); + $this->quoteRepository->save($vippsQuote); + } catch (\Throwable $t) { + $this->logger->error($t->getMessage()); + } + } + } +} diff --git a/Observer/CheckoutSubmitBefore.php b/Observer/CheckoutSubmitBefore.php new file mode 100644 index 00000000..92c88c05 --- /dev/null +++ b/Observer/CheckoutSubmitBefore.php @@ -0,0 +1,96 @@ +cartRepository = $cartRepository; + $this->quoteRepository = $quoteRepository; + $this->logger = $logger; + } + + /** + * @param Observer $observer + */ + public function execute(Observer $observer) + { + /** @var CartInterface $quote */ + $quote = $observer->getData('quote'); + if (!$quote || !($quote instanceof CartInterface)) { + return; + } + + $payment = $quote->getPayment(); + if (!$payment || !($payment instanceof PaymentInterface)) { + return; + } + + if ('vipps' == $payment->getMethod()) { + try { + $vippsQuote = $this->quoteRepository->loadNewByQuote($quote->getId()); + + if ($quote->getReservedOrderId() !== $vippsQuote->getReservedOrderId()) { + $quote->setReservedOrderId($vippsQuote->getReservedOrderId()); + $this->cartRepository->save($quote); + } + } catch (\Throwable $t) { + $this->logger->error($t->getMessage()); + } + } + } +} diff --git a/Observer/OrderPaymentAfter.php b/Observer/OrderPaymentAfter.php index efedb344..28d391a7 100644 --- a/Observer/OrderPaymentAfter.php +++ b/Observer/OrderPaymentAfter.php @@ -63,15 +63,13 @@ public function execute(Observer $observer) if (!$payment || $payment->getMethod() != 'vipps') { return; } - + $order = $payment->getOrder(); - + $status = $this->config->getValue('order_status'); if ($status == OrderStatus::STATUS_PAYMENT_REVIEW) { $order->setState(Order::STATE_PAYMENT_REVIEW); $order->setStatus(Order::STATE_PAYMENT_REVIEW); } - - return; } } diff --git a/etc/adminhtml/system/cancellation.xml b/etc/adminhtml/system/cancellation.xml index 78e17184..4507fd5a 100644 --- a/etc/adminhtml/system/cancellation.xml +++ b/etc/adminhtml/system/cancellation.xml @@ -18,6 +18,16 @@ + + + Magento\Config\Model\Config\Source\Yesno + + + + payment/vipps/cancellation_cart_persistence + + diff --git a/etc/config.xml b/etc/config.xml index 86c95002..c88eafd4 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -39,6 +39,7 @@ + 1 auto 3 10 diff --git a/etc/di.xml b/etc/di.xml index 4e3f80df..50b531bc 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -114,9 +114,6 @@ - VippsInitiateCommand VippsCaptureCommand VippsRefundCommand @@ -404,4 +401,15 @@ Vipps\Payment\Model\Logger + + + + Vipps\Payment\Model\Logger + + + + + Vipps\Payment\Model\Logger + + diff --git a/etc/events.xml b/etc/events.xml index fedd46de..343b932e 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -23,4 +23,10 @@ + + + + + + diff --git a/etc/frontend/di.xml b/etc/frontend/di.xml index be43af28..0491d0b7 100755 --- a/etc/frontend/di.xml +++ b/etc/frontend/di.xml @@ -79,4 +79,13 @@ + + + + + + + Vipps\Payment\Gateway\Config\Config + + From 81f6843dc1789c17f489ea16a36d3b83aa2fdb3d Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 6 May 2021 18:56:42 +0300 Subject: [PATCH 2/5] VIPPS-383: Backport fixes to 2.2 --- .../System/Config/TestCredentials.php | 87 +++++++++++++ .../System/Config/TestCredentials.php | 122 ++++++++++++++++++ Model/TokenProvider.php | 2 +- etc/adminhtml/system.xml | 8 ++ view/adminhtml/requirejs-config.js | 22 ++++ .../system/config/testcredentials.phtml | 24 ++++ view/adminhtml/web/js/testcredentials.js | 86 ++++++++++++ 7 files changed, 350 insertions(+), 1 deletion(-) create mode 100644 Block/Adminhtml/System/Config/TestCredentials.php create mode 100644 Controller/Adminhtml/System/Config/TestCredentials.php create mode 100644 view/adminhtml/requirejs-config.js create mode 100644 view/adminhtml/templates/system/config/testcredentials.phtml create mode 100644 view/adminhtml/web/js/testcredentials.js diff --git a/Block/Adminhtml/System/Config/TestCredentials.php b/Block/Adminhtml/System/Config/TestCredentials.php new file mode 100644 index 00000000..2ad2c401 --- /dev/null +++ b/Block/Adminhtml/System/Config/TestCredentials.php @@ -0,0 +1,87 @@ +setTemplate('Vipps_Payment::system/config/testcredentials.phtml'); + return $this; + } + + /** + * Unset some non-related element parameters + * + * @param \Magento\Framework\Data\Form\Element\AbstractElement $element + * @return string + * @since 100.1.0 + */ + public function render(\Magento\Framework\Data\Form\Element\AbstractElement $element) + { + $element = clone $element; + $element->unsScope()->unsCanUseWebsiteValue()->unsCanUseDefaultValue(); + return parent::render($element); + } + + /** + * Get the button and scripts contents + * + * @param \Magento\Framework\Data\Form\Element\AbstractElement $element + * @return string + * @since 100.1.0 + */ + protected function _getElementHtml(\Magento\Framework\Data\Form\Element\AbstractElement $element) + { + $originalData = $element->getOriginalData(); + $this->addData( + [ + 'button_label' => __($originalData['button_label']), + 'html_id' => $element->getHtmlId(), + 'ajax_url' => $this->getUrl('vipps/system_config/testcredentials'), + 'field_mapping' => str_replace('"', '\\"', json_encode($this->_getFieldMapping())) + ] + ); + + return $this->_toHtml(); + } + + /** + * Returns configuration fields required to perform the ping request + * + * @return array + * @since 100.1.0 + */ + protected function _getFieldMapping() + { + return [ + 'store_switcher' => 'store_switcher', + 'store_group_switcher' => 'store_group_switcher', + 'website_switcher' => 'website_switcher', + ]; + } +} diff --git a/Controller/Adminhtml/System/Config/TestCredentials.php b/Controller/Adminhtml/System/Config/TestCredentials.php new file mode 100644 index 00000000..8fa13352 --- /dev/null +++ b/Controller/Adminhtml/System/Config/TestCredentials.php @@ -0,0 +1,122 @@ +resultJsonFactory = $resultJsonFactory; + $this->tagFilter = $tagFilter; + $this->tokenProvider = $tokenProvider; + $this->storeManager = $storeManager; + } + + /** + * Check for connection to server + * + * @return \Magento\Framework\Controller\Result\Json + */ + public function execute() + { + $result = [ + 'success' => false, + 'errorMessage' => '', + ]; + + try { + $options = $this->getRequest()->getParams(); + + $storeId = $options['store_switcher'] ?? 0; + $storeGroupId = $options['store_group_switcher'] ?? 0; + $websiteId = $options['website_switcher'] ?? 0; + + if ($storeId) { + $this->storeManager->setCurrentStore($storeId); + } elseif ($storeGroupId) { + $storeId = $this->storeManager->getGroup($storeGroupId)->getDefaultStoreId(); + $this->storeManager->setCurrentStore($storeId); + } elseif ($websiteId) { + $storeGroupId = $this->storeManager->getWebsite($websiteId)->getDefaultGroupId(); + $storeId = $this->storeManager->getGroup($storeGroupId)->getDefaultStoreId(); + $this->storeManager->setCurrentStore($storeId); + } + + $this->tokenProvider->regenerate(); + $result['success'] = true; + } catch (\Throwable $t) { + $message = $t->getMessage(); + if ($t->getPrevious()) { + $message .= PHP_EOL . $t->getPrevious()->getMessage(); + } + + $result['errorMessage'] = $this->tagFilter->filter($message); + } + + /** @var \Magento\Framework\Controller\Result\Json $resultJson */ + $resultJson = $this->resultJsonFactory->create(); + return $resultJson->setData($result); + } +} diff --git a/Model/TokenProvider.php b/Model/TokenProvider.php index e2534b56..0eaf1e13 100644 --- a/Model/TokenProvider.php +++ b/Model/TokenProvider.php @@ -192,7 +192,7 @@ private function readJwt() $this->logger->debug('Token fetched from Vipps'); } catch (\Exception $e) { //@codingStandardsIgnoreLine $this->logger->critical($e->getMessage()); - throw new AuthenticationException(__('Can\'t retrieve access token from Vipps.')); + throw new AuthenticationException(__('Can\'t retrieve access token from Vipps.'), $e); } return $jwt; } diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml index 33132de6..88282e88 100644 --- a/etc/adminhtml/system.xml +++ b/etc/adminhtml/system.xml @@ -97,6 +97,14 @@ payment/vipps/subscription_key1 Magento\Config\Model\Config\Backend\Encrypted + + diff --git a/view/adminhtml/requirejs-config.js b/view/adminhtml/requirejs-config.js new file mode 100644 index 00000000..7b00367d --- /dev/null +++ b/view/adminhtml/requirejs-config.js @@ -0,0 +1,22 @@ +/** + * Copyright 2021 Vipps + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +var config = { + map: { + '*': { + testVippsCredentials: 'Vipps_Payment/js/testcredentials' + } + } +}; diff --git a/view/adminhtml/templates/system/config/testcredentials.phtml b/view/adminhtml/templates/system/config/testcredentials.phtml new file mode 100644 index 00000000..cb11c941 --- /dev/null +++ b/view/adminhtml/templates/system/config/testcredentials.phtml @@ -0,0 +1,24 @@ + + diff --git a/view/adminhtml/web/js/testcredentials.js b/view/adminhtml/web/js/testcredentials.js new file mode 100644 index 00000000..c10d6c5d --- /dev/null +++ b/view/adminhtml/web/js/testcredentials.js @@ -0,0 +1,86 @@ +/** + * Copyright 2021 Vipps + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED + * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/** + * @api + */ +define([ + 'jquery', + 'Magento_Ui/js/modal/alert', + 'jquery/ui' +], function ($, alert) { + 'use strict'; + + $.widget('mage.testVippsCredentials', { + options: { + url: '', + elementId: '', + successText: '', + failedText: '', + fieldMapping: '' + }, + + /** + * Bind handlers to events + */ + _create: function () { + this._on({ + 'click': $.proxy(this._connect, this) + }); + }, + + /** + * Method triggers an AJAX request to check search engine connection + * @private + */ + _connect: function () { + var result = this.options.failedText, + element = $('#' + this.options.elementId), + self = this, + params = {}, + msg = '', + fieldToCheck = this.options.fieldToCheck || 'success'; + + element.removeClass('success').addClass('fail'); + params['form_key'] = FORM_KEY; + $.each($.parseJSON(this.options.fieldMapping), function (key, el) { + params[key] = $('#' + el).val(); + }); + $.ajax({ + url: this.options.url, + showLoader: true, + data: params, + headers: this.options.headers || {} + }).done(function (response) { + if (response[fieldToCheck]) { + element.removeClass('fail').addClass('success'); + result = self.options.successText; + } else { + msg = response.errorMessage; + + if (msg) { + alert({ + content: msg + }); + } + } + }).always(function () { + $('#' + self.options.elementId + '_result').text(result); + }); + } + }); + + return $.mage.testVippsCredentials; +}); From 42b0732fea6af53f068d8050beff15b141cd95ab Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 6 May 2021 18:58:18 +0300 Subject: [PATCH 3/5] VIPPS-383: Backport fixes to 2.2 --- Model/QuoteManagement.php | 14 +++++++------- Setup/UpgradeSchema.php | 30 ++++++++++++++++++++++++++++++ etc/module.xml | 2 +- 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/Model/QuoteManagement.php b/Model/QuoteManagement.php index b0a94dc4..9bf32151 100644 --- a/Model/QuoteManagement.php +++ b/Model/QuoteManagement.php @@ -1,6 +1,6 @@ quoteFactory->create(); + /** @var Quote $vippsQuote */ + $vippsQuote = $this->quoteFactory->create(); - $monitoringQuote + $vippsQuote ->setQuoteId($cart->getId()) ->setStoreId($cart->getStoreId()) ->setReservedOrderId($cart->getReservedOrderId()); - return $this->quoteRepository->save($monitoringQuote); + return $this->quoteRepository->save($vippsQuote); } /** diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index dbe9c347..f4edd08f 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -16,8 +16,10 @@ namespace Vipps\Payment\Setup; +use Magento\Framework\DB\Adapter\AdapterInterface; use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Setup\ModuleContextInterface; +use Magento\Framework\Setup\ModuleDataSetupInterface; use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Framework\Setup\UpgradeSchemaInterface; @@ -79,6 +81,10 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con ); } + if (version_compare($context->getVersion(), '2.3.1', '<')) { + $this->addVippsQuoteIndex($installer); + } + $installer->endSetup(); } @@ -262,4 +268,28 @@ private function addStoreIdToQuote(SchemaSetupInterface $installer) ] ); } + + /** + * @param SchemaSetupInterface $installer + */ + private function addVippsQuoteIndex(SchemaSetupInterface $installer) + { + $connection = $installer->getConnection(); + $tableName = $installer->getTable('vipps_quote'); + + $connection + ->addIndex( + $tableName, + $connection->getIndexName($tableName, 'reserved_order_id'), + 'reserved_order_id', + AdapterInterface::INDEX_TYPE_UNIQUE + ); + $connection + ->addIndex( + $tableName, + $connection->getIndexName($tableName, 'order_id'), + 'order_id', + AdapterInterface::INDEX_TYPE_UNIQUE + ); + } } diff --git a/etc/module.xml b/etc/module.xml index 8d4efd5d..af60b515 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -14,7 +14,7 @@ ~ IN THE SOFTWARE. --> - + From 4c59db40a12528ff1fdcc11ec2964502930b2d8d Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 6 May 2021 19:00:05 +0300 Subject: [PATCH 4/5] Bump module version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4c81a61b..d4f3dfe6 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "type": "magento2-module", "description": "Vipps Payment Method", "license": "proprietary", - "version": "2.2.12", + "version": "2.2.13", "require": { "magento/framework": "101.0.*", "magento/module-sales": "101.0.*", From 9924b6eaf9f67bc1adf09dbf7617e217b77bc923 Mon Sep 17 00:00:00 2001 From: Volodymyr Klymenko Date: Thu, 6 May 2021 19:04:36 +0300 Subject: [PATCH 5/5] VIPPS-383: Backport fixes to 2.2 --- Observer/CheckoutSubmitAllAfter.php | 1 - Observer/CheckoutSubmitBefore.php | 1 - 2 files changed, 2 deletions(-) diff --git a/Observer/CheckoutSubmitAllAfter.php b/Observer/CheckoutSubmitAllAfter.php index 7f520f29..b235e82a 100644 --- a/Observer/CheckoutSubmitAllAfter.php +++ b/Observer/CheckoutSubmitAllAfter.php @@ -13,7 +13,6 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -declare(strict_types=1); namespace Vipps\Payment\Observer; diff --git a/Observer/CheckoutSubmitBefore.php b/Observer/CheckoutSubmitBefore.php index 92c88c05..c189c154 100644 --- a/Observer/CheckoutSubmitBefore.php +++ b/Observer/CheckoutSubmitBefore.php @@ -13,7 +13,6 @@ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -declare(strict_types=1); namespace Vipps\Payment\Observer;