razorpay_flutter_banking_wrapper_sdk 1.0.0 copy "razorpay_flutter_banking_wrapper_sdk: ^1.0.0" to clipboard
razorpay_flutter_banking_wrapper_sdk: ^1.0.0 copied to clipboard

Flutter plugin for Razorpay Banking Payment SDK. iOS only - wraps the native Checkout Bank Facade SDK.

example/lib/main.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:razorpay_flutter_banking_wrapper_sdk/razorpay_flutter_banking_wrapper_sdk.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Banking Payment Example',
      theme: ThemeData(
        colorSchemeSeed: const Color(0xFF072654),
        useMaterial3: true,
      ),
      home: const PaymentScreen(),
    );
  }
}

enum InputMode { form, rawJson }

class PaymentScreen extends StatefulWidget {
  const PaymentScreen({super.key});

  @override
  State<PaymentScreen> createState() => _PaymentScreenState();
}

class _PaymentScreenState extends State<PaymentScreen> {
  final _payment = RazorpayBankingPayment();
  bool _isLoading = false;
  String _status = 'Ready';

  InputMode _inputMode = InputMode.form;

  // Form fields (same structure as iOS demo)
  final _configTypeController = TextEditingController(text: 'NTRP_PAYMENT');
  final _epController = TextEditingController(text: 'dev');
  final _merchIdValController = TextEditingController();
  final _encRequestParamController = TextEditingController();
  final _reqDigitalSignatureController = TextEditingController();
  final _bankController = TextEditingController(text: 'hdfc');
  final _authKeyController = TextEditingController();

  // Raw JSON
  final _rawJsonController = TextEditingController(
    text: '''{
  "config_type": "NTRP_PAYMENT",
  "ep": "dev",
  "data": {
    "merchIdVal": "",
    "encrequestparameter": "",
    "reqdigitalsignature": "",
    "authKey": "",
    "bank": "hdfc"
  }
}''',
  );

  // Log lines (what we pass + callback events)
  final List<String> _logLines = [];
  static const int _maxLogLines = 50;

  @override
  void initState() {
    super.initState();
    _payment.initialize(
      showLoader: () {
        _addLog('[Callback] showLoader()');
        setState(() => _isLoading = true);
      },
      hideLoader: () {
        _addLog('[Callback] hideLoader()');
        setState(() => _isLoading = false);
      },
      onPaymentSuccess: (response) {
        _addLog('[Callback] onPaymentSuccess(response: $response)');
        setState(() {
          _status = 'Payment Successful!\n${response ?? ''}';
        });
      },
      onPaymentError: (error, response) {
        _addLog(
          '[Callback] onPaymentError(code: ${error.code}, description: ${error.description}, response: $response)',
        );
        setState(() {
          _status =
              'Payment Failed\nCode: ${error.code}\nDescription: ${error.description}'
              '${response != null ? '\nResponse: $response' : ''}';
        });
      },
    );
    _addLog('App started. Choose Form or Raw JSON, fill values, then tap Start Payment.');
  }

  @override
  void dispose() {
    _configTypeController.dispose();
    _epController.dispose();
    _merchIdValController.dispose();
    _encRequestParamController.dispose();
    _reqDigitalSignatureController.dispose();
    _bankController.dispose();
    _authKeyController.dispose();
    _rawJsonController.dispose();
    _payment.dispose();
    super.dispose();
  }

  void _addLog(String message) {
    final line = '${DateTime.now().toIso8601String().substring(11, 19)} $message';
    setState(() {
      _logLines.insert(0, line);
      if (_logLines.length > _maxLogLines) _logLines.removeLast();
    });
    debugPrint('[RazorpayBanking] $line');
  }

  Map<String, dynamic> _buildConfigFromForm() {
    return {
      'config_type': _configTypeController.text.trim().isEmpty
          ? 'NTRP_PAYMENT'
          : _configTypeController.text.trim(),
      'ep': _epController.text.trim().isEmpty ? 'dev' : _epController.text.trim(),
      'data': {
        'merchIdVal': _merchIdValController.text.trim(),
        'encrequestparameter': _encRequestParamController.text.trim(),
        'reqdigitalsignature': _reqDigitalSignatureController.text.trim(),
        'bank': _bankController.text.trim().isEmpty ? 'hdfc' : _bankController.text.trim().toLowerCase(),
        'authKey': _authKeyController.text.trim(),
      },
    };
  }

  Map<String, dynamic>? _parseRawJson() {
    final raw = _rawJsonController.text.trim();
    if (raw.isEmpty) return null;
    try {
      final decoded = json.decode(raw) as Map<String, dynamic>;
      return Map<String, dynamic>.from(decoded);
    } catch (e) {
      _addLog('Parse error: $e');
      return null;
    }
  }

  void _startPayment() {
    final Map<String, dynamic> config;
    if (_inputMode == InputMode.rawJson) {
      final parsed = _parseRawJson();
      if (parsed == null) {
        setState(() => _status = 'Error: Invalid JSON');
        return;
      }
      config = parsed;
    } else {
      config = _buildConfigFromForm();
    }

    // Log exactly what we pass to open
    try {
      final jsonStr = const JsonEncoder.withIndent('  ').convert(config);
      _addLog('open() called with config:\n$jsonStr');
      // Debug: verify authKey is present (iOS SDK expects authKey)
      final data = config['data'] as Map<String, dynamic>?;
      final authKey = data?['authKey'] as String?;
      if (authKey != null) {
        final prefix = authKey.length >= 4 ? authKey.substring(0, 4) : authKey;
        _addLog('authKey length=${authKey.length}, prefix=$prefix...');
      } else {
        _addLog('WARN: data.authKey is null or missing');
      }
    } catch (_) {
      _addLog('open() called with config: $config');
    }

    _payment.open(config);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Banking Payment Example'),
      ),
      body: Column(
        children: [
          Expanded(
            child: SingleChildScrollView(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  // Input mode
                  Text('Input mode', style: Theme.of(context).textTheme.titleSmall),
                  const SizedBox(height: 8),
                  SegmentedButton<InputMode>(
                    segments: const [
                      ButtonSegment(value: InputMode.form, label: Text('Form'), icon: Icon(Icons.edit_note)),
                      ButtonSegment(value: InputMode.rawJson, label: Text('Raw JSON'), icon: Icon(Icons.code)),
                    ],
                    selected: {_inputMode},
                    onSelectionChanged: (Set<InputMode> s) {
                      setState(() => _inputMode = s.first);
                    },
                  ),
                  const SizedBox(height: 16),

                  if (_inputMode == InputMode.form) ...[
                    _buildTextField('config_type', _configTypeController, hint: 'NTRP_PAYMENT'),
                    _buildTextField('ep', _epController, hint: 'dev | uat | prod'),
                    _buildTextField('data.merchIdVal', _merchIdValController),
                    _buildTextField('data.encrequestparameter', _encRequestParamController, maxLines: 2),
                    _buildTextField('data.reqdigitalsignature', _reqDigitalSignatureController, maxLines: 2),
                    _buildTextField('data.bank', _bankController, hint: 'hdfc'),
                    _buildTextField('data.authKey', _authKeyController),
                  ] else
                    Column(
                      crossAxisAlignment: CrossAxisAlignment.stretch,
                      children: [
                        Text('Raw JSON (passed as-is to open)', style: Theme.of(context).textTheme.titleSmall),
                        const SizedBox(height: 8),
                        TextField(
                          controller: _rawJsonController,
                          maxLines: 14,
                          decoration: InputDecoration(
                            hintText: 'Paste full config JSON',
                            border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
                            alignLabelWithHint: true,
                          ),
                          style: const TextStyle(fontFamily: 'monospace', fontSize: 12),
                        ),
                      ],
                    ),

                  const SizedBox(height: 16),
                  FilledButton.icon(
                    key: const Key('start_payment_button'),
                    onPressed: _isLoading ? null : _startPayment,
                    icon: _isLoading ? const SizedBox(width: 20, height: 20, child: CircularProgressIndicator(strokeWidth: 2)) : const Icon(Icons.payment),
                    label: Text(_isLoading ? 'Loading...' : 'Start Payment'),
                  ),
                  const SizedBox(height: 12),
                  Text('Status', style: Theme.of(context).textTheme.titleSmall),
                  const SizedBox(height: 4),
                  Container(
                    width: double.infinity,
                    padding: const EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      color: Theme.of(context).colorScheme.surfaceContainerHighest.withOpacity(0.5),
                      borderRadius: BorderRadius.circular(8),
                    ),
                    child: Text(_status, style: Theme.of(context).textTheme.bodyMedium),
                  ),
                ],
              ),
            ),
          ),
          // Log panel
          Container(
            height: 140,
            margin: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.grey.shade900,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
                  child: Text('Log (payload & callbacks)', style: TextStyle(color: Colors.grey.shade300, fontSize: 12, fontWeight: FontWeight.w600)),
                ),
                const Divider(height: 1, color: Colors.grey),
                Expanded(
                  child: ListView.builder(
                    padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
                    itemCount: _logLines.length,
                    itemBuilder: (_, i) {
                      return Text(
                        _logLines[i],
                        style: const TextStyle(color: Colors.greenAccent, fontFamily: 'monospace', fontSize: 11),
                        maxLines: 2,
                        overflow: TextOverflow.ellipsis,
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildTextField(String label, TextEditingController controller, {String? hint, int maxLines = 1}) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: TextField(
        controller: controller,
        maxLines: maxLines,
        decoration: InputDecoration(
          labelText: label,
          hintText: hint,
          border: const OutlineInputBorder(),
          isDense: true,
        ),
      ),
    );
  }
}
1
likes
150
points
59
downloads

Publisher

verified publisherrazorpay.com

Weekly Downloads

Flutter plugin for Razorpay Banking Payment SDK. iOS only - wraps the native Checkout Bank Facade SDK.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on razorpay_flutter_banking_wrapper_sdk

Packages that implement razorpay_flutter_banking_wrapper_sdk