Search

I have a e-commerce site set up using FoxyCart for the basket/checkout system and everything is running nicely but now I need to develop a simple stock/inventory control system in Symphony that will use the xml data feed available after a successful foxy cart transaction to update the product stock levels.

I have added a number field to my products section to store the quantity in stock. So far so good.

I think what I will need for the xml data feed coming from foxy cart is a custom event of some sort. Never done one of these before though and I'm quite new to php. Can anyone point me in the right direction on this? I know some members of the community have used Foxy Cart in the past, has any one integrated the xml feed successfully? If some one could point out the basic steps/skeleton code I need for a custom event that would be ace :)

The wiki page about the foxy cart transactional xml data feed is here

Have you had a look at the XML importer extension already? There is also a recent article that might be of interest to you. If you plan to run the xml importer from an event, you should also look at this.

Thanks @antoine, thats a great starting point for me.

I think, as you suggest, that I will need a page with a custom event attached to it. I can tell FoxyCart to send the encrypted xml transaction datasource there and invoke my custom event which can decyrpt the xml. From there I guess I can invoke the xml importer extension to update my product entries.

I'll report back once I get my head round things a bit better :)

Ok, I have set up a custom event that runs when the encrypted foxy cart is posted to the page. Here is the custom event:


<?php

    require_once(TOOLKIT . '/class.event.php');

    Class eventtestdatasource extends Event{

        const ROOTELEMENT = 'testdatasource';

        public $eParamFILTERS = array(
            
        );

        public static function about(){
            return array(
                'name' => 'testdatasource',
                'author' => array(
                    'name' => 'Dave Coggins',
                    'website' => 'http://localhost/sandbox.local',
                    'email' => 'dave.coggins2k@googlemail.com'),
                'version' => 'Symphony 2.2.5',
                'release-date' => '2012-04-20T02:08:33+00:00',
                'trigger-condition' => 'action[testdatasource]'
            );
        }

        public static function getSource(){
            return '1';
        }

        public static function allowEditorToParse(){
            return false;
        }

        public static function documentation(){
            return '';
        }
        public function load(){
            $myKey = 'topsecret'; // your foxy cart datafeed key

            if (isset($_POST["FoxyData"])) {
                $FoxyData_encrypted = urldecode($_POST["FoxyData"]);
                $FoxyData_decrypted = rc4crypt::decrypt($myKey,$FoxyData_encrypted);
                var_dump($FoxyData_decrypted);
                return $this->__trigger();  
            } 
        }
        

        protected function __trigger(){
            include(TOOLKIT . '/events/event.section.php');
            return $result;
        }

    }

    // ======================================================================================
// RC4 ENCRYPTION CLASS
// Do not modify.
// ======================================================================================
/**
 * RC4Crypt 3.2
 *
 * RC4Crypt is a petite library that allows you to use RC4
 * encryption easily in PHP. It's OO and can produce outputs
 * in binary and hex.
 *
 * (C) Copyright 2006 Mukul Sabharwal [http://mjsabby.com]
 *     All Rights Reserved
 *
 * @link http://rc4crypt.devhome.org
 * @author Mukul Sabharwal 
 * @version $Id: class.rc4crypt.php,v 3.2 2006/03/10 05:47:24 mukul Exp $
 * @copyright Copyright © 2006 Mukul Sabharwal
 * @license http://www.gnu.org/copyleft/gpl.html
 * @package RC4Crypt
 */
 
/**
 * RC4 Class
 * @package RC4Crypt
 */
