Dont forget the fix mentioned here:
http://www.zen-cart.com/forum/showpo...&postcount=534
Since the new update on download section has not been up yet.
Printable View
Dont forget the fix mentioned here:
http://www.zen-cart.com/forum/showpo...&postcount=534
Since the new update on download section has not been up yet.
1. Make sure you use the most recent version (if you want me to check it for you, pm me the ftp info)
using rev235. Is that the latest?
2. Make sure you updated all the patches for paypal ipn (especially if you are on version prior to 1.3.8)
Im using 138 are there any new patches needed?
3. Check if you are using CURL proxy,
No I'm not using should I?
4. Check if you entered your email correctly in paypal setting (the paypal email you use in zc admin has to be exactly the same with the one you have on your paypal profile, and it is CASE SENSITIVE)
It still does everything right except the return part to the store which registers the order(which works fine btw with any other payment mod)
5. Check if you entered the correct PDT token
double checked
Also, turn on the debug, make sure you also do this to turn on extra debug as well:
create a new file:
/includes/extra_datafiles/ipn_debug_on.php
with this content:
<?php define('IPN_EXTRA_DEBUG_DETAILS', 'All');
And what does the debug say? :)
EMAIL 1
IPN INFO - POST VARS to be sent back for validation:
To: www.paypal.com:80
POST /cgi-bin/webscr HTTP/1.1
Host: www.paypal.com
Content-type: application/x-www-form-urlencoded
Content-length: 84
Connection: close
Array
(
[cmd] => _notify-sync
[tx] =>
[at] => 9e6t3**********vm9S0
)
EMAIL 2:
IPN INFO - Confirmation/Validation response
FAIL
EMAIL 3:
PDT Returned Data Array
(
)
EMAIL 4:
IPN WARNING :: PDT Transaction was not marked as SUCCESS. Keep this report
for potential use in fraud investigations.
IPN Info =
FAIL
EMAIL 5:
PDT WARNING :: Order not marked as "Completed". Check for Pending reasons
or wait for IPN to complete.
[payment_status] =>
[pending_reason] =>
EMAIL 6:
PDT WARNING :: Transaction already exists. Perhaps IPN already added it.
PDT processing ended.
Is it because I tried to order same test product earlier?
Failed from the 2nd stage already, overwrite this file content:
includes/init_includes/init_ssu.php by this, then post the log again
PHP Code:
<?php
/**
* Simple SEO URL
* $Author: yellow1912 $
* $Rev: 257 $
* @license http://www.zen-cart.com/license/2_0.txt GNU Public License V2.0
*/
class simple_seo_url extends yclass {
var $cache_folder;
var $pc_cache_folder; // parameter caching, currently use for links contain products_id and cPath only to avoid database loading
var $categories_cache_folder;
var $products_cache_folder;
var $catalog_dir;
var $link;
var $extension;
var $category_identifier;
// these 2 vars store categories/products name in array so we dont have to query/retrieve them more than once per page load
var $categories_names = array();
var $products_names = array();
var $exclude_list;
var $products_handlers = array();
var $products_identifiers = array();
// this is a php4 constructor
function simple_seo_url(){
// This is here to support multiple product types shorthand links
// if you want to add support for your custom type, first you have to go to your database and check table products_types
$this->products_handlers = array('1'=> 'product_info',
'2'=> 'product_music_info',
'3'=> 'document_general_info',
'4'=> 'document_product_info',
'5'=> 'product_free_shipping_info',
'6'=> 'document_website_info'
);
$this->products_identifiers = array('1'=> SSU_ID_DELIMITER.'p'.SSU_ID_DELIMITER,
'2'=> SSU_ID_DELIMITER.'m'.SSU_ID_DELIMITER,
'3'=> SSU_ID_DELIMITER.'g'.SSU_ID_DELIMITER,
'4'=> SSU_ID_DELIMITER.'d'.SSU_ID_DELIMITER,
'5'=> SSU_ID_DELIMITER.'f'.SSU_ID_DELIMITER,
'6'=> SSU_ID_DELIMITER.'w'.SSU_ID_DELIMITER
);
// set some vars
$this->category_identifier = (((int)SSU_MAX_LEVEL > 0) ? SSU_ID_DELIMITER : '').'c'.SSU_ID_DELIMITER;
// the list of pages we want to exclude from using ssu links
$this->exclude_list = explode(',',SSU_EXCLUDE_LIST);
// attempt to check current protocol being used
// $request_type is a global var of ZC
global $request_type;
if($request_type == 'NONSSL'){
$this->catalog_dir = DIR_WS_CATALOG;
$this->link = HTTP_SERVER.DIR_WS_CATALOG;
}
else{
$this->catalog_dir = DIR_WS_HTTPS_CATALOG;
$this->link = HTTPS_SERVER.DIR_WS_HTTPS_CATALOG;
}
$this->cache_folder = DIR_FS_SQL_CACHE."/ssu/";
$this->pc_cache_folder = $this->cache_folder.'pc/';
$this->categories_cache_folder = $this->cache_folder.'categories/';
$this->products_cache_folder = $this->cache_folder.'products/';
$this->extension = SSU_FILE_EXTENSION;
$this->extension = trim($this->extension);
}
// this function will rebuild $_GET array
// it will also take care of redirection if needed
function parse_url(){
global $request_type;
$mr_regex = array($this->catalog_dir);
if(!empty($this->extension)) $mr_regex[] = '/\./'.$this->extension.'/';
// we want to remove $catalog_dir and extension if any
// we preg_replace because we want to limit the number of replacement, maybe there is a better way to to this?
$mr = trim(($this->catalog_dir=='/' && empty($this->extension)) ? getenv('REQUEST_URI') : preg_replace($mr_regex,'',getenv('REQUEST_URI'), 1),'/');
$mr_parts = explode('/', current(explode('?', str_replace(array('&','&','=','?'),'/',$mr))));
// here we check if this is a product info link by searching for category and product identifier.
if(strpos($mr_parts[0],$this->category_identifier) !== false){
if(($product_type = $this->_get_product_handler($mr_parts[1])) != false)
$_get['main_page'] = $product_type;
else
$_get['main_page'] = 'index';
}
elseif(($product_type = $this->_get_product_handler($mr_parts[0])) != false)
$_get['main_page'] = $product_type;
else{
$_get['main_page'] = isset($_GET['main_page'])? $_GET['main_page'] : $mr_parts[0];
unset($mr_parts[0]);
$mr_parts = array_values($mr_parts);
}
if($this->_check_exclude_list($_get['main_page'])) return;
// if the query string is there, lets rebuild the path and redirect our users
if(strpos($mr,'index.php?') !== false){
$params = str_replace('index.php?','', $mr);
$this->_redirect($this->ssu_link('',$params,$request_type));
}
$mr_parts_count = count($mr_parts);
// TODO: tidy up this, improve performance
for($i= 0; $i < $mr_parts_count; $i++){
// TODO: a more efficient way to do this?
if(strpos($mr_parts[$i],$this->category_identifier) !== false){
$_get['cPath'] = end(explode(SSU_ID_DELIMITER, $mr_parts[$i]));
}
elseif($this->_get_product_handler($mr_parts[$i]) !== false){
$_get['products_id'] = end(explode(SSU_ID_DELIMITER, $mr_parts[$i]));
}
elseif(isset($mr_parts[($i + 1)])){
$_get[$mr_parts[$i]] = $mr_parts[($i + 1)];
$i++;
}
else{
$_get[$mr_parts[$i]] = '';
$i++;
}
}
// if our current link contains the category or product name, we want to make sure the name is correct, otherwise we do a redirection
if(((strpos($mr, $this->category_identifier) !== false) || $this->_get_product_handler($mr) !== false)){
$params = '';
// here we will attempt to rebuild the link using $_get array, and see if it matches the current link
// we want to take out zenid however
$temp = $_get;
$page = '';
if(isset($temp[zen_session_name()])) unset($temp[zen_session_name()]);
if(isset($temp['main_page'])) {$page = $temp['main_page']; unset($temp['main_page']);}
foreach ($temp as $key => $value)
$params .= '&' . $key . '=' . $value;
$link = $this->ssu_params($page, $params, true);
if(strpos($mr,$link) === false)
$this->_redirect($this->link.(!empty($page) ? $page.'/' : '').$link);
}
// set $_GET
$_GET = $_get;
// rebuild $PHP_SELF which is used by ZC in several places
$GLOBALS['PHP_SELF'] = $this->catalog_dir.'index.php';//print_r($_get);die();
}
function _redirect($link){
if($link === false) $link = $this->link;
Header( "HTTP/1.1 301 Moved Permanently" );
Header( "Location: $link" );
exit;
}
// this function builds the ssu links
function ssu_link($page = '', $parameters = '', $connection = 'NONSSL', $add_session_id = true, $search_engine_safe = true, $static = false, $use_dir_ws_catalog = true){
global $request_type, $session_started, $http_domain, $https_domain;
// if this is anything other than index.php, dont ssu it
if(strpos($page, '.php') !== false && strpos($page, 'index.php') === false)
return false;
if($page=='index' && empty($parameters)) return false;
// this is for the way ZC builds ezpage links. $page is empty and $parameters contains main_page
// remember. non-static links always have index.php?main_page=
// so first we check if this is static
if(strpos($page, 'main_page=') !== false){
$parameters = $page;
}
// remove index.php? if exists
if(($index_start = strpos($parameters, 'index.php?')) !== false) $parameters = substr($parameters, $index_start+10);
if(($main_page_start = strpos($parameters, 'main_page=')) !== false){
$main_page_end = strpos($parameters.'&', '&', $main_page_start);
$page = substr($parameters, $main_page_start+10, $main_page_end-$main_page_start-10);
$parameters = (($main_page_start > 1) ? substr($parameters, 0, $main_page_start-1) : '' ).substr($parameters, $main_page_end);
}
if(empty($page))
return false;
if($this->_check_exclude_list($page)) return false;
if(($parameters = $this->ssu_params($page, $parameters)) === false)
return false;
$link = '';
// build the http://www.site.com
$link = HTTP_SERVER;
if ($connection == 'SSL' && ENABLE_SSL == 'true')
$link = HTTPS_SERVER ;
// attach the shop folder if any -> http://www.site.com/shop
if ($use_dir_ws_catalog) {
if ($connection == 'SSL' && ENABLE_SSL == 'true') {
$link .= DIR_WS_HTTPS_CATALOG;
} else {
$link .= DIR_WS_CATALOG;
}
}
$sid = '';
// Build session id
if ( ($add_session_id == true) && ($session_started == true) && (SESSION_FORCE_COOKIE_USE == 'False') ) {
if (defined('SID') && zen_not_null(SID)) {
$sid = SID;
} elseif ( ( ($request_type == 'NONSSL') && ($connection == 'SSL') && (ENABLE_SSL == 'true') ) || ( ($request_type == 'SSL') && ($connection == 'NONSSL') ) ) {
if ($http_domain != $https_domain) {
$sid = zen_session_name() . '=' . zen_session_id();
}
}
}
//$sid = str_replace('=','/',$sid);
// Any param? No? return page.extension
if(empty($parameters)){
if(empty($page))
return false;
elseif(empty($sid))
return $link.$page.(trim(SSU_FILE_EXTENSION)=='' ? '' : '.'.SSU_FILE_EXTENSION);
else
return $link.$page.'?'.$sid;
}
$link .= $page.(!empty($page) ? '/' : '').$parameters;
while (substr($link, -1) == '/') $link = substr($link, 0, -1);
// append sid
if(!empty($sid))
$link .= '?'.$sid;
return $link;
}
// this function takes the parameters in the query string and turns that to our nice looking link
function ssu_params(&$page, $parameters, $re_calculate_cpath= false){
$parameters = trim($parameters,' ?&');
$cachefile = $this->pc_cache_folder.md5($page.$parameters);
$params = '';
$use_cache = false;
$set_cache = false;
$is_product_info_page = in_array($page, $this->products_handlers);
$is_category_page = ($page == 'index' && (strpos($parameters,'cPath=') !==false));
if(!empty($parameters)){
// if this contains cPath or products_id lets attempt to use cache
if((strpos($parameters,'cPath=') !== false) || (strpos($parameters,'products_id=') !== false)){
$use_cache = true;
if (($params = @file_get_contents($cachefile)) === false)
$set_cache = true;
}
// process params if we dont use cache or need to reset cache
if(!$use_cache || $set_cache){
// No need for '?'
$parameters = str_replace('/', '%2f', $parameters);
$parameters = str_replace(array('&','&','=','?'),'/',$parameters);
// clean up
//$parameters = preg_replace('/\/\/+/', '/', $parameters);
$parameters = trim($parameters, '/');
$cPath='';
$products_id='';
// Appending category name and product name into the link
$parameters = explode('/',$parameters);
$param_counter = count($parameters);
// we want to make sure the order of params for product info and category pages
if($is_product_info_page)
$params = "%s/%p";
elseif($is_category_page)
$params = "%s";
for($i=0; $i < $param_counter; $i++){
if($parameters[$i]=='cPath'){
$cPath = $parameters[++$i];
if(!$is_product_info_page && !$is_category_page)
$params .= "/%s";
}
elseif($parameters[$i]=='products_id'){
$products_id = $parameters[++$i];
if(!$is_product_info_page && !$is_category_page)
$params .= "/%p";
}
elseif(!empty($parameters[$i]) && !empty($parameters[$i+1])){
$params .= "/".$parameters[$i]."/".$parameters[++$i];
}
else
$i++;
}
// dont trust the info passed to us, always rebuild the cPath in this case
if($is_product_info_page && !empty($products_id)){
$cPath = zen_get_product_path($products_id);
}
elseif(!empty($cPath) && $re_calculate_cpath && !empty($products_id)){
$cPath = zen_get_product_path($products_id);
}
if(!empty($cPath)){
$params = str_replace('%s', $this->get_category_name($cPath), $params);
}
if(!empty($products_id)){
$params = str_replace('%p', $this->get_product_name($products_id), $params);
}
}
}
if($is_product_info_page || $is_category_page)
$page ='';
$params = trim($params, '/');
if($set_cache) $this->_write_to_file($cachefile, trim($params,'/')); // we cache the whole link so that we dont have to recalculate it again
// TODO: change the way we check if the language is already in the link
$params .= (isset($_SESSION['languages_code']) && strpos($params, 'language/') === false && $_SESSION['languages_code'] != DEFAULT_LANGUAGE) ? '/language/' . $_SESSION['languages_code'] : '';
return $params;
}
// We store the name in files because we dont want to query database too many times (which will kill the server)
// This is how the files are named:
// for category: 'c' followed by category id
// for product : 'p' followed by product id
function get_category_name($cPath){
if((int)SSU_MAX_LEVEL <= 0)
return 'c'.SSU_ID_DELIMITER.$cPath;
if(empty($cPath)) return '';
if(isset($this->categories_names[$cPath])) return $this->categories_names[$cPath];
$file_name = $this->build_category_filename($cPath);
// lets attempt to retrieve from file first, shall we?
if(($content = @file_get_contents($this->categories_cache_folder.$file_name)) == false)
$content = $this->set_category_name($cPath);
$this->categories_names[$cPath] = $content;
return $content;
}
// this function retrieves categories names from db, save it to file, returns it
function set_category_name($cPath){
$category_ids = explode('_',$cPath);
$cat_counter = count($category_ids);
$i = $cat_counter-(int)SSU_MAX_LEVEL;
if($i < 0) $i = 0;
$result = '';
// this may not be the best way to build the category name, but we do this once per cPath only
while($i<=($cat_counter-1)){
$sql_query = 'SELECT categories_name FROM '.TABLE_CATEGORIES_DESCRIPTION.' WHERE categories_id ='.$category_ids[$i].' LIMIT 1';
$result .= SSU_NAME_DELIMITER.$this->_get_name_from_db($file_name,$sql_query,'categories_name');
$i++;
}
$result = trim($this->_parse_name($result), SSU_NAME_DELIMITER);
$result = $result.$this->category_identifier.$cPath;
// now append the id
$file_name = $this->build_category_filename($cPath);
// write to file EVEN if we get an empty content
$this->_write_to_file($this->categories_cache_folder.$file_name, $result);
return $result;
}
function build_category_filename($cPath){
if(!empty($cPath))
return "c$cPath";
else
return "";
}
function get_product_name($products_id){
$products_id = zen_get_prid($products_id);
if(empty($products_id)) return '';
if(isset($this->products_names[$products_id])) return $this->products_names[$products_id];
$file_name = $this->build_product_filename($products_id);
if(($content = @file_get_contents($this->products_cache_folder.$file_name)) == false)
$content = $this->set_product_name($products_id);
$this->products_names[$products_id] = $content;
return $content;
}
// this function retrieves categories names from db, save it to file, returns it
function set_product_name($products_id){
$file_name = $this->build_product_filename($products_id);
$sql_query = 'SELECT d.products_name, p.products_type FROM '.TABLE_PRODUCTS.' p, '.TABLE_PRODUCTS_DESCRIPTION.' d WHERE p.products_id ='.$products_id.' AND d.products_id= p.products_id LIMIT 1';
$result = $this->_get_name_from_db($file_name,$sql_query);
$result = $this->_parse_name($result->fields['products_name']).$this->products_identifiers[$result->fields['products_type']].$products_id;
// write to file EVEN if we get an empty content, that just means the productname is blank
$this->_write_to_file($this->products_cache_folder.$file_name, $result);
return $result;
}
function build_product_filename($products_id){
return "p$products_id";
}
// this function attempts to get the name of the current product/category name from database
function _get_name_from_db($file_name, $sql_query, $field_name = ''){
global $db;
$result = '';
$sql_result = $db->Execute($sql_query);
if($sql_result->RecordCount() > 0){
if(!empty($field_name))
$result = $sql_result->fields[$field_name];
else
$result = $sql_result;
}
return $result;
}
// this function parses the name we retrieve from database
function _parse_name($name){
// Remove short words first
if(($word_length = (int)SSU_MINIMUM_WORD_LENGTH - 1) > 0)
$name = preg_replace('/\b\w{1,'.$word_length.'}\b\s*/', '', $name); // TODO: improve this mechanism
// trim the sentence
if ((int)SSU_MAX_NAME_LENGTH > 0 && strlen($name) > (int)SSU_MAX_NAME_LENGTH){
preg_match('/(.{' . (int)SSU_MAX_NAME_LENGTH . '}.*?)\b/', $name, $matches);
$name = rtrim($matches[1]);
}
// we replace any non alpha numeric character by the name delimiter
$name = preg_replace("/[^a-zA-Z0-9s]/", SSU_NAME_DELIMITER, $name);
// remove excess SSU_NAME_DELIMITER
$name = preg_replace('/'.SSU_NAME_DELIMITER.SSU_NAME_DELIMITER.'+/', SSU_NAME_DELIMITER, $name);
// remove anything that looks like our identifiers in the name
$name = str_replace($this->category_identifier, '', $name);
foreach($this->products_identifiers as $products_identifier)
$name = str_replace($products_identifier, '', $name);
// remove trailing _
$name = strtolower(trim($name, SSU_NAME_DELIMITER));
return $name;
}
// this function writes the category/product name to the approriate file so we dont have to query database over and over
function _write_to_file($file_link, $content){
// write into file. Cant use file_put_contents since it's not in php4
if($handle = @fopen($file_link, "w")){
if (fwrite($handle, $content) === FALSE) {
// TODO: sound the alarm
}
fclose($handle);
}
else{
// TODO: sound the alarm here
}
}
// this function checks if the page is in the excluded list
function _check_exclude_list($page){
if(in_array($page, $this->exclude_list))
return true;
}
function _get_product_handler($string){
foreach($this->products_identifiers as $key => $value){
if(strpos($string, $value) !== false) return $this->products_handlers[$key];
}
return false;
}
}
first came up with this when returning from paypal
2006 MySQL server has gone away
in:
[SELECT count(*) as count FROM subscribers WHERE email_address = '[email protected]']
!!!! = ?? I didnt get to confirmation page before pushing refresh. Btw should I enter the paypal return address to zzz.com/store/checkout_success? or store/index.php?main_page=checkout_process?
DEBUG EMAILS (email etc changed):
IPN INFO - POST VARS to be sent back for validation:
To: www.paypal.com:80
POST /cgi-bin/webscr HTTP/1.1
Host: www.paypal.com
Content-type: application/x-www-form-urlencoded
Content-length: 101
Connection: close
Array
(
######[cmd] => _notify-sync
######[tx] => 7DH37035JU2103918
######[at] => 9e6t3**********vm9S0
)
IPN INFO - POST VARS to be sent back for validation:
To: www.paypal.com:80
POST /cgi-bin/webscr HTTP/1.1
Host: www.paypal.com
Content-type: application/x-www-form-urlencoded
Content-length: 101
Connection: close
Array
(
######[cmd] => _notify-sync
######[tx] => 7DH37035JU2103918
######[at] => 9e6t3**********vm9S0
)
IPN INFO - Confirmation/Validation response
SUCCESS
PDT Returned Data Array
(
######[payment_date] => 09:43:58 Sep 04, 2008 PDT
######[txn_type] => web_accept
######[last_name] => ZZZ
######[receipt_id] => ZZZ
######[residence_country] => FI
######[item_name] => ZZZ Purchase
######[payment_gross] =>
######[mc_currency] => EUR
######[business] => [email protected]
######[payment_type] => instant
######[protection_eligibility] => None
######[payer_status] => unverified
######[tax] => 0.00
######[payer_email] => [email protected]
######[txn_id] => 7DH37035JU2103918
######[quantity] => 1
######[receiver_email] => [email protected]
######[first_name] => XXX
######[payer_id] => WYUXEH8VBQKR6
######[receiver_id] => 987AEXXXX
######[item_number] => Store Receipt
######[payment_status] => Completed
######[payment_fee] =>
######[mc_fee] => 0.38
######[shipping] => 0.00
######[mc_gross] => 1.00
######[custom] => zenid=bb2a0838921a1562f5d66f0caa760ad7
######[charset] => windows-1252
)
IPN INFO :: Transaction email details.
From IPN = [email protected]
From CONFIG = [email protected]
IPN WARNING :: Currency/Amount Mismatch. ##Details:
PayPal email address = [email protected]
| mc_currency = EUR
| submitted_currency = EUR
| order_currency =
| mc_gross = 1.00
| converted_amount = 0.00
| order_amount =
argh... I do feel soooo stupid right now. I've been looking the new layout that long that totally forgot that I have maintenance on.. Many thanks for your effort and help though yello - much appreciated as always.
Hey Yellow, do you know if this is compatible with Google Base Feeder by numinix or the XML sitemap generator?
Thanks.
Yup, all feeders and rss,
Yup, all feeders and rss. In short, any thing that uses zencart href function.