Payum / PayumBundle

Payum offers everything you need to work with payments. From simplest use cases to very advanced ones.
https://payum.gitbook.io/payum
MIT License
569 stars 142 forks source link

Fixed the ordering of method procerss, that mothod that loads all ext… #550

Closed joesenova closed 1 year ago

joesenova commented 1 year ago

…ensions into config for the Payum Bundle to function correctly.

The empty config passed for twig.env replaced the $currentConfig(already set config to only the twig config) at array_replace_recursive Thus the other extensions that needs to be added to the config gets chuckedm as they are not in the $currentConfig anymore. The method addCoreGatewayFactoryConfig changed here https://github.com/Payum/Core/commit/f444c4cf8466679d775c8e6caa73eaf161cc338d

This was a premature assessment on my behalf, it turned out to be a complex Twig issue, where our app has many Twig overloadds and custom extensions.

what-the-diff[bot] commented 1 year ago

PR Summary

joesenova commented 1 year ago

@pierredup @makasim

Please check this commit and merge int a new tagged release 2.5.2

Thank you.

pierredup commented 1 year ago

I don't think changing the order of the calls will change anything, since the config will just be added to the builder in the same way, there is nothing that would override any other config. Let's continue the discussion in #549, and if there is indeed something that needs to be changed here, then the PR can be re-opened.

joesenova commented 1 year ago

Have you tested this on your side to see what it does before closing the PR?

I got it working by swooping the order.

if you have a look what happens in Payum Core with the method being called you’ll see what is going on.

I have forked the branch and will use my own branch until this issue is resolved.

pierredup commented 1 year ago

I have a Symfony 6 setup that works without any errors.

if you have a look what happens in Payum Core with the method being called you’ll see what is going on.

Can you elaborate on this? What is happening in Payum core? On my test setup, the twig.env config is correctly being added to the builder

