flutter_upi_gateway
A production-quality, cross-platform Flutter plugin for initiating and handling Unified Payments Interface (UPI) payment flows on Android and iOS using standard upi://pay URI deep-link mechanisms.
Disclaimer & Compliance Statement
IMPORTANT:
- This package initiates UPI payment flows using standard operating system URI and Intent mechanisms. It does NOT implement the UPI network/protocol itself.
- It does NOT connect directly to NPCI's internal switch.
- It does NOT collect, request, transmit, log, or store UPI PINs, OTPs, or banking credentials.
- It does NOT bypass bank/UPI application authentication.
- It treats client-side UPI app responses as untrusted input and exposes the raw response alongside parsed status.
- Merchant/Backend Requirement: Client-side response status must be independently verified against your merchant backend or payment gateway settlement webhook.
Features
- π App Discovery: Detect installed UPI-capable payment apps (Google Pay, PhonePe, Paytm, BHIM, etc.).
- π System Chooser Launch: Launch native system app chooser (
pay). - π― Targeted Launch: Direct launch of a specific installed UPI app (
payWithApp). - π Standard UPI URI Generator: Construct spec-compliant, URL-encoded
upi://payURIs. - π± QR Code Payload: Generate standard UPI QR code string payloads.
- π‘οΈ Syntactic VPA Validation: Validate Virtual Payment Address (VPA / UPI ID) format locally.
- π Safe Response Parser: Parse native response parameters into strongly typed
UpiPaymentResultobjects. - π Security-First: Zero PIN/credential exposure, disabled debug logging by default.
Installation
Add flutter_upi_gateway to your pubspec.yaml:
dependencies:
flutter_upi_gateway: ^1.0.0
Platform Setup
Android Setup
Add the <queries> element inside your android/app/src/main/AndroidManifest.xml (outside the <application> tag) to support Android 11+ (API Level 30+) package visibility:
<manifest xmlns:android="http://schemas.android.com/apk/android">
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="upi" />
</intent>
</queries>
<application>
...
</application>
</manifest>
iOS Setup
Add the supported UPI URL schemes under LSApplicationQueriesSchemes in ios/Runner/Info.plist:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>upi</string>
<string>gpay</string>
<string>phonepe</string>
<string>paytmmp</string>
<string>bhim</string>
<string>cred</string>
<string>amazonpay</string>
<string>mobikwik</string>
</array>
Usage
1. Check UPI Availability & Installed Apps
import 'package:flutter_upi_gateway/flutter_upi_gateway.dart';
// Check if device can process UPI payments
final bool isAvailable = await FlutterUpiGateway.isAvailable();
// List installed UPI applications
final List<UpiApp> installedApps = await FlutterUpiGateway.getInstalledUpiApps();
for (final app in installedApps) {
print('App Name: ${app.name}, Package/Scheme: ${app.id}');
}
2. Syntactically Validate a UPI ID (VPA)
final bool isValid = FlutterUpiGateway.isValidVpa('merchant@upi'); // returns true
final String? error = FlutterUpiGateway.validateVpa('invalid_vpa');
if (error != null) {
print('VPA Validation error: $error');
}
3. Initiate Payment via System Chooser
final request = UpiPaymentRequest(
payeeVpa: 'merchant@upi',
payeeName: 'Acme Merchant',
amount: 499.00,
currency: 'INR',
transactionId: 'ORDER_123456',
transactionReference: 'REF_987654',
transactionNote: 'Order #123456 Payment',
);
try {
final UpiPaymentResult result = await FlutterUpiGateway.pay(request);
if (result.isSuccess) {
print('Client Payment Success! Txn ID: ${result.transactionId}');
print('Approval Ref No (RRN): ${result.approvalReferenceNumber}');
} else if (result.isCancelled) {
print('User cancelled payment');
} else {
print('Payment failed with status: ${result.status}');
}
} on UpiException catch (e) {
print('UPI Exception (${e.code}): ${e.message}');
}
4. Initiate Payment targeting a specific UPI app
final result = await FlutterUpiGateway.payWithApp(
request,
appId: selectedApp.id, // e.g. 'com.phonepe.app' on Android or 'gpay' on iOS
);
5. Generate URI / QR Code Payload
final Uri paymentUri = FlutterUpiGateway.buildPaymentUri(request);
final String qrPayload = FlutterUpiGateway.buildQrPayload(request);
print('UPI URI: $paymentUri');
// upi://pay?pa=merchant%40upi&pn=Acme+Merchant&am=499.00&cu=INR&tr=REF_987654&tn=Order...
Server-Side Payment Verification
Client-side results returned from UPI applications are susceptible to tampering on rooted or modified devices. For production applications:
class MyServerPaymentVerifier implements PaymentVerificationProvider {
@override
Future<bool> verifyPaymentServerSide(UpiPaymentResult clientResult) async {
final response = await http.post(
Uri.parse('https://api.yourdomain.com/v1/payments/verify'),
body: jsonEncode({
'transactionId': clientResult.transactionId,
'approvalRefNo': clientResult.approvalReferenceNumber,
'rawResponse': clientResult.rawResponse,
}),
);
return response.statusCode == 200;
}
}
License
MIT License. See LICENSE for details.