Introduction to Razorpay Flutter Integration

Accepting digital payments in mobile applications requires a robust, secure, and user-friendly checkout flow. When building e-commerce apps or subscription platforms targeted at India-focused commerce, the official razorpay_flutter Flutter package offers a straightforward integration point for processing payments via UPI, Credit/Debit cards, Net Banking, and digital wallets.

Instead of manually constructing webviews or native platform bridges, the razorpay_flutter package wraps native Razorpay Android and iOS SDKs into a unified Dart interface. In a clean Flutter architecture, payment logic should be isolated inside a dedicated service module so that your core domain logic stays decoupled from specific vendor SDKs.

When to Use the Package

You should consider using the razorpay_flutter package when:

  • You need to support standard Razorpay checkout overlays inside an e-commerce, ticketing, or service app.
  • You are targeting Android and iOS devices using Razorpay's native mobile SDK capabilities.
  • You want to streamline payment event handling (success, error, wallet selection) using standard Dart streams and callbacks.

Architecture Tip: Place your payment logic behind a contract interface (e.g., PaymentRepository or PaymentService). This keeps presentation widgets clean and facilitates mock testing without triggering actual payment SDK calls.

Installing razorpay_flutter

To add the package to your Flutter project, run the standard pub command in your terminal:

Code
flutter pub add razorpay_flutter

This updates your pubspec.yaml file with the latest compatible version. Always check the official package listing on pub.dev to review current version compatibility and native SDK prerequisites before deploying to production.

Setting Up Event Listeners and Checkout

The core entry point for managing payment flows is the Razorpay class. You instantiate the object, attach event listeners for various checkout outcomes, and trigger open() with your checkout options dictionary.

Basic Payment Integration Example

Here is a complete example demonstrating how to initialize the plugin, listen for events, trigger checkout, and properly clean up native listeners upon disposal:

Dart / Flutter
import 'package:flutter/material.dart';
import 'package:razorpay_flutter/razorpay_flutter.dart';

class CheckoutPage extends StatefulWidget {
  const CheckoutPage({Key? key}) : super(key: key);

  @override
  State<CheckoutPage> createState() => _CheckoutPageState();
}

class _CheckoutPageState extends State<CheckoutPage> {
  late Razorpay _razorpay;

  @override
  void initState() {
    super.initState();
    _razorpay = Razorpay();
    _razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
    _razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
    _razorpay.on(Razorpay.EVENT_EXTERNAL_WALLET, _handleExternalWallet);
  }

  @override
  void dispose() {
    _razorpay.clear();
    super.dispose();
  }

  void _handlePaymentSuccess(PaymentSuccessResponse response) {
    // Note: Send response.paymentId, response.orderId, and response.signature
    // to your backend server for signature verification before fulfilling the order.
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Payment Successful: ${response.paymentId}')),
    );
  }

  void _handlePaymentError(PaymentFailureResponse response) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Payment Failed: ${response.code} - ${response.message}')),
    );
  }

  void _handleExternalWallet(ExternalWalletResponse response) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('External Wallet Selected: ${response.walletName}')),
    );
  }

  void _startCheckout() {
    var options = {
      'key': 'rzp_test_YOUR_KEY_HERE',
      'amount': 1000, // Amount in lowest currency unit (e.g., 1000 paise = 10 INR)
      'name': 'Sample Store',
      'description': 'Order #12345',
      'retry': {'enabled': true, 'max_count': 1},
      'send_sms_hash': true,
      'prefill': {
        'contact': '9876543210',
        'email': 'customer@example.com',
      },
      'external': {
        'wallets': ['paytm']
      }
    };

    try {
      _razorpay.open(options);
    } catch (e) {
      debugPrint('Error launching Razorpay checkout: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Razorpay Checkout')),
      body: Center(
        child: ElevatedButton(
          onPressed: _startCheckout,
          child: const Text('Pay Now'),
        ),
      ),
    );
  }
}

Platform Setup Notes

Depending on target platforms and Android/iOS build configurations, review these setup prerequisites:

  • Android ProGuard Configuration: If using code obfuscation (R8/ProGuard) on Android, verify that standard Razorpay retention rules are preserved as specified in the official package documentation.
  • iOS Deployment Target: Ensure your iOS minimum deployment target matches or exceeds the platform requirements specified on pub.dev.
  • Server-Side Order Creation: Always generate the official Razorpay order_id via your secure backend API rather than trusting client-side price values. Pass this order_id inside the options map.

Common Pitfalls and Best Practices

Warning: Never hardcode production API secret keys in your Flutter source code. Your API secret belongs strictly on your backend server.

  • Forgetting to call clear(): Memory leaks and duplicate callback triggers occur if you do not invoke _razorpay.clear() when disposing your widget or service state.
  • Skipping Server Signature Verification: Receiving EVENT_PAYMENT_SUCCESS on the client device is not sufficient proof of payment authorization. Always verify the backend signature hash using your API secret.
  • Tight Coupling: Avoid instantiating and handling Razorpay direct logic deep inside UI widgets. Encapsulate implementation details inside a service provider or state manager (e.g., Bloc, Riverpod, or Provider).

Searchable Developer Terms

When researching solutions or debugging implementation steps, developers frequently search for:

  • razorpay_flutter payment gateway integration
  • Flutter Razorpay event listeners payment success error
  • Razorpay payment SDK setup Flutter Android iOS
  • Flutter accept UPI card payments Razorpay

Frequently Asked Questions

What is the primary purpose of the razorpay_flutter package?

The razorpay_flutter package allows Flutter developers to integrate native Razorpay payment checkout workflows into Android and iOS applications.

How do I handle successful or failed payments in razorpay_flutter?

You attach listeners to the Razorpay instance using .on() for Razorpay.EVENT_PAYMENT_SUCCESS, Razorpay.EVENT_PAYMENT_ERROR, and Razorpay.EVENT_EXTERNAL_WALLET.

Why should I clear the Razorpay instance?

Calling _razorpay.clear() unregisters native event listeners, preventing memory leaks and duplicate callback execution when widgets unmount.

Is client-side payment confirmation enough for order processing?

No. You should always transmit paymentId, orderId, and signature to your secure backend to verify HMAC SHA256 signatures before fulfilling an order.