Skip to content

Commit f9e17bd

Browse files
committed
Merge remote-tracking branch 'upstream/10.0.x' into 10.0.x
2 parents 1421e86 + 6e518c4 commit f9e17bd

File tree

21 files changed

+751
-174
lines changed

21 files changed

+751
-174
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Bitpay\BPCheckout\Block\Checkout\Onepage\Success;
5+
6+
use Bitpay\BPCheckout\Model\Client;
7+
use Bitpay\BPCheckout\Model\Config;
8+
use Bitpay\BPCheckout\Model\TransactionRepository;
9+
use BitPaySDK\Model\Invoice\Invoice;
10+
use Magento\Checkout\Block\Onepage\Success as MagentoSuccess;
11+
use Magento\Checkout\Model\Session;
12+
use Magento\Sales\Model\Order;
13+
use Magento\Sales\Model\Order\Config as OrderConfig;
14+
use Magento\Framework\App\Http\Context as HttpContext;
15+
use Magento\Framework\UrlInterface;
16+
use Magento\Framework\View\Element\Template\Context;
17+
18+
class PayButton extends MagentoSuccess
19+
{
20+
/**
21+
* @var TransactionRepository
22+
*/
23+
protected TransactionRepository $transactionRepository;
24+
25+
/**
26+
* @var Client
27+
*/
28+
protected Client $client;
29+
30+
/**
31+
* @var Config
32+
*/
33+
protected Config $config;
34+
35+
/**
36+
* @var UrlInterface
37+
*/
38+
protected UrlInterface $url;
39+
40+
/**
41+
* @var Invoice|null
42+
*/
43+
protected ?Invoice $invoice = null;
44+
45+
/**
46+
* @param Context $context
47+
* @param Session $checkoutSession
48+
* @param OrderConfig $orderConfig
49+
* @param HttpContext $httpContext
50+
* @param TransactionRepository $transactionRepository
51+
* @param Client $client
52+
* @param Config $config
53+
* @param UrlInterface $url
54+
* @param array $data
55+
*/
56+
public function __construct(
57+
Context $context,
58+
Session $checkoutSession,
59+
OrderConfig $orderConfig,
60+
HttpContext $httpContext,
61+
TransactionRepository $transactionRepository,
62+
Client $client,
63+
Config $config,
64+
UrlInterface $url,
65+
array $data = []
66+
) {
67+
parent::__construct($context, $checkoutSession, $orderConfig, $httpContext, $data);
68+
69+
$this->transactionRepository = $transactionRepository;
70+
$this->client = $client;
71+
$this->config = $config;
72+
$this->url = $url;
73+
}
74+
75+
/**
76+
* Returns true when Pay button be displayed
77+
*
78+
* @return bool
79+
*/
80+
public function canViewPayButton(): bool
81+
{
82+
if ($this->config->getBitpayCheckoutSuccess() === 'standard'
83+
&& $this->config->getBitpayInvoiceCloseHandling() === 'keep_order') {
84+
$invoice = $this->getBitpayInvoice();
85+
86+
return $invoice !== null;
87+
}
88+
89+
return false;
90+
}
91+
92+
/**
93+
* Returns button url
94+
*
95+
* @return string
96+
*/
97+
public function getButtonUrl(): string
98+
{
99+
return $this->url->getUrl('bpcheckout/invoice/pay', [
100+
'_query' => [
101+
'order_id' => $this->getOrder()->getId(), 'invoice_id' => $this->getBitpayInvoice()->getId()
102+
]
103+
]);
104+
}
105+
106+
/**
107+
* Get BitPay invoice by last order
108+
*
109+
* @return Invoice|null
110+
*/
111+
protected function getBitpayInvoice(): ?Invoice
112+
{
113+
if (!$this->invoice) {
114+
$order = $this->getOrder();
115+
if ($order->canInvoice()) {
116+
$transactions = $this->transactionRepository
117+
->findByOrderIdAndTransactionStatus($order->getIncrementId(), 'new');
118+
if (!empty($transactions)) {
119+
$lastTransaction = array_pop($transactions);
120+
$client = $this->client->initialize();
121+
$invoice = $client->getInvoice($lastTransaction['transaction_id']);
122+
123+
$this->invoice = $invoice;
124+
}
125+
}
126+
}
127+
128+
return $this->invoice;
129+
}
130+
131+
/**
132+
* Get order instance based on last order ID
133+
*
134+
* @return Order
135+
*/
136+
protected function getOrder(): Order
137+
{
138+
return $this->_checkoutSession->getLastRealOrder();
139+
}
140+
}

