Jump to content



Photo

Enable PayPal Express Checkout, quick question


  • Please log in to reply
17 replies to this topic

#1   pedgette

pedgette
  • Members
  • 79 posts
  • Real Name:P.E.

Posted 13 January 2008 - 10:06

I have uploaded all of the new files to the correct directories and have done the manual file edits using this guide, http://svn.oscommerc...ommerce?cs=1699

I went into admin>modules>payment and enabled the module and inserted by sandbox info. I then went to my site and test it, the paypal image shows up fine but when I click on it I'm redirected to login.php. I copied the URL that the button links to and it is: mysite.com/ext/modules/payment/paypal/express.php

I'm not sure where I went wrong, any insight would be great. Thanks.

#2   pedgette

pedgette
  • Members
  • 79 posts
  • Real Name:P.E.

Posted 13 January 2008 - 10:18

Ok, I guess I thought this worked a little differently and no longer required a user to have an account to make a purchase. But after I was redirected to the login page I used one of my testing login names and finished a payment through the sandbox server, is this exactly how it's suppose to work? Thanks for your time.

#3 ONLINE   Harald Ponce de Leon

Harald Ponce de Leon

    Healthy Giraffe

  • Core Team
  • 4,845 posts
  • Real Name:Harald Ponce de Leon
  • Gender:Male
  • Location:Solingen, Germany

Posted 13 January 2008 - 11:49

Hi Paul..

Yes, that is correct behaviour of the module. It is not the preferred manner for the Express Checkout payment method however many more codebase changes would be required to properly support a purchase without a customer account.

The codebase changes going in 2.2 RC1/RC2/Final are minimal changes to ease the upgrade procedure for existing store owners. Although we are focusing the bigger changes for the 3.0 release, a 2.3 release will also be made where such features will be available on based on the 2.2 core. (The 3.0 release has a new core that is not compatible to 2.2)
Harald Ponce de Leon

#4   NewToon

NewToon
  • Members
  • 5 posts
  • Real Name:Guido Neuland

Posted 15 January 2008 - 20:09

Thanks very much for bringing the paypal express checkout to life!

I have a question: If I run the order with the new paypal express checkout there is no billing adress
afterwards and the following error-code inside the checkout_confirmation.php:

htmlspecialchars() expects parameter 1 to be string, array given....


Does anybody have an idea why that happens? It´s just if I use the new paypal payment.

Kind regards from Germany

Guido

#5   kms

kms
  • Members
  • 6 posts
  • Real Name:Sameer

Posted 27 January 2008 - 16:55

Hi

I installed this contribution, and am receiving this error on shopping_cart.php

Fatal error: Call to undefined method: payment->checkout_initialization_method()

I have checked my code changes, and am missing something.

Appreciate your help with identifying it.

thanks

Sam

#6   kms

kms
  • Members
  • 6 posts
  • Real Name:Sameer

Posted 27 January 2008 - 18:13

Please ignore my previous message _ i found the error I had made while incorporating the changes in /includes/classes/payment.php
thanks

#7   Bharat

Bharat
  • Members
  • 8 posts
  • Real Name:Bharat

Posted 01 February 2008 - 18:56

Please ignore my previous message _ i found the error I had made while incorporating the changes in /includes/classes/payment.php
thanks


Hi Sameer,
I have the same problem but can't trace what I have done wrong.... what was the error you made and sorted? Any pointers useful.

#8   cbx040

cbx040
  • Members
  • 27 posts
  • Real Name:Simon
  • Location:Northamptonshire, UK

Posted 12 February 2008 - 15:18

This function needs to be added to

/includes/classes/payment.php

[codebox]
function checkout_initialization_method() {
$initialize_array = array();



if (is_array($this->modules)) {

reset($this->modules);

while (list(, $value) = each($this->modules)) {

$class = substr($value, 0, strrpos($value, '.'));

if ($GLOBALS[$class]->enabled && method_exists($GLOBALS[$class], 'checkout_initialization_method')) {

$initialize_array[] = $GLOBALS[$class]->checkout_initialization_method();

}

}

}



return $initialize_array;

}

[/codebox]

#9   jamesblackburn

jamesblackburn
  • Members
  • 75 posts
  • Real Name:James Blackburn
  • Location:UK

