The "stripe_payment_element" plugin did not specify a "offsite-payment" form class

Created on 20 January 2024, 10 months ago

Problem/Motivation

Order Checkout page giving white screen "unexpected error" message.
Log reports this error:
Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException: The "stripe_payment_element" plugin did not specify a "offsite-payment" form class in Drupal\Core\Plugin\PluginFormFactory->createInstance() (line 41 of /var/www/html/artsite/web/core/lib/Drupal/Core/Plugin/PluginFormFactory.php).

Steps to reproduce

Install Drupal commerce 2.37 + Commerce Stripe 8.x-1.1, configure new store, install stripe Payment gateway -- set to stripe payment element.

I have my Stripe account set to test mode. I've made sure the payment keys are set correctly. When I try run a test purchase as an anoymous user, I'm getting the above message when I try to visit the checkout page.

If I go back to the Order Review page, and I refresh it, the customer address fields are all written to the page, and then the credit-card payment form appears on the site. If I fill it with test card information, click "Pay and complete purchase" - then it goes to white screen.

πŸ› Bug report
Status

Active

Version

1.1

Component

Code

Created by

πŸ‡ΊπŸ‡ΈUnited States wheelercreek

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

Merge Requests

Comments & Activities

  • Issue created by @wheelercreek
  • πŸ‡ΊπŸ‡ΈUnited States wheelercreek

    Looks like if I create the payment gateway as a card element rather than payment element, that works. So I think that will do for me.

  • I'm getting the same error. Payment Element is the preferred method with Stripe

  • πŸ‡ΊπŸ‡¦Ukraine marchuk.vitaliy Rivne, UA

    @wheelercreek @jeffgtr
    This sounds like a cache issue. Can you try clearing the cache and check again?
    And you should definitely use the Stripe Payment Element plugin because the Stripe Card Element uses a deprecated API.

  • πŸ‡«πŸ‡·France nicolas bouteille

    Hi,
    We have Payment Element enabled for 3 days now in production.
    A lot of payments have successfully passed.
    I just saw this same error pass right now for a customer
    IntegrationError: Invalid value for stripe.confirmPayment(): elements should have a mounted Payment Element or Express Checkout Element.
    At review step.
    This error came along with another error
    The "stripe_payment_element" plugin did not specify a "offsite-payment" form class
    Coming from
    commerce/modules/payment/src/Plugin/Commerce/InlineForm/PaymentGatewayForm.php(103): Drupal\Core\Plugin\PluginFormFactory->createInstance()
    commerce/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentProcess.php(218)

    $pane_form['offsite_payment'] = $inline_form->buildInlineForm($pane_form['offsite_payment'], $form_state);
    

    seems to be the line that triggers the error

    I have no clue what the customer did or faced to generate this error.
    I am surprised the customer could access the "payment process" step since the PaymentIntent was never confirmed...
    Did he try to hack the checkout flow and submit the pane manually in js?

  • πŸ‡«πŸ‡·France nicolas bouteille

    Hi,
    I discovered a use case when this can happen. A client sent me an screenshot of the review step on her iPad. PaymentElement did not load correctly so it was missing. (could not reproduce bug on 2 iPads btw)
    There was only two checkboxes of CGVs that we enforce users to check before they can submit, and the "Pay and complete purchase" button.
    Because an error had occured while mounting PaymentElement, the JS code that is supposed to override the form submit (prevent default etc) never got executed.
    So when she submitted the form, she went the regular Commerce way of submitting the Review step and then going to the PaymentProcess step. Leading to this specific error.

    In order to prevent this from happening in the future :
    1/ we disable the submit button in case error happen that prevent our code from overriding submit in JS
    here

    if (!drupalSettings.commerceStripePaymentElement || !drupalSettings.commerceStripePaymentElement.publishableKey) {
            myCustomLogErrorCode() // custom code  to log the error and send us an alert mail
            Drupal.commerceStripe.displayError(drupalSettings.utils.CUSTOM_ERROR_MESSAGE);
            $("form.my-custom-checkout-flow-form .form-actions input.form-submit").prop("disabled", true);
            return;
          }

    and here we actually added a try catch :

    try {
                // Create an instance of Stripe Elements.
                var elements = stripe.elements(settings.createElementsOptions);
                var paymentElement = elements.create('payment', settings.paymentElementOptions);
                paymentElement.mount('#' + settings.elementId);
              } catch (e) {
                myCustomLogErrorCode() // custom code  to log the error and send us an alert mail
                Drupal.commerceStripe.displayError(drupalSettings.utils.CUSTOM_ERROR_MESSAGE);
                $("form.my-custom-checkout-flow-form .form-actions input.form-submit").prop("disabled", true);
                return;
              }
    

    What we also did, because in our case even when confirming payment with an existing payment method we always use the return_url and never submit the form in JS, we also added a custom check in validateForm of our CheckoutFlow plugin that prevents the review step from ever being submitted. The only way to pass this step in our case is throught the return_url passed to stripe.confirmPayment()

    public function validateForm(array &$form, FormStateInterface $form_state) {
        parent::validateForm($form, $form_state);
        if ($form['#step_id'] === 'review') {
          myCustomLogErrorCode() // custom code  to log the error and send us an alert mail      
          $form_state->setError($form, CustomUtils::CUSTOM_ERROR_MESSAGE);
        }
      }
  • πŸ‡¬πŸ‡§United Kingdom gillarf

    I'm seeing the same error consistently on our site.

    Did anyone get to the bottom of this?

  • πŸ‡«πŸ‡·France nicolas bouteille

    Yes it happens when PaymentElement JS does not load correctly and customers can submit the Review step form without actually paying. Read above for more details. I have not been able to understand how / when this happens though.

  • πŸ‡«πŸ‡·France nicolas bouteille

    I have two new information about this:
    First, Stripe Payment Element not loading/displaying can be because of js.stripe.com/v3 being blocked.
    In our case OneTrust cookie consent tool was blocking Stripe JS until we set this cookie as Strictly Necessary.

    Second, when we face the error
    The "stripe_payment_element" plugin did not specify a "offsite-payment" form class
    we can conclude that we've reached the line
    $pane_form['offsite_payment'] = $inline_form->buildInlineForm($pane_form['offsite_payment'], $form_state);
    in PaymentProcess.php
    However, we are not supposed to reach this line with PaymentElement as a Payment Gateway inside PaymentProcess.php
    we are supposed to enter the preceding if statement:

    if ($payment_gateway_plugin instanceof SupportsStoredPaymentMethodsInterface && !$this->order->get('payment_method')->isEmpty()) {
      $payment->payment_method = $this->order->get('payment_method')->entity;
      $payment_gateway_plugin->createPayment($payment, $this->configuration['capture']);
      $this->checkoutFlow->redirectToStep($next_step_id);
    }
    

    Which directly redirects to the next step, so the rest of the code should not be executed...
    So chances are we did not enter it because $this->order->get('payment_method')->isEmpty()
    So you can start digging why the order does not have a payment_method at that moment... which is not supposed to happen

    BTW, I have noticed in StripePaymentElement->onReturn()
    there is no $order->save() after

    $this->createPaymentMethod($payment_method, $payment_details);
    $order->set('payment_method', $payment_method);
    

    if nothing goes wrong the order eventually gets saved later on with the payment_method associated, but in our case it seems something went wrong (it seems to user got logged out...), so the payment method was actually created but never associated with the order, so we were facing the error The "stripe_payment_element" plugin did not specify a "offsite-payment" form class because the order did not have any payment_method attached when reaching "payment" step.

    I had to manually unlock the order + set the payment_method ID in the payment_method column of the order directly in the database and delete the order entry in cache_entity to unblock the situation...

  • First commit to issue fork.
    • TomTech β†’ committed dbdf4a18 on 8.x-1.x
      Issue #3416076 by TomTech, Nicolas Bouteille, wheelercreek, jeffgtr,...
  • Assigned to TomTech
  • Status changed to Fixed 5 months ago
  • πŸ‡ΊπŸ‡ΈUnited States TomTech

    The code was originally written in a way that did not expect to get to this particular code path. We can see that it is possible.

    As @nicolas-bouteille noted, if the js does not load properly, the form can be submitted, causing this error.

    We've added two protections around this:
    1. The form is now loaded with the submit button disabled, until the js properly loads. We're NOT disabling it during initial js load, because it is possible that the js might never load (e.g. if a 3rd party script error causes js to fail), or even if it does, there is a race condition that the button is enabled until the js executes. (This does mean that a user can encounter a scenario where the button is NOT enabled, since the script did not load/execute, but this is at least a better experience than a WSOD.)

    2. If a user is still somehow able to submit the form (much less likely now), we have implemented the offsite form, though we simply redirect them back to the review page. Hopefully, any js loading issues will be resolved on the return trip.

    The other scenario mentioned involves an error occurring during the onReturn() callback. If there is a reproducible case there, go ahead and open up a new issue so that situation can be handled appropriately.

  • Automatically closed - issue fixed for 2 weeks with no activity.

  • πŸ‡ΊπŸ‡¦Ukraine gilmord πŸ‡ΊπŸ‡¦Ukraine

    I updated commerce_stripe module, and now payment button is disabled, I assume this is due to this issue
    No special steps - add to cart, go to checkout, enter billing information, on payment step fill card details - unable to submit, button is disabled

  • πŸ‡ΊπŸ‡¦Ukraine gilmord πŸ‡ΊπŸ‡¦Ukraine

    Js file was update, but library version not updated
    So button disabled with php code in stripe review payn, and js stays cached by cloudflare, varnish etc

    This needs to be fixed.

  • πŸ‡«πŸ‡·France nicolas bouteille

    This morning again I got a case where the payment_method had not been correctly associated to the order, leading later on to the error "The "stripe_payment_element" plugin did not specify a "offsite-payment" form class"...
    The payment_method had been created, but the payment_method column was null for the order.
    You did not tell me what you think about adding
    $order->save()
    after

    $this->createPaymentMethod($payment_method, $payment_details);
    $order->set('payment_method', $payment_method);
    

    in StripePaymentElement->onReturn()
    any reason it is not done?

  • πŸ‡¬πŸ‡§United Kingdom brenm

    I've got this exact same issue.

    New Drupal 10 install - 10.3.6
    Drupal Commerce 2.40
    Drupal Commerce Stripe 1.1

    I get to the review stage on checkout but the Stripe payment details don't appear.
    I click the "Pay and Complete Purchase" button
    I get the error

    Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException: The "stripe_payment_element" plugin did not specify a "offsite-payment" form class in Drupal\Core\Plugin\PluginFormFactory->createInstance() (line 41 of /app/web/core/lib/Drupal/Core/Plugin/PluginFormFactory.php).

    I press the back button and then I refresh the page. The stripe payment form appears.

    This is happening in both TEST and LIVE modes. I've double and triple checked my public and private keys. I'm using "Stripe Payment Element"

    How do I resolve this? It's driving me nuts!

Production build 0.71.5 2024