Controller/Invoice/Pay.php

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Bitpay\BPCheckout\Controller\Invoice;
5+
6+
use Bitpay\BPCheckout\Model\Client;
7+
use Bitpay\BPCheckout\Model\Config;
8+
use Bitpay\BPCheckout\Model\TransactionRepository;
9+
use Magento\Checkout\Model\Session;
10+
use Magento\Framework\App\Action\HttpGetActionInterface;
11+
use Magento\Framework\App\RequestInterface;
12+
use Magento\Framework\Controller\Result\RedirectFactory;
13+
use Magento\Framework\Controller\ResultInterface;
14+
use Magento\Framework\Exception\LocalizedException;
15+
use Magento\Framework\Message\Manager;
16+
use Magento\Framework\Phrase;
17+
use Magento\Sales\Api\OrderRepositoryInterface;
18+
19+
class Pay implements HttpGetActionInterface
20+
{
21+
/**
22+
* @var RequestInterface
23+
*/
24+
private $request;
25+
26+
/**
27+
* @var Manager
28+
*/
29+
protected Manager $messageManager;
30+
31+
/**
32+
* @var RedirectFactory
33+
*/
34+
protected $resultRedirectFactory;
35+
36+
/**
37+
* @var OrderRepositoryInterface
38+
*/
39+
protected $orderRepository;
40+
41+
/**
42+
* @var TransactionRepository
43+
*/
44+
protected TransactionRepository $transactionRepository;
45+
46+
/**
47+
* @var Session
48+
*/
49+
protected Session $checkoutSession;
50+
51+
/**
52+
* @var Client
53+
*/
54+
protected Client $client;
55+
56+
/**
57+
* @var Config
58+
*/
59+
protected Config $config;
60+
61+
/**
62+
* @param RequestInterface $request
63+
* @param Manager $messageManager
64+
* @param RedirectFactory $resultRedirectFactory
65+
* @param OrderRepositoryInterface $orderRepository
66+
* @param TransactionRepository $transactionRepository
67+
* @param Session $checkoutSession
68+
* @param Client $client
69+
* @param Config $config
70+
*/
71+
public function __construct(
72+
RequestInterface $request,
73+
Manager $messageManager,
74+
RedirectFactory $resultRedirectFactory,
75+
OrderRepositoryInterface $orderRepository,
76+
TransactionRepository $transactionRepository,
77+
Session $checkoutSession,
78+
Client $client,
79+
Config $config,
80+
) {
81+
$this->request = $request;
82+
$this->messageManager = $messageManager;
83+
$this->resultRedirectFactory = $resultRedirectFactory;
84+
$this->orderRepository = $orderRepository;
85+
$this->transactionRepository = $transactionRepository;
86+
$this->checkoutSession = $checkoutSession;
87+
$this->client = $client;
88+
$this->config = $config;
89+
}
90+
91+
/**
92+
* Get checkout customer info
93+
*
94+
* @return ResultInterface
95+
*/
96+
public function execute()
97+
{
98+
$orderId = $this->request->getParam('order_id', null);
99+
$invoiceId = $this->request->getParam('invoice_id', null);
100+
101+
try {
102+
if (!$orderId || !$invoiceId || $this->config->getBitpayCheckoutSuccess() !== 'standard'
103+
|| $this->config->getBitpayInvoiceCloseHandling() !== 'keep_order') {
104+
throw new LocalizedException(new Phrase('Invalid request!'));
105+
}
106+
107+
/** @var \Magento\Sales\Model\Order $order */
108+
$order = $this->orderRepository->get($orderId);
109+
if (!$order->canInvoice()) {
110+
throw new LocalizedException(new Phrase('Order already paid!'));
111+
}
112+
113+
$client = $this->client->initialize();
114+
$invoice = $client->getInvoice($invoiceId);
115+
$invoiceStatus = $invoice->getStatus();
116+
if ($invoiceStatus === 'paid' || $invoiceStatus === 'confirmed' || $invoiceStatus === 'complete') {
117+
throw new LocalizedException(new Phrase('The invoice has already been paid!'));
118+
} elseif ($invoiceStatus === 'expired') {
119+
throw new LocalizedException(new Phrase('The invoice has expired!'));
120+
} elseif ($invoiceStatus !== 'new') {
121+
throw new LocalizedException(new Phrase('The invoice is invalid or expired!'));
122+
}
123+
124+
$this->checkoutSession->setLastSuccessQuoteId($order->getQuoteId())
125+
->setLastQuoteId($order->getQuoteId())
126+
->setLastOrderId($order->getEntityId());
127+
128+
return $this->resultRedirectFactory->create()->setUrl($invoice->getUrl());
129+
} catch (\Exception $exception) {
130+
$this->messageManager->addErrorMessage($exception->getMessage());
131+
132+
return $this->resultRedirectFactory->create()->setPath('checkout/cart');
133+
} catch (\Error $error) {
134+
$this->messageManager->addErrorMessage('Invalid request!');
135+
136+
return $this->resultRedirectFactory->create()->setPath('checkout/cart');
137+
}
138+
}
139+
}

