Check for Customer Email Address Exists in Checkout – Magento

cecheckoutonepage-checkoutonestepcheckout

I'm using FireCheckout (a one page checkout extension). Some input fields have a prototype

.observe('change', function() { ... }); 

attached to them. For instance, once you entered your zipcode an Ajax Call follows (/firecheckout/index/saveBilling/) and the shipping methods and order review get reloaded.

I want to keep my checkout process as simple as possible, so I'm not working with guest checkouts or forced account creation. I've set FireCheckout to automatically create an account at checkout and send a welcome mail with password to the user.

However I noticed that when /saveBilling/ is called FireCheckout (version 2.5.3) also checks if the email address the user entered already exists or not. If exists, a popup is shown with the option to login.

I was very happy to see that FireCheckout was able to do this. However, the first field that automatically triggers this behavior is the zipcode.

So I've added .observe('change', function() { … }); to the email input field and voila, I almost get the behavior I'm looking for.

However …
Of course it's not this simple. While testing I noticed that it only works if a user has entered his or her first and lastname as well.

Now I have of course consulted FireCheckout support to help me out with this, but were not very helpful. Actually the fact that the email address gets checked while /saveBilling/ is called was a bug (!) – they believe that the email address should be checked no earlier then that a user has filled in all their information and presses the 'place order' button – at which moment the customer is unable to proceed and is forced to either use another email address or to login. (IMHO plain stupid and killing conversion)

I have found the code that is responsible for /saveBilling/ and checking the email address and here is (I think) the important part of that code, taken from /app/code/local/TM/FireCheckout/Model/Type/Standard.php:

public function saveBilling($data, $customerAddressId, $validate = true)
{
    if (empty($data)) {
        return array('error' => -1, 'message' => $this->_helper->__('Invalid data.'));
    }

    /* old code */
    if (isset($data['register_account']) && $data['register_account']) {
        $this->getQuote()->setCheckoutMethod(self::METHOD_REGISTER);
    } else if ($this->getCustomerSession()->isLoggedIn()) {
        $this->getQuote()->setCheckoutMethod(self::METHOD_CUSTOMER);
    } else {
        $this->getQuote()->setCheckoutMethod(self::METHOD_GUEST);
    }
    /* eof old code */

    $address = $this->getQuote()->getBillingAddress();
    /* @var $addressForm Mage_Customer_Model_Form */
    $addressForm = Mage::getModel('customer/form');
    $addressForm->setFormCode('customer_address_edit')
        ->setEntityType('customer_address')
        ->setIsAjaxRequest(Mage::app()->getRequest()->isAjax());

    if (!empty($customerAddressId)) {
        $customerAddress = Mage::getModel('customer/address')->load($customerAddressId);
        if ($customerAddress->getId()) {
            if ($customerAddress->getCustomerId() != $this->getQuote()->getCustomerId()) {
                return array('error' => 1,
                    'message' => $this->_helper->__('Customer Address is not valid.')
                );
            }
            $address->importCustomerAddress($customerAddress)->setSaveInAddressBook(0);
            // invalid validation of saved street address
            if (version_compare(Mage::helper('firecheckout')->getMagentoVersion(), '1.5.1.0') >= 0) {
                $addressForm->setEntity($address);
                if ($validate) {
                    $addressErrors = $addressForm->validateData($address->getData());
                    if ($addressErrors !== true) {
                        return array('error' => 1, 'message' => $addressErrors);
                    }
                }
            }
        }
    } else {
        $address->setCustomerAddressId(null);
        $addressForm->setEntity($address);
        // emulate request object
        $addressData = $addressForm->extractData($addressForm->prepareRequest($data));
        if ($validate) {
            $addressErrors  = $addressForm->validateData($addressData);
            if ($addressErrors !== true) {
                return array('error' => 1, 'message' => $addressErrors);
            }
        }
        $addressForm->compactData($addressData);
        //unset billing address attributes which were not shown in form
        foreach ($addressForm->getAttributes() as $attribute) {
            if (!isset($data[$attribute->getAttributeCode()])) {
                $address->setData($attribute->getAttributeCode(), NULL);
            }
        }

        // Additional form data, not fetched by extractData (as it fetches only attributes)
        $address->setSaveInAddressBook(empty($data['save_in_address_book']) ? 0 : 1);
    }

    // set email for newly created user
    if (!$address->getEmail() && $this->getQuote()->getCustomerEmail()) {
        $address->setEmail($this->getQuote()->getCustomerEmail());
    }

    // This is the part of the code that checks if the user already exists // 
    if (!$this->getQuote()->getCustomerId() && self::METHOD_REGISTER == $this->getQuote()->getCheckoutMethod()) {
        if ($this->_customerEmailExists($address->getEmail(), Mage::app()->getWebsite()->getId())) {
            return array('error' => 1, 'message' => $this->_customerEmailExistsMessage);
        }
    }

The last part of the code is of course the part where the email address gets checked. I've noticed that as long as the customer has not filled in his or her first and lastname $address->getEmail() == null and as such the function is unable to return the error message.

Digging further and further in to Magento code I found the following code in magento core onepage.php (public function saveBilling($data, $customerAddressId)):

if (!$this->getQuote()->getCustomerId() && Mage_Sales_Model_Quote::CHECKOUT_METHOD_REGISTER == $this->getQuote()->getCheckoutMethod()) {
    if ($this->_customerEmailExists($address->getEmail(), Mage::app()->getWebsite()->getId())) {
          return array('error' => 1,
           'message' => Mage::helper('checkout')->__('There is already a customer registered using this email address')
        );
     }
 }

To me it seems this core Magento code does exactly the same thing as the FireCheckout code. I suspect that it is Magento itself that somehow requires billing:firstname and billing:lastname to be present before it's able to compare billing:email to email addresses already in the database.

So my question basically comes down to:
How do I modify FireCheckout / Magento in such a way that – at Checkout – I can check if an email address already exists or not.

Best Answer

Looking at Using Javascript to validate response in the text box pointed me in the right direction.

I still have to clean up the code and integrate it correctly in firecheckout, but the following code basically does exactly what I want:

/** 
 * Check if customer email address already exists 
 */ 

public function checkEmailexistsAction()
{       

    if ($this->_expireAjax()) {
        echo 'Ajax Expired'; 
        return;
    }
    if (!$this->getRequest()->isPost()) {
        echo '!isPost'; 
        return;
    }

    $websiteId = Mage::app()->getWebsite()->getId();
    $data = $this->getRequest()->getPost('billing', array());
    $email = $data['email'];

    $customer = Mage::getModel('customer/customer');

    if ($websiteId) {
        $customer->setWebsiteId($websiteId);
        }

    $customer->loadByEmail($email);

    if ($customer->getId()) {
        // This is the part of the code that runs when a customer exists ... 
        echo 'Yup - this one exists!, email address = ' . $email . ' and ID = ' . $customer->getId(); 
        }
    else {
        // This is the part of the code that runs when a customer does not yet exist ...
        echo 'Nope.. This one is not there...'; 
    }
}

Posting 'billing[email]=someemailaddress@somehost.org' to /firecheckout/index/checkEmailexists now reliably returns a yes or no.

Related Topic