Posted 29 February 2008 - 11:44

I have installed the contribution by making all the code changes to add PayPal express but get the error below in admin.

Fatal error: Cannot redeclare class paypal_express in /modules/payment/paypal_express.php on line 13

I have double checked that all the code is correct. The only file I think might be incorrect is the classes/order.php??

Can anyone tell me where I have gone wrong?


[codebox] $customer_address_query = tep_db_query("select c.customers_firstname, c.customers_lastname, c.customers_telephone, c.customers_email_address, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, co.countries_id, co.countries_name, co.countries_iso_code_2, co.countries_iso_code_3, co.address_format_id, ab.entry_state from " . TABLE_CUSTOMERS . " c, " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " co on (ab.entry_country_id = co.countries_id) where c.customers_id = '" . (int)$customer_id . "' and ab.customers_id = '" . (int)$customer_id . "' and c.customers_default_address_id = ab.address_book_id");

$customer_address = tep_db_fetch_array($customer_address_query);

if (is_array($sendto) && !empty($sendto)) {
$shipping_address = array('entry_firstname' => $sendto['firstname'],
'entry_lastname' => $sendto['lastname'],
'entry_company' => $sendto['company'],
'entry_street_address' => $sendto['street_address'],
'entry_suburb' => $sendto['suburb'],
'entry_postcode' => $sendto['postcode'],
'entry_city' => $sendto['city'],
'entry_zone_id' => $sendto['zone_id'],
'zone_name' => $sendto['zone_name'],
'entry_country_id' => $sendto['country_id'],
'countries_id' => $sendto['country_id'],
'countries_name' => $sendto['country_name'],
'countries_iso_code_2' => $sendto['country_iso_code_2'],
'countries_iso_code_3' => $sendto['country_iso_code_3'],
'address_format_id' => $sendto['address_format_id'],
'entry_state' => $sendto['zone_name']);
} else {


$shipping_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$sendto . "'");

$shipping_address = tep_db_fetch_array($shipping_address_query);

}


if (is_array($billto) && !empty($billto)) {
$billing_address = array('entry_firstname' => $billto['firstname'],
'entry_lastname' => $billto['lastname'],
'entry_company' => $billto['company'],
'entry_street_address' => $billto['street_address'],
'entry_suburb' => $billto['suburb'],
'entry_postcode' => $billto['postcode'],
'entry_city' => $billto['city'],
'entry_zone_id' => $billto['zone_id'],
'zone_name' => $billto['zone_name'],
'entry_country_id' => $billto['country_id'],
'countries_id' => $billto['country_id'],
'countries_name' => $billto['country_name'],
'countries_iso_code_2' => $billto['country_iso_code_2'],
'countries_iso_code_3' => $billto['country_iso_code_3'],
'address_format_id' => $billto['address_format_id'],
'entry_state' => $billto['zone_name']);
} else {
$billing_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$billto . "'");
$billing_address = tep_db_fetch_array($billing_address_query);
}

$tax_address_query = tep_db_query("select ab.entry_country_id, ab.entry_zone_id from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)($this->content_type == 'virtual' ? $billto : $sendto) . "'");
$tax_address = tep_db_fetch_array($tax_address_query);

if ($this->content_type == 'virtual') {
$tax_address = array('entry_country_id' => $billing_address['entry_country_id'],
'entry_zone_id' => $billing_address['entry_zone_id']);
} else {
$tax_address = array('entry_country_id' => $shipping_address['entry_country_id'],
'entry_zone_id' => $shipping_address['entry_zone_id']);
}[/codebox]

#10   jamesblackburn

jamesblackburn
  • Members
  • 75 posts
  • Real Name:James Blackburn
  • Location:UK

Posted 29 February 2008 - 12:44

I have installed the contribution by making all the code changes to add PayPal express but get the error below in admin.

Fatal error: Cannot redeclare class paypal_express in /modules/payment/paypal_express.php on line 13

I have double checked that all the code is correct. The only file I think might be incorrect is the classes/order.php??

Can anyone tell me where I have gone wrong?

$customer_address_query = tep_db_query("select c.customers_firstname, c.customers_lastname, c.customers_telephone, c.customers_email_address, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, co.countries_id, co.countries_name, co.countries_iso_code_2, co.countries_iso_code_3, co.address_format_id, ab.entry_state from " . TABLE_CUSTOMERS . " c, " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " co on (ab.entry_country_id = co.countries_id) where c.customers_id = '" . (int)$customer_id . "' and ab.customers_id = '" . (int)$customer_id . "' and c.customers_default_address_id = ab.address_book_id");      $customer_address = tep_db_fetch_array($customer_address_query);if (is_array($sendto) && !empty($sendto)) {          $shipping_address = array('entry_firstname' => $sendto['firstname'],                                    'entry_lastname' => $sendto['lastname'],                                    'entry_company' => $sendto['company'],                                    'entry_street_address' => $sendto['street_address'],                                    'entry_suburb' => $sendto['suburb'],                                    'entry_postcode' => $sendto['postcode'],                                    'entry_city' => $sendto['city'],                                    'entry_zone_id' => $sendto['zone_id'],                                    'zone_name' => $sendto['zone_name'],                                    'entry_country_id' => $sendto['country_id'],                                    'countries_id' => $sendto['country_id'],                                    'countries_name' => $sendto['country_name'],                                    'countries_iso_code_2' => $sendto['country_iso_code_2'],                                    'countries_iso_code_3' => $sendto['country_iso_code_3'],                                    'address_format_id' => $sendto['address_format_id'],                                    'entry_state' => $sendto['zone_name']);        } else {       $shipping_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$sendto . "'");      $shipping_address = tep_db_fetch_array($shipping_address_query);      }if (is_array($billto) && !empty($billto)) {          $billing_address = array('entry_firstname' => $billto['firstname'],                                   'entry_lastname' => $billto['lastname'],                                   'entry_company' => $billto['company'],                                   'entry_street_address' => $billto['street_address'],                                   'entry_suburb' => $billto['suburb'],                                   'entry_postcode' => $billto['postcode'],                                   'entry_city' => $billto['city'],                                   'entry_zone_id' => $billto['zone_id'],                                   'zone_name' => $billto['zone_name'],                                   'entry_country_id' => $billto['country_id'],                                   'countries_id' => $billto['country_id'],                                   'countries_name' => $billto['country_name'],                                   'countries_iso_code_2' => $billto['country_iso_code_2'],                                   'countries_iso_code_3' => $billto['country_iso_code_3'],                                   'address_format_id' => $billto['address_format_id'],                                   'entry_state' => $billto['zone_name']);        } else {            $billing_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$billto . "'");            $billing_address = tep_db_fetch_array($billing_address_query);        } $tax_address_query = tep_db_query("select ab.entry_country_id, ab.entry_zone_id from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)($this->content_type == 'virtual' ? $billto : $sendto) . "'");        $tax_address = tep_db_fetch_array($tax_address_query); 	          if ($this->content_type == 'virtual') {          $tax_address = array('entry_country_id' => $billing_address['entry_country_id'],                               'entry_zone_id' => $billing_address['entry_zone_id']);        } else {          $tax_address = array('entry_country_id' => $shipping_address['entry_country_id'],                               'entry_zone_id' => $shipping_address['entry_zone_id']);        }


How silly do I feel?! The wrong file had been put in the language folder.

#11   pcdoctoroncall

pcdoctoroncall
  • Members
  • 6 posts
  • Real Name:Ryan

Posted 29 February 2008 - 12:49

Hi Guys, I too am in the process of trying to Integrate Paypal Express as my only method of payment into my partners shop online. I have successfully installed osCommerce latest edition and see that it has icons for Paypal but i have no idea what code needs changed so the buttons show up in the places you'd expect them to be, checkout etc and payments successfully made with the transaction amount obviously going into my partners paypal business account. I looked at the link posted in the First message written in this post but wasn't really sure what i was to do as it just showed you code but didn't say what needed to be done, any help on this would be much appreciated.

#12   applelinks

applelinks
  • Members
  • 64 posts
  • Real Name:Peter Hallen
  • Location:Long Island, New York

Posted 07 March 2008 - 03:27

htmlspecialchars() expects parameter 1 to be string, array given


What worked for us was to look at /catalog/includes/classes/order.php

We had an old version of the file and we missing this code from the file. An eay way to fins what you are missing is download the newest verson of OSC and do a Diff of the files

Hope this helpes


if (is_array($sendto) && !empty($sendto)) {        $shipping_address = array('entry_firstname' => $sendto['firstname'],                                  'entry_lastname' => $sendto['lastname'],                                  'entry_company' => $sendto['company'],                                  'entry_street_address' => $sendto['street_address'],                                  'entry_suburb' => $sendto['suburb'],                                  'entry_postcode' => $sendto['postcode'],                                  'entry_city' => $sendto['city'],                                  'entry_zone_id' => $sendto['zone_id'],                                  'zone_name' => $sendto['zone_name'],                                  'entry_country_id' => $sendto['country_id'],                                  'countries_id' => $sendto['country_id'],                                  'countries_name' => $sendto['country_name'],                                  'countries_iso_code_2' => $sendto['country_iso_code_2'],                                  'countries_iso_code_3' => $sendto['country_iso_code_3'],                                  'address_format_id' => $sendto['address_format_id'],                                  'entry_state' => $sendto['zone_name']);      } elseif (is_numeric($sendto)) {        $shipping_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$sendto . "'");        $shipping_address = tep_db_fetch_array($shipping_address_query);      } else {        $shipping_address = array('entry_firstname' => null,                                  'entry_lastname' => null,                                  'entry_company' => null,                                  'entry_street_address' => null,                                  'entry_suburb' => null,                                  'entry_postcode' => null,                                  'entry_city' => null,                                  'entry_zone_id' => null,                                  'zone_name' => null,                                  'entry_country_id' => null,                                  'countries_id' => null,                                  'countries_name' => null,                                  'countries_iso_code_2' => null,                                  'countries_iso_code_3' => null,                                  'address_format_id' => 0,                                  'entry_state' => null);      }      if (is_array($billto) && !empty($billto)) {        $billing_address = array('entry_firstname' => $billto['firstname'],                                 'entry_lastname' => $billto['lastname'],                                 'entry_company' => $billto['company'],                                 'entry_street_address' => $billto['street_address'],                                 'entry_suburb' => $billto['suburb'],                                 'entry_postcode' => $billto['postcode'],                                 'entry_city' => $billto['city'],                                 'entry_zone_id' => $billto['zone_id'],                                 'zone_name' => $billto['zone_name'],                                 'entry_country_id' => $billto['country_id'],                                 'countries_id' => $billto['country_id'],                                 'countries_name' => $billto['country_name'],                                 'countries_iso_code_2' => $billto['country_iso_code_2'],                                 'countries_iso_code_3' => $billto['country_iso_code_3'],                                 'address_format_id' => $billto['address_format_id'],                                 'entry_state' => $billto['zone_name']);      } else {        $billing_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$billto . "'");        $billing_address = tep_db_fetch_array($billing_address_query);      }      if ($this->content_type == 'virtual') {        $tax_address = array('entry_country_id' => $billing_address['entry_country_id'],                             'entry_zone_id' => $billing_address['entry_zone_id']);      } else {        $tax_address = array('entry_country_id' => $shipping_address['entry_country_id'],                             'entry_zone_id' => $shipping_address['entry_zone_id']);      }

----------------------------
Long Island, New York

#13   jasper77

jasper77
  • Members
  • 10 posts
  • Real Name:Jessica Balew

Posted 08 March 2008 - 01:30

Hi Paul..

Yes, that is correct behaviour of the module. It is not the preferred manner for the Express Checkout payment method however many more codebase changes would be required to properly support a purchase without a customer account.

The codebase changes going in 2.2 RC1/RC2/Final are minimal changes to ease the upgrade procedure for existing store owners. Although we are focusing the bigger changes for the 3.0 release, a 2.3 release will also be made where such features will be available on based on the 2.2 core. (The 3.0 release has a new core that is not compatible to 2.2)




Hi Harald and hi everybody!


I have installed the Paypal Express Checkout, and I've got the PayPal module from before. The problem is that I can't see any difference between them two. The only difference when i made few transactions with Paypal Express Checkout is thet after the paypal website brings you back to your order confirmation page, also meantime you can go and put more items in your basket you can browse other pages and at the end when you go to checkout and select again the radio button for Paypal Express Checkout will place the order straight away.


Ok, but where is the problem?


When you are buying with or without account (I've got the two options) you can select the desired shipping address which can be different than your paypal address. So after purchase you will receive the Instant Payment notification from paypal and you will see the pending (by default) order status in your admin panel. BUT, in your mail box the email from paypal will show you the registered address of the buyer - the address in his PayPal account, not the address entered by him in the delivery information meantime in your admin panel you will see the address entered by the buyer which is very confusing????

PayPal email will say Delivery Information: ABC.... Admin Panel will show: Shipping Address: XYZ.....


two completely different things?



I don't think buyers will go to their paypal account and change addresses, they will automatically click continue.



There can be two options: Either this is a big problem that need to be sorted, or I am missing something???



Please guys I need your help?



Many thanks!

#14   Cat_a_log

Cat_a_log
  • Members
  • 2 posts
  • Real Name:Alessandro

Posted 19 August 2009 - 16:40

Hi,
I installed the module but I have a problem when I return from the paypal page to checkout_confirmation, essentially in session I haven't the same data of payment and shipping selected before being directed to paypal.
Any suggestions?
Sorry for my bad english

#15   Richard Cranium

Richard Cranium
  • Members
  • 144 posts
  • Real Name:David

Posted 14 January 2010 - 22:42

Thanks very much for bringing the paypal express checkout to life!

I have a question: If I run the order with the new paypal express checkout there is no billing adress
afterwards and the following error-code inside the checkout_confirmation.php:

Does anybody have an idea why that happens? It´s just if I use the new paypal payment.


I'm having this same problem. So far, I have narrowed down the problem to the fact that when PayPal returns to the site at checkout_confirmation.php, $billto has the billing address (from PayPal) but the $order array is empty. This leads me to believe that somewhere there's supposed to be an exchange of info and it's not happening. I suspect that $order is theoretically supposed to have the store's default address in it. This would explain the comments above regarding the different addresses of PayPal returning versus what the OSC store says is the "billing" or "shipping" address.

I have not focused on the shipping address yet (produces same htmlspecialchars message) until I isolate the billing addr problem. I would bet they are related. I can see this error is produced because the $order array is emtpy and there is no trap for this condition (when the $order var is null) before calling the htmlspecialchars function.

I am contemplating adding code to force the store to use PayPal's shipping and billing addresses for the order, but I have not looked into that yet. Obviously that raises issues if either address is or is not already in the database. Anyone have any suggestions on where I ought to look for the problem that is causing the $order array to get lost? At least that would get it working and I'll then just have the conundrum raised above over whether to ignore the PayPal address entry or not.

I hope that all make sense. It's a somewhat complex issue considering the priority of which address to use if they are different. First I would just like suggestions on how to get back to square one and get $order working.

If no one replies, I'll see what I can sort out and post my findings.

David

#16   Richard Cranium

Richard Cranium
  • Members
  • 144 posts
  • Real Name:David

Posted 18 January 2010 - 14:21

Ok... after many hours of tinkering I found a solution and fixed a few bugs. Perhaps this info will benefit someone else as well. It has taken me hours to figure out what the problem was.

Short answer: there’s a variable that is used in 3 different ways – either as an array, a Boolean (true/false), or as an integer. The fact that the same variable is global and used in three possible ways is IMHO a poor idea, but it’s how some critical back-end components work for the shopping cart and checkout so I had to find a way to work around it without breaking anything. The fix is short and simple to implement.

What I found is in my case the problem is that the order delivery and billing information are not populated during the PayPal Express Checkout process. Shipping (Delivery) info is gleaned from PayPal’s servers, but the OSC routines that grab this data are not invoked during the process. This causes all kinds of problems. First, no shipping cost is calculated. Second, you end up with these error messages on the checkout_confirmation.php page. Since the user has to be logged in to get the PayPal Express Checkout icon, it seems logical that OSC should know this info, but it is not getting passed into the right variables to make everything run smoothly ($order->delivery array is empty when it gets to checkout_confirmation.php). To fix this we need to establish the shipping/delivery address before the code gets to checkout_confirmation.php. It must be setup prior to the shipping calculation (unless you want to give all of your PayPal Express customers free shipping).

IMHO, the PayPal shipping address should trump what the OSC store has on file anyway. [Though this brings up the question of whether your store should automatically check to see if the PayPal address is on file with the OSC store or not and add it if it's not in the customer's address book, I am not going to delve into that right now.]

Another critical issue is that the shipping info is not being processed (at least not for my store), meaning if I only fixed the delivery address then the customer's order would go through but they would not be charged anything for shipping!

The code fix below will force the PayPal returned shipping (delivery) address as what is displayed in your store. If your customer changes this, they will have to go through the complete (normal) checkout process. This also takes care of another bug regarding mis-matched variable array keys (names).

In checkout_confirmation.php, locate this code (beginning around line 136):

<!-- body_text //-->
    <td width="100%" valign="top"><table border="0" width="100%" cellspacing="0" cellpadding="0">
      <tr>
        <td><table border="0" width="100%" cellspacing="0" cellpadding="0">
          <tr>
            <td class="pageHeading"><?php echo HEADING_TITLE; ?></td>
            <td class="pageHeading" align="right"><?php echo tep_image(DIR_WS_IMAGES . 'table_background_confirmation.gif', HEADING_TITLE, HEADING_IMAGE_WIDTH, HEADING_IMAGE_HEIGHT); ?></td>
          </tr>
        </table></td>
      </tr>
      <tr>
        <td><?php echo tep_draw_separator('pixel_trans.gif', '100%', '10'); ?></td>
      </tr>
      <tr>
        <td><table border="0" width="100%" cellspacing="1" cellpadding="2" class="infoBox">
          <tr class="infoBoxContents">
<?php
  if ($sendto != false) {

INSERT the following afterwards:

// BOF fix PayPal Express delivery addr display error
   		$order->delivery = $billto;
   		$order->delivery['format_id'] = $order->delivery['address_format_id']; // make names compatible
// EOF fix PayPal Express delivery addr display error

This will force the PayPal returned shipping address into the delivery address confirmation.

-------------

Now, for some bug fixes:

1. Code was returning “MODULE_PAYMENT_PAYPAL_EXPRESS_TEXT_TITLE” as payment method. To fix, open /ext/modules/payment/paypal/express.php and find this code starting around line 29:

  if ($cart->count_contents() < 1) {
    tep_redirect(tep_href_link(FILENAME_SHOPPING_CART));
  }

  require('includes/modules/payment/paypal_express.php');

  $paypal_express = new paypal_express();

REPLACE WITH:

  if ($cart->count_contents() < 1) {
    tep_redirect(tep_href_link(FILENAME_SHOPPING_CART));
  }

  require(DIR_WS_LANGUAGES . $language . '/modules/payment/paypal_express.php');
  require('includes/modules/payment/paypal_express.php');

  $paypal_express = new paypal_express(); 

2. $sendto is used as both an array and a variable. The code that tries to use $sendto as a variable sometimes fails when it’s being used only as an array (and the $sendto variable itself - not the array part - has no value assigned). $sendto is supposed to contain the shipping/delivery address returned by PayPal. The normal shipping methods use $sendto as a variable to store the desired shipping address, retrieved from the customer’s address book.

There are a few possible ways to resolve the problem for PayPal Express. What should be done IMHO is to use the PayPal returned shipping address, since that is the only address the customer can change and it’s also where PayPal expects you to ship the order.

So to solve the problem, a code fix is needed for the section trying to determine the customer's delivery (shipping) address. Essentially, it is necessary to modify the reading $sendto to first check and see if $sendto is an array. If it is, then it should contain the shipping address info. If it is not an array then it should contain the number of the default shipping address within the customer’s address book.

Modify /ext/modules/payment/paypal/express.php and insert code just before the redirect to checkout_confirmation.php when the cart does not contain a virtual product.

The code change required is:

Open /ext/modules/payment/paypal/express.php and find this code around line 202:

          if (!tep_session_is_registered('payment')) tep_session_register('payment');
          $payment = $paypal_express->code;

          if (!tep_session_is_registered('ppe_token')) tep_session_register('ppe_token');
          $ppe_token = $response_array['TOKEN'];

          if (!tep_session_is_registered('ppe_payerid')) tep_session_register('ppe_payerid');
          $ppe_payerid = $response_array['PAYERID'];

          tep_redirect(tep_href_link(FILENAME_CHECKOUT_CONFIRMATION, '', 'SSL'));

CHANGE TO:

          if (!tep_session_is_registered('payment')) tep_session_register('payment');
          $payment = $paypal_express->code;

          if (!tep_session_is_registered('ppe_token')) tep_session_register('ppe_token');
          $ppe_token = $response_array['TOKEN'];

          if (!tep_session_is_registered('ppe_payerid')) tep_session_register('ppe_payerid');
          $ppe_payerid = $response_array['PAYERID'];

// added by Richard Cranium to trap bad address error condition
          if (!is_array($sendto)) $sendto = $customer_default_address_id; // set customer default address if we do not have delivery addr from PayPal

          tep_redirect(tep_href_link(FILENAME_CHECKOUT_CONFIRMATION, '', 'SSL'));

The other possible state of $sendto is binary (true/false). If set to ‘false’, that is because the cart contains a virtual (downloadable) product. In that scenario it is not desirable to calc any shipping charge, so $sendto will rightfully not contain an array of delivery information. It may still be possible with the code changes I am outlining here to still have the delivery address fall through if the only product ordered is downloadable. I don't have a good test store atm with any downloadable content, so I have not explored this possibility atm. However, this could easily be solved by adding a check for $sendto == false in /ext/modules/payment/paypal/express.php and forcing the use of the customer's default address as the delivery address. See bug fix #3 below for a brief explanation and pointing out the code section that would need to be modified to do this.


3. Order delivery details blank

For me, the /ext/modules/payment/paypal/express.php code was not assigning a delivery address. The delivery address is stored in $order->delivery (which is an array), but the data does not get transferred to checkout_confirmation, even though all the other array parts are carried over. Why?

The problem is that $order does not transfer over to checkout_confirmation intact. Because there is no order_id number yet, the order referencing checks in various files grab the cart contents and attempt to pull the customer’s shipping/delivery address from $sendto as a variable (and not an array). If the code has not assigned a numeric value to $sendto, the result is the htmlspecialchars() error in the delivery address field when checkout_confirmation.php attempts to display an address via a pointer that does not exist. If $sendto is equal to an integer value, the integer is a pointer to the customer's default address on file with the OSC store.

If $sendto is being used as an array only (you can use a variable as both at the same time), any query of the integer value of $sendto will be invalid (looking at $sendto by itself would return the value “array”), resulting in an error or null by the (int) operation. This can create a state where it’s impossible for checkout_confirmation.php to retrieve a shipping address. Plus we don’t want it to pull an address from the database anyway, we want it to use what PayPal sent. To fix this problem, checkout_confirmation (or the cart function) needs to grab the delivery address from the PayPal data.

One method is to compare the PayPal address information with the customer’s addresses on file (in their address book) and create a new shipping address for the customer if a match is not found. Then store the pointer for this address as an integer in $sendto and the standard functions involved will take care of the rest. Another (and easier) option is to modify /includes/classes/order.php so the cart function will pull the delivery address data if $sendto is an array and not being used as a variable (which also enables this method to be used for other payment solutions).

A final possible state of $sendto is a boolean value (true/false). If set to ‘false’, that is because the cart contains a virtual (downloadable) product. In that scenario it is not desirable to calc any shipping charge, so $sendto will rightfully not contain an array of delivery information. It may be possible with the code changes I am outlining for the delivery address fall through if the only product ordered is downloadable. I don't have a good test store atm with any downloadable content, so I have not explored this possibility. However, this could easily be solved by adding a check for $sendto == false in /ext/modules/payment/paypal/express.php and forcing the use of the customer's default address as the delivery address (and change $sendto to an integer).

To re-cap, there are 4 possible states of $sendto:
a. array only
b. integer only
c. array and integer
d. true/false value only


Find the following:

    function cart() {
      global $HTTP_POST_VARS, $customer_id, $sendto, $billto, $cart, $languages_id, $currency, $currencies, $shipping, $payment, $comments, $customer_default_address_id;

      $this->content_type = $cart->get_content_type();

      $customer_address_query = tep_db_query("select c.customers_firstname, c.customers_lastname, c.customers_telephone, c.customers_email_address, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, co.countries_id, co.countries_name, co.countries_iso_code_2, co.countries_iso_code_3, co.address_format_id, ab.entry_state from " . TABLE_CUSTOMERS . " c, " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " co on (ab.entry_country_id = co.countries_id) where c.customers_id = '" . (int)$customer_id . "' and ab.customers_id = '" . (int)$customer_id . "' and c.customers_default_address_id = ab.address_book_id");
      $customer_address = tep_db_fetch_array($customer_address_query);

Immediately after the code above you should see this:

      	$shipping_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$sendto . "'");
      	$shipping_address = tep_db_fetch_array($shipping_address_query);

REPLACE those 2 lines with this:
// BOF htmlspecialchars bug fix for PayPal Express by Richard Cranium
	  if (is_numeric($sendto)) { // grab address book ref if we have one
      	$shipping_address_query = tep_db_query("select ab.entry_firstname, ab.entry_lastname, ab.entry_company, ab.entry_street_address, ab.entry_suburb, ab.entry_postcode, ab.entry_city, ab.entry_zone_id, z.zone_name, ab.entry_country_id, c.countries_id, c.countries_name, c.countries_iso_code_2, c.countries_iso_code_3, c.address_format_id, ab.entry_state from " . TABLE_ADDRESS_BOOK . " ab left join " . TABLE_ZONES . " z on (ab.entry_zone_id = z.zone_id) left join " . TABLE_COUNTRIES . " c on (ab.entry_country_id = c.countries_id) where ab.customers_id = '" . (int)$customer_id . "' and ab.address_book_id = '" . (int)$sendto . "'");
      	$shipping_address = tep_db_fetch_array($shipping_address_query);
	  } elseif (is_array($sendto)) { // or if an array then get the delivery info from the array
// setup $shipping_address array with the delivery address info
// instead of using customer address book
    	$shipping_address = array('entry_firstname' => $sendto['firstname'],
								  'entry_lastname' => $sendto['lastname'],
								  'entry_company' => $sendto['company'],
								  'entry_street_address' => $sendto['street_address'],
								  'entry_suburb' => $sendto['suburb'],
								  'entry_city' => $sendto['city'],
								  'entry_postcode' => $sendto['postcode'],
								  'entry_state' => '',
								  'zone_name' => $sendto['zone_name'],
								  'entry_zone_id' => $sendto['zone_id'],
								  'countries_id' => $sendto['country_id'],
								  'countries_name' => $sendto['countries_name'],
								  'countries_iso_code_2' => $sendto['country_iso_code_2'],
								  'countries_iso_code_3' => $sendto['country_iso_code_3'],
								  'entry_country_id' => $sendto['country_id'],
								  'address_format_id' => $sendto['address_format_id']);
	  }
// EOF htmlspecialchars bug fix for PayPal Express by Richard Cranium

The solution above will use a customer’s address book entry if called for (when $sendto is numeric). Otherwise the shipping address will be grabbed from $sendto (when it is an array containing the shipping info). Although I don’t think it’s necessary, another check could be added to see if $sendto == false and the whole routine could be trapped at the end to make sure nothing falls through the cracks. But if $sendto == false then you should never get to this code anyway. The only possible conditions for $sendto are holding an integer, a boolean (true/false), or an array.

#17   aftabn10

aftabn10
  • Members
  • 225 posts
  • Real Name:Aftab
  • Gender:Male
  • Location:Manchester

Posted 25 March 2010 - 12:07

Hi Paul..

Yes, that is correct behaviour of the module. It is not the preferred manner for the Express Checkout payment method however many more codebase changes would be required to properly support a purchase without a customer account.

The codebase changes going in 2.2 RC1/RC2/Final are minimal changes to ease the upgrade procedure for existing store owners. Although we are focusing the bigger changes for the 3.0 release, a 2.3 release will also be made where such features will be available on based on the 2.2 core. (The 3.0 release has a new core that is not compatible to 2.2)


Harald,

You mentioned in your post that some minimal changes would be required to enable a user to purchase without an account. I have managed to install the Paypal Express Checkout but when clicking on the checkout button, the user would then be taken to the website login page rather than the paypals website.

What changes would i need to make to get this corrected? Currently I have the RC2a version.

Thanks in advance.

#18   minghoo

minghoo
  • Members
  • 107 posts
  • Real Name:sean wang

Posted 22 July 2010 - 19:14

the instruction( http://svn.oscommerc...ommerce?cs=1699 ) is no longer existed for manual installation, does anyone still have a copy want to share?


Thanks a lot!

sean