Adding Paynow payments to a Flutter app (part 1)

Lim Chee Keen
3 min readNov 8, 2023

--

Flutter is the top cross-platform development framework as of 2023. It is perfect for small teams as you can deploy on Android and iOS with a single codebase. Additionally, there are a variety of in-app payment options which integrate well with Flutter’s framework, so developers using Flutter should have no trouble picking those that suit their app needs and customers. These include Google Pay, Apple Pay, PayPal and credit card payments through Stripe.

Here is the problem: there is currently no Flutter library or SDK which offers PayNow integration out-of-the-box. The Stripe PayNow and HitPay Online Payment libraries are exclusively in JavaScript for the client-side. Fortunately there is a way to enable PayNow payment within a Flutter app.

In this 2-part article, I will present the steps to allow PayNow transactions on a Flutter app. I will use the following Flutter packages: cloud_functions and webview_flutter. We also need to deploy a webpage to host a PayNow UI. We will use React deployed with Firebase hosting, but other options will work fine. Lastly, we should have a backend server to create the payment intent and return the client secret.

Flow diagram for Paynow payments on Flutter

Above is the full design of the PayNow payment flow. In part 1, we will tackle getting the payment intent and creating the web page. In part 2 we will create a Flutter page and complete the PayNow payment. Let’s begin with getting the payment intent!

We need Flutter to call the server to create a payment intent and get the client secret. The server also serves as a webhook endpoint for Stripe to call when the payment succeeds. We are using Firebase Cloud Functions in this example.

import 'package:cloud_functions/cloud_functions.dart';

final res = await FirebaseFunctions.instance
.httpsCallable('createPaymentIntent')
.call((<String, String>{
'amount': '$amount',
}));
return res.data;
}

Then we handle the call in the backend. Below is the serverless function deployed to Firebase Cloud Functions. We make use of the Stripe package.

import Stripe from 'stripe'

export const createPaymentIntent= functions.https.onCall(async (data) => {
const amount = data.amount

const stripe = new Stripe(`${YOUR_STRIPE_KEY}`, {
apiVersion: '2022-11-15',
})

try {
const paymentIntent = await stripe.paymentIntents.create({
payment_method_types: ['paynow'],
payment_method_data: {
type: 'paynow',
},
amount: amount,
currency: 'sgd',
})

return paymentIntent.client_secret
} catch (error) {
functions.logger.error(error)
return null
}
})

Before we can show the PayNow UI in Flutter, we first have to deploy a webpage to host the JavaScript SDK for the payment service (Stripe). The code sample uses React but it works with other frameworks like Vue. If you already have this, you may skip to part 2.

The following code is adopted as part of Stripe’s documentation. The packages we are using are react-helmet, react-router-dom, react-stripe-js and stripe-js.


import React from "react"
import { Helmet } from "react-helmet"
import { useSearchParams } from "react-router-dom"
import { Elements } from "@stripe/react-stripe-js"
import { loadStripe } from "@stripe/stripe-js"

export default function PaynowPage() {
const stripePromise = loadStripe(stripePublishableKey)
return (
<>
<Helmet>
<script src="https://js.stripe.com/v3/"></script>
</Helmet>
<Elements stripe={stripePromise}>
<Paynow />
</Elements>
</>
)
}

export function Paynow() {
const stripe = useStripe()
const params = useParams()

const confirmPayment = () => {
if (!stripe || !params.clientSecret) return
stripe.confirmPayNowPayment(params.clientSecret).then((res) => {
if (res.error) {
console.error(res.error)
return
}
if (res.paymentIntent.status === "succeeded") {
window.YOUR_CHANNEL_NAME.postMessage('message_to_flutter')
}
})
}

return (
<div className="paynow-page">
<h1>Paynow Payment</h1>
<button onClick={confirmPayment}>Get QR code</button>
</div>
)
}

Deploy this to your hosting service and we are done with part 1! We will show this webpage in Flutter and complete the payment in part 2.

--

--

Lim Chee Keen

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