I have been experimenting on building a self-service app that accepts transaction based payments. I was looking into Paypal and Stripe and at the moment started off with Stripe. Stripe has a simple checkout that launches their own popup, accepts the payment data and submits it to their servers and returns a token (all happening in the browser) and then this token can be submitted from the browser to your server code. The server code would take the token and does the actual charge. Apart from I had to do the integration into a VueJS app and load the dependent js files dynamically only when a user wants to pay, the integration was pretty smooth. Best thing is that Stripe website has some cards for testing the integration which comes very handy.
As I was testing the various cards listed, I saw sample 3D Secure cards and they haven’t worked with my integration. I got an error that the card got declined. So, I read further documentation on 3d Secure cards and how the integration works. Stripe has something called Elements which allows you to embed the payment UI right into your website (but still secure and you won’t know your customer data) and they have an API for 3d secure cards based on the elements. However, given I already did the token based checkout integration (which is actually very simple and intuitive for end users), I didn’t wanted to start over. I managed to do the integration and the rest of the post is about how I did it.
Thing is, not all cards are 3d secure cards and even those that are, not all require going through the 3d secure payment process. So, it’s important to be able to detect when that flow is needed and when it’s not. Unfortunately, the card object given in the token payload from the regular checkout process doesn’t have this information on whether it needs the 3d secure process or not. To over come this, rather than directly submitting the token payload to the sever in the token call back, we can first create a source using the token.id. This returns a source which contains a card with more details and one of them is whether the three_d_secure is required or not. If it’s not required, just submitting the source payload to the server (instead of the token payload) should be sufficient given that the server side charge seamlessly works with token id or source id (of course, you need to make sure the server side code covers all these cases in a generic manner).
Assuming the three_d_secure is required, then we need to create another secure 3d source from the first source. This will be the 3rd call from browser to stripe (token -> source -> secure source). As part of this 3rd call, we also need to specify a return url. Unlike the previous flows that rely on a callback being made from the current browser page to the server, the 3d secure payment process requires first redirecting to a 3rd party where the payment details can be confirmed and after that a redirect is done to the browser. The redirect url can contain any context information that is needed to tie back to the product/service being purchased.
The return URL provides the 3d secure source id, client secret and livemode parameters. This 3d secure source id can be passed to the charge API like the token id or the first source id obtained from the token. Assuming the user went through the 3d secure payment flow and confirmed the payment, the server side API will succeed. Otherwise the same card declined error will happen.
Stripe even has a 3d secure card payment test page. Interestingly, the page has both to accept the payment and to cancel and in both cases the same callback URL with the same parameters seems to be passed. This makes it not know whether the user went through the confirmation process or not and have to issue the server side charge call to figure out what happened.
Note that, even when a card requires 3d secure process, the redirect object can indicate redirect.status as succeeded which means we don’t need to go through the redirect process. This is probably a decision either the card issuer or Stripe (with it’s ML) making that decision to reduce friction and enhance UX.
Overall, this exercise made me to learn a bit more about the online payment integration and the different types of payment flows. Hope this write up helps anything trying to do something similar.
I made the same mistake. They should really warn you about the checkout solution that it doesnt handle 3dsecure. Anyway, how do you create a source using the token.id?