249 lines
7.8 KiB
Dart
249 lines
7.8 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/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<BillProduct> products = [];
|
|
List<PaymentProcessor> paymentProcessors = [];
|
|
hc.BillProvider? selectedProvider;
|
|
BillProduct? selectedProduct;
|
|
PaymentProcessor? selectedPaymentProcessor;
|
|
String region = '';
|
|
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;
|
|
}
|
|
|
|
// formData can come from a repeat transaction
|
|
// otherwise build it from elements of the normal flow
|
|
Future<ApiResponse<Map<String, dynamic>>> 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,
|
|
region: model.region,
|
|
);
|
|
|
|
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<String, dynamic>.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<ApiResponse<List<BillProduct>>> getProducts(String providerId) async {
|
|
try {
|
|
model.isLoading = true;
|
|
notifyListeners();
|
|
|
|
List<dynamic> 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<ApiResponse<List<PaymentProcessor>>> getPaymentProcessors() async {
|
|
try {
|
|
model.isLoading = true;
|
|
model.paymentProcessors = getFakePaymentProcessors();
|
|
notifyListeners();
|
|
|
|
List<dynamic> 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<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,
|
|
),
|
|
];
|
|
}
|
|
}
|