Jump to content

Foxycart XML datafeed integration...


alejandro
 Share

Recommended Posts

Hello,

Maybe not a PW directly related question, but just in case anyone has done it before:

I'm triying to process the transaction data received from foxycart api, in a page/endpoint in PW. In this page there is a script such as:

//Set Globals and Get Settings
$apikey = "my api key";

//-----------------------------------------------------
// TRANSACTION DATAFEED
//-----------------------------------------------------
if (isset($_POST["FoxyData"])) {

	//DECRYPT (required)
	//-----------------------------------------------------
	$FoxyData_decrypted = foxycart_decrypt($_POST["FoxyData"]);
	$xml = simplexml_load_string($FoxyData_decrypted, NULL, LIBXML_NOCDATA);
 
	//For Each Transaction
	foreach($xml->transactions->transaction as $transaction) {
 
		//This variable will tell us whether this is a multi-ship store or not
		$is_multiship = 0;
 
		//Get FoxyCart Transaction Information
		//Simply setting lots of helpful data to PHP variables so you can access it easily
		//If you need to access more variables, you can see some sample XML here: http://wiki.foxycart.com/v/1.1/transaction_xml_datafeed
		$transaction_id = (string)$transaction->id;
		$transaction_date = (string)$transaction->transaction_date;
		$customer_ip = (string)$transaction->customer_ip;
		$customer_id = (string)$transaction->customer_id;
		$customer_first_name = (string)$transaction->customer_first_name;
		$customer_last_name = (string)$transaction->customer_last_name;
		$customer_company = (string)$transaction->customer_company;
		$customer_email = (string)$transaction->customer_email;
		$customer_password = (string)$transaction->customer_password;
		$customer_address1 = (string)$transaction->customer_address1;
		$customer_address2 = (string)$transaction->customer_address2;
		$customer_city = (string)$transaction->customer_city;
		$customer_state = (string)$transaction->customer_state;
		$customer_postal_code = (string)$transaction->customer_postal_code;
		$customer_country = (string)$transaction->customer_country;
		$customer_phone = (string)$transaction->customer_phone;

 

			//Putting the Custom Fields in an array if they are there
			if (!empty($shipto_address->custom_fields)) {
				foreach($shipto_address->custom_fields->custom_field as $custom_field) {
					$shipto[$shipto_name]['custom_fields'][(string)$custom_field->custom_field_name] = (string)$custom_field->custom_field_value;
				}
			}
		}
 
		//For Each Transaction Detail
		foreach($transaction->transaction_details->transaction_detail as $transaction_detail) {
			$product_name = (string)$transaction_detail->product_name;
			$product_code = (string)$transaction_detail->product_code;
			$product_quantity = (int)$transaction_detail->product_quantity;
			$product_price = (double)$transaction_detail->product_price;
			$product_shipto = (double)$transaction_detail->shipto;
			$category_code = (string)$transaction_detail->category_code;
			$product_delivery_type = (string)$transaction_detail->product_delivery_type;
			$sub_token_url = (string)$transaction_detail->sub_token_url;
			$subscription_frequency = (string)$transaction_detail->subscription_frequency;
			$subscription_startdate = (string)$transaction_detail->subscription_startdate;
			$subscription_nextdate = (string)$transaction_detail->subscription_nextdate;
			$subscription_enddate = (string)$transaction_detail->subscription_enddate;
 
			//These are the options for the product
			$transaction_detail_options = array();
			foreach($transaction_detail->transaction_detail_options->transaction_detail_option as $transaction_detail_option) {
				$product_option_name = $transaction_detail_option->product_option_name;
				$product_option_value = (string)$transaction_detail_option->product_option_value;
				$price_mod = (double)$transaction_detail_option->price_mod;
				$weight_mod = (double)$transaction_detail_option->weight_mod;
 
			}
  
			//If you have custom code to run for each product, put it here:
		}
 
		//If you have custom code to run for each order, put it here:
		$p = new Page();
 		$p->parent = $pages->get("1350");
 		$p->template = $templates->get("receipt");
 		$p->title = "Transaction id: ". $transaction_id;
 		$p->save();
 	}
	//All Done!
	die("foxy");

//-----------------------------------------------------
// NO POST CONTENT SENT
//-----------------------------------------------------
} else {
	die('No Content Received From Datafeed');
}
//Decrypt Data From Source
function foxycart_decrypt($src) {
    	global $apikey;
	return rc4crypt::decrypt($apikey,urldecode($src));
}
 
