cypress icon indicating copy to clipboard operation
cypress copied to clipboard

After version 5.5 Stripe Checkout tests break out of the iframe

Open TuureKaunisto opened this issue 3 years ago • 34 comments

Current behavior

When testing a Stripe Checkout workflow the stripe.redirectToCheckout or the redirected page breaks out of the iframe and instead of completing the test just hangs.

Desired behavior

I'd expect the application to not break out of the iframe and the test runner to continue working even after being redirected to the Stripe Checkout page. (Like it did with version 5.5.0 and below)

Test code to reproduce

Here's a link to a repo with a simple React app to reproduce the issue. https://github.com/kide-science/cypress-test-tiny

Unfortunately I was not able to reproduce the issue with vanilla JS. Stripe seems to do the redirect with:

window.top.location.href = redirectUrl

Setting the top.location.href seems to work without breaking out of the iframe, which leads me to believe that there are additional checks before the redirect or on the Stripe hosted Checkout page after the redirect that cause the test to break out of the iframe.

Versions

Cypress version: 6.0.1 Last known working Cypress version: 5.5.0 Browser: Chrome 87 MacOS Mojave

TuureKaunisto avatar Dec 03 '20 14:12 TuureKaunisto

This is a blocker issue for us. thanks for creating a bug @TuureKaunisto

@jennifer-shehane - any ETA or workaround to this issue?

rangabarath avatar Dec 04 '20 04:12 rangabarath

I am able to see the issue of the application taking over the entire Test Runner frame. This is pretty weird because I can only get this test passing on Cypress 5.5.0. Before 5.5.0 and after 5.5.0 break out of the frame.

If I had to guess, I would suspect that this PR made this test work: https://github.com/cypress-io/cypress/pull/8827 and we had to make a patch to that behavior since then because it broke other apps. https://github.com/cypress-io/cypress/pull/9018 (I haven't verified this though).

5.5.0

Screen Shot 2020-12-04 at 2 05 17 PM

5.4.0 / 5.6.0

jennifer-shehane avatar Dec 04 '20 07:12 jennifer-shehane

I can reproduce it for other sites as well. Verified on 5.3 and 5.5.

AmazingTurtle avatar Feb 08 '21 12:02 AmazingTurtle

yes, having the same issue. Hope it get's resolved soon! I think this is something that Stripe has to fix, as it's happening in other iframed situations too, not only in cypress.

the-baaron avatar Feb 14 '21 13:02 the-baaron

can confirm the issue, waiting for a fix for this.

viewportdesign avatar Feb 16 '21 19:02 viewportdesign

yes, having the same issue. Hope it get's resolved soon! I think this is something that Stripe has to fix, as it's happening in other iframed situations too, not only in cypress.

I think Stripe tries to break out of iframes for good security reasons and are unlikely to change that behaviour since it's surely intentional.

TuureKaunisto avatar Feb 19 '21 14:02 TuureKaunisto

I bumped into the same issue when i tried to prepare E2E test for stripe payment flow. Here is how i handled it, maybe it will be useful for somebody.

I found that stripe checkout url can be generated with this call. await stripe._controller.action.createPaymentPageWithSession({ betas: stripe._betas, mids: stripe._mids(), sessionId: '' }); I simply wrote generated url to file and then run another test where read url from file and visit checkout page.

It's usage of not official stripe sdk methods and hack on hack, but i think it's ok for me as i still want to use cypress.

MikeKoval avatar Feb 21 '21 18:02 MikeKoval

@jennifer-shehane is this issue something that might get fixed in an upcoming version? Perhaps the conflict with existing apps that rely on the current behaviour could be solved by implementing some setting that enables the Stripe compatibility only for those that opt in.

TuureKaunisto avatar Feb 25 '21 11:02 TuureKaunisto

Having this issue as well, would love a clear statement on whether it's done intentionally like @TuureKaunisto suggested.

AlbertMN avatar Mar 12 '21 15:03 AlbertMN

Having this issue as well, would love a clear statement on whether it's done intentionally like @TuureKaunisto suggested.

I got a reply confirming it's intentional from Stripe support:

Checked-in with our engineering team, and they confirmed that Checkout is behaving as expected. Additional context: We intentionally do a top level redirect in Checkout and it is by-design that we don't accommodate iframes (it breaks wallets, certain redirect flows, etc.). It seems like some versions of Cypress hacked around this and their hack now no longer works or they removed the hack.

TuureKaunisto avatar Apr 20 '21 09:04 TuureKaunisto

@TuureKaunisto yeah, got the same reply some time after my comment - forgot to add it :) Think this means the issue can be closed.

AlbertMN avatar Apr 20 '21 10:04 AlbertMN

Before closing it would be good to have a suggested workaround from someone at Cypress. Not being able to test checkout flows is pretty problematic.

wilsonpage avatar Apr 20 '21 10:04 wilsonpage

For us the best solution would be to have a setting that enables the hack that fixes this in 5.5. If it's off by default, it shouldn't break other apps. Otherwise we're stuck at using 5.5 if no other workaround is found. Not testing the payment workflow is not an option.

The workaround @MikeKoval suggested seems promising, but I would strongly prefer to write a test that tells the runner to click a button rather than calling a Stripe method manually since then I'm not testing if the button works anymore.

Would it perhaps be possible for the runner to detect when the iframe takes over the whole browser tab and handle the case more gracefully? I think the last I checked the whole test just froze.

I'm hoping Stripe Checkout is popular enough to warrant considering these otherwise suboptimal solutions.

TuureKaunisto avatar Apr 20 '21 13:04 TuureKaunisto

Stripe have a change in beta which exposes the final checkout url when requesting the checkout-session-id. This means you don't need stripe.redirectToCheckout(). You can request beta access by reaching out to them (see my issue).

I've tested it and it works great, but I'm still unable to test the checkout flow as Stripe Checkout fails to run when detecting it's inside an iframe 😩 So we still need something on the Cypress side to spoof checkout.stripe.com into thinking it's not inside an iframe.

For now I'm relying on jest-puppeteer to test my checkout flow fully, but I would rather be able to do this using Cypress.

wilsonpage avatar Apr 23 '21 12:04 wilsonpage

The response from the Cypress team is that iframe support is coming. I know this isn't a great solution for people today and it's a big feature so will take time to build. But if we take time aside to find workarounds for all the situations for features that aren't truly supported, then it will delay our team delivering the actual feature.

Stripe testing is a big request for iframe testing and we'll definitely cover the most common testing situations. As mentioned earlier, Stripe intentionally doesn't want itself run within an iframe, so we have to address the core issues.

This issue was updated to reflect the API's we're currently working on: https://github.com/cypress-io/cypress/issues/136

jennifer-shehane avatar Apr 26 '21 20:04 jennifer-shehane

oh my fcking god. Now you're telling me not built yet? fuck... I almost launch production with it..

tngflx avatar Jun 29 '21 07:06 tngflx

Anyone got any ideas how we are meant to test this then?

We still need the stripe webhooks to fire after completing a test with stripe checkout. The best of bad bunch of ideas I had was implementing the creation of the subscription logic (yikes) via the API then hoping could progress to our success screen after that in the tests.

jacktomlinson avatar Jul 22 '21 10:07 jacktomlinson

Anyone got any ideas how we are meant to test this then?

We decided to stick to using the only working version: 5.5.0 until there is a fix or a better workaround.

TuureKaunisto avatar Aug 09 '21 14:08 TuureKaunisto

implementing the creation of the subscription logic (yikes) via the API

This is what we did. I hated every second of it but it works fine.

vojtapol avatar Aug 18 '21 15:08 vojtapol

@MikeKoval

I found that stripe checkout url can be generated with this call. await stripe._controller.action.createPaymentPageWithSession({ betas: stripe._betas, mids: stripe._mids(), sessionId: '' }); I simply wrote generated url to file and then run another test where read url from file and visit checkout page.

I am currently trying to implement @MikeKoval's work around but am having trouble generating a session/ sessionId inside of Cypress, can anyone share an implementation for the creation of the subscription logic as discussed above?

crispinamuriel avatar Oct 01 '21 13:10 crispinamuriel

@crispinamuriel what trouble do you have? please provide more details, i can try to help. you can DM also. My E2E workaround looks like this:

 cy.window()
    .then(async window => {
      const { object: { url } } = await window.stripe._controller.action.createPaymentPageWithSession({ betas: window.stripe._betas, mids: window.stripe._mids(), sessionId });

      cy.writeFile('stripe.json', JSON.stringify({ url }));
    });

In app itself i just listen additional query parameter provided by E2E test to prevent this redirect to checkout page:

await stripe.redirectToCheckout({
          sessionId: data.sessionId,
        });

MikeKoval avatar Oct 03 '21 08:10 MikeKoval

Thank you @MikeKoval , I will try this. I tried to create a session inside the cypress tests and not my app and was getting stuck there.

crispinamuriel avatar Oct 03 '21 14:10 crispinamuriel

We just started getting an error on our Stripe tests using Cypress 5.5.0 which was fixed by adding the following to the beforeEach in our tests:

cy.on('uncaught:exception', (err) => {
  // Allow stripe error: "paymentRequest Element didn't mount normally"
  if (err.message.includes('paymentRequest')) {
    return false;
  }
});

You can also do the same globally with Cypress.on('.... if required.

alexbrazier avatar Jan 11 '22 18:01 alexbrazier

Recently I was trying to apply some workarounds to this 'Stripe issue' and update cypress to version 9.2.1 but nothing really works well. I had to revert it back to 5.5

vladmykol avatar Jan 12 '22 11:01 vladmykol

Is there a hack to set window.top on modern web browsers?

This seems like a security thing and maybe isn't fixable in code (like cypress) that runs alongside stripe. Using a framework like puppeteer might be the way to go since it relays commands to the web browser. Will let you know if I figure something else out tho. https://docs.cypress.io/guides/references/trade-offs#Inside-the-browser

rrabello-asp avatar Mar 08 '22 05:03 rrabello-asp

Hello,

I am facing the same issue as well. I am using cypress 9.0.0 and have currently no intention of downgrading just to solve the stripe problem as this will introduce just some other issues. Also downgrading 4 Major versions seems like a bad thing to do. Are there any updates on this issue? Is there a way to really test the full checkout flow? like pressing a button to open the checkout window.

Thanks.

Quasarman avatar Jun 27 '22 07:06 Quasarman

The same issue for me, Have had to just have the test redirect when the pay button is clicked, which means it cant test if there is an error before the redirect.

cy.get('#pay-button').contains('Pay').click()
cy.visit(URL+'/confirmation')

doomedramen avatar Jun 30 '22 16:06 doomedramen

using cypress-plugin-stripe-elements to get around this https://www.npmjs.com/package/cypress-plugin-stripe-elements

CrispinaRMuriel avatar Jun 30 '22 19:06 CrispinaRMuriel

This worked for me flawlessly ! Also it removes the iframe issue. Not sure if people would accept it as a real e2e tests tho.

https://github.com/riccardogiorato/cypress-for-everything/blob/main/examples/stripe/cypress/e2e/stripe-checkout.cy.ts

rafaelaazevedo avatar Sep 13 '22 22:09 rafaelaazevedo

Any update on this issue? it's pretty much impossible to test Stripe checkout atm with Cypress.

dmolin avatar Oct 03 '22 14:10 dmolin