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..47f0a51e 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,45 +234,63 @@ 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; } $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($quote); + $this->prepareQuote($clonedQuote); + $clonedQuote->collectTotals(); - /** @var Quote $quote */ - $quote = $this->cartRepository->get($quote->getId()); - if ($quote->getReservedOrderId() !== $reservedOrderId) { - return null; + $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); } - // set quote active, collect totals and place order - $quote->setIsActive(true); - $quote->collectTotals(); - $this->validateAmount($quote, $transaction); - $orderId = $this->cartManagement->placeOrder($quote->getId()); + $clonedQuote->setReservedOrderId(null); + $this->cartRepository->save($clonedQuote); - $quote->setReservedOrderId(null); - $this->cartRepository->save($quote); + return $order; + } - 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); + } } /** @@ -287,6 +328,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 +383,7 @@ private function notify($order) * @param CartInterface $quote * @param Transaction $transaction * - * @throws LocalizedException + * @throws WrongAmountException */ private function validateAmount(CartInterface $quote, Transaction $transaction) { @@ -313,7 +391,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