updating sequence of flows
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -60,15 +60,10 @@ class GatewayController extends ChangeNotifier {
|
||||
dynamic response = workflowResponse['body'];
|
||||
|
||||
model.status = response['status'];
|
||||
transactionController.model.paymentStatus = response['status'];
|
||||
transactionController.updateReceiptData(response);
|
||||
transactionController.model.paymentStatus = response['paymentStatus'];
|
||||
|
||||
if(model.status == 'PENDING') {
|
||||
_showErrorSnackBar('Transaction status still pending');
|
||||
}
|
||||
|
||||
if(model.status != 'FAILED') {
|
||||
_showErrorSnackBar(response['errorMessage'].isEmpty ? 'Transaction failed' : response['errorMessage']);
|
||||
if(model.status == 'SUCCESS') {
|
||||
transactionController.updateReceiptData(response);
|
||||
}
|
||||
notifyListeners();
|
||||
} catch (e) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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'];
|
||||
|
||||
@@ -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,91 +57,64 @@ class _IntegrationScreenState extends State<IntegrationScreen> {
|
||||
title: const Text('Completing your transaction'),
|
||||
),
|
||||
body: ListenableBuilder(
|
||||
listenable: gatewayController,
|
||||
listenable: controller,
|
||||
builder: (context, child) {
|
||||
return ListenableBuilder(
|
||||
listenable: controller,
|
||||
builder: (context, child) {
|
||||
if (controller.model.status == 'SUCCESS') {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.go('/receipt');
|
||||
});
|
||||
}
|
||||
if (controller.model.status == 'SUCCESS') {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
context.go('/receipt');
|
||||
});
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: EdgeInsets.all(50),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
return Container(
|
||||
padding: EdgeInsets.all(50),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(height: 75),
|
||||
|
||||
if(transactionController.model.paymentStatus != 'SUCCESS')
|
||||
Text(
|
||||
'We\'ve successfully collected your funds. We are now '
|
||||
'updating your billing account. This won\'t take long',
|
||||
style: TextStyle(fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Image.asset('assets/peak-animation.gif', width: 150),
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: LinearProgressIndicator(),
|
||||
),
|
||||
SizedBox(height: 50),
|
||||
if(controller.model.status == 'FAILED')
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'We failed to check your payment status',
|
||||
'We failed to update your billing account',
|
||||
style: TextStyle(fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 50,),
|
||||
SizedBox(height: 20,),
|
||||
Skeletonizer(
|
||||
enabled: gatewayController.model.isLoading,
|
||||
enabled: controller.model.isLoading,
|
||||
child: OutlinedButton(
|
||||
child: Text('Check latest status'),
|
||||
child: Text('Retry'),
|
||||
onPressed: () {
|
||||
gatewayController.poll(transactionController.model.receiptData?['id']);
|
||||
controller.doIntegration();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
if(transactionController.model.paymentStatus == 'SUCCESS')
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'We\'ve successfully collected your funds. We are now '
|
||||
'updating your billing account. This won\'t take long',
|
||||
style: TextStyle(fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
Image.asset('assets/peak-animation.gif', width: 150),
|
||||
SizedBox(
|
||||
width: 50,
|
||||
child: LinearProgressIndicator(),
|
||||
),
|
||||
SizedBox(height: 50),
|
||||
if(controller.model.status == 'FAILED')
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
'We failed to update your billing account',
|
||||
style: TextStyle(fontSize: 20),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
SizedBox(height: 20,),
|
||||
Skeletonizer(
|
||||
enabled: controller.model.isLoading,
|
||||
child: OutlinedButton(
|
||||
child: Text('Retry'),
|
||||
onPressed: () {
|
||||
controller.doIntegration();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,43 +610,47 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
if(receiptController.model.receiptData?["integrationStatus"] != "SUCCESS" &&
|
||||
receiptController.model.receiptData?["paymentStatus"] == "SUCCESS")
|
||||
Skeletonizer(
|
||||
enabled:
|
||||
receiptController
|
||||
.model
|
||||
.isLoading,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Additional action",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Text("Billing account update failed, please tap on retry to try again",
|
||||
Column(
|
||||
children: [
|
||||
const SizedBox(height: 10),
|
||||
Skeletonizer(
|
||||
enabled:
|
||||
receiptController
|
||||
.model
|
||||
.isLoading,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Additional action",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary,
|
||||
fontWeight:
|
||||
FontWeight.bold,
|
||||
),
|
||||
textAlign: TextAlign.end,
|
||||
softWrap: true,
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: Text("Billing account update failed, please tap on Retry to try again",
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.primary,
|
||||
),
|
||||
textAlign: TextAlign.end,
|
||||
softWrap: true,
|
||||
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 10,),
|
||||
|
||||
Skeletonizer(
|
||||
@@ -767,39 +779,44 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
||||
.model
|
||||
.receiptData?["additionalData"]
|
||||
.map<Widget>((data) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
data["name"] ??
|
||||
"",
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
16,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.bold,
|
||||
return Skeletonizer(
|
||||
enabled: receiptController
|
||||
.model
|
||||
.isLoading,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment
|
||||
.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
data["name"] ??
|
||||
"",
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
16,
|
||||
fontWeight:
|
||||
FontWeight
|
||||
.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
data["value"] ??
|
||||
"",
|
||||
style:
|
||||
TextStyle(
|
||||
fontSize:
|
||||
16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
Text(
|
||||
data["value"] ??
|
||||
"",
|
||||
style:
|
||||
TextStyle(
|
||||
fontSize:
|
||||
16,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
})
|
||||
.toList(),
|
||||
|
||||
@@ -41,6 +41,7 @@ class TransactionModel {
|
||||
providerLabel: '',
|
||||
);
|
||||
String paymentStatus = 'PENDING';
|
||||
String pollStatus = 'PENDING';
|
||||
String? errorMessage;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user