Razorpay POS Bridge Integration in Flutter: Trigger Payments Without App on POS Device
When I first got assigned to integrate a POS system with Razorpay during my internship, I didn’t quite realize what I was stepping into. With limited experience in POS payment systems, I jumped in without fully grasping the range of options and complexity involved. Razorpay’s documentation starts by covering POS Bridge — a feature that allows card, UPI, and other digital payments even when the app isn’t installed directly on the POS device. Since it was the first option in the documentation, I decided to go with it, unaware at the time that there were actually multiple integration methods.
If you’ve been to restaurants or supermarkets, you’ve likely noticed that when the cashier selects “Card” or “UPI” as the payment option, the POS machine activates automatically, processing the payment without any manual input. This seamless experience is made possible by a Razorpay feature called POS Bridge. Interestingly, POS Bridge is particularly useful when an app isn’t installed directly on the POS device but still needs to trigger transactions across various payment methods. Since Razorpay’s official documentation begins with POS Bridge, it immediately caught my attention and led me to explore it as a solution.
If you’re using an app installed directly on the POS device, I recommend checking out my previous post on RAZORPAY POS Integration in Flutter — Android SDK Solution:
In that post, I walk you through the integration process for using the Android SDK. However, in this blog, I’ll guide you on how to integrate Razorpay’s POS Bridge into your Flutter app, so you can smoothly handle card, UPI, and other digital payments — even without the app being installed on the POS device.
Overview of the Payment Flow
To make a payment via Ezetap’s POS system using Razorpay, we’ll need to follow these basic steps:
- Initiate the Payment: This involves calling an API to start the transaction. You specify the amount, customer details, and the device that will handle the payment.
- Polling the Payment Status: After initiating the payment, we’ll need to regularly check the status to see if the payment was successful, failed, or cancelled.
- Canceling a Payment: If needed, the payment process can be canceled by sending a cancellation request to the API.
Step 1: Initiating a Payment
To initiate the payment, you make a request to the Razorpay API with details like the amount and transaction reference number. Once the API responds with a unique request ID (p2pRequestId
), it’s time to start polling the status.
Key Points to Keep in Mind:
- The request should include details like the payment mode (e.g., CARD, UPI, etc.) or ALL to list all modes, along with the device ID that will handle the transaction, and the
username
andappKey
provided for authentication. - This step involves sending a POST request to the Razorpay start API with the necessary data.
Future<RazorpayStartModel> getStartAPI({required double amount}) async {
final deviceInfo = await DeviceInfoPlugin().androidInfo;
final paymentRequest = {
"username": "<Your username>",
"appKey": "<Your app key>",
"amount": "<Your Amount>,
"externalRefNumber": "<Unique reference number>",
"pushTo": {"deviceId": "${deviceInfo.serialNumber}|ezetap_android"},
"mode": "ALL"
};
final apiUrl = "https://demo.ezetap.com/api/3.0/p2padapter/pay"; // Demo API link for starting payment
// Send the payment initiation request
final response = await _sendPostRequest(Uri.parse(apiUrl), paymentRequest);
return RazorpayStartModel.fromJson(response);
}
Step 2: Polling the Payment Status
After initiating the payment, it’s important to monitor the transaction status. Razorpay recommends polling the status at intervals to check if the transaction is authorized, failed, or canceled.
Polling is done by calling the status API and checking the response for the transaction status. Here’s the basic flow:
- AUTHORIZED: The payment was successfully completed.
- FAILED: The transaction failed, and action is needed.
- P2P_DEVICE_RECEIVED: The POS terminal has received the payment request but hasn’t responded yet.
- P2P_DEVICE_CANCELED: The user canceled the transaction on the POS terminal.
The status API endpoint varies based on the operation you’re performing, so we’ll dynamically adjust the endpoint URL accordingly.
Future<RazorpayStatusModel> getStatusAPI({required String reqId}) async {
final statusRequest = {
"username": "<Your username>",
"appKey": "<Your app key>",
"origP2pRequestId": reqId,
};
final apiUrl = "https://demo.ezetap.com/api/3.0/p2padapter/status"; // Demo API link for status polling
// Send the status polling request
final response = await _sendPostRequest(Uri.parse(apiUrl), statusRequest);
return RazorpayStatusModel.fromJson(response);
}
Step 3: Handling Payment Cancellation
If a transaction needs to be canceled, a request to the cancellation API is sent. The origP2pRequestId
identifies the transaction, and the device ID ensures the cancellation request goes to the right POS terminal.
Again, we adjust the API endpoint based on the operation, using /cancel
for cancellations.
Future<RazorpayCancelModel> cancelAPI({required String reqId}) async {
final cancelRequest = {
"username": "<Your username>",
"appKey": "<Your app key>",
"origP2pRequestId": reqId,
"pushTo": {"deviceId": "device_id_placeholder"}
};
final apiUrl = "https://demo.ezetap.com/api/3.0/p2padapter/cancel"; // Demo API link for payment cancellation
// Send the cancellation request
final response = await _sendPostRequest(Uri.parse(apiUrl), cancelRequest);
return RazorpayCancelModel.fromJson(response);
}
Best Practices for Polling and Cancelling
Here are some best practices to follow:
- Poll every 10 seconds: Start polling the status 30 seconds after initiating the payment, and continue polling every 10 seconds until the 150th second.
- Handle specific response codes: Based on the response, handle the success, failure, or cancellation accordingly.
- Time out gracefully: If the status is not updated after polling multiple times, you may want to cancel the transaction manually.
Wrapping Up
Integrating Razorpay with Ezetap POS terminals through APIs simplifies the payment process, offering merchants a flexible and reliable payment system. By handling the initiation, status polling, and cancellation efficiently, you can ensure a smooth transaction experience.
This integration will provide real-time feedback on payment statuses and give users control over cancellations when necessary. Follow the best practices, and always ensure the system is robust to handle edge cases like transaction failures or timeouts.