Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override frontcontroller or modulefrontcontroller init() method for checkout

My problem is as follows. I have created a payment module for prestashop 1.7. The order is validated // created when the payment is initiated. This happens in the payment.php controller by using the validateOrder Method:

        $this->module->validateOrder(
                (int) $cartId,
                $this->module->statuses[$orderStatus],
                $prestaTotal,
                'paymentmodule',
                null,
                array(),
                null,
                false,
                $customer->secure_key
                );

So a new order is created and the cart is now related to an order. And the customer get's redirected to the payment provider. They can eigther pay, or press the "cancel" button.

The webhook.php recieves the status of the order from the payment provider and updates the order status in prestashop. If the order is paid, they get redirected to the order confirmation page. But if it's cancelled, the cart is gone.

This happens, because prestashop checks if the order exists. And if it does, the cart gets deleted. The init() method in FronController.php is responsible for this:

/* Cart already exists */
    if ((int) $this->context->cookie->id_cart) {
        if (!isset($cart)) {
            $cart = new Cart($this->context->cookie->id_cart);
        }

        if (Validate::isLoadedObject($cart) && $cart->OrderExists()) {
            PrestaShopLogger::addLog('Frontcontroller::init - Cart cannot be loaded or an order has already been placed using this cart', 1, null, 'Cart', (int) $this->context->cookie->id_cart, true);
            unset($this->context->cookie->id_cart, $cart, $this->context->cookie->checkedTOS);
            $this->context->cookie->check_cgv = false;
        }

So i created a Method in return.php controller to make a copy of the cart which belongs to an existing order, so you have a new cart:

class PaymentModuleFrontController extends ModuleFrontController
{

    public function initContent()
    {
        parent::initContent();
        $cartId = Tools::getValue('cart_id');
        $cart = new Cart((int) $cartId);
        $data['info'] = $this->module->getPaymentBy('cart_id', (int)$cartId);
                $orderId = Order::getOrderByCartId($cartId);
                $orderStatus = $data['info']['bank_status'];

                if (Validate::isLoadedObject($cart) && 
                $cart->OrderExists() && 
                $orderStatus === 'cancelled')
                {
                    $oldCart = new Cart(Order::getCartIdStatic($orderId, $this->context->customer->id));
                    $duplication = $oldCart->duplicate();
                    if (!$duplication || !Validate::isLoadedObject($duplication['cart'])) 
                    {
                        $this->errors[] = Tools::displayError('Sorry. We cannot renew your order.');
                    } 
                    elseif (!$duplication['success']) 
                    {
                        $this->errors[] = Tools::displayError('Some items are no longer available, and we are unable to renew your order.');
                    } 
                    else 
                    {
                        $this->context->cookie->id_cart = $duplication['cart']->id;
                        $context = $this->context;
                        $context->cart = $duplication['cart'];
                        CartRule::autoAddToCart($context);
                        $this->context->cookie->write();
                        if (Configuration::get('PS_ORDER_PROCESS_TYPE') == 1) 
                        {
                            Tools::redirect('index.php?controller=order-opc');
                        }
                    Tools::redirect('index.php?controller=order');
                    }   

                }
      }

A copy of the cart will be created they get redirected to the checkout.

So hooray, there cart is not gone (which normally happens if the order exists for the cart), they can choose a different payment option and a brand new order is created.

But, a customer can also press the "back to previous page" button of the browser when they are on the payment providers page.

enter image description here

They would be redirected back to the checkout, but in this case, my Method isn't called so the cart is gone. This happens because the init() Method in Frontcontroller.php is called. And because the order is validated // created, the cart gets deleted.

So, i added a new controller in the payment module named checkout:

$this->controllers = array('payment, return, webhook, checkout');

and placed checkout.php in the folder controllers/front of my module.

I extended the class FrontController and created an override for the init() Method. My code:

class PaymentModuleFrontController extends FrontController
{

    public function init()
    {

        $data = array();
        $cartId = $this->context->cart->id;
        $cart = new Cart($cartId);
        $orderId = Order::getOrderByCartId($cartId);
        $data['info'] = $this->module->getPaymentBy('cart_id', (int)$cartId); //gets payment from db. I checked it and this is correct
        $orderStatus = $data['info']['status']; //gets the status. I checked it and it's correct

        if (Validate::isLoadedObject($cart) && 
        $cart->OrderExists() && 
        $orderStatus === 'open') 
        {
            $oldCart = new Cart(Order::getCartIdStatic($orderId, $this->context->customer->id));
            $duplication = $oldCart->duplicate();
            if (!$duplication || !Validate::isLoadedObject($duplication['cart'])) 
            {
                $this->errors[] = Tools::displayError('Problem duplicating cart.');
            } 
            elseif (!$duplication['success']) 
            {
                $this->errors[] = Tools::displayError('Problem duplicating cart.');
            } 
            else 
            {
                $this->context->cookie->id_cart = $duplication['cart']->id;
                $context = $this->context;
                $context->cart = $duplication['cart'];
                CartRule::autoAddToCart($context);
                $this->context->cookie->write();
                if (Configuration::get('PS_ORDER_PROCESS_TYPE') == 1) 
                {
                Tools::redirect('index.php?controller=order-opc');
              }
              Tools::redirect('index.php?controller=order');
            }
        }
        parent::init();

    }


}

I have no idea what i'm doing wrong because this works fine for my return controller but it still removes the cart if i go back to the previous page with the browser button.

Could i use my code in a hook which is used on the checkout / order page and add it in my PaymentModule Class ?

important, i want to establish this without creating an override file for FrontController.php so it has to be done with a module controller or by my payment module Class.

I hope someone can help me figure this out.

like image 678
Aurora Avatar asked Dec 04 '17 19:12

Aurora


Video Answer


2 Answers

The approach that you are following to create an order is not correct. Instead of creating the order when the user is redirected to the Payment provider, you should create the order on the basis of the response received from the Payment Provider.

This means that the order is generated only if the payment is successful, else the user will see the error on the checkout page and the cart will stay as it is as well.

The approach that you are following is very risky as you are overriding a method that is called on each and every page in PrestaShop and my recommendation is that you should not be doing that.

like image 194
Wolfack Avatar answered Oct 18 '22 20:10

Wolfack


You can set id_cart in your payment url and get it in your controller. So when you create a payment option set id_cart in url query:

public function hookDisplayPayment($params){
    $externalOption = new PaymentOption();
    $externalOption->setCallToActionText('Payment Method')
          ->setAction($this->context->link->getModuleLink($this->name, 'payment', ['id_cart'=>$this->context->cart->id], true))
          ->setAdditionalInformation($plugin->description)
          ->setLogo(Media::getMediaPath(_PS_MODULE_DIR_.$this->name.'/logo.jpg'));
    return $externalOption;
}

Then get related cart in payment controller:

class PaymentModuleFrontController extends FrontController
{

    public function init()
    {
        $cart = new Cart(Tools::getValue('id_cart'));
        //more...
    }
 }
like image 1
Danoosh Avatar answered Oct 18 '22 19:10

Danoosh