import 'dart:async'; import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:kmobile/api/services/neft_service.dart'; import 'package:kmobile/api/services/rtgs_service.dart'; import 'package:kmobile/api/services/imps_service.dart'; import 'package:kmobile/data/models/imps_transaction.dart'; import 'package:kmobile/widgets/bank_logos.dart'; import 'package:kmobile/data/models/beneficiary.dart'; import 'package:kmobile/data/models/neft_transaction.dart'; import 'package:kmobile/data/models/payment_response.dart'; import 'package:kmobile/data/models/rtgs_transaction.dart'; import 'package:kmobile/di/injection.dart'; import 'package:kmobile/features/fund_transfer/screens/payment_animation.dart'; import 'package:kmobile/features/fund_transfer/screens/transaction_pin_screen.dart'; import '../../../l10n/app_localizations.dart'; import 'package:kmobile/api/services/payment_service.dart'; import 'package:kmobile/data/models/transfer.dart'; enum TransactionMode { neft, rtgs, imps } class FundTransferAmountScreen extends StatefulWidget { final String debitAccountNo; final Beneficiary creditBeneficiary; final String remitterName; final bool isOwnBank; const FundTransferAmountScreen({ super.key, required this.debitAccountNo, required this.creditBeneficiary, required this.remitterName, this.isOwnBank = false, }); @override State createState() => _FundTransferAmountScreenState(); } class _FundTransferAmountScreenState extends State { final _amountController = TextEditingController(); final _formKey = GlobalKey(); TransactionMode _selectedMode = TransactionMode.neft; @override void dispose() { _amountController.dispose(); super.dispose(); } void _onProceed() { if (_formKey.currentState!.validate()) { final amount = double.tryParse(_amountController.text) ?? 0; if (widget.isOwnBank) { Navigator.push( context, MaterialPageRoute( builder: (context) => TransactionPinScreen( onPinCompleted: (pinScreenContext, tpin) async { final transfer = Transfer( fromAccount: widget.debitAccountNo, toAccount: widget.creditBeneficiary.accountNo, toAccountType: 'Savings', // Assuming 'SB' for savings amount: _amountController.text, tpin: tpin, ); final paymentService = getIt(); final paymentResponseFuture = paymentService.processQuickPayWithinBank(transfer); Navigator.of(pinScreenContext).pushReplacement( MaterialPageRoute( builder: (_) => PaymentAnimationScreen( paymentResponse: paymentResponseFuture), ), ); }, ), ), ); } else { if (_selectedMode == TransactionMode.rtgs && amount < 200000) { showDialog( context: context, builder: (ctx) => AlertDialog( title: Text(AppLocalizations.of(context).invalidRtgs), content: Text(AppLocalizations.of(context).invalidRtgsPopUp), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(), child: Text(AppLocalizations.of(context).ok), ), ], ), ); return; // Stop further execution } Navigator.push( context, MaterialPageRoute( builder: (context) => TransactionPinScreen( onPinCompleted: (pinScreenContext, tpin) async { if (_selectedMode == TransactionMode.neft) { final neftTx = NeftTransaction( fromAccount: widget.debitAccountNo, toAccount: widget.creditBeneficiary.accountNo, amount: _amountController.text, ifscCode: widget.creditBeneficiary.ifscCode, remitterName: widget.remitterName, beneficiaryName: widget.creditBeneficiary.name, tpin: tpin, ); final neftService = getIt(); final completer = Completer(); Navigator.of(pinScreenContext).pushReplacement( MaterialPageRoute( builder: (_) => PaymentAnimationScreen( paymentResponse: completer.future), ), ); try { final neftResponse = await neftService.processNeftTransaction(neftTx); final paymentResponse = PaymentResponse( isSuccess: neftResponse.message.toUpperCase() == 'SUCCESS', date: DateTime.now(), creditedAccount: neftTx.toAccount, amount: neftTx.amount, currency: 'INR', utr: neftResponse.utr, ); completer.complete(paymentResponse); } on DioException catch (e) { String errorMessage; if (e.response != null && e.response!.data != null) { try { // final error = jsonDecode(e.response!.toString())['error']; final error = e.response?.data['error']; errorMessage = { "INCORRECT_TPIN": "Please Enter the correct TPIN", "INSUFFICIENT_FUNDS": "Your account does not have sufficient balance" }[error] ?? "Something Went Wrong"; } catch (_) { errorMessage = "Something Went Wrong"; } } else { errorMessage = "Something Went Wrong"; } final paymentResponse = PaymentResponse( isSuccess: false, errorMessage: errorMessage, ); completer.complete(paymentResponse); } catch (e) { if (!pinScreenContext.mounted) return; final paymentResponse = PaymentResponse( isSuccess: false, errorMessage: AppLocalizations.of(pinScreenContext) .somethingWentWrong, ); completer.complete(paymentResponse); } } //IMPS transaction else if (_selectedMode == TransactionMode.imps) { final impsTx = ImpsTransaction( fromAccount: widget.debitAccountNo, toAccount: widget.creditBeneficiary.accountNo, amount: _amountController.text, ifscCode: widget.creditBeneficiary.ifscCode, remitterName: widget.remitterName, beneficiaryName: widget.creditBeneficiary.name, tpin: tpin, ); final impsService = getIt(); final completer = Completer(); Navigator.of(pinScreenContext).pushReplacement( MaterialPageRoute( builder: (_) => PaymentAnimationScreen( paymentResponse: completer.future), ), ); try { final impsResponse = await impsService.processImpsTransaction(impsTx); final paymentResponse = PaymentResponse( isSuccess: impsResponse.message.toUpperCase() == 'SUCCESS', date: DateTime.now(), creditedAccount: impsTx.toAccount, amount: impsTx.amount, currency: 'INR', utr: impsResponse.utr, ); completer.complete(paymentResponse); } on DioException catch (e) { final error = jsonDecode(e.response.toString())['error']; var errorMessage = { "INCORRECT_TPIN": "Please Enter the correct TPIN", "INSUFFICIENT_FUNDS": "Your account does not have sufficient balance" }[error] ?? "Something Went Wrong"; final paymentResponse = PaymentResponse( isSuccess: false, errorMessage: errorMessage, ); completer.complete(paymentResponse); } catch (e) { final paymentResponse = PaymentResponse( isSuccess: false, errorMessage: "Something went Wrong", ); completer.complete(paymentResponse); } } //RTGS else { final rtgsTx = RtgsTransaction( fromAccount: widget.debitAccountNo, toAccount: widget.creditBeneficiary.accountNo, amount: _amountController.text, ifscCode: widget.creditBeneficiary.ifscCode, remitterName: widget.remitterName, beneficiaryName: widget.creditBeneficiary.name, tpin: tpin, ); final rtgsService = getIt(); final completer = Completer(); Navigator.of(pinScreenContext).pushReplacement( MaterialPageRoute( builder: (_) => PaymentAnimationScreen( paymentResponse: completer.future), ), ); try { final rtgsResponse = await rtgsService.processRtgsTransaction(rtgsTx); final paymentResponse = PaymentResponse( isSuccess: rtgsResponse.message.toUpperCase() == 'SUCCESS', date: DateTime.now(), creditedAccount: rtgsTx.toAccount, amount: rtgsTx.amount, currency: 'INR', utr: rtgsResponse.utr, ); completer.complete(paymentResponse); } on DioException catch (e) { final error = jsonDecode(e.response.toString())['error']; var errorMessage = { "INCORRECT_TPIN": "Please Enter the correct TPIN", "INSUFFICIENT_FUNDS": "Your account does not have sufficient balance" // ignore: use_build_context_synchronously }[error] ?? "Something Went Wrong"; final paymentResponse = PaymentResponse( isSuccess: false, errorMessage: errorMessage, ); completer.complete(paymentResponse); } catch (e) { final paymentResponse = PaymentResponse( isSuccess: false, errorMessage: "Something Went Wrong", ); completer.complete(paymentResponse); } } }, ), ), ); } } } @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); return Scaffold( appBar: AppBar( title: Text(loc.fundTransfer), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Debit Account (User) Text( loc.debitFrom, style: Theme.of(context).textTheme.titleSmall, ), Card( elevation: 0, margin: const EdgeInsets.symmetric(vertical: 8.0), child: ListTile( leading: Image.asset( 'assets/images/logo.png', width: 40, height: 40, ), title: Text(widget.remitterName), subtitle: Text(widget.debitAccountNo), ), ), const SizedBox(height: 24), // Credit Account (Beneficiary) Text( AppLocalizations.of(context).creditedTo, style: Theme.of(context).textTheme.titleSmall, ), Card( elevation: 0, margin: const EdgeInsets.symmetric(vertical: 8.0), child: ListTile( leading: getBankLogo(widget.creditBeneficiary.bankName), title: Text(widget.creditBeneficiary.name), subtitle: Text(widget.creditBeneficiary.accountNo), ), ), const SizedBox(height: 24), if (!widget.isOwnBank) ...[ // Transaction Mode Selection Text( AppLocalizations.of(context).selectTransactionType, style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 12), Container( decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey.shade300), ), child: ToggleButtons( isSelected: [ _selectedMode == TransactionMode.neft, _selectedMode == TransactionMode.rtgs, _selectedMode == TransactionMode.imps, ], onPressed: (index) { setState(() { _selectedMode = TransactionMode.values[index]; }); }, borderRadius: BorderRadius.circular(10), selectedColor: Theme.of(context).colorScheme.onPrimary, fillColor: Theme.of(context).primaryColor, color: Theme.of(context).colorScheme.onSurface, borderColor: Colors.transparent, selectedBorderColor: Colors.transparent, splashColor: Theme.of(context).primaryColor, highlightColor: Theme.of(context).primaryColor, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 24.0, vertical: 12.0), child: Text(AppLocalizations.of(context).neft), ), Padding( padding: const EdgeInsets.symmetric( horizontal: 24.0, vertical: 12.0), child: Text(AppLocalizations.of(context).rtgs), ), Padding( padding: const EdgeInsets.symmetric( horizontal: 24.0, vertical: 12.0), child: Text(AppLocalizations.of(context).imps), ), ], ), ), const SizedBox(height: 24), ], // Amount TextFormField( controller: _amountController, keyboardType: TextInputType.number, decoration: InputDecoration( labelText: loc.amount, border: const OutlineInputBorder(), prefixIcon: const Icon(Icons.currency_rupee), ), validator: (value) { if (value == null || value.isEmpty) { return loc.amountRequired; } if (double.tryParse(value) == null || double.parse(value) <= 0) { return loc.validAmount; } return null; }, ), const Spacer(), // Proceed Button SizedBox( width: double.infinity, child: ElevatedButton( onPressed: _onProceed, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical: 16), ), child: Text(AppLocalizations.of(context).proceed), ), ), ], ), ), ), ); } }