import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:provider/provider.dart'; import 'package:qpay/models/api_response.dart'; import 'package:qpay/screens/transaction_controller.dart'; import 'package:qpay/widgets/app_snack_bar.dart'; import '../../http/http.dart'; part 'confirm_controller.freezed.dart'; part 'confirm_controller.g.dart'; class ConfirmModel { bool isLoading = false; String status = ''; String? errorMessage; bool isCancelled = false; } @freezed abstract class ConfirmData with _$ConfirmData { const factory ConfirmData({ required String status, required String errorMessage, }) = _ConfirmData; factory ConfirmData.fromJson(Map json) => _$ConfirmDataFromJson(json); } class ConfirmController extends ChangeNotifier { final ConfirmModel model = ConfirmModel(); late TransactionController transactionController; final Http http = Http(); BuildContext context; ConfirmController(this.context) { transactionController = Provider.of( context, listen: false, ); } void _showErrorSnackBar(String? message) { WidgetsBinding.instance.addPostFrameCallback((_) { AppSnackBar.showError(context, message.toString()); }); } Future>> doTransaction() async { try { model.isLoading = true; notifyListeners(); // todo: find out if we still need to update these fields transactionController.model.formData = transactionController .model .formData .copyWith( type: "REQUEST", trace: transactionController.model.confirmationData['trace'], id: transactionController.model.confirmationData['id'], authType: transactionController.model.selectedPaymentProcessor.authType, ); dynamic workflowResponse = await http.get( '/public/transaction/request/' '${transactionController.model.confirmationData['id']}/' '${transactionController.model.selectedPaymentProcessor.authType}', ); logger.i(workflowResponse.toString()); dynamic response = workflowResponse['body']; model.isLoading = false; notifyListeners(); if (response['status'] != 'FAILED') { model.status = response['status']; transactionController.updateReceiptData(response); return ApiResponse.success(Map.from(response)); } else { model.status = response['status']; model.errorMessage = response['errorMessage']; return ApiResponse.failure( response['errorMessage'] ?? "Transaction failed", ); } } catch (e) { logger.e(e); model.errorMessage = e.toString(); model.isLoading = false; notifyListeners(); return ApiResponse.failure( "Problem completing the transaction, please try again.", ); } } Future>> pollTransaction(String uid) async { while (!model.isCancelled) { // Check again after delay in case it was cancelled during the delay if (model.isCancelled) break; try { dynamic response = await http.get('/public/transaction/poll/$uid'); logger.i(response.toString()); if (response['status'] == 'SUCCESS') { model.status = response['status']; transactionController.updateReceiptData(response); model.isCancelled = true; notifyListeners(); return ApiResponse.success(Map.from(response)); } } catch (e) { logger.e(e); _showErrorSnackBar( "Network error. Please try again or contact support", ); } // Check cancellation flag instead of true // Wait 5 seconds before the next poll await Future.delayed(const Duration(seconds: 5)); } return ApiResponse.failure("Polling cancelled"); } Future>> updateVelocityTransactionOtp( String id, Map transaction, ) async { try { model.isLoading = true; notifyListeners(); dynamic response = await http.put( '/public/transaction/$id/velocity-otp', transaction, ); model.isLoading = false; notifyListeners(); if (response['status'] == 'SUCCESS') { return ApiResponse.success(Map.from(response)); } else { model.errorMessage = response['errorMessage'] ?? "Problem updating transaction. Please try again or contact support"; return ApiResponse.failure(model.errorMessage!); } } on DioException catch (e, s) { if (kDebugMode) { logger.e(e); logger.e(s); } model.isLoading = false; notifyListeners(); return ApiResponse.failure( "Network error. Please try again or contact support", ); } catch (e, s) { if (kDebugMode) { logger.e(e); logger.e(s); } model.isLoading = false; notifyListeners(); return ApiResponse.failure( "Problem updating that transaction. Please try again or contact support", ); } } @override void dispose() { // TODO: implement dispose super.dispose(); } }