From 0ead8d78ed16dbcd4d1938fc8157fcf0da3c81f3 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 4 Sep 2023 10:21:07 +0200 Subject: [PATCH 01/14] Improvement: Point Of Sale icon update --- view/adminhtml/web/images/pointofsale.svg | 39 ++++++------------- .../web/images/methods/pointofsale.svg | 39 ++++++------------- 2 files changed, 22 insertions(+), 56 deletions(-) diff --git a/view/adminhtml/web/images/pointofsale.svg b/view/adminhtml/web/images/pointofsale.svg index 117d4b18770..e1edfe808fa 100644 --- a/view/adminhtml/web/images/pointofsale.svg +++ b/view/adminhtml/web/images/pointofsale.svg @@ -1,29 +1,12 @@ - - - - - - + + + + + + + + + + + diff --git a/view/frontend/web/images/methods/pointofsale.svg b/view/frontend/web/images/methods/pointofsale.svg index 117d4b18770..e1edfe808fa 100644 --- a/view/frontend/web/images/methods/pointofsale.svg +++ b/view/frontend/web/images/methods/pointofsale.svg @@ -1,29 +1,12 @@ - - - - - - + + + + + + + + + + + From eb2544a781b286abcdbf09a5ffc08080fc76dab5 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Tue, 5 Sep 2023 09:33:10 +0200 Subject: [PATCH 02/14] Bugfix: Make sure the link to the payment page is in the first order confirmation --- Block/Info/Base.php | 68 +++++++----------------- Service/OrderLockService.php | 11 ++-- Test/Integration/Block/Info/BaseTest.php | 11 ++-- 3 files changed, 32 insertions(+), 58 deletions(-) diff --git a/Block/Info/Base.php b/Block/Info/Base.php index f1151dc66ed..51f0184b4db 100644 --- a/Block/Info/Base.php +++ b/Block/Info/Base.php @@ -34,12 +34,10 @@ class Base extends Info * @var DateTime\TimezoneInterface */ private $timezone; - /** * @var Registry */ private $registry; - /** * @var PriceCurrencyInterface */ @@ -66,23 +64,17 @@ public function __construct( $this->price = $price; } - /** - * @return bool|string - */ - public function getCheckoutType() + public function getCheckoutType(): ?string { try { return $this->getInfo()->getAdditionalInformation('checkout_type'); } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); - return false; + return null; } } - /** - * @return bool|string - */ - public function getExpiresAt() + public function getExpiresAt(): ?string { try { if ($expiresAt = $this->getInfo()->getAdditionalInformation('expires_at')) { @@ -92,80 +84,67 @@ public function getExpiresAt() $this->mollieHelper->addTolog('error', $e->getMessage()); } - return false; + return null; } /** - * @param int|null $storeId - * @return bool|string + * @param mixed $storeId */ - public function getPaymentLink($storeId = null) + public function getPaymentLink($storeId = null): ?string { if ($checkoutUrl = $this->getCheckoutUrl()) { return $this->mollieHelper->getPaymentLinkMessage($checkoutUrl, $storeId); } - return false; + return null; } - /** - * @return bool|string - */ - public function getCheckoutUrl() + public function getCheckoutUrl(): ?string { try { return $this->getInfo()->getAdditionalInformation('checkout_url'); } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); - return false; + return null; } } - /** - * @return bool|string - */ - public function getPaymentStatus() + public function getPaymentStatus(): ?string { try { return $this->getInfo()->getAdditionalInformation('payment_status'); } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); - return false; + return null; } } - /** - * @return bool|string - */ - public function getDashboardUrl() + public function getDashboardUrl(): ?string { try { return $this->getInfo()->getAdditionalInformation('dashboard_url'); } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); - return false; + return null; } } - public function getChangePaymentStatusUrl(): string + public function getChangePaymentStatusUrl(): ?string { try { return (string)$this->getInfo()->getAdditionalInformation('mollie_change_payment_state_url'); } catch (\Exception $exception) { - return ''; + return null; } } - /** - * @return bool|string - */ - public function getMollieId() + public function getMollieId(): ?string { try { return $this->getInfo()->getAdditionalInformation('mollie_id'); } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); - return false; + return null; } } @@ -189,10 +168,9 @@ public function isBuyNowPayLaterMethod(): bool } /** - * @return mixed * @throws \Magento\Framework\Exception\LocalizedException */ - public function getPaymentImage() + public function getPaymentImage(): string { $code = $this->getInfo()->getMethod(); if (strpos($code, 'mollie_methods_') !== false) { @@ -202,10 +180,7 @@ public function getPaymentImage() return $code . '.svg'; } - /** - * @return string|null - */ - public function getOrderId() + public function getOrderId(): ?string { try { return $this->getInfo()->getParentId(); @@ -240,10 +215,7 @@ public function formatPrice($amount) return $this->price->format($amount); } - /** - * @return OrderInterface|null - */ - private function getOrder() + private function getOrder(): ?OrderInterface { return $this->registry->registry('current_order'); } diff --git a/Service/OrderLockService.php b/Service/OrderLockService.php index a414273dc78..6a11c6343cf 100644 --- a/Service/OrderLockService.php +++ b/Service/OrderLockService.php @@ -43,9 +43,9 @@ public function __construct( $this->config = $config; } - public function execute(OrderInterface $order, callable $callback) + public function execute(OrderInterface $originalOrder, callable $callback) { - $key = $this->getKeyName($order); + $key = $this->getKeyName($originalOrder); if ($this->lockService->checkIfIsLockedWithWait($key)) { throw new LocalizedException(__('Unable to get lock for %1', $key)); } @@ -59,13 +59,13 @@ public function execute(OrderInterface $order, callable $callback) $connection->beginTransaction(); // Save this value, so we can restore it after the order has been saved. - $mollieTransactionId = $order->getMollieTransactionId(); + $mollieTransactionId = $originalOrder->getMollieTransactionId(); // The order repository uses caching to make sure it only loads the order once, but in this case we want // the latest version of the order, so we need to make sure we get a new instance of the repository. /** @var OrderRepositoryInterface $orderRepository */ $orderRepository = $this->orderRepositoryFactory->create(); - $order = $orderRepository->get($order->getEntityId()); + $order = $orderRepository->get($originalOrder->getEntityId()); // Restore the transaction ID as it might not be set on the saved order yet. // This is required further down the process. @@ -75,6 +75,9 @@ public function execute(OrderInterface $order, callable $callback) $result = $callback($order); $orderRepository->save($order); $connection->commit(); + + // Update the original order with the new data. + $originalOrder->setData($order->getData()); } catch (\Exception $e) { $connection->rollBack(); throw $e; diff --git a/Test/Integration/Block/Info/BaseTest.php b/Test/Integration/Block/Info/BaseTest.php index c9105ae0dd3..be349cfb4b5 100644 --- a/Test/Integration/Block/Info/BaseTest.php +++ b/Test/Integration/Block/Info/BaseTest.php @@ -36,23 +36,22 @@ public function testReturnsTheMollieId() $this->assertEquals('ord_123abc', $instance->getMollieId()); } - public function returnsFalseWhenInfoIsNotAvailable() + public function returnsNullWhenInfoIsNotAvailable() { return [ ['getDashboardUrl'], ['getMollieId'], - ['getRemainderAmount'], ]; } /** - * @dataProvider returnsFalseWhenInfoIsNotAvailable + * @dataProvider returnsNullWhenInfoIsNotAvailable */ - public function testReturnsFalseWhenInfoIsNotAvailable($method) + public function testReturnsNullWhenInfoIsNotAvailable($method) { /** @var Base $instance */ $instance = $this->objectManager->create(Base::class); - $this->assertFalse($instance->{$method}()); + $this->assertNull($instance->{$method}()); } public function testReturnsTheRemainderAmount() @@ -66,4 +65,4 @@ public function testReturnsTheRemainderAmount() $instance->setData('info', $info); $this->assertEquals('100', $instance->getRemainderAmount()); } -} \ No newline at end of file +} From 5618934d5975e234d83c75ef5e41e65ca9dc3d61 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Tue, 5 Sep 2023 11:32:18 +0200 Subject: [PATCH 03/14] Bugfix: Add the shipping method title to the shipping methods shown in Apple Pay --- Controller/ApplePay/ShippingMethods.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Controller/ApplePay/ShippingMethods.php b/Controller/ApplePay/ShippingMethods.php index 50be29895ce..6d4377f3dfe 100644 --- a/Controller/ApplePay/ShippingMethods.php +++ b/Controller/ApplePay/ShippingMethods.php @@ -110,7 +110,7 @@ public function execute() 'shipping_methods' => array_map(function ($method) { return [ 'identifier' => $method->getCarrierCode() . '_' . $method->getMethodCode(), - 'label' => $method->getCarrierTitle(), + 'label' => $method->getMethodTitle() . ' - ' . $method->getCarrierTitle(), 'amount' => number_format($method->getPriceInclTax(), 2, '.', ''), 'detail' => '', ]; From f3b5beee0e7bf957c3e910d9d1b1c3b17f93634e Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Tue, 5 Sep 2023 12:48:45 +0200 Subject: [PATCH 04/14] Improvement: Better message for disabled methods --- Config.php | 2 +- Observer/ConfigObserver.php | 43 ++++++++++++------- etc/adminhtml/di.xml | 13 ++++++ etc/events.xml | 2 +- .../config/message/unavailable-methods.phtml | 15 +++++++ 5 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 view/adminhtml/templates/system/config/message/unavailable-methods.phtml diff --git a/Config.php b/Config.php index b428598dc9e..9fcb87b2724 100644 --- a/Config.php +++ b/Config.php @@ -586,7 +586,7 @@ public function pointofsaleAllowedCustomerGroups(int $storeId = null) * @param null|int|string $storeId * @return string */ - public function getMethodTitle($method, $storeId = null) + public function getMethodTitle($method, $storeId = null): string { return $this->getPath($this->addMethodToPath(static::PAYMENT_METHOD_PAYMENT_TITLE, $method), $storeId); } diff --git a/Observer/ConfigObserver.php b/Observer/ConfigObserver.php index f7641f37b04..e9d09a17f21 100644 --- a/Observer/ConfigObserver.php +++ b/Observer/ConfigObserver.php @@ -9,6 +9,8 @@ use Magento\Framework\Event\ObserverInterface; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Message\ManagerInterface; +use Mollie\Payment\Config; +use Mollie\Payment\Model\Methods\Pointofsale; use Mollie\Payment\Model\Mollie as MollieModel; use Mollie\Payment\Helper\General as MollieHelper; @@ -32,6 +34,10 @@ class ConfigObserver implements ObserverInterface * @var MollieHelper */ private $mollieHelper; + /** + * @var Config + */ + private $config; /** * ConfigObserver constructor. @@ -43,11 +49,13 @@ class ConfigObserver implements ObserverInterface public function __construct( ManagerInterface $messageManager, MollieModel $mollieModel, - MollieHelper $mollieHelper + MollieHelper $mollieHelper, + Config $config ) { $this->messageManager = $messageManager; $this->mollieModel = $mollieModel; $this->mollieHelper = $mollieHelper; + $this->config = $config; } /** @@ -73,20 +81,20 @@ public function execute(EventObserver $observer) * @param $storeId * @param $modus * - * @return mixed + * @return void */ - public function validatePaymentMethods($storeId, $modus) + public function validatePaymentMethods($storeId, string $modus): void { if (!class_exists('Mollie\Api\CompatibilityChecker')) { $error = $this->mollieHelper->getPhpApiErrorMessage(false); $this->mollieHelper->disableExtension(); $this->mollieHelper->addTolog('error', $error); $this->messageManager->addErrorMessage($error); - return false; + return; } if ($modus == 'test') { - return false; + return; } try { @@ -94,11 +102,11 @@ public function validatePaymentMethods($storeId, $modus) } catch (\Exception $e) { $this->mollieHelper->addTolog('error', $e->getMessage()); $this->messageManager->addErrorMessage($e->getMessage()); - return false; + return; } if (empty($apiMethods)) { - return false; + return; } $activeMethods = $this->mollieHelper->getAllActiveMethods($storeId); @@ -108,18 +116,21 @@ public function validatePaymentMethods($storeId, $modus) $methods[$apiMethod->id] = $apiMethod; } - $errors = []; - foreach ($activeMethods as $k => $v) { - $code = $v['code']; - if (!isset($methods[$code])) { - $errors[] = __('%1: method not enabled in Mollie Dashboard', ucfirst($code)); - continue; + $disabledMethods = []; + foreach ($activeMethods as $method) { + $code = $method['code']; + if ($code != Pointofsale::CODE && !isset($methods[$code])) { + $disabledMethods[] = $this->config->getMethodTitle($code); } } - if (!empty($errors)) { - $errorMethods = implode(', ', $errors); - $this->messageManager->addErrorMessage($errorMethods); + if ($disabledMethods) { + $this->messageManager->addComplexErrorMessage( + 'MollieUnavailableMethodsMessage', + [ + 'methods' => $disabledMethods, + ] + ); } } } diff --git a/etc/adminhtml/di.xml b/etc/adminhtml/di.xml index 495856351c3..62e46de1711 100644 --- a/etc/adminhtml/di.xml +++ b/etc/adminhtml/di.xml @@ -31,4 +31,17 @@ + + + + + + \Magento\Framework\View\Element\Message\Renderer\BlockRenderer::CODE + + Mollie_Payment::system/config/message/unavailable-methods.phtml + + + + + diff --git a/etc/events.xml b/etc/events.xml index dceb43120be..870d48fd088 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -1,6 +1,6 @@ - + diff --git a/view/adminhtml/templates/system/config/message/unavailable-methods.phtml b/view/adminhtml/templates/system/config/message/unavailable-methods.phtml new file mode 100644 index 00000000000..c23c72b6ec1 --- /dev/null +++ b/view/adminhtml/templates/system/config/message/unavailable-methods.phtml @@ -0,0 +1,15 @@ + +

+ From 8e1767429ac0eea2ef342163ed82e303bcbd4e5f Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Tue, 12 Sep 2023 09:21:21 +0200 Subject: [PATCH 05/14] Typo: ordersProcessTraction -> ordersProcessTransaction #690 --- Model/Methods/Reorder.php | 4 ++-- Model/Methods/Voucher.php | 4 ++-- Model/Mollie.php | 8 ++++---- Test/Integration/Model/MollieTest.php | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Model/Methods/Reorder.php b/Model/Methods/Reorder.php index 9364175b0d5..15de29109a7 100644 --- a/Model/Methods/Reorder.php +++ b/Model/Methods/Reorder.php @@ -66,7 +66,7 @@ public function __construct( AssetRepository $assetRepository, Config $config, Timeout $timeout, - ProcessTransaction $ordersProcessTraction, + ProcessTransaction $ordersProcessTransaction, OrderLockService $orderLockService, MollieApiClient $mollieApiClient, TransactionToOrderRepositoryInterface $transactionToOrderRepository, @@ -93,7 +93,7 @@ public function __construct( $assetRepository, $config, $timeout, - $ordersProcessTraction, + $ordersProcessTransaction, $orderLockService, $mollieApiClient, $transactionToOrderRepository, diff --git a/Model/Methods/Voucher.php b/Model/Methods/Voucher.php index c5e000095d3..f31c53ffbf4 100644 --- a/Model/Methods/Voucher.php +++ b/Model/Methods/Voucher.php @@ -62,7 +62,7 @@ public function __construct( AssetRepository $assetRepository, Config $config, Timeout $timeout, - ProcessTransaction $ordersProcessTraction, + ProcessTransaction $ordersProcessTransaction, OrderLockService $orderLockService, MollieApiClient $mollieApiClient, TransactionToOrderRepositoryInterface $transactionToOrderRepository, @@ -89,7 +89,7 @@ public function __construct( $assetRepository, $config, $timeout, - $ordersProcessTraction, + $ordersProcessTransaction, $orderLockService, $mollieApiClient, $transactionToOrderRepository, diff --git a/Model/Mollie.php b/Model/Mollie.php index 4afc1d16224..2c7af59ac87 100644 --- a/Model/Mollie.php +++ b/Model/Mollie.php @@ -99,7 +99,7 @@ class Mollie extends Adapter /** * @var ProcessTransaction */ - private $ordersProcessTraction; + private $ordersProcessTransaction; /** * @var OrderLockService @@ -131,7 +131,7 @@ public function __construct( AssetRepository $assetRepository, Config $config, Timeout $timeout, - ProcessTransaction $ordersProcessTraction, + ProcessTransaction $ordersProcessTransaction, OrderLockService $orderLockService, \Mollie\Payment\Service\Mollie\MollieApiClient $mollieApiClient, TransactionToOrderRepositoryInterface $transactionToOrderRepository, @@ -167,7 +167,7 @@ public function __construct( $this->assetRepository = $assetRepository; $this->config = $config; $this->timeout = $timeout; - $this->ordersProcessTraction = $ordersProcessTraction; + $this->ordersProcessTransaction = $ordersProcessTransaction; $this->orderLockService = $orderLockService; $this->mollieApiClient = $mollieApiClient; $this->transactionToOrderRepository = $transactionToOrderRepository; @@ -363,7 +363,7 @@ public function processTransactionForOrder(OrderInterface $order, $type = 'webho $paymentToken ) { if (substr($transactionId, 0, 4) == 'ord_') { - $result = $this->ordersProcessTraction->execute($order, $type)->toArray(); + $result = $this->ordersProcessTransaction->execute($order, $type)->toArray(); } else { $mollieApi = $this->mollieApiClient->loadByStore($order->getStoreId()); $result = $this->paymentsApi->processTransaction($order, $mollieApi, $type, $paymentToken); diff --git a/Test/Integration/Model/MollieTest.php b/Test/Integration/Model/MollieTest.php index 706dadf012d..1e325fa163b 100644 --- a/Test/Integration/Model/MollieTest.php +++ b/Test/Integration/Model/MollieTest.php @@ -72,7 +72,7 @@ public function testProcessTransactionUsesTheCorrectApi($orderId, $type) $instance = $this->objectManager->create(Mollie::class, [ 'paymentsApi' => $paymentsApiMock, 'mollieHelper' => $mollieHelperMock, - 'ordersProcessTraction' => $orderProcessTransactionFake, + 'ordersProcessTransaction' => $orderProcessTransactionFake, 'mollieApiClient' => $mollieApiMock, ]); From e2640ec0f0724fe9eee7b81cd01c206d35c88f67 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Tue, 12 Sep 2023 15:12:27 +0200 Subject: [PATCH 06/14] Improvement: Remove log line for clarity #689 --- Block/Form/Pointofsale.php | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Block/Form/Pointofsale.php b/Block/Form/Pointofsale.php index f65a1649596..7f197ad22e8 100644 --- a/Block/Form/Pointofsale.php +++ b/Block/Form/Pointofsale.php @@ -8,8 +8,8 @@ use Magento\Framework\View\Element\Template\Context; use Magento\Payment\Block\Form; +use Mollie\Api\Exceptions\ApiException; use Mollie\Api\Resources\Terminal; -use Mollie\Payment\Logger\MollieLogger; use Mollie\Payment\Service\Mollie\MollieApiClient; /** @@ -27,21 +27,15 @@ class Pointofsale extends Form * @var MollieApiClient */ private $mollieApiClient; - /** - * @var MollieLogger - */ - private $logger; public function __construct( Context $context, MollieApiClient $mollieApiClient, - MollieLogger $logger, array $data = [] ) { parent::__construct($context, $data); $this->mollieApiClient = $mollieApiClient; - $this->logger = $logger; } /** @@ -62,8 +56,7 @@ public function getTerminals(): array try { $mollieApiClient = $this->mollieApiClient->loadByStore((int)$storeId); $terminals = $mollieApiClient->terminals->page(); - } catch (\Mollie\Api\Exceptions\ApiException $exception) { - $this->logger->addErrorLog('terminals', $exception->getMessage()); + } catch (ApiException $exception) { return []; } From 68ccd104c4366a5653a483e487270c4abd038bd7 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 18 Sep 2023 14:32:41 +0200 Subject: [PATCH 07/14] Feature: Implement asynchronous captures --- Model/Client/Payments.php | 13 +++-- Model/Client/Payments/CapturePayment.php | 39 ++++++++------ .../Payments/Processors/SuccessfulPayment.php | 11 +++- .../CaptureInvoice.php | 52 ++++++++++++++++++ .../CreateMollieShipment.php | 4 -- .../Order/CanRegisterCaptureNotification.php | 17 ++++-- .../Mollie/Order/CreateInvoiceOnShipment.php | 2 +- .../e2e/magento/backend/manual-capture.cy.js | 54 +++++++++++++++---- Test/End-2-end/cypress/support/commands.js | 6 ++- .../support/pages/backend/InvoicePage.js | 20 +++++++ .../support/pages/backend/OrdersPage.js | 6 +++ .../CanRegisterCaptureNotificationTest.php | 29 ++++++++-- .../Order/CreateInvoiceOnShipmentTest.php | 4 +- etc/events.xml | 3 ++ 14 files changed, 213 insertions(+), 47 deletions(-) create mode 100644 Observer/SalesOrderInvoiceRegister/CaptureInvoice.php create mode 100644 Test/End-2-end/cypress/support/pages/backend/InvoicePage.js diff --git a/Model/Client/Payments.php b/Model/Client/Payments.php index 55522187658..34bc74936f5 100644 --- a/Model/Client/Payments.php +++ b/Model/Client/Payments.php @@ -12,13 +12,11 @@ use Magento\Framework\Model\AbstractModel; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Model\Order; -use Magento\Sales\Model\Order\Invoice; use Magento\Sales\Model\OrderRepository; use Mollie\Api\MollieApiClient; use Mollie\Api\Resources\Payment as MolliePayment; use Mollie\Api\Types\PaymentStatus; use Mollie\Payment\Helper\General as MollieHelper; -use Mollie\Payment\Model\Adminhtml\Source\InvoiceMoment; use Mollie\Payment\Model\Client\Payments\ProcessTransaction; use Mollie\Payment\Service\Mollie\DashboardUrl; use Mollie\Payment\Service\Mollie\Order\CanRegisterCaptureNotification; @@ -386,9 +384,18 @@ public function processTransaction(Order $order, $mollieApi, $type = 'webhook', $payment->setCurrencyCode($order->getBaseCurrencyCode()); $payment->setIsTransactionClosed(true); - if ($this->canRegisterCaptureNotification->execute($order) || + if ($this->canRegisterCaptureNotification->execute($order, $paymentData) || $type != static::TRANSACTION_TYPE_WEBHOOK ) { + if ($paymentData->getAmountCaptured() != 0.0) { + $order->addCommentToStatusHistory( + __( + 'Successfully captured an amount of %1.', + $order->getBaseCurrency()->formatTxt($paymentData->getAmountCaptured()) + ) + ); + } + $payment->registerCaptureNotification($order->getBaseGrandTotal(), true); } diff --git a/Model/Client/Payments/CapturePayment.php b/Model/Client/Payments/CapturePayment.php index c7f4fd06ddd..858158d7150 100644 --- a/Model/Client/Payments/CapturePayment.php +++ b/Model/Client/Payments/CapturePayment.php @@ -2,9 +2,12 @@ namespace Mollie\Payment\Model\Client\Payments; +use Magento\Framework\Pricing\PriceCurrencyInterface; +use Magento\Sales\Api\Data\InvoiceInterface; use Magento\Sales\Api\Data\OrderInterface; use Magento\Sales\Api\Data\ShipmentInterface; use Magento\Sales\Api\OrderRepositoryInterface; +use Magento\Sales\Model\Order; use Magento\Sales\Model\Order\Email\Sender\InvoiceSender; use Mollie\Payment\Helper\General; use Mollie\Payment\Service\Mollie\MollieApiClient; @@ -47,6 +50,10 @@ class CapturePayment * @var ShouldEmailInvoice */ private $shouldEmailInvoice; + /** + * @var PriceCurrencyInterface + */ + private $price; public function __construct( MollieApiClient $mollieApiClient, @@ -55,7 +62,8 @@ public function __construct( OrderRepositoryInterface $orderRepository, InvoiceSender $invoiceSender, OrderCommentHistory $orderCommentHistory, - ShouldEmailInvoice $shouldEmailInvoice + ShouldEmailInvoice $shouldEmailInvoice, + PriceCurrencyInterface $price ) { $this->mollieApiClient = $mollieApiClient; $this->partialInvoice = $partialInvoice; @@ -64,15 +72,16 @@ public function __construct( $this->invoiceSender = $invoiceSender; $this->orderCommentHistory = $orderCommentHistory; $this->shouldEmailInvoice = $shouldEmailInvoice; + $this->price = $price; } - public function execute(ShipmentInterface $shipment, OrderInterface $order): void + public function execute(InvoiceInterface $invoice): void { + $order = $invoice->getOrder(); $payment = $order->getPayment(); - $invoice = $this->partialInvoice->createFromShipment($shipment); - if (!$invoice) { - return; - } + + $order->setState(Order::STATE_PAYMENT_REVIEW); + $status = $order->getConfig()->getStateDefaultStatus(Order::STATE_PAYMENT_REVIEW); $captureAmount = $invoice->getBaseGrandTotal(); @@ -88,17 +97,15 @@ public function execute(ShipmentInterface $shipment, OrderInterface $order): voi } $capture = $mollieApi->paymentCaptures->createForId($mollieTransactionId, $data); - $payment->setTransactionId($capture->id); - $payment->registerCaptureNotification($captureAmount, true); - - $this->orderRepository->save($order); - $sendInvoice = $this->shouldEmailInvoice->execute($order->getStoreId(), $payment->getMethod()); - if ($invoice && $invoice->getId() && !$invoice->getEmailSent() && $sendInvoice) { - $this->invoiceSender->send($invoice); - $message = __('Notified customer about invoice #%1', $invoice->getIncrementId()); - $this->orderCommentHistory->add($order, $message, true); - } + $order->addCommentToStatusHistory( + __( + 'Trying to capture %1. Capture ID: %2', + $this->price->format($captureAmount), + $capture->id + ), + $status + ); } } diff --git a/Model/Client/Payments/Processors/SuccessfulPayment.php b/Model/Client/Payments/Processors/SuccessfulPayment.php index bb98a20c40c..52b6d0e688d 100644 --- a/Model/Client/Payments/Processors/SuccessfulPayment.php +++ b/Model/Client/Payments/Processors/SuccessfulPayment.php @@ -155,9 +155,18 @@ private function handlePayment(OrderInterface $magentoOrder, Payment $molliePaym $payment->setTransactionId($magentoOrder->getMollieTransactionId()); $payment->setIsTransactionClosed(true); - if ($this->canRegisterCaptureNotification->execute($magentoOrder) || + if ($this->canRegisterCaptureNotification->execute($magentoOrder, $molliePayment) || $type != Payments::TRANSACTION_TYPE_SUBSCRIPTION ) { + if ($molliePayment->getAmountCaptured() != 0.0) { + $magentoOrder->addCommentToStatusHistory( + __( + 'Successfully captured amount of %1.', + $magentoOrder->getBaseCurrency()->formatTxt($molliePayment->getAmountCaptured()) + ) + ); + } + $payment->registerCaptureNotification($magentoOrder->getBaseGrandTotal(), true); } diff --git a/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php b/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php new file mode 100644 index 00000000000..514d1aa01f5 --- /dev/null +++ b/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php @@ -0,0 +1,52 @@ +capturePayment = $capturePayment; + $this->config = $config; + } + + public function execute(Observer $observer) + { + /** @var OrderInterface $order */ + $order = $observer->getData('order'); + $transactionId = $order->getMollieTransactionId() ?? ''; + $useOrdersApi = substr($transactionId, 0, 4) == 'ord_'; + if ($useOrdersApi || !$this->config->useManualCapture((int)$order->getStoreId())) { + return; + } + + /** @var InvoiceInterface $invoice */ + $invoice = $observer->getData('invoice'); + + $this->capturePayment->execute($invoice); + } +} diff --git a/Observer/SalesOrderShipmentSaveBefore/CreateMollieShipment.php b/Observer/SalesOrderShipmentSaveBefore/CreateMollieShipment.php index 5e7ce3dc2f9..f3dcd80b084 100644 --- a/Observer/SalesOrderShipmentSaveBefore/CreateMollieShipment.php +++ b/Observer/SalesOrderShipmentSaveBefore/CreateMollieShipment.php @@ -59,9 +59,5 @@ public function execute(Observer $observer) if ($useOrdersApi) { $this->ordersApi->createShipment($shipment, $order); } - - if (!$useOrdersApi && $this->config->useManualCapture((int)$order->getStoreId())) { - $this->capturePayment->execute($shipment, $order); - } } } diff --git a/Service/Mollie/Order/CanRegisterCaptureNotification.php b/Service/Mollie/Order/CanRegisterCaptureNotification.php index 553ffbbdb35..e935b33d349 100644 --- a/Service/Mollie/Order/CanRegisterCaptureNotification.php +++ b/Service/Mollie/Order/CanRegisterCaptureNotification.php @@ -1,8 +1,15 @@ config = $config; } - public function execute(OrderInterface $order): bool + public function execute(OrderInterface $order, Payment $molliePayment): bool { - if ($this->config->useManualCapture($order->getStoreId()) && - $order->getPayment()->getMethod() == Creditcard::CODE + if (!$this->config->useManualCapture($order->getStoreId()) || + $order->getPayment()->getMethod() != Creditcard::CODE ) { - return false; + return true; } - return true; + return $molliePayment->isPaid() && $molliePayment->getAmountCaptured() !== 0.0; } } diff --git a/Service/Mollie/Order/CreateInvoiceOnShipment.php b/Service/Mollie/Order/CreateInvoiceOnShipment.php index e624ab6b9fb..3c642f973b6 100644 --- a/Service/Mollie/Order/CreateInvoiceOnShipment.php +++ b/Service/Mollie/Order/CreateInvoiceOnShipment.php @@ -38,7 +38,7 @@ public function execute(OrderInterface $order): bool $this->config->useManualCapture($order->getStoreId()) && $api == 'payments' ) { - return true; + return false; } return false; diff --git a/Test/End-2-end/cypress/e2e/magento/backend/manual-capture.cy.js b/Test/End-2-end/cypress/e2e/magento/backend/manual-capture.cy.js index 1a7c1157ef4..53452ea0497 100644 --- a/Test/End-2-end/cypress/e2e/magento/backend/manual-capture.cy.js +++ b/Test/End-2-end/cypress/e2e/magento/backend/manual-capture.cy.js @@ -5,7 +5,7 @@ import ComponentsAction from "Actions/checkout/ComponentsAction"; import MollieHostedPaymentPage from "Pages/mollie/MollieHostedPaymentPage"; import CheckoutSuccessPage from "Pages/frontend/CheckoutSuccessPage"; import OrdersPage from "Pages/backend/OrdersPage"; -import ShipmentPage from "Pages/backend/ShipmentPage"; +import InvoicePage from "Pages/backend/InvoicePage"; const configuration = new Configuration(); const checkoutPaymentPage = new CheckoutPaymentPage(); @@ -14,17 +14,17 @@ const components = new ComponentsAction(); const mollieHostedPaymentPage = new MollieHostedPaymentPage(); const checkoutSuccessPage = new CheckoutSuccessPage(); const ordersPage = new OrdersPage(); -const shipmentPage = new ShipmentPage(); +const invoicePage = new InvoicePage(); describe('Manual capture works as expected', () => { after(() => { - cy.backendLogin(); + cy.backendLogin(false); // Make sure to set this back to No to not influence other tests configuration.setValue('Advanced', 'Triggers & Languages', 'Manual Capture', 'No'); }); - it('C1064183: Validate that with manual capture enabled the invoice is created when placing the order', () => { + it('C1064183: Validate that with manual capture disabled the invoice is created when placing the order', () => { configuration.setValue('Advanced', 'Triggers & Languages', 'Manual Capture', 'No'); visitCheckoutPayment.visit(); @@ -57,7 +57,7 @@ describe('Manual capture works as expected', () => { }); }); - it('C1064182: Validate that with manual capture enabled the invoice is created when a shipment is created', () => { + it('C1064182: Validate that with manual capture enabled the invoice is not automatically created', () => { configuration.setValue('Advanced', 'Triggers & Languages', 'Manual Capture', 'Yes'); visitCheckoutPayment.visit(); @@ -88,13 +88,47 @@ describe('Manual capture works as expected', () => { cy.get('@order-id').then((orderId) => { ordersPage.assertOrderHasNoInvoices(orderId); }); + }); - ordersPage.ship(); + it('C1572711: Validate that with manual capture enabled the capture is done when the invoice is created', () => { + configuration.setValue('Advanced', 'Triggers & Languages', 'Manual Capture', 'Yes'); - shipmentPage.ship(); + visitCheckoutPayment.visit(); - cy.get('@order-id').then((orderId) => { - ordersPage.assertOrderHasInvoice(orderId); - }); + checkoutPaymentPage.selectPaymentMethod('Credit Card'); + + components.fillComponentsForm( + 'Mollie Tester', + '3782 822463 10005', + '1230', + '1234' + ); + + checkoutPaymentPage.placeOrder(); + + mollieHostedPaymentPage.selectStatus('authorized'); + + checkoutSuccessPage.assertThatOrderSuccessPageIsShown(); + + cy.backendLogin(); + + cy.get('@order-id').then((orderId) => { + ordersPage.openOrderById(orderId); + }); + + ordersPage.invoice(); + invoicePage.invoice(); + + // Give the webhook some time to process + cy.wait(5000); + cy.reload(); + + cy.contains('Trying to capture').should('be.visible'); + cy.contains('Successfully captured an amount of').should('be.visible'); + cy.contains('Registered notification about captured amount of').should('be.visible'); + + cy.get('@order-id').then((orderId) => { + ordersPage.assertOrderHasInvoice(orderId); + }); }); }); diff --git a/Test/End-2-end/cypress/support/commands.js b/Test/End-2-end/cypress/support/commands.js index 21a6875fcbe..7e4e83f2017 100644 --- a/Test/End-2-end/cypress/support/commands.js +++ b/Test/End-2-end/cypress/support/commands.js @@ -7,7 +7,7 @@ Cypress.log = function (opts, ...other) { return origLog(opts, ...other); }; -Cypress.Commands.add('backendLogin', () => { +Cypress.Commands.add('backendLogin', (visitDashboard = true) => { const username = 'exampleuser'; const password = 'examplepassword123'; @@ -20,7 +20,9 @@ Cypress.Commands.add('backendLogin', () => { cy.url().should('include', '/admin/admin/dashboard'); }); - cy.visit('/admin/admin/dashboard'); + if (visitDashboard) { + cy.visit('/admin/admin/dashboard'); + } }); Cypress.Commands.add('getIframeBody', (selector) => { diff --git a/Test/End-2-end/cypress/support/pages/backend/InvoicePage.js b/Test/End-2-end/cypress/support/pages/backend/InvoicePage.js new file mode 100644 index 00000000000..fd7e1ac8f81 --- /dev/null +++ b/Test/End-2-end/cypress/support/pages/backend/InvoicePage.js @@ -0,0 +1,20 @@ +/* + * Copyright Magmodules.eu. All rights reserved. + * See COPYING.txt for license details. + */ + +export default class InvoicePage { + invoice() { + cy.get('#system_messages').should('have.length.gte', 0); + + // Last element on the page so javascript has time to load + cy.get('.magento-version').should('be.visible'); + cy.wait(500); + + cy.get('[data-ui-id="order-items-submit-button"]').should('be.enabled').click(); + + cy.get('[data-ui-id="sales-order-tabs-tab-sales-order-view-tabs"] .ui-state-active').should('be.visible'); + + cy.url().should('include', '/admin/sales/order/view/order_id/'); + } +} diff --git a/Test/End-2-end/cypress/support/pages/backend/OrdersPage.js b/Test/End-2-end/cypress/support/pages/backend/OrdersPage.js index 7083e6e5d73..56b3b537b33 100644 --- a/Test/End-2-end/cypress/support/pages/backend/OrdersPage.js +++ b/Test/End-2-end/cypress/support/pages/backend/OrdersPage.js @@ -52,4 +52,10 @@ export default class OrdersPage { cy.url().should('include', '/admin/order_shipment/new/order_id/'); } + + invoice() { + cy.get('#order_invoice').should('be.enabled').click(); + + cy.url().should('include', '/admin/sales/order_invoice/new/order_id/'); + } } diff --git a/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php b/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php index bb2e92e2851..cf59c99afce 100644 --- a/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php +++ b/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php @@ -1,7 +1,15 @@ objectManager->create(CanRegisterCaptureNotification::class); - $this->assertTrue($instance->execute($order)); + $molliePayment = new Payment(new MollieApiClient()); + $molliePayment->status = 'paid'; + $molliePayment->amountCaptured = new \stdClass(); + $molliePayment->amountCaptured->value = -999; + + $this->assertTrue($instance->execute($order, $molliePayment)); } /** @@ -35,7 +48,12 @@ public function testCanCaptureWhenEnabledButNotCreditcard(): void /** @var CanRegisterCaptureNotification $instance */ $instance = $this->objectManager->create(CanRegisterCaptureNotification::class); - $this->assertTrue($instance->execute($order)); + $molliePayment = new Payment(new MollieApiClient()); + $molliePayment->status = 'paid'; + $molliePayment->amountCaptured = new \stdClass(); + $molliePayment->amountCaptured->value = -999; + + $this->assertTrue($instance->execute($order, $molliePayment)); } /** @@ -51,6 +69,11 @@ public function testCannotCaptureWhenEnabledAndCreditcard(): void /** @var CanRegisterCaptureNotification $instance */ $instance = $this->objectManager->create(CanRegisterCaptureNotification::class); - $this->assertFalse($instance->execute($order)); + $molliePayment = new Payment(new MollieApiClient()); + $molliePayment->status = 'paid'; + $molliePayment->amountCaptured = new \stdClass(); + $molliePayment->amountCaptured->value = -999; + + $this->assertFalse($instance->execute($order, $molliePayment)); } } diff --git a/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php b/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php index 3dccca7fc3d..3c4552f5bc2 100644 --- a/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php +++ b/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php @@ -41,7 +41,7 @@ public function testIsEnabledByMethod(string $method): void * @magentoConfigFixture default_store payment/mollie_general/enable_manual_capture 1 * @return void */ - public function testIsEnabledWhenCreatePaymentAuthorizationIsEnabledAndApiIsPayments(): void + public function testIsDisabledWhenCreatePaymentAuthorizationIsEnabledAndApiIsPayments(): void { $order = $this->loadOrderById('100000001'); $order->getPayment()->setMethod('mollie_methods_creditcard'); @@ -50,7 +50,7 @@ public function testIsEnabledWhenCreatePaymentAuthorizationIsEnabledAndApiIsPaym /** @var CreateInvoiceOnShipment $instance */ $instance = $this->objectManager->create(CreateInvoiceOnShipment::class); - $this->assertTrue($instance->execute($order)); + $this->assertFalse($instance->execute($order)); } /** diff --git a/etc/events.xml b/etc/events.xml index dceb43120be..70a7a48f8b5 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -61,6 +61,9 @@ + + + From 672cf132d5bae1d28b3f39ab46d2bf9fd450eeed Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 18 Sep 2023 15:49:48 +0200 Subject: [PATCH 08/14] Feature: Async captures for Apple Pay --- .../CaptureInvoice.php | 22 ++++++++-- .../Order/CanRegisterCaptureNotification.php | 14 +++--- Service/Mollie/Order/CanUseManualCapture.php | 43 +++++++++++++++++++ .../Mollie/Order/CreateInvoiceOnShipment.php | 24 ++++++----- Service/Mollie/Order/UsedMollieApi.php | 23 ++++++++++ Service/Order/TransactionPart/CaptureMode.php | 15 +++---- .../CanRegisterCaptureNotificationTest.php | 3 +- .../Order/CreateInvoiceOnShipmentTest.php | 6 ++- .../Order/TransactionPart/CaptureModeTest.php | 6 ++- 9 files changed, 118 insertions(+), 38 deletions(-) create mode 100644 Service/Mollie/Order/CanUseManualCapture.php create mode 100644 Service/Mollie/Order/UsedMollieApi.php diff --git a/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php b/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php index 514d1aa01f5..02a2f778ffa 100644 --- a/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php +++ b/Observer/SalesOrderInvoiceRegister/CaptureInvoice.php @@ -14,6 +14,8 @@ use Magento\Sales\Api\Data\OrderInterface; use Mollie\Payment\Config; use Mollie\Payment\Model\Client\Payments\CapturePayment; +use Mollie\Payment\Service\Mollie\Order\CanUseManualCapture; +use Mollie\Payment\Service\Mollie\Order\UsedMollieApi; class CaptureInvoice implements ObserverInterface { @@ -25,22 +27,34 @@ class CaptureInvoice implements ObserverInterface * @var CapturePayment */ private $capturePayment; + /** + * @var CanUseManualCapture + */ + private $canUseManualCapture; + /** + * @var UsedMollieApi + */ + private $usedMollieApi; public function __construct( Config $config, - CapturePayment $capturePayment + CapturePayment $capturePayment, + CanUseManualCapture $canUseManualCapture, + UsedMollieApi $usedMollieApi ) { $this->capturePayment = $capturePayment; $this->config = $config; + $this->canUseManualCapture = $canUseManualCapture; + $this->usedMollieApi = $usedMollieApi; } public function execute(Observer $observer) { /** @var OrderInterface $order */ $order = $observer->getData('order'); - $transactionId = $order->getMollieTransactionId() ?? ''; - $useOrdersApi = substr($transactionId, 0, 4) == 'ord_'; - if ($useOrdersApi || !$this->config->useManualCapture((int)$order->getStoreId())) { + if ($this->usedMollieApi->execute($order) == UsedMollieApi::TYPE_ORDERS || + !$this->canUseManualCapture->execute($order) + ) { return; } diff --git a/Service/Mollie/Order/CanRegisterCaptureNotification.php b/Service/Mollie/Order/CanRegisterCaptureNotification.php index e935b33d349..06681327e08 100644 --- a/Service/Mollie/Order/CanRegisterCaptureNotification.php +++ b/Service/Mollie/Order/CanRegisterCaptureNotification.php @@ -10,27 +10,23 @@ use Magento\Sales\Api\Data\OrderInterface; use Mollie\Api\Resources\Payment; -use Mollie\Payment\Config; -use Mollie\Payment\Model\Methods\Creditcard; class CanRegisterCaptureNotification { /** - * @var Config + * @var CanUseManualCapture */ - private $config; + private $canUseManualCapture; public function __construct( - Config $config + CanUseManualCapture $canUseManualCapture ) { - $this->config = $config; + $this->canUseManualCapture = $canUseManualCapture; } public function execute(OrderInterface $order, Payment $molliePayment): bool { - if (!$this->config->useManualCapture($order->getStoreId()) || - $order->getPayment()->getMethod() != Creditcard::CODE - ) { + if (!$this->canUseManualCapture->execute($order)) { return true; } diff --git a/Service/Mollie/Order/CanUseManualCapture.php b/Service/Mollie/Order/CanUseManualCapture.php new file mode 100644 index 00000000000..5fabb6285fe --- /dev/null +++ b/Service/Mollie/Order/CanUseManualCapture.php @@ -0,0 +1,43 @@ +config = $config; + } + + public function execute(OrderInterface $order): bool + { + if (!$this->config->useManualCapture((int)$order->getStoreId())) { + return false; + } + + $method = $order->getPayment()->getMethod(); + $supportedMethods = [ApplePay::CODE, Creditcard::CODE]; + if (!in_array($method, $supportedMethods)) { + return false; + } + + return true; + } +} diff --git a/Service/Mollie/Order/CreateInvoiceOnShipment.php b/Service/Mollie/Order/CreateInvoiceOnShipment.php index 3c642f973b6..ac7a3ff2019 100644 --- a/Service/Mollie/Order/CreateInvoiceOnShipment.php +++ b/Service/Mollie/Order/CreateInvoiceOnShipment.php @@ -3,19 +3,24 @@ namespace Mollie\Payment\Service\Mollie\Order; use Magento\Sales\Api\Data\OrderInterface; -use Mollie\Payment\Config; class CreateInvoiceOnShipment { /** - * @var Config + * @var CanUseManualCapture */ - private $config; + private $canUseManualCapture; + /** + * @var UsedMollieApi + */ + private $usedMollieApi; public function __construct( - Config $config + CanUseManualCapture $canUseManualCapture, + UsedMollieApi $usedMollieApi ) { - $this->config = $config; + $this->canUseManualCapture = $canUseManualCapture; + $this->usedMollieApi = $usedMollieApi; } public function execute(OrderInterface $order): bool @@ -32,13 +37,10 @@ public function execute(OrderInterface $order): bool return true; } - $transactionId = $order->getMollieTransactionId() ?? ''; - $api = substr($transactionId, 0, 4) == 'ord_' ? 'orders' : 'payments'; - if ($methodCode == 'mollie_methods_creditcard' && - $this->config->useManualCapture($order->getStoreId()) && - $api == 'payments' + if ($this->usedMollieApi->execute($order) == UsedMollieApi::TYPE_PAYMENTS && + $this->canUseManualCapture->execute($order) ) { - return false; + return true; } return false; diff --git a/Service/Mollie/Order/UsedMollieApi.php b/Service/Mollie/Order/UsedMollieApi.php new file mode 100644 index 00000000000..280ee51bec2 --- /dev/null +++ b/Service/Mollie/Order/UsedMollieApi.php @@ -0,0 +1,23 @@ +getMollieTransactionId() ?? ''; + return substr($transactionId, 0, 4) == 'ord_' ? self::TYPE_ORDERS : self::TYPE_PAYMENTS; + } +} diff --git a/Service/Order/TransactionPart/CaptureMode.php b/Service/Order/TransactionPart/CaptureMode.php index e66c1ce7c0b..fedf7e56cd5 100644 --- a/Service/Order/TransactionPart/CaptureMode.php +++ b/Service/Order/TransactionPart/CaptureMode.php @@ -3,22 +3,21 @@ namespace Mollie\Payment\Service\Order\TransactionPart; use Magento\Sales\Api\Data\OrderInterface; -use Mollie\Payment\Config; use Mollie\Payment\Model\Client\Orders; -use Mollie\Payment\Model\Client\Payments; +use Mollie\Payment\Service\Mollie\Order\CanUseManualCapture; use Mollie\Payment\Service\Order\TransactionPartInterface; class CaptureMode implements TransactionPartInterface { /** - * @var Config + * @var CanUseManualCapture */ - private $config; + private $canUseManualCapture; public function __construct( - Config $config + CanUseManualCapture $canUseManualCapture ) { - $this->config = $config; + $this->canUseManualCapture = $canUseManualCapture; } public function process(OrderInterface $order, $apiMethod, array $transaction) @@ -27,9 +26,7 @@ public function process(OrderInterface $order, $apiMethod, array $transaction) return $transaction; } - if ($order->getPayment()->getMethod() != 'mollie_methods_creditcard' || - !$this->config->useManualCapture($order->getStoreId()) - ) { + if (!$this->canUseManualCapture->execute($order)) { return $transaction; } diff --git a/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php b/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php index cf59c99afce..075d8504ee2 100644 --- a/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php +++ b/Test/Integration/Service/Mollie/Order/CanRegisterCaptureNotificationTest.php @@ -64,7 +64,8 @@ public function testCanCaptureWhenEnabledButNotCreditcard(): void public function testCannotCaptureWhenEnabledAndCreditcard(): void { $order = $this->loadOrderById('100000001'); - $order->getPayment()->setMethod('mollie_methods_creditcard'); + $methods = ['mollie_methods_applepay', 'mollie_methods_creditcard']; + $order->getPayment()->setMethod($methods[array_rand($methods)]); /** @var CanRegisterCaptureNotification $instance */ $instance = $this->objectManager->create(CanRegisterCaptureNotification::class); diff --git a/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php b/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php index 3c4552f5bc2..fa68bb60df7 100644 --- a/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php +++ b/Test/Integration/Service/Mollie/Order/CreateInvoiceOnShipmentTest.php @@ -44,7 +44,8 @@ public function testIsEnabledByMethod(string $method): void public function testIsDisabledWhenCreatePaymentAuthorizationIsEnabledAndApiIsPayments(): void { $order = $this->loadOrderById('100000001'); - $order->getPayment()->setMethod('mollie_methods_creditcard'); + $methods = ['mollie_methods_applepay', 'mollie_methods_creditcard']; + $order->getPayment()->setMethod($methods[array_rand($methods)]); $order->setMollieTransactionId('tr_1234567890'); /** @var CreateInvoiceOnShipment $instance */ @@ -62,7 +63,8 @@ public function testIsDisabledWhenCreatePaymentAuthorizationIsEnabledAndApiIsPay public function testIsDisabledWhenCreatePaymentAuthorizationIsEnabledAndApiIsOrders(): void { $order = $this->loadOrderById('100000001'); - $order->getPayment()->setMethod('mollie_methods_creditcard'); + $methods = ['mollie_methods_applepay', 'mollie_methods_creditcard']; + $order->getPayment()->setMethod($methods[array_rand($methods)]); $order->setMollieTransactionId('ord_1234567890'); /** @var CreateInvoiceOnShipment $instance */ diff --git a/Test/Integration/Service/Order/TransactionPart/CaptureModeTest.php b/Test/Integration/Service/Order/TransactionPart/CaptureModeTest.php index b6e18f99447..0f7634a7109 100644 --- a/Test/Integration/Service/Order/TransactionPart/CaptureModeTest.php +++ b/Test/Integration/Service/Order/TransactionPart/CaptureModeTest.php @@ -4,6 +4,7 @@ use Mollie\Payment\Model\Client\Orders; use Mollie\Payment\Model\Client\Payments; +use Mollie\Payment\Model\Methods\ApplePay; use Mollie\Payment\Model\Methods\Creditcard; use Mollie\Payment\Model\Methods\Ideal; use Mollie\Payment\Service\Order\TransactionPart\CaptureMode; @@ -32,7 +33,7 @@ public function testDoesNothingWhenTheApiMethodIsOrders(): void * @magentoDataFixture Magento/Sales/_files/order.php * @return void */ - public function testDoesNothingWhenThePaymentMethodIsNotCreditcard(): void + public function testDoesNothingWhenThePaymentMethodIsNotCreditCardOrApplePay(): void { $order = $this->loadOrderById('100000001'); $order->getPayment()->setMethod(Ideal::CODE); @@ -72,7 +73,8 @@ public function testDoesNothingWhenNotEnabled(): void public function testSetsTheModeWhenApplicable(): void { $order = $this->loadOrderById('100000001'); - $order->getPayment()->setMethod(Creditcard::CODE); + $methods = [ApplePay::CODE, Creditcard::CODE]; + $order->getPayment()->setMethod($methods[array_rand($methods)]); /** @var CaptureMode $instance */ $instance = $this->objectManager->create(CaptureMode::class); From 4200885d4c0ebac7d31d4e3f4a193d04588b0330 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 18 Sep 2023 16:11:34 +0200 Subject: [PATCH 09/14] Test fix --- Service/Mollie/Order/CreateInvoiceOnShipment.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Service/Mollie/Order/CreateInvoiceOnShipment.php b/Service/Mollie/Order/CreateInvoiceOnShipment.php index ac7a3ff2019..ab83e7f9c8a 100644 --- a/Service/Mollie/Order/CreateInvoiceOnShipment.php +++ b/Service/Mollie/Order/CreateInvoiceOnShipment.php @@ -40,9 +40,9 @@ public function execute(OrderInterface $order): bool if ($this->usedMollieApi->execute($order) == UsedMollieApi::TYPE_PAYMENTS && $this->canUseManualCapture->execute($order) ) { - return true; + return false; } - return false; + return true; } } From 7976b5a2a2aa8bf50873d6c72a5fe14fab59a574 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Mon, 18 Sep 2023 16:30:47 +0200 Subject: [PATCH 10/14] Test fix --- .../Mollie/Order/CreateInvoiceOnShipment.php | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/Service/Mollie/Order/CreateInvoiceOnShipment.php b/Service/Mollie/Order/CreateInvoiceOnShipment.php index ab83e7f9c8a..0affa6b5c6a 100644 --- a/Service/Mollie/Order/CreateInvoiceOnShipment.php +++ b/Service/Mollie/Order/CreateInvoiceOnShipment.php @@ -6,23 +6,6 @@ class CreateInvoiceOnShipment { - /** - * @var CanUseManualCapture - */ - private $canUseManualCapture; - /** - * @var UsedMollieApi - */ - private $usedMollieApi; - - public function __construct( - CanUseManualCapture $canUseManualCapture, - UsedMollieApi $usedMollieApi - ) { - $this->canUseManualCapture = $canUseManualCapture; - $this->usedMollieApi = $usedMollieApi; - } - public function execute(OrderInterface $order): bool { $methodCode = $order->getPayment()->getMethod(); @@ -37,12 +20,6 @@ public function execute(OrderInterface $order): bool return true; } - if ($this->usedMollieApi->execute($order) == UsedMollieApi::TYPE_PAYMENTS && - $this->canUseManualCapture->execute($order) - ) { - return false; - } - - return true; + return false; } } From f4302fa42227c11e35ec29ba94d78e3b1a66dfe3 Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 21 Sep 2023 10:32:22 +0200 Subject: [PATCH 11/14] Disable Maestro and vPay networks when manual capture is enabled --- Block/Applepay/Shortcut/Button.php | 13 ++++- Block/Product/View/ApplePay.php | 13 ++++- Model/MollieConfigProvider.php | 30 +++--------- Service/Mollie/ApplePay/SupportedNetworks.php | 36 ++++++++++++++ .../Mollie/ApplePay/SupportedNetworksTest.php | 49 +++++++++++++++++++ .../applepay/minicart/applepay-button.phtml | 3 +- .../templates/product/view/applepay.phtml | 3 +- .../frontend/web/js/view/applepay/minicart.js | 2 +- .../method-renderer/applepay-direct.js | 2 +- .../web/js/view/product/apple-pay-button.js | 3 +- 10 files changed, 125 insertions(+), 29 deletions(-) create mode 100644 Service/Mollie/ApplePay/SupportedNetworks.php create mode 100644 Test/Integration/Service/Mollie/ApplePay/SupportedNetworksTest.php diff --git a/Block/Applepay/Shortcut/Button.php b/Block/Applepay/Shortcut/Button.php index ce51a66ab28..4fdeb067e23 100644 --- a/Block/Applepay/Shortcut/Button.php +++ b/Block/Applepay/Shortcut/Button.php @@ -7,6 +7,7 @@ use Magento\Framework\View\Element\Template; use Magento\Store\Model\ScopeInterface; use Mollie\Payment\Config; +use Mollie\Payment\Service\Mollie\ApplePay\SupportedNetworks; class Button extends Template implements ShortcutInterface { @@ -16,21 +17,26 @@ class Button extends Template implements ShortcutInterface * @var Session */ private $checkoutSession; - /** * @var Config */ private $config; + /** + * @var SupportedNetworks + */ + private $supportedNetworks; public function __construct( Template\Context $context, Session $checkoutSession, Config $config, + SupportedNetworks $supportedNetworks, array $data = [] ) { parent::__construct($context, $data); $this->checkoutSession = $checkoutSession; $this->config = $config; + $this->supportedNetworks = $supportedNetworks; } /** @@ -87,4 +93,9 @@ public function getButtonClasses(): string return implode(' ', $classes); } + + public function getSupportedNetworks(): array + { + return $this->supportedNetworks->execute((int)$this->_storeManager->getStore()->getId()); + } } diff --git a/Block/Product/View/ApplePay.php b/Block/Product/View/ApplePay.php index 7f81e4f0008..af1ed9072de 100644 --- a/Block/Product/View/ApplePay.php +++ b/Block/Product/View/ApplePay.php @@ -11,6 +11,7 @@ use Magento\Framework\Registry; use Magento\Framework\View\Element\Template; use Mollie\Payment\Config; +use Mollie\Payment\Service\Mollie\ApplePay\SupportedNetworks; class ApplePay extends Template { @@ -18,21 +19,26 @@ class ApplePay extends Template * @var Registry */ private $registry; - /** * @var Config */ private $config; + /** + * @var SupportedNetworks + */ + private $supportedNetworks; public function __construct( Template\Context $context, Registry $registry, Config $config, + SupportedNetworks $supportedNetworks, array $data = [] ) { parent::__construct($context, $data); $this->registry = $registry; $this->config = $config; + $this->supportedNetworks = $supportedNetworks; } public function getProductName(): string @@ -81,4 +87,9 @@ public function getButtonClasses() return implode(' ', $classes); } + + public function getSupportedNetworks(): array + { + return $this->supportedNetworks->execute((int)$this->_storeManager->getStore()->getId()); + } } diff --git a/Model/MollieConfigProvider.php b/Model/MollieConfigProvider.php index 6734f16d11e..8a3b3bee048 100644 --- a/Model/MollieConfigProvider.php +++ b/Model/MollieConfigProvider.php @@ -8,7 +8,6 @@ use Magento\Checkout\Model\ConfigProviderInterface; use Magento\Checkout\Model\Session as CheckoutSession; -use Magento\Framework\Escaper; use Magento\Framework\Locale\Resolver; use Magento\Framework\View\Asset\Repository as AssetRepository; use Magento\Payment\Helper\Data as PaymentHelper; @@ -19,6 +18,7 @@ use Mollie\Payment\Config; use Mollie\Payment\Helper\General as MollieHelper; use Mollie\Payment\Model\Mollie as MollieModel; +use Mollie\Payment\Service\Mollie\ApplePay\SupportedNetworks; use Mollie\Payment\Service\Mollie\GetIssuers; use Mollie\Payment\Service\Mollie\MethodParameters; @@ -62,10 +62,6 @@ class MollieConfigProvider implements ConfigProviderInterface * @var array */ private $methods = []; - /** - * @var Escaper - */ - private $escaper; /** * @var AssetRepository */ @@ -111,46 +107,35 @@ class MollieConfigProvider implements ConfigProviderInterface * @var MethodParameters */ private $methodParameters; - /** - * MollieConfigProvider constructor. - * - * @param Mollie $mollieModel - * @param MollieHelper $mollieHelper - * @param PaymentHelper $paymentHelper - * @param CheckoutSession $checkoutSession - * @param AssetRepository $assetRepository - * @param Escaper $escaper - * @param Resolver $localeResolver - * @param Config $config - * @param GetIssuers $getIssuers - * @param StoreManagerInterface $storeManager - * @param MethodParameters $methodParameters + * @var SupportedNetworks */ + private $supportedNetworks; + public function __construct( MollieModel $mollieModel, MollieHelper $mollieHelper, PaymentHelper $paymentHelper, CheckoutSession $checkoutSession, AssetRepository $assetRepository, - Escaper $escaper, Resolver $localeResolver, Config $config, GetIssuers $getIssuers, StoreManagerInterface $storeManager, - MethodParameters $methodParameters + MethodParameters $methodParameters, + SupportedNetworks $supportedNetworks ) { $this->mollieModel = $mollieModel; $this->mollieHelper = $mollieHelper; $this->paymentHelper = $paymentHelper; $this->checkoutSession = $checkoutSession; - $this->escaper = $escaper; $this->assetRepository = $assetRepository; $this->config = $config; $this->localeResolver = $localeResolver; $this->getIssuers = $getIssuers; $this->storeManager = $storeManager; $this->methodParameters = $methodParameters; + $this->supportedNetworks = $supportedNetworks; foreach ($this->methodCodes as $code) { $this->methods[$code] = $this->getMethodInstance($code); @@ -188,6 +173,7 @@ public function getConfig(): array $config['payment']['mollie']['locale'] = $this->getLocale($storeId); $config['payment']['mollie']['creditcard']['use_components'] = $this->config->creditcardUseComponents($storeId); $config['payment']['mollie']['applepay']['integration_type'] = $this->config->applePayIntegrationType($storeId); + $config['payment']['mollie']['applepay']['supported_networks'] = $this->supportedNetworks->execute((int)$storeId); $config['payment']['mollie']['store']['name'] = $storeName; $config['payment']['mollie']['store']['currency'] = $this->config->getStoreCurrency($storeId); $config['payment']['mollie']['vault']['enabled'] = $this->config->isMagentoVaultEnabled($storeId); diff --git a/Service/Mollie/ApplePay/SupportedNetworks.php b/Service/Mollie/ApplePay/SupportedNetworks.php new file mode 100644 index 00000000000..d1c700b1ef6 --- /dev/null +++ b/Service/Mollie/ApplePay/SupportedNetworks.php @@ -0,0 +1,36 @@ +config = $config; + } + + public function execute(int $storeId = null): array + { + $output = ['amex', 'masterCard', 'visa']; + if (!$this->config->useManualCapture($storeId)) { + $output[] = 'maestro'; + $output[] = 'vPay'; + } + + return $output; + } +} diff --git a/Test/Integration/Service/Mollie/ApplePay/SupportedNetworksTest.php b/Test/Integration/Service/Mollie/ApplePay/SupportedNetworksTest.php new file mode 100644 index 00000000000..e63ee4dafad --- /dev/null +++ b/Test/Integration/Service/Mollie/ApplePay/SupportedNetworksTest.php @@ -0,0 +1,49 @@ +objectManager->create(SupportedNetworks::class); + + $result = $instance->execute(); + foreach (['amex', 'masterCard', 'visa', 'maestro', 'vPay'] as $network) { + $this->assertContains($network, $result); + } + } + + /** + * @magentoConfigFixture default_store payment/mollie_general/enable_manual_capture 1 + * @return void + */ + public function testDoesNotIncludeMaestroAndVpayWhenManualCaptureIsEnabled(): void + { + /** @var SupportedNetworks $instance */ + $instance = $this->objectManager->create(SupportedNetworks::class); + + $result = $instance->execute(); + foreach (['amex', 'masterCard', 'visa'] as $network) { + $this->assertContains($network, $result); + } + + foreach (['maestro', 'vPay'] as $network) { + $this->assertNotContains($network, $result); + } + } +} diff --git a/view/frontend/templates/applepay/minicart/applepay-button.phtml b/view/frontend/templates/applepay/minicart/applepay-button.phtml index cb35241d1fd..562c512a1f9 100644 --- a/view/frontend/templates/applepay/minicart/applepay-button.phtml +++ b/view/frontend/templates/applepay/minicart/applepay-button.phtml @@ -14,7 +14,8 @@ use Magento\Framework\Escaper; "storeName": "escapeJs($block->getStoreName()); ?>", "grandTotalAmount": "escapeJs($block->getBaseGrandTotal()); ?>", "storeCountry": "escapeJs($block->getStoreCountry()); ?>", - "storeCurrency": "escapeJs($block->getStoreCurrency()); ?>" + "storeCurrency": "escapeJs($block->getStoreCurrency()); ?>", + "supportedNetworks": getSupportedNetworks()); ?> } }' class="mollie-applepay-minicart mollie-applepay-button-hidden"> diff --git a/view/frontend/templates/product/view/applepay.phtml b/view/frontend/templates/product/view/applepay.phtml index 02274c590eb..26167ceb874 100644 --- a/view/frontend/templates/product/view/applepay.phtml +++ b/view/frontend/templates/product/view/applepay.phtml @@ -25,7 +25,8 @@ if (!$block->isEnabled()) { "currencyCode": "escapeJs($block->getCurrencyCode()); ?>", "countryCode": "escapeJs($block->getCountryCode()); ?>", "productName": "escapeJs($block->getProductName()); ?>", - "storeName": "escapeJs($block->getStoreName()); ?>" + "storeName": "escapeJs($block->getStoreName()); ?>", + "supportedNetworks": getSupportedNetworks()); ?> } } } diff --git a/view/frontend/web/js/view/applepay/minicart.js b/view/frontend/web/js/view/applepay/minicart.js index 5b7204664fd..a42d16c211d 100644 --- a/view/frontend/web/js/view/applepay/minicart.js +++ b/view/frontend/web/js/view/applepay/minicart.js @@ -42,7 +42,7 @@ define([ var request = { countryCode: this.storeCountry, currencyCode: this.storeCurrency, - supportedNetworks: ['amex', 'maestro', 'masterCard', 'visa', 'vPay'], + supportedNetworks: this.supportedNetworks, merchantCapabilities: ['supports3DS'], total: { label: this.storeName, diff --git a/view/frontend/web/js/view/payment/method-renderer/applepay-direct.js b/view/frontend/web/js/view/payment/method-renderer/applepay-direct.js index 1cb3e4926d7..eb920e76e24 100644 --- a/view/frontend/web/js/view/payment/method-renderer/applepay-direct.js +++ b/view/frontend/web/js/view/payment/method-renderer/applepay-direct.js @@ -65,7 +65,7 @@ define( var request = { countryCode: window.checkoutConfig.defaultCountryId, currencyCode: window.checkoutConfig.payment.mollie.store.currency, - supportedNetworks: ['amex', 'maestro', 'masterCard', 'visa', 'vPay'], + supportedNetworks: window.checkoutConfig.payment.mollie.applepay.supported_networks, merchantCapabilities: ['supports3DS'], total: { label: checkoutConfig.mollie.store.name, diff --git a/view/frontend/web/js/view/product/apple-pay-button.js b/view/frontend/web/js/view/product/apple-pay-button.js index a4e8aee38b5..8381a23006e 100644 --- a/view/frontend/web/js/view/product/apple-pay-button.js +++ b/view/frontend/web/js/view/product/apple-pay-button.js @@ -29,6 +29,7 @@ define([ countryCode: null, productName: null, storeName: null, + supportedNetworks: [], initObservable: function () { this._super().observe([ @@ -57,7 +58,7 @@ define([ var request = { countryCode: this.countryCode, currencyCode: this.currencyCode, - supportedNetworks: ['amex', 'maestro', 'masterCard', 'visa', 'vPay'], + supportedNetworks: this.supportedNetworks, merchantCapabilities: ['supports3DS'], total: { type: 'final', From e91a6d7e171ac865a57dd281313c2b78ebbfc0ee Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 21 Sep 2023 11:12:28 +0200 Subject: [PATCH 12/14] Bugfix: Make Billie error message translatable --- view/frontend/web/js/view/payment/method-renderer/billie.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/billie.js b/view/frontend/web/js/view/payment/method-renderer/billie.js index 34209891959..3ee00a66244 100644 --- a/view/frontend/web/js/view/payment/method-renderer/billie.js +++ b/view/frontend/web/js/view/payment/method-renderer/billie.js @@ -17,7 +17,7 @@ define( function ( $, _, - __, + $t, ko, url, storage, @@ -39,7 +39,7 @@ define( if (!billingAddress || !billingAddress.company) { this.messageContainer.addErrorMessage({ - message: __('Please enter a company name.') + message: $t('Please enter a company name.') }); return false; From 0763b9a1d55b9a930779fbebda7498815bb7f30d Mon Sep 17 00:00:00 2001 From: Michiel Gerritsen Date: Thu, 21 Sep 2023 11:13:27 +0200 Subject: [PATCH 13/14] Remove method --- .../js/view/payment/method-renderer/billie.js | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/view/frontend/web/js/view/payment/method-renderer/billie.js b/view/frontend/web/js/view/payment/method-renderer/billie.js index 3ee00a66244..69b346c9956 100644 --- a/view/frontend/web/js/view/payment/method-renderer/billie.js +++ b/view/frontend/web/js/view/payment/method-renderer/billie.js @@ -46,29 +46,6 @@ define( } return true; - }, - - bbbbbeforePlaceOrder: function () { - var serviceUrl; - - /** - * We retrieve a payment token. This is used to start the transaction once the order is placed. - */ - if (customer.isLoggedIn()) { - serviceUrl = urlBuilder.createUrl('/carts/mine/mollie/payment-token', {}); - } else { - serviceUrl = urlBuilder.createUrl('/guest-carts/:quoteId/mollie/payment-token', { - quoteId: quote.getQuoteId() - }); - } - - var promise = storage.get(serviceUrl); - - promise.done( function (result) { - this.paymentToken(result); - }.bind(this)); - - return promise; } } ); From 23c91dd7f1dc8b4d4e5a3913740a3e293e5a7dab Mon Sep 17 00:00:00 2001 From: Marvin Besselsen Date: Thu, 21 Sep 2023 15:37:53 +0200 Subject: [PATCH 14/14] Version bump --- composer.json | 2 +- etc/config.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index c905cafbe81..1c05c08eee0 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "mollie/magento2", "description": "Mollie Payment Module for Magento 2", - "version": "2.30.1", + "version": "2.31.0", "keywords": [ "mollie", "payment", diff --git a/etc/config.xml b/etc/config.xml index 6176ed6d23e..016e8de82d9 100644 --- a/etc/config.xml +++ b/etc/config.xml @@ -3,7 +3,7 @@ - v2.30.1 + v2.31.0 0 0 test