Magento multiple websites share shopping cart

rita picture rita · Sep 11, 2012 · Viewed 10.2k times · Source

I have a bit of a dilemma with a Magento website I am building. I have two websites with a store each set up in order to enable multiple currencies checkout for each site. So the only difference between the two sites (on two different domains) that are managed via one common magento installation is the currency display and checkout currency. This is all working fine so far. However I am trying to share the checkout session between the sites so the shopping cart stays the same when switching between the sites. I manage to get the correct session id added to the switching url so each site knows what session we are looking for. However the method that actually displays the shopping cart does not seem to work website independent - for example in

Mage_Checkout_Model_Session

_getQuoteIdKey() -> uses the current website id to check for the quote id in the session.

And I can not figure out what/how to overwrite this functionality so that each website shares exactly the same shopping cart data!

my $_SESSION['checkout'] variable shows the same on each site but with this website id included the data is no use for the shopping cart: 'quote_id_4' => string '13' (length=2)

Any ideas whether this is possible at all?

Answer

rita picture rita · Sep 12, 2012

After I searched for an answer to this and stumbled across the same question being asked since 2009 without a definite solution, I finally had to look into the deep end of the code myself - and voila, I have a working solution. Here a detailed guide for anyone who wants to set up Magento with

  • multiple currencies that don't just get displayed but actually charged in the selected currency
  • share the shopping cart throughout websites and not just stores

The main problem with this combination is that with the default Magento structure you can only ever do one or the other, not the two combined.

