Flutter Android Mollie Integration

June Ligan
7 min readSep 8, 2021

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šŸ˜ƒ)

Photo by Nadi Lindsay from Pexels

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.

Mollieā€™s Payments API flow overview
Mollieā€™s API flow overview as of Sept. 4, 2021

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

This is the terminal after clicking the downloaded ngrok application
ngrok link of your local server in the internet

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

The created payment in Mollie

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.

The transaction is now paid

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

--

--