razorpay_flutter_banking_wrapper_sdk 1.0.0
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,
),
),
);
}
}