// ======================================================================================
// RC4 ENCRYPTION CLASS
// Do not modify.
// =====================================================================================
.
.
. ..script continues with the encryption, part of the code's been cut, just for clarity...

The script captures a FoxyCart $_Post variable, decrypts it, and parses the XML data with SimpleXML and apparently works fine, as tested by Foxycart support, and returns ok ('foxy') to Foxycart servers. But the pages in PW are not created. Well, if the PW code is placed outside the foreach, not intended, it works but has no access to the PHP variables, eg $transaction_id number.

Another weird thing, for me, is that every page that holds this script appears as "301 moved permanently" when requested the http headers, and maybe could be this the culprit ¿?.

Well, as said before, just in case anyone here has done it before...

Thanks in advance, Alejandro.

Link to comment
Share on other sites

Of course, but do not give any credit to me, is just copied from the FoxyCart docs  :rolleyes:

https://wiki.foxycart.com/v/1.1/transaction_xml_datafeed

/Script by David Hollander, www.foxy-shop.com
//version 1.0, 7/9/2012
 
//Set Globals and Get Settings
$apikey = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
 
//-----------------------------------------------------
// TRANSACTION DATAFEED
//-----------------------------------------------------
if (isset($_POST["FoxyData"])) {
 
 
	//DECRYPT (required)
	//-----------------------------------------------------
	$FoxyData_decrypted = foxycart_decrypt($_POST["FoxyData"]);
	$xml = simplexml_load_string($FoxyData_decrypted, NULL, LIBXML_NOCDATA);
 
 
	//For Each Transaction
	foreach($xml->transactions->transaction as $transaction) {
 
		//This variable will tell us whether this is a multi-ship store or not
		$is_multiship = 0;
 
		//Get FoxyCart Transaction Information
		//Simply setting lots of helpful data to PHP variables so you can access it easily
		//If you need to access more variables, you can see some sample XML here: http://wiki.foxycart.com/v/1.1/transaction_xml_datafeed
		$transaction_id = (string)$transaction->id;
		$transaction_date = (string)$transaction->transaction_date;
		$customer_ip = (string)$transaction->customer_ip;
		$customer_id = (string)$transaction->customer_id;
		$customer_first_name = (string)$transaction->customer_first_name;
		$customer_last_name = (string)$transaction->customer_last_name;
		$customer_company = (string)$transaction->customer_company;
		$customer_email = (string)$transaction->customer_email;
		$customer_password = (string)$transaction->customer_password;
		$customer_address1 = (string)$transaction->customer_address1;
		$customer_address2 = (string)$transaction->customer_address2;
		$customer_city = (string)$transaction->customer_city;
		$customer_state = (string)$transaction->customer_state;
		$customer_postal_code = (string)$transaction->customer_postal_code;
		$customer_country = (string)$transaction->customer_country;
		$customer_phone = (string)$transaction->customer_phone;
 
 
		//This is for a multi-ship store. The shipping addresses will go in a $shipto array with the address name as the key
		$shipto = array();
		foreach($transaction->shipto_addresses->shipto_address as $shipto_address) {
			$is_multiship = 1;
			$shipto_name = (string)$shipto_address->address_name;
			$shipto[$shipto_name] = array(
				'first_name' => (string)$shipto_address->shipto_first_name,
				'last_name' => (string)$shipto_address->shipto_last_name,
				'company' => (string)$shipto_address->shipto_company,
				'address1' => (string)$shipto_address->shipto_address1,
				'address2' => (string)$shipto_address->shipto_address2,
				'city' => (string)$shipto_address->shipto_city,
				'state' => (string)$shipto_address->shipto_state,
				'postal_code' => (string)$shipto_address->shipto_postal_code,
				'country' => (string)$shipto_address->shipto_country,
				'shipping_service_description' => (string)$shipto_address->shipto_shipping_service_description,
				'subtotal' => (string)$shipto_address->shipto_subtotal,
				'tax_total' => (string)$shipto_address->shipto_tax_total,
				'shipping_total' => (string)$shipto_address->shipto_shipping_total,
				'total' => (string)$shipto_address->shipto_,
				'custom_fields' => array()
			);
 
			//Putting the Custom Fields in an array if they are there
			if (!empty($shipto_address->custom_fields)) {
				foreach($shipto_address->custom_fields->custom_field as $custom_field) {
					$shipto[$shipto_name]['custom_fields'][(string)$custom_field->custom_field_name] = (string)$custom_field->custom_field_value;
				}
			}
		}
 
		//This is setup for a single ship store
		if (!$is_multiship) {
			$shipping_first_name = (string)$transaction->shipping_first_name ? (string)$transaction->shipping_first_name : $customer_first_name;
			$shipping_last_name = (string)$transaction->shipping_last_name ? (string)$transaction->shipping_last_name : $customer_last_name;
			$shipping_company = (string)$transaction->shipping_company ? (string)$transaction->shipping_company : $customer_company;
			$shipping_address1 = (string)$transaction->shipping_address1 ? (string)$transaction->shipping_address1 : $customer_address1;
			$shipping_address2 = (string)$transaction->shipping_address1 ? (string)$transaction->shipping_address2 : $customer_address2;
			$shipping_city = (string)$transaction->shipping_city ? (string)$transaction->shipping_city : $customer_city;
			$shipping_state = (string)$transaction->shipping_state ? (string)$transaction->shipping_state : $customer_state;
			$shipping_postal_code = (string)$transaction->shipping_postal_code ? (string)$transaction->shipping_postal_code : $customer_postal_code;
			$shipping_country = (string)$transaction->shipping_country ? (string)$transaction->shipping_country : $customer_country;
			$shipping_phone = (string)$transaction->shipping_phone ? (string)$transaction->shipping_phone : $customer_phone;
			$shipto_shipping_service_description = (string)$transaction->shipto_shipping_service_description;
		}
 
		//Putting the Custom Fields in an array if they are there. These are on the top level and could be there for both single ship and multiship stores
		$custom_fields = array();
		if (!empty($transaction->custom_fields)) {
			foreach($transaction->custom_fields->custom_field as $custom_field) {
				$custom_fields[(string)$custom_field->custom_field_name] = (string)$custom_field->custom_field_value;
			}
		}
 
 
 
 
 
 
		//For Each Transaction Detail
		foreach($transaction->transaction_details->transaction_detail as $transaction_detail) {
			$product_name = (string)$transaction_detail->product_name;
			$product_code = (string)$transaction_detail->product_code;
			$product_quantity = (int)$transaction_detail->product_quantity;
			$product_price = (double)$transaction_detail->product_price;
			$product_shipto = (double)$transaction_detail->shipto;
			$category_code = (string)$transaction_detail->category_code;
			$product_delivery_type = (string)$transaction_detail->product_delivery_type;
			$sub_token_url = (string)$transaction_detail->sub_token_url;
			$subscription_frequency = (string)$transaction_detail->subscription_frequency;
			$subscription_startdate = (string)$transaction_detail->subscription_startdate;
			$subscription_nextdate = (string)$transaction_detail->subscription_nextdate;
			$subscription_enddate = (string)$transaction_detail->subscription_enddate;
 
			//These are the options for the product
			$transaction_detail_options = array();
			foreach($transaction_detail->transaction_detail_options->transaction_detail_option as $transaction_detail_option) {
				$product_option_name = $transaction_detail_option->product_option_name;
				$product_option_value = (string)$transaction_detail_option->product_option_value;
				$price_mod = (double)$transaction_detail_option->price_mod;
				$weight_mod = (double)$transaction_detail_option->weight_mod;
 
 
 
 
			}
 
 
 
 
 
			//If you have custom code to run for each product, put it here:
 
 
 
 
 
 
 
 
 
 
		}
 
		//If you have custom code to run for each order, put it here:
 
 
 
 
 //////// PW code is supposed to be here 
 
 
 
 
 
 
 
	}
 
	//All Done!
	die("foxy");
 
 
 
 
 
 
 
//-----------------------------------------------------
// NO POST CONTENT SENT
//-----------------------------------------------------
} else {
	die('No Content Received From Datafeed');
}
 
 
 
 
//Decrypt Data From Source
function foxycart_decrypt($src) {
    	global $apikey;
	return rc4crypt::decrypt($apikey,urldecode($src));
}
 
 
// ======================================================================================
// 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.
 *
 * © 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
 */
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);
	}
}

