I'm trying out some of the updates on the dev branch (really interested in the shipment admin). I switched from 2.11 to dev and ran into the error below. I tried both the beta2 and rc1 versions of the Commerce Stripe module. Seem to have the same error regardless. Paypal express gateway still works. The configurations appear to be pulling the api keys into the payment gateway but errors out when doing the ApiRequest.
The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">Stripe\Error\Authentication</em>: No API key provided. (HINT: set your API key using "Stripe::setApiKey(<API-KEY>)". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions. in <em class="placeholder">Stripe\ApiRequestor->_requestRaw()</em> (line <em class="placeholder">219</em> of <em class="placeholder">/var/www/html/vendor/stripe/stripe-php/lib/ApiRequestor.php</em>). <pre class="backtrace">Stripe\ApiRequestor->request('get', '/v1/customers/cus_DxiiTbC35pU4XK', Array, Array) (Line: 31)
Stripe\ApiResource->refresh() (Line: 132)
Stripe\ApiResource::_retrieve('cus_DxiiTbC35pU4XK', NULL) (Line: 40)
Stripe\Customer::retrieve('cus_DxiiTbC35pU4XK') (Line: 322)
Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\Stripe->doCreatePaymentMethod(Object, Array) (Line: 261)
Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\Stripe->createPaymentMethod(Object, Array) (Line: 173)
Drupal\commerce_payment\PluginForm\PaymentMethodAddForm->submitConfigurationForm(Array, Object) (Line: 186)
Drupal\commerce_payment\Plugin\Commerce\InlineForm\PaymentGatewayForm->submitInlineForm(Array, Object) (Line: 155)
Drupal\commerce\Plugin\Commerce\InlineForm\InlineFormBase::runSubmit(Array, Object)
call_user_func_array(Array, Array) (Line: 130)
Drupal\commerce_order\Element\ProfileSelect::doExecuteSubmitHandlers(Array, Object) (Line: 123)
Drupal\commerce_order\Element\ProfileSelect::doExecuteSubmitHandlers(Array, Object) (Line: 123)
Drupal\commerce_order\Element\ProfileSelect::doExecuteSubmitHandlers(Array, Object) (Line: 107)
Drupal\commerce_order\Element\ProfileSelect::executeElementSubmitHandlers(Array, Object)
call_user_func_array(Array, Array) (Line: 82)
Drupal\Core\Form\FormValidator->executeValidateHandlers(Array, Object) (Line: 275)
Drupal\Core\Form\FormValidator->doValidateForm(Array, Object, 'commerce_checkout_flow_multistep_default') (Line: 118)
Drupal\Core\Form\FormValidator->validateForm('commerce_checkout_flow_multistep_default', Array, Object) (Line: 575)
Drupal\Core\Form\FormBuilder->processForm('commerce_checkout_flow_multistep_default', Array, Object) (Line: 318)
Drupal\Core\Form\FormBuilder->buildForm('commerce_checkout_flow_multistep_default', Object) (Line: 216)
Drupal\Core\Form\FormBuilder->getForm(Object, 'order_information') (Line: 94)
Drupal\commerce_checkout\Controller\CheckoutController->formPage(Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 582)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() (Line: 151)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 669)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)If I roll the commit back to #f9b3442bd590d0f20eb778286b497336de6d6ea0 which is right before the inline form updates I don't have the error. Could always be something else going wrong on my end but I wanted to post this in case someone else had seen this come up. Going to revert back to my latest stable set of modules for now.
Comments
Comment #2
bojanz commentedThis could be a factor. On a -dev install of Commerce and Shipping ProfileSelect is not supposed to be used.
So if it's used it might be triggering submit from the wrong place.
Comment #3
Jon352 commentedJust for fun I checked out the dev branches again and tried it against a custom checkout flow. The end result is the same but the ProfileSelect is not being called in this case.
List of modules I'm using via composer. I tried to loosen up the versions but lock in on the dev versions of commerce and commerce_shipping:
Its very well some combination of modules or configurations I'm using that's causing the issue. I am able to get around the issue right now by targeting some commits in my working composer file. The commits working for me are right before the inline form refactor. The shipping admin updates are pretty great so I want to start using them in my local dev environment.
Targeted commits and patches that work for me. Specific use cases but maybe will help someone else.
Comment #4
smccabe commentedI am able to reproduce this with -dev of commerce and stripe, will investigate.
Comment #5
smccabe commentedOk so this is a problem with the stripe module, but it maybe warrants some extra documentation in the payment gateway examples. Also this involves some form api innards, which I am not an expert on, so my explanation might not be totally correct.
The payment gateway is part of the form, so it gets cached along with everything in the form, which basically serializes everything and saves it in form cache. This means that the constructor isn't called again when the submit is processed, so the static variable assignment of the stripe key doesn't happen. This doesn't effect most other payment modules, as they don't set a static like Stripe does, so any objects or config they have are reloaded just like anything else in Drupal.
I didn't dig into the difference in the Commerce versions too specifically, but I assume the changes with inline forms caused the forms to be called differently so that the constructor doesn't coincidentally get called, thus exposing this bug.
Example:
In stripe,
\Stripe\Stripe::setApiKey($key);is called in the constructor, this means the API key is only set on the first run and any subsequent calls don't have the ApiKey set, like when doCreatePaymentMethod is called.Solution:
I think the solution here is to just save the key to the payment gateway class and call setApiKey before performing any actions.
@bojanz let me know if we should add anything extra to the examples or if I should just kick this issue over to stripe or make a new issue there.
Comment #6
smccabe commentedAdded in stripe issue w/ patch #3030159: Broken on Commerce 2.12
Comment #7
bojanz commentedThank you Shawn, this is of great help.
We are indeed serializing the payment gateway form instead of reconstructing it on payment submit.
I did not expect gateways to rely on this behavior.
Note that we also have a bug report for the old behavior: #2918420: Plugins are instantiated multiple times when adding/editing the parent entity.
I need to do a grep of other gateways to see whether others also do something like this in the constructor.
EDIT:
Grepped and found the following affected modules:
commerce_moyasar (has -dev release only), commerce_omise and commerce_payjp (in beta).
Comment #8
Jon352 commentedThank you so much guys!
Comment #9
bojanz commentedI discussed this problem with Matt, and we agreed to proceed with the current approach (instead of reverting it).
It is both cleaner code-wise and makes sense conceptually (where a plugin expects it won't get recreated all the time).
We discussed introducing a mandatory init() method, but it didn't make sense to introduce a required convention just to account for a (hopefully small and shrinking) number of SDKs that are still bad enough to depend on static global access.
So, let's fix the individual modules, and document the workaround.
Created a change record:
https://www.drupal.org/node/3031851
Opened a documentation issue:
https://github.com/drupalcommerce/commerce-docs/issues/282
Opened issues for commerce_moyasar, commerce_omise, commerce_payjp:
#3031845: Broken with Commerce 2.12
#3031846: Broken with Commerce 2.12
#3031847: Broken with Commerce 2.12