From e33a64bf1baa251c6abf9e3724a40a8bce6992ee Mon Sep 17 00:00:00 2001 From: Szymon Wlodarski Date: Thu, 5 Dec 2024 15:40:53 +0100 Subject: [PATCH] SP-1106 - redirect user to checkout success page on invoice close and show pay button if invoice isn't paid --- Block/Checkout/Onepage/Success/PayButton.php | 140 +++++++++ Controller/Invoice/Pay.php | 139 +++++++++ Helper/ReturnHash.php | 55 ++++ Model/BPRedirect.php | 15 +- Model/IpnManagement.php | 44 ++- Model/ResourceModel/Transaction.php | 26 ++ Model/TransactionRepository.php | 12 + Test/Integration/Model/BPRedirectTest.php | 20 +- Test/Integration/Model/IpnManagementTest.php | 17 +- .../Model/ResourceModel/TransactionTest.php | 5 + Test/Unit/Model/BPRedirectTest.php | 41 ++- Test/Unit/Model/IpnManagementTest.php | 32 +- .../Model/ResourceModel/TransactionTest.php | 11 +- .../layout/checkout_onepage_success.xml | 11 + .../checkout/order/success/pay-button.phtml | 9 + .../web/images/Pay-with-BitPay-CardGroup.svg | 276 +++++++++--------- 16 files changed, 647 insertions(+), 206 deletions(-) create mode 100644 Block/Checkout/Onepage/Success/PayButton.php create mode 100644 Controller/Invoice/Pay.php create mode 100644 Helper/ReturnHash.php create mode 100644 view/frontend/layout/checkout_onepage_success.xml create mode 100644 view/frontend/templates/checkout/order/success/pay-button.phtml diff --git a/Block/Checkout/Onepage/Success/PayButton.php b/Block/Checkout/Onepage/Success/PayButton.php new file mode 100644 index 0000000..a043338 --- /dev/null +++ b/Block/Checkout/Onepage/Success/PayButton.php @@ -0,0 +1,140 @@ +transactionRepository = $transactionRepository; + $this->client = $client; + $this->config = $config; + $this->url = $url; + } + + /** + * Returns true when Pay button be displayed + * + * @return bool + */ + public function canViewPayButton(): bool + { + if ($this->config->getBitpayCheckoutSuccess() === 'standard' + && $this->config->getBitpayInvoiceCloseHandling() === 'keep_order') { + $invoice = $this->getBitpayInvoice(); + + return $invoice !== null; + } + + return false; + } + + /** + * Returns button url + * + * @return string + */ + public function getButtonUrl(): string + { + return $this->url->getUrl('bpcheckout/invoice/pay', [ + '_query' => [ + 'order_id' => $this->getOrder()->getId(), 'invoice_id' => $this->getBitpayInvoice()->getId() + ] + ]); + } + + /** + * Get BitPay invoice by last order + * + * @return Invoice|null + */ + protected function getBitpayInvoice(): ?Invoice + { + if (!$this->invoice) { + $order = $this->getOrder(); + if ($order->canInvoice()) { + $transactions = $this->transactionRepository + ->findByOrderIdAndTransactionStatus($order->getIncrementId(), 'new'); + if (!empty($transactions)) { + $lastTransaction = array_pop($transactions); + $client = $this->client->initialize(); + $invoice = $client->getInvoice($lastTransaction['transaction_id']); + + $this->invoice = $invoice; + } + } + } + + return $this->invoice; + } + + /** + * Get order instance based on last order ID + * + * @return Order + */ + protected function getOrder(): Order + { + return $this->_checkoutSession->getLastRealOrder(); + } +} diff --git a/Controller/Invoice/Pay.php b/Controller/Invoice/Pay.php new file mode 100644 index 0000000..eb2f544 --- /dev/null +++ b/Controller/Invoice/Pay.php @@ -0,0 +1,139 @@ +request = $request; + $this->messageManager = $messageManager; + $this->resultRedirectFactory = $resultRedirectFactory; + $this->orderRepository = $orderRepository; + $this->transactionRepository = $transactionRepository; + $this->checkoutSession = $checkoutSession; + $this->client = $client; + $this->config = $config; + } + + /** + * Get checkout customer info + * + * @return ResultInterface + */ + public function execute() + { + $orderId = $this->request->getParam('order_id', null); + $invoiceId = $this->request->getParam('invoice_id', null); + + try { + if (!$orderId || !$invoiceId || $this->config->getBitpayCheckoutSuccess() !== 'standard' + || $this->config->getBitpayInvoiceCloseHandling() !== 'keep_order') { + throw new LocalizedException(new Phrase('Invalid request!')); + } + + /** @var \Magento\Sales\Model\Order $order */ + $order = $this->orderRepository->get($orderId); + if (!$order->canInvoice()) { + throw new LocalizedException(new Phrase('Order already paid!')); + } + + $client = $this->client->initialize(); + $invoice = $client->getInvoice($invoiceId); + $invoiceStatus = $invoice->getStatus(); + if ($invoiceStatus === 'paid' || $invoiceStatus === 'confirmed' || $invoiceStatus === 'complete') { + throw new LocalizedException(new Phrase('The invoice has already been paid!')); + } elseif ($invoiceStatus === 'expired') { + throw new LocalizedException(new Phrase('The invoice has expired!')); + } elseif ($invoiceStatus !== 'new') { + throw new LocalizedException(new Phrase('The invoice is invalid or expired!')); + } + + $this->checkoutSession->setLastSuccessQuoteId($order->getQuoteId()) + ->setLastQuoteId($order->getQuoteId()) + ->setLastOrderId($order->getEntityId()); + + return $this->resultRedirectFactory->create()->setUrl($invoice->getUrl()); + } catch (\Exception $exception) { + $this->messageManager->addErrorMessage($exception->getMessage()); + + return $this->resultRedirectFactory->create()->setPath('checkout/cart'); + } catch (\Error $error) { + $this->messageManager->addErrorMessage('Invalid request!'); + + return $this->resultRedirectFactory->create()->setPath('checkout/cart'); + } + } +} diff --git a/Helper/ReturnHash.php b/Helper/ReturnHash.php new file mode 100644 index 0000000..70c8de8 --- /dev/null +++ b/Helper/ReturnHash.php @@ -0,0 +1,55 @@ +encryptor = $encryptor; + + parent::__construct($context); + } + + /** + * Generates return hash + * + * @param OrderInterface $order + * @return string + */ + public function generate(OrderInterface $order): string + { + return $this->encryptor->hash( + "{$order->getIncrementId()}:{$order->getCustomerEmail()}:{$order->getProtectCode()}" + ); + } + + /** + * Checks if returnHash is valid + * + * @param string $returnHashToCheck + * @param OrderInterface $order + * @return bool + */ + public function isValid(string $returnHashToCheck, OrderInterface $order): bool + { + return $returnHashToCheck === $this->generate($order); + } +} diff --git a/Model/BPRedirect.php b/Model/BPRedirect.php index 0eeb638..337c141 100755 --- a/Model/BPRedirect.php +++ b/Model/BPRedirect.php @@ -1,6 +1,7 @@ checkoutSession = $checkoutSession; $this->orderInterface = $orderInterface; @@ -83,7 +84,7 @@ public function __construct( $this->client = $client; $this->orderRepository = $orderRepository; $this->bitpayInvoiceRepository = $bitpayInvoiceRepository; - $this->encryptor = $encryptor; + $this->returnHashHelper = $returnHashHelper; } /** @@ -114,9 +115,10 @@ public function execute(ResultInterface $defaultResult, string $returnId = null) } $isStandardCheckoutSuccess = $this->config->getBitpayCheckoutSuccess() === 'standard'; - $returnHash = $this->encryptor->hash("$incrementId:{$order->getCustomerEmail()}:{$order->getProtectCode()}"); + if ($isStandardCheckoutSuccess && $returnId) { - if ($returnId !== $returnHash) { + $returnHash = $this->returnHashHelper->generate($order); + if (!$this->returnHashHelper->isValid($returnId, $order)) { $this->checkoutSession->clearHelperData(); return $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT) @@ -127,6 +129,7 @@ public function execute(ResultInterface $defaultResult, string $returnId = null) } try { + $returnHash = $this->returnHashHelper->generate($order); $baseUrl = $this->config->getBaseUrl(); $order = $this->setToPendingAndOverrideMagentoStatus($order); $modal = $this->config->getBitpayUx() === 'modal'; diff --git a/Model/IpnManagement.php b/Model/IpnManagement.php index 742b74d..f38a60a 100755 --- a/Model/IpnManagement.php +++ b/Model/IpnManagement.php @@ -5,6 +5,7 @@ use Bitpay\BPCheckout\Api\IpnManagementInterface; use Bitpay\BPCheckout\Exception\IPNValidationException; +use Bitpay\BPCheckout\Helper\ReturnHash; use Bitpay\BPCheckout\Logger\Logger; use Bitpay\BPCheckout\Model\Ipn\BPCItem; use Bitpay\BPCheckout\Model\Ipn\Validator; @@ -41,6 +42,7 @@ class IpnManagement implements IpnManagementInterface protected Request $request; protected Client $client; protected Response $response; + protected ReturnHash $returnHashHelper; /** * @param ResponseFactory $responseFactory @@ -57,6 +59,7 @@ class IpnManagement implements IpnManagementInterface * @param Request $request * @param Client $client * @param Response $response + * @param ReturnHash $returnHashHelper * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ public function __construct( @@ -73,7 +76,8 @@ public function __construct( Invoice $invoice, Request $request, Client $client, - Response $response + Response $response, + ReturnHash $returnHashHelper ) { $this->coreRegistry = $registry; $this->responseFactory = $responseFactory; @@ -89,6 +93,7 @@ public function __construct( $this->request = $request; $this->client = $client; $this->response = $response; + $this->returnHashHelper = $returnHashHelper; } /** @@ -104,21 +109,30 @@ public function postClose() try { $orderID = $this->request->getParam('orderID', null); $order = $this->orderInterface->loadByIncrementId($orderID); - $orderData = $order->getData(); - $quoteID = $orderData['quote_id']; - $quote = $this->quoteFactory->create()->loadByIdWithoutStore($quoteID); - if ($quote->getId()) { - $quote->setIsActive(1)->setReservedOrderId(null)->save(); - $this->checkoutSession->replaceQuote($quote); - $invoiceCloseHandling = $this->config->getBitpayInvoiceCloseHandling(); - if ($invoiceCloseHandling !== 'keep_order') { - $this->coreRegistry->register('isSecureArea', 'true'); - $order->delete(); - $this->coreRegistry->unregister('isSecureArea'); + $invoiceCloseHandling = $this->config->getBitpayInvoiceCloseHandling(); + if ($this->config->getBitpayCheckoutSuccess() === 'standard' && $invoiceCloseHandling === 'keep_order') { + $this->checkoutSession->setLastSuccessQuoteId($order->getQuoteId()) + ->setLastQuoteId($order->getQuoteId()) + ->setLastOrderId($order->getEntityId()); + + $returnHash = $this->returnHashHelper->generate($order); + $redirectUrl = $this->url->getUrl( + 'checkout/onepage/success', + ['_query' => ['return_id' => $returnHash]] + ); + } else { + $orderData = $order->getData(); + $quoteID = $orderData['quote_id']; + $quote = $this->quoteFactory->create()->loadByIdWithoutStore($quoteID); + if ($quote->getId()) { + $quote->setIsActive(1)->setReservedOrderId(null)->save(); + $this->checkoutSession->replaceQuote($quote); + if ($invoiceCloseHandling !== 'keep_order') { + $this->coreRegistry->register('isSecureArea', 'true'); + $order->delete(); + $this->coreRegistry->unregister('isSecureArea'); + } } - $response->setRedirect($redirectUrl)->sendResponse(); - - return; } $response->setRedirect($redirectUrl)->sendResponse(); diff --git a/Model/ResourceModel/Transaction.php b/Model/ResourceModel/Transaction.php index 15c318b..6d9486b 100755 --- a/Model/ResourceModel/Transaction.php +++ b/Model/ResourceModel/Transaction.php @@ -63,6 +63,32 @@ public function findBy(string $orderId, string $orderInvoiceId): ?array return $row; } + /** + * Find transaction by order_id and transaction_status + * + * @param string $orderId + * @param string $transactionStatus + * @return array|null + */ + public function findByOrderIdAndTransactionStatus(string $orderId, string $transactionStatus): ?array + { + $connection = $this->getConnection(); + $tableName = $connection->getTableName(self::TABLE_NAME); + + $sql = $connection->select() + ->from($tableName) + ->where('order_id = ?', $orderId) + ->where('transaction_status = ?', $transactionStatus); + + $row = $connection->fetchAll($sql); + + if (!$row) { + return null; + } + + return $row; + } + /** * Update transaction * diff --git a/Model/TransactionRepository.php b/Model/TransactionRepository.php index 760c59b..83d3e27 100755 --- a/Model/TransactionRepository.php +++ b/Model/TransactionRepository.php @@ -40,6 +40,18 @@ public function findBy(string $orderId, string $orderInvoiceId): ?array return $this->resourceTransaction->findBy($orderId, $orderInvoiceId); } + /** + * Find Transactions by order_id and transaction_status + * + * @param string $orderId + * @param string $transactionStatus + * @return array|null + */ + public function findByOrderIdAndTransactionStatus(string $orderId, string $transactionStatus): ?array + { + return $this->resourceTransaction->findByOrderIdAndTransactionStatus($orderId, $transactionStatus); + } + /** * Update Transaction * diff --git a/Test/Integration/Model/BPRedirectTest.php b/Test/Integration/Model/BPRedirectTest.php index b8212f9..c2fb30d 100755 --- a/Test/Integration/Model/BPRedirectTest.php +++ b/Test/Integration/Model/BPRedirectTest.php @@ -3,6 +3,7 @@ namespace Bitpay\BPCheckout\Test\Integration\Model; +use Bitpay\BPCheckout\Helper\ReturnHash; use Bitpay\BPCheckout\Model\BitpayInvoiceRepository; use Bitpay\BPCheckout\Model\BPRedirect; use Bitpay\BPCheckout\Model\Client; @@ -97,8 +98,9 @@ class BPRedirectTest extends TestCase * @var ResultFactory $resultFactory */ private $resultFactory; + /** - * @var Client $client + * @var Client|MockObject $client */ private $client; @@ -112,6 +114,11 @@ class BPRedirectTest extends TestCase */ private $bitpayInvoiceRepository; + /** + * @var ReturnHash $returnHash + */ + private $returnHash; + public function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); @@ -128,6 +135,7 @@ public function setUp(): void $this->client = $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock(); $this->orderRepository = $this->objectManager->get(OrderRepository::class); $this->bitpayInvoiceRepository = $this->objectManager->get(BitpayInvoiceRepository::class); + $this->returnHash = $this->objectManager->get(ReturnHash::class); $this->bpRedirect = new BPRedirect( $this->checkoutSession, @@ -142,7 +150,8 @@ public function setUp(): void $this->resultFactory, $this->client, $this->orderRepository, - $this->bitpayInvoiceRepository + $this->bitpayInvoiceRepository, + $this->returnHash ); } @@ -178,7 +187,8 @@ public function testExecute(): void $this->invoice->expects($this->once())->method('BPCCreateInvoice') ->willReturn($invoice); - $this->bpRedirect->execute(); + $defaultResult = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_PAGE); + $this->bpRedirect->execute($defaultResult); $customerInfo = $this->checkoutSession->getCustomerInfo(); $this->assertEquals('customer@example.com', $customerInfo['email']); @@ -218,8 +228,10 @@ public function testExecuteException(): void $this->invoice->expects($this->once())->method('BPCCreateInvoice') ->willThrowException(new LocalizedException(new Phrase('Invalid token'))); + + $defaultResult = $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_PAGE); - $this->bpRedirect->execute(); + $this->bpRedirect->execute($defaultResult); $this->assertEquals( 'We are unable to place your Order at this time', $this->messageManager->getMessages()->getLastAddedMessage()->getText() diff --git a/Test/Integration/Model/IpnManagementTest.php b/Test/Integration/Model/IpnManagementTest.php index 22f7696..155bb90 100644 --- a/Test/Integration/Model/IpnManagementTest.php +++ b/Test/Integration/Model/IpnManagementTest.php @@ -9,9 +9,8 @@ use Bitpay\BPCheckout\Model\TransactionRepository; use BitPaySDK\Model\Invoice\Buyer; use Magento\Framework\ObjectManagerInterface; -use Bitpay\BPCheckout\Api\IpnManagementInterface; +use Bitpay\BPCheckout\Helper\ReturnHash; use Bitpay\BPCheckout\Logger\Logger; -use Bitpay\BPCheckout\Model\Ipn\BPCItem; use Magento\Checkout\Model\Session; use Magento\Framework\App\ResponseFactory; use Magento\Framework\DataObject; @@ -42,7 +41,7 @@ class IpnManagementTest extends TestCase private $responseFactory; /** - * @var OrderInterface $url + * @var UrlInterface $url */ private $url; @@ -87,7 +86,7 @@ class IpnManagementTest extends TestCase private $transactionRepository; /** - * @var Invoice|\PHPUnit\Framework\MockObject\MockObject $invoice + * @var Invoice|MockObject $invoice */ private $invoice; @@ -111,6 +110,11 @@ class IpnManagementTest extends TestCase */ private $response; + /** + * @var ReturnHash $returnHash + */ + private ReturnHash $returnHash; + public function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); @@ -128,6 +132,8 @@ public function setUp(): void $this->request = $this->objectManager->get(Request::class); $this->client = $this->getMockBuilder(Client::class)->disableOriginalConstructor()->getMock(); $this->response = $this->objectManager->get(Response::class); + $this->returnHash = $this->objectManager->get(ReturnHash::class); + $this->ipnManagement = new IpnManagement( $this->responseFactory, $this->url, @@ -142,7 +148,8 @@ public function setUp(): void $this->invoice, $this->request, $this->client, - $this->response + $this->response, + $this->returnHash ); } diff --git a/Test/Integration/Model/ResourceModel/TransactionTest.php b/Test/Integration/Model/ResourceModel/TransactionTest.php index 60e2250..66c73b4 100755 --- a/Test/Integration/Model/ResourceModel/TransactionTest.php +++ b/Test/Integration/Model/ResourceModel/TransactionTest.php @@ -20,6 +20,11 @@ class TransactionTest extends TestCase */ private $transactionResource; + /** + * @var Context $context + */ + private $context; + public function setUp(): void { $this->objectManager = Bootstrap::getObjectManager(); diff --git a/Test/Unit/Model/BPRedirectTest.php b/Test/Unit/Model/BPRedirectTest.php index fa99cbe..196e597 100755 --- a/Test/Unit/Model/BPRedirectTest.php +++ b/Test/Unit/Model/BPRedirectTest.php @@ -3,6 +3,7 @@ namespace Bitpay\BPCheckout\Test\Unit\Model; +use Bitpay\BPCheckout\Helper\ReturnHash; use Bitpay\BPCheckout\Model\BitpayInvoiceRepository; use Bitpay\BPCheckout\Model\BPRedirect; use Bitpay\BPCheckout\Logger\Logger; @@ -16,11 +17,8 @@ use Magento\Framework\Message\Manager; use Magento\Framework\Registry; use Magento\Framework\UrlInterface; -use Magento\Quote\Api\Data\PaymentInterface; -use Magento\Sales\Api\Data\OrderInterface; 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; @@ -39,7 +37,7 @@ class BPRedirectTest extends TestCase private $bpRedirect; /** - * @var Seesion|MockObject $checkoutSession + * @var Session|MockObject $checkoutSession */ private $checkoutSession; @@ -64,12 +62,12 @@ class BPRedirectTest extends TestCase private $client; /** - * @var OrderRepository $orderRepository + * @var OrderRepository|MockObject $orderRepository */ private $orderRepository; /** - * @var BitpayInvoiceRepository $bitpayInvoiceRepository + * @var BitpayInvoiceRepository|MockObject $bitpayInvoiceRepository */ private $bitpayInvoiceRepository; @@ -104,9 +102,9 @@ class BPRedirectTest extends TestCase private $resultFactory; /** - * @var EncryptorInterface|MockObject $encryptor + * @var ReturnHash|MockObject $returnHash */ - private $encryptor; + private $returnHash; public function setUp(): void { @@ -123,7 +121,7 @@ public function setUp(): void $this->resultFactory = $this->getMock(ResultFactory::class); $this->orderRepository = $this->getMock(OrderRepository::class); $this->bitpayInvoiceRepository = $this->getMock(BitpayInvoiceRepository::class); - $this->encryptor = $this->getMock(EncryptorInterface::class); + $this->returnHash = $this->getMock(ReturnHash::class); $this->bpRedirect = $this->getClass(); } @@ -155,7 +153,10 @@ public function testExecute($ux): void 'getUrl' ) ->withConsecutive(['bitpay-invoice', ['_query' => ['order_id' => $incrementId]]], ['checkout/cart']) - ->willReturnOnConsecutiveCalls('http://localhost/bitpay-invoice?order_id=' . $incrementId, 'http://localhost/checkout/cart'); + ->willReturnOnConsecutiveCalls( + 'http://localhost/bitpay-invoice?order_id=' . $incrementId, + 'http://localhost/checkout/cart' + ); $billingAddress->expects($this->once())->method('getData') ->willReturn(['first_name' => 'test', 'last_name' => 'test1']); @@ -186,6 +187,9 @@ public function testExecute($ux): void $this->resultFactory->expects($this->once())->method('create')->willReturn($result); } + /** + * @var \Magento\Framework\View\Result\Page + */ $page = $this->getMock(\Magento\Framework\View\Result\Page::class); $this->bpRedirect->execute($page); @@ -203,7 +207,6 @@ public function getUxDataProvider(): array public function testExecuteNoOrderId(): void { - $response = $this->getMock(\Magento\Framework\HTTP\PhpEnvironment\Response::class); $this->checkoutSession->expects($this->once()) ->method('getData') ->with('last_order_id') @@ -212,6 +215,9 @@ public function testExecuteNoOrderId(): void $result->expects($this->once())->method('setUrl')->willReturnSelf(); $this->resultFactory->expects($this->once())->method('create')->willReturn($result); + /** + * @var \Magento\Framework\View\Result\Page + */ $page = $this->getMock(\Magento\Framework\View\Result\Page::class); $this->bpRedirect->execute($page); @@ -237,6 +243,9 @@ public function testExecuteNoBitpayPaymentMethod(): void $order->expects($this->once())->method('getPayment')->willReturn($payment); $this->order->expects($this->once())->method('load')->with($lastOrderId)->willReturn($order); + /** + * @var \Magento\Framework\View\Result\Page + */ $page = $this->getMock(\Magento\Framework\View\Result\Page::class); $this->assertSame($page, $this->bpRedirect->execute($page)); @@ -268,7 +277,10 @@ public function testExecuteException($exceptionType): void 'getUrl' ) ->withConsecutive(['bitpay-invoice', ['_query' => ['order_id' => $incrementId]]], ['checkout/cart']) - ->willReturnOnConsecutiveCalls('http://localhost/bitpay-invoice?order_id=' . $incrementId, 'http://localhost/checkout/cart'); + ->willReturnOnConsecutiveCalls( + 'http://localhost/bitpay-invoice?order_id=' . $incrementId, + 'http://localhost/checkout/cart' + ); $billingAddress->expects($this->once())->method('getData') ->willReturn(['first_name' => 'test', 'last_name' => 'test1']); @@ -288,6 +300,9 @@ public function testExecuteException($exceptionType): void ->method('BPCCreateInvoice') ->willThrowException(new $exceptionType('something went wrong')); + /** + * @var \Magento\Framework\View\Result\Page + */ $page = $this->getMock(\Magento\Framework\View\Result\Page::class); $this->bpRedirect->execute($page); @@ -368,7 +383,7 @@ private function getClass(): BPRedirect $this->client, $this->orderRepository, $this->bitpayInvoiceRepository, - $this->encryptor + $this->returnHash ); } diff --git a/Test/Unit/Model/IpnManagementTest.php b/Test/Unit/Model/IpnManagementTest.php index c43082b..f1dcf68 100644 --- a/Test/Unit/Model/IpnManagementTest.php +++ b/Test/Unit/Model/IpnManagementTest.php @@ -3,21 +3,16 @@ namespace Bitpay\BPCheckout\Test\Unit\Model; -use Bitpay\BPCheckout\Exception\IPNValidationException; use Bitpay\BPCheckout\Model\Client; use Bitpay\BPCheckout\Model\Config; use Bitpay\BPCheckout\Model\Invoice; use Bitpay\BPCheckout\Model\IpnManagement; -use Bitpay\BPCheckout\Api\IpnManagementInterface; +use Bitpay\BPCheckout\Helper\ReturnHash; use Bitpay\BPCheckout\Logger\Logger; -use Bitpay\BPCheckout\Model\Ipn\BPCItem; use Bitpay\BPCheckout\Model\TransactionRepository; use BitPaySDK\Model\Invoice\Buyer; -use Hoa\Iterator\Mock; use Magento\Checkout\Model\Session; use Magento\Framework\App\ResponseFactory; -use Magento\Framework\App\Response; -use Magento\Framework\DataObject; use Magento\Framework\Registry; use Magento\Framework\Serialize\Serializer\Json; use Magento\Framework\UrlInterface; @@ -109,6 +104,11 @@ class IpnManagementTest extends TestCase */ private $response; + /** + * @var ReturnHash|MockObject + */ + private $returnHashHelper; + public function setUp(): void { $this->coreRegistry = $this->getMock(Registry::class); @@ -125,6 +125,7 @@ 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->returnHashHelper = $this->getMock(ReturnHash::class); $this->ipnManagement = $this->getClass(); } @@ -160,9 +161,7 @@ public function testPostCloseKeepOrder(): void { $this->config->expects($this->once())->method('getBitpayInvoiceCloseHandling')->willReturn('keep_order'); - $quoteId = 21; $cartUrl = 'http://localhost/checkout/cart?reload=1'; - $quote = $this->getMock(Quote::class); $response = $this->getMock(\Magento\Framework\HTTP\PhpEnvironment\Response::class); $order = $this->getMock(Order::class); $orderId = '000000012'; @@ -170,15 +169,15 @@ public function testPostCloseKeepOrder(): void $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); - $quote->expects($this->once())->method('loadByIdWithoutStore')->willReturnSelf(); - $quote->expects($this->once())->method('getId')->willReturn($quoteId); - $quote->expects($this->once())->method('setIsActive')->willReturnSelf(); - $quote->expects($this->once())->method('setReservedOrderId')->willReturnSelf(); - - $this->quoteFactory->expects($this->once())->method('create')->willReturn($quote); + $this->checkoutSession + ->method('__call') + ->willReturnCallback(fn($operation) => match ([$operation]) { + ['setLastSuccessQuoteId'] => $this->checkoutSession, + ['setLastQuoteId'] => $this->checkoutSession, + ['setLastOrderId'] => $this->checkoutSession + }); $response->expects($this->once())->method('setRedirect')->willReturnSelf(); $order->expects($this->never())->method('delete')->willReturnSelf(); @@ -428,7 +427,8 @@ private function getClass(): IpnManagement $this->invoice, $this->request, $this->client, - $this->response + $this->response, + $this->returnHashHelper ); } diff --git a/Test/Unit/Model/ResourceModel/TransactionTest.php b/Test/Unit/Model/ResourceModel/TransactionTest.php index 59079dd..6525cf9 100755 --- a/Test/Unit/Model/ResourceModel/TransactionTest.php +++ b/Test/Unit/Model/ResourceModel/TransactionTest.php @@ -23,10 +23,15 @@ class TransactionTest extends TestCase */ private $adapter; + /** + * @var Context|MockObject $context + */ + private $context; + public function setUp(): void { $this->prepareContext(); - $this->transaction = new Transaction($this->contex); + $this->transaction = new Transaction($this->context); } public function testAdd(): void @@ -94,8 +99,8 @@ private function prepareContext(): void $resourceConnection = $this->getMockBuilder(ResourceConnection::class)->disableOriginalConstructor()->getMock(); $this->adapter = $this->getMockBuilder(AdapterInterface::class)->disableOriginalConstructor()->getMock(); $resourceConnection->expects($this->once())->method('getConnection')->willReturn($this->adapter); - $this->contex = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); - $this->contex->expects($this->once())->method('getResources')->willReturn($resourceConnection); + $this->context = $this->getMockBuilder(Context::class)->disableOriginalConstructor()->getMock(); + $this->context->expects($this->once())->method('getResources')->willReturn($resourceConnection); } private function prepareTableName(): void diff --git a/view/frontend/layout/checkout_onepage_success.xml b/view/frontend/layout/checkout_onepage_success.xml new file mode 100644 index 0000000..1fd881b --- /dev/null +++ b/view/frontend/layout/checkout_onepage_success.xml @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/view/frontend/templates/checkout/order/success/pay-button.phtml b/view/frontend/templates/checkout/order/success/pay-button.phtml new file mode 100644 index 0000000..f71cb72 --- /dev/null +++ b/view/frontend/templates/checkout/order/success/pay-button.phtml @@ -0,0 +1,9 @@ + +getOrderId() && $block->canViewPayButton()) :?> +
+
+ + <?= $block->escapeHtml(__('Pay with BitPay')) ?> + +
+ diff --git a/view/frontend/web/images/Pay-with-BitPay-CardGroup.svg b/view/frontend/web/images/Pay-with-BitPay-CardGroup.svg index 4313224..590c6e3 100644 --- a/view/frontend/web/images/Pay-with-BitPay-CardGroup.svg +++ b/view/frontend/web/images/Pay-with-BitPay-CardGroup.svg @@ -1,7 +1,7 @@ - + @@ -14,153 +14,141 @@ - - - - - - - - - - - - - - - + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -169,10 +157,10 @@ - + - +