Helper/ReturnHash.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
declare(strict_types=1);
3+
4+
namespace Bitpay\BPCheckout\Helper;
5+
6+
use Magento\Framework\App\Helper\Context;
7+
use Magento\Framework\App\Helper\AbstractHelper;
8+
use Magento\Framework\Encryption\EncryptorInterface;
9+
use Magento\Sales\Api\Data\OrderInterface;
10+
11+
class ReturnHash extends AbstractHelper
12+
{
13+
/**
14+
* @var EncryptorInterface
15+
*/
16+
protected EncryptorInterface $encryptor;
17+
18+
/**
19+
* @param Context $context
20+
* @param EncryptorInterface $encryptor
21+
*/
22+
public function __construct(
23+
Context $context,
24+
EncryptorInterface $encryptor
25+
) {
26+
$this->encryptor = $encryptor;
27+
28+
parent::__construct($context);
29+
}
30+
31+
/**
32+
* Generates return hash
33+
*
34+
* @param OrderInterface $order
35+
* @return string
36+
*/
37+
public function generate(OrderInterface $order): string
38+
{
39+
return $this->encryptor->hash(
40+
"{$order->getIncrementId()}:{$order->getCustomerEmail()}:{$order->getProtectCode()}"
41+
);
42+
}
43+
44+
/**
45+
* Checks if returnHash is valid
46+
*
47+
* @param string $returnHashToCheck
48+
* @param OrderInterface $order
49+
* @return bool
50+
*/
51+
public function isValid(string $returnHashToCheck, OrderInterface $order): bool
52+
{
53+
return $returnHashToCheck === $this->generate($order);
54+
}
55+
}

Model/BPRedirect.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22
namespace Bitpay\BPCheckout\Model;
33

4+
use Bitpay\BPCheckout\Helper\ReturnHash;
45
use Bitpay\BPCheckout\Logger\Logger;
56
use Magento\Checkout\Model\Session;
67
use Magento\Framework\DataObject;
@@ -35,6 +36,7 @@ class BPRedirect
3536
protected Client $client;
3637
protected OrderRepository $orderRepository;
3738
protected BitpayInvoiceRepository $bitpayInvoiceRepository;
39+
protected ReturnHash $returnHashHelper;
3840
protected EncryptorInterface $encryptor;
3941

4042
/**
@@ -51,6 +53,7 @@ class BPRedirect
5153
* @param Client $client
5254
* @param OrderRepository $orderRepository
5355
* @param BitpayInvoiceRepository $bitpayInvoiceRepository
56+
* @param ReturnHash $returnHashHelper
5457
* @param EncryptorInterface $encryptor
5558
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
5659
*/
@@ -68,6 +71,7 @@ public function __construct(
6871
Client $client,
6972
OrderRepository $orderRepository,
7073
BitpayInvoiceRepository $bitpayInvoiceRepository,
74+
ReturnHash $returnHashHelper,
7175
EncryptorInterface $encryptor,
7276
) {
7377
$this->checkoutSession = $checkoutSession;
@@ -83,6 +87,7 @@ public function __construct(
8387
$this->client = $client;
8488
$this->orderRepository = $orderRepository;
8589
$this->bitpayInvoiceRepository = $bitpayInvoiceRepository;
90+
$this->returnHashHelper = $returnHashHelper;
8691
$this->encryptor = $encryptor;
8792
}
8893

@@ -114,9 +119,10 @@ public function execute(ResultInterface $defaultResult, string $returnId = null)
114119
}
115120

116121
$isStandardCheckoutSuccess = $this->config->getBitpayCheckoutSuccess() === 'standard';
117-
$returnHash = $this->encryptor->hash("$incrementId:{$order->getCustomerEmail()}:{$order->getProtectCode()}");
122+
118123
if ($isStandardCheckoutSuccess && $returnId) {
119-
if ($returnId !== $returnHash) {
124+
$returnHash = $this->returnHashHelper->generate($order);
125+
if (!$this->returnHashHelper->isValid($returnId, $order)) {
120126
$this->checkoutSession->clearHelperData();
121127

122128
return $this->resultFactory->create(\Magento\Framework\Controller\ResultFactory::TYPE_REDIRECT)
@@ -127,6 +133,7 @@ public function execute(ResultInterface $defaultResult, string $returnId = null)
127133
}
128134

129135
try {
136+
$returnHash = $this->returnHashHelper->generate($order);
130137
$baseUrl = $this->config->getBaseUrl();
131138
$order = $this->setToPendingAndOverrideMagentoStatus($order);
132139
$redirectUrl = $this->url->getUrl('bitpay-invoice', ['_query' => ['order_id' => $incrementId]]);

0 commit comments

Comments
 (0)