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://pay URIs.
  • πŸ“± 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 UpiPaymentResult objects.
  • πŸ”’ 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.