diff --git a/Exception/HMACVerificationException.php b/Exception/HMACVerificationException.php
new file mode 100644
index 0000000..87ccf0a
--- /dev/null
+++ b/Exception/HMACVerificationException.php
@@ -0,0 +1,9 @@
+checkoutSession = $checkoutSession;
$this->orderInterface = $orderInterface;
@@ -85,6 +88,7 @@ public function __construct(
$this->orderRepository = $orderRepository;
$this->bitpayInvoiceRepository = $bitpayInvoiceRepository;
$this->returnHashHelper = $returnHashHelper;
+ $this->encryptor = $encryptor;
}
/**
@@ -150,7 +154,8 @@ public function execute(ResultInterface $defaultResult, string $returnId = null)
$order->getId(),
$invoiceID,
$invoice->getExpirationTime(),
- $invoice->getAcceptanceWindow()
+ $invoice->getAcceptanceWindow(),
+ $this->encryptor->encrypt($this->config->getToken())
);
$this->transactionRepository->add($incrementId, $invoiceID, 'new');
diff --git a/Model/BitpayInvoiceRepository.php b/Model/BitpayInvoiceRepository.php
index bae178e..8afe623 100755
--- a/Model/BitpayInvoiceRepository.php
+++ b/Model/BitpayInvoiceRepository.php
@@ -21,11 +21,17 @@ public function __construct(BitpayInvoice $bitpayInvoice)
* @param string $invoiceID
* @param int $expirationTime
* @param int|null $acceptanceWindow
+ * @param string|null $bitpayToken
* @return void
*/
- public function add(string $orderId, string $invoiceID, int $expirationTime, ?int $acceptanceWindow): void
- {
- $this->bitpayInvoice->add($orderId, $invoiceID, $expirationTime, $acceptanceWindow);
+ public function add(
+ string $orderId,
+ string $invoiceID,
+ int $expirationTime,
+ ?int $acceptanceWindow,
+ ?string $bitpayToken
+ ): void {
+ $this->bitpayInvoice->add($orderId, $invoiceID, $expirationTime, $acceptanceWindow, $bitpayToken);
}
/**
diff --git a/Model/Ipn/WebhookVerifier.php b/Model/Ipn/WebhookVerifier.php
new file mode 100644
index 0000000..676a985
--- /dev/null
+++ b/Model/Ipn/WebhookVerifier.php
@@ -0,0 +1,32 @@
+coreRegistry = $registry;
$this->responseFactory = $responseFactory;
$this->url = $url;
$this->quoteFactory = $quoteFactory;
- $this->orderInterface = $orderInterface;
+ $this->orderFactory = $orderFactory;
$this->checkoutSession = $checkoutSession;
$this->logger = $logger;
$this->config = $config;
@@ -93,6 +121,9 @@ public function __construct(
$this->request = $request;
$this->client = $client;
$this->response = $response;
+ $this->bitpayInvoiceRepository = $bitpayInvoiceRepository;
+ $this->encryptor = $encryptor;
+ $this->webhookVerifier = $webhookVerifier;
$this->returnHashHelper = $returnHashHelper;
}
@@ -108,7 +139,7 @@ public function postClose()
$response = $this->responseFactory->create();
try {
$orderID = $this->request->getParam('orderID', null);
- $order = $this->orderInterface->loadByIncrementId($orderID);
+ $order = $this->orderFactory->create()->loadByIncrementId($orderID);
$invoiceCloseHandling = $this->config->getBitpayInvoiceCloseHandling();
if ($this->config->getBitpayCheckoutSuccess() === 'standard' && $invoiceCloseHandling === 'keep_order') {
$this->checkoutSession->setLastSuccessQuoteId($order->getQuoteId())
@@ -151,10 +182,22 @@ public function postClose()
public function postIpn()
{
try {
- $allData = $this->serializer->unserialize($this->request->getContent());
+ $requestBody = $this->request->getContent();
+ $allData = $this->serializer->unserialize($requestBody);
$data = $allData['data'];
$event = $allData['event'];
$orderId = $data['orderId'];
+
+ $bitPayInvoiceData = $this->bitpayInvoiceRepository->getByOrderId($orderId);
+ if (!empty($bitPayInvoiceData['bitpay_token'])) {
+ $signingKey = $this->encryptor->decrypt($bitPayInvoiceData['bitpay_token']);
+ $xSignature = $this->request->getHeader('x-signature');
+
+ if (!$this->webhookVerifier->isValidHmac($signingKey, $xSignature, $requestBody)) {
+ throw new HMACVerificationException('HMAC Verification Failed!');
+ }
+ }
+
$orderInvoiceId = $data['id'];
$row = $this->transactionRepository->findBy($orderId, $orderInvoiceId);
$client = $this->client->initialize();
@@ -179,7 +222,7 @@ public function postIpn()
$invoiceStatus = $this->invoice->getBPCCheckInvoiceStatus($client, $orderInvoiceId);
$updateWhere = ['order_id = ?' => $orderId, 'transaction_id = ?' => $orderInvoiceId];
$this->transactionRepository->update('transaction_status', $invoiceStatus, $updateWhere);
- $order = $this->orderInterface->loadByIncrementId($orderId);
+ $order = $this->orderFactory->create()->loadByIncrementId($orderId);
switch ($event['name']) {
case Invoice::COMPLETED:
if ($invoiceStatus == 'complete') {
diff --git a/Model/ResourceModel/BitpayInvoice.php b/Model/ResourceModel/BitpayInvoice.php
index 536401a..40e8f7c 100755
--- a/Model/ResourceModel/BitpayInvoice.php
+++ b/Model/ResourceModel/BitpayInvoice.php
@@ -26,10 +26,16 @@ public function _construct()
* @param string $invoiceID
* @param int $expirationTime
* @param int|null $acceptanceWindow
+ * @param string|null $bitpayToken
* @return void
*/
- public function add(string $orderId, string $invoiceID, int $expirationTime, ?int $acceptanceWindow)
- {
+ public function add(
+ string $orderId,
+ string $invoiceID,
+ int $expirationTime,
+ ?int $acceptanceWindow,
+ ?string $bitpayToken
+ ) {
$connection = $this->getConnection();
$table_name = $connection->getTableName(self::TABLE_NAME);
$connection->insert(
@@ -38,7 +44,8 @@ public function add(string $orderId, string $invoiceID, int $expirationTime, ?in
'order_id' => $orderId,
'invoice_id' => $invoiceID,
'expiration_time' => $expirationTime,
- 'acceptance_window'=> $acceptanceWindow
+ 'acceptance_window'=> $acceptanceWindow,
+ 'bitpay_token' => $bitpayToken
]
);
}
diff --git a/Test/Integration/Model/BPRedirectTest.php b/Test/Integration/Model/BPRedirectTest.php
index bf9266f..7cae7f9 100755
--- a/Test/Integration/Model/BPRedirectTest.php
+++ b/Test/Integration/Model/BPRedirectTest.php
@@ -24,6 +24,7 @@
use Magento\Sales\Model\OrderRepository;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
use Magento\Framework\Encryption\EncryptorInterface;
/**
@@ -111,6 +112,13 @@ class BPRedirectTest extends TestCase
* @var EncryptorInterface|MockObject $encryptor
*/
private $encryptor;
+
+ /**
+ * @var ReturnHash $returnHash
+ */
+ private $returnHash;
+
+
public function setUp(): void
{
$this->objectManager = Bootstrap::getObjectManager();
@@ -118,20 +126,31 @@ public function setUp(): void
$this->orderInterface = $this->objectManager->get(OrderInterface::class);
$this->config = $this->objectManager->get(Config::class);
$this->transactionRepository = $this->objectManager->get(TransactionRepository::class);
+ /**
+ * @var Invoice|MockObject
+ */
$this->invoice = $this->getMockBuilder(Invoice::class)->disableOriginalConstructor()->getMock();
$this->messageManager = $this->objectManager->get(Manager::class);
$this->registry = $this->objectManager->get(Registry::class);
$this->url = $this->objectManager->get(UrlInterface::class);
$this->logger = $this->objectManager->get(Logger::class);
$this->resultFactory = $this->objectManager->get(ResultFactory::class);
+ /**
+ * @var Client|MockObject
+ */
$this->client = $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock();
$this->orderRepository = $this->objectManager->get(OrderRepository::class);
$this->bitpayInvoiceRepository = $this->objectManager->get(BitpayInvoiceRepository::class);
$this->bitpayInvoiceRepository = $this->objectManager->get(BitpayInvoiceRepository::class);
+ /**
+ * @var EncryptorInterface|MockObject
+ */
$this->encryptor = $this->getMockBuilder(EncryptorInterface::class)
->disableOriginalConstructor()
->getMock();
+ $this->returnHash = $this->objectManager->get(ReturnHash::class);
+
$this->bpRedirect = new BPRedirect(
$this->checkoutSession,
$this->orderInterface,
@@ -146,6 +165,7 @@ public function setUp(): void
$this->client,
$this->orderRepository,
$this->bitpayInvoiceRepository,
+ $this->returnHash,
$this->encryptor
);
}
@@ -197,7 +217,6 @@ public function testExecute(): void
$this->assertEquals('100000001', $result[0]['order_id']);
$this->assertEquals('new', $result[0]['transaction_status']);
$this->assertEquals('test', $this->config->getBitpayEnv());
- $this->assertEquals('redirect', $this->config->getBitpayUx());
$this->assertEquals($bitpayMethodCode, $methodCode);
}
diff --git a/Test/Integration/Model/IpnManagementTest.php b/Test/Integration/Model/IpnManagementTest.php
index 174cafa..466b740 100644
--- a/Test/Integration/Model/IpnManagementTest.php
+++ b/Test/Integration/Model/IpnManagementTest.php
@@ -9,20 +9,24 @@
use Bitpay\BPCheckout\Model\TransactionRepository;
use BitPaySDK\Model\Invoice\Buyer;
use Magento\Framework\ObjectManagerInterface;
-use Bitpay\BPCheckout\Helper\ReturnHash;
use Bitpay\BPCheckout\Logger\Logger;
+use Bitpay\BPCheckout\Model\BitpayInvoiceRepository;
+use Bitpay\BPCheckout\Model\Ipn\WebhookVerifier;
+use Bitpay\BPCheckout\Helper\ReturnHash;
use Magento\Checkout\Model\Session;
use Magento\Framework\App\ResponseFactory;
use Magento\Framework\DataObject;
+use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Registry;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\UrlInterface;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Framework\Webapi\Rest\Response;
use Magento\Quote\Model\QuoteFactory;
-use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Model\OrderFactory;
use Magento\TestFramework\Helper\Bootstrap;
use PHPUnit\Framework\TestCase;
+use PHPUnit\Framework\MockObject\MockObject;
/**
* @SuppressWarnings(PHPMD.TooManyFields)
@@ -56,9 +60,9 @@ class IpnManagementTest extends TestCase
private $quoteFactory;
/**
- * @var OrderInterface $orderInterface
+ * @var OrderFactory|MockObject $orderFactory
*/
- private $orderInterface;
+ private $orderFactory;
/**
* @var Registry $coreRegistry
@@ -101,7 +105,7 @@ class IpnManagementTest extends TestCase
private $objectManager;
/**
- * @var Client $client
+ * @var Client|MockObject $client
*/
private $client;
@@ -110,10 +114,25 @@ class IpnManagementTest extends TestCase
*/
private $response;
+ /**
+ * @var BitpayInvoiceRepository|MockObject $bitpayInvoiceRepository
+ */
+ private $bitpayInvoiceRepository;
+
+ /**
+ * @var EncryptorInterface|MockObject $encryptor
+ */
+ private $encryptor;
+
+ /**
+ * @var WebhookVerifier|MockObject $webhookVerifier
+ */
+ private $webhookVerifier;
+
/**
* @var ReturnHash $returnHash
*/
- private ReturnHash $returnHash;
+ private $returnHash;
public function setUp(): void
{
@@ -122,24 +141,33 @@ public function setUp(): void
$this->responseFactory = $this->objectManager->get(ResponseFactory::class);
$this->url = $this->objectManager->get(UrlInterface::class);
$this->quoteFactory = $this->objectManager->get(QuoteFactory::class);
- $this->orderInterface = $this->objectManager->get(OrderInterface::class);
+ $this->orderFactory = $this->objectManager->get(OrderFactory::class);
$this->checkoutSession = $this->objectManager->get(Session::class);
$this->logger = $this->objectManager->get(Logger::class);
$this->config = $this->objectManager->get(Config::class);
$this->serializer = $this->objectManager->get(Json::class);
$this->transactionRepository = $this->objectManager->get(TransactionRepository::class);
+ /**
+ * @var Invoice|MockObject
+ */
$this->invoice = $this->getMockBuilder(Invoice::class)->disableOriginalConstructor()->getMock();
$this->request = $this->objectManager->get(Request::class);
+ /**
+ * @var Client|MockObject
+ */
$this->client = $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock();
$this->response = $this->objectManager->get(Response::class);
+ $this->bitpayInvoiceRepository = $this->objectManager->get(BitpayInvoiceRepository::class);
+ $this->encryptor =$this->objectManager->get(EncryptorInterface::class);
+ $this->webhookVerifier = $this->objectManager->get(WebhookVerifier::class);
$this->returnHash = $this->objectManager->get(ReturnHash::class);
-
+
$this->ipnManagement = new IpnManagement(
$this->responseFactory,
$this->url,
$this->coreRegistry,
$this->checkoutSession,
- $this->orderInterface,
+ $this->orderFactory,
$this->quoteFactory,
$this->logger,
$this->config,
@@ -149,6 +177,9 @@ public function setUp(): void
$this->request,
$this->client,
$this->response,
+ $this->bitpayInvoiceRepository,
+ $this->encryptor,
+ $this->webhookVerifier,
$this->returnHash
);
}
@@ -159,15 +190,15 @@ public function setUp(): void
*/
public function testPostClose()
{
- $order = $this->orderInterface->loadByIncrementId('100000001');
- $this->request->setParam('orderID', $order->getEntityId());
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->request->setParam('orderID', $order->getIncrementId());
$quoteId = $order->getQuoteId();
/** @var \Magento\Quote\Model\Quote $quote */
$this->quoteFactory->create()->loadByIdWithoutStore($quoteId);
$this->ipnManagement->postClose();
-
- $this->assertTrue($this->orderInterface->loadByIncrementId('100000001')->isDeleted());
+
+ $this->assertNull($this->orderFactory->create()->loadByIncrementId('100000001')->getId());
$this->assertEquals($quoteId, $this->checkoutSession->getQuoteId());
}
@@ -177,14 +208,14 @@ public function testPostClose()
*/
public function testPostCloseKeepOrder()
{
- $order = $this->orderInterface->loadByIncrementId('100000001');
- $this->request->setParam('orderID', $order->getEntityId());
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
+ $this->request->setParam('orderID', $order->getIncrementId());
$quoteId = $order->getQuoteId();
/** @var \Magento\Quote\Model\Quote $quote */
$this->quoteFactory->create()->loadByIdWithoutStore($quoteId);
$this->ipnManagement->postClose();
- $this->assertFalse($this->orderInterface->loadByIncrementId('100000001')->isDeleted());
+ $this->assertEquals($order->getId(), $this->orderFactory->create()->loadByIncrementId('100000001')->getId());
$this->assertEquals($quoteId, $this->checkoutSession->getQuoteId());
}
@@ -224,7 +255,7 @@ public function testPostIpn()
$this->ipnManagement->postIpn();
- $order = $this->orderInterface->loadByIncrementId($orderId);
+ $order = $this->orderFactory->create()->loadByIncrementId('100000001');
$result = $this->transactionRepository->findBy($orderId, $orderInvoiceId);
$this->assertEquals('complete', $result[0]['transaction_status']);
diff --git a/Test/Unit/Model/BPRedirectTest.php b/Test/Unit/Model/BPRedirectTest.php
index c855dd5..11f08bf 100755
--- a/Test/Unit/Model/BPRedirectTest.php
+++ b/Test/Unit/Model/BPRedirectTest.php
@@ -21,6 +21,7 @@
use Magento\Framework\UrlInterface;
use \Magento\Framework\Controller\Result\Redirect;
use Magento\Framework\Controller\ResultFactory;
+use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Sales\Model\Order;
use Magento\Payment\Model\MethodInterface;
use Magento\Sales\Model\OrderRepository;
@@ -118,6 +119,11 @@ class BPRedirectTest extends TestCase
*/
private $returnHash;
+ /**
+ * @var EncryptorInterface|MockObject $encryptor
+ */
+ private $encryptor;
+
public function setUp(): void
{
$this->checkoutSession = $this->getMock(Session::class);
@@ -136,6 +142,7 @@ public function setUp(): void
$this->orderRepository = $this->getMock(OrderRepository::class);
$this->bitpayInvoiceRepository = $this->getMock(BitpayInvoiceRepository::class);
$this->returnHash = $this->getMock(ReturnHash::class);
+ $this->encryptor = $this->getMock(EncryptorInterface::class);
$this->bpRedirect = $this->getClass();
}
@@ -175,7 +182,7 @@ public function testExecute(): void
$billingAddress->expects($this->once())->method('getFirstName')->willReturn('test');
$billingAddress->expects($this->once())->method('getLastName')->willReturn('test1');
$order = $this->getOrder($incrementId, $payment, $billingAddress, $lastOrderId);
- $this->prepareConfig($baseUrl, 'redirect');
+ $this->prepareConfig($baseUrl);
$method->expects($this->once())->method('getCode')->willReturn(Config::BITPAY_PAYMENT_METHOD_NAME);
$payment->expects($this->once())->method('getMethodInstance')->willReturn($method);
$this->order->expects($this->once())->method('load')->with($lastOrderId)->willReturn($order);
@@ -196,7 +203,7 @@ public function testExecute(): void
$this->resultFactory->expects($this->once())->method('create')->willReturn($result);
/**
- * @var \Magento\Framework\View\Result\Page
+ * @var \Magento\Framework\Controller\ResultInterface|MockObject
*/
$page = $this->getMock(\Magento\Framework\View\Result\Page::class);
@@ -214,7 +221,7 @@ public function testExecuteNoOrderId(): void
$this->resultFactory->expects($this->once())->method('create')->willReturn($result);
/**
- * @var \Magento\Framework\View\Result\Page
+ * @var \Magento\Framework\Controller\ResultInterface|MockObject
*/
$page = $this->getMock(\Magento\Framework\View\Result\Page::class);
@@ -242,7 +249,7 @@ public function testExecuteNoBitpayPaymentMethod(): void
$this->order->expects($this->once())->method('load')->with($lastOrderId)->willReturn($order);
/**
- * @var \Magento\Framework\View\Result\Page
+ * @var \Magento\Framework\Controller\ResultInterface|MockObject
*/
$page = $this->getMock(\Magento\Framework\View\Result\Page::class);
@@ -285,7 +292,7 @@ public function testExecuteException($exceptionType): void
$billingAddress->expects($this->once())->method('getFirstName')->willReturn('test');
$billingAddress->expects($this->once())->method('getLastName')->willReturn('test1');
$order = $this->getOrder($incrementId, $payment, $billingAddress, null);
- $this->prepareConfig($baseUrl, 'redirect');
+ $this->prepareConfig($baseUrl);
$method->expects($this->once())->method('getCode')->willReturn(Config::BITPAY_PAYMENT_METHOD_NAME);
$payment->expects($this->once())->method('getMethodInstance')->willReturn($method);
$this->order->expects($this->once())->method('load')->with($lastOrderId)->willReturn($order);
@@ -299,7 +306,7 @@ public function testExecuteException($exceptionType): void
->willThrowException(new $exceptionType('something went wrong'));
/**
- * @var \Magento\Framework\View\Result\Page
+ * @var \Magento\Framework\Controller\ResultInterface|MockObject
*/
$page = $this->getMock(\Magento\Framework\View\Result\Page::class);
@@ -337,7 +344,7 @@ private function getOrder(string $incrementId, MockObject $payment, MockObject $
return $order;
}
- private function prepareConfig(string $baseUrl, string $ux): void
+ private function prepareConfig(string $baseUrl): void
{
$this->config->expects($this->once())->method('getBPCheckoutOrderStatus')->willReturn('pending');
$this->config->expects($this->once())->method('getBaseUrl')->willReturn($baseUrl);
@@ -380,7 +387,8 @@ private function getClass(): BPRedirect
$this->client,
$this->orderRepository,
$this->bitpayInvoiceRepository,
- $this->returnHash
+ $this->returnHash,
+ $this->encryptor
);
}
diff --git a/Test/Unit/Model/Ipn/WebhookVerifierTest.php b/Test/Unit/Model/Ipn/WebhookVerifierTest.php
new file mode 100644
index 0000000..6b3a201
--- /dev/null
+++ b/Test/Unit/Model/Ipn/WebhookVerifierTest.php
@@ -0,0 +1,43 @@
+webhookVerifier = new WebhookVerifier();
+ }
+
+ public function testIsValidHmac(): void
+ {
+ $this->assertTrue(
+ $this->webhookVerifier->isValidHmac(
+ 'testkey',
+ 'SKEpFPexQ4ko9QAEre51+n+ypvQQidUheDl3+4irEOQ=',
+ '{"data":{"test":true}'
+ )
+ );
+ }
+
+ public function testIsValidHmacFalse(): void
+ {
+ $this->assertFalse(
+ $this->webhookVerifier->isValidHmac(
+ 'differentkey',
+ 'SKEpFPexQ4ko9QAEre51+n+ypvQQidUheDl3+4irEOQ=',
+ '{"data":{"test":true}'
+ )
+ );
+ }
+}
diff --git a/Test/Unit/Model/IpnManagementTest.php b/Test/Unit/Model/IpnManagementTest.php
index f1dcf68..81d2da3 100644
--- a/Test/Unit/Model/IpnManagementTest.php
+++ b/Test/Unit/Model/IpnManagementTest.php
@@ -7,19 +7,22 @@
use Bitpay\BPCheckout\Model\Config;
use Bitpay\BPCheckout\Model\Invoice;
use Bitpay\BPCheckout\Model\IpnManagement;
-use Bitpay\BPCheckout\Helper\ReturnHash;
+use Bitpay\BPCheckout\Model\Ipn\WebhookVerifier;
use Bitpay\BPCheckout\Logger\Logger;
+use Bitpay\BPCheckout\Model\BitpayInvoiceRepository;
+use Bitpay\BPCheckout\Helper\ReturnHash;
use Bitpay\BPCheckout\Model\TransactionRepository;
use BitPaySDK\Model\Invoice\Buyer;
use Magento\Checkout\Model\Session;
use Magento\Framework\App\ResponseFactory;
+use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Registry;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Framework\UrlInterface;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Quote\Model\Quote;
use Magento\Quote\Model\QuoteFactory;
-use Magento\Sales\Api\Data\OrderInterface;
+use Magento\Sales\Model\OrderFactory;
use Magento\Sales\Model\Order;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -50,9 +53,9 @@ class IpnManagementTest extends TestCase
private $quoteFactory;
/**
- * @var OrderInterface|MockObject
+ * @var OrderFactory|MockObject
*/
- private $orderInterface;
+ private $orderFactory;
/**
* @var Registry|MockObject
@@ -104,6 +107,21 @@ class IpnManagementTest extends TestCase
*/
private $response;
+ /**
+ * @var BitpayInvoiceRepository|MockObject $bitpayInvoiceRepository
+ */
+ private $bitpayInvoiceRepository;
+
+ /**
+ * @var EncryptorInterface|MockObject $encryptor
+ */
+ private $encryptor;
+
+ /**
+ * @var WebhookVerifier|MockObject $webhookVerifier
+ */
+ protected $webhookVerifier;
+
/**
* @var ReturnHash|MockObject
*/
@@ -115,7 +133,7 @@ public function setUp(): void
$this->responseFactory = $this->getMock(ResponseFactory::class);
$this->url = $this->getMock(UrlInterface::class);
$this->quoteFactory = $this->getMock(QuoteFactory::class);
- $this->orderInterface = $this->getMock(\Magento\Sales\Model\Order::class);
+ $this->orderFactory = $this->getMock(\Magento\Sales\Model\OrderFactory::class);
$this->checkoutSession = $this->getMock(Session::class);
$this->logger = $this->getMock(Logger::class);
$this->config = $this->getMock(Config::class);
@@ -125,6 +143,9 @@ public function setUp(): void
$this->request = $this->getMock(Request::class);
$this->client = $this->getMock(Client::class);
$this->response = $this->getMock(\Magento\Framework\Webapi\Rest\Response::class);
+ $this->bitpayInvoiceRepository = $this->getMock(BitpayInvoiceRepository::class);
+ $this->encryptor = $this->getMock(EncryptorInterface::class);
+ $this->webhookVerifier = $this->getMock(WebhookVerifier::class);
$this->returnHashHelper = $this->getMock(ReturnHash::class);
$this->ipnManagement = $this->getClass();
}
@@ -138,11 +159,17 @@ public function testPostClose(): void
$order = $this->getMock(Order::class);
$orderId = '000000012';
$this->url->expects($this->once())->method('getUrl')->willReturn($cartUrl);
-
+ $order->expects($this->once())
+ ->method('loadByIncrementId')
+ ->with($orderId)
+ ->willReturnSelf();
$this->request->expects($this->once())->method('getParam')->willReturn($orderId);
$this->responseFactory->expects($this->once())->method('create')->willReturn($response);
$order->expects($this->once())->method('getData')->willReturn(['quote_id' => $quoteId]);
- $this->orderInterface->expects($this->once())->method('loadByIncrementId')->willReturn($order);
+
+ $this->orderFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($order);
$quote->expects($this->once())->method('loadByIdWithoutStore')->willReturnSelf();
$quote->expects($this->once())->method('getId')->willReturn($quoteId);
@@ -169,7 +196,14 @@ public function testPostCloseKeepOrder(): void
$this->request->expects($this->once())->method('getParam')->willReturn($orderId);
$this->responseFactory->expects($this->once())->method('create')->willReturn($response);
- $this->orderInterface->expects($this->once())->method('loadByIncrementId')->willReturn($order);
+
+ $order->expects($this->once())
+ ->method('loadByIncrementId')
+ ->with($orderId)
+ ->willReturnSelf();
+ $this->orderFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($order);
$this->checkoutSession
->method('__call')
@@ -195,11 +229,16 @@ public function testPostCloseQuoteNotFound(): void
$this->url->expects($this->once())
->method('getUrl')
->willReturn('http://localhost/checkout/cart?reload=1');
-
+ $order->expects($this->once())
+ ->method('loadByIncrementId')
+ ->with($orderId)
+ ->willReturnSelf();
$this->responseFactory->expects($this->once())->method('create')->willReturn($response);
$this->request->expects($this->once())->method('getParam')->willReturn($orderId);
$order->expects($this->once())->method('getData')->willReturn(['quote_id' => $quoteId]);
- $this->orderInterface->expects($this->once())->method('loadByIncrementId')->willReturn($order);
+ $this->orderFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($order);
$quote->expects($this->once())->method('loadByIdWithoutStore')->willReturnSelf();
$quote->expects($this->once())->method('getId')->willReturn(null);
$this->quoteFactory->expects($this->once())->method('create')->willReturn($quote);
@@ -214,13 +253,19 @@ public function testPostCloseExeception(): void
$orderId = '000000012';
$response = $this->getMock(\Magento\Framework\HTTP\PhpEnvironment\Response::class);
$order = $this->getMock(Order::class);
+ $order->expects($this->once())
+ ->method('loadByIncrementId')
+ ->with($orderId)
+ ->willReturnSelf();
$this->url->expects($this->once())
->method('getUrl')
->willReturn('http://localhost/checkout/cart?reload=1');
$this->responseFactory->expects($this->once())->method('create')->willReturn($response);
$this->request->expects($this->once())->method('getParam')->willReturn($orderId);
$order->expects($this->once())->method('getData')->willReturn([]);
- $this->orderInterface->expects($this->once())->method('loadByIncrementId')->willReturn($order);
+ $this->orderFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($order);
$response->expects($this->once())->method('setRedirect')->willReturnSelf();
@@ -379,6 +424,53 @@ public function testPostIpnCompleteInvalid(): void
$this->ipnManagement->postIpn();
}
+ public function testPostIpnHmacVerificationSuccess(): void
+ {
+ $this->bitpayInvoiceRepository->expects($this->once())->method('getByOrderId')->willReturn([
+ 'order_id' => 12,
+ 'invoice_id' => '12',
+ 'expiration_time' => 1726740384932,
+ 'acceptance_window'=> '',
+ 'bitpay_token' => '0:3:testtokenencoded'
+ ]);
+ $this->encryptor->expects($this->once())->method('decrypt')->willReturn('testtoken');
+ $this->request->expects($this->once())->method('getHeader')->with('x-signature')->willReturn('test');
+ $this->webhookVerifier->expects($this->once())->method('isValidHmac')->willReturn(true);
+ $this->response->expects($this->never())->method('addMessage');
+
+ $this->preparePostIpn('invoice_completed', 'test');
+
+ $this->ipnManagement->postIpn();
+ }
+
+ public function testPostIpnHmacVerificationFailure(): void
+ {
+ $orderInvoiceId = '12';
+ $data = $this->prepareData($orderInvoiceId, 'invoice_completed');
+ $serializer = new Json();
+ $serializerData = $serializer->serialize($data);
+ $this->serializer->expects($this->once())->method('unserialize')->willReturn($data);
+ $this->request->expects($this->once())->method('getContent')->willReturn($serializerData);
+
+ $this->bitpayInvoiceRepository->expects($this->once())->method('getByOrderId')->willReturn([
+ 'order_id' => 12,
+ 'invoice_id' => '12',
+ 'expiration_time' => 1726740384932,
+ 'acceptance_window'=> '',
+ 'bitpay_token' => '0:3:testtokenencoded'
+ ]);
+ $this->encryptor->expects($this->once())->method('decrypt')->willReturn('testtoken');
+ $this->request->expects($this->once())->method('getHeader')->with('x-signature')->willReturn('test');
+ $this->webhookVerifier->expects($this->once())->method('isValidHmac')->willReturn(false);
+
+ $this->response->expects($this->once())
+ ->method('addMessage')
+ ->with('HMAC Verification Failed!', 500)
+ ->willReturnSelf();
+
+ $this->ipnManagement->postIpn();
+ }
+
private function preparePostIpn(string $eventName, string $invoiceStatus): void
{
$orderInvoiceId = '12';
@@ -403,7 +495,13 @@ private function preparePostIpn(string $eventName, string $invoiceStatus): void
$this->config->expects($this->once())->method('getToken')->willReturn('test');
$this->invoice->expects($this->once())->method('getBPCCheckInvoiceStatus')->willReturn($invoiceStatus);
$order = $this->getMock(Order::class);
- $this->orderInterface->expects($this->once())->method('loadByIncrementId')->willReturn($order);
+ $order->expects($this->once())
+ ->method('loadByIncrementId')
+ ->with($data['data']['orderId'])
+ ->willReturnSelf();
+ $this->orderFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($order);
}
private function getMock(string $className): MockObject
@@ -418,7 +516,7 @@ private function getClass(): IpnManagement
$this->url,
$this->coreRegistry,
$this->checkoutSession,
- $this->orderInterface,
+ $this->orderFactory,
$this->quoteFactory,
$this->logger,
$this->config,
@@ -428,6 +526,9 @@ private function getClass(): IpnManagement
$this->request,
$this->client,
$this->response,
+ $this->bitpayInvoiceRepository,
+ $this->encryptor,
+ $this->webhookVerifier,
$this->returnHashHelper
);
}
diff --git a/Test/Unit/Model/ResourceModel/TransactionTest.php b/Test/Unit/Model/ResourceModel/TransactionTest.php
index 184b7ed..427168b 100755
--- a/Test/Unit/Model/ResourceModel/TransactionTest.php
+++ b/Test/Unit/Model/ResourceModel/TransactionTest.php
@@ -14,7 +14,7 @@
class TransactionTest extends TestCase
{
/**
- * @var Context $context
+ * @var Context|MockObject $context
*/
private $context;
@@ -28,11 +28,6 @@ class TransactionTest extends TestCase
*/
private $adapter;
- /**
- * @var Context|MockObject $context
- */
- private $context;
-
public function setUp(): void
{
$this->prepareContext();
diff --git a/etc/db_schema.xml b/etc/db_schema.xml
index 9ae2172..d2f635f 100644
--- a/etc/db_schema.xml
+++ b/etc/db_schema.xml
@@ -25,6 +25,7 @@
comment="Expiration time to pay invoice"/>
+