So lets set Magento up for multiple currencies first.

  • Create a website for each currency with a corresponding store and store view (not just store views, complete websites)
  • Set System - Configuration - Currency Setup per website to the respective currency. All three entries Base Currecny, Default Display Currency and Allowed currencies should be set to just one and the same currency.
  • Go back to the default overall config scope and set System - Configuration - Catalog - - - Price the Catalog Price Scope to “Website”
  • You can also define you currency rates in System - Manage Currency Rates
  • For each website scope set the appropriate System - Configuration - Web - Secure and Unsecure base url.
  • In your .htaccess file add this at the top (replace the appropriate website domains and code you entered when setting up the websites (here http://website-us.local with base_us and http://website-uk.local with code base_uk)

    SetEnvIf Host website-us.local MAGE_RUN_CODE=base_us SetEnvIf Host website-us.local MAGE_RUN_TYPE=website SetEnvIf Host ^website-us.local MAGE_RUN_CODE=base_us SetEnvIf Host ^website-us.local MAGE_RUN_TYPE=website

    SetEnvIf Host website-uk.local MAGE_RUN_CODE=base_uk SetEnvIf Host website-uk.local MAGE_RUN_TYPE=website SetEnvIf Host ^website-uk.local MAGE_RUN_CODE=base_uk SetEnvIf Host ^website-uk.local MAGE_RUN_TYPE=website

  • Overwrite the method convertPrice in Mage/Core/Model/Store and change the method convertPrice - this will ensure that the prices are always displayed in the correct conversion AND the correct currency sign.

    /**
     * Convert price from default currency to current currency
     *
     * @param   double $price
     * @param   boolean $format             Format price to currency format
     * @param   boolean $includeContainer   Enclose into <span class="price"><span>
     * @return  double
     */
    public function convertPrice($price, $format = false, $includeContainer = true)
    {
        $categories = Mage::getModel('catalog/category')->getCollection();
        $categ_ids=$categories->getAllIds();
        $baseCurrencyCode = Mage::app()->getBaseCurrencyCode();
        $allowedCurrencies = Mage::getModel('directory/currency')->getConfigAllowCurrencies();
        $currencyRates = Mage::getModel('directory/currency')->getCurrencyRates($baseCurrencyCode,array_values($allowedCurrencies));
    
        if ($this->getCurrentCurrency() && $this->getBaseCurrency()) {
            $value = $this->getBaseCurrency()->convert($price, $this->getCurrentCurrency());
        } else {
            $value = $price;
        }
    
    
        if($this->getCurrentCurrencyCode() != $baseCurrencyCode)
        {
            $value = $price * $currencyRates[$this->getCurrentCurrencyCode()];
        }
        if ($this->getCurrentCurrency() && $format) {
            $value = $this->formatPrice($value, $includeContainer);
        }
        return $value;
    }
    

    }

But of course we also want to share the users data, cart and login throughout the websites we just set up.

  • While in default config scope set System - Configuration - Customer Configuration - Account Sharing Options - Share Customer Accounts to Global

  • Overwrite magento/app/code/core/Mage/Checkout/Model/Session.php and replace this method:

    protected function _getQuoteIdKey() { return 'quote_id'; //return 'quote_id_' . $websites[1]; }

  • Overwrite magento/app/code/core/Mage/Sales/Model/Quote.php and change the method getSharedStoreIds to:

    public function getSharedStoreIds() { $ids = $this->_getData('shared_store_ids'); if (is_null($ids) || !is_array($ids)) { $arrStoreIds = array(); foreach(Mage::getModel('core/website')->getCollection() as $website) { $arrStoreIds = array_merge($arrStoreIds,$website->getStoreIds()); } return $arrStoreIds; /*if ($website = $this->getWebsite()) { return $website->getStoreIds(); } var_dump($this->getStore()->getWebsite()->getStoreIds());exit(); return $this->getStore()->getWebsite()->getStoreIds(); */ } return $ids; }

  • Overwrite magento/app/code/core/Mage/Customers/Model/Customer.php and change again the method getSharedWebsiteIds() to:

    public function getSharedWebsiteIds() { $ids = $this->_getData('shared_website_ids'); if ($ids === null) { $ids = array(); if ((bool)$this->getSharingConfig()->isWebsiteScope()) { $ids[] = $this->getWebsiteId(); } else { foreach (Mage::app()->getWebsites() as $website) { $ids[] = $website->getId(); } } $this->setData('shared_website_ids', $ids); } return $ids; }

  • If you use the wishlist option you should do the same for the method in magento/app/code/core/Mage/Wishlist/Model/Wishlist.php and change getSharedWebsiteIds so it not only loads the store ids from the current website but from all of them

  • Now we also have to implement a currency (website) switch on the frontend stores and pass the correct session ids inbetween so magento knows what stores to look for. I imitated the currency switch here and added the following dropdown to

magento/app/design/frontend/default/yourtheme/template/directory/currency.phtml

This loads all websites and applies the current Session Id as a query string so magento knows on any domain which session to use.

<?php
/**
 * Currency switcher
 *
 * @see Mage_Directory_Block_Currency
 */
?>
<div class="top-currency">
<?php
$websites = Mage::getModel('core/website')->getCollection();
$this_session_id = Mage::getSingleton('core/session', array('name' => 'frontend'))->getSessionId();
?>
<select id="website-changer" onChange="document.location=this.options[selectedIndex].value">
<?php
foreach($websites as $website):
    $default_store = $website->getDefaultStore();
    $website_currency = $default_store->getBaseCurrency()->getCurrencyCode();
    $url_obj = new Mage_Core_Model_Url();
    $default_store_path = $url_obj->getBaseUrl(array('_store'=> $default_store->getCode()));
    $default_store_path .= Mage::getSingleton('core/url')->escape(ltrim(Mage::app()->getRequest()->getRequestString(), '/'));
    $default_store_path = explode('?', $default_store_path);
    $default_store_path = $default_store_path[0] . '?SID=' . $this_session_id;

?>
    <option <? if(strstr($default_store_path,Mage::getBaseUrl())):?>selected="selected"<?endif; ?> value="<?=$default_store_path ?>">
    <?=$website_currency?>
    </option>
<?endforeach;?>
</select>
</div>

This query string will only be applied the first time you switch but magento will remember the session id after that stored in a cookie.

  • In order for this to work properly, overwrite

magento/app/code/core/Mage/Core/Model/Session/Abstract/Varien.php

and

replace this

// potential custom logic for session id (ex. switching between hosts)
$this->setSessionId();

with

// potential custom logic for session id (ex. switching between hosts)
/* Amend to ensure shopping carts are shared between websites */
if (isset($_COOKIE['lastsid']))
{
  session_decode(file_get_contents(Mage::getBaseDir('session').'/sess_'.$_COOKIE['lastsid']));
            setcookie ('lastsid', '', time() - 3600);
        }

        if (isset($_GET['SID']))
        {
            $this->setSessionId($_GET['SID']);
            session_decode(file_get_contents(Mage::getBaseDir('session') . '/sess_' . $_GET['SID']));
            setcookie('lastsid', $_GET['SID']);
            $_COOKIE['lastsid'] = $_GET['SID'];
        }
        else
        {
            $this->setSessionId();
        }
        /* Amend end */

This should now display multiple currencies, charge in multiple currencies across mutliple websites and on top of this share logins and shopping carts. This solution is a collection of knowledge I found on the interwebs combined with a few bits and pieces I figured out myself, so thanks for anyone who gave advice!