Just let me know if it works for you. I can´t get it working.

Link to comment
Share on other sites

Doesn´t work for me  :'(

Meanwhile i found a different solution, it needs a bit of javascript:

1) In my endpoint set up a script that'd request the transaction info using curl. It needs the transacton id to do so:

<?php
$foxy_domain = "domain.foxycart.com";
$foxyData = array();
$foxyData["api_token"] = "apikey";
$foxyData["api_action"] = "transaction_get";
$foxyData["transaction_id"] = $_GET["fcorderid"];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://" . $foxy_domain . "/api");
curl_setopt($ch, CURLOPT_POSTFIELDS, $foxyData);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
// If you get SSL errors, you can uncomment the following, or ask your host to add the appropriate CA bundle
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$response = trim(curl_exec($ch));
 
// The following if block will print any CURL errors you might have
// if ($response == false) {
//   print "CURL Error: \n" . curl_error($ch);
// } else {
//   print "Response to customer save of " . $foxyData['customer_email'] . "\n";
//   print $response;
// }
curl_close($ch);
 
$foxyResponse = simplexml_load_string($response, NULL, LIBXML_NOCDATA);

	$client = $foxyResponse->transaction->customer_first_name;
	$transaction_id = $foxyResponse->transaction->id;
	$email =  $foxyResponse->transaction->customer_email;

	$product =  $foxyResponse->transaction->transaction_details->transaction_detail->product_name;
	$price =  $foxyResponse->transaction->transaction_details->transaction_detail->product_price;

	foreach ($foxyResponse->transaction->transaction_details->transaction_detail->transaction_detail_options->transaction_detail_option as $option) {
			$product_option_name = $transaction_detail_option->product_option_name;
			$product_option_value = (string)$transaction_detail_option->product_option_value;
			$price_mod = (double)$transaction_detail_option->price_mod;
	}

	print "<pre>";
		print_r($foxyResponse);
	print "</pre>";
		
		$fwd = $pages->get("title=$transaction_id");

		if($fwd->id) {
			$session->redirect($fwd->url);
		} else {
			$p = new Page();
	 		$p->parent = $pages->get("1350");
	 		$p->template = $templates->get("upload_area");
	 		
	 		$p->title = $transaction_id;
	 		$p->email = $foxyResponse->transaction->customer_email;
	 		$p->fcdate = $foxyResponse->transaction->transaction_date;
	 		$p->fcfirst = $foxyResponse->transaction->customer_first_name;
	 		$p->fclast = $foxyResponse->transaction->customer_last_name;
	 		$p->fcprice = $foxyResponse->transaction->order_total;
	 		$p->fcproduct = $foxyResponse->transaction->transaction_details->transaction_detail->product_name;

	 		$p->save();
	 		echo $p->render();
	 	}

