Files
velocity-pay-flutter/lib/screens/pay/pay_controller.dart
2025-07-24 23:53:06 +02:00

239 lines
7.3 KiB
Dart

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/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<BillProduct> products = [];
List<PaymentProcessor> 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<String, dynamic> 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<String, dynamic> 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<TransactionController>(
context,
listen: false,
);
model.selectedProvider = transactionController.model.selectedProvider;
model.selectedProduct = transactionController.model.selectedProduct;
}
void _showErrorSnackBar(String? message) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message.toString()),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.deepOrange,
),
);
});
}
// formData can come from a repeat transaction
// otherwise build it from elements of the normal flow
Future<void> 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: 'peak',
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,
);
dynamic response = await http.post(
'/transaction',
transactionController.model.formData,
);
logger.i(response.toString());
if (response['status'] == 'SUCCESS') {
model.status = response['status'];
transactionController.updateConfirmationData(response);
} else {
model.status = response['status'];
model.errorMessage =
response['errorMessage'] ??
"Problem with the transaction. Please try again or contact support";
_showErrorSnackBar(model.errorMessage);
}
} on DioException catch (e, s) {
logger.e(s);
_showErrorSnackBar("Network error. Please try again or contact support");
} catch (e, s) {
logger.e(s);
_showErrorSnackBar(
"Problem processing that transaction. Please try again or contact support",
);
}
model.isLoading = false;
notifyListeners();
}
Future<void> getProducts(String providerId) async {
model.products = getFakeProducts();
try {
model.isLoading = true;
List<dynamic> response = await http.get(
'/providers/$providerId/products',
);
model.products = response.map((e) => BillProduct.fromJson(e)).toList();
} catch (e) {
logger.e(e);
_showErrorSnackBar(
"Problem fetching products, are you connected to the internet?",
);
}
model.isLoading = false;
notifyListeners();
}
Future<void> getPaymentProcessors() async {
try {
model.isLoading = true;
model.paymentProcessors = getFakePaymentProcessors();
notifyListeners();
List<dynamic> response = await http.get('/payment-processors');
model.paymentProcessors =
response.map((e) => PaymentProcessor.fromJson(e)).toList();
notifyListeners();
} catch (e) {
logger.e(e);
_showErrorSnackBar(
"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<PaymentProcessor> 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<BillProduct> getFakeProducts() {
return [
BillProduct(
uid: '1',
name: 'Product 1',
description: 'Description 1',
displayName: 'Product 1',
defaultAmount: 100,
requiresAmount: true,
),
];
}
}