Stripe Checkout in mobile Flutter app
Have you ever struggled to integrate card payments into your mobile Flutter app? If so, today is your lucky day! In this post, I present how to use Stripe Checkout in the Flutter app without any hassle!
. . .
What is Stripe Checkout?
Stripe Checkout is a feature from Stripe which handles collecting all the payment data for you. Normally, services like Stripe, Square or Braintree offer you storing all payments information your app collects but they leave the UI part to the developers. This means having to handle all those boring stuff like card validation, 3D authentication, etc. which is not something you as a developer want to do.
Stripe Checkout is a customizable website to which you can redirect your users and where all the data will be collected and send to the backend. It leaves very little for you to worry about, therefore it’s easier and safer than the traditional way.
. . .
Payment flow
The flow is relatively easy, user presses the button, we (as a mobile app) ask the server to create a session, server returns the session id, we redirect to checkout page with that id. That’s the beauty of Stripe Checkout, it leaves very little to do for us as mobile developers.
. . .
Server Stub
Since I want this post to be as understandable as possible, I decided to create a stub of a real server. We will create a Server
class in our Flutter app which will call Stripe API. This reduces the complexity of this workflow as we only need one source code to get the payment, however, it also introduces HUGE SECURITY VULNERABILITY as in the example I will have a Secret Key obtained from Stripe in my mobile app. This is a big no-no. You can’t have your secret key installed somewhere in your users’ devices. I know it works but please, DON’T DO IT IN REAL APPS. This stuff should be done by the backend team. ?
. . .
Getting the session id
Let’s start with simple button which will call our “server” and get the new session id for the checkout:
. . .
Preparing the webview
Stripe checkout works only in web technologies (HTML + CSS). It means we have to use WebView package to display it. What’s more, we need to have control on our webview so that we can include stripe script in it to redirect to the checkout page later on.
Notice that our HTML page is stored in a normal string and then just encoded. It could also be loaded from the file if you prefer it that way. It also has the <script>
tag with the Stripe library being loaded.
Now we can redirect to the CheckoutPage
after obtaining the session id:
. . .
Redirecting to Stripe Checkout
Once we have the webview, we can use it do display the Stripe Checkout page. According to the docs, all we have to do is call js function stripe.redirectToCheckout(...)
. Luckily, WebViewController
allows us to just run any javascript code inside the webview and that’s what we’ll do.
The important part here is when we will do it. We would like to redirect to checkout as soon as possible but unfortunately, we need to wait for stripe library defined in the previous step to be loaded. Because of that, we will not call it in initState
but in onPageFinished
callback which is being called only after all the scripts have been downloaded.
And this is enough to proceed with the payment!
. . .
Handling the result
After completing the form, the payment will be handled by Stripe. It will also send the webhook event to the backend to process the transaction from the business perspective. As a mobile developer, all you have to do is nicely show the user, that the checkout has succeeded. How can we do it?
When we created a checkout session in the “server” we passed the successUrl
and cancelUrl
. Stripe will redirect the user to those URLs based on the payment result. Obviously, we can just have prepared the website with proper message etc, but what we can also do is catch those redirects and handle the result directly in Flutter. All we have to do is look for certain URLs used in the webview using navigationDelegate
.
. . .
And that’s it!
Now the whole payment process looks like this:
Important notice on Live integration!
Some of you mentioned that what I showed you in the video doesn’t work for Stripe live, and I am sorry to say that it is true. HOWEVER! There is an easy solution to that, basically, we need to have our simple HTML deployed on the Https website and it will still work! I’ve put the same HTML in a repository and deployed it on GitHub pages and it worked on live! I’ve just paid myself 2 PLN 😀 I’m sorry for not checking it on live before publishing. Let me know if it works for you.
Notice how little work it required comparing to creating the form by yourself!
As always, the whole code is on the Github [here].
The YouTube version is [here].
And if you want to learn more about Stripe Checkout, the official docs are very well written.
If you have any questions or ideas for improvements, let me know in the comments!
Cheers! ?
Marcin Szałek
Founder of Fidev
Flutter enthusiast since Alpha release in 2017. Believes that sharing is caring, which lead him to start a technical blog dedicated fo Flutter in its early days. Loves to see beautiful designs become real apps and is willing to help make it happen. Enjoys sunny beaches far from home.
Join the newsletter!
Join the newsletter to keep track with latest posts and get my special Widget to animate views' entrances without any hassle for FREE!
Check out other posts!
Stripe Checkout in Flutter Web
Flutter Web is getting more mature every day. If you want to accept payments using Stripe Checkout in your Flutter web application, this article is just for you!
Interacting with Widgets using Framy
Have you ever developed a widget or a page and you wanted to make sure it works correctly in different scenarios but then it turned out that you can’t just reproduce all the cases you want to cover? Framy may solve such problems!
Water Drop effect in Flutter
In this post, I’d like to share with you how to achieve a water drop effect in Flutter. Why would you spend your time learning such skill? Well, I have no idea but it looks cool so let’s get started! ?
line_items is just about product but i also want to charge the shipping cost, please reply
I’m not sure but I think you can just add another product as “Shipping”.
my WebView is keep freezing, any solution ? ?
Great article, works perfectly on dev mode. But Https is required for initial URL in production mode …
Correct. It is 🙂
Fantastic article, thanks for sharing, so is there a way to make it work with production keys? In the github code http is used, and in the web https, but it does not work.
ahhh, I just read in the continuation of the article that you have to activate client-only integration. I have to try it! thanks!
Sorry. I thought it was a “stripe for web” post where it had more sense. You were right, however I found the solution for that:
We need to have our simple HTML deployed on the Https website and it will still work! I’ve put the same HTML in a repository and deployed it on GitHub pages and it worked on live! I’ve just paid myself 2 PLN ? I’m sorry for not checking it on live before publishing. Let me know if it works for you.
Yes I have made it works using an Https page it was the easy part and it works live using Android.
Unfortunaltely for IOS my subscription require Apple in-app purchase to be published … 30% of commission and lot of time lost to integrate their buggy solution 🙁 ! (and you need a real iPhone to test it of course ….) .
Even if I point to ‘https://raw.githubusercontent.com/MarcinusX/test1/main/index.html’ I get this error now: “Blocked script execution in ‘https://raw.githubusercontent.com/MarcinusX/test1/main/index.html’ because the document’s frame is sandboxed and the ‘allow-scripts’ permission is not set. I’m on Android 10.
Where should our secret api key be stored in a production app?
It should NOT be in your app. Put it on your server. I’m using Google Cloud Functions (with python) and the key is in that function. https://youtu.be/vr0Gfvp5v1A
Great Article
Thanks
i want to redirect to login page once payment is done. How to do it?
Great Article
Thanks
i want to redirect to login page once payment is done. How to do it?
Great share, thank you Marcin. But I’m having issues with the redirect to stripe. I see one of them was maybe answered below with https, but one of them was with CORs. I don’t see a way to set headers with this _controller.evaluateJavascript(redirectToCheckoutJs); Do you know of a way?
Is it possible to remove the back arrow and logo on the top left of the stripe web checkout form ?
I don’t think so
I have a problem of “Unrecognized feature: ‘payment’.”, source: https://js.stripe.com/v3/