paygic 1.0.3
paygic: ^1.0.3 copied to clipboard
Official plugin of Paygic Upi Payments by Paygic payment gateway. Powered by Yes Bank.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:paygic/paygic.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Paygic UPI Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF2B1966),
),
useMaterial3: true,
),
home: const PaymentDemoPage(),
);
}
}
class PaymentDemoPage extends StatefulWidget {
const PaymentDemoPage({super.key});
@override
State<PaymentDemoPage> createState() => _PaymentDemoPageState();
}
class _PaymentDemoPageState extends State<PaymentDemoPage> {
// Form controllers
final _midController = TextEditingController(text: '');
final _tokenController = TextEditingController(text: '');
final _amountController = TextEditingController(text: '1');
final _orderIdController = TextEditingController();
final _nameController = TextEditingController(text: 'Test Customer');
final _emailController = TextEditingController(text: 'test@example.com');
final _mobileController = TextEditingController(text: '9876543210');
PaymentResult? _lastResult;
bool _isLoading = false;
bool _hasUpiApp = false;
@override
void initState() {
super.initState();
_checkUpiAvailability();
_generateOrderId();
}
void _generateOrderId() {
_orderIdController.text = 'ORDER_${DateTime.now().millisecondsSinceEpoch}';
}
Future<void> _checkUpiAvailability() async {
final hasUpi = await Paygic.hasUpiApp();
setState(() {
_hasUpiApp = hasUpi;
});
}
Future<void> _initiatePayment() async {
// Validate inputs
if (_midController.text.isEmpty) {
_showError('Please enter Merchant ID (MID)');
return;
}
if (_tokenController.text.isEmpty) {
_showError('Please enter API Token');
return;
}
setState(() {
_isLoading = true;
_lastResult = null;
});
try {
// NEW SIMPLIFIED API - Just call initiatePayment!
final result = await Paygic.initiatePayment(
context: context,
mid: _midController.text.trim(),
token: _tokenController.text.trim(),
amount: double.tryParse(_amountController.text) ?? 1.0,
merchantReferenceId: _orderIdController.text.trim(),
customerName: _nameController.text.trim(),
customerEmail: _emailController.text.trim(),
customerMobile: _mobileController.text.trim(),
);
setState(() {
_lastResult = result;
_isLoading = false;
});
// Handle result
if (mounted) {
_handleResult(result);
}
// Generate new order ID for next payment
_generateOrderId();
} catch (e) {
setState(() {
_isLoading = false;
});
if (mounted) {
_showError('Error: $e');
}
}
}
void _handleResult(PaymentResult result) {
if (result.isSuccess) {
// Payment successful!
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Row(
children: [
Icon(Icons.check_circle, color: Colors.green, size: 32),
SizedBox(width: 12),
Text('Payment Successful'),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildResultRow('UTR', result.utr ?? 'N/A'),
_buildResultRow('Amount', '₹${result.amount?.toStringAsFixed(2) ?? 'N/A'}'),
_buildResultRow('Status', result.txnStatus ?? 'SUCCESS'),
],
),
actions: [
ElevatedButton(
onPressed: () => Navigator.pop(context),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
child: const Text('Done'),
),
],
),
);
} else if (result.isFailed) {
// Payment failed
_showError(result.errorMessage ?? 'Payment failed');
} else if (result.isCancelled) {
// User cancelled - show subtle message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Payment cancelled'),
duration: Duration(seconds: 2),
),
);
}
}
Widget _buildResultRow(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
Text(value),
],
),
);
}
void _showError(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Colors.red,
duration: const Duration(seconds: 4),
),
);
}
Future<void> _checkInstalledApps() async {
final apps = await Paygic.getInstalledUpiApps();
if (mounted) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Installed UPI Apps'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: apps.isEmpty
? [const Text('No UPI apps found')]
: apps.map((app) => ListTile(
leading: const Icon(Icons.payment),
title: Text(app.displayName),
)).toList(),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'),
),
],
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Paygic UPI Demo'),
backgroundColor: const Color(0xFF2B1966),
foregroundColor: Colors.white,
actions: [
IconButton(
icon: const Icon(Icons.apps),
onPressed: _checkInstalledApps,
tooltip: 'Check UPI Apps',
),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// UPI Availability indicator
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _hasUpiApp ? Colors.green.shade50 : Colors.orange.shade50,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Icon(
_hasUpiApp ? Icons.check_circle : Icons.warning,
color: _hasUpiApp ? Colors.green : Colors.orange,
),
const SizedBox(width: 8),
Text(
_hasUpiApp ? 'UPI apps available' : 'No UPI apps detected',
style: TextStyle(
color: _hasUpiApp ? Colors.green.shade700 : Colors.orange.shade700,
),
),
],
),
),
const SizedBox(height: 24),
// Merchant Credentials Section
const Text(
'Merchant Credentials',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
const SizedBox(height: 8),
TextField(
controller: _midController,
decoration: const InputDecoration(
labelText: 'Merchant ID (MID)',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.business),
),
),
const SizedBox(height: 12),
TextField(
controller: _tokenController,
decoration: const InputDecoration(
labelText: 'API Token',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.key),
),
obscureText: true,
),
const SizedBox(height: 24),
// Payment Details Section
const Text(
'Payment Details',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
const SizedBox(height: 8),
Row(
children: [
Expanded(
child: TextField(
controller: _amountController,
decoration: const InputDecoration(
labelText: 'Amount (₹)',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.currency_rupee),
),
keyboardType: TextInputType.number,
),
),
const SizedBox(width: 12),
Expanded(
child: TextField(
controller: _orderIdController,
decoration: InputDecoration(
labelText: 'Order ID',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
icon: const Icon(Icons.refresh),
onPressed: _generateOrderId,
),
),
),
),
],
),
const SizedBox(height: 24),
// Customer Details Section
const Text(
'Customer Details',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
const SizedBox(height: 8),
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Customer Name',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.person),
),
),
const SizedBox(height: 12),
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Customer Email',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.email),
),
keyboardType: TextInputType.emailAddress,
),
const SizedBox(height: 12),
TextField(
controller: _mobileController,
decoration: const InputDecoration(
labelText: 'Customer Mobile',
border: OutlineInputBorder(),
prefixIcon: Icon(Icons.phone),
),
keyboardType: TextInputType.phone,
),
const SizedBox(height: 24),
// Pay button
ElevatedButton(
onPressed: _isLoading ? null : _initiatePayment,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF2B1966),
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(vertical: 16),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: _isLoading
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.payment),
SizedBox(width: 8),
Text(
'Pay Now',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
],
),
),
const SizedBox(height: 24),
// Last result
if (_lastResult != null) ...[
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: _lastResult!.isSuccess
? Colors.green.shade50
: (_lastResult!.isCancelled
? Colors.grey.shade100
: Colors.red.shade50),
borderRadius: BorderRadius.circular(8),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Last Payment: ${_lastResult!.status.name.toUpperCase()}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
if (_lastResult!.utr != null)
Text('UTR: ${_lastResult!.utr}'),
if (_lastResult!.amount != null)
Text('Amount: ₹${_lastResult!.amount?.toStringAsFixed(2)}'),
if (_lastResult!.errorMessage != null)
Text('Message: ${_lastResult!.errorMessage}'),
],
),
),
],
const SizedBox(height: 24),
// Instructions
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.blue.shade50,
borderRadius: BorderRadius.circular(8),
),
child: const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(Icons.info, color: Colors.blue),
SizedBox(width: 8),
Text(
'How it works',
style: TextStyle(
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
],
),
SizedBox(height: 8),
Text(
'1. Enter your Paygic MID and Token\n'
'2. Fill payment and customer details\n'
'3. Click "Pay Now"\n'
'4. Choose UPI app and complete payment\n'
'5. Result returned automatically!',
style: TextStyle(fontSize: 13),
),
],
),
),
],
),
),
);
}
@override
void dispose() {
_midController.dispose();
_tokenController.dispose();
_amountController.dispose();
_orderIdController.dispose();
_nameController.dispose();
_emailController.dispose();
_mobileController.dispose();
super.dispose();
}
}