Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix payment loading for custom payment pages #2901

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

Magnusrm
Copy link
Contributor

@Magnusrm Magnusrm commented Jan 13, 2025

Description

This PR moves the "automatic" logic from the PaymentComponent into new context, "PaymentPayment".
This allows us to set a loading state that displays loading of the whole form, and not hust the payment component, while we are navigating to Nets or other payment providers and while we are navigating to the next process step after a payment is complete.

The payment provider seems to need to be placed within the PDFWrapper in order to be able to call next() from useProcessNavigation.

Related Issue(s)

Verification/QA

  • Manual functionality testing
    • I have tested these changes manually
    • Creator of the original issue (or service owner) has been contacted for manual testing (or will be contacted when released in alpha)
    • No testing done/necessary
  • Automated tests
    • Unit test(s) have been added/updated
    • Cypress E2E test(s) have been added/updated
    • No automatic tests are needed here (no functional changes/additions)
    • I want someone to help me make some tests
  • UU/WCAG (follow these guidelines until we have our own)
    • I have tested with a screen reader/keyboard navigation/automated wcag validator
    • No testing done/necessary (no DOM/visual changes)
    • I want someone to help me perform accessibility testing
  • User documentation @ altinn-studio-docs
    • Has been added/updated
    • No functionality has been changed/added, so no documentation is needed
    • I will do that later/have created an issue
  • Support in Altinn Studio
    • Issue(s) created for support in Studio
    • This change/feature does not require any changes to Altinn Studio
  • Sprint board
    • The original issue (or this PR itself) has been added to the Team Apps project and to the current sprint board
    • I don't have permissions to do that, please help me out
  • Labels
    • I have added a kind/* label to this PR for proper release notes grouping
    • I don't have permissions to add labels, please help me out

@Magnusrm Magnusrm changed the title Fix/payment-loading Fix payment loading for custom payment pages Jan 13, 2025
@Magnusrm Magnusrm added the kind/bug Something isn't working label Jan 13, 2025
@Magnusrm Magnusrm self-assigned this Jan 13, 2025
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
17.9% Coverage on New Code (required ≥ 45%)
0.0% Condition Coverage on New Code (required ≥ 45%)

See analysis details on SonarQube Cloud

@Magnusrm Magnusrm marked this pull request as ready for review January 17, 2025 09:08
@bjosttveit
Copy link
Member

The payment provider seems to need to be placed within the PDFWrapper in order to be able to call next() from useProcessNavigation.

Could it be added to the FormProvider here instead? It should only require the ProcessNavigationProvider, not the PDFWrapper.

<OrderDetailsProvider>
  {hasProcess ? (
    <ProcessNavigationProvider>
      <PaymentProvider>
        <Provider value={undefined}>{children}</Provider>
      </PaymentProvider>
    </ProcessNavigationProvider>
  ) : (
    <Provider value={undefined}>{children}</Provider>
  )}
</OrderDetailsProvider>

@Magnusrm
Copy link
Contributor Author

Could it be added to the FormProvider here instead? It should only require the ProcessNavigationProvider, not the PDFWrapper.

I did try this, but it did not work. The backend would get stuck and eventually fail with PDF generation failed. I did not manage to find out why 🤔

Copy link
Contributor

@olemartinorg olemartinorg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I like the direction this is taking, but I have a few concerns - see comments.

<PresentationComponent type={ProcessTaskType.Data}>
<Form />
</PresentationComponent>
<PaymentProvider>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably be placed late in <FormProvider> instead, otherwise the Payment component will now crash the app if placed in:

  • Stateless forms
  • Custom receipts (!)
  • Sub forms

None of these are probably likely use-cases, but it's better to validate that in the component than having the whole app crash because of it. FormProvider is 'the place' for all providers needed by form components, at least.

export const PaymentContext = createContext<PaymentContextProps | undefined>(undefined);

export const PaymentProvider: React.FC<PaymentContextProvider> = ({ children }) => {
const [loading, setLoading] = useState<boolean>(false);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why this has to be a separate state. Why not use busy directly, instead of setting loading when busy && !paymentError? Double state-keeping like this can lead to brittleness, as I've already seen in the busy state for that matter.. If this is loading should probably be a function of 'is the call to process/next running', which you can get from tanstack (see for example useIsSaving()).

Comment on lines +67 to +69
if (loading) {
return <Loader reason='Navigating to external payment solution' />;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There doesn't seem to be a way to get away from this loader, but that might be intentional? 🤔

return <Loader reason='Navigating to external payment solution' />;
}

return <PaymentContext.Provider value={contextValue}>{children}</PaymentContext.Provider>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this can lead to re-renders of all form components when anything in this provider changes. The contextValue object is at least not memoized, so this context will provide a new value every re-render. Have you checked if this affects anything when (for example) submitting a form (not necessarily a form with payment)? If useProcessNavigation() causes this to re-render because process navigation became busy, that might lead to expensive re-renders immediately when the submit button is pressed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Btw, these things are difficult to get right, which is why we made createZustandContext() and used that pattern in many of these global form-providers. That way you can set all the state you want, change it however you want, but never cause full-tree re-renders unless every component in the tree uses the state from zustand.

@olemartinorg
Copy link
Contributor

I did try this, but it did not work. The backend would get stuck and eventually fail with PDF generation failed. I did not manage to find out why 🤔

@Magnusrm What happens if you just load the instance with ?pdf=1 in the URL? In my experience, the full PDF generator adds a lot of complexities that can fail, so the provider placement may not be the issue there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Components in payment layout-set show while create payment is loading
3 participants