It’s a common problem: you want to style, template or test the order success page, but each time you refresh the page Magento 2 redirects you to the cart page. This is the default behaviour of Magento 1 & 2: when you’re order is complete, you’re only allowed to see this page once. Refreshing the page means that you’re redirected away from it. This makes it quite hard to style or test this page without having to create fake orders each time.
There is a method to disable this redirect. There are various article on how to do this for Magento 1, but I haven’t found a simple one for Magento 2. So I decided to write one myself.
Let’s take a peek under the hood
If we look at the controller that’s responsible for rendering the success page (Magento\Checkout\Controller\Onepage\Success ), we see a very interesting line:
1 2 3 |
if (!$this->_objectManager->get('Magento\Checkout\Model\Session\SuccessValidator')->isValid()) { return $this->resultRedirectFactory->create()->setPath('checkout/cart'); } |
This line is the line that causes the redirect. The Magento\Checkout\Model\Session\SuccessValidator::isValid() -method returns either true or false , and if it returns false we get our redirect. So let’s just take a look at this function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * @return bool */ public function isValid() { if (!$this->checkoutSession->getLastSuccessQuoteId()) { return false; } if (!$this->checkoutSession->getLastQuoteId() || !$this->checkoutSession->getLastOrderId()) { return false; } return true; } |
Now this is something we can work with! Why? Because it’s a public method! And we can make use of Magento 2’s Interceptors (plugins) to modify the output of this function.
The plugin
The first thing we need to do, is add (or edit) our modules’ file etc/frontend/di.xml :
1 2 3 4 5 6 7 8 9 10 11 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <!-- Plugin to make it possible to style/test order success page: --> <type name="Magento\Checkout\Model\Session\SuccessValidator"> <plugin name="plugin_checkout_successvalidator" type="Vendor\Module\Plugin\Magento\Checkout\Model\Session\SuccessValidator"/> </type> </config> |
Secondly, we need to create our plugin file Plugin\Magento\Checkout\Model\Session\SuccessValidator.php :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
<?php namespace Vendor\Module\Plugin\Magento\Checkout\Model\Session; use Magento\Framework\Api\Search\SearchCriteriaFactory; use Magento\Sales\Model\Order; use Magento\Sales\Model\OrderRepository; use Magento\Sales\Model\ResourceModel\Order\Collection; /** * Class SuccessValidator * @package Vendor\Module\Plugin\Magento\Checkout\Model\Session */ class SuccessValidator { /** * @var \Magento\Checkout\Model\Session */ protected $checkoutSession; /** * @var \Magento\Sales\Model\ResourceModel\Order\CollectionFactory */ protected $orderCollectionFactory; /** * SuccessValidator constructor. * @param OrderRepository $orderRepository * @param \Magento\Checkout\Model\Session $checkoutSession */ public function __construct( \Magento\Sales\Model\ResourceModel\Order\CollectionFactory $orderCollectionFactory, \Magento\Checkout\Model\Session $checkoutSession ) { $this->checkoutSession = $checkoutSession; $this->orderCollectionFactory = $orderCollectionFactory; } /** * @param \Magento\Checkout\Model\Session\SuccessValidator $successValidator * @param boolean $returnValue * @return boolean */ public function afterIsValid(\Magento\Checkout\Model\Session\SuccessValidator $successValidator, $returnValue) { /** @var Order $order */ $order = $this->orderCollectionFactory->create() ->setPageSize(1) ->setOrder('entity_id', 'DESC') ->addFieldToFilter('status', ['eq' => 'complete']) ->getFirstItem(); if ($order->getId()) { $this->checkoutSession->setLastOrderId($order->getId()); $this->checkoutSession->setLastQuoteId($order->getQuoteId()); $this->checkoutSession->setLastSuccessQuoteId($order->getQuoteId()); return true; } return $returnValue; } } |
There are some interesting things to note from this script.
- First of all, we need to be able to communicate with the checkout session, so we create a protected variable called $checkoutSession .
- Secondly we want our page to just show the latest completed order. For that purpose we create an order collection and just filter and sort it so we get our last completed order.
- And last but not least, when an order is found, we override the parameters of our checkout session so that Magento’s native code is fooled by our plugin.
Voila! You can now refresh the order success page as much as you want.
Improvements
Needless to say, this script is purely used for development / styling / testing purposes because it has one obvious flaw: it will always load an order from the database and will never use it’s native functionality. To fix this flaw you need to wrap everything in afterIsValid() , except the last line, so you can execute the code when some requirements are met. For example, if you only want it to work when you add ‘?test’ to the URL:
1 2 3 4 5 6 7 8 |
public function afterIsValid(\Magento\Checkout\Model\Session\SuccessValidator $successValidator, $returnValue) { if (isset($_GET['test'])) { // ... the logic as described above ... } return $returnValue; } |
Or if you want to make use of system configuration:
1 2 3 4 5 6 7 8 9 |
public function afterIsValid(\Magento\Checkout\Model\Session\SuccessValidator $successValidator, $returnValue) { // Assuming you know how to create a function that loads something from the configuration: if ($this->getConfigValue('path/to/config')) { // ... the logic as described above ... } return $returnValue; } |
Well, I think you’re smart enough to figure it out! I hope this article helps you.
Visitors give this article an average rating of 4.2 out of 5.
How would you rate this article?
★ ★ ★ ★ ★
Just what I needed to win the game!
Thanks,
Nice article this help me a lot.
Hello. This code does not work when there are no one order.
Thanks for all this useful information!
We used various methods, however, the easiest way to have the checkout page customized is to use a free or paid extension. Hopefully there are plenty of companies who offer those.
For Magento 2 we used Checkout Success Page module by Plumrocket. Now styling checkout success page is a piece of cake for us!
hey, how to override the layout for checkout success page?
Why not just comment out the line in Magento\Checkout\Controller\Onepage\Success ($session->clearQuote(); ) and when you’re done, revert it back?