When performing certain actions during the checkout (Paypal), we were getting an error message:

Fatal error: Cannot use object of type Drupal\Core\StringTranslation\TranslatableMarkup as array in /var/aegir/platforms/puc_8.2.2_00/web/core/lib/Drupal/Core/Form/FormBuilder.php on line 527

The errors appear intermittently. Sometimes when returning from a canceled Paypal order screen, and sometimes when pressing 'Back' on the checkout review page.

The code in FormBuilder.php, line 527 is:

    $form['#form_id'] = $form_id;

So for some reason, $form is actually an object, not an array. I dumped the variable and here's the first part of it...

object(Drupal\Core\StringTranslation\TranslatableMarkup)#1574 (5) {
  ["string":protected]=>
  string(41) "PayPal reported an error: @code: @message"
  ["translatedMarkup":protected]=>
  NULL
  ["options":protected]=>
  array(0) {
  }
  ["stringTranslation":protected]=>
  object(Drupal\Core\StringTranslation\TranslationManager)#314 (4) {
    ["translators":protected]=>
    array(1) {
      [30]=>
      array(1) {
        [0]=>
        object(Drupal\Core\StringTranslation\Translator\CustomStrings)#317 (3) {
          ["settings":protected]=>
          object(Drupal\Core\Site\Settings)#11 (2) {
            ["storage":"Drupal\Core\Site\Settings":private]=>

That led me to check the Translation modules, which were not enabled.

I think I've found the magic missing module "Interface Translation". Enabling that module seems to fix the error.
Based on this, I believe it should be added as a dependency to one or all of Ubercart, uc_credit, uc_payment, or uc_paypal?
UPDATE: Possibly "Configuration Translation" also needs to be enabled...

Comments

millenniumtree created an issue. See original summary.

millenniumtree’s picture

Issue summary: View changes
millenniumtree’s picture

Title: Missing dependency Interface Translation » Missing dependencies for Translation
TR’s picture

Title: Missing dependencies for Translation » PHP error when using PayPal
Component: Module integration » Payment
Status: Needs review » Postponed (maintainer needs more info)

I don't know what the issue is, but it's not a missing dependency. The translation infrastructure (like t() and TranslatableMarkup) is built into core Drupal, and in fact MUST be used by all modules. The translation-specific modules you mention are not required for modules to work in English, but some or all are required only when you want to present your site in a different language.

What your error report implies is that within the core Drupal FormBuilder.php, the $form variable is an object of type TranslatableMarkup. So on line 527, when it tries to assign a value to $form['#form_id'], you get a PHP error telling you that you can't treat an object ($form, which is an object of type TranslatableMarkup) as an array (by using [] and trying to assign an element of that array). Again, this points away from a translation issue or dependency because $form should NEVER be a TranslatableMarkup object at this point.

in FormBuilder.php, $form is initialized as a array, as it should be. Apparently the problem comes in a few lines above 527 where it tries to do $form = call_user_func_array($callback, $args);. If you can try to find out what $callback is when there is an error that would help - it would tell us which Ubercart form/method is being invoked at that point.

millenniumtree’s picture

OK, I can force the error if I modify ubercart/payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalExpressCheckout.php line 185 to always fail the $response['ACK'] (added the "TRUE ||" in the if)

    $response = $this->sendNvpRequest($request);

    if (TRUE || $response['ACK'] != 'Success') {
      \Drupal::logger('uc_paypal')->error('NVP API request failed with @code: @message', ['@code' => $response['L_ERRORCODE0'], '@message' => $response['L_LONGMESSAGE0']]);
      return $this->t('PayPal reported an error: @code: @message', ['@code' => $response['L_ERRORCODE0'], '@message' => $response['L_LONGMESSAGE0']]);
    }

So I believe the return line on 187 is causing it. The return value of buildRedirectForm() should be a form array, but instead it's a t() result.

orderSubmit() has a similar return, but that method doesn't normally return anything, so probably won't cause an error.

millenniumtree’s picture

Issue summary: View changes
TR’s picture

Version: 8.x-4.0-alpha5 » 8.x-4.x-dev
Status: Postponed (maintainer needs more info) » Active

That would explain the error. What is the full text of the error message in your dblog (logged by line 186)?

millenniumtree’s picture

I believe it's a missing shipping address (specifically the state). Paypal doesn't like that, and we may open a new issue just for that issue.

But as the code is now, any error returned by Paypal will cause a white-screen-error because ubercart/payment/uc_paypal/src/Plugin/Ubercart/PaymentMethod/PayPalExpressCheckout.php returns the error message object instead of a form array.

Can we instead return the form array with an error state set and/or drupal_set_message()?

millenniumtree’s picture

Turn shipping boxes off entirely (disabled in UC config and UC Paypal config).

Then, when clicking "Submit order", the following is logged in the syslog (we don't use database logging).

https://example.com/cart/checkout/review?q=cart%2Fcheckout%2Freview|https://example.com/cart/checkout/review|1||NVP API request failed with 10729: The field Shipping Address State is required
https://example.com/cart/checkout/review?q=cart%2Fcheckout%2Freview|https://example.com/cart/checkout/review|1||NVP API request failed with 10410: Invalid token.

Note that this is with a drupal_set_message instead of the return line... With the return line, the page blows up instead of bringing you back to the checkout page.

CJdriver’s picture

I'm having the same Fatal Error appear when trying to submit the form, but my circumstances are a bit different than what you are seeing millenniumtree.

I followed your same steps in #5 but my response is different in the logs. On line 187 I am getting an array response and not a t() result from PayPal, but the error in the log is "NVP API request failed with 10002: Security header is not valid". Not sure if this is due to the fact I have my checkout method in sandbox mode or not.

I checked and my $request to PayPal does include a shipping state in the array being used with sendNvpRequest() so that may be why I'm seeing a different error? Not too up on troubleshooting this type of stuff.

TR’s picture

@CJdriver: A google search shows that your error is because you're using the wrong credentials. Be aware that your sandbox credentials are different than your production credentials.

CJdriver’s picture

Didn't realize the sandbox had separate API credentials... after updating I'm still getting the fatal error after receiving a success response from PayPal.
Array ( [TOKEN] => EC-1P30xxx [TIMESTAMP] => 2016-11-30T19:33:49Z [CORRELATIONID] => a796a7xxx [ACK] => Success [VERSION] => 3.0 [BUILD] => 000000 )

millenniumtree’s picture

I think the main issue here is that ANY error response from Paypal is being handled incorrectly by the module, and generates a fatal error instead of a pretty error message.

TR’s picture

Yes, we've established that the error response from PayPal isn't being handled correctly. But there are two problems:

1) We shouldn't be receiving error responses from PayPal at all except in exceptional circumstances. That's why we haven't seen this before. So in order to ensure that this error response isn't because of something Ubercart is doing, we need to know what the exact PayPal error is - is this bad credentials, negative product price, invalid card number, or what? Some of that may be setup/configuration error that should be handled differently than runtime errors.

You said you think it's a missing state in the shipping address, but that field is required by Ubercart so PayPal shouldn't ever see that data - the form should fail validation and ask the user for the missing information before sending the data to PayPal.

2) When we do receive the error, we need to inform the customer of the problem in a meaningful way. I believe, but am not sure, that the only errors we get should back here are things the customer has no control over. In that case all we can do is inform the customer that the site is messed up and log the error.