Razorpay Integration In Flutter Web

Hitesh Verma
5 min readSep 23, 2021

Hey folks, do you want to integrate Razorpay in your flutter app but came to know that official Razorpay package razorpay_flutter doesn’t support web platform! This article will help you then :-)

Find the updated article here.

Photo by Pickawood on Unsplash

Step 1: Get Razorpay credentials.

Signup for the Razorpay account if you’ve not done it yet and generate API keys in test mode. After successful key generation, you’ll get your Key ID and Key Secret. Save them somewhere for later reference.

Step 2: Load Razorpay library for flutter web.

To use Razorpay in your flutter web, you’ve to load Razorpay’s web library first.

Go to the index.html file inside the web folder of your flutter project and add the below script before closing the body.

<body>
...
<script src="https://checkout.razorpay.com/v1/checkout.js"></script></body>

Step 3: Write custom checkout logic for the web.

If you were using Razorpay in android or ios, checkout would’ve been managed by the package itself. Since razorpay_flutter doesn’t support web, we have to write this logic ourselves.

Inside the web folder of your flutter project, create a new folder named js. Inside this new folder create a file named payment.js

Although, naming is not necessary to be the same, but to follow along it is advised to use the same names as above.

Inside the payment.js file write the following piece of code.

function checkout(optionsStr) {
var options = JSON.parse(optionsStr);
options.handler = function (response) {
window.sessionStorage.setItem('razorpayStatus',
'SUCCESS');
window.sessionStorage.setItem('razorpayOrderId',
response.razorpay_order_id);
window.sessionStorage.setItem('paymentId',
response.razorpay_payment_id);
window.sessionStorage.setItem('signature',
response.razorpay_signature);
}
var rzp1 = new Razorpay(options); rzp1.on('payment.failed', function (response) {
window.sessionStorage.setItem('razorpayStatus', 'FAILED');
});
rzp1.open();
}

checkout() function is receiving the checkout params as a parameter from the flutter side and passing it to the Razorpay web library we’ve imported in the previous step for opening the checkout popup and then writing the payment status in the sessionStorage of the browser.

Also, import this script in index.html file inside web folder of your flutter project.

<body>
...
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
<script src="js/payment.js"></script>
</body>

Step 4: Write Server Code

In order to complete a payment process in Razorpay we have to perform certain tasks:

  1. Create an order against the payment
  2. Generate hash for the order
  3. Start checkout
  4. Verify signature hash from the checkout success with the generated hash

I’ve used firebase cloud function for backend service and written server side code in NodeJS. But you can choice your own server side language the concept remains the same!

razorpay_controller.js

const admin = require('firebase-admin');
const Razorpay = require('razorpay');
const crypto = require('crypto');
class RazorPayController { constructor() {
this._razorpay = new Razorpay({
key_id: '<replace_with_your_Razorpay_Key_ID>',
key_secret: '<replace_with_your_Razorpay_Key_Secret>'
});
}
// to create order for the payment
async createOrder(body) {
const amount = body.amount; // amount in paisa
if (!amount || isNaN(amount)) {
throw new Error('Invalid amount');
}
const options = { amount, currency: 'INR' };
const order = await this._razorpay.orders.create(options);
await admin.firestore()
.collection('transactions')
.add({
orderId: order.id,
amount,
dateTime: Date.now(),
status: 'PENDING'
});
return order.id;
}
// to verify payment signature
async verifyPayment(body) {
const orderId = body.orderId;
const razorpayOrderId = body.razorpayOrderId;
const razorpayPaymentId = body.paymentId;
const razorpaySignature = body.signature;
if (!orderId
|| !razorpayOrderId
|| !razorpayPaymentId
|| !razorpaySignature
) {
throw new Error('Invalid request');
}
const snap = await admin.firestore()
.collection('transactions')
.where('orderId', '==', orderId)
.limit(1)
.get();
if (snap.docs.length === 0) {
throw new Error('Invalid order Id');
}
const sign = crypto.createHmac(
'sha256',
'<replace_with_your_Razorpay_Key_Secret>'
).update(`${orderId}|${razorpayPaymentId}`).digest('hex');
await snapshot.docs[0].ref.set({
orderId,
razorpayOrderId,
razorpayPaymentId,
razorpaySignature,
status: sign === razorpaySignature ? 'COMPLETED' : 'FAILED'
}, { merge: true });
return sign === razorpaySignature;
}
}
module.exports = new RazorPayController();

index.js

