updating sequence of flows
This commit is contained in:
@@ -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://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.1.164:6950/api
|
||||||
; BASE_URL=http://192.168.120.160:6950/api
|
; BASE_URL=http://192.168.120.160:6950/api
|
||||||
; BASE_URL=http://10.10.2.92:6950/api
|
; BASE_URL=http://10.10.2.92:6950/api
|
||||||
|
|||||||
@@ -110,9 +110,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/gateway',
|
path: '/gateway',
|
||||||
builder:
|
builder: (context, state) => const GatewayScreen(),
|
||||||
(context, state) =>
|
|
||||||
GatewayScreen(url: state.extra as String),
|
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: '/receipt',
|
path: '/receipt',
|
||||||
|
|||||||
@@ -84,14 +84,11 @@ class ConfirmController extends ChangeNotifier {
|
|||||||
if (transactionController.model.selectedPaymentProcessor.authType ==
|
if (transactionController.model.selectedPaymentProcessor.authType ==
|
||||||
"WEB") {
|
"WEB") {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
context.push(
|
context.push('/gateway');
|
||||||
'/gateway',
|
|
||||||
extra: transactionController.model.receiptData?['targetUrl'],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await pollTransaction(transactionController.model.receiptData?['id']);
|
await pollTransaction(transactionController.model.receiptData?['id']);
|
||||||
context.push('/receipt');
|
context.push('/integration');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
|
|||||||
@@ -60,15 +60,10 @@ class GatewayController extends ChangeNotifier {
|
|||||||
dynamic response = workflowResponse['body'];
|
dynamic response = workflowResponse['body'];
|
||||||
|
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
transactionController.model.paymentStatus = response['status'];
|
transactionController.model.paymentStatus = response['paymentStatus'];
|
||||||
|
|
||||||
|
if(model.status == 'SUCCESS') {
|
||||||
transactionController.updateReceiptData(response);
|
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();
|
notifyListeners();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import 'package:qpay/screens/gateway/gateway_controller.dart';
|
|||||||
import 'package:webview_flutter/webview_flutter.dart';
|
import 'package:webview_flutter/webview_flutter.dart';
|
||||||
|
|
||||||
class GatewayScreen extends StatefulWidget {
|
class GatewayScreen extends StatefulWidget {
|
||||||
final String url;
|
const GatewayScreen({super.key});
|
||||||
const GatewayScreen({super.key, required this.url});
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<GatewayScreen> createState() => _GatewayScreenState();
|
State<GatewayScreen> createState() => _GatewayScreenState();
|
||||||
@@ -17,20 +16,22 @@ class GatewayScreen extends StatefulWidget {
|
|||||||
class _GatewayScreenState extends State<GatewayScreen> {
|
class _GatewayScreenState extends State<GatewayScreen> {
|
||||||
late WebViewController _webViewController;
|
late WebViewController _webViewController;
|
||||||
late GatewayController controller;
|
late GatewayController controller;
|
||||||
|
late String url;
|
||||||
|
|
||||||
bool integrationStarted = false;
|
bool integrationStarted = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_initWebView(widget.url);
|
|
||||||
controller = GatewayController(context);
|
controller = GatewayController(context);
|
||||||
|
url = controller.transactionController.model.receiptData?['targetUrl'];
|
||||||
|
_initWebView(url);
|
||||||
|
|
||||||
bool simulate = bool.parse(dotenv.env['SIMULATE_PAYMENT_SUCCESS']!);
|
bool simulate = bool.parse(dotenv.env['SIMULATE_PAYMENT_SUCCESS']!);
|
||||||
if(simulate) {
|
if(simulate) {
|
||||||
sleep(Duration(seconds: 10));
|
sleep(Duration(seconds: 10));
|
||||||
String url = widget.url.replaceAll("/pay/", "/receipt/");
|
String targetUrl = url.replaceAll("/pay/", "/receipt/");
|
||||||
_webViewController.loadRequest(Uri.parse(url));
|
_webViewController.loadRequest(Uri.parse(targetUrl));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +72,6 @@ class _GatewayScreenState extends State<GatewayScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
integrationStarted = true;
|
integrationStarted = true;
|
||||||
});
|
});
|
||||||
await controller.poll(controller.transactionController.model.receiptData?['id']);
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
context.go('/integration');
|
context.go('/integration');
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:qpay/http/http.dart';
|
import 'package:qpay/http/http.dart';
|
||||||
|
import 'package:qpay/screens/gateway/gateway_controller.dart';
|
||||||
import 'package:qpay/screens/transaction_controller.dart';
|
import 'package:qpay/screens/transaction_controller.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ class IntegrationController extends ChangeNotifier {
|
|||||||
final IntegrationModel model = IntegrationModel();
|
final IntegrationModel model = IntegrationModel();
|
||||||
final Http http = Http();
|
final Http http = Http();
|
||||||
late TransactionController transactionController;
|
late TransactionController transactionController;
|
||||||
|
late GatewayController gatewayController;
|
||||||
late SharedPreferences prefs;
|
late SharedPreferences prefs;
|
||||||
|
|
||||||
BuildContext context;
|
BuildContext context;
|
||||||
@@ -27,6 +29,8 @@ class IntegrationController extends ChangeNotifier {
|
|||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
gatewayController = GatewayController(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showErrorSnackBar(String? message) {
|
void _showErrorSnackBar(String? message) {
|
||||||
@@ -58,6 +62,8 @@ class IntegrationController extends ChangeNotifier {
|
|||||||
if (response['status'] == 'SUCCESS') {
|
if (response['status'] == 'SUCCESS') {
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
transactionController.updateReceiptData(response);
|
transactionController.updateReceiptData(response);
|
||||||
|
|
||||||
|
gatewayController.poll(transactionController.model.confirmationData['id']);
|
||||||
} else {
|
} else {
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
model.errorMessage = response['errorMessage'];
|
model.errorMessage = response['errorMessage'];
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:qpay/screens/gateway/gateway_controller.dart';
|
|
||||||
import 'package:qpay/screens/transaction_controller.dart';
|
import 'package:qpay/screens/transaction_controller.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
|
|
||||||
@@ -17,7 +16,6 @@ class IntegrationScreen extends StatefulWidget {
|
|||||||
class _IntegrationScreenState extends State<IntegrationScreen> {
|
class _IntegrationScreenState extends State<IntegrationScreen> {
|
||||||
late IntegrationController controller;
|
late IntegrationController controller;
|
||||||
late TransactionController transactionController;
|
late TransactionController transactionController;
|
||||||
late GatewayController gatewayController;
|
|
||||||
|
|
||||||
bool integrating = false;
|
bool integrating = false;
|
||||||
|
|
||||||
@@ -25,27 +23,12 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
controller = IntegrationController(context);
|
controller = IntegrationController(context);
|
||||||
gatewayController = GatewayController(context);
|
|
||||||
transactionController = Provider.of<TransactionController>(
|
transactionController = Provider.of<TransactionController>(
|
||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
controller.doIntegration();
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showErrorSnackBar(String? message) {
|
void _showErrorSnackBar(String? message) {
|
||||||
@@ -74,9 +57,6 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
|
|||||||
title: const Text('Completing your transaction'),
|
title: const Text('Completing your transaction'),
|
||||||
),
|
),
|
||||||
body: ListenableBuilder(
|
body: ListenableBuilder(
|
||||||
listenable: gatewayController,
|
|
||||||
builder: (context, child) {
|
|
||||||
return ListenableBuilder(
|
|
||||||
listenable: controller,
|
listenable: controller,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
if (controller.model.status == 'SUCCESS') {
|
if (controller.model.status == 'SUCCESS') {
|
||||||
@@ -94,28 +74,6 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
|
|||||||
children: [
|
children: [
|
||||||
SizedBox(height: 75),
|
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(
|
Column(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
@@ -157,8 +115,6 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:qpay/screens/gateway/gateway_controller.dart';
|
import 'package:qpay/screens/gateway/gateway_controller.dart';
|
||||||
import 'package:qpay/screens/transaction_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:qpay/screens/receipt/receipt_controller.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
import 'package:share_plus/share_plus.dart';
|
||||||
import 'package:skeletonizer/skeletonizer.dart';
|
import 'package:skeletonizer/skeletonizer.dart';
|
||||||
|
import 'package:timeago/timeago.dart' as timeago;
|
||||||
import 'package:widgets_to_image/widgets_to_image.dart';
|
import 'package:widgets_to_image/widgets_to_image.dart';
|
||||||
|
|
||||||
class ReceiptScreen extends StatefulWidget {
|
class ReceiptScreen extends StatefulWidget {
|
||||||
@@ -96,6 +98,11 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
receiptController.setLoading(true);
|
receiptController.setLoading(true);
|
||||||
await gatewayController.poll(receiptController.model.receiptData?["id"]);
|
await gatewayController.poll(receiptController.model.receiptData?["id"]);
|
||||||
if(gatewayController.model.status == "SUCCESS"){
|
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();
|
await _retry();
|
||||||
}
|
}
|
||||||
receiptController.setLoading(false);
|
receiptController.setLoading(false);
|
||||||
@@ -253,21 +260,22 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
CrossAxisAlignment.center,
|
CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: 5),
|
SizedBox(height: 5),
|
||||||
// receiptController
|
|
||||||
// .model
|
|
||||||
// .receiptData?["providerImage"] !=
|
|
||||||
// null
|
|
||||||
// ? Image.asset(
|
|
||||||
// 'assets/${receiptController.model.receiptData?["providerImage"] ?? ''}',
|
|
||||||
// width: 50,
|
|
||||||
// )
|
|
||||||
// : SizedBox.shrink(),
|
|
||||||
Text(
|
Text(
|
||||||
receiptController
|
receiptController
|
||||||
.model
|
.model
|
||||||
.receiptData?["billName"] ??
|
.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" &&
|
if(receiptController.model.receiptData?["integrationStatus"] != "SUCCESS" &&
|
||||||
receiptController.model.receiptData?["paymentStatus"] == "SUCCESS")
|
receiptController.model.receiptData?["paymentStatus"] == "SUCCESS")
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
Skeletonizer(
|
Skeletonizer(
|
||||||
enabled:
|
enabled:
|
||||||
receiptController
|
receiptController
|
||||||
@@ -624,7 +634,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
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(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
color: Theme.of(
|
color: Theme.of(
|
||||||
@@ -639,6 +649,8 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
SizedBox(height: 10,),
|
SizedBox(height: 10,),
|
||||||
|
|
||||||
Skeletonizer(
|
Skeletonizer(
|
||||||
@@ -767,7 +779,11 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
.model
|
.model
|
||||||
.receiptData?["additionalData"]
|
.receiptData?["additionalData"]
|
||||||
.map<Widget>((data) {
|
.map<Widget>((data) {
|
||||||
return Column(
|
return Skeletonizer(
|
||||||
|
enabled: receiptController
|
||||||
|
.model
|
||||||
|
.isLoading,
|
||||||
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment:
|
mainAxisAlignment:
|
||||||
@@ -800,6 +816,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
height: 10,
|
height: 10,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.toList(),
|
.toList(),
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ class TransactionModel {
|
|||||||
providerLabel: '',
|
providerLabel: '',
|
||||||
);
|
);
|
||||||
String paymentStatus = 'PENDING';
|
String paymentStatus = 'PENDING';
|
||||||
|
String pollStatus = 'PENDING';
|
||||||
String? errorMessage;
|
String? errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user