Flutter Android Mollie Integration
This article will help us on how to integrate Mollie as our 3rd party payment method in our Android Flutter Application. (will just add another article for the IOS soonš)
Though the setup are already discussed in Mollie documentation but still Iāll just want to share how I did it and my experiences about it. So, Letās get started? š
First, weāll have to know how Mollie Payments work when integrating it in our application. Hereās the diagram of their Mollieās API flow.
Step 1ļøā£ This is where the customer clicks the payment or checkout button in your flutter app.
Step 2ļøā£(grayed-out) where your flutter app sends request in the background to your Backend to trigger the create payment request to Mollie API then respond to the flutter app the checkout-URL from the Mollieās create-payment API.
NOTE: make sure to include the redirectUrl(will talk more about this later) in your request to be consumed by the Backend server.(The redirectUrl will be included in the payment request to Mollie).
Step 3ļøā£after your Backend Server response the ācheckout-urlā in step 2, make sure to launch that URL using the native browser where the customer can input their payment method infos and then click pay to be submitted directly to Mollie API.
Step 4ļøā£(grayed-out) Mollie will now call your webhook URL(aka BE API to listen that thereās a change of payment status given the id).
Reference: https://docs.mollie.com/overview/webhooks#example
Step 5ļøā£(grayed-out) This is where you should get the actual status of the payment made by the customer in the Mollie API once the Step 4 is done. NOTE: Mollieās reason about why the status is not included in the Step 4 webhook and it is because of the security issues. Hereās how to get the latest payment object from Mollie https://docs.mollie.com/reference/v2/payments-api/get-payment#example
Step 6ļøā£The Mollie process ends and this will be the part where your āredirect urlā will be invoked. So, in our case, from the native browser, it should go back to your flutter app
This is mostly the same with Mollieās docs but I included here the part where flutter is our focus. Hereās the reference:
https://docs.mollie.com/payments/accepting-payments#working-with-the-payments-api
Since we already know how Mollie integration works, we can now proceed to our Proof of concept flutter app and Backend server and weāll also include the explanation on each step of the above mentioned if needed š
Make sure to create a test account in Mollie because we will use its āApiKeyā in our Backend Server.
Reference: https://help.mollie.com/hc/en-us/articles/214041689-How-can-I-test-the-payment-methods-and-webhook-on-my-website-
1ļøā£First, letās create our simple nodejs backend server locally where we will send our payment-request to Mollieās create and webhook endpoint for Mollie to use.
And we also have to make sure that the webhook that weāve passed on to the Mollie will go through the internet because Mollie will contact to that webhook-endpoint via internet.
1.1 Iāve used ngrok for temporary online server in our local machine.
Reference: https://ngrok.com/docs
- Run your ngrok then get your https server link
Weāll make use the ngrok āhttpsā address later.
1.2 We will use an expressjs server.
Reference: https://medium.com/@haybams/build-a-restful-api-with-node-js-and-express-js-d7e59c7a3dfb
Assuming we now have our app.js as our main app and should contain these codes below:
import express from 'express';
import mollieClient from '@mollie/api-client';const mollie = mollieClient.createMollieClient(
{ apiKey: 'test_******************************' }
);const app = express();
// This is to request body from the pay request
app.use(express.json())
// This is to get the id from the request body of mollie's webhook app.use(express.urlencoded({ extended: true })); //app.post('/pay', async (req, res) => { const payment = await mollie.payments.create({
amount: {
//Mollie enforce the correct number of decimals through strings
currency: 'USD',
value: '10.00',
},
description: 'Order #12345',
redirectUrl: 'your-app-scheme://your-app-host',
webhookUrl:'https://************.ngrok.io/webhook',
metadata:{
order_id: '12345',
},
}); res.status(200).send({ checkoutUrl: payment.checkoutUrl });
});app.post('/webhook', async (req, res) => {
const paymentId = req.body.id;
const payment = await mollie.payments.get(paymentId); // Update your DB HERE based on the status of the payment res.status(200).send();
});const PORT = 4000;
app.listen(PORT, () => {
console.log(`server running on port ${PORT}`);
});
So, Now, weāll proceed to our flutter app.
2ļøā£Open your android folder separately in your IDE (Intellij/VSCode), This will help us to use the plugins of the IDE for easy access. Letās get started initialize our android app.
2.1 Setup our intent-filter in our android > app > src > main > AndroidManifest.xml requirements
Reference: https://docs.mollie.com/mobile-apps/getting-started-payments#android
<intent-filter>
<!-- This is where Mollie redirect back after processed the payment. -->
<data android:scheme="your-app-scheme" android:host="your-app-host" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
</intent-filter>
2.2 This is in my Java class in android>app>src>main>java>io>flutter>plugins
package io.flutter.plugins;
import androidx.annotation.Keep;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.FlutterEngine;
/**
* Generated file. Do not edit.
* This file is generated by the Flutter tool based on the
* plugins that support the Android platform.
*/
@Keep
public final class GeneratedPluginRegistrant {
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new com.it_nomads.fluttersecurestorage.FlutterSecureStoragePlugin());
flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin());
}
}
2.3 This is in my MainActivity.kt
import android.content.Intent
import android.net.Uri
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity() {
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
val action: String? = intent.action
val data: Uri? = intent.data
val routeIntent = action == Intent.ACTION_VIEW
val engine = flutterEngine
val redirectUrl = "${data?.scheme}://${data?.host}"
val mollieRedirectUrl = "your-app-scheme://your-app-host"
if (routeIntent && engine != null && redirectUrl == mollieRedirectUrl) {
// Pushing a new route when new intent received
// pops the previous view of the app
engine.navigationChannel.popRoute()
// navigate to new route
engine.navigationChannel.pushRoute("/after-payment-view")
}
redirectToCheckingPaymentResultView(intent.action, intent.data)
}
2.4 Make sure to include the āafter payment viewā in you flutter route and in my case, I have this code in my lib>app>app.dart
@StackedApp(routes: [
...
...
MaterialRoute(page: AfterPaymentView),
])
Assuming we already have an āAfterPaymentViewā Widget
2.5 And to generate itās String route name, Iām using ābuild-runnerā to automatically creates the āapp.router.dartā where contains this auto generated code
Reference: https://pub.dev/packages/build_runner
// GENERATED CODE - DO NOT MODIFY BY HAND
...
...class Routes {
...
static const String afterPaymentView = '/after-payment-view';
}
....
....
I think the setup is already done. Weāll now proceed on accessing our āpayā endpoint in your flutter app.
Assuming we already have a āpayā button in our app that when you click it will automatically access the BE server
Assuming that we already know how to connect to our BE server using the āpackage:http/http.dartā as http
Letās just proceed where we will show the checkout test-webview of Mollie
After accessing the payment endpoint of your local endpoint, it should respond a checkout url and we should launch it so that weāll invoke the checkout webview of Mollie. And in our code, we should have these
var data = json.decode(response.body);
String? url = data.checkoutUrl;
if (await canLaunch(url!))
await launch(url);
else
// can't launch url, there is some error
throw "Could not launch $url";
Note: Iāve added below the reference regarding the library of the canLaunch & launch methods.
and this is the web view of checkout testing of Mollie
Note: In the test web view checkout, you can select what status you want to your testing payment, for flexibility purposes, I guess š
Once we click the Continue, underneath, it will automatically go to its own Mollieās endpoint to process the payment (Note: the webview is Mollieās implementation, afaik, we canāt update its process) and then Mollie will now connects to our webhook (in stepĀ 4) and after everything(stepĀ 5), (step 6) it will redirect back to our app using the redirectUrl that weāve setup in our android Manifest and in the Backend server.
and yeah, thatās it, weāre done YEY! š
Anyway, sorry for the long post š
Other considerations:
To enable payment methods of Mollie
If you have comments or suggestions feel free or even question, Iām glad to answer it as best as I can.
References:
POST Parameters ExpressJS ā https://www.digitalocean.com/community/tutorials/use-expressjs-to-get-url-and-post-parameters
Launching the Mollieās checkout URL ā https://stackoverflow.com/a/43889379
Get JSON request body in express ā https://stackoverflow.com/a/49943829
Android Redirect URL ā https://stackoverflow.com/a/31876044