class rc4crypt {
    /**
     * The symmetric encryption function
     *
     * @param string $pwd Key to encrypt with (can be binary of hex)
     * @param string $data Content to be encrypted
     * @param bool $ispwdHex Key passed is in hexadecimal or not
     * @access public
     * @return string
     */
    function encrypt ($pwd, $data, $ispwdHex = 0)
    {
        if ($ispwdHex)
            $pwd = @pack('H*', $pwd); // valid input, please!
 
        $key[] = '';
        $box[] = '';
        $cipher = '';
 
        $pwd_length = strlen($pwd);
        $data_length = strlen($data);
 
        for ($i = 0; $i < 256; $i++)
        {
            $key[$i] = ord($pwd[$i % $pwd_length]);
            $box[$i] = $i;
        }
        for ($j = $i = 0; $i < 256; $i++)
        {
            $j = ($j + $box[$i] + $key[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        for ($a = $j = $i = 0; $i < $data_length; $i++)
        {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $k = $box[(($box[$a] + $box[$j]) % 256)];
            $cipher .= chr(ord($data[$i]) ^ $k);
        }
        return $cipher;
    }
    /**
     * Decryption, recall encryption
     *
     * @param string $pwd Key to decrypt with (can be binary of hex)
     * @param string $data Content to be decrypted
     * @param bool $ispwdHex Key passed is in hexadecimal or not
     * @access public
     * @return string
     */
    function decrypt ($pwd, $data, $ispwdHex = 0)
    {
        return rc4crypt::encrypt($pwd, $data, $ispwdHex);
    }
}

I am using the the tester script found here to simulate the XML that would could from a valid Foxy Cart transaction.

The decryption all works and I can do a var_dump of the php variable $FoxyData_decrypted and get the following:


string(4249) "<?xml version='1.0' standalone='yes'?>
<foxydata>
    <datafeed_version>XML FoxyCart Version 0.6</datafeed_version>
    <transactions>
        <transaction>
            <id>616</id>
            <transaction_date>2007-05-04 20:53:57</transaction_date>
            <customer_id>122</customer_id>
            <customer_first_name>John</customer_first_name>
            <customer_last_name>Doe</customer_last_name>
            <customer_address1>12345 Any Street</customer_address1>
            <customer_address2></customer_address2>
            <customer_city>Any City</customer_city>
            <customer_state>TN</customer_state>
            <customer_postal_code>37013</customer_postal_code>
            <customer_country>US</customer_country>
            <customer_phone>(123) 456-7890</customer_phone>
            <customer_email>someone@somewhere.com</customer_email>
            <customer_ip>71.228.237.177</customer_ip>
            <shipping_first_name>John</shipping_first_name>
            <shipping_last_name>Doe</shipping_last_name>
            <shipping_address1>1234 Any Street</shipping_address1>
            <shipping_address2></shipping_address2>
            <shipping_city>Some City</shipping_city>
            <shipping_state>TN</shipping_state>
            <shipping_postal_code>37013</shipping_postal_code>
            <shipping_country>US</shipping_country>
            <shipping_phone></shipping_phone>
            <shipping_service_description>UPS: Ground</shipping_service_description>
            <purchase_order></purchase_order>
            <product_total>20.00</product_total>
            <tax_total>0.00</tax_total>
            <shipping_total>4.38</shipping_total>
            <order_total>24.38</order_total>
            <order_total>24.38</order_total>
            <customer_password>1aab23051b24582c5dc8e23fc595d505</customer_password>
            <custom_fields>
                <custom_field>
                    <custom_field_name>My_Cool_Text</custom_field_name>
                    <custom_field_value>Value123</custom_field_value>
                </custom_field>
                <custom_field>
                    <custom_field_name>Another_Custom_Field</custom_field_name>
                    <custom_field_value>10</custom_field_value>
                </custom_field>
            </custom_fields>
            <transaction_details>
                <transaction_detail>
                    <product_name>foo</product_name>
                    <product_price>20.00</product_price>
                    <product_quantity>1</product_quantity>
                    <product_weight>0.10</product_weight>
                    <product_code></product_code>
                    <subscription_frequency>1m</subscription_frequency>
                    <subscription_startdate>2007-07-07</subscription_startdate>
                    <next_transaction_date>2007-08-07</next_transaction_date>
                    <shipto>John Doe</shipto>
                    <category_description>Default for all products</category_description>
                    <category_code>DEFAULT</category_code>
                    <product_delivery_type>shipped</product_delivery_type>
                    <transaction_detail_options>
                        <transaction_detail_option>
                            <product_option_name>color</product_option_name>
                            <product_option_value>blue</product_option_value>
                            <price_mod></price_mod>
                            <weight_mod></weight_mod>
                        </transaction_detail_option>
                    </transaction_detail_options>
                </transaction_detail>
            </transaction_details>
            <shipto_addresses>
                <shipto_address>
                    <address_name>John Doe</address_name>
                    <shipto_first_name>John</shipto_first_name>
                    <shipto_last_name>Doe</shipto_last_name>
                    <shipto_address1>2345 Some Address</shipto_address1>
                    <shipto_address2></shipto_address2>
                    <shipto_city>Some City</shipto_city>
                    <shipto_state>TN</shipto_state>
                    <shipto_postal_code>37013</shipto_postal_code>
                    <shipto_country>US</shipto_country>
                    <shipto_shipping_service_description>DHL: Next Afternoon</shipto_shipping_service_description>
                    <shipto_subtotal>52.15</shipto_subtotal>
                    <shipto_tax_total>6.31</shipto_tax_total>
                    <shipto_shipping_total>15.76</shipto_shipping_total>
                    <shipto_total>74.22</shipto_total>
                    <shipto_custom_fields>
                        <shipto_custom_field>
                            <shipto_custom_field_name>My_Custom_Info</shipto_custom_field_name>
                            <shipto_custom_field_value>john's stuff</shipto_custom_field_value>
                        </shipto_custom_field>
                        <shipto_custom_field>
                            <shipto_custom_field_name>More_Custom_Info</shipto_custom_field_name>
                            <shipto_custom_field_value>more of john's stuff</shipto_custom_field_value>
                        </shipto_custom_field>
                    </shipto_custom_fields>
                </shipto_address>
            </shipto_addresses>
        </transaction>
    </transactions>
</foxydata>"

which all looks correct :)

The next bit I am stuck at though. What i want to do is update the quantity field in my products section section by subtracting the quantity ordered in each transaction detail XML entry for the corresponding product.

I know I probably need a for loop to loop through all of the transaction_detail nodes but I'm not sure how to do this using php.

The next question I have is how do I update the records in my product section using php? To keep things simple I am using the id from the product section as the product_code in the foxy cart XML.

I have a number field called quantity for each product so guess I need to find the product by id and subtract the quantity ordered from the amount stored in the quantity field in the product section entry.

PHP is all pretty new to me and I am not sure of what is the best way of doing this.

One more question, if some one could also explain the relationship between the load function and the trigger function in an event that would be greatly appreciated.

Thanks in advanced.

On second thoughts, I believe the xml importer will be of no use for you regarding the product quantity. But you might still want to import the transaction details inside symphony for the record.

One more question, if some one could also explain the relationship between the load function and the trigger function in an event that would be greatly appreciated.

As far as I know the load() function is executed on page load, and the __trigger() function is executed from the where you want, usually the load() function. The result return from __trigger() is added to the <events/> element in the xml output. Which in your case will be of use only for debug, as your page/event will be executed in the 'background'.

So i think you should move your decryption logic inside the __trigger() function. I don't think you will need the section toolkit. So you can remove include(TOOLKIT . '/events/event.section.php');

What you need to do is somehow load your xml inside an XML object. So you can traverse the XML tree, to get the products id and quantities from the transaction. For each product you will then update the value inside symphony accordingly.

To update your entries inside symphony you should look at the entry manager in the API.

Fetching entries to get their current values would look something like this:

include_once(TOOLKIT . '/class.entrymanager.php');

$entryManager = new EntryManager(Symphony::Engine());
$entries =& $entryManager->fetch(array(), 2);

$twitters = array();

foreach ($entries as $entry) {
    $entryData = $entry->getData();
    $twitters[] = $entryData[69]["value"];
}

The above load twitters usernames from field 69 from section 2. To get your fields ids and sections ids, look at the entity diagram extension.

The the big question now is how to traverse your XML tree. That I don't know, I've done that yet. Perhaps the XMLelement ? Or, some php XML function.

Or instead of traversing the XML, you could import the transaction inside a section with 3 fields, Transaction ID, Product ID and Quantity using the XML importer. Then fetch those entries from that transaction, and update your product stock quantity accordingly.

@davecoggins did you have any success with updating stock levels in Symphony after each Foxycart transaction? If so would you care to share how you have things integrated? I'm just about to approach a simple store where I'd like to use Symphony in combination with Foxycart but would also need stock to be updated after each successful transaction.

Thanks

As it happens I am about to launch a site in 10 days or so using foxycart which will be using the foxycart transaction xml after each successful transaction. For this site we are not using it for stock control but just to update the number of sales counter on the front page of site. It wouldn't be too difficult though to extend that to stock control.

Once i get the site launched i'll post the code (which is a custom event) for parsing the foxycart transaction xml.

Sounds like very good timing, that would be great! It sounds like it shouldn't be too tricky to alter things to just decrement the stock quantity within Symphony from there.

The only other thing I'd ideally like is to be able to grab the order details from the same transactional xml feed and then store the order details within symphony. This is mainly just for user convenience but would be nice. As it comes from the same feed I'd presume it to be possible but would probably require a second event to trigger. Have you had need for this at all in your store development?

Thanks.

Hey Dave, just wandered if you'd got around to launching your Foxycart store and publishing your Foxycart custom event code yet at all? It would be a great starting point as I'm just working on a Foxycart store now.

We're launching the site next week now but i'll try and get something written up over the weekend which I can share here.

Can anyone point me towards an existing addon which would be good to use as an example for this particular use case? Specifically it needs to process an XML response from a set URL, parse through the contents and then update a field in a single Symphony section. I've currently used a little SymQL and raw queries to meet deadlines but I'd like to refactor this to work in harmony with the Symphony API. I'm very much new and learning from the beginning as this is my first custom Event so having a close working example would be a big help.

@Dave, have you had the chance to put anything up online with regard to this yet?

Thanks

sorry @ijy, it has just been one of those weeks..

Here is my custom event for updating a field which stores a count for the total number of foxycart transactions: public function load(){ if(isset($_POST["FoxyData"])) return $this->__trigger(); }

protected function __trigger(){


  $result = new XMLElement($this->ROOTELEMENT);
  $entry_manager = new EntryManager(Symphony::Engine());
  $field_manager = new FieldManager(Symphony::Engine());
  $foxy_key = Symphony::Configuration()->get('api_key', 'foxy_cart');

  //Decrypt the incoming foxycart transaction xml
  $foxy_data_encrypted = urldecode($_POST["FoxyData"]);
  $foxy_data_decrypted = rc4crypt::decrypt($foxy_key, $foxy_data_encrypted);

  //Parse string into XML object
  $foxy_data_xml = simplexml_load_string($foxy_data_decrypted);

  //Traverse the foxy transaction xml object and work out the total number of products sold
  $total_products = 0;
  //For each foxy cart transaction
  foreach($foxy_data_xml->transactions->transaction as $transaction){
    //For each transaction detail
    foreach($transaction->transaction_details->transaction_detail as $transaction_detail){
      $product_quantity = (int)$transaction_detail->product_quantity;
      $total_products += $product_quantity;   
    }
  }

  //Update the sales counter record

  $entry = $entry_manager->fetch(39);

  $entry = current($entry);
  $entry_data = $entry->getData();

  $number_of_sales_field = $field_manager->fetch(
    $field_manager->fetchFieldIDFromElementName('total-number-of-sales')
  );

  $current_number_of_sales = $entry_data[$number_of_sales_field->get('id')]['value'];

  $new_number_of_sales = $current_number_of_sales + $total_products;

  $entry->setData(
    $number_of_sales_field->get('id'),
    $number_of_sales_field->processRawFieldData($new_number_of_sales, $status)
  );


  // Update Entry record

  if($entry->commit()) {
    $result->setAttribute('result', 'success');
  }
  else {
    $result->setAttribute('result', 'error');
  }
  return $result;
}

}

// ====================================================================================== // RC4 ENCRYPTION CLASS // Do not modify. // ====================================================================================== /** * RC4Crypt 3.2 * * RC4Crypt is a petite library that allows you to use RC4 * encryption easily in PHP. It's OO and can produce outputs * in binary and hex. * * (C) Copyright 2006 Mukul Sabharwal [http://mjsabby.com] * All Rights Reserved * * @link http://rc4crypt.devhome.org * @author Mukul Sabharwal mjsabby@gmail.com * @version $Id: class.rc4crypt.php,v 3.2 2006/03/10 05:47:24 mukul Exp $ * @copyright Copyright © 2006 Mukul Sabharwal * @license http://www.gnu.org/copyleft/gpl.html * @package RC4Crypt */

/** * RC4 Class * @package RC4Crypt / class rc4crypt { /** * The symmetric encryption function * * @param string $pwd Key to encrypt with (can be binary of hex) * @param string $data Content to be encrypted * @param bool $ispwdHex Key passed is in hexadecimal or not * @access public * @return string */ function encrypt ($pwd, $data, $ispwdHex = 0) { if ($ispwdHex) $pwd = @pack('H', $pwd); // valid input, please!

  $key[] = '';
  $box[] = '';
  $cipher = '';

  $pwd_length = strlen($pwd);
  $data_length = strlen($data);

  for ($i = 0; $i < 256; $i++)
  {
    $key[$i] = ord($pwd[$i % $pwd_length]);
    $box[$i] = $i;
  }
  for ($j = $i = 0; $i < 256; $i++)
  {
    $j = ($j + $box[$i] + $key[$i]) % 256;
    $tmp = $box[$i];
    $box[$i] = $box[$j];
    $box[$j] = $tmp;
  }
  for ($a = $j = $i = 0; $i < $data_length; $i++)
  {
    $a = ($a + 1) % 256;
    $j = ($j + $box[$a]) % 256;
    $tmp = $box[$a];
    $box[$a] = $box[$j];
    $box[$j] = $tmp;
    $k = $box[(($box[$a] + $box[$j]) % 256)];
    $cipher .= chr(ord($data[$i]) ^ $k);
  }
  return $cipher;
}
/**
 * Decryption, recall encryption
 *
 * @param string $pwd Key to decrypt with (can be binary of hex)
 * @param string $data Content to be decrypted
 * @param bool $ispwdHex Key passed is in hexadecimal or not
 * @access public
 * @return string
 */
function decrypt ($pwd, $data, $ispwdHex = 0)
{
  return rc4crypt::encrypt($pwd, $data, $ispwdHex);
}

}

I've got my foxy cart key stored in the config file.

This is my first custom event also so probably not the best example. If you have any questions, let me know, I'll try and write a description about it if I get time later today.

Likewise, I'd like to hear any experience you have using foxycart. Hope it helps.

Hey Dave, no worries at all, I've had much the same this week. :P

That's awesome, thanks for sharing the code. It's a great starting point and even from first glance it's given me a few ideas on a better way to refactor things. If you get the chance to add any description then it will be gratefully received but I can understand that you're busy so just as and when you get a free moment. I've got something working and up and live at the moment so at this point it's just a case of re-factoring what I have to be able to share and re-use it in the future. I'll look to modify things based on your example and any other Events which I find to be similar and then look to publish what I have for critique. We may be able to learn bits from each other on this one. I'd also like to follow this up with the next step of grabbing order info from the XML response and saving to a separate section so hopefully this first step will pave the way.

Thanks again, much appreciated.

If anyone can also highlight any other Events following a similar structure then it will be good to learn from the ninjas in building events/extensions.

Create an account or sign in to comment.

Symphony • Open Source XSLT CMS

Server Requirements

  • PHP 5.3-5.6 or 7.0-7.3
  • PHP's LibXML module, with the XSLT extension enabled (--with-xsl)
  • MySQL 5.5 or above
  • An Apache or Litespeed webserver
  • Apache's mod_rewrite module or equivalent

Compatible Hosts

Sign in

Login details