const functions = require("firebase-functions");
const admin = require('firebase-admin');
const cors = require('cors');
const controller = require('./controllers/razorpay_controller');admin.initializeApp();
const app = express();
app.use(cors({ origin: true }));
app.use(express.json());
app.post('/createOrder', async (req, res) => {
try{
const orderId = await controller.createOrder(req.body);
res.status(200).send({ orderId });
} catch(e){
res.status(400).send({errorMessage: e.message});
}
});
app.post('/verifyPayment', async (req, res) => {
try{
const verified = await controller.verfiyPayment(req.body);
if(verified){
res.status(200).send({message: 'Order has been verified!'});
} else {
res.send(400).send({message: 'Order not verified'});
}
} catch (e){
res.send(400).send({errorMessage: e.message});
}
});
exports.r = functions.region('asia-south1').https.onRequest(app);

Step 5: Write methods in flutter to call server side functions

Add dependencies to pubspec.yaml file of your flutter project.

dependencies:
...
http: <latest_version>

Create a new folder named service inside lib folder of your flutter project. And create a file named api_service.dart inside this service folder and write following piece of code in the file.

import 'dart:convert';
import 'package:http/http.dart' as http;
class ApiService { /// to create order for initiating payment
///@param amount in paisa
Future<String?> createOrder(int amount) async {
try {
final res = await _post(
'<baseURL_of_your_server_hosting>/createOrder', {
'amount': amount,
});
if (res.statusCode == 200) {
final data = jsonDecode(res.body);
return data['orderId'];
}
} catch (e) {
print(e.toString());
}
return null;
}
/// verify the payment
Future<bool> verifyPayment(Map<String, dynamic> body) async {
try {
final res = await _post(
'<baseURL_of_your_server_hosting>/verifyPayment,
body,
);
return res.statusCode == 200;
} catch (e) {
log(e.toString());
}
return false;
}
/// send post request
Future<http.Response> _post(
String url,
Map<String, dynamic> body,
) async {
return http.post(
Uri.parse(url),
headers: {'Content-Type': 'application/json'},
body: jsonEncode(body),
).timeout(Duration(seconds: 10));
}
}

Step 6: Connecting dots.

We have loaded the Razorpay web library, written the checkout function in JS, also written the server side code for creating order and verifying payment signature, and written flutter methods to call these server-side functions. Now all we have to do is to connect all the dots.

Inside the service folder, create another file named payment_service.dart and write the following piece of code in the file.

import 'dart:async';
import 'dart:convert';
import 'dart:js' as js;
import 'dart:html' as html;
import './api_service.dart';
class PaymentService {
final _apiService = ApiService();
final _timerDuration = const Duration(seconds: 1);
Timer? _timer;
Future<void> startPayment(
int amount,
String? name,
String? email,
VoidCallback onSuccess,
VoidCallback onFailed,
) async {
String? orderId = await _apiService.createOrder(amount);
if (orderId == null){
onFailed();
return;
}
final options = {
'key': '<replace_with_your_Razopay_Key_ID>',
'amount': amount, // amount in paisa
'name': '<replace_with_your_appname>',
'order_id': orderId,
'prefill': {'name': name, 'email': email}
};
if (kIsWeb) {
final data = await _checkout(options);
if (data['razorpayStatus'] == 'SUCCESS') {
bool verified = await _verifyOrder(
orderId,
data['razorpayOrderId],
data['paymentId'],
data['signature'],
);
if(verified){
onSuccess();
return;
}
}

onFailed();
}
}
/// starts checkout
Future<Map<String, String>> _checkout(
Map<String, dynamic> options
) async {
Completer<Map<String, String>> completer =
Completer<Map<String, String>>();
// calls js function defined in Step 3
js.context.callMethod('checkout', [jsonEncode(options)]);
_timer = Timer.periodic(_timerDuration, (timer) {
if(html.window.sessionStorage.containsKey('razorpayStatus'){
Map<String, String> data = Map.fromEntries(
html.window.sessionStorage.entries,
);
html.window.sessionStorage.clear();
completer.complete(data);
_timer!.cancel();
_timer = null;
}
});
return completer.future;
}
/// verify order signature
Future<bool> _verifyOrder(
String orderId,
String razorpayOrderId,
String paymentId,
String signature,
) async {
return _apiService.verifyOrder({
'orderId': _orderId,
'razorpayOrderId': razorpayOrderId,
'paymentId': paymentId,
'signature': signature,
});
}
}

The checkout() method of PaymentService is calling the checkout() function of the js we have defined in step 3. And then listening for write to occur in the sessionStorage of the browser.

Step 7: Start payment process.

To the start the payment process, now all you’ve to do is to call the startPayment() method of the PaymentService class, and pass the required arguments.

void toastMessage(bool success){
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Container(
color: success? Colors.green: Colors.red,
padding: const EdgeInsets.all(10),
child: Text(
success ? 'Payment Successful' : 'Payment Failed',
),
),
),
);
}
PaymentService().startPayment(
100,
'Test',
'test@gmail.com',
() => toastMessage(true),
() => toastMessage(false),
);

That’s it! You’ve successfully integrated the Razorpay payment gateway in your flutter web app. You can now replace the test API Keys with Live API Keys after testing your integration.

Thank you.

--

--