Cannot pass TranslatableMarkup to Stripe API

Created on 9 September 2025, 23 days ago

Problem/Motivation

Error PHP message: Uncaught PHP Exception TypeError: "Drupal\Component\Utility\Html::escape():

The code to create a stripe customer attempts to pass TranslatableMarkup to the Stripe API.

Proposed resolution

Since translation is likely needed to have a description, we should render this before passing it to the Stripe class.

Backtrace:

#0 /var/www/html/web/core/lib/Drupal/Component/Render/FormattableMarkup.php(202): Drupal\Component\Utility\Html::escape()
#1 /var/www/html/web/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php(195): Drupal\Component\Render\FormattableMarkup::placeholderFormat()
#2 /var/www/html/web/core/lib/Drupal/Component/Utility/ToStringTrait.php(15): Drupal\Core\StringTranslation\TranslatableMarkup->render()
#3 /var/www/html/vendor/stripe/stripe-php/lib/Util/Util.php(233): Drupal\Core\StringTranslation\TranslatableMarkup->__toString()
#4 /var/www/html/vendor/stripe/stripe-php/lib/Util/Util.php(172): Stripe\Util\Util::urlEncode()
#5 /var/www/html/vendor/stripe/stripe-php/lib/HttpClient/CurlClient.php(225): Stripe\Util\Util::encodeParameters()
#6 /var/www/html/vendor/stripe/stripe-php/lib/HttpClient/CurlClient.php(279): Stripe\HttpClient\CurlClient->constructRequest()
#7 /var/www/html/vendor/stripe/stripe-php/lib/ApiRequestor.php(464): Stripe\HttpClient\CurlClient->request()
#8 /var/www/html/vendor/stripe/stripe-php/lib/ApiRequestor.php(134): Stripe\ApiRequestor->_requestRaw()
#9 /var/www/html/vendor/stripe/stripe-php/lib/ApiOperations/Request.php(109): Stripe\ApiRequestor->request()
#10 /var/www/html/vendor/stripe/stripe-php/lib/Customer.php(67): Stripe\ApiResource::_staticRequest()
#11 /var/www/html/web/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/StripePaymentElement.php(1244): Stripe\Customer::create()
#12 /var/www/html/web/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/StripePaymentElement.php(1147): Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripePaymentElement->attachCustomerToStripePaymentMethod()
#13 /var/www/html/web/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/StripePaymentElement.php(673): Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripePaymentElement->createPaymentMethod()
#14 /var/www/html/web/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/StripePaymentElement.php(580): Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripePaymentElement->createPaymentMethodFromStripePaymentMethod()
#15 /var/www/html/web/modules/contrib/commerce/modules/payment/src/Controller/PaymentCheckoutController.php(147): Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripePaymentElement->onReturn()
#16 [internal function]: Drupal\commerce_payment\Controller\PaymentCheckoutController->returnPage()
#17 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array()
#18 /var/www/html/web/core/lib/Drupal/Core/Render/Renderer.php(627): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#19 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(121): Drupal\Core\Render\Renderer->executeInRenderContext()
#20 /var/www/html/web/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext()
#21 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(183): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}()
#22 /var/www/html/vendor/symfony/http-kernel/HttpKernel.php(76): Symfony\Component\HttpKernel\HttpKernel->handleRaw()
#23 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/Session.php(53): Symfony\Component\HttpKernel\HttpKernel->handle()
#24 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(48): Drupal\Core\StackMiddleware\Session->handle()
#25 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/ContentLength.php(28): Drupal\Core\StackMiddleware\KernelPreHandle->handle()
#26 /var/www/html/web/core/modules/big_pipe/src/StackMiddleware/ContentLength.php(32): Drupal\Core\StackMiddleware\ContentLength->handle()
#27 /var/www/html/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(116): Drupal\big_pipe\StackMiddleware\ContentLength->handle()
#28 /var/www/html/web/core/modules/page_cache/src/StackMiddleware/PageCache.php(90): Drupal\page_cache\StackMiddleware\PageCache->pass()
#29 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(48): Drupal\page_cache\StackMiddleware\PageCache->handle()
#30 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(51): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle()
#31 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/AjaxPageState.php(53): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle()
#32 /var/www/html/web/core/lib/Drupal/Core/StackMiddleware/StackedHttpKernel.php(51): Drupal\Core\StackMiddleware\AjaxPageState->handle()
#33 /var/www/html/web/core/lib/Drupal/Core/DrupalKernel.php(715): Drupal\Core\StackMiddleware\StackedHttpKernel->handle()
#34 /var/www/html/web/index.php(19): Drupal\Core\DrupalKernel->handle()
#35 {main}
🐛 Bug report
Status

Active

Version

2.1

Component

Code

Created by

🇺🇸United States andyg5000 North Carolina, USA

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @andyg5000
  • 🇺🇸United States andyg5000 North Carolina, USA
  • Pipeline finished with Success
    23 days ago
    Total: 399s
    #593530
  • 🇮🇱Israel jsacksick

    I don't think the call to render() is necessary:

    TranslatableMarkup uses the ToStringTrait() which implements the magic __toString() method:

    
      /**
       * Implements the magic __toString() method.
       */
      public function __toString() {
        try {
          return (string) $this->render();
        }
        catch (\Exception $e) {
          // User errors in __toString() methods are considered fatal in the Drupal
          // error handler.
          trigger_error(get_class($e) . ' thrown while calling __toString on a ' . static::class . ' object in ' . $e->getFile() . ' on line ' . $e->getLine() . ': ' . $e->getMessage(), E_USER_WARNING);
          // In case we are using another error handler that did not fatal on the
          // E_USER_ERROR, we terminate execution. However, for test purposes allow
          // a return value.
          return $this->_die();
        }
      }
    

    Btw, looking at the stacktrace, I see that the render() method is actually invoked:

    #2 /var/www/html/web/core/lib/Drupal/Component/Utility/ToStringTrait.php(15): Drupal\Core\StringTranslation\TranslatableMarkup->render()
    #3 /var/www/html/vendor/stripe/stripe-php/lib/Util/Util.php(233): Drupal\Core\StringTranslation\TranslatableMarkup->__toString()
    

    I think you're misinterpreting the error, the error you're seeing seems to be due to a placeholder that can't be properly replaced (likely due to an empty email??)

    The stacktrace actually shows that:

    #1 /var/www/html/web/core/lib/Drupal/Core/StringTranslation/TranslatableMarkup.php(195): Drupal\Component\Render\FormattableMarkup::placeholderFormat()
    

    and then:

    #0 /var/www/html/web/core/lib/Drupal/Component/Render/FormattableMarkup.php(202): Drupal\Component\Utility\Html::escape()
    

    Line 202 is:

              $args[$key] = Html::escape($value);
    

    So I'm like 99% sure that this is happening due to an empty email. Would you be able to confirm that?
    If so, ther right fix would be to pass an empty string instread of NULL.

  • Pipeline finished with Success
    22 days ago
    Total: 283s
    #594585
Production build 0.71.5 2024