diff --git a/Controller/Checkout/Redirect.php b/Controller/Checkout/Redirect.php
index 5a075198217..6731a4d7f4d 100644
--- a/Controller/Checkout/Redirect.php
+++ b/Controller/Checkout/Redirect.php
@@ -6,7 +6,12 @@
namespace Mollie\Payment\Controller\Checkout;
+use Exception;
+use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Payment\Helper\Data as PaymentHelper;
+use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Api\OrderManagementInterface;
+use Magento\Store\Model\ScopeInterface;
use Mollie\Payment\Helper\General as MollieHelper;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
@@ -37,27 +42,41 @@ class Redirect extends Action
* @var MollieHelper
*/
protected $mollieHelper;
+ /**
+ * @var OrderManagementInterface
+ */
+ private $orderManagement;
+ /**
+ * @var ScopeConfigInterface
+ */
+ private $scopeConfig;
/**
* Redirect constructor.
*
- * @param Context $context
- * @param Session $checkoutSession
- * @param PageFactory $resultPageFactory
- * @param PaymentHelper $paymentHelper
- * @param MollieHelper $mollieHelper
+ * @param Context $context
+ * @param Session $checkoutSession
+ * @param PageFactory $resultPageFactory
+ * @param PaymentHelper $paymentHelper
+ * @param MollieHelper $mollieHelper
+ * @param OrderManagementInterface $orderManagement
+ * @param ScopeConfigInterface $scopeConfig
*/
public function __construct(
Context $context,
Session $checkoutSession,
PageFactory $resultPageFactory,
PaymentHelper $paymentHelper,
- MollieHelper $mollieHelper
+ MollieHelper $mollieHelper,
+ OrderManagementInterface $orderManagement,
+ ScopeConfigInterface $scopeConfig
) {
$this->checkoutSession = $checkoutSession;
$this->resultPageFactory = $resultPageFactory;
$this->paymentHelper = $paymentHelper;
$this->mollieHelper = $mollieHelper;
+ $this->orderManagement = $orderManagement;
+ $this->scopeConfig = $scopeConfig;
parent::__construct($context);
}
@@ -102,11 +121,41 @@ public function execute()
$this->checkoutSession->restoreQuote();
$this->_redirect('checkout/cart');
}
- } catch (\Exception $e) {
+ } catch (Exception $e) {
$this->messageManager->addExceptionMessage($e, __($e->getMessage()));
$this->mollieHelper->addTolog('error', $e->getMessage());
$this->checkoutSession->restoreQuote();
+ $this->cancelUnprocessedOrder($order, $e->getMessage());
$this->_redirect('checkout/cart');
}
}
+
+ private function cancelUnprocessedOrder(OrderInterface $order, $message)
+ {
+ if (!empty($order->getMollieTransactionId())) {
+ return;
+ }
+
+ if (!$this->scopeConfig->isSetFlag(
+ 'payment/mollie_general/cancel_failed_orders',
+ ScopeInterface::SCOPE_STORE
+ )) {
+ return;
+ }
+
+ try {
+ $historyMessage = __('Canceled because an error occurred while redirecting the customer to Mollie');
+ if ($message) {
+ $historyMessage .= ': ' . PHP_EOL . $message;
+ }
+
+ $this->orderManagement->cancel($order->getEntityId());
+ $order->addCommentToStatusHistory($order->getEntityId(), $historyMessage);
+
+ $this->mollieHelper->addToLog('info', sprintf('Canceled order %s', $order->getIncrementId()));
+ } catch (Exception $e) {
+ $message = sprintf('Cannot cancel order %s: %s', $order->getIncrementId(), $e->getMessage());
+ $this->mollieHelper->addToLog('error', $message);
+ }
+ }
}
diff --git a/Controller/Checkout/Webhook.php b/Controller/Checkout/Webhook.php
index 8f910916a94..581cdf71f3b 100644
--- a/Controller/Checkout/Webhook.php
+++ b/Controller/Checkout/Webhook.php
@@ -78,6 +78,11 @@ public function execute()
}
} catch (\Exception $e) {
$this->mollieHelper->addTolog('error', $e->getMessage());
+
+ $result = $this->resultFactory->create(ResultFactory::TYPE_JSON);
+ $result->setHttpResponseCode(503);
+
+ return $result;
}
}
}
diff --git a/Helper/General.php b/Helper/General.php
index d615f9ade28..0950949a859 100755
--- a/Helper/General.php
+++ b/Helper/General.php
@@ -600,7 +600,6 @@ public function getAllActiveMethods($storeId)
'mollie_methods_bancontact',
'mollie_methods_banktransfer',
'mollie_methods_belfius',
- 'mollie_methods_bitcoin',
'mollie_methods_creditcard',
'mollie_methods_ideal',
'mollie_methods_kbc',
@@ -614,6 +613,7 @@ public function getAllActiveMethods($storeId)
'mollie_methods_klarnasliceit',
'mollie_methods_giftcard',
'mollie_methods_przelewy24',
+ 'mollie_methods_applepay',
];
foreach ($methodCodes as $methodCode) {
diff --git a/Helper/Tests.php b/Helper/Tests.php
index c8533126908..4e9747ef779 100644
--- a/Helper/Tests.php
+++ b/Helper/Tests.php
@@ -66,7 +66,10 @@ public function getMethods($testKey = null, $liveKey = null)
try {
$availableMethods = [];
$mollieApi = $this->mollieModel->loadMollieApi($testKey);
- $methods = $mollieApi->methods->all(["resource" => "orders"]);
+ $methods = $mollieApi->methods->all([
+ 'resource' => 'orders',
+ 'includeWallets' => 'applepay',
+ ]);
foreach ($methods as $apiMethod) {
$availableMethods[] = ucfirst($apiMethod->id);
@@ -96,7 +99,11 @@ public function getMethods($testKey = null, $liveKey = null)
try {
$availableMethods = [];
$mollieApi = $this->mollieModel->loadMollieApi($liveKey);
- $methods = $mollieApi->methods->all(["resource" => "orders"]);
+ $methods = $mollieApi->methods->all([
+ 'resource' => 'orders',
+ 'includeWallets' => 'applepay',
+ ]);
+
foreach ($methods as $apiMethod) {
$availableMethods[] = ucfirst($apiMethod->id);
}
diff --git a/Model/Client/Orders.php b/Model/Client/Orders.php
index 0b3932db2a2..4d10d634e28 100644
--- a/Model/Client/Orders.php
+++ b/Model/Client/Orders.php
@@ -19,9 +19,9 @@
use Magento\Sales\Model\Order\InvoiceRepository;
use Magento\Sales\Model\Service\InvoiceService;
use Magento\Checkout\Model\Session\Proxy as CheckoutSession;
-use Mollie\Api\Resources\PaymentFactory;
use Mollie\Payment\Helper\General as MollieHelper;
use Mollie\Payment\Model\OrderLines;
+use Mollie\Payment\Service\Order\ProcessAdjustmentFee;
/**
* Class Orders
@@ -74,24 +74,24 @@ class Orders extends AbstractModel
*/
private $registry;
/**
- * @var PaymentFactory
+ * @var ProcessAdjustmentFee
*/
- private $paymentFactory;
+ private $adjustmentFee;
/**
* Orders constructor.
*
- * @param OrderLines $orderLines
- * @param OrderSender $orderSender
- * @param InvoiceSender $invoiceSender
- * @param InvoiceService $invoiceService
- * @param OrderRepository $orderRepository
- * @param InvoiceRepository $invoiceRepository
- * @param CheckoutSession $checkoutSession
- * @param ManagerInterface $messageManager
- * @param Registry $registry
- * @param MollieHelper $mollieHelper
- * @param PaymentFactory $paymentFactory
+ * @param OrderLines $orderLines
+ * @param OrderSender $orderSender
+ * @param InvoiceSender $invoiceSender
+ * @param InvoiceService $invoiceService
+ * @param OrderRepository $orderRepository
+ * @param InvoiceRepository $invoiceRepository
+ * @param CheckoutSession $checkoutSession
+ * @param ManagerInterface $messageManager
+ * @param Registry $registry
+ * @param MollieHelper $mollieHelper
+ * @param ProcessAdjustmentFee $adjustmentFee
*/
public function __construct(
OrderLines $orderLines,
@@ -104,7 +104,7 @@ public function __construct(
ManagerInterface $messageManager,
Registry $registry,
MollieHelper $mollieHelper,
- PaymentFactory $paymentFactory
+ ProcessAdjustmentFee $adjustmentFee
) {
$this->orderLines = $orderLines;
$this->orderSender = $orderSender;
@@ -116,7 +116,7 @@ public function __construct(
$this->messageManager = $messageManager;
$this->registry = $registry;
$this->mollieHelper = $mollieHelper;
- $this->paymentFactory = $paymentFactory;
+ $this->adjustmentFee = $adjustmentFee;
}
/**
@@ -692,30 +692,7 @@ public function createOrderRefund(Order\Creditmemo $creditmemo, Order $order)
/**
* Check for creditmemo adjustment fee's, positive and negative.
*/
- if ($creditmemo->getAdjustment() !== 0.0) {
- $mollieOrder = $mollieApi->orders->get($order->getMollieTransactionId(), ['embed' => 'payments']);
- $payments = $mollieOrder->_embedded->payments;
-
- try {
- $payment = $this->paymentFactory->create([$mollieApi]);
- $payment->id = current($payments)->id;
-
- $mollieApi->payments->refund($payment, [
- 'amount' => [
- 'currency' => $order->getOrderCurrencyCode(),
- 'value' => $this->mollieHelper->formatCurrencyValue(
- $creditmemo->getAdjustment(),
- $order->getOrderCurrencyCode()
- ),
- ]
- ]);
- } catch (\Exception $exception) {
- $this->mollieHelper->addTolog('error', $exception->getMessage());
- throw new LocalizedException(
- __('Mollie API: %1', $exception->getMessage())
- );
- }
- }
+ $this->adjustmentFee->handle($mollieApi, $order, $creditmemo);
/**
* Check if Shipping Fee needs to be refunded.
@@ -734,7 +711,7 @@ public function createOrderRefund(Order\Creditmemo $creditmemo, Order $order)
}
}
- if (!$creditmemo->getAllItems()) {
+ if (!$creditmemo->getAllItems() || $this->adjustmentFee->doNotRefundInMollie()) {
return $this;
}
diff --git a/Model/Methods/Bitcoin.php b/Model/Methods/ApplePay.php
similarity index 67%
rename from Model/Methods/Bitcoin.php
rename to Model/Methods/ApplePay.php
index c052c096218..de8406565ed 100644
--- a/Model/Methods/Bitcoin.php
+++ b/Model/Methods/ApplePay.php
@@ -6,21 +6,15 @@
namespace Mollie\Payment\Model\Methods;
-use Mollie\Payment\Model\Mollie;
-
-/**
- * Class Bitcoin
- *
- * @package Mollie\Payment\Model\Methods
- */
-class Bitcoin extends Mollie
+class ApplePay extends \Mollie\Payment\Model\Mollie
{
+
/**
* Payment method code
*
* @var string
*/
- protected $_code = 'mollie_methods_bitcoin';
+ protected $_code = 'mollie_methods_applepay';
/**
* Info instructions block path
@@ -28,4 +22,4 @@ class Bitcoin extends Mollie
* @var string
*/
protected $_infoBlockType = 'Mollie\Payment\Block\Info\Base';
-}
+}
\ No newline at end of file
diff --git a/Model/Mollie.php b/Model/Mollie.php
index 38b1a6b3205..e3762cb750f 100644
--- a/Model/Mollie.php
+++ b/Model/Mollie.php
@@ -530,6 +530,9 @@ public function getPaymentMethods($storeId)
$mollieApi = $this->loadMollieApi($apiKey);
- return $mollieApi->methods->all(["resource" => "orders"]);
+ return $mollieApi->methods->all([
+ 'resource' => 'orders',
+ 'includeWallets' => 'applepay',
+ ]);
}
}
diff --git a/Model/MollieConfigProvider.php b/Model/MollieConfigProvider.php
index 26b26654379..d8b88e30347 100644
--- a/Model/MollieConfigProvider.php
+++ b/Model/MollieConfigProvider.php
@@ -30,7 +30,6 @@ class MollieConfigProvider implements ConfigProviderInterface
'mollie_methods_bancontact',
'mollie_methods_banktransfer',
'mollie_methods_belfius',
- 'mollie_methods_bitcoin',
'mollie_methods_creditcard',
'mollie_methods_ideal',
'mollie_methods_kbc',
@@ -44,6 +43,7 @@ class MollieConfigProvider implements ConfigProviderInterface
'mollie_methods_klarnasliceit',
'mollie_methods_giftcard',
'mollie_methods_przelewy24',
+ 'mollie_methods_applepay',
];
/**
* @var array
@@ -207,9 +207,11 @@ public function getActiveMethods($mollieApi)
try {
$quote = $this->checkoutSession->getQuote();
$amount = $this->mollieHelper->getOrderAmountByQuote($quote);
- $params = ["amount[value]" => $amount['value'],
- "amount[currency]" => $amount['currency'],
- "resource" => "orders"
+ $params = [
+ 'amount[value]' => $amount['value'],
+ 'amount[currency]' => $amount['currency'],
+ 'resource' => 'orders',
+ 'includeWallets' => 'applepay',
];
$apiMethods = $mollieApi->methods->all($params);
diff --git a/README.md b/README.md
index 14c00f33234..6a8b07fdd27 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,9 @@ Mollie requires no minimum costs, no fixed contracts, no hidden costs. At Mollie
- Klarna
-- Giftcards
+- Giftcards
+
+- Apple Pay
## Configuration, FAQ and Troubleshooting ##
If you experience problems with the extension installation, setup or whenever you need more information about how to setup the Mollie Payment extension in Magento 2.x, please see our [WIKI Page](https://github.com/mollie/magento2/wiki) or send an e-mail to [info@mollie.com](mailto:info@mollie.com) with an exact description of the problem.
diff --git a/Service/Order/ProcessAdjustmentFee.php b/Service/Order/ProcessAdjustmentFee.php
new file mode 100644
index 00000000000..f11600298e7
--- /dev/null
+++ b/Service/Order/ProcessAdjustmentFee.php
@@ -0,0 +1,94 @@
+mollieHelper = $mollieHelper;
+ $this->paymentFactory = $paymentFactory;
+ }
+
+ public function handle(MollieApiClient $mollieApi, OrderInterface $order, CreditmemoInterface $creditmemo)
+ {
+ if ($creditmemo->getAdjustment() > 0) {
+ $this->positive($mollieApi, $order, $creditmemo);
+ }
+
+ if ($creditmemo->getAdjustmentNegative() < 0) {
+ $this->negative($mollieApi, $order, $creditmemo);
+ }
+ }
+
+ public function doNotRefundInMollie()
+ {
+ return $this->doNotRefundInMollie;
+ }
+
+ private function positive(MollieApiClient $mollieApi, OrderInterface $order, CreditmemoInterface $creditmemo)
+ {
+ $this->doNotRefundInMollie = false;
+
+ $this->paymentRefund($mollieApi, $order, $creditmemo->getAdjustment());
+ }
+
+ private function negative(MollieApiClient $mollieApi, OrderInterface $order, CreditmemoInterface $creditmemo)
+ {
+ $this->doNotRefundInMollie = true;
+
+ $amountToRefund = $creditmemo->getSubtotal() - $creditmemo->getAdjustmentNegative();
+
+ $this->paymentRefund($mollieApi, $order, $amountToRefund);
+ }
+
+ private function paymentRefund(MollieApiClient $mollieApi, OrderInterface $order, $amount)
+ {
+ $mollieOrder = $mollieApi->orders->get($order->getMollieTransactionId(), ['embed' => 'payments']);
+ $payments = $mollieOrder->_embedded->payments;
+
+ try {
+ $payment = $this->paymentFactory->create([$mollieApi]);
+ $payment->id = current($payments)->id;
+
+ $mollieApi->payments->refund($payment, [
+ 'amount' => [
+ 'currency' => $order->getOrderCurrencyCode(),
+ 'value' => $this->mollieHelper->formatCurrencyValue(
+ $amount,
+ $order->getOrderCurrencyCode()
+ ),
+ ]
+ ]);
+ } catch (\Exception $exception) {
+ $this->mollieHelper->addTolog('error', $exception->getMessage());
+ throw new LocalizedException(
+ __('Mollie API: %1', $exception->getMessage())
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/Setup/UpgradeData.php b/Setup/UpgradeData.php
index bcc7c10a07a..14281669660 100644
--- a/Setup/UpgradeData.php
+++ b/Setup/UpgradeData.php
@@ -6,9 +6,12 @@
namespace Mollie\Payment\Setup;
+use Magento\Sales\Setup\SalesSetupFactory;
use Magento\Config\Model\ResourceModel\Config;
+use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\ResourceConnection;
-use Magento\Sales\Setup\SalesSetupFactory;
+use Magento\Framework\App\Config\ScopeConfigInterface;
+use Magento\Framework\App\Config\Storage\WriterInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
@@ -20,7 +23,6 @@
*/
class UpgradeData implements UpgradeDataInterface
{
-
/**
* Sales setup factory
*
@@ -38,21 +40,37 @@ class UpgradeData implements UpgradeDataInterface
*/
private $resourceConfig;
+ /**
+ * @var WriterInterface
+ */
+ private $configWriter;
+
+ /**
+ * @var StoreManagerInterface
+ */
+ private $storeManager;
+
/**
* UpgradeData constructor.
*
* @param SalesSetupFactory $salesSetupFactory
* @param ResourceConnection $resourceConnection
* @param Config $resourceConfig
+ * @param WriterInterface $configWriter
+ * @param StoreManagerInterface $storeManager
*/
public function __construct(
SalesSetupFactory $salesSetupFactory,
ResourceConnection $resourceConnection,
- Config $resourceConfig
+ Config $resourceConfig,
+ WriterInterface $configWriter,
+ StoreManagerInterface $storeManager
) {
$this->salesSetupFactory = $salesSetupFactory;
$this->resourceConnection = $resourceConnection;
$this->resourceConfig = $resourceConfig;
+ $this->configWriter = $configWriter;
+ $this->storeManager = $storeManager;
}
/**
@@ -65,11 +83,15 @@ public function upgrade(
) {
$setup->startSetup();
- if (version_compare($context->getVersion(), "1.4.0", "<")) {
+ if (version_compare($context->getVersion(), '1.4.0', '<')) {
$this->createMollieShipmentId($setup);
}
- if (version_compare($context->getVersion(), '1.5.3', '<')) {
+ if (version_compare($context->getVersion(), '1.6.0', '<')) {
+ $this->removeBitcoinConfiguration();
+ }
+
+ if (version_compare($context->getVersion(), '1.6.1', '<')) {
$this->upgradeActiveState();
}
@@ -139,4 +161,27 @@ private function setCorrectWebsiteDefault($path)
$this->resourceConfig->saveConfig($path, '0', 'default');
}
+
+ private function removeBitcoinConfiguration()
+ {
+ $paths = [
+ 'payment/mollie_methods_bitcoin/active',
+ 'payment/mollie_methods_bitcoin/title',
+ 'payment/mollie_methods_bitcoin/method',
+ 'payment/mollie_methods_bitcoin/payment_description',
+ 'payment/mollie_methods_bitcoin/allowspecific',
+ 'payment/mollie_methods_bitcoin/specificcountry',
+ 'payment/mollie_methods_bitcoin/min_order_total',
+ 'payment/mollie_methods_bitcoin/max_order_total',
+ 'payment/mollie_methods_bitcoin/sort_order',
+ ];
+
+ foreach ($this->storeManager->getStores() as $store) {
+ foreach ($paths as $path) {
+ $this->configWriter->delete($path, ScopeConfigInterface::SCOPE_TYPE_DEFAULT);
+ $this->configWriter->delete($path, 'stores', $store->getId());
+ $this->configWriter->delete($path, 'websites', $store->getId());
+ }
+ }
+ }
}
diff --git a/Tests/Fakes/Model/Methods/IdealFake.php b/Tests/Fakes/Model/Methods/IdealFake.php
new file mode 100644
index 00000000000..01f75838a22
--- /dev/null
+++ b/Tests/Fakes/Model/Methods/IdealFake.php
@@ -0,0 +1,15 @@
+_objectManager->get(Session::class)->setLastRealOrderId('100000001');
+
+ $this->_objectManager->configure([
+ 'preferences' => [
+ Ideal::class => IdealFake::class,
+ ],
+ ]);
+
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->dispatch('mollie/checkout/redirect');
+
+ $newOrder = $this->loadOrder();
+ $this->assertEquals(Order::STATE_CANCELED, $newOrder->getState());
+ }
+
+ /**
+ * @magentoConfigFixture current_store payment/mollie_general/cancel_failed_orders 0
+ * @magentoDbIsolation disabled
+ * @magentoDataFixture createOrder
+ */
+ public function testDoesNotCancelWhenDisabled()
+ {
+ $this->_objectManager->get(Session::class)->setLastRealOrderId('100000001');
+
+ $this->_objectManager->configure([
+ 'preferences' => [
+ Ideal::class => IdealFake::class,
+ ],
+ ]);
+
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->dispatch('mollie/checkout/redirect');
+
+ $newOrder = $this->loadOrder();
+ $this->assertNotEquals(Order::STATE_CANCELED, $newOrder->getState());
+ }
+
+ private function loadOrder()
+ {
+ $criteria = $this->_objectManager->create(SearchCriteriaBuilder::class)
+ ->addFilter('increment_id', '100000001', 'eq')->create();
+
+ $repository = $this->_objectManager->get(OrderRepositoryInterface::class);
+ $result = $repository->getList($criteria)->getItems();
+ return array_shift($result);
+ }
+
+ /**
+ * This is a copy of dev/tests/integration/testsuite/Magento/Sales/_files/order.php,
+ * but with the Mollie payment method.
+ *
+ * @throws \Magento\Framework\Exception\LocalizedException
+ */
+ static public function createOrder()
+ {
+ require BP . '/dev/tests/integration/testsuite/Magento/Sales/_files/default_rollback.php';
+ require BP . '/dev/tests/integration/testsuite/Magento/Catalog/_files/product_simple.php';
+ /** @var \Magento\Catalog\Model\Product $product */
+
+ $addressData = include BP . '/dev/tests/integration/testsuite/Magento/Sales/_files/address_data.php';
+
+ $objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager();
+
+ $billingAddress = $objectManager->create(OrderAddress::class, ['data' => $addressData]);
+ $billingAddress->setAddressType('billing');
+
+ $shippingAddress = clone $billingAddress;
+ $shippingAddress->setId(null)->setAddressType('shipping');
+
+ /** @var Payment $payment */
+ $payment = $objectManager->create(Payment::class);
+ $payment->setMethod('mollie_methods_ideal');
+
+ /** @var OrderItem $orderItem */
+ $orderItem = $objectManager->create(OrderItem::class);
+ $orderItem->setProductId($product->getId())
+ ->setQtyOrdered(2)
+ ->setBasePrice($product->getPrice())
+ ->setPrice($product->getPrice())
+ ->setRowTotal($product->getPrice())
+ ->setProductType('simple');
+
+ /** @var Order $order */
+ $order = $objectManager->create(Order::class);
+ $order->setIncrementId('100000001')
+ ->setState(Order::STATE_PROCESSING)
+ ->setStatus($order->getConfig()->getStateDefaultStatus(Order::STATE_PROCESSING))
+ ->setSubtotal(100)
+ ->setGrandTotal(100)
+ ->setBaseSubtotal(100)
+ ->setBaseGrandTotal(100)
+ ->setCustomerIsGuest(true)
+ ->setCustomerEmail('customer@null.com')
+ ->setBillingAddress($billingAddress)
+ ->setShippingAddress($shippingAddress)
+ ->setStoreId($objectManager->get(StoreManagerInterface::class)->getStore()->getId())
+ ->addItem($orderItem)
+ ->setPayment($payment);
+
+ /** @var OrderRepositoryInterface $orderRepository */
+ $orderRepository = $objectManager->create(OrderRepositoryInterface::class);
+ $orderRepository->save($order);
+ }
+}
diff --git a/Tests/Integration/Controller/Checkout/WebhookTest.php b/Tests/Integration/Controller/Checkout/WebhookTest.php
new file mode 100644
index 00000000000..42610b5b8b8
--- /dev/null
+++ b/Tests/Integration/Controller/Checkout/WebhookTest.php
@@ -0,0 +1,28 @@
+createMock(Mollie::class);
+ $mollieModel->method('getOrderIdByTransactionId')->willReturn(123);
+ $mollieModel->method('processTransaction')->willThrowException(new \Exception('[TEST] Something went wrong'));
+
+ $this->_objectManager->addSharedInstance($mollieModel, Mollie::class);
+
+ $this->getRequest()->setMethod(Request::METHOD_POST);
+ $this->getRequest()->setParams([
+ 'id' => 'ord_123ABC',
+ ]);
+
+ $this->dispatch('mollie/checkout/webhook');
+
+ $this->assertSame(503, $this->getResponse()->getHttpResponseCode());
+ }
+}
diff --git a/Tests/Unit/Model/Methods/AbstractMethodTest.php b/Tests/Unit/Model/Methods/AbstractMethodTest.php
new file mode 100644
index 00000000000..05473765f0c
--- /dev/null
+++ b/Tests/Unit/Model/Methods/AbstractMethodTest.php
@@ -0,0 +1,105 @@
+objectManager = new ObjectManager($this);
+ }
+
+ public function testHasAnExistingModel()
+ {
+ $this->assertTrue(class_exists($this->instance), 'We expect that the class ' . $this->instance . ' exists');
+ }
+
+ public function testHasTheCorrectCode()
+ {
+ /**
+ * The parent constructor of this class calls the ObjectManager, which isn't available in unit tests. So skip
+ * the constructor.
+ */
+ $reflection = new \ReflectionClass($this->instance);
+ $instance = $reflection->newInstanceWithoutConstructor();
+
+ $this->assertEquals('mollie_methods_' . $this->code, $instance->getCode());
+ }
+
+ public function testIsListedAsActiveMethod()
+ {
+ $scopeConfig = $this->createMock(ScopeConfigInterface::class);
+ $scopeConfig->method('getValue')->willReturn(1);
+
+ $context = $this->objectManager->getObject(Context::class, [
+ 'scopeConfig' => $scopeConfig,
+ ]);
+
+ /** @var MollieHelper $helper */
+ $helper = $this->objectManager->getObject(MollieHelper::class, [
+ 'context' => $context,
+ ]);
+
+ $methods = $helper->getAllActiveMethods(1);
+
+ if ($this->code == 'paymentlink') {
+ $this->assertArrayNotHasKey('mollie_methods_' . $this->code, $methods);
+ return;
+ }
+
+ $this->assertArrayHasKey('mollie_methods_' . $this->code, $methods);
+ }
+
+ public function testThatTheMethodIsActive()
+ {
+ /** @var Method $method */
+ $method = $this->objectManager->getObject(Method::class);
+ $method->id = $this->code;
+ $method->image = new \stdClass;
+ $method->image->size2x = 'http://www.example.com/image.png';
+
+ /** @var MethodCollection $methodCollection */
+ $methodCollection = $this->objectManager->getObject(MethodCollection::class);
+ $methodCollection[] = $method;
+
+ $mollieApiClient = $this->createMock(MollieApiClient::class);
+ $mollieApiClient->methods = $this->createMock(MethodEndpoint::class);
+ $mollieApiClient->methods->method('all')->willReturn($methodCollection);
+
+ /** @var MollieConfigProvider $instance */
+ $instance = $this->objectManager->getObject(MollieConfigProvider::class);
+ $methods = $instance->getActiveMethods($mollieApiClient);
+
+ $this->assertArrayHasKey('mollie_methods_' . $this->code, $methods);
+ $this->assertEquals($method->image->size2x, $methods['mollie_methods_' . $this->code]['image']);
+ }
+}
\ No newline at end of file
diff --git a/Tests/Unit/Model/Methods/ApplePayTest.php b/Tests/Unit/Model/Methods/ApplePayTest.php
new file mode 100644
index 00000000000..be490ea1227
--- /dev/null
+++ b/Tests/Unit/Model/Methods/ApplePayTest.php
@@ -0,0 +1,32 @@
+createMock(MollieApiClient::class);
+ $mollieApiClient->methods = $this->createMock(MethodEndpoint::class);
+
+ $mollieApiClient->methods->expects($this->once())->method('all')->with($this->callback(function ($arguments) {
+ $this->assertArrayHasKey('includeWallets', $arguments);
+ $this->assertEquals('applepay', $arguments['includeWallets']);
+
+ return true;
+ }));
+
+ /** @var MollieConfigProvider $instance */
+ $instance = $this->objectManager->getObject(MollieConfigProvider::class);
+ $instance->getActiveMethods($mollieApiClient);
+ }
+}
diff --git a/Tests/Unit/Model/Methods/BancontactTest.php b/Tests/Unit/Model/Methods/BancontactTest.php
new file mode 100644
index 00000000000..2bc03c7f25e
--- /dev/null
+++ b/Tests/Unit/Model/Methods/BancontactTest.php
@@ -0,0 +1,12 @@
+getMockBuilder(PaymentFactory::class);
+ $mockBuilder->setMethods(['create']);
+ $paymentFactory = $mockBuilder->getMock();
+ $paymentFactory->method('create')->willReturn($this->createMock(Payment::class));
+
+ $this->objectManager = new ObjectManager($this);
+ $this->instance = $this->objectManager->getObject(ProcessAdjustmentFee::class, [
+ 'paymentFactory' => $paymentFactory,
+ 'mollieHelper' => $this->objectManager->getObject(General::class)
+ ]);
+ }
+
+ public function testRefundsPositive()
+ {
+ $creditmemo = $this->createMock(CreditmemoInterface::class);
+ $creditmemo->method('getAdjustment')->willReturn(123);
+
+ $mollieApi = $this->buildMollieApiMock();
+
+ $this->paymentEndpoint->expects($this->once())->method('refund')->with(
+ $this->isInstanceOf(Payment::class), // $payment
+ $this->callback(function ($argument) {
+ $this->assertSame('123.00', $argument['amount']['value']);
+ return true;
+ }) // $data
+ );
+
+ $this->instance->handle($mollieApi, $this->getOrderMock(), $creditmemo);
+ }
+
+ public function testRefundsNegative()
+ {
+ $creditmemo = $this->createMock(CreditmemoInterface::class);
+ $creditmemo->method('getAdjustmentNegative')->willReturn(-123);
+
+ $mollieApi = $this->buildMollieApiMock();
+
+ $this->paymentEndpoint->expects($this->once())->method('refund')->with(
+ $this->isInstanceOf(Payment::class), // $payment
+ $this->callback(function ($argument) {
+ $this->assertSame('123.00', $argument['amount']['value']);
+ return true;
+ }) // $data
+ );
+
+ $this->instance->handle($mollieApi, $this->getOrderMock(), $creditmemo);
+ }
+
+ public function doNotRefundInMollieProvider()
+ {
+ return [
+ [123, null, false],
+ [null, -123, true],
+ ];
+ }
+
+ /**
+ * @dataProvider doNotRefundInMollieProvider
+ */
+ public function testDoNotRefundInMollie($getAdjustment, $getAdjustmentNegative, $expected)
+ {
+ $creditmemo = $this->createMock(CreditmemoInterface::class);
+ $creditmemo->method('getAdjustment')->willReturn($getAdjustment);
+ $creditmemo->method('getAdjustmentNegative')->willReturn($getAdjustmentNegative);
+
+ $mollieApi = $this->buildMollieApiMock();
+
+ $this->paymentEndpoint->method('refund');
+
+ $this->instance->handle($mollieApi, $this->getOrderMock(), $creditmemo);
+
+ $this->assertSame($expected, $this->instance->doNotRefundInMollie());
+ }
+
+ /**
+ * @param \PHPUnit_Framework_MockObject_MockObject $mollieOrder
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ private function buildMollieApiMock()
+ {
+ $mollieOrder = $this->createMock(MollieOrder::class);
+ $mollieOrder->_embedded = new \stdClass();
+
+ $payment = new \stdClass();
+ $payment->id = 123;
+ $mollieOrder->_embedded->payments = [$payment];
+
+ $this->paymentEndpoint = $this->createMock(PaymentEndpoint::class);
+
+ $mollieApi = $this->createMock(MollieApiClient::class);
+ $orderEndpoint = $this->createMock(OrderEndpoint::class);
+ $orderEndpoint->method('get')->willReturn($mollieOrder);
+ $mollieApi->orders = $orderEndpoint;
+ $mollieApi->payments = $this->paymentEndpoint;
+
+ return $mollieApi;
+ }
+
+ /**
+ * @return \PHPUnit_Framework_MockObject_MockObject
+ */
+ private function getOrderMock()
+ {
+ $order = $this->getMockBuilder(OrderInterface::class)
+ ->setMethods(['getMollieTransactionId', 'getOrderCurrencyCode'])
+ ->getMockForAbstractClass();
+ $order->method('getOrderCurrencyCode')->willReturn('EUR');
+
+ return $order;
+ }
+}
diff --git a/etc/adminhtml/methods.xml b/etc/adminhtml/methods.xml
index 3474226f9ef..7e57736b89c 100644
--- a/etc/adminhtml/methods.xml
+++ b/etc/adminhtml/methods.xml
@@ -9,7 +9,6 @@
-
@@ -24,4 +23,5 @@
+
diff --git a/etc/adminhtml/methods/bitcoin.xml b/etc/adminhtml/methods/applepay.xml
similarity index 78%
rename from etc/adminhtml/methods/bitcoin.xml
rename to etc/adminhtml/methods/applepay.xml
index c3a08fdea36..375c83ba476 100644
--- a/etc/adminhtml/methods/bitcoin.xml
+++ b/etc/adminhtml/methods/applepay.xml
@@ -1,19 +1,20 @@
-
- Bitcoin
+
+ Apple Pay
Enabled
Magento\Config\Model\Config\Source\Yesno
- payment/mollie_methods_bitcoin/active
+ payment/mollie_methods_applepay/active
+ Please note: This payment method is only visible when the device of the user Apple Pay has enabled and the checkout is served over HTTPS.]]>
Title
- payment/mollie_methods_bitcoin/title
+ payment/mollie_methods_applepay/title
1
@@ -22,7 +23,7 @@
showInStore="1">
Method
Mollie\Payment\Model\Adminhtml\Source\Method
- payment/mollie_methods_bitcoin/method
+ payment/mollie_methods_applepay/method
1
@@ -32,7 +33,7 @@
Description
- payment/mollie_methods_bitcoin/payment_description
+ payment/mollie_methods_applepay/payment_description
{ordernumber} : The order number for this transaction
{storename} : The name of the store
@@ -47,7 +48,7 @@
showInWebsite="1" showInStore="1">
Payment from Applicable Countries
Magento\Payment\Model\Config\Source\Allspecificcountries
- payment/mollie_methods_bitcoin/allowspecific
+ payment/mollie_methods_applepay/allowspecific
1
@@ -57,7 +58,7 @@
Payment from Specific Countries
Magento\Directory\Model\Config\Source\Country
1
- payment/mollie_methods_bitcoin/specificcountry
+ payment/mollie_methods_applepay/specificcountry
1
@@ -65,7 +66,7 @@
Minimum Order Total
- payment/mollie_methods_bitcoin/min_order_total
+ payment/mollie_methods_applepay/min_order_total
1
@@ -73,7 +74,7 @@
Maximum Order Total
- payment/mollie_methods_bitcoin/max_order_total
+ payment/mollie_methods_applepay/max_order_total
1
@@ -82,7 +83,7 @@
showInStore="1">
Sort Order
validate-number
- payment/mollie_methods_bitcoin/sort_order
+ payment/mollie_methods_applepay/sort_order
1
diff --git a/etc/adminhtml/system.xml b/etc/adminhtml/system.xml
index 1fa1adb761b..2dbfca425a8 100644
--- a/etc/adminhtml/system.xml
+++ b/etc/adminhtml/system.xml
@@ -120,9 +120,15 @@
showInWebsite="0" showInStore="0">
Show Transaction Details
Magento\Config\Model\Config\Source\Yesno
- payment/mollie_general/transaction_details
Display Transaction Details like e.g. IBAN, BIC, Paypal Reference, Card Holder etc.
+
+ Cancel the order when the connection fails
+ Magento\Config\Model\Config\Source\Yesno
+ payment/mollie_general/cancel_failed_orders
+
+
Debug
@@ -141,9 +147,9 @@
Compatibility
Mollie\Payment\Block\Adminhtml\Render\Heading
here.]]>
+ In case of a warning or error please contact your Developer or Hosting Company.
+ Specific information about the errors and warnings can be found
+ here .]]>
diff --git a/etc/config.xml b/etc/config.xml
index 564ccfd5f23..9cb492f6700 100644
--- a/etc/config.xml
+++ b/etc/config.xml
@@ -14,6 +14,7 @@
1
Mollie\Payment\Model\Methods\General
1
+ 0
1
@@ -44,15 +45,6 @@
0
-
- 1
- Mollie\Payment\Model\Methods\Bitcoin
- Bitcoin
- {ordernumber}
- order
- 0
-
-
1
Mollie\Payment\Model\Methods\Creditcard
@@ -186,6 +178,15 @@
0
+
+ 0
+ Mollie\Payment\Model\Methods\ApplePay
+ Apple Pay
+ {ordernumber}
+ order
+ 0
+
+
diff --git a/etc/module.xml b/etc/module.xml
index 7b775d6fc33..ca6c6d1b03b 100644
--- a/etc/module.xml
+++ b/etc/module.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/etc/payment.xml b/etc/payment.xml
index 50f82d7cbd7..6deafb2c1e0 100644
--- a/etc/payment.xml
+++ b/etc/payment.xml
@@ -10,9 +10,6 @@
0
-
- 0
-
0
@@ -55,5 +52,8 @@
0
+
+ 0
+
diff --git a/i18n/de_DE.csv b/i18n/de_DE.csv
index 003589f7f9c..1c97228dcab 100644
--- a/i18n/de_DE.csv
+++ b/i18n/de_DE.csv
@@ -6,7 +6,6 @@
"Bancontact","Bankkontakt"
"Banktransfer","Banküberweisung"
"Belfius","Belfius"
-"Bitcoin","Bitcoin"
"Branding","Branding"
"Check the Mollie extension technical requirements by running the self test. In case of a warning or error please contact your Developer or Hosting Company.","Überprüfen Sie die technischen Anforderungen der Mollie Erweiterung, indem Sie einen Selbsttest durchführen. Im Falle einer Warnung oder eines Fehlers kontaktieren Sie bitte Ihren Entwickler oder Ihre Hosting Firma."
"Compatibility","Kompatibilität"
diff --git a/i18n/en_US.csv b/i18n/en_US.csv
index a511dd9edb1..3e3243b6d8a 100644
--- a/i18n/en_US.csv
+++ b/i18n/en_US.csv
@@ -6,7 +6,6 @@
"Bancontact","Bancontact"
"Banktransfer","Banktransfer"
"Belfius","Belfius"
-"Bitcoin","Bitcoin"
"Branding","Branding"
"Check the Mollie extension technical requirements by running the self test. In case of a warning or error please contact your Developer or Hosting Company.","Check the Mollie extension technical requirements by running the self test. In case of a warning or error please contact your Developer or Hosting Company."
"Compatibility","Compatibility"
@@ -81,4 +80,5 @@
"Language Payment Page","Language Payment Page"
"Let Mollie automatically detect the language or force the language from the store view.","Let Mollie automatically detect the language or force a language for your store view."
"Payment of type %1 has been rejected. Decision is based on order and outcome of risk assessment.","Payment of type %1 has been rejected. Decision is based on order and outcome of risk assessment."
-"Warning: we recommend to use a unique payment status for pending Banktransfer payments","Warning: we recommend to use a unique payment status for pending Banktransfer payments"
\ No newline at end of file
+"Warning: we recommend to use a unique payment status for pending Banktransfer payments","Warning: we recommend to use a unique payment status for pending Banktransfer payments"
+"Please note: This payment method is only visible when the device of the user Apple Pay has enabled and the checkout is served over HTTPS.","Please note: This payment method is only visible when the device of the user Apple Pay has enabled and the checkout is served over HTTPS."
\ No newline at end of file
diff --git a/i18n/es_ES.csv b/i18n/es_ES.csv
index 9e313765049..360731334be 100644
--- a/i18n/es_ES.csv
+++ b/i18n/es_ES.csv
@@ -6,7 +6,6 @@
"Bancontact","Bancontact"
"Banktransfer","Transferencia bancaria"
"Belfius","Belfius"
-"Bitcoin","Bitcoin"
"Branding","Branding"
"Check the Mollie extension technical requirements by running the self test. In case of a warning or error please contact your Developer or Hosting Company.","Compruebe los requisitos técnicos de la extensión Mollie ejecutando el autodiagnóstico. En caso de una advertencia o error, póngase en contacto con su desarrollador o empresa de hosting."
"Compatibility","Compatibilidad"
diff --git a/i18n/fr_FR.csv b/i18n/fr_FR.csv
index 35d540dd913..b0ca24610fa 100644
--- a/i18n/fr_FR.csv
+++ b/i18n/fr_FR.csv
@@ -6,7 +6,6 @@
"Bancontact","Bancontact"
"Banktransfer","Virement bancaire"
"Belfius","Belfius"
-"Bitcoin","Bitcoin"
"Branding","Marquage"
"Check the Mollie extension technical requirements by running the self test. In case of a warning or error please contact your Developer or Hosting Company.","Vérifiez les exigences techniques de l'extension Mollie en exécutant l'auto-test. En cas d'avertissement ou d'erreur, veuillez contacter votre développeur ou votre hébergeur."
"Compatibility","Compatibilité"
diff --git a/i18n/nl_NL.csv b/i18n/nl_NL.csv
index 2ce906429e0..43aff6924ad 100644
--- a/i18n/nl_NL.csv
+++ b/i18n/nl_NL.csv
@@ -6,7 +6,6 @@
"Bancontact","Bancontact"
"Banktransfer","Banktransfer"
"Belfius","Belfius"
-"Bitcoin","Bitcoin"
"Branding","Branding"
"Check the Mollie extension technical requirements by running the self test. In case of a warning or error please contact your Developer or Hosting Company.","Controleer de technische eisen van de Mollie extensie door de zelf test uit te voeren. In het geval van een error of waarschuwing, neem contact op met uw Developer of Hosting Partij."
"Compatibility","Compatibility"
diff --git a/view/adminhtml/templates/form/mollie_paymentlink.phtml b/view/adminhtml/templates/form/mollie_paymentlink.phtml
index b16b2b14b3d..fcf09163c5a 100644
--- a/view/adminhtml/templates/form/mollie_paymentlink.phtml
+++ b/view/adminhtml/templates/form/mollie_paymentlink.phtml
@@ -24,7 +24,6 @@ $code; ?>" style="display:none">
Bancontact
Banktransfer
Belfius
- Bitcoin
Creditcard
EPS
Giftcard
diff --git a/view/adminhtml/web/images/applepay.png b/view/adminhtml/web/images/applepay.png
new file mode 100644
index 00000000000..d2a13238216
Binary files /dev/null and b/view/adminhtml/web/images/applepay.png differ
diff --git a/view/adminhtml/web/images/bitcoin.png b/view/adminhtml/web/images/bitcoin.png
deleted file mode 100755
index f162e8b8e52..00000000000
Binary files a/view/adminhtml/web/images/bitcoin.png and /dev/null differ
diff --git a/view/frontend/layout/checkout_index_index.xml b/view/frontend/layout/checkout_index_index.xml
index 07af04da5c6..82681050bcb 100644
--- a/view/frontend/layout/checkout_index_index.xml
+++ b/view/frontend/layout/checkout_index_index.xml
@@ -31,9 +31,6 @@
-
- true
- -
-
- true
-
-
- true
@@ -76,6 +73,9 @@
-
- true
+ -
+
- true
+
diff --git a/view/frontend/web/js/view/payment/method-renderer.js b/view/frontend/web/js/view/payment/method-renderer.js
index 23e43f35da7..0791517456f 100644
--- a/view/frontend/web/js/view/payment/method-renderer.js
+++ b/view/frontend/web/js/view/payment/method-renderer.js
@@ -18,7 +18,6 @@ define(
{type: 'mollie_methods_bancontact', component: defaultComponent},
{type: 'mollie_methods_banktransfer', component: defaultComponent},
{type: 'mollie_methods_belfius', component: defaultComponent},
- {type: 'mollie_methods_bitcoin', component: defaultComponent},
{type: 'mollie_methods_creditcard', component: defaultComponent},
{type: 'mollie_methods_ideal', component: idealComponent},
{type: 'mollie_methods_kbc', component: kbcComponent},
@@ -33,6 +32,27 @@ define(
{type: 'mollie_methods_giftcard', component: giftcardComponent},
{type: 'mollie_methods_przelewy24', component: defaultComponent}
];
+
+ function canUseApplePay()
+ {
+ try {
+ return window.ApplePaySession && window.ApplePaySession.canMakePayments();
+ } catch (error) {
+ console.warn('Error when trying to check Apple Pay:', error);
+ return false;
+ }
+ }
+
+ /**
+ * Only add Apple Pay if the current client supports Apple Pay.
+ */
+ if (canUseApplePay()) {
+ methods.push({
+ type: 'mollie_methods_applepay',
+ component: defaultComponent
+ });
+ }
+
$.each(methods, function (k, method) {
if (window.checkoutConfig.payment.isActive[method['type']]) {
rendererList.push(method);