coreGatewayFactoryConfig^ array:12 [
  0 => "payum.template.layout"
  1 => "payum.template.obtain_credit_card"
  2 => "payum.paths"
  3 => "payum.action.get_http_request"
  4 => "payum.action.obtain_credit_card"
  5 => "twig.env"
  6 => "payum.extension.psr_logger"
  7 => "payum.extension.log_executed_actions"
  8 => "payum.extension.profile_collector"
  9 => "payum.prepend_extensions"
  10 => "payum.extension.payum.extension.storage.app_entity_paymentdetails"
  11 => "payum.extension.payum.extension.storage.app_entity_payment"

Also the commit you have linked to, has been added in 2016, so I'm not sure if that broke something if it's been there for many years

joesenova commented 1 year ago

Once you have loads of custom twig extensions and overloads, it completely breaks the Payum config builder.

To move that to the last thing that gets auto laded, it just makes sure that the current bundle has all it's state loaded correctly. As Twig can be overloaded and extended in so may ways.

Without being rude, I'll just leave it here as it seems you don't want to understand the problem we are facing and the complexity of our application we have. You have the code so use it you want.

I was trying to help, as this might be an issue on other complex Symfony applications too.

Have a good evening.

joesenova commented 1 year ago
Screenshot 2023-08-12 at 19 52 58

Left:$instance dump with twig moved to the bottom - With this all works 100% Right: @instamce dump with twig above - here no storages are being registered

joesenova commented 1 year ago
Screenshot 2023-08-12 at 20 15 32

Same dum as above, but this show the main object the above snippet is from: Again:

pierredup commented 1 year ago

Without being rude, I'll just leave it here as it seems you don't want to understand the problem we are facing and the complexity of our application we have.

That is exactly what I'm trying to do, I'm trying to understand the issue and figure out the root cause. Even if you have millions of custom twig extensions and overloaded config, it should not cause other configs to be cleared, so I'm trying to figure out why this might happen

pierredup commented 1 year ago

I'll merge the PR and create a new release, since the change shouldn't do any harm, but I think there are other weird things that are being done in your application that overrides some config that it shouldn't which is causing this issue for you specifically

joesenova commented 1 year ago

I’ll send you a list of custom extensions we have running , and what each of them is doing in a nutshell.

I have gone through the entire extension loading phase over and over again. I can’t see anything that is out of place or not loading/working as expected.

we have loads of these extensions and overload on twig.

joesenova commented 1 year ago

@pierredup

I had a look at our custom twig Filters/Functions, we have a Filter setup to get Subscription date form the User table, which has a join to the Payum Transaction table.

This does a call on the Payum gateway->execute(GetRecurringPaymentsProfileDetails).

When I took that Filter out of the equation all started working. This method in UserController includes Payum to get the current user gateway to get the transaction date and some other details.

Are there any "rules" inplace that we need to adhere to when creating these Filters that include Payum service as part of the Twig Env?

I moved the code out of the Filter directly into our User model, all works now. i however think there might be a underlying issue. Feels like when Payum was loaded into the TwigFIlters before the package config was built, causes this issue.

Can we investigate that, and see if there is a deeper issue, or if this is a on-off edge case with our app with all the Twig Extensions and Filters?

pierredup commented 1 year ago

@joesenova Thanks, that's really helpful and narrows the problem down by a lot.

Are there any "rules" inplace that we need to adhere to when creating these Filters that include Payum service as part of the Twig Env?

There shouldn't be, although something weird might happen during the container compilation step. The strange thing is that this seems to only be a problem when upgrading to Symfony 6, so there might be some underlying change in Symfony's DI component that causes this break, so it might be something that we need to handle in our compiler passes

I'll investigate this issue further with the information at hand

joesenova commented 1 year ago

No worries @pierredup, and thank you for the reply.

If you need any other information, please let me know, I have a branch where this is in a "broken" state and will be happy to pull in changes and test.

Or even do some debugging where needed on my code base to help get this issue resolved.

pierredup commented 1 year ago

Okay I think I have found the root cause.

The payum service is set to use the payum.builder as a factory for creating the instance. This means that whenever you inject the Payum class, it will use the PayumBuilder->getPayum() method to instantiate the class. The PayumBuilder class is also defined as a service in the container, so that all it's dependencies can be added.

By default, all services in Symfony are shared, which means you will always get the same instance when requesting a service. Since PayumBuilder is used to create all the dependencies for Payum, and it requires the Twig env which might register an extension that also depends on Payum, it creates a circular dependency. But PayumBuilder is only a factory, and you don't rely on it directly in your classes, so it doesn't cause any errors during compliation.

But what does happen, is the order in which the gateway config is added.

Firstly, the payum.builder is added to the container, then it loads some dependencies.

$container->services['payum.builder'] = $instance = new \Payum\Core\PayumBuilder();

// .... 

$instance->addCoreGatewayFactoryConfig(['twig.env' => ($container->privates['twig'] ?? $container->load('getTwigService'))]);
$instance->addCoreGatewayFactoryConfig(...);

In this case, the twig service is added to the core config before the other config. When the twig service is loaded and an extension is registered that uses the Payum class, the service container will again use the payum.builder factory to create the Payum instance. But this time the payum.builder is already defined in the service container, but doesn't have all the config set yet. So the Payum class will be created, and added to the container (because services are shared by default), which means it won't have all the necessary config added.

There are 2 options to fix this:

@joesenova I have created a PR with the second option as a resolution (#551). If you don't mind, can you please test this fix and let me know if everything is working as expected.

joesenova commented 1 year ago

Thanks for the feedback @pierredup ,

this makes sense, is kind of what I assumed. With the limited knowledge of Payum it was a bit difficult to get to the 100% rot cause. I agree with the 2nd way, your PR, of fixing the problem, this solves future problems too, with other extensions and dependencies.

I will be testing this fix on my environment which s broken, and will revert back soonest.

joesenova commented 1 year ago

@pierredup ,

I have made this change, and still run into the exact same problem.

pierredup commented 1 year ago

Then there might be something else that might also cause some issues. I was able to replicate the error using a Twig extension that uses the Payum service, and the change in #551 resolved the error. Maybe there is some other service config that also needs to be adjusted

joesenova commented 1 year ago

Thansk @pierredup

I will go through the same process I went through the weekend, and see what I can see with regards to the fix and see where it breaks.

Error:

Token storage must be configured. -> vendor/payum/core/Payum/Core/PayumBuilder.php:380


WORKS::

When I change:

FROM:

<service id="payum" class="Payum\Core\Payum">
    <factory service="payum.builder" method="getPayum" />
</service>

TO:

<service id="payum" class="Payum\Core\Payum" shared="false">
    <factory service="payum.builder" method="getPayum" />
</service>

And remove this shared="false" from <service id="payum.builder" class="Payum\Core\PayumBuilder" shared="false">

pierredup commented 1 year ago

The problem with setting shared="false" on the payum service instead of the builder, is that you will get an instance of Payum injected into the twig extension without the storages, while other instances using Payum will have the storages correctly configured. I'm curious why the shared="false" on the payum.builder service does not work. Another option is to set shared="false" on both the payum and payum.builder instances (and set public="false" on payum.builder), but this means a full re-initialisation in every place that Payum is injected, but this will be bad for performance (E.G if you have a 100 twig extensions that rely on Payum, that means 100 initialisations on every request that uses twig, which is not ideal)

pierredup commented 1 year ago

@joesenova Can you try the below on the vendor/payum-bundle/Resources/config/payum.xml config

diff --git a/Resources/config/payum.xml b/Resources/config/payum.xml
index 15c1de8..43b59d2 100644
--- a/Resources/config/payum.xml
+++ b/Resources/config/payum.xml
@@ -18,7 +18,7 @@

         <defaults public="true" />

-        <service id="payum.builder" class="Payum\Core\PayumBuilder">
+        <service id="payum.builder" class="Payum\Core\PayumBuilder" public="false">
             <call method="setMainRegistry">
                 <argument type="service" id="payum.static_registry" />
             </call>
@@ -46,7 +46,7 @@
             </call>
         </service>

-        <service id="payum" class="Payum\Core\Payum">
+        <service id="payum" class="Payum\Core\Payum" lazy="true">
             <factory service="payum.builder" method="getPayum" />
         </service>
joesenova commented 1 year ago

@pierredup

Yes will try this tomorrow first thing in the morning.

joesenova commented 1 year ago

@pierredup

I can confirm this is working perfectly.

Thanks for the assist, I think this is the better way of solving this issue, as the Token Storage was the next issue I was going to run into with the swop of the config loader setting Twig to load last.

If all tests pass on this change with this PR I think we have a winner.

pierredup commented 1 year ago

@joesenova Release 2.5.2 is now available which includes the fix. Thanks for the report and the assistance in testing!