Adding PayNow payments to a Flutter app (part 2)

Lim Chee Keen
2 min readNov 8, 2023

--

In part 1, we saw how to create a payment intent and setup a webpage to host a PayNow UI. In part 2, we will create the Flutter webview to complete payment.

Flow diagram for Paynow payments on Flutter

We use the client secret from part 1 and navigate to a new Flutter page which will have a webview.

await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PaynowPage(clientSecret: clientSecret),
),
);

The PayNow page in the Flutter app will accept the client secret and go to the URL to complete the payment.


import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class PaynowPage extends StatefulWidget {
final String clientSecret;

const PaynowPage({super.key, required this.clientSecret});

@override
State<PaynowPage> createState() => _PaynowPageState();
}

class _PaynowPageState extends State<PaynowPage> {
late final WebViewController controller;

void initController() {
controller = WebViewController()
..loadRequest(
Uri.parse(
'${YOUR_URL}'), //enter your URL here
);
}

@override
void initState() {
super.initState();
initController();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Paynow'),
),
body: WebViewWidget(
controller: controller,
),
);
}
}

The user should now see the button to call Stripe to show the QR code. Tapping the button will show this popup. Note: this may look different for Android and iOS.

Users can pay with any participating bank in Singapore with this QR code. Once the payment is processed, we need a way for the webpage to send information to the Flutter app to let the app know what to do for different scenarios like payment success, payment failure, canceled payment, etc. This can be done through the JavaScript channel.

Modify the controller to recieve messages from the webpage.

controller = WebViewController()
..loadRequest(
Uri.parse(
'${YOUR_URL}'), //enter your URL here
)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(YOUR_CHANNEL_NAME,
onMessageReceived: (message) {
if (message.message == 'my_message') {
// do something
}
if (message.message == 'my_other_message') {
// do something else
}
});

That’s it! You can add other elements to the payment flow like Webhooks and saving the QR code to the device, but we will not cover this here. One point to note is that there is a time limit of 1 hour to complete the transaction starting from the time the payment intent was created.

If you want to see the finished product of this workflow, download Bizbulk’s app on Google or Apple. Hope this helps save transaction costs for your app!

--

--

Lim Chee Keen

Former Navy Captain Turned Software Engineer | Flutter & React developer | ML & AI programmer | Co-founder for Group Buy service