When using an Irish address (no postcode) PayPal is failing with incorrect billing address.

Delving deeper and getting the long error description it states that the postalcode field is required. According to PayPal WPP spec (http://www.paypalobjects.com/en_GB/pdf/PP_WebsitePaymentsPro_IntegrationGuide.pdf) this field is not mandatory.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

daveetheridge’s picture

Status: Active » Needs review

To correct change line 279 to

foreach (array('thoroughfare', 'locality', 'postal_code', 'country') as $address_key) {

to

foreach (array('thoroughfare', 'locality', 'country') as $address_key) {

this will work with all countries that currently don't have post codes. This is supported by PayPal as the post code / zip field is NOT mandatory.

rszrama’s picture

Title: PayPal WPP expecting a post code / zip » Allow the merchant to configure which address fields must be present in commerce_paypal_wpp_submit_form_submit()
Version: 7.x-1.0-rc1 » 7.x-2.x-dev
Category: bug » feature
Status: Needs review » Active

Hmm, technically speaking (according to that PDF), none of the address fields would be required. However, that PDF is 6 years old, so I'm hesitant to make any sweeping changes based on it alone. One thought - perhaps it would just be better to let the merchant specify which address fields must be present to consider the address valid?

Moving to 2.x; we'd implement the new code here and backport.

rszrama’s picture

Here's the relevant documentation I could turn up, which shows postal code as required at least for US usage:

https://developer.paypal.com/webapps/developer/docs/classic/api/merchant...

That leads me to believe making this configurable is the best solution for now. I doubt it's realistic to find all the different countries that don't require a postal code by trial and error and keep the code up to date. Could lead to subpar integration for international companies where some addresses require a postal code and others don't, but at least it gives folks like yourself a working solution.

daveetheridge’s picture

Good solution. The required field issue may also apply to PayPal WPS and other payment gateway modules.

Anonymous’s picture

Hello,
Has there been any movement on this ticket?

I noticed someone else has posted symptoms that sound like the underlying issue here. https://drupal.org/node/2154365

While Reading #3 on this thread , I wonder if a clever way to get around this "magically" would be to piggy back off of dynamic address field and get the required fields that way since their module handles the collection of Billing Information and they're using standardized rules for what to show for what countries.

Just a thought.

Thanks,
Stan

lluisandreu’s picture

I guess you can always use the Commerce Single Address module, if you are only operating in one country/region.

Bird-Kid’s picture

Issue summary: View changes

I was not sure whether I should comment, update this issue or submit this as a new issue, so I figured I would just append it as a new comment.

Problem/Motivation

Today we have received notice of a consumer that he could not complete the checkout, being given the error message

We cannot process your credit card payment without a valid billing address.

Upon looking at his order details and diving into the code, apparently the reason lies within the missing postal_code value in the address. It was missing because the country he was ordering from does not have postal codes (example for such countries: Ireland, Hong Kong, etc.).
The issue can be reproduced with these steps:

  1. Create a cart and initiate the checkout.
  2. Pick a country that has no Postal Code such as Ireland or Hong Kong.
  3. Fill out the rest, process to shipping and review.
  4. Pick Credit Card as payment method, fill in credit card credentials and submit.
  5. You will be stuck on the same page and provided with the error message mentioned above.

Proposed resolution

I did not want to completely remove the check on the postal code value (as was proposed above), so I borrowed some code from the addressfield module's format plugin and replaced this:

    foreach (array('thoroughfare', 'locality', 'postal_code', 'country') as $address_key) {
      if (empty($address_value[$address_key])) {
        $valid_billing_address = FALSE;
      }
    }

with this:

    $address_had_postal_code = TRUE;
    // Those countries do not seem to have a relevant postal code.
    // Duplicate of $countries_no_postal_code in "address.inc" format plugin of the addresfield module
    static $countries_no_postal_code = array('AF', 'AG', 'AL', 'AO', 'BB', 'BI', 'BJ', 'BO', 'BS', 'BW', 'BZ', 'CF', 'CG', 'CM', 'CO', 'DJ', 'DM', 'EG', 'ER', 'FJ', 'GD', 'GH', 'GM', 'GQ', 'GY', 'HK', 'IE', 'KI', 'KM', 'KP', 'KY', 'LC', 'LY', 'ML', 'MR', 'NA', 'NR', 'RW', 'SB', 'SC', 'SL', 'SR', 'ST', 'TD', 'TG', 'TL', 'TO', 'TT', 'TV', 'TZ', 'UG', 'VC', 'VU', 'WS', 'ZW');
    if (in_array($address_value['country'], $countries_no_postal_code)) {
        $address_had_postal_code = FALSE;
    }

    foreach (array('thoroughfare', 'locality', 'country') as $address_key) {
      if (empty($address_value[$address_key])) {
        $valid_billing_address = FALSE;
      }
    }

    // Check postal code only if the address is supposed to have one
    if ($address_had_postal_code && empty($address_value['postal_code'])) {
      $valid_billing_address = FALSE;
    }

Remaining tasks

Arguably a nicer way to handle this would be to integrate the structure outputted by the addres.inc format plugin from the addressfield module directly into here, see if the field postal_code has been set and only flag the shipping address as not valid (read: invalid) if the array key has been set in the returned format array AND is empty in $shipping_address. However, being rather new to Drupal (been five exciting months for me now) I could not manage to integrate the addressfield_format_address_generate function into the Commerce PayPal WPP module.

swentel’s picture

SOLVED

Oh wow, this was fantastic .. at some point we switched the order of the 'Payment' and 'Billing information' pane. This has influence of the order of the submit functions which are triggered. Setting the billing information first solves the problem. But we want it in a different order, which I can probably solve otherwise.

Created separate bug report, see https://drupal.org/node/2284307

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

Ironically, I'm able to reproduce this as well, but in a different context. In our case, the validation /always/ fails the first time, second time everything is right.
Note: everything is filled in, so the required validation that Drupal performs passed fine.
All other validations go fine to, so we enter commerce_paypal_wpp_submit_form_submit().

The first time, the first if statement in the code underneath is returning the false, so for some reason, the address/billing values are not populated on either the $order or the $order_wrapper object. I'm not sure how and why this is not happening. However, clicking to go forward again /without/ changing anything on the form then works and we can start paying.

I'm not sure yet who's responsible for populating the commerce_customer_billing key, but once I figure that one out, I'll be able to get around this.
Note: we're currently on commerce paypal 7.x-2.2 and commerce 7.x-1.8 - but looking at the diff between both latest release, there isn't anything I can see that would affect or fix this (although, granted, I didn't test it yet).

 if (empty($order->commerce_customer_billing) || empty($order_wrapper->commerce_customer_billing->value()->commerce_customer_address)) {
    // Comes in here the first time, although everything is filled in nice. Second submit all is fine and neither the if or else code failes
    $valid_billing_address = FALSE;
  }
  else {
    // Check the values in the billing address array required by PayPal.
    $address_value = $order_wrapper->commerce_customer_billing->commerce_customer_address->value();

    if (empty($address_value['name_line']) && empty($address_value['first_name'])) {
      $valid_billing_address = FALSE;
    }

    foreach (array('thoroughfare', 'locality', 'postal_code', 'country') as $address_key) {
      if (empty($address_value[$address_key])) {
        $valid_billing_address = FALSE;
      }
    }
  }
Marko B’s picture

#7 Bird-Kid solution is certanly something we could use for start. There are significant no. of countries without postal code. Currently you can complete purchase for this countries because postal_code is required and there is non available to enter so basically PayPal WPP is not useable in those countries.

Marko B’s picture

Category: Feature request » Bug report
Status: Active » Needs review
FileSize
2.21 KB

I made patch from that code above. Works good.

jackbravo’s picture

I agree with @rzrama in #3: it would be very hard to keep track this list of countries without postal codes inside of the commerce_paypal module.

We should do like @sshivell suggests in #5 and let the addressfield module keep track of which countries have postal_code as a required field. It already does that. For example, postal code is already a required field for the US, not required for Ireland (IE), so I don't think there is a need to double check that in commerce_paypal.

NewZeal’s picture

A foolproof way of solving this problem without using a patch or hacking is to supply the following function to your custom module. This saves the postal code as the (meaningless) ' - ' so that it passes WPP validation and doesn't cause confusion when printed in invoices.

/**
 * Implements commerce_customer_profile_presave();
 *
 * Ensure that postal code has a value (for places like Hong Kong)
 *
 * @param $profile
 */
function MYODULE_commerce_customer_profile_presave($profile) {
  if (!isset ($profile->commerce_customer_address['und'][0]['postal_code']) || $profile->commerce_customer_address['und'][0]['postal_code'] == '' ) {
    $profile->commerce_customer_address['und'][0]['postal_code'] = ' - ';
  }
}
scotwith1t’s picture

For me, the key was this:

   if (empty($address_value['name_line']) && empty($address_value['first_name'])) {
      $valid_billing_address = FALSE;
    }

I don't see it documented anywhere that the name field is required for WPP. While it may be fairly obvious, since WPP is basically a credit card processor and every CC processor requires a name to go with the CC, because we were upgrading from WPS and hadn't needed the Name fields on our form before, it wasn't exactly clear why it wasn't working for us initially. Hope this helps someone else out.

Igor Mancos’s picture

For existing billing profiles work this code:

/**
 * Implements hook_entity_load().
 */
function MYMODULE_entity_load($entities, $type) {
  if ($type == 'commerce_order') {
    foreach ($entities as $entity) {
      $order_wrapper = entity_metadata_wrapper('commerce_order', $entity);
      if (isset($order_wrapper->commerce_customer_billing->commerce_customer_address)) {
        if (empty($order_wrapper->commerce_customer_billing->commerce_customer_address->locality->value())) {
          $order_wrapper->commerce_customer_billing->commerce_customer_address->locality->set(' - ');
        }
        if (empty($order_wrapper->commerce_customer_billing->commerce_customer_address->postal_code->value())) {
          $order_wrapper->commerce_customer_billing->commerce_customer_address->postal_code->set(' - ');
        }
      }
    }
  }
}
mglaman’s picture

Status: Needs review » Closed (duplicate)

This was fixed in #2939890: Some countries erroneously fail address validation., which checks the required address format properly. This was tested for the Irish address use case.