Note: All the information in this post and the final project is in an experimental sandbox as these matters require regulatory oversight from the concerned authorities.
UPI is amazing! The seamlessness and convenience it provides are great. However, there are a few problems I noticed with UPI that we’ll be looking into in this post.
A few of those issues include:
I wanted to tackle the third issue primarily, and that is when I realized that UPI QR Codes are nothing but simple upi://
protocol URLs with some basic information about the receiver’s UPI Address in the following format:
upi://pay?pa=<merchant_upi_id>&pn=<payee_name>&am=<amount>&tn=<transaction_notes>
This standard format enables different UPI Apps to read from a single UPI QR Code, and it’s one of the most brilliant innovations from NPCI in my opinion.
Now, there’s a simple customization hack that can be done to enable UPI QR Codes to be used with other payment methods, one that doesn’t involve making any changes to the existing UPI standard, but working around it to enable other payment methods.
A brief overview of a possible solution and the one that I would be working on in this post is:
upi://
protocol.
The Merchant Onboarding and Verification Flow will look something like the following:
The Post Payment Flow would look something like the following:
Let me be honest, if you’re a Product Manager reading this post, I know you’re most likely going “What a terrible idea! This just slows down the user process.” and you would be correct.
When people use UPI, they do so for the convenience of simply scanning a QR code, entering an amount and a pin, and going on with their life. Users don’t like using Credit Cards or swiping Debit Cards at Small Grocery stores because of the following reasons:
The process we described in the above section does not simplify the experience for the end-user at all, it is merely a way/experiment to use UPI’s ubiquitous standard and reach to give a niche set of users who would use providers like BNPL Providers or Credit Cards or what not to make payments.
One might be correct to identify that since a step of the merchant onboarding involves them scanning the QR Code connected to their UPI ID, what’s stopping someone else from registering the QR Code under their name and registering their bank account to trick other people into paying them instead of the shopkeeper/merchant.
There is a simple way to verify a UPI ID belongs to a specific person or a merchant, one of them is obviously to use the APIs available to check the phone numbers linked to a UPI ID that has been scanned.
Another, more robust version is to use the following flow:
Only verified QR Codes can be used to make payments post this change integration. At a regulatory level, KYC can help alleviate a lot of the concerns related to identity fraud.
The following parts of the post are going to be a little technical, so please follow along if you’re curious about developing this as even I am not sure how this will work.
We’ll be using the following setup:
/api
.Supabase already provides a useful auth
table set that’ll store information about our users, we’ll however need to create a few more tables for merchant, transaction and payment-related data.
For a start, the following database setup should do well for us:
Supabase has a great feature called Row Level Security Policies that allows you to specify who has access to what resources in which tables. Consider it similar to Firebase Security Rules that work well with your Authentication layer to give you useful information and lets you decide the action the user can perform, ranging from Select, Create, Update, and Delete.
On our backend, i.e: Trusted environments, we’ll be utilizing the Supabase Service Role Key from our project to initialize our client, so we get access to the entire supabase ecosystem and bypass row-level security policies for resources to get everything done quickly.
We’ll create a couple of clients for our Supabase app, one with regular privileges for our client-side, and one with full privileges to our backend resources, to be used only on the server-side for backend processing.
// api/supabase/index.js
import { createClient } from "@supabase/supabase-js";
let supabase;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_KEY;
if (!supabase || typeof window === "undefined")
supabase = createClient(supabaseUrl, supabaseKey);
export default supabase;
// api/supabase/adminClient.js
import { createClient } from "@supabase/supabase-js";
let supabase;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_KEY;
if (!supabase || typeof window === "undefined")
supabase = createClient(supabaseUrl, supabaseKey);
export default supabase;
To remove the entire hassle for our users to have to log in with email/password or phone numbers and worry about OTPs, we’ll just offload the authentication work to Google.
Supabase has a sufficiently good implementation of Google OAuth-based login for our users to use.
Supabase has a guide on setting up Google Auth for your project.
// api/auth.js
import supabase from "./supabase";
export const signInWithGoogle = () =>
supabase.auth.signIn(
{ provider: "google" },
{ redirectTo: window.location.toString() }
);
export const verifyAccessToken = (accessToken) =>
supabase.auth.api.getUser(accessToken);
When you’re using a mobile app to make payments via UPI, the experience is seamless, you open your UPI App, scan the QR Code and proceed. We want to build something similar for the web, a way to scan QR Code interactively by just allowing the website permission to your camera for a specific amount of time, pointing your device at the QR Code and making the payment.
I was surprised to see there are already amazing libraries in JavaScript that will help us do the same, listing them below:
You can find a nice demo of the qr-scanner
library here: https://nimiq.github.io/qr-scanner/demo/
I decided to use it because it is light and has the handling setup for a lot of things out of the box including Flashlights, Camera Lists and even turning camera usage off when you switch the tab to some other tab or become inactive.
QR Codes are nothing but pieces of text, most often links. The only thing we need to remember about UPI QR Codes is that they are links but start with the upi://
protocol as discussed before.
The UPI ID is stored in the pa
parameter in the query parameters of the URL.
Getting the UPI ID is very simple:
const getUPIIdFromQRLink = (link) => {
return new URLSearchParams(
new URL(link.replace("upi://", "https://")).search
).get("pa");
};
As mentioned above in the flow diagrams, we would need a webhook from Razorpay to confirm that payment was received. With the information present in the notes
field for the payment order created, we’ll link it to a specific transaction that the user tried to complete.
If you’re not aware of what webhooks are, you can read this post of mine.
Using Next.js’ api
routes feature, we’ll create an API Endpoint that Razorpay can hit, with a payment.successful
or payment.failed
response.
export default async function (req, res) {
const event = req.body.event;
switch (event) {
case "payment.successful":
// Mark the payment as successful
// Transfer the money to the merchant using Razorpay route
break;
case "payment.failed":
// Mark the payment as failed
break;
}
return res.status(200);
}
Razorpay Route APIs will help us transfer money from the received payment to the merchant’s account.
You can see the outcome at Alt’s website. It’s a work in progress, I’ll keep adding more information and flows to it as time passes.
Taking a product like this live requires operational overheads like making merchants aware of the product, getting them to start using it and of course, gathering customers to use the system.
On top of that, there are regulatory concerns about taking a product like this to market.