Home

Geeklog: What I Learnt Today / Adam

Magento only stores shipping address firstname on Paypal Express orders

> Magento and Paypal Express combine to have a difficulty with first and last names. So you end up with the shipping address only having the firstname filled in and the last name empty.
I did some research and found most of the answer herehttps://magento.stackexchange.com/questions/99884/paypal-express-firstname-and-lastname-instead-of-shiptoname, so thanks to https://magento.stackexchange.com/users/5288/loeffel.
It seems to be that Magento processes the response from Paypal the SHIPTONAME value being firstname lastname. In app/code/core/mage/Paypal/Model/Api/Nvp.php in the function _exportAddressses

$shippingAddress->addData(array(
     'firstname'  => $data['SHIPTONAME'],
));

Thats ok mostly but it causes some issues if your shipper requires lastname and firstname so can be a bit annoying.
So you can change it if you need to.

$nameParts = $this->splitNameIntoFirstAndLast($data['SHIPTONAME']);
$shippingAddress->addData(array(
     'firstname'  => $nameParts['firstname'],
     'lastname'  => $nameParts['lastname']
));

I've added a function splitNameIntoFirstAndLast to that file to split the name eg Colin Smith to 'firstname' = Colin, 'lastname' = Smith. I can write a function given the customers of the store that had this problem ok working out about 98% of them. Probably because the customers mostly have European names. I think you would have to rethink this if your customers were from different places say Chinese or Vietnamese for example.
The other file you have to change is app/code/core/mage/Paypal/Model/Express/Checkout.php which just comments out this line in the function returnFromPaypal which makes that lastname editable.

$shippingAddress->setLastname(null);

The function I wrote for splitting names looks like this. I think this is the bit you would want to change depending on your customers. I expect I'll tweak this over time.

function splitNameIntoFirstAndLast($originalName){
$debug = true; /* if we have debug on we are testing and count failures * about 1.48% have empty last names in past 7308 with no last name in db */ if($debug){ global $count; } $titles = ['Dr','Dr.','Mrs','Mrs.','Mr','Mr.','Ms.', 'Ms', 'Miss.', 'Miss']; $name = trim($originalName); /* one bloke had name Colin Smith */ $name = str_replace(' ',' ', $name); /* if the name begins with a . like '. colin smith' */; $name = preg_replace('/^\. /', '', $name); /* if the name ends with a . like 'colin smith.' */; $name = preg_replace('/\.$/', '', $name); /* replace multiple whitespaces with one people like colin smith */ $name = preg_replace('/(\s){1,}/',' ', $name); if(strpos($name,',') !== false){ /* if they enter their name with commas in then its probably best to leave it, we could reverse it but its not always surname, firstname */ //$name = trim(implode(' ', array_reverse(explode(',', $name)))); }
$foundTitle = $cofound = false; $nameParts = explode(' ', $name);
/* colin smith C/O Some Company */ if(stripos($name,'C/O') !== false){ $nameParts = preg_split('/C\/O/i', $name); $nameParts[1] = substr($name, stripos($name,'C/O'),3).$nameParts[1]; $cofound = true; }
$currentNamePartIndex = 0; $firstName = $nameParts[$currentNamePartIndex]; if(count($nameParts) > 2 && in_array($nameParts[0], $titles)){ $foundTitle = true; $currentNamePartIndex = 2; $firstName = $nameParts[1]; }else if(count($nameParts) > 2 && strlen($nameParts[$currentNamePartIndex]) == 1){ $currentNamePartIndex = 0; $firstName = ''; while(strlen($nameParts[$currentNamePartIndex]) == 1){ $firstName .= $nameParts[$currentNamePartIndex].' '; $currentNamePartIndex++; } $currentNamePartIndex-1; }else{ $currentNamePartIndex = $currentNamePartIndex+1; }
$lastName = implode(' ',array_slice($nameParts,$currentNamePartIndex));
/* odds and sods */ /* camel case like ColinMcSomething */ if($lastName == '' && mb_strtoupper($firstName, 'utf-8') != $firstName){ $nameParts = preg_split('/(?=[A-Z])/', $firstName, -1, PREG_SPLIT_NO_EMPTY); if(count($nameParts) > 1){ $firstName = $nameParts[0]; $lastName = implode('', array_slice($nameParts,1)); } }
/* colin@twit.com */ if($lastName == '' && strpos($firstName,'@') !== false){ $nameParts = explode('@', $firstName); $firstName = $nameParts[0]; $lastName = implode('', array_slice($nameParts,1)); }
/* colin no last name */ if($lastName == ''){ $lastName = 'Empty'; if($debug){ $count++; } }
$parts = array($originalName, $firstName, $lastName);
return array('firstname' => $parts[1], 'lastname' => $parts[2]); }


This is on Magento 1.9.3.4 The query I used to build a list of names from your database to test your name splitting function is

SELECT DISTINCT(CONCAT('"',prefix_sales_flat_order_address.firstname,'",'))
FROM prefix_sales_flat_order_address
JOIN prefix_sales_flat_order
on (prefix_sales_flat_order.shipping_address_id = prefix_sales_flat_order_address.entity_id AND prefix_sales_flat_order_address.address_type = 'shipping')
or (prefix_sales_flat_order.billing_address_id = prefix_sales_flat_order_address.entity_id AND
prefix_sales_flat_order_address.address_type = 'billing')
WHERE
prefix_sales_flat_order.created_at > '2017-01-01 00:00:00'
AND lastname is null
AND prefix_sales_flat_order_address.address_type = 'shipping';

I'm getting about 1.5% with 'Empty' as lastname.

/ Adam