Search code examples
magentoauthorize.netonepage-checkout

Magento Authorize.net Direct Post not emptying cart (not setting quote as inactive)


In a site I am building that uses Authorize.net Direct Post as the payment method, I am running into an issue where the cart will not be inactivated after successful order completion. In Mage_Authorizenet_Model_Directpost, I was able to confirm that the quote is being inactivated on line 574 as part of the order authorization step.

Mage::getModel('sales/quote')
    ->load($order->getQuoteId())
    ->setIsActive(false)
    ->save();

However, in directpost.js, when Authorize.net loads the IFrame and the returnQuote function is called, it redirects to Mage_Authorizenet_Directpost_PaymentController's returnQuoteAction which calls the _returnCustomerQuote function.

if ($order->getId()) {
            $quote = Mage::getModel('sales/quote')
                ->load($order->getQuoteId());
            if ($quote->getId()) {
                $quote->setIsActive(1)
                    ->setReservedOrderId(NULL)
                    ->save();
                $this->_getCheckout()->replaceQuote($quote);
            }
            $this->_getDirectPostSession()->removeCheckoutOrderIncrementId($incrementId);
            $this->_getDirectPostSession()->unsetData('quote_id');
            if ($cancelOrder) {
                $order->registerCancellation($errorMsg)->save();
            }
        }

Notice that the quote is being set as active again. I don't know why they do this unless the order is canceled. I'm thinking that maybe I'm missing something in the logic here. We have done some onepage checkout customization and some customization on the actual order submission, but I don't see anything that would affect this. Is Magento expecting that the quote will get disabled later down the call chain? Its hard for me to debug this since I can't step through the code given that Authorize.net Direct Post won't relay a response back to my local.

Thanks for any help that can be offered.


Solution

  • From what I can tell, it looks like _returnCustomerQuote could be called if the request parameters from Authorize.net contain an error message, even if it was successful. This is from the redirectAction of the Mage_Authorizenet_Directpost_PaymentController

        if (!empty($redirectParams['success'])
            && isset($redirectParams['x_invoice_num'])
            && isset($redirectParams['controller_action_name'])
        ) {
            $this->_getDirectPostSession()->unsetData('quote_id');
            $params['redirect_parent'] = Mage::helper('authorizenet')->getSuccessOrderUrl($redirectParams);
        }
        if (!empty($redirectParams['error_msg'])) {
            $cancelOrder = empty($redirectParams['x_invoice_num']);
            $this->_returnCustomerQuote($cancelOrder, $redirectParams['error_msg']);
        }
    

    However, I could tell in my situation by looking at access.log [combined] that there was an entry coming from /checkout/onepage going to /authorizenet/directpost_payment/returnQuote.

    After pulling my hair for a while and digging around the code and doing some research I feel like this could only be a bug in the loadIframe function that is bound to onLoadIframe in directpost.js.

         loadIframe : function() {
            if (this.paymentRequestSent) {
                switch (this.controller) {
                    case 'onepage':
                        this.paymentRequestSent = false;
                        if (!this.hasError) {
                            this.returnQuote();
                        }
                        break;
                    case 'sales_order_edit':
                    case 'sales_order_create':
                        if (!this.orderRequestSent) {
                            this.paymentRequestSent = false;
                            if (!this.hasError) {
                                this.returnQuote();
                            } else {
                                this.changeInputOptions('disabled', false);
                                toggleSelectsUnderBlock($('loading-mask'), true);
                                $('loading-mask').hide();
                                enableElements('save');
                            }
                        }
                        break;
                }
                if (this.tmpForm) {
                    document.body.removeChild(this.tmpForm);
                }
            }
        },
    

    Seems to me that it is checking to see if the payment request was sent when the redirect action fills the iframe. If the payment request was sent, if it was using the onepage controller, and if there was NO error in the response, then return the quote, and thus leave it active and items remain in the cart...

    That doesn't make sense to me, so I removed the bang from this.hasError. Now the cart is cleared after the order, and I don't seem to have any other problems

    It looks like this now, I would love for someone to tell me I am wrong (seriously).

    loadIframe : function() {
        if (this.paymentRequestSent) {
            switch (this.controller) {
                case 'onepage':
                    this.paymentRequestSent = false;
                    if (this.hasError) {
                        this.returnQuote();
                    }
                    break;
                case 'sales_order_edit':
                case 'sales_order_create':
                    if (!this.orderRequestSent) {
                        this.paymentRequestSent = false;
                        if (!this.hasError) {
                            this.returnQuote();
                        } else {
                            this.changeInputOptions('disabled', false);
                            toggleSelectsUnderBlock($('loading-mask'), true);
                            $('loading-mask').hide();
                            enableElements('save');
                        }
                    }
                    break;
            }
            if (this.tmpForm) {
                document.body.removeChild(this.tmpForm);
            }
        }
    },
    

    It seems like this would be in the case 'sales_order_create' controller, but I'm leaving Magento alone for now.