updating sequence of flows

This commit is contained in:
2025-11-02 19:49:25 +02:00
parent 087a9aa4bc
commit 895925d32f
9 changed files with 149 additions and 179 deletions

View File

@@ -1,6 +1,6 @@
; BASE_URL=https://peakapi.qantra.co.zw/api
BASE_URL=https://peakapi.qantra.co.zw/api
; BASE_URL=http://192.168.100.26:6950/api
BASE_URL=http://10.69.5.204:6950/api
; BASE_URL=http://10.69.5.204:6950/api
; BASE_URL=http://192.168.1.164:6950/api
; BASE_URL=http://192.168.120.160:6950/api
; BASE_URL=http://10.10.2.92:6950/api

View File

@@ -110,9 +110,7 @@ class _MyAppState extends State<MyApp> {
),
GoRoute(
path: '/gateway',
builder:
(context, state) =>
GatewayScreen(url: state.extra as String),
builder: (context, state) => const GatewayScreen(),
),
GoRoute(
path: '/receipt',

View File

@@ -84,14 +84,11 @@ class ConfirmController extends ChangeNotifier {
if (transactionController.model.selectedPaymentProcessor.authType ==
"WEB") {
if (context.mounted) {
context.push(
'/gateway',
extra: transactionController.model.receiptData?['targetUrl'],
);
context.push('/gateway');
}
} else {
await pollTransaction(transactionController.model.receiptData?['id']);
context.push('/receipt');
context.push('/integration');
}
} else {
model.status = response['status'];

View File

@@ -60,15 +60,10 @@ class GatewayController extends ChangeNotifier {
dynamic response = workflowResponse['body'];
model.status = response['status'];
transactionController.model.paymentStatus = response['status'];
transactionController.model.paymentStatus = response['paymentStatus'];
if(model.status == 'SUCCESS') {
transactionController.updateReceiptData(response);
if(model.status == 'PENDING') {
_showErrorSnackBar('Transaction status still pending');
}
if(model.status != 'FAILED') {
_showErrorSnackBar(response['errorMessage'].isEmpty ? 'Transaction failed' : response['errorMessage']);
}
notifyListeners();
} catch (e) {

View File

@@ -7,8 +7,7 @@ import 'package:qpay/screens/gateway/gateway_controller.dart';
import 'package:webview_flutter/webview_flutter.dart';
class GatewayScreen extends StatefulWidget {
final String url;
const GatewayScreen({super.key, required this.url});
const GatewayScreen({super.key});
@override
State<GatewayScreen> createState() => _GatewayScreenState();
@@ -17,20 +16,22 @@ class GatewayScreen extends StatefulWidget {
class _GatewayScreenState extends State<GatewayScreen> {
late WebViewController _webViewController;
late GatewayController controller;
late String url;
bool integrationStarted = false;
@override
void initState() {
super.initState();
_initWebView(widget.url);
controller = GatewayController(context);
url = controller.transactionController.model.receiptData?['targetUrl'];
_initWebView(url);
bool simulate = bool.parse(dotenv.env['SIMULATE_PAYMENT_SUCCESS']!);
if(simulate) {
sleep(Duration(seconds: 10));
String url = widget.url.replaceAll("/pay/", "/receipt/");
_webViewController.loadRequest(Uri.parse(url));
String targetUrl = url.replaceAll("/pay/", "/receipt/");
_webViewController.loadRequest(Uri.parse(targetUrl));
}
}
@@ -71,7 +72,6 @@ class _GatewayScreenState extends State<GatewayScreen> {
setState(() {
integrationStarted = true;
});
await controller.poll(controller.transactionController.model.receiptData?['id']);
WidgetsBinding.instance.addPostFrameCallback((_) {
context.go('/integration');

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:qpay/http/http.dart';
import 'package:qpay/screens/gateway/gateway_controller.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -18,6 +19,7 @@ class IntegrationController extends ChangeNotifier {
final IntegrationModel model = IntegrationModel();
final Http http = Http();
late TransactionController transactionController;
late GatewayController gatewayController;
late SharedPreferences prefs;
BuildContext context;
@@ -27,6 +29,8 @@ class IntegrationController extends ChangeNotifier {
context,
listen: false,
);
gatewayController = GatewayController(context);
}
void _showErrorSnackBar(String? message) {
@@ -58,6 +62,8 @@ class IntegrationController extends ChangeNotifier {
if (response['status'] == 'SUCCESS') {
model.status = response['status'];
transactionController.updateReceiptData(response);
gatewayController.poll(transactionController.model.confirmationData['id']);
} else {
model.status = response['status'];
model.errorMessage = response['errorMessage'];

View File

@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:provider/provider.dart';
import 'package:qpay/screens/gateway/gateway_controller.dart';
import 'package:qpay/screens/transaction_controller.dart';
import 'package:skeletonizer/skeletonizer.dart';
@@ -17,7 +16,6 @@ class IntegrationScreen extends StatefulWidget {
class _IntegrationScreenState extends State<IntegrationScreen> {
late IntegrationController controller;
late TransactionController transactionController;
late GatewayController gatewayController;
bool integrating = false;
@@ -25,27 +23,12 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
void initState() {
super.initState();
controller = IntegrationController(context);
gatewayController = GatewayController(context);
transactionController = Provider.of<TransactionController>(
context,
listen: false,
);
}
@override
void didChangeDependencies() async {
super.didChangeDependencies();
if(integrating) return;
String status = Provider.of<TransactionController>(context).model.paymentStatus;
if(status == 'SUCCESS') {
setState(() {
integrating = true;
});
await controller.doIntegration();
}
controller.doIntegration();
}
void _showErrorSnackBar(String? message) {
@@ -74,9 +57,6 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
title: const Text('Completing your transaction'),
),
body: ListenableBuilder(
listenable: gatewayController,
builder: (context, child) {
return ListenableBuilder(
listenable: controller,
builder: (context, child) {
if (controller.model.status == 'SUCCESS') {
@@ -94,28 +74,6 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
children: [
SizedBox(height: 75),
if(transactionController.model.paymentStatus != 'SUCCESS')
Column(
children: [
Text(
'We failed to check your payment status',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
SizedBox(height: 50,),
Skeletonizer(
enabled: gatewayController.model.isLoading,
child: OutlinedButton(
child: Text('Check latest status'),
onPressed: () {
gatewayController.poll(transactionController.model.receiptData?['id']);
},
),
),
],
),
if(transactionController.model.paymentStatus == 'SUCCESS')
Column(
children: [
Text(
@@ -157,8 +115,6 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
),
);
},
);
}
),
);
}

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
import 'package:qpay/screens/gateway/gateway_controller.dart';
import 'package:qpay/screens/transaction_controller.dart';
@@ -6,6 +7,7 @@ import 'package:go_router/go_router.dart';
import 'package:qpay/screens/receipt/receipt_controller.dart';
import 'package:share_plus/share_plus.dart';
import 'package:skeletonizer/skeletonizer.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'package:widgets_to_image/widgets_to_image.dart';
class ReceiptScreen extends StatefulWidget {
@@ -96,6 +98,11 @@ class _ReceiptScreenState extends State<ReceiptScreen>
receiptController.setLoading(true);
await gatewayController.poll(receiptController.model.receiptData?["id"]);
if(gatewayController.model.status == "SUCCESS"){
receiptController.getReceiptData(transactionController.model.receiptData?["id"]);
if(transactionController.model.receiptData?["pollingStatus"]) {
receiptController.setLoading(false);
return; // no need to proceed if we've already polled successfully
}
await _retry();
}
receiptController.setLoading(false);
@@ -253,21 +260,22 @@ class _ReceiptScreenState extends State<ReceiptScreen>
CrossAxisAlignment.center,
children: [
SizedBox(height: 5),
// receiptController
// .model
// .receiptData?["providerImage"] !=
// null
// ? Image.asset(
// 'assets/${receiptController.model.receiptData?["providerImage"] ?? ''}',
// width: 50,
// )
// : SizedBox.shrink(),
Text(
receiptController
.model
.receiptData?["billName"] ??
"",
style: TextStyle(fontSize: 14),
style: TextStyle(fontSize: 16),
),
Text(
receiptController
.model
.receiptData?["createdAt"] != null ?
DateFormat.yMMMd().add_jm().format(DateTime.parse(
receiptController
.model
.receiptData?["createdAt"])) : "",
style: TextStyle(fontSize: 15),
),
],
),
@@ -602,9 +610,11 @@ class _ReceiptScreenState extends State<ReceiptScreen>
),
],
),
const SizedBox(height: 10),
if(receiptController.model.receiptData?["integrationStatus"] != "SUCCESS" &&
receiptController.model.receiptData?["paymentStatus"] == "SUCCESS")
Column(
children: [
const SizedBox(height: 10),
Skeletonizer(
enabled:
receiptController
@@ -624,7 +634,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
),
),
Expanded(
child: Text("Billing account update failed, please tap on retry to try again",
child: Text("Billing account update failed, please tap on Retry to try again",
style: TextStyle(
fontSize: 16,
color: Theme.of(
@@ -639,6 +649,8 @@ class _ReceiptScreenState extends State<ReceiptScreen>
],
),
),
],
),
SizedBox(height: 10,),
Skeletonizer(
@@ -767,7 +779,11 @@ class _ReceiptScreenState extends State<ReceiptScreen>
.model
.receiptData?["additionalData"]
.map<Widget>((data) {
return Column(
return Skeletonizer(
enabled: receiptController
.model
.isLoading,
child: Column(
children: [
Row(
mainAxisAlignment:
@@ -800,6 +816,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
height: 10,
),
],
),
);
})
.toList(),

View File

@@ -41,6 +41,7 @@ class TransactionModel {
providerLabel: '',
);
String paymentStatus = 'PENDING';
String pollStatus = 'PENDING';
String? errorMessage;
}