diff --git a/Block/PaymentFee/Sales/Creditmemo.php b/Block/PaymentFee/Sales/Creditmemo.php new file mode 100644 index 00000000000..48e2b0ca559 --- /dev/null +++ b/Block/PaymentFee/Sales/Creditmemo.php @@ -0,0 +1,33 @@ +getParentBlock(); + $creditmemo = $parentBlock->getCreditmemo(); + + if (!(int)$creditmemo->getBaseMolliePaymentFee()) { + return; + } + + $total = new DataObject([ + 'code' => 'mollie_payment_fee', + 'value' => $creditmemo->getMolliePaymentFee() + $creditmemo->getMolliePaymentFeeTax(), + 'label' => __('Payment Fee'), + ]); + + $parentBlock->addTotalBefore($total, 'tax'); + } +} diff --git a/Block/PaymentFee/Sales/CreditmemoNew.php b/Block/PaymentFee/Sales/CreditmemoNew.php new file mode 100644 index 00000000000..298b788233b --- /dev/null +++ b/Block/PaymentFee/Sales/CreditmemoNew.php @@ -0,0 +1,54 @@ +creditmemoService = $creditmemoService; + } + + public function initTotals() + { + /** @var Totals $parentBlock */ + $parentBlock = $this->getParentBlock(); + $order = $parentBlock->getOrder(); + $creditmemo = $parentBlock->getCreditmemo(); + + if (!($order->getMolliePaymentFee() + $order->getMolliePaymentFeeTax())) { + return; + } + + if (!$this->creditmemoService->isFullOrLastPartialCreditmemo($creditmemo)) { + return; + } + + $total = new DataObject([ + 'code' => 'mollie_payment_fee', + 'value' => $order->getMolliePaymentFee() + $order->getMolliePaymentFeeTax(), + 'label' => __('Payment Fee'), + ]); + + $parentBlock->addTotalBefore($total, 'tax'); + } +} diff --git a/Block/PaymentFee/Sales/Order.php b/Block/PaymentFee/Sales/Order.php new file mode 100644 index 00000000000..f77df6eec77 --- /dev/null +++ b/Block/PaymentFee/Sales/Order.php @@ -0,0 +1,33 @@ +getParentBlock(); + $order = $parentBlock->getOrder(); + + if (!($order->getMolliePaymentFee() + $order->getMolliePaymentFeeTax())) { + return; + } + + $total = new DataObject([ + 'code' => 'mollie_payment_fee', + 'value' => $order->getMolliePaymentFee() + $order->getMolliePaymentFeeTax(), + 'label' => __('Payment Fee'), + ]); + + $parentBlock->addTotalBefore($total, 'tax'); + } +} diff --git a/Config.php b/Config.php new file mode 100644 index 00000000000..843e3300fd3 --- /dev/null +++ b/Config.php @@ -0,0 +1,95 @@ +config = $config; + } + + /** + * @param $path + * @param $storeId + * @return string + */ + private function getPath($path, $storeId) + { + return $this->config->getValue($path, ScopeInterface::SCOPE_STORE, $storeId); + } + + public function statusPendingBanktransfer($storeId = null) + { + return $this->config->getValue( + static::XML_PATH_STATUS_PENDING_BANKTRANSFER, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + public function statusNewPaymentLink($storeId = null) + { + return $this->config->getValue( + static::XML_PATH_STATUS_NEW_PAYMENT_LINK, + ScopeInterface::SCOPE_STORE, + $storeId + ); + } + + /** + * @param int|null $storeId + * @return string + */ + public function klarnaPaylaterPaymentSurcharge($storeId = null) + { + return $this->getPath(static::PAYMENT_KLARNAPAYLATER_PAYMENT_SURCHARGE, $storeId); + } + + /** + * @param int|null $storeId + * @return string + */ + public function klarnaPaylaterPaymentSurchargeTaxClass($storeId = null) + { + return $this->getPath(static::PAYMENT_KLARNAPAYLATER_PAYMENT_SURCHARGE_TAX_CLASS, $storeId); + } + + /** + * @param int|null $storeId + * @return string + */ + public function klarnaSliceitPaymentSurcharge($storeId = null) + { + return $this->getPath(static::PAYMENT_KLARNASLICEIT_PAYMENT_SURCHARGE, $storeId); + } + + /** + * @param int|null $storeId + * @return string + */ + public function klarnaSliceitPaymentSurchargeTaxClass($storeId = null) + { + return $this->getPath(static::PAYMENT_KLARNASLICEIT_PAYMENT_SURCHARGE_TAX_CLASS, $storeId); + } +} diff --git a/Controller/Checkout/Redirect.php b/Controller/Checkout/Redirect.php index 6731a4d7f4d..54b58de955e 100644 --- a/Controller/Checkout/Redirect.php +++ b/Controller/Checkout/Redirect.php @@ -9,6 +9,7 @@ use Exception; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Payment\Helper\Data as PaymentHelper; +use Magento\Payment\Model\MethodInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderManagementInterface; use Magento\Store\Model\ScopeInterface; @@ -121,11 +122,11 @@ public function execute() $this->checkoutSession->restoreQuote(); $this->_redirect('checkout/cart'); } - } catch (Exception $e) { - $this->messageManager->addExceptionMessage($e, __($e->getMessage())); - $this->mollieHelper->addTolog('error', $e->getMessage()); + } catch (Exception $exception) { + $this->formatExceptionMessage($exception, $methodInstance); + $this->mollieHelper->addTolog('error', $exception->getMessage()); $this->checkoutSession->restoreQuote(); - $this->cancelUnprocessedOrder($order, $e->getMessage()); + $this->cancelUnprocessedOrder($order, $exception->getMessage()); $this->_redirect('checkout/cart'); } } @@ -158,4 +159,25 @@ private function cancelUnprocessedOrder(OrderInterface $order, $message) $this->mollieHelper->addToLog('error', $message); } } + + /** + * @param Exception $exception + * @param MethodInterface $methodInstance + */ + private function formatExceptionMessage(Exception $exception, MethodInterface $methodInstance) + { + if (stripos($exception->getMessage(), 'cURL error 28') !== false) { + $this->messageManager->addErrorMessage( + __( + 'A Timeout while connecting to %1 occurred, this could be the result of an outage. ' . + 'Please try again or select another payment method.', + $methodInstance->getTitle() + ) + ); + + return; + } + + $this->messageManager->addExceptionMessage($exception, __($exception->getMessage())); + } } diff --git a/Helper/General.php b/Helper/General.php index f717fa9c95c..eb8c9a88e2d 100755 --- a/Helper/General.php +++ b/Helper/General.php @@ -18,9 +18,10 @@ use Magento\Sales\Model\OrderRepository; use Magento\Store\Model\Information; use Magento\Store\Model\StoreManagerInterface; -use Magento\Config\Model\ResourceModel\Config; +use Magento\Config\Model\ResourceModel\Config as ResourceConfig; use Magento\Payment\Helper\Data as PaymentHelper; use Mollie\Api\Resources\Order as MollieOrder; +use Mollie\Payment\Config; use Mollie\Payment\Logger\MollieLogger; use Magento\SalesRule\Model\Coupon; use Magento\SalesRule\Model\ResourceModel\Coupon\Usage as CouponUsage; @@ -30,6 +31,7 @@ * Class General * * @package Mollie\Payment\Helper + * @deprecated These helper classes will be removed in a future release. */ class General extends AbstractHelper { @@ -96,7 +98,7 @@ class General extends AbstractHelper */ private $storeManager; /** - * @var Config + * @var ResourceConfig */ private $resourceConfig; /** @@ -143,6 +145,10 @@ class General extends AbstractHelper * @var OrderManagementInterface */ private $orderManagement; + /** + * @var Config + */ + private $config; /** * General constructor. @@ -151,7 +157,7 @@ class General extends AbstractHelper * @param PaymentHelper $paymentHelper * @param OrderRepository $orderRepository * @param StoreManagerInterface $storeManager - * @param Config $resourceConfig + * @param ResourceConfig $resourceConfig * @param ModuleListInterface $moduleList * @param ProductMetadataInterface $metadata * @param Resolver $resolver @@ -161,13 +167,14 @@ class General extends AbstractHelper * @param CouponUsage $couponUsage * @param OrderCommentHistory $orderCommentHistory * @param OrderManagementInterface $orderManagement + * @param Config $config */ public function __construct( Context $context, PaymentHelper $paymentHelper, OrderRepository $orderRepository, StoreManagerInterface $storeManager, - Config $resourceConfig, + ResourceConfig $resourceConfig, ModuleListInterface $moduleList, ProductMetadataInterface $metadata, Resolver $resolver, @@ -176,7 +183,8 @@ public function __construct( Coupon $coupon, CouponUsage $couponUsage, OrderCommentHistory $orderCommentHistory, - OrderManagementInterface $orderManagement + OrderManagementInterface $orderManagement, + Config $config ) { $this->paymentHelper = $paymentHelper; $this->storeManager = $storeManager; @@ -191,8 +199,9 @@ public function __construct( $this->coupon = $coupon; $this->couponUsage = $couponUsage; $this->orderCommentHistory = $orderCommentHistory; - parent::__construct($context); $this->orderManagement = $orderManagement; + $this->config = $config; + parent::__construct($context); } /** @@ -455,12 +464,14 @@ public function getStatusProcessing($storeId = 0) * Selected pending (payment) status for banktransfer * * @param int $storeId + * @deprecated + * @see Config::statusPendingBanktransfer() * * @return mixed */ public function getStatusPendingBanktransfer($storeId = 0) { - return $this->getStoreConfig(self::XML_PATH_STATUS_PENDING_BANKTRANSFER, $storeId); + return $this->config->statusPendingBanktransfer($storeId); } /** diff --git a/Model/Adminhtml/Backend/VerifiyPaymentFee.php b/Model/Adminhtml/Backend/VerifiyPaymentFee.php new file mode 100644 index 00000000000..96cd601ca7e --- /dev/null +++ b/Model/Adminhtml/Backend/VerifiyPaymentFee.php @@ -0,0 +1,65 @@ +priceCurrency = $priceCurrency; + } + + public function beforeSave() + { + $value = $this->getValue(); + $this->setValue(str_replace(',', '.', $value)); + + if ((double)$value > static::MAXIMUM_PAYMENT_FEE_AMOUNT) { + $message = __( + 'Please make sure the payment surcharge does not exceed %1.', + $this->priceCurrency->format(static::MAXIMUM_PAYMENT_FEE_AMOUNT) + ); + + throw new ValidatorException($message); + } + } +} diff --git a/Model/Adminhtml/Source/NewStatus.php b/Model/Adminhtml/Source/NewStatus.php new file mode 100644 index 00000000000..2478b242035 --- /dev/null +++ b/Model/Adminhtml/Source/NewStatus.php @@ -0,0 +1,17 @@ +loadMollieApi($apiKey); - $mollieApi->orders->cancel($transactionId); + + $mollieOrder = $mollieApi->orders->get($transactionId); + if ($mollieOrder->status != 'expired') { + $mollieApi->orders->cancel($transactionId); + } } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); throw new LocalizedException( @@ -617,7 +621,7 @@ public function createShipment(Order\Shipment $shipment, Order $order) $shipment->setMollieShipmentId($mollieShipmentId); /** - * Check if Transactions needs to be captures (eg. Klarna methods) + * Check if Transactions needs to be captured (eg. Klarna methods) */ $payment = $order->getPayment(); if (!$payment->getIsTransactionClosed()) { @@ -626,8 +630,9 @@ public function createShipment(Order\Shipment $shipment, Order $order) $invoice = $this->partialInvoice->createFromShipment($shipment); } - $captureAmount = $this->getCaptureAmount($order, $invoice); + $captureAmount = $this->getCaptureAmount($order, $invoice->getId() ? $invoice : null); + $payment->setTransactionId($transactionId); $payment->registerCaptureNotification($captureAmount, true); $this->orderRepository->save($order); diff --git a/Model/Methods/Paymentlink.php b/Model/Methods/Paymentlink.php index dfbabdc69ab..add70a6c9e9 100644 --- a/Model/Methods/Paymentlink.php +++ b/Model/Methods/Paymentlink.php @@ -68,6 +68,10 @@ public function initialize($paymentAction, $stateObject) $order->setCanSendNewEmailFlag(false); $order->save(); + if ($status = $this->config->statusNewPaymentLink($order->getStoreId())) { + $stateObject->setStatus($status); + } + $this->startTransaction($order); } diff --git a/Model/Mollie.php b/Model/Mollie.php index fdc05a09466..7d73883bf5f 100644 --- a/Model/Mollie.php +++ b/Model/Mollie.php @@ -23,11 +23,14 @@ use Magento\Sales\Model\ResourceModel\Order\CollectionFactory as OrderFactory; use Magento\Framework\Api\SearchCriteriaBuilder; use Magento\Checkout\Model\Session as CheckoutSession; +use Mollie\Payment\Config; +use Mollie\Api\MollieApiClient; use Mollie\Payment\Helper\General as MollieHelper; use Mollie\Payment\Model\Client\Orders as OrdersApi; use Mollie\Payment\Model\Client\Payments as PaymentsApi; use Magento\Framework\Exception\LocalizedException; use Magento\Framework\View\Asset\Repository as AssetRepository; +use Mollie\Payment\Service\Mollie\Timeout; /** * Class Mollie @@ -67,6 +70,10 @@ class Mollie extends AbstractMethod * @var bool */ protected $_canUseInternal = false; + /** + * @var Config + */ + protected $config; /** * @var array */ @@ -111,6 +118,10 @@ class Mollie extends AbstractMethod * @var ResourceConnection */ private $resourceConnection; + /** + * @var Timeout + */ + private $timeout; /** * Mollie constructor. @@ -131,6 +142,8 @@ class Mollie extends AbstractMethod * @param SearchCriteriaBuilder $searchCriteriaBuilder * @param AssetRepository $assetRepository * @param ResourceConnection $resourceConnection + * @param Config $config + * @param Timeout $timeout * @param AbstractResource|null $resource * @param AbstractDb|null $resourceCollection * @param array $data @@ -152,6 +165,8 @@ public function __construct( SearchCriteriaBuilder $searchCriteriaBuilder, AssetRepository $assetRepository, ResourceConnection $resourceConnection, + Config $config, + Timeout $timeout, AbstractResource $resource = null, AbstractDb $resourceCollection = null, array $data = [] @@ -178,6 +193,8 @@ public function __construct( $this->searchCriteriaBuilder = $searchCriteriaBuilder; $this->assetRepository = $assetRepository; $this->resourceConnection = $resourceConnection; + $this->config = $config; + $this->timeout = $timeout; } /** @@ -186,6 +203,8 @@ public function __construct( * @param \Magento\Quote\Api\Data\CartInterface|null $quote * * @return bool + * @throws LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException */ public function isAvailable(\Magento\Quote\Api\Data\CartInterface $quote = null) { @@ -245,35 +264,46 @@ public function startTransaction(Order $order) $method = $this->mollieHelper->getApiMethod($order); if ($method == 'order') { - try { - $transactionResult = $this->ordersApi->startTransaction($order, $mollieApi); - } catch (\Exception $e) { - $methodCode = $this->mollieHelper->getMethodCode($order); - if ($methodCode != 'klarnapaylater' && $methodCode != 'klarnasliceit') { - $this->mollieHelper->addTolog('error', $e->getMessage()); - $transactionResult = $this->paymentsApi->startTransaction($order, $mollieApi); - } else { - throw new LocalizedException(__($e->getMessage())); - } - } - } else { - $transactionResult = $this->paymentsApi->startTransaction($order, $mollieApi); + return $this->startTransactionUsingTheOrdersApi($order, $mollieApi); + } + + return $this->timeout->retry( function () use ($order, $mollieApi) { + return $this->paymentsApi->startTransaction($order, $mollieApi); + }); + } + + private function startTransactionUsingTheOrdersApi(Order $order, MollieApiClient $mollieApi) + { + try { + return $this->timeout->retry( function () use ($order, $mollieApi) { + return $this->ordersApi->startTransaction($order, $mollieApi); + }); + } catch (\Exception $exception) { + $this->mollieHelper->addTolog('error', $exception->getMessage()); + } + + $methodCode = $this->mollieHelper->getMethodCode($order); + if ($methodCode == 'klarnapaylater' || $methodCode == 'klarnasliceit') { + throw new LocalizedException(__($exception->getMessage())); } - return $transactionResult; + // Retry the order using the "payment" method. + return $this->timeout->retry( function () use ($order, $mollieApi) { + return $this->paymentsApi->startTransaction($order, $mollieApi); + }); } /** * @param $apiKey * - * @return \Mollie\Api\MollieApiClient + * @return MollieApiClient * @throws \Mollie\Api\Exceptions\ApiException * @throws LocalizedException */ public function loadMollieApi($apiKey) { if (class_exists('Mollie\Api\MollieApiClient')) { - $mollieApiClient = new \Mollie\Api\MollieApiClient(); + $mollieApiClient = new MollieApiClient(); $mollieApiClient->setApiKey($apiKey); $mollieApiClient->addVersionString('Magento/' . $this->mollieHelper->getMagentoVersion()); $mollieApiClient->addVersionString('MollieMagento2/' . $this->mollieHelper->getExtensionVersion()); diff --git a/Model/OrderLines.php b/Model/OrderLines.php index a812daf0a5c..75fd942164f 100644 --- a/Model/OrderLines.php +++ b/Model/OrderLines.php @@ -18,6 +18,8 @@ use Mollie\Payment\Model\ResourceModel\OrderLines\CollectionFactory as OrderLinesCollectionFactory; use Mollie\Payment\Helper\General as MollieHelper; use Magento\Framework\Exception\LocalizedException; +use Mollie\Payment\Service\Order\Creditmemo as CreditmemoService; +use Mollie\Payment\Service\Order\Lines\PaymentFee; use Mollie\Payment\Service\Order\Lines\StoreCredit; class OrderLines extends AbstractModel @@ -39,6 +41,14 @@ class OrderLines extends AbstractModel * @var StoreCredit */ private $storeCredit; + /** + * @var PaymentFee + */ + private $paymentFee; + /** + * @var CreditmemoService + */ + private $creditmemoService; /** * OrderLines constructor. @@ -49,6 +59,8 @@ class OrderLines extends AbstractModel * @param Context $context * @param Registry $registry * @param StoreCredit $storeCredit + * @param PaymentFee $paymentFee + * @param CreditmemoService $creditmemoService * @param AbstractResource|null $resource * @param AbstractDb|null $resourceCollection * @param array $data @@ -60,6 +72,8 @@ public function __construct( Context $context, Registry $registry, StoreCredit $storeCredit, + PaymentFee $paymentFee, + CreditmemoService $creditmemoService, AbstractResource $resource = null, AbstractDb $resourceCollection = null, array $data = [] @@ -68,6 +82,8 @@ public function __construct( $this->orderLinesFactory = $orderLinesFactory; $this->orderLinesCollection = $orderLinesCollection; $this->storeCredit = $storeCredit; + $this->paymentFee = $paymentFee; + $this->creditmemoService = $creditmemoService; parent::__construct($context, $registry, $resource, $resourceCollection, $data); } @@ -187,6 +203,10 @@ public function getOrderLines(Order $order) $orderLines[] = $this->storeCredit->getOrderLine($order, $forceBaseCurrency); } + if ($this->paymentFee->orderHasPaymentFee($order)) { + $orderLines[] = $this->paymentFee->getOrderLine($order, $forceBaseCurrency); + } + $this->saveOrderLines($orderLines, $order); foreach ($orderLines as &$orderLine) { unset($orderLine['item_id']); @@ -425,6 +445,11 @@ public function getCreditmemoOrderLines($creditmemo, $addShipping) $orderLines[] = ['id' => $storeCreditLine->getLineId(), 'quantity' => 1]; } + $paymentFeeCreditLine = $this->getPaymentFeeCreditItemLineOrder($orderId); + if ($paymentFeeCreditLine->getId() && !$this->creditmemoService->hasItemsLeftToRefund($creditmemo)) { + $orderLines[] = ['id' => $paymentFeeCreditLine->getLineId(), 'quantity' => 1]; + } + return ['lines' => $orderLines]; } @@ -458,6 +483,19 @@ public function getStoreCreditItemLineOrder($orderId) return $storeCreditLine; } + /** + * @param $orderId + * + * @return OrderLines + */ + public function getPaymentFeeCreditItemLineOrder($orderId) + { + return $this->orderLinesCollection->create() + ->addFieldToFilter('order_id', ['eq' => $orderId]) + ->addFieldToFilter('type', ['eq' => 'surcharge']) + ->getLastItem(); + } + /** * @param $orderId * diff --git a/Model/PaymentFee/Creditmemo/Total/PaymentFee.php b/Model/PaymentFee/Creditmemo/Total/PaymentFee.php new file mode 100644 index 00000000000..d974a0fb5c2 --- /dev/null +++ b/Model/PaymentFee/Creditmemo/Total/PaymentFee.php @@ -0,0 +1,56 @@ +creditmemoService = $creditmemoService; + parent::__construct($data); + } + + /** + * @param Creditmemo $creditmemo + * @return $this|AbstractTotal + */ + public function collect(Creditmemo $creditmemo) + { + if (!$this->creditmemoService->isFullOrLastPartialCreditmemo($creditmemo)) { + return $this; + } + + $order = $creditmemo->getOrder(); + $paymentFee = $order->getData('mollie_payment_fee'); + $basePaymentFee = $order->getData('base_mollie_payment_fee'); + $paymentFeeTax = $order->getData('mollie_payment_fee_tax'); + $basePaymentFeeTax = $order->getData('base_mollie_payment_fee_tax'); + + $creditmemo->setData('mollie_payment_fee', $paymentFee); + $creditmemo->setData('base_mollie_payment_fee', $basePaymentFee); + $creditmemo->setData('mollie_payment_fee_tax', $paymentFeeTax); + $creditmemo->setData('base_mollie_payment_fee_tax', $basePaymentFeeTax); + + $creditmemo->setGrandTotal($creditmemo->getGrandTotal() + $paymentFee + $paymentFeeTax); + $creditmemo->setBaseGrandTotal($creditmemo->getBaseGrandTotal() + $basePaymentFee + $basePaymentFeeTax); + $creditmemo->setTaxAmount($creditmemo->getTaxAmount() + $paymentFeeTax); + $creditmemo->setBaseTaxAmount($creditmemo->getBaseTaxAmount() + $basePaymentFeeTax); + + return $this; + } +} diff --git a/Model/PaymentFee/Invoice/Total/PaymentFee.php b/Model/PaymentFee/Invoice/Total/PaymentFee.php new file mode 100644 index 00000000000..3b28710d9d1 --- /dev/null +++ b/Model/PaymentFee/Invoice/Total/PaymentFee.php @@ -0,0 +1,38 @@ +getOrder(); + $paymentFee = $order->getData('mollie_payment_fee'); + $basePaymentFee = $order->getData('base_mollie_payment_fee'); + $paymentFeeTax = $order->getData('mollie_payment_fee_tax'); + $basePaymentFeeTax = $order->getData('base_mollie_payment_fee_tax'); + + $invoice->setData('mollie_payment_fee', $paymentFee); + $invoice->setData('base_mollie_payment_fee', $basePaymentFee); + $invoice->setData('mollie_payment_fee_tax', $paymentFeeTax); + $invoice->setData('base_mollie_payment_fee_tax', $basePaymentFeeTax); + + $invoice->setGrandTotal($invoice->getGrandTotal() + $paymentFee); + $invoice->setBaseGrandTotal($invoice->getBaseGrandTotal() + $basePaymentFee); + $invoice->setTaxAmount($invoice->getTaxAmount() + $paymentFeeTax); + $invoice->setBaseTaxAmount($invoice->getBaseTaxAmount() + $basePaymentFeeTax); + + return $this; + } +} diff --git a/Model/PaymentFee/Order/Pdf/Total/PaymentFee.php b/Model/PaymentFee/Order/Pdf/Total/PaymentFee.php new file mode 100644 index 00000000000..988f3c98a4e --- /dev/null +++ b/Model/PaymentFee/Order/Pdf/Total/PaymentFee.php @@ -0,0 +1,51 @@ +currency = $currency; + } + + public function getTotalsForDisplay() + { + $source = $this->getSource(); + $amount = $source->getMolliePaymentFee() + $source->getMolliePaymentFeeTax(); + + if (!$amount) { + return []; + } + + return [ + [ + 'amount' => $this->currency->format($amount, false), + 'label' => __('Payment Fee'), + 'font_size' => $this->getFontSize() ? $this->getFontSize() : 7, + ], + ]; + } +} diff --git a/Model/PaymentFee/Quote/Address/Total/PaymentFee.php b/Model/PaymentFee/Quote/Address/Total/PaymentFee.php new file mode 100644 index 00000000000..d4b4c710f7f --- /dev/null +++ b/Model/PaymentFee/Quote/Address/Total/PaymentFee.php @@ -0,0 +1,93 @@ +paymentFeeConfig = $paymentFeeConfig; + $this->priceCurrency = $priceCurrency; + } + + /** + * @param Quote $quote + * @param ShippingAssignmentInterface $shippingAssignment + * @param Total $total + * @return $this|AbstractTotal + */ + public function collect(Quote $quote, ShippingAssignmentInterface $shippingAssignment, Total $total) + { + parent::collect($quote, $shippingAssignment, $total); + + if (!$shippingAssignment->getItems() || !$this->paymentFeeConfig->isAvailableForMethod($quote)) { + return $this; + } + + $baseAmount = $this->paymentFeeConfig->excludingTax($quote); + $amount = $this->priceCurrency->convert($baseAmount); + + $total->setTotalAmount('mollie_payment_fee', $amount); + $total->setBaseTotalAmount('mollie_payment_fee', $baseAmount); + + $attributes = $quote->getExtensionAttributes(); + + if (!$attributes) { + return $this; + } + + $attributes->setMolliePaymentFee($amount); + $attributes->setBaseMolliePaymentFee($amount); + + return $this; + } + + /** + * @param Quote $quote + * @param Total $total + * @return array + */ + public function fetch(Quote $quote, Total $total) + { + if (!$this->paymentFeeConfig->isAvailableForMethod($quote)) { + return []; + } + + return [ + 'code' => 'mollie_payment_fee', + 'title' => __('Payment Fee'), + 'value' => $this->paymentFeeConfig->includingTax($quote), + ]; + } + + /** + * @return \Magento\Framework\Phrase + */ + public function getLabel() + { + return __('Payment Fee'); + } +} diff --git a/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php b/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php new file mode 100644 index 00000000000..79df6605def --- /dev/null +++ b/Model/PaymentFee/Quote/Address/Total/PaymentFeeTax.php @@ -0,0 +1,67 @@ +paymentFeeConfig = $paymentFeeConfig; + $this->priceCurrency = $priceCurrency; + } + + /** + * @param Quote $quote + * @param ShippingAssignmentInterface $shippingAssignment + * @param Total $total + * @return $this|AbstractTotal + */ + public function collect(Quote $quote, ShippingAssignmentInterface $shippingAssignment, Total $total) + { + parent::collect($quote, $shippingAssignment, $total); + + if (!$shippingAssignment->getItems() || !$this->paymentFeeConfig->isAvailableForMethod($quote)) { + return $this; + } + + $baseAmount = $this->paymentFeeConfig->tax($quote); + $amount = $this->priceCurrency->convert($baseAmount); + + $total->addTotalAmount('tax', $amount); + $total->addBaseTotalAmount('tax', $baseAmount); + + $extensionAttributes = $quote->getExtensionAttributes(); + + if (!$extensionAttributes) { + return $this; + } + + $extensionAttributes->setMolliePaymentFeeTax($amount); + $extensionAttributes->setBaseMolliePaymentFeeTax($baseAmount); + + return $this; + } +} diff --git a/Observer/SalesModelServiceQuoteSubmitBefore/CopyPaymentFeeToOrder.php b/Observer/SalesModelServiceQuoteSubmitBefore/CopyPaymentFeeToOrder.php new file mode 100644 index 00000000000..193425404b3 --- /dev/null +++ b/Observer/SalesModelServiceQuoteSubmitBefore/CopyPaymentFeeToOrder.php @@ -0,0 +1,43 @@ +getEvent()->getData('order'); + + /* @var CartInterface $quote */ + $quote = $observer->getEvent()->getData('quote'); + + if (!$order || !$quote) { + return; + } + + $extensionAttributes = $quote->getExtensionAttributes(); + + if (!$extensionAttributes) { + return; + } + + $order->setMolliePaymentFee($extensionAttributes->getMolliePaymentFee()); + $order->setMolliePaymentFeeTax($extensionAttributes->getMolliePaymentFeeTax()); + + $order->setBaseMolliePaymentFee($extensionAttributes->getBaseMolliePaymentFee()); + $order->setBaseMolliePaymentFeeTax($extensionAttributes->getBaseMolliePaymentFeeTax()); + } +} diff --git a/Service/Config/PaymentFee.php b/Service/Config/PaymentFee.php new file mode 100644 index 00000000000..5beef7374c3 --- /dev/null +++ b/Service/Config/PaymentFee.php @@ -0,0 +1,139 @@ +config = $config; + $this->taxCalculation = $taxCalculation; + } + + /** + * @param Quote $quote + * @return float + */ + public function includingTax(Quote $quote) + { + $method = $quote->getPayment()->getMethod(); + + $amount = $this->getAmount($method, $quote->getStoreId()); + + return (double)str_replace(',', '.', $amount); + } + + /** + * @param Quote $quote + * @return float + */ + public function excludingTax(Quote $quote) + { + $amount = $this->includingTax($quote); + $tax = $this->tax($quote); + + return $amount - $tax; + } + + /** + * @param Quote $quote + * @return float + */ + public function tax(Quote $quote) + { + $shippingAddress = $quote->getShippingAddress(); + $billingAddress = $quote->getBillingAddress(); + $customerTaxClassId = $quote->getCustomerTaxClassId(); + $storeId = $quote->getStoreId(); + + $request = $this->taxCalculation->getRateRequest( + $shippingAddress, + $billingAddress, + $customerTaxClassId, + $storeId + ); + + $taxClassId = $this->getTaxClass($quote); + $request->setProductClassId($taxClassId); + + $rate = $this->taxCalculation->getRate($request); + + $fee = $this->includingTax($quote); + $result = $this->taxCalculation->calcTaxAmount( + $fee, + $rate, + true, + false + ); + + return $result; + } + + /** + * @param Quote $quote + * @return bool + */ + public function isAvailableForMethod(Quote $quote) + { + $method = $quote->getPayment()->getMethod(); + + return in_array($method, ['mollie_methods_klarnapaylater', 'mollie_methods_klarnasliceit']); + } + + /** + * @param Quote $quote + * @return string|null + */ + private function getTaxClass(Quote $quote) + { + $method = $quote->getPayment()->getMethod(); + + if ($method == 'mollie_methods_klarnapaylater') { + return $this->config->klarnaPaylaterPaymentSurchargeTaxClass($quote->getStoreId()); + } + + if ($method == 'mollie_methods_klarnasliceit') { + return $this->config->klarnaSliceitPaymentSurchargeTaxClass($quote->getStoreId()); + } + + return null; + } + + /** + * @param $method + * @param $storeId + * @return int|string + */ + private function getAmount($method, $storeId) + { + if ($method == 'mollie_methods_klarnapaylater') { + return $this->config->klarnaPaylaterPaymentSurcharge($storeId); + } + + if ($method == 'mollie_methods_klarnasliceit') { + return $this->config->klarnaSliceitPaymentSurcharge($storeId); + } + + return 0; + } +} diff --git a/Service/Mollie/Timeout.php b/Service/Mollie/Timeout.php new file mode 100644 index 00000000000..97a046e0325 --- /dev/null +++ b/Service/Mollie/Timeout.php @@ -0,0 +1,55 @@ +attemptCallback($callback)) { + return $result; + } + + $tries++; + } while ($tries < 3); + + throw $this->lastException; + } + + private function attemptCallback(callable $callback) + { + try { + return $callback(); + } catch (\Exception $exception) { + $this->lastException = $exception; + $this->handle($exception); + + return false; + } + } + + private function handle(\Exception $exception) + { + if (stripos($exception->getMessage(), 'cURL error 28') !== false) { + return; + } + + throw $exception; + } +} diff --git a/Service/Order/Creditmemo.php b/Service/Order/Creditmemo.php new file mode 100644 index 00000000000..d6dd308bac4 --- /dev/null +++ b/Service/Order/Creditmemo.php @@ -0,0 +1,55 @@ +getAllItems() as $item) { + /** @var OrderItemInterface $orderItem */ + $orderItem = $item->getOrderItem(); + $refundable = $orderItem->getQtyOrdered() - $orderItem->getQtyRefunded(); + + if ($refundable != $item->getQty()) { + return false; + } + } + + return true; + } + + /** + * @param CreditmemoInterface $creditmemo + * @return bool + */ + public function hasItemsLeftToRefund(CreditmemoInterface $creditmemo) + { + /** @var OrderItemInterface $order */ + $order = $creditmemo->getOrder(); + + /** @var OrderItemInterface $item */ + foreach ($order->getAllItems() as $item) { + $refundable = $item->getQtyOrdered() - $item->getQtyRefunded(); + + if ($refundable) { + return true; + } + } + + return false; + } +} diff --git a/Service/Order/Lines/PaymentFee.php b/Service/Order/Lines/PaymentFee.php new file mode 100644 index 00000000000..d9e21d5f756 --- /dev/null +++ b/Service/Order/Lines/PaymentFee.php @@ -0,0 +1,64 @@ +mollieHelper = $mollieHelper; + } + + /** + * @param OrderInterface $order + * @return bool + */ + public function orderHasPaymentFee(OrderInterface $order) + { + return (float)$order->getData('base_mollie_payment_fee') && (float)$order->getData('mollie_payment_fee'); + } + + /** + * @param OrderInterface $order + * @param $forceBaseCurrency + * @return array + */ + public function getOrderLine(OrderInterface $order, $forceBaseCurrency) + { + $currency = $forceBaseCurrency ? $order->getBaseCurrencyCode() : $order->getOrderCurrencyCode(); + $amount = $order->getData('base_mollie_payment_fee'); + $taxAmount = $order->getData('base_mollie_payment_fee_tax'); + + $vatRate = 0; + if ($taxAmount) { + $vatRate = round(($taxAmount / $amount) * 100, 2); + } + + return [ + 'type' => 'surcharge', + 'name' => __('Payment Fee'), + 'quantity' => 1, + 'unitPrice' => $this->mollieHelper->getAmountArray($currency, $amount + $taxAmount), + 'totalAmount' => $this->mollieHelper->getAmountArray($currency, $amount + $taxAmount), + 'vatRate' => $vatRate, + 'vatAmount' => $this->mollieHelper->getAmountArray($currency, $taxAmount), + ]; + } +} diff --git a/Service/Order/ProcessAdjustmentFee.php b/Service/Order/ProcessAdjustmentFee.php index c9bef6f760f..43d7f1c63e5 100644 --- a/Service/Order/ProcessAdjustmentFee.php +++ b/Service/Order/ProcessAdjustmentFee.php @@ -65,13 +65,11 @@ private function negative(MollieApiClient $mollieApi, OrderInterface $order, Cre { $this->doNotRefundInMollie = true; - $amountToRefund = $order->getGrandTotal() - abs($creditmemo->getAdjustmentNegative()); - $this->refundUsingPayment->execute( $mollieApi, $order->getMollieTransactionId(), $order->getOrderCurrencyCode(), - $amountToRefund + $creditmemo->getGrandTotal() ); } -} \ No newline at end of file +} diff --git a/Setup/UpgradeSchema.php b/Setup/UpgradeSchema.php index d9760edef24..3748b12b3f3 100644 --- a/Setup/UpgradeSchema.php +++ b/Setup/UpgradeSchema.php @@ -6,6 +6,7 @@ namespace Mollie\Payment\Setup; +use Magento\Framework\DB\Ddl\Table; use Magento\Framework\Setup\SchemaSetupInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\UpgradeSchemaInterface; @@ -29,10 +30,14 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con { $setup->startSetup(); - if (version_compare($context->getVersion(), "1.4.0", "<")) { + if (version_compare($context->getVersion(), '1.4.0', '<')) { $this->createTable($setup, MollieOrderLines::getData()); } + if (version_compare($context->getVersion(), '1.8.0', '<')) { + $this->addPaymentFeeColumns($setup); + } + $setup->endSetup(); } @@ -61,4 +66,29 @@ public function createTable(SchemaSetupInterface $setup, $tableData) $connection->createTable($table); } } + + /** + * @param SchemaSetupInterface $setup + */ + private function addPaymentFeeColumns(SchemaSetupInterface $setup) + { + $connection = $setup->getConnection(); + + $defintion = [ + 'type' => Table::TYPE_DECIMAL, + 'length' => '12,4', + 'default' => 0.0000, + 'nullable' => true, + 'comment' => 'Mollie Payment Fee', + ]; + + foreach (['quote', 'quote_address', 'sales_order', 'sales_invoice', 'sales_creditmemo'] as $table) { + $tableName = $setup->getTable($table); + + $connection->addColumn($tableName, 'mollie_payment_fee', $defintion); + $connection->addColumn($tableName, 'base_mollie_payment_fee', $defintion); + $connection->addColumn($tableName, 'mollie_payment_fee_tax', $defintion); + $connection->addColumn($tableName, 'base_mollie_payment_fee_tax', $defintion); + } + } } diff --git a/Test/Integration/Model/Adminhtml/Backend/VerifiyPaymentFeeTest.php b/Test/Integration/Model/Adminhtml/Backend/VerifiyPaymentFeeTest.php new file mode 100644 index 00000000000..b198b805f15 --- /dev/null +++ b/Test/Integration/Model/Adminhtml/Backend/VerifiyPaymentFeeTest.php @@ -0,0 +1,43 @@ +objectManager->create(VerifiyPaymentFee::class); + + $instance->setValue('1,23'); + + $instance->beforeSave(); + + $this->assertSame('1.23', $instance->getValue()); + } + + public function testThrowsAnExceptionWhenTheAmountIsTooHigh() + { + /** @var VerifiyPaymentFee $instance */ + $instance = $this->objectManager->create(VerifiyPaymentFee::class); + + $instance->setValue(VerifiyPaymentFee::MAXIMUM_PAYMENT_FEE_AMOUNT + 0.01); + + try { + $instance->beforeSave(); + } catch (ValidatorException $exception) { + $this->assertInstanceOf(ValidatorException::class, $exception); + $this->assertContains((string)VerifiyPaymentFee::MAXIMUM_PAYMENT_FEE_AMOUNT, $exception->getMessage()); + return; + } + + $this->fail('We expected an ' . ValidatorException::class . ' but got none'); + } +} diff --git a/Test/Integration/Model/Methods/PaymentlinkTest.php b/Test/Integration/Model/Methods/PaymentlinkTest.php new file mode 100644 index 00000000000..6ae52324f3f --- /dev/null +++ b/Test/Integration/Model/Methods/PaymentlinkTest.php @@ -0,0 +1,35 @@ +loadOrderById('100000001'); + + $status = 'newPendingStatus'; + + /** @var Paymentlink $instance */ + $instance = $this->objectManager->get(Paymentlink::class); + + $instance->setInfoInstance($order->getPayment()); + + $statusObject = new DataObject(); + $instance->initialize('new', $statusObject); + + $this->assertEquals($status, $statusObject->getData('status')); + } +} diff --git a/Test/Integration/Model/MollieTest.php b/Test/Integration/Model/MollieTest.php index bb271d0c96b..9006e6cee3e 100644 --- a/Test/Integration/Model/MollieTest.php +++ b/Test/Integration/Model/MollieTest.php @@ -2,7 +2,9 @@ namespace Mollie\Payment\Model; +use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Mollie\Api\Exceptions\ApiException; use Mollie\Payment\Model\Client\Orders; use Mollie\Payment\Model\Client\Payments; use Mollie\Payment\Test\Integration\TestCase; @@ -48,4 +50,95 @@ public function testProcessTransactionUsesTheCorrectApi($orderId, $type) $instance->processTransaction($order->getEntityId()); } + + public function testStartTransactionWithMethodOrder() + { + /** @var OrderInterface $order */ + $order = $this->objectManager->create(OrderInterface::class); + + $helperMock = $this->createMock(\Mollie\Payment\Helper\General::class); + $helperMock->method('getApiKey')->willReturn('test_dummyapikeywhichmustbe30characterslong'); + $helperMock->method('getApiMethod')->willReturn('order'); + + $ordersApiMock = $this->createMock(Orders::class); + $ordersApiMock->method('startTransaction')->willReturn('order'); + + $paymentsApiMock = $this->createMock(Payments::class); + $paymentsApiMock->expects($this->never())->method('startTransaction'); + + /** @var Mollie $instance */ + $instance = $this->objectManager->create(Mollie::class, [ + 'ordersApi' => $ordersApiMock, + 'paymentsApi' => $paymentsApiMock, + 'mollieHelper' => $helperMock, + ]); + + $result = $instance->startTransaction($order); + + $this->assertEquals('order', $result); + } + + public function testStartTransactionWithMethodPayment() + { + /** @var OrderInterface $order */ + $order = $this->objectManager->create(OrderInterface::class); + + $helperMock = $this->createMock(\Mollie\Payment\Helper\General::class); + $helperMock->method('getApiKey')->willReturn('test_dummyapikeywhichmustbe30characterslong'); + $helperMock->method('getApiMethod')->willReturn('payment'); + + $ordersApiMock = $this->createMock(Orders::class); + $ordersApiMock->expects($this->never())->method('startTransaction'); + + $paymentsApiMock = $this->createMock(Payments::class); + $paymentsApiMock->method('startTransaction')->willReturn('payment'); + + /** @var Mollie $instance */ + $instance = $this->objectManager->create(Mollie::class, [ + 'ordersApi' => $ordersApiMock, + 'paymentsApi' => $paymentsApiMock, + 'mollieHelper' => $helperMock, + ]); + + $result = $instance->startTransaction($order); + + $this->assertEquals('payment', $result); + } + + /** + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Mollie\Api\Exceptions\ApiException + * @throws \ReflectionException + */ + public function testRetriesOnACurlTimeout() + { + /** @var OrderInterface $order */ + $order = $this->objectManager->create(OrderInterface::class); + + $helperMock = $this->createMock(\Mollie\Payment\Helper\General::class); + $helperMock->method('getApiKey')->willReturn('test_dummyapikeywhichmustbe30characterslong'); + $helperMock->method('getApiMethod')->willReturn('order'); + + $ordersApiMock = $this->createMock(Orders::class); + $ordersApiMock->expects($this->exactly(3))->method('startTransaction')->willThrowException( + new ApiException( + 'cURL error 28: Connection timed out after 10074 milliseconds ' . + '(see http://curl.haxx.se/libcurl/c/libcurl-errors.html)' + ) + ); + + $paymentsApiMock = $this->createMock(Payments::class); + $paymentsApiMock->expects($this->once())->method('startTransaction')->willReturn('payment'); + + /** @var Mollie $instance */ + $instance = $this->objectManager->create(Mollie::class, [ + 'ordersApi' => $ordersApiMock, + 'paymentsApi' => $paymentsApiMock, + 'mollieHelper' => $helperMock, + ]); + + $result = $instance->startTransaction($order); + + $this->assertEquals('payment', $result); + } } diff --git a/Test/Integration/Model/PaymentFee/Invoice/Total/PaymentFeeTest.php b/Test/Integration/Model/PaymentFee/Invoice/Total/PaymentFeeTest.php new file mode 100644 index 00000000000..4394ac74020 --- /dev/null +++ b/Test/Integration/Model/PaymentFee/Invoice/Total/PaymentFeeTest.php @@ -0,0 +1,44 @@ +objectManager->create(PaymentFee::class); + + /** @var InvoiceInterface $invoice */ + $invoice = $this->objectManager->create(InvoiceInterface::class); + + /** @var OrderInterface $order */ + $order = $this->objectManager->create(OrderInterface::class); + $order->setData('mollie_payment_fee', 1.61); + $order->setData('base_mollie_payment_fee', 1.61); + $order->setData('mollie_payment_fee_tax', 0.34); + $order->setData('base_mollie_payment_fee_tax', 0.34); + + $invoice->setOrder($order); + + $instance->collect($invoice); + + $this->assertEquals(1.61, $invoice->getData('mollie_payment_fee')); + $this->assertEquals(1.61, $invoice->getData('base_mollie_payment_fee')); + $this->assertEquals(0.34, $invoice->getData('mollie_payment_fee_tax')); + $this->assertEquals(0.34, $invoice->getData('base_mollie_payment_fee_tax')); + + $this->assertEquals(1.61, $invoice->getGrandTotal()); + $this->assertEquals(1.61, $invoice->getBaseGrandTotal()); + $this->assertEquals(0.34, $invoice->getTaxAmount()); + $this->assertEquals(0.34, $invoice->getBaseTaxAmount()); + } +} diff --git a/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php b/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php new file mode 100644 index 00000000000..f5d2939fbbb --- /dev/null +++ b/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTaxTest.php @@ -0,0 +1,117 @@ +objectManager->create(PaymentFeeTax::class); + + /** @var Total $total */ + $total = $this->objectManager->create(Total::class); + + $quote = $this->getQuote(); + $this->assertEquals(0, $total->getTotalAmount('tax')); + $this->assertEquals(0, $total->getBaseTotalAmount('tax')); + $this->assertEquals(0, $quote->getExtensionAttributes()->getMolliePaymentFeeTax()); + $this->assertEquals(0, $quote->getExtensionAttributes()->getBaseMolliePaymentFeeTax()); + + $instance->collect($quote, $this->getShippingAssignment(), $total); + + $this->assertEquals(0, $total->getTotalAmount('tax')); + $this->assertEquals(0, $total->getBaseTotalAmount('tax')); + $this->assertEquals(0, $quote->getExtensionAttributes()->getMolliePaymentFeeTax()); + $this->assertEquals(0, $quote->getExtensionAttributes()->getBaseMolliePaymentFeeTax()); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + * @magentoConfigFixture current_store payment/mollie_methods_klarnapaylater/payment_surcharge 1,95 + * @magentoConfigFixture current_store payment/mollie_methods_klarnasliceit/payment_surcharge_tax_class 2 + */ + public function testDoesApplyIfTheMethodIsSupported() + { + $paymentFeeMock = $this->createPartialMock(PaymentFeeConfig::class, ['tax']); + $paymentFeeMock->method('tax')->willReturn(0.33); + + /** @var PaymentFeeTax $instance */ + $instance = $this->objectManager->create(PaymentFeeTax::class, [ + 'paymentFeeConfig' => $paymentFeeMock, + ]); + + /** @var Total $total */ + $total = $this->objectManager->create(Total::class); + + $shippingAssignment = $this->getShippingAssignment(); + + $shippingAssignment->setItems([ + $this->objectManager->create(CartItemInterface::class), + ]); + + $quote = $this->getQuote('mollie_methods_klarnapaylater'); + + $this->assertEquals(0, $total->getTotalAmount('tax')); + $this->assertEquals(0, $total->getBaseTotalAmount('tax')); + $this->assertEquals(0, $quote->getExtensionAttributes()->getMolliePaymentFeeTax()); + $this->assertEquals(0, $quote->getExtensionAttributes()->getBaseMolliePaymentFeeTax()); + + $instance->collect($quote, $shippingAssignment, $total); + + $this->assertEquals(0.33, $total->getTotalAmount('tax')); + $this->assertEquals(0.33, $total->getBaseTotalAmount('tax')); + $this->assertEquals(0.33, $quote->getExtensionAttributes()->getMolliePaymentFeeTax()); + $this->assertEquals(0.33, $quote->getExtensionAttributes()->getBaseMolliePaymentFeeTax()); + } + + /** + * @return ShippingAssignmentInterface + */ + private function getShippingAssignment() + { + /** @var AddressInterface $address */ + $address = $this->objectManager->create(AddressInterface::class); + + /** @var ShippingInterface $shipping */ + $shipping = $this->objectManager->create(ShippingInterface::class); + $shipping->setAddress($address); + + /** @var ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $this->objectManager->create(ShippingAssignmentInterface::class); + $shippingAssignment->setShipping($shipping); + + return $shippingAssignment; + } + + /** + * @param null $method + * @return CartInterface + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getQuote($method = null) + { + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->objectManager->create(Session::class); + $quote = $session->getQuote(); + + $quote->getPayment()->setMethod($method); + + return $quote; + } +} diff --git a/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTest.php b/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTest.php new file mode 100644 index 00000000000..fab74e860dc --- /dev/null +++ b/Test/Integration/Model/PaymentFee/Quote/Address/Total/PaymentFeeTest.php @@ -0,0 +1,115 @@ +objectManager->create(PaymentFee::class); + + /** @var Total $total */ + $total = $this->objectManager->create(Total::class); + + $quote = $this->getQuote(); + $this->assertEquals(0, $total->getTotalAmount('mollie_payment_fee')); + $this->assertEquals(0, $total->getBaseTotalAmount('mollie_payment_fee')); + $this->assertEquals(0, $quote->getExtensionAttributes()->getMolliePaymentFee()); + $this->assertEquals(0, $quote->getExtensionAttributes()->getBaseMolliePaymentFee()); + + $instance->collect($quote, $this->getShippingAssignment(), $total); + + $this->assertEquals(0, $total->getTotalAmount('mollie_payment_fee')); + $this->assertEquals(0, $total->getBaseTotalAmount('mollie_payment_fee')); + $this->assertEquals(0, $quote->getExtensionAttributes()->getMolliePaymentFee()); + $this->assertEquals(0, $quote->getExtensionAttributes()->getBaseMolliePaymentFee()); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + */ + public function testDoesApplyIfTheMethodIsSupported() + { + $paymentFeeMock = $this->createPartialMock(PaymentFeeConfig::class, ['excludingTax']); + $paymentFeeMock->method('excludingTax')->willReturn(1.61); + + /** @var PaymentFee $instance */ + $instance = $this->objectManager->create(PaymentFee::class, [ + 'paymentFeeConfig' => $paymentFeeMock, + ]); + + /** @var Total $total */ + $total = $this->objectManager->create(Total::class); + + $shippingAssignment = $this->getShippingAssignment(); + + $shippingAssignment->setItems([ + $this->objectManager->create(CartItemInterface::class), + ]); + + $quote = $this->getQuote('mollie_methods_klarnapaylater'); + + $this->assertEquals(0, $total->getTotalAmount('mollie_payment_fee')); + $this->assertEquals(0, $total->getBaseTotalAmount('mollie_payment_fee')); + $this->assertEquals(0, $quote->getExtensionAttributes()->getMolliePaymentFee()); + $this->assertEquals(0, $quote->getExtensionAttributes()->getBaseMolliePaymentFee()); + + $instance->collect($quote, $shippingAssignment, $total); + + $this->assertEquals(1.61, $total->getTotalAmount('mollie_payment_fee')); + $this->assertEquals(1.61, $total->getBaseTotalAmount('mollie_payment_fee')); + $this->assertEquals(1.61, $quote->getExtensionAttributes()->getMolliePaymentFee()); + $this->assertEquals(1.61, $quote->getExtensionAttributes()->getBaseMolliePaymentFee()); + } + + /** + * @return ShippingAssignmentInterface + */ + private function getShippingAssignment() + { + /** @var AddressInterface $address */ + $address = $this->objectManager->create(AddressInterface::class); + + /** @var ShippingInterface $shipping */ + $shipping = $this->objectManager->create(ShippingInterface::class); + $shipping->setAddress($address); + + /** @var ShippingAssignmentInterface $shippingAssignment */ + $shippingAssignment = $this->objectManager->create(ShippingAssignmentInterface::class); + $shippingAssignment->setShipping($shipping); + + return $shippingAssignment; + } + + /** + * @param null $method + * @return CartInterface + * @throws \Magento\Framework\Exception\LocalizedException + * @throws \Magento\Framework\Exception\NoSuchEntityException + */ + private function getQuote($method = null) + { + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->objectManager->create(Session::class); + $quote = $session->getQuote(); + + $quote->getPayment()->setMethod($method); + + return $quote; + } +} diff --git a/Test/Integration/Service/Config/PaymentFeeTest.php b/Test/Integration/Service/Config/PaymentFeeTest.php new file mode 100644 index 00000000000..b3c51b73e32 --- /dev/null +++ b/Test/Integration/Service/Config/PaymentFeeTest.php @@ -0,0 +1,134 @@ +objectManager->create(PaymentFee::class); + + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->objectManager->create(Session::class); + $quote = $session->getQuote(); + $quote->getPayment()->setMethod('mollie_methods_' . $method); + + $result = $instance->includingTax($quote); + + $this->assertEquals($amount, $result); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + * @magentoConfigFixture current_store payment/mollie_methods_klarnasliceit/payment_surcharge 1,95 + * @magentoConfigFixture current_store payment/mollie_methods_klarnasliceit/payment_surcharge_tax_class 2 + */ + public function testReturnsTheCorrectExcludingTaxAmount() + { + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->objectManager->create(Session::class); + $quote = $session->getQuote(); + + /** @var \Magento\Tax\Api\Data\TaxRateInterface $rate */ + $rate = $this->objectManager->create(\Magento\Tax\Api\Data\TaxRateInterface::class); + $rate->setTaxCountryId('US'); + $rate->setTaxRegionId(0); + $rate->setTaxPostcode('*'); + $rate->setCode('testshipping_testshipping'); + $rate->setRate(21); + + $this->objectManager->get(TaxRateRepositoryInterface::class)->save($rate); + + /** @var \Magento\Tax\Model\ResourceModel\Calculation $taxCalculation */ + $taxCalculation = $this->objectManager->create(\Magento\Tax\Model\ResourceModel\Calculation::class); + $taxCalculation->getConnection()->insert($taxCalculation->getMainTable(), [ + 'tax_calculation_rate_id' => $rate->getId(), + 'tax_calculation_rule_id' => 1, + 'customer_tax_class_id' => $quote->getCustomerTaxClassId(), + 'product_tax_class_id' => 2, + ]); + + /** @var PaymentFee $instance */ + $instance = $this->objectManager->create(PaymentFee::class); + + $quote->getPayment()->setMethod('mollie_methods_klarnasliceit'); + + $result = $instance->excludingTax($quote); + + $this->assertEquals(1.6115702479339, $result); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + */ + public function testReturnsZeroIfNotAValidPaymentMethod() + { + /** @var PaymentFee $instance */ + $instance = $this->objectManager->create(PaymentFee::class); + + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->objectManager->create(Session::class); + $quote = $session->getQuote(); + $quote->getPayment()->setMethod('not_relevant_payment_method'); + + $result = $instance->includingTax($quote); + + $this->assertSame(0.0, $result); + } + + /** + * @magentoDataFixture Magento/Checkout/_files/quote_with_payment_saved.php + */ + public function isAvailableForMethodProvider() + { + return [ + ['mollie_methods_klarnapaylater', true], + ['mollie_methods_klarnasliceit', true], + ['not_relevant_payment_method', false], + ]; + } + + /** + * @dataProvider isAvailableForMethodProvider + */ + public function testIsAvailableForMethod($method, $expected) + { + /** @var PaymentFee $instance */ + $instance = $this->objectManager->create(PaymentFee::class); + + /** @var $session \Magento\Checkout\Model\Session */ + $session = $this->objectManager->create(Session::class); + $quote = $session->getQuote(); + $quote->getPayment()->setMethod($method); + + $result = $instance->isAvailableForMethod($quote); + + $this->assertSame($expected, $result); + } +} diff --git a/Test/Integration/Service/Order/Lines/PaymentFeeTest.php b/Test/Integration/Service/Order/Lines/PaymentFeeTest.php new file mode 100644 index 00000000000..2a1d25c41aa --- /dev/null +++ b/Test/Integration/Service/Order/Lines/PaymentFeeTest.php @@ -0,0 +1,41 @@ +objectManager->create(OrderInterface::class); + + $order->setData('mollie_payment_fee', 1); + $order->setData('base_mollie_payment_fee', 1); + + /** @var PaymentFee $instance */ + $instance = $this->objectManager->create(PaymentFee::class); + + $this->assertTrue($instance->orderHasPaymentFee($order)); + } + + public function testGetOrderLine() + { + /** @var OrderInterface $order */ + $order = $this->objectManager->create(OrderInterface::class); + + $order->setData('base_mollie_payment_fee', 1); + $order->setData('base_mollie_payment_fee_tax', 0.21); + + /** @var PaymentFee $instance */ + $instance = $this->objectManager->create(PaymentFee::class); + + $line = $instance->getOrderLine($order); + } +} diff --git a/Test/Integration/Setup/SchemaTest.php b/Test/Integration/Setup/SchemaTest.php index 391a67cd691..da216ff639a 100644 --- a/Test/Integration/Setup/SchemaTest.php +++ b/Test/Integration/Setup/SchemaTest.php @@ -8,6 +8,16 @@ class SchemaTest extends TestCase { + /** + * @var \Magento\Framework\App\ObjectManager + */ + private $objectManager; + + protected function setUp() + { + $this->objectManager = ObjectManager::getInstance(); + } + public function addedColumnsHaveIndexesProvider() { return [ @@ -21,9 +31,8 @@ public function addedColumnsHaveIndexesProvider() */ public function testAddedColumnsHaveIndexes($tableName, $columnName) { - $om = ObjectManager::getInstance(); /** @var ResourceConnection $resource */ - $resource = $om->get(ResourceConnection::class); + $resource = $this->objectManager->get(ResourceConnection::class); $connection = $resource->getConnection(); $tableName = $resource->getTableName($tableName); @@ -39,4 +48,37 @@ public function testAddedColumnsHaveIndexes($tableName, $columnName) $this->fail('There was no index found for `' . $columnName . '` in `' . $tableName . '`'); } -} \ No newline at end of file + + public function thePaymentFeeColumnsExistsProvider() + { + return [ + ['quote'], + ['quote_address'], + ['sales_order'], + ['sales_invoice'], + ['sales_creditmemo'], + ]; + } + + /** + * @dataProvider thePaymentFeeColumnsExistsProvider + */ + public function testThePaymentFeeColumnsExists($tableName) + { + /** @var ResourceConnection $resource */ + $resource = $this->objectManager->get(ResourceConnection::class); + $connection = $resource->getConnection(); + + $tableName = $resource->getTableName($tableName); + $columns = $connection->fetchAll('SHOW COLUMNS FROM ' . $tableName); + + $columns = array_map( function ($column) { + return $column['Field']; + }, $columns); + + $this->assertTrue(in_array('mollie_payment_fee', $columns)); + $this->assertTrue(in_array('base_mollie_payment_fee', $columns)); + $this->assertTrue(in_array('mollie_payment_fee_tax', $columns)); + $this->assertTrue(in_array('base_mollie_payment_fee_tax', $columns)); + } +} diff --git a/Test/Unit/Service/Order/ProcessAdjustmentFeeTest.php b/Test/Unit/Service/Order/ProcessAdjustmentFeeTest.php index 14a61c515dd..c2575e8a80f 100644 --- a/Test/Unit/Service/Order/ProcessAdjustmentFeeTest.php +++ b/Test/Unit/Service/Order/ProcessAdjustmentFeeTest.php @@ -65,12 +65,13 @@ public function testRefundsNegative() { $creditmemo = $this->createMock(CreditmemoInterface::class); $creditmemo->method('getAdjustmentNegative')->willReturn(-123); + $creditmemo->method('getGrandTotal')->willReturn(123); $this->refundUsingPaymentMock->expects($this->once())->method('execute')->with( $this->isInstanceOf(MollieApiClient::class), 999, 'EUR', - -123 + 123 ); $this->instance->handle($this->createmock(MollieApiClient::class), $this->getOrderMock(), $creditmemo); diff --git a/composer.json b/composer.json index 941f5891073..54bdf2f61e1 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "mollie/magento2", "description": "Mollie Payment Module for Magento 2", - "version": "1.7.1", + "version": "1.8.0", "keywords": [ "mollie", "payment", diff --git a/etc/adminhtml/methods/klarnapaylater.xml b/etc/adminhtml/methods/klarnapaylater.xml index f7433935288..1c667de6a76 100644 --- a/etc/adminhtml/methods/klarnapaylater.xml +++ b/etc/adminhtml/methods/klarnapaylater.xml @@ -4,13 +4,13 @@ - Magento\Config\Model\Config\Source\Yesno payment/mollie_methods_klarnapaylater/active - payment/mollie_methods_klarnapaylater/title @@ -18,7 +18,7 @@ 1 - Magento\Payment\Model\Config\Source\Allspecificcountries @@ -27,7 +27,7 @@ 1 - Magento\Directory\Model\Config\Source\Country @@ -37,7 +37,7 @@ 1 - payment/mollie_methods_klarnapaylater/min_order_total @@ -45,7 +45,7 @@ 1 - payment/mollie_methods_klarnapaylater/max_order_total @@ -53,7 +53,26 @@ 1 - + + payment/mollie_methods_klarnapaylater/payment_surcharge + + Mollie\Payment\Model\Adminhtml\Backend\VerifiyPaymentFee + + 1 + + + + + payment/mollie_methods_klarnapaylater/payment_surcharge_tax_class + Magento\Tax\Model\TaxClass\Source\Product + + 1 + + + validate-number diff --git a/etc/adminhtml/methods/klarnasliceit.xml b/etc/adminhtml/methods/klarnasliceit.xml index d5c2dbe7ae2..778fff664ac 100644 --- a/etc/adminhtml/methods/klarnasliceit.xml +++ b/etc/adminhtml/methods/klarnasliceit.xml @@ -4,13 +4,13 @@ - Magento\Config\Model\Config\Source\Yesno payment/mollie_methods_klarnasliceit/active - payment/mollie_methods_klarnasliceit/title @@ -18,7 +18,7 @@ 1 - Magento\Payment\Model\Config\Source\Allspecificcountries @@ -27,7 +27,7 @@ 1 - Magento\Directory\Model\Config\Source\Country @@ -37,7 +37,7 @@ 1 - payment/mollie_methods_klarnasliceit/min_order_total @@ -45,7 +45,7 @@ 1 - payment/mollie_methods_klarnasliceit/max_order_total @@ -53,7 +53,27 @@ 1 - + + payment/mollie_methods_klarnasliceit/payment_surcharge + + Mollie\Payment\Model\Adminhtml\Backend\VerifiyPaymentFee + + 1 + + + + + + payment/mollie_methods_klarnasliceit/payment_surcharge_tax_class + \Magento\Tax\Model\TaxClass\Source\Product + + 1 + + + validate-number diff --git a/etc/adminhtml/methods/paymentlink.xml b/etc/adminhtml/methods/paymentlink.xml index 5b673041ff1..e00dcc111e1 100644 --- a/etc/adminhtml/methods/paymentlink.xml +++ b/etc/adminhtml/methods/paymentlink.xml @@ -4,13 +4,13 @@ - Magento\Config\Model\Config\Source\Yesno payment/mollie_methods_paymentlink/active - payment/mollie_methods_paymentlink/title @@ -18,7 +18,7 @@ 1 - Magento\Config\Model\Config\Source\Yesno @@ -27,7 +27,7 @@ 1 - Magento\Config\Model\Config\Source\Yesno @@ -38,7 +38,15 @@ 1 - + + Mollie\Payment\Model\Adminhtml\Source\NewStatus + payment/mollie_methods_paymentlink/order_status_new + + 1 + + + Magento\Payment\Model\Config\Source\Allspecificcountries @@ -47,7 +55,7 @@ 1 - Magento\Directory\Model\Config\Source\Country @@ -57,7 +65,7 @@ 1 - payment/mollie_methods_paymentlink/min_order_total @@ -65,7 +73,7 @@ 1 - payment/mollie_methods_paymentlink/max_order_total diff --git a/etc/events.xml b/etc/events.xml index 85ee169b1db..d2ef57600fc 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -15,4 +15,7 @@ + + + diff --git a/etc/extension_attributes.xml b/etc/extension_attributes.xml new file mode 100644 index 00000000000..069b63d768a --- /dev/null +++ b/etc/extension_attributes.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/etc/fieldset.xml b/etc/fieldset.xml new file mode 100644 index 00000000000..1c844ae2121 --- /dev/null +++ b/etc/fieldset.xml @@ -0,0 +1,34 @@ + + + +
+ + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +
+
+
diff --git a/etc/module.xml b/etc/module.xml index cf88b7c73c6..7318d10c08f 100644 --- a/etc/module.xml +++ b/etc/module.xml @@ -1,4 +1,4 @@ - + diff --git a/etc/pdf.xml b/etc/pdf.xml new file mode 100644 index 00000000000..aa108d3333f --- /dev/null +++ b/etc/pdf.xml @@ -0,0 +1,13 @@ + + + + + Payment fee + mollie_payment_fee + Mollie\Payment\Model\PaymentFee\Order\Pdf\Total\PaymentFee + 7 + false + 450 + + + diff --git a/etc/sales.xml b/etc/sales.xml new file mode 100644 index 00000000000..9081f7194db --- /dev/null +++ b/etc/sales.xml @@ -0,0 +1,19 @@ + + +
+ + + + +
+
+ + + +
+
+ + + +
+
diff --git a/i18n/en_US.csv b/i18n/en_US.csv index ca60cc8b1b0..12482497da1 100644 --- a/i18n/en_US.csv +++ b/i18n/en_US.csv @@ -89,3 +89,4 @@ "The payment-status will updated automatically by default, but in case of any interruption you can use this function to fetch the payment status manually.","The payment-status will updated automatically by default, but in case of any interruption you can use this function to fetch the payment status manually." When to create the invoice?,When to create the invoice? "When do we create the invoice?
On authorize: Create a full invoice when the order is authorized.
On shipment: Create a (partial) invoice when a shipment is created.","When do we create the invoice?
On authorize: Create a full invoice when the order is authorized.
On shipment: Create a (partial) invoice when a shipment is created." +"Please make sure the payment surcharge does not exceed %1.","Please make sure the payment surcharge does not exceed %1." diff --git a/i18n/nl_NL.csv b/i18n/nl_NL.csv index a31d256adc3..32f684a86f5 100644 --- a/i18n/nl_NL.csv +++ b/i18n/nl_NL.csv @@ -85,4 +85,5 @@ "Error While Fetching","Error Tijdens Ophalen" "Fetch Status","Status Ophalen" "The latest status from Mollie has been retrieved","De laatste status is bij Mollie opgehaald" -"The payment-status will updated automatically by default, but in case of any interruption you can use this function to fetch the payment status manually.","De betaal status wordt standaard bijgewerkt. Mocht dit door omstandigheden niet het geval zijn dan kun u deze hier handmatig laten ophalen." \ No newline at end of file +"The payment-status will updated automatically by default, but in case of any interruption you can use this function to fetch the payment status manually.","De betaal status wordt standaard bijgewerkt. Mocht dit door omstandigheden niet het geval zijn dan kun u deze hier handmatig laten ophalen." +"Please make sure the payment surcharge does not exceed %1.","Zorg ervoor dat de betaal fee niet meer bedraagt dan %1." diff --git a/view/adminhtml/layout/sales_order_creditmemo_new.xml b/view/adminhtml/layout/sales_order_creditmemo_new.xml new file mode 100644 index 00000000000..ba6bd18084b --- /dev/null +++ b/view/adminhtml/layout/sales_order_creditmemo_new.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml b/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml new file mode 100644 index 00000000000..ba6bd18084b --- /dev/null +++ b/view/adminhtml/layout/sales_order_creditmemo_updateqty.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/adminhtml/layout/sales_order_creditmemo_view.xml b/view/adminhtml/layout/sales_order_creditmemo_view.xml new file mode 100644 index 00000000000..a28c2cc4c65 --- /dev/null +++ b/view/adminhtml/layout/sales_order_creditmemo_view.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/adminhtml/layout/sales_order_invoice_view.xml b/view/adminhtml/layout/sales_order_invoice_view.xml new file mode 100644 index 00000000000..c24ccad8149 --- /dev/null +++ b/view/adminhtml/layout/sales_order_invoice_view.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/adminhtml/layout/sales_order_view.xml b/view/adminhtml/layout/sales_order_view.xml new file mode 100644 index 00000000000..6727489b28c --- /dev/null +++ b/view/adminhtml/layout/sales_order_view.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml index 3b8b7924f88..fd91ae2e8b4 100644 --- a/view/frontend/layout/checkout_index_index.xml +++ b/view/frontend/layout/checkout_index_index.xml @@ -83,6 +83,46 @@ + + + + Mollie_Payment/js/view/checkout/save-payment-method + before-place-order + checkoutProvider + + + + + + + + + + + + + + + + + Mollie_Payment/js/view/checkout/summary/payment-fee + 20 + + Mollie_Payment/checkout/summary/payment-fee + Payment Fee + + + + + + + + + + Magento_Tax/js/view/checkout/summary/item/details/subtotal + + + diff --git a/view/frontend/layout/sales_email_order_creditmemo_items.xml b/view/frontend/layout/sales_email_order_creditmemo_items.xml new file mode 100644 index 00000000000..a28c2cc4c65 --- /dev/null +++ b/view/frontend/layout/sales_email_order_creditmemo_items.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_email_order_invoice_items.xml b/view/frontend/layout/sales_email_order_invoice_items.xml new file mode 100644 index 00000000000..f8377254659 --- /dev/null +++ b/view/frontend/layout/sales_email_order_invoice_items.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_email_order_items.xml b/view/frontend/layout/sales_email_order_items.xml new file mode 100644 index 00000000000..d05c813de6c --- /dev/null +++ b/view/frontend/layout/sales_email_order_items.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_order_creditmemo.xml b/view/frontend/layout/sales_order_creditmemo.xml new file mode 100644 index 00000000000..a28c2cc4c65 --- /dev/null +++ b/view/frontend/layout/sales_order_creditmemo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_order_invoice.xml b/view/frontend/layout/sales_order_invoice.xml new file mode 100644 index 00000000000..f8377254659 --- /dev/null +++ b/view/frontend/layout/sales_order_invoice.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_order_print.xml b/view/frontend/layout/sales_order_print.xml new file mode 100644 index 00000000000..d05c813de6c --- /dev/null +++ b/view/frontend/layout/sales_order_print.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_order_printcreditmemo.xml b/view/frontend/layout/sales_order_printcreditmemo.xml new file mode 100644 index 00000000000..a28c2cc4c65 --- /dev/null +++ b/view/frontend/layout/sales_order_printcreditmemo.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/layout/sales_order_printinvoice.xml b/view/frontend/layout/sales_order_printinvoice.xml new file mode 100644 index 00000000000..5f1a887c579 --- /dev/null +++ b/view/frontend/layout/sales_order_printinvoice.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/view/frontend/layout/sales_order_view.xml b/view/frontend/layout/sales_order_view.xml new file mode 100644 index 00000000000..d05c813de6c --- /dev/null +++ b/view/frontend/layout/sales_order_view.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/view/frontend/web/js/view/checkout/save-payment-method.js b/view/frontend/web/js/view/checkout/save-payment-method.js new file mode 100644 index 00000000000..2a495f3a58f --- /dev/null +++ b/view/frontend/web/js/view/checkout/save-payment-method.js @@ -0,0 +1,74 @@ +define([ + 'uiComponent', + 'mage/storage', + 'Magento_Checkout/js/model/quote', + 'Magento_Checkout/js/model/resource-url-manager', + 'Magento_Checkout/js/model/totals', + 'Magento_Checkout/js/action/get-totals' +], function ( + Component, + storage, + quote, + resourceUrlManager, + totals, + reloadTotals +) { + var isApplicableMethod = function (method) { + return method === 'mollie_methods_klarnapaylater' || method === 'mollie_methods_klarnasliceit'; + }; + + return Component.extend({ + initialize: function () { + this._super(); + + var oldMethod; + quote.paymentMethod.subscribe( function (oldValue) { + if (oldValue && oldValue.method) { + oldMethod = oldValue.method; + } + }, this, 'beforeChange'); + + quote.paymentMethod.subscribe( function (newValue) { + // If the old method was a payment fee method we also need to update + if (isApplicableMethod(oldMethod) || isApplicableMethod(newValue.method)) { + this.savePaymentMethod(newValue.method); + } + }.bind(this)); + }, + + savePaymentMethod: function (method) { + var params = {}; + var payload = {}; + + if (resourceUrlManager.getCheckoutMethod() === 'guest') { + params = { + cartId: quote.getQuoteId() + }; + payload.email = quote.guestEmail; + } + + var urls = { + 'guest': '/guest-carts/:cartId/set-payment-information', + 'customer': '/carts/mine/set-payment-information' + }; + var url = resourceUrlManager.getUrl(urls, params); + + payload.paymentMethod = { + method: method + }; + + payload.billingAddress = quote.billingAddress(); + + totals.isLoading(true); + storage.post( + url, + JSON.stringify(payload) + ).done( function () { + reloadTotals([]); + totals.isLoading(false); + }).error( function () { + totals.isLoading(false); + }); + } + }); +}); diff --git a/view/frontend/web/js/view/checkout/summary/payment-fee.js b/view/frontend/web/js/view/checkout/summary/payment-fee.js new file mode 100644 index 00000000000..35bfaf2eb20 --- /dev/null +++ b/view/frontend/web/js/view/checkout/summary/payment-fee.js @@ -0,0 +1,49 @@ +define( + [ + 'knockout', + 'Magento_Checkout/js/view/summary/abstract-total', + 'Magento_Checkout/js/model/quote', + 'Magento_Catalog/js/price-utils', + 'Magento_Checkout/js/model/totals' + ], + function ( + ko, + Component, + quote, + priceUtils, + totals + ) { + return Component.extend({ + defaults: { + isFullTaxSummaryDisplayed: window.checkoutConfig.isFullTaxSummaryDisplayed || false, + template: 'Mollie_Payment/checkout/summary/payment-fee' + }, + + totals: quote.getTotals(), + isTaxDisplayedInGrandTotal: window.checkoutConfig.includeTaxInGrandTotal || false, + + initialize() { + this._super(); + + this.price = ko.computed( function () { + var price = 0, + segment = totals.getSegment('mollie_payment_fee'); + + if (this.totals() && segment) { + price = segment.value; + } + + return price; + }, this); + }, + + isDisplayed: function() { + return this.price(); + }, + + getValue: function() { + return this.getFormattedPrice(this.price()); + } + }); + } +); diff --git a/view/frontend/web/template/checkout/summary/payment-fee.html b/view/frontend/web/template/checkout/summary/payment-fee.html new file mode 100644 index 00000000000..ee57c1fdbc2 --- /dev/null +++ b/view/frontend/web/template/checkout/summary/payment-fee.html @@ -0,0 +1,10 @@ + + + + + + + + + +