import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:qpay/http/http.dart'; import 'package:qpay/models/api_response.dart'; import 'package:qpay/screens/home/home_controller.dart' as hc; import 'package:qpay/screens/transaction_controller.dart'; import 'package:qpay/screens/transaction_controller.dart' as tc; part 'pay_controller.freezed.dart'; part 'pay_controller.g.dart'; class PayScreenModel { List products = []; List paymentProcessors = []; hc.BillProvider? selectedProvider; BillProduct? selectedProduct; PaymentProcessor? selectedPaymentProcessor; bool isLoading = false; String? errorMessage; String? status; } @freezed abstract class PaymentProcessor with _$PaymentProcessor { const factory PaymentProcessor({ required String name, required String description, required String image, required String accountFieldName, required String accountFieldLabel, required String label, required String authType, }) = _PaymentProcessor; factory PaymentProcessor.fromJson(Map json) => _$PaymentProcessorFromJson(json); } @freezed abstract class BillProduct with _$BillProduct { const factory BillProduct({ required String uid, required String name, required String description, required String displayName, required double defaultAmount, required bool requiresAmount, }) = _BillProduct; factory BillProduct.fromJson(Map json) => _$BillProductFromJson(json); } class PayController extends ChangeNotifier { final PayScreenModel model = PayScreenModel(); final Http http = Http(); late SharedPreferences prefs; late TransactionController transactionController; BuildContext context; PayController(this.context) { transactionController = Provider.of( context, listen: false, ); model.selectedProvider = transactionController.model.selectedProvider; model.selectedProduct = transactionController.model.selectedProduct; } // formData can come from a repeat transaction // otherwise build it from elements of the normal flow Future>> doTransaction([tc.FormData? formData]) async { try { model.isLoading = true; notifyListeners(); prefs = await SharedPreferences.getInstance(); transactionController.model.formData = formData ?? transactionController.model.formData.copyWith( type: "CONFIRM", billClientId: model.selectedProvider?.clientId ?? '', debitRef: 'Velocity', debitCurrency: 'USD', creditAccount: transactionController.model.formData.creditAccount, creditPhone: transactionController.model.formData.creditPhone, billName: model.selectedProvider?.name ?? '', paymentProcessorLabel: model.selectedPaymentProcessor?.label ?? '', paymentProcessorName: model.selectedPaymentProcessor?.name ?? '', paymentProcessorImage: model.selectedPaymentProcessor?.image ?? '', providerImage: model.selectedProvider?.image ?? '', providerLabel: model.selectedProvider?.label ?? '', userId: prefs.getString("userId") ?? '', creditName: transactionController.model.formData.creditName, creditEmail: transactionController.model.formData.creditEmail, productUid: model.selectedProduct?.uid, billProductName: model.selectedProduct?.name, ); dynamic workflowResponse = await http.post( '/public/transaction', transactionController.model.formData, ); logger.i(workflowResponse.toString()); dynamic response = workflowResponse['body']; model.isLoading = false; notifyListeners(); if (response['status'] == 'SUCCESS') { model.status = response['status']; transactionController.updateConfirmationData(response); return ApiResponse.success(Map.from(response)); } else { model.status = response['status']; model.errorMessage = response['errorMessage'] ?? "Problem with the transaction. Please try again or contact support"; return ApiResponse.failure(model.errorMessage!); } } on DioException catch (e, s) { logger.e(s); model.isLoading = false; notifyListeners(); return ApiResponse.failure("Network error. Please try again or contact support"); } catch (e, s) { logger.e(s); model.isLoading = false; notifyListeners(); return ApiResponse.failure( "Problem processing that transaction. Please try again or contact support", ); } } Future>> getProducts(String providerId) async { try { model.isLoading = true; notifyListeners(); List response = await http.get( '/public/providers/$providerId/products', ); model.products = response.map((e) => BillProduct.fromJson(e)).toList(); model.isLoading = false; notifyListeners(); return ApiResponse.success(model.products); } catch (e) { logger.e(e); model.isLoading = false; notifyListeners(); return ApiResponse.failure( "Problem fetching products, are you connected to the internet?", ); } } Future>> getPaymentProcessors() async { try { model.isLoading = true; model.paymentProcessors = getFakePaymentProcessors(); notifyListeners(); List response = await http.get('/public/payment-processors'); model.paymentProcessors = response.map((e) => PaymentProcessor.fromJson(e)).toList(); model.isLoading = false; notifyListeners(); return ApiResponse.success(model.paymentProcessors); } catch (e) { logger.e(e); model.isLoading = false; notifyListeners(); return ApiResponse.failure( "Problem fetching payment processors, are you connected to the internet?", ); } } void updateAdditionalRecipientDetails(String name, String email) { transactionController.model.formData = transactionController.model.formData .copyWith(creditName: name, creditEmail: email); notifyListeners(); } void updatePhone(String phone) { transactionController.model.formData = transactionController.model.formData .copyWith(debitPhone: phone); notifyListeners(); } void updateAmount(String amount) { transactionController.model.formData = transactionController.model.formData .copyWith(amount: amount); notifyListeners(); } void updateSelectedProduct(BillProduct product) { model.selectedProduct = product; notifyListeners(); } void updateSelectedPaymentProcessor(PaymentProcessor processor) { model.selectedPaymentProcessor = processor; notifyListeners(); } List getFakePaymentProcessors() { return [ PaymentProcessor( name: "EcoCash", description: "Pay using your mobile wallet.", image: "ecocash.png", accountFieldName: "phoneNumber", accountFieldLabel: "EcoCash Phone Number", label: "ECOCASH", authType: "REMOTE", ), ]; } List getFakeProducts() { return [ BillProduct( uid: '1', name: 'Product 1', description: 'Description 1', displayName: 'Product 1', defaultAmount: 100, requiresAmount: true, ), ]; } }