/ javascript

The curious case of the broken credit card form

I work at Spoke and we have a great QA team. Once when I asked angad from our QA team how things were looking, his reply was “there aren’t any issues yet.”

One morning our QA team pinged us to let us know that our credit card forms didn’t work on IE. We use Stripe Elements for our billing purposes. Stripe Elements works in a very interesting way, you create an empty div where you want the credit card input to show up, and Stripe does all the heavy lifting by inserting an iframe which accepts the credit card info and returns an opaque token, which cannot be used to reconstruct the credit card data. This makes things like PCI compliance simple.

Anyway getting back to our story, our very first reaction was, “maybe Stripe Elements has an issue with IE 11,” so we contacted them and learnt that things were working on their end.

Suchit was poking around developer tools on IE when he saw a dreaded CORS error on the console.

SEC7118: XMLHttpRequest for https://js.stripe.com/v3/ required Cross Origin Resource Sharing (CORS).

But Stripe should take care of CORS! Why does it not work on IE11 alone? So now we’re trying to figure out if IE handles the pre-flight options in a different way or why the headers we’re getting back are amiss.

Now amidst our poking around we realized that our credit card forms didn’t work on MS Edge either. That made things a lot worse! Well until we decided to test production, “Its broken on production too!”

“Has Stripe just not realized they’ve broken something yet?” We still hadn’t accepted that we had something wrong on our end.

We tried a bunch of things, including an attempt to binary search our release versions to find out when our form broke and hopefully find the offending change. With some work we got a 2 month old version of our app working! Nope no dice, it was still broken. In the meanwhile, to confuse us further the CORS issue was not a consistent repro. In fact it just stopped showing up.

Pretty confused at this point, we started to compare the difference between the iframe inserted on chrome and IE. That's when we realized the iframe was empty on IE. Our next step was to open the iframe URL on a tab of its own to see if that opened up or caused issues.

When we open it up we see a log statement on the console, something about the font not rendering correctly. Stripe allows you to pass a custom font in for the input field and we used that to style our input fields.

So what was wrong here? Well this was where things got interesting. We base64 encoded our custom font and passed that to the stripe SDK. We never checked exactly how Stripe used this data. Turns out Stripe just put all the data as part of the URL to the iFrame, essentially a GET. This is where we learnt that IE has a max limit of 2083 characters.. Our base64 encoded font was well over 15,000 characters

Time to test out our theory! Disable our custom font and the credit card input work fin on both IE11 & edge. According to this post, Chrome, firefox and Safari support URLs of upto 100K chracters.

After this it was simply a matter of serving our font using URLs instead of dumping the entire content in the src. We should have probably done this in the first place but we had some issues getting it to work and base64 encoding seemed like a valid solution.

I wouldn’t say supporting only 2000 characters was the worst thing you could do. Hey Microsoft even had a site clearly stating this. But what would have saved us quite a bit of time, was a related & comprehensible error somewhere. And no, a random spurious unrelated CORS header doesn't count!

A big thank you to my partner in crime during this investigation, Suchit Agarwal and our employer Spoke. If you are ever looking for a better way to manage your workplace requests make sure you check us out!