2) In the Foxycart receipt template I use a bit of javascript to get the transaction id and send it with a hidden form and a hidden target iframe to avoid redirection.

Link to comment
Share on other sites

that's cool - didn't know you could request fc transactions via curl..

not sure why the code i posted didn't work for you though - i tested by re-feeding transactions from FC and was eventually able to have it reply with success and also it was successfully updating the processwire page (in my case decrementing the inventory)//

did you include all of the scripts;

// get these from http://modx.com/extras/package/foxycartinventory

$rc4cryptPath = "/path/to/class.rc4crypt.php";
$xmlParserPath = "/path/to/class.xmlparser_php5.php";

i also have datafeed log working but commented out, since it's only needed for testing...

anyway thanks for posting your solution, that could be useful at some point in the future...

Link to comment
Share on other sites

Finally! It works!

Well it was about slashes... when including the scripts 

$rc4cryptPaht = "/inc/clas...."
 

 doesn´t work

$rc4cryptPaht = "inc/clas...."
 

it works. Same for the parser.

Addtionally in the Foxycart settings, the datafeed URL needs a slash at the end, otherwise returns an error:

http://site.com/feed/
 

Now, it returns 'foxy' and creates the page in PW.  :)

ehem, as a designer, these "subtleties" don´t seem always that important.

Your script is working great, thank you.

Maybe you could post this Foxycart integration to their docs: https://wiki.foxycart.com/integration/start

Link to comment
Share on other sites

hi alejandro - cool - glad that's working... and you reminded me that i probably don't need to put the server path, that's why i had the leading slashes, i can include those liek you did since they are in the templates directory...

Link to comment
Share on other sites

  • 6 years later...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...