aligning logic to backend workflows
This commit is contained in:
@@ -79,7 +79,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
themeMode: ThemeMode.light, // Force light mode regardless of OS setting
|
themeMode: ThemeMode.light, // Force light mode regardless of OS setting
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
routerConfig: GoRouter(
|
routerConfig: GoRouter(
|
||||||
initialLocation: '/',
|
initialLocation: '/receipt',
|
||||||
routes: [
|
routes: [
|
||||||
ShellRoute(
|
ShellRoute(
|
||||||
builder: (context, state, child) {
|
builder: (context, state, child) {
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ class ConfirmController extends ChangeNotifier {
|
|||||||
model.isLoading = true;
|
model.isLoading = true;
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
|
||||||
|
// todo: find out if we still need to update these fields
|
||||||
transactionController
|
transactionController
|
||||||
.model
|
.model
|
||||||
.formData = transactionController.model.formData.copyWith(
|
.formData = transactionController.model.formData.copyWith(
|
||||||
@@ -66,11 +67,14 @@ class ConfirmController extends ChangeNotifier {
|
|||||||
authType: transactionController.model.selectedPaymentProcessor.authType,
|
authType: transactionController.model.selectedPaymentProcessor.authType,
|
||||||
);
|
);
|
||||||
|
|
||||||
dynamic response = await http.post(
|
dynamic workflowResponse = await http.get(
|
||||||
'/public/transaction/request',
|
'/public/transaction/request/'
|
||||||
transactionController.model.formData,
|
'${transactionController.model.confirmationData['id']}/'
|
||||||
|
'${transactionController.model.selectedPaymentProcessor.authType}'
|
||||||
);
|
);
|
||||||
logger.i(response.toString());
|
logger.i(workflowResponse.toString());
|
||||||
|
|
||||||
|
dynamic response = workflowResponse['body'];
|
||||||
|
|
||||||
if (response['status'] != 'FAILED') {
|
if (response['status'] != 'FAILED') {
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
@@ -133,4 +137,11 @@ class ConfirmController extends ChangeNotifier {
|
|||||||
await Future.delayed(const Duration(seconds: 5));
|
await Future.delayed(const Duration(seconds: 5));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// TODO: implement dispose
|
||||||
|
super.dispose();
|
||||||
|
model.isCancelled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,8 +58,11 @@ class GatewayController extends ChangeNotifier {
|
|||||||
if (model.isCancelled) break;
|
if (model.isCancelled) break;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
dynamic response = await http.get('/public/transaction/poll/$uid');
|
dynamic workflowResponse = await http.get('/public/transaction/poll/$uid');
|
||||||
logger.i(response.toString());
|
|
||||||
|
logger.i(workflowResponse.toString());
|
||||||
|
|
||||||
|
dynamic response = workflowResponse['body'];
|
||||||
|
|
||||||
if (response['status'] == 'SUCCESS') {
|
if (response['status'] == 'SUCCESS') {
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
|
|||||||
@@ -101,6 +101,10 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(prefs.getString("userId") == null) {
|
||||||
|
prefs.setString("userId", Uuid().v4());
|
||||||
|
};
|
||||||
|
|
||||||
await homeController.getTransactions(prefs.getString("userId")!);
|
await homeController.getTransactions(prefs.getString("userId")!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +201,7 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
actions: [
|
actions: [
|
||||||
if(_isLoggedIn)
|
if(_isLoggedIn)
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.exit_to_app_sharp),
|
icon: const Icon(Icons.turn_slight_right_sharp),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
@@ -525,16 +529,16 @@ class _HomeScreenState extends State<HomeScreen>
|
|||||||
),
|
),
|
||||||
SizedBox(width: 10),
|
SizedBox(width: 10),
|
||||||
Icon(
|
Icon(
|
||||||
e['status'] == 'SUCCESS'
|
e['integrationStatus'] == 'SUCCESS'
|
||||||
? Icons.check_circle
|
? Icons.check_circle
|
||||||
: e['status'] == 'PENDING'
|
: e['integrationStatus'] == 'PENDING'
|
||||||
? Icons.pending
|
? Icons.pending
|
||||||
: Icons.cancel,
|
: Icons.cancel,
|
||||||
size: 16,
|
size: 16,
|
||||||
color:
|
color:
|
||||||
e['status'] == 'SUCCESS'
|
e['integrationStatus'] == 'SUCCESS'
|
||||||
? Colors.green
|
? Colors.green
|
||||||
: e['status'] == 'PENDING'
|
: e['integrationStatus'] == 'PENDING'
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.red,
|
: Colors.red,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -111,11 +111,13 @@ class PayController extends ChangeNotifier {
|
|||||||
productUid: model.selectedProduct?.uid,
|
productUid: model.selectedProduct?.uid,
|
||||||
);
|
);
|
||||||
|
|
||||||
dynamic response = await http.post(
|
dynamic workflowResponse = await http.post(
|
||||||
'/public/transaction',
|
'/public/transaction',
|
||||||
transactionController.model.formData,
|
transactionController.model.formData,
|
||||||
);
|
);
|
||||||
logger.i(response.toString());
|
logger.i(workflowResponse.toString());
|
||||||
|
|
||||||
|
dynamic response = workflowResponse['body'];
|
||||||
|
|
||||||
if (response['status'] == 'SUCCESS') {
|
if (response['status'] == 'SUCCESS') {
|
||||||
model.status = response['status'];
|
model.status = response['status'];
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
|
|||||||
transactionController.model.formData.creditEmail ?? '';
|
transactionController.model.formData.creditEmail ?? '';
|
||||||
|
|
||||||
// Initialize phone number and country code
|
// Initialize phone number and country code
|
||||||
updatePhoneNumber(transactionController.model.formData.creditPhone ?? '');
|
// updatePhoneNumber(transactionController.model.formData.creditPhone ?? '');
|
||||||
|
|
||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
@@ -296,7 +296,7 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
'Notification Phone Number',
|
'Debit Phone Number',
|
||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.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:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:qpay/screens/receipt/receipt_controller.dart';
|
import 'package:qpay/screens/receipt/receipt_controller.dart';
|
||||||
@@ -17,6 +18,7 @@ class ReceiptScreen extends StatefulWidget {
|
|||||||
class _ReceiptScreenState extends State<ReceiptScreen>
|
class _ReceiptScreenState extends State<ReceiptScreen>
|
||||||
with TickerProviderStateMixin {
|
with TickerProviderStateMixin {
|
||||||
late TransactionController transactionController;
|
late TransactionController transactionController;
|
||||||
|
late GatewayController gatewayController;
|
||||||
late Animation<double> _fadeAnimation;
|
late Animation<double> _fadeAnimation;
|
||||||
late Animation<Offset> _slideAnimation;
|
late Animation<Offset> _slideAnimation;
|
||||||
late AnimationController _controller;
|
late AnimationController _controller;
|
||||||
@@ -34,7 +36,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
context,
|
context,
|
||||||
listen: false,
|
listen: false,
|
||||||
);
|
);
|
||||||
|
gatewayController = GatewayController(context);
|
||||||
receiptController = ReceiptController(context);
|
receiptController = ReceiptController(context);
|
||||||
|
|
||||||
receiptController.getReceiptData(
|
receiptController.getReceiptData(
|
||||||
@@ -81,6 +83,25 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_retry() async {
|
||||||
|
receiptController.setLoading(true);
|
||||||
|
await gatewayController.pollTransaction(receiptController.model.receiptData?["id"]);
|
||||||
|
if(gatewayController.model.status == "SUCCESS"){
|
||||||
|
receiptController.getReceiptData(transactionController.model.receiptData?["id"]);
|
||||||
|
}
|
||||||
|
receiptController.setLoading(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// TODO: implement dispose
|
||||||
|
super.dispose();
|
||||||
|
_controller.dispose();
|
||||||
|
_tabController.dispose();
|
||||||
|
gatewayController.dispose();
|
||||||
|
receiptController.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -174,12 +195,12 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
Icon(
|
Icon(
|
||||||
transactionController
|
transactionController
|
||||||
.model
|
.model
|
||||||
.receiptData?["status"] ==
|
.receiptData?["integrationStatus"] ==
|
||||||
'SUCCESS'
|
'SUCCESS'
|
||||||
? Icons.check_circle
|
? Icons.check_circle
|
||||||
: transactionController
|
: transactionController
|
||||||
.model
|
.model
|
||||||
.receiptData?["status"] ==
|
.receiptData?["integrationStatus"] ==
|
||||||
'PENDING'
|
'PENDING'
|
||||||
? Icons.pending
|
? Icons.pending
|
||||||
: Icons.cancel,
|
: Icons.cancel,
|
||||||
@@ -187,12 +208,12 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
color:
|
color:
|
||||||
transactionController
|
transactionController
|
||||||
.model
|
.model
|
||||||
.receiptData?["status"] ==
|
.receiptData?["integrationStatus"] ==
|
||||||
'SUCCESS'
|
'SUCCESS'
|
||||||
? Colors.green
|
? Colors.green
|
||||||
: transactionController
|
: transactionController
|
||||||
.model
|
.model
|
||||||
.receiptData?["status"] ==
|
.receiptData?["integrationStatus"] ==
|
||||||
'PENDING'
|
'PENDING'
|
||||||
? Colors.orange
|
? Colors.orange
|
||||||
: Colors.red,
|
: Colors.red,
|
||||||
@@ -338,9 +359,9 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.share,
|
Icons.share,
|
||||||
color:
|
color:
|
||||||
Theme.of(
|
Theme.of(
|
||||||
context,
|
context,
|
||||||
).colorScheme.primary,
|
).colorScheme.primary,
|
||||||
size: 24,
|
size: 24,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -355,6 +376,48 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
if(receiptController.model.receiptData?["integrationStatus"] == "PENDING" &&
|
||||||
|
receiptController.model.receiptData?["paymentStatus"] == "SUCCESS")
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
width: 50,
|
||||||
|
height: 50,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Colors.white,
|
||||||
|
border: Border.all(
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.primary
|
||||||
|
.withOpacity(0.3),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_retry();
|
||||||
|
},
|
||||||
|
icon: Icon(
|
||||||
|
Icons.redo,
|
||||||
|
color:
|
||||||
|
Theme.of(
|
||||||
|
context,
|
||||||
|
).colorScheme.primary,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
"Retry",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@@ -437,7 +500,7 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
Text(
|
Text(
|
||||||
transactionController
|
transactionController
|
||||||
.model
|
.model
|
||||||
.receiptData?["status"] ??
|
.receiptData?["integrationStatus"] ??
|
||||||
"",
|
"",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
@@ -446,6 +509,49 @@ class _ReceiptScreenState extends State<ReceiptScreen>
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if(transactionController
|
||||||
|
.model
|
||||||
|
.receiptData?["errorMessage"] != null &&
|
||||||
|
transactionController.model.receiptData?["errorMessage"] != "")
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Skeletonizer(
|
||||||
|
enabled:
|
||||||
|
receiptController
|
||||||
|
.model
|
||||||
|
.isLoading,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
MainAxisAlignment
|
||||||
|
.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"Message",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight:
|
||||||
|
FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
transactionController
|
||||||
|
.model
|
||||||
|
.receiptData?["errorMessage"] ??
|
||||||
|
"",
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
|
softWrap: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
Skeletonizer(
|
Skeletonizer(
|
||||||
enabled:
|
enabled:
|
||||||
|
|||||||
Reference in New Issue
Block a user