payu_bharatkosh_encryption_wrapper 1.0.0-alpha.1
payu_bharatkosh_encryption_wrapper: ^1.0.0-alpha.1 copied to clipboard
Flutter wrapper for BharatKosh encryption SDK that internally uses PayU CheckoutPro native SDKs for Android and iOS.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:payu_bharatkosh_encryption_wrapper/payu_bharatkosh_encryption_wrapper.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'BharatKosh Encryption Wrapper Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF00A86B),
brightness: Brightness.light,
),
useMaterial3: true,
fontFamily: 'SF Pro Display',
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF00A86B),
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const PaymentPage(),
);
}
}
class PaymentPage extends StatefulWidget {
const PaymentPage({super.key});
@override
State<PaymentPage> createState() => _PaymentPageState();
}
class _PaymentPageState extends State<PaymentPage> {
// Default sample values from iOS sample app
static const String _defaultEncryptedPayload =
"lxtLlNkazs3eF7p5ZvO2jpEuBB9T3UzD50t1S2xyQuY5FyGpmRXY0MEMfWEv2u7bfEhcWTQy9Cd4u7UowNanz60xenaIZAZoL0AsSKdWIK3WgE0yjvIzeWDJvJaOmhzF4/Jtu9OgMwU2Wn6ut+o0bvomrwv+Shpiz8DuuLK04aOk/kObh/0s0hWmVSMEs/PS0YtbxaMBQ56I0Gn2GGCd55NW+9bG4xmq3M1uFEmpvhavSn4vT/+GTc2iSilyQIRjz8Rzop8P3mdIP5Lh8CuIu0YstBe6VAMlQnCmg/Dn0chnLg7emLH+vithrlj3gnWTUh46CxP2k06r2lsx2T+A2ZBr3iDltAAs9sPxWpdU1/U=";
static const String _defaultDigitalSignature =
"MIIIXQYJKoZIhvcNAQcCoIIITjCCCEoCAQExCzAJBgUrDgMCGgUAMAsGCSqGSIb3DQEHAaCCBoowggaGMIIFbqADAgECAhAO3fBUToyGrcAPhQi9H4D6MA0GCSqGSIb3DQEBCwUAMGAxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xHzAdBgNVBAMTFkdlb1RydXN0IFRMUyBSU0EgQ0EgRzEwHhcNMjUwNjA0MDAwMDAwWhcNMjYwNjMwMjM1OTU5WjB0MQswCQYDVQQGEwJJTjEOMAwGA1UECBMFRGVsaGkxEjAQBgNVBAcTCU5ldyBEZWxoaTErMCkGA1UEChMiUHVibGljIEZpbmFuY2lhbCBNYW5hZ2VtZW50IFN5c3RlbTEUMBIGA1UEAxMLcGZtcy5uaWMuaW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDg6NqBngGxVzPskuUrBmGyDQc5kxZhU3MYkQV3l4Z5LIlbjj8JeYzA0T5naibJewOhtR3L8l58EBAT/yMBpEA+f+l7bQ0u8bNx3lqG4PeJhhXvrx8YGL2CYtw6gGUV1xEkyNFL0TrUAu7f1s9ti3zFhauxuPhuBTk5j0bDioadyOTsuUldrKUmX58jaqbVu21fhbpczT2jDMNL15KAFfBrI/98pxZzQFoa+EZvURlSPhvJsWp99hrbcc8Z5Xqxfq1zfesG7Vxip5OADzZp7tgInf1digQlanPBj/RQLp+PXIxedCmCxvA9WQZzMZflwcuretNog1QN+mfANUk4C+l5AgMBAAGjggMmMIIDIjAfBgNVHSMEGDAWgBSUT9Rdi+Sk4qaA/v3Y+QDvo74CVzAdBgNVHQ4EFgQUOdofENk5PIUlmpZoUI+xsS9OjhswJwYDVR0RBCAwHoILcGZtcy5uaWMuaW6CD3d3dy5wZm1zLm5pYy5pbjA+BgNVHSAENzA1MDMGBmeBDAECAjApMCcGCCsGAQUFBwIBFhtodHRwOi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY2RwLmdlb3RydXN0LmNvbS9HZW9UcnVzdFRMU1JTQUNBRzEuY3JsMHYGCCsGAQUFBwEBBGowaDAmBggrBgEFBQcwAYYaaHR0cDovL3N0YXR1cy5nZW90cnVzdC5jb20wPgYIKwYBBQUHMAKGMmh0dHA6Ly9jYWNlcnRzLmdlb3RydXN0LmNvbS9HZW9UcnVzdFRMU1JTQUNBRzEuY3J0MAwGA1UdEwEB/wQCMAAwggF/BgorBgEEAdZ5AgQCBIIBbwSCAWsBaQB3AA5XlLzzrqk+MxssmQez95Dfm8I9cTIl3SGpJaxhxU4hAAABlzqpDQsAAAQDAEgwRgIhAKIZBO+FndUGvy5Tvr0opiXvURYmmnhQRtcNCNteUr4xAiEAwzWVLcSSZz3IUjxH3XXsVQVVjXkG1XZWTFOgRUU94jMAdgBkEcRspBLsp4kcogIuALyrTygH1B41J6vq/tUDyX3N8AAAAZc6qQ1IAAAEAwBHMEUCIQDLf2Daaa37eah3CyN+eYGdSxRncb+3WTGjICaXFHV0cQIgCuyf9bZN4rOctVSuyfy8+HhPHbWHXLb3/sJ3iLsl9AEAdgBJnJtp3h187Pw23s2HZKa4W68Kh4AZ0VVS++nrKd34wwAAAZc6qQ1jAAAEAwBHMEUCIElQObNpPtSNe8wO2xYSuLaDNRzp18T28Ers2wpj/eo2AiEAviAU6UQ10N/DWDcx9smUGPU+OkfcTSc8jzBRz5ijwyowDQYJKoZIhvcNAQELBQADggEBAB8Fxj4itsnx+38vYDJMyCSYMtMmQNdv7tjUfU3PsKAGE+ZnyqoJTzuEugwMSbZtCSJxrr+y6bt3x4IDZWKDBI1KCiypbQbbF3mZq4Tfr/SXFHnAuyr10GVr3hg5tVL6gGLInZc0LpWlLH1HlLcZkot1l7xUZf4Ku9i8yETvNtes1CLJUzrMqMrXZPnVprPoqUbuOlk6EKGo5nC0pS3QQaxQ8KkXscnfqO3DH1Unhvk7EbBN6y3wmxgR+V55Xup9v6w/A8fRydwDrrZMGTW7Rcun8jXOedf0dnWYKDyVre/UVlFXKhZfRLAPVrKT/Tn+zmbaGOwnKlAFo9lkDYooYvcxggGbMIIBlwIBATB0MGAxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xHzAdBgNVBAMTFkdlb1RydXN0IFRMUyBSU0EgQ0EgRzECEA7d8FROjIatwA+FCL0fgPowCQYFKw4DAhoFADANBgkqhkiG9w0BAQEFAASCAQCUFmLtgUZtIxiS0mP41QLVAVSJl9e+uj+eKDlYgwikXC2zRpT9FmG6Pc+lbMPEM1m785WGeZ6iBSmlWfweqXjf6ATZkRF5/IXpZBTgdczE+5tlkuvDcKTi3iYw5CvpUyO2nLkGBd7lSqZEet88xGHmBSegn8tY0dm4RVrbZF19w+rLpYxnMTd1pRgBj/cAmsEDyVW/kGTHvm5KEyIMgF7ocSajj6PzXCMk8ipLDJie6jPHMhxV0mICAKGCWe9RrRh3JyfzkJkmpbLQnrp+e7PZZyWUZoH2Ra1VhwDX/IiEwfW5zveVp5TStR7xrDzi/Qnloivo86NlIeCNj7i+GxC5";
static const String _defaultMerchantId = "g9jmjm";
final _encryptedPayloadController = TextEditingController(text: _defaultEncryptedPayload);
final _digitalSignatureController = TextEditingController(text: _defaultDigitalSignature);
final _merchantIdController = TextEditingController(text: _defaultMerchantId);
bool _isProduction = false;
bool _isLoading = false;
String? _responseText;
BharatkoshResultType? _resultType;
final BharatkoshWrapper _wrapper = BharatkoshWrapper();
@override
void dispose() {
_encryptedPayloadController.dispose();
_digitalSignatureController.dispose();
_merchantIdController.dispose();
super.dispose();
}
Future<void> _startPaymentWithCallback() async {
if (!_validateInputs()) return;
setState(() {
_isLoading = true;
_responseText = null;
_resultType = null;
});
_wrapper.startPayment(
encRequestParameter: _encryptedPayloadController.text.trim(),
reqDigitalSignature: _digitalSignatureController.text.trim(),
merchIdVal: _merchantIdController.text.trim(),
isProduction: _isProduction,
callback: _PaymentCallback(
onResult: (type, response) {
if (mounted) {
setState(() {
_isLoading = false;
_resultType = type;
_responseText = response;
});
_showResultDialog(type, response);
}
},
),
);
}
Future<void> _startPaymentAsync() async {
if (!_validateInputs()) return;
setState(() {
_isLoading = true;
_responseText = null;
_resultType = null;
});
try {
final result = await _wrapper.startPaymentAsync(
encRequestParameter: _encryptedPayloadController.text.trim(),
reqDigitalSignature: _digitalSignatureController.text.trim(),
merchIdVal: _merchantIdController.text.trim(),
isProduction: _isProduction,
);
if (mounted) {
setState(() {
_isLoading = false;
_resultType = result.type;
_responseText = result.encryptedResponse;
});
_showResultDialog(result.type, result.encryptedResponse);
}
} catch (e) {
if (mounted) {
setState(() {
_isLoading = false;
_resultType = BharatkoshResultType.error;
_responseText = e.toString();
});
_showResultDialog(BharatkoshResultType.error, e.toString());
}
}
}
bool _validateInputs() {
if (_encryptedPayloadController.text.trim().isEmpty) {
_showSnackBar('Please enter encrypted payload');
return false;
}
if (_digitalSignatureController.text.trim().isEmpty) {
_showSnackBar('Please enter digital signature');
return false;
}
if (_merchantIdController.text.trim().isEmpty) {
_showSnackBar('Please enter merchant ID');
return false;
}
return true;
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
void _showResultDialog(BharatkoshResultType type, String response) {
final color = _getResultColor(type);
final icon = _getResultIcon(type);
final title = _getResultTitle(type);
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Row(
children: [
Icon(icon, color: color),
const SizedBox(width: 8),
Text(title, style: TextStyle(color: color)),
],
),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'Encrypted Response:',
style: TextStyle(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(8),
),
child: SelectableText(
response,
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 12,
),
),
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
Color _getResultColor(BharatkoshResultType type) {
switch (type) {
case BharatkoshResultType.success:
return Colors.green;
case BharatkoshResultType.failure:
return Colors.red;
case BharatkoshResultType.cancel:
return Colors.orange;
case BharatkoshResultType.error:
return Colors.red.shade800;
}
}
IconData _getResultIcon(BharatkoshResultType type) {
switch (type) {
case BharatkoshResultType.success:
return Icons.check_circle;
case BharatkoshResultType.failure:
return Icons.cancel;
case BharatkoshResultType.cancel:
return Icons.highlight_off;
case BharatkoshResultType.error:
return Icons.error;
}
}
String _getResultTitle(BharatkoshResultType type) {
switch (type) {
case BharatkoshResultType.success:
return 'Payment Success';
case BharatkoshResultType.failure:
return 'Payment Failed';
case BharatkoshResultType.cancel:
return 'Payment Cancelled';
case BharatkoshResultType.error:
return 'Error';
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('BharatKosh Payment'),
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header Card
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Icon(
Icons.payment,
size: 48,
color: Theme.of(context).colorScheme.primary,
),
const SizedBox(height: 8),
Text(
'BharatKosh Encryption Wrapper',
style: Theme.of(context).textTheme.titleLarge,
),
const SizedBox(height: 4),
Text(
'Enter the encrypted payload from BharatKosh to start payment',
style: Theme.of(context).textTheme.bodySmall,
textAlign: TextAlign.center,
),
],
),
),
),
const SizedBox(height: 16),
// Input Fields
TextField(
controller: _encryptedPayloadController,
decoration: const InputDecoration(
labelText: 'Encrypted Payload',
hintText: 'Enter encrypted request parameter',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.lock),
),
maxLines: 3,
),
const SizedBox(height: 16),
TextField(
controller: _digitalSignatureController,
decoration: const InputDecoration(
labelText: 'Digital Signature',
hintText: 'Enter request digital signature',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.verified_user),
),
maxLines: 2,
),
const SizedBox(height: 16),
TextField(
controller: _merchantIdController,
decoration: const InputDecoration(
labelText: 'Merchant ID',
hintText: 'Enter merchant ID value',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.business),
),
),
const SizedBox(height: 16),
// Environment Toggle
Card(
child: SwitchListTile(
title: const Text('Production Environment'),
subtitle: Text(
_isProduction
? 'Using production servers'
: 'Using UAT/test servers',
),
secondary: Icon(
_isProduction ? Icons.cloud : Icons.cloud_outlined,
color: _isProduction ? Colors.green : Colors.orange,
),
value: _isProduction,
onChanged: (value) {
setState(() {
_isProduction = value;
});
},
),
),
const SizedBox(height: 24),
// Payment Buttons
Row(
children: [
Expanded(
child: FilledButton.icon(
onPressed: _isLoading ? null : _startPaymentWithCallback,
icon: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.payment),
label: const Text('Pay (Callback)'),
),
),
const SizedBox(width: 8),
Expanded(
child: FilledButton.tonal(
onPressed: _isLoading ? null : _startPaymentAsync,
child: const Text('Pay (Async)'),
),
),
],
),
const SizedBox(height: 24),
// Result Display
if (_resultType != null && _responseText != null)
Card(
color: _getResultColor(_resultType!).withOpacity(0.1),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
_getResultIcon(_resultType!),
color: _getResultColor(_resultType!),
),
const SizedBox(width: 8),
Text(
_getResultTitle(_resultType!),
style: TextStyle(
fontWeight: FontWeight.bold,
color: _getResultColor(_resultType!),
),
),
],
),
const SizedBox(height: 12),
const Text(
'Encrypted Response:',
style: TextStyle(fontWeight: FontWeight.w500),
),
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: SelectableText(
_responseText!,
style: const TextStyle(
fontFamily: 'monospace',
fontSize: 11,
),
),
),
],
),
),
),
],
),
),
);
}
}
/// Callback implementation for payment results.
class _PaymentCallback implements BharatkoshWrapperCallback {
final void Function(BharatkoshResultType type, String response) onResult;
_PaymentCallback({required this.onResult});
@override
void onSuccess(String encryptedResponse) {
onResult(BharatkoshResultType.success, encryptedResponse);
}
@override
void onFailure(String encryptedResponse) {
onResult(BharatkoshResultType.failure, encryptedResponse);
}
@override
void onCancel(String encryptedResponse) {
onResult(BharatkoshResultType.cancel, encryptedResponse);
}
@override
void onError(String encryptedResponse) {
onResult(BharatkoshResultType.error, encryptedResponse);
}
}