fixes on onboarding flow

This commit is contained in:
2025-10-27 23:19:46 +02:00
parent 4375a869ed
commit bcc9d629c1
10 changed files with 188 additions and 7 deletions

View File

@@ -1,5 +1,5 @@
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

BIN
assets/peak-animation.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

View File

@@ -3,6 +3,7 @@ import 'package:provider/provider.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:qpay/screens/confirm/confirm_screen.dart'; import 'package:qpay/screens/confirm/confirm_screen.dart';
import 'package:qpay/screens/gateway/gateway_screen.dart'; import 'package:qpay/screens/gateway/gateway_screen.dart';
import 'package:qpay/screens/integration/integration_screen.dart';
import 'package:qpay/screens/onboarding/complete_screen.dart'; import 'package:qpay/screens/onboarding/complete_screen.dart';
import 'package:qpay/screens/onboarding/landing/landing_screen.dart'; import 'package:qpay/screens/onboarding/landing/landing_screen.dart';
import 'package:qpay/screens/onboarding/login/login_screen.dart'; import 'package:qpay/screens/onboarding/login/login_screen.dart';
@@ -117,6 +118,10 @@ class _MyAppState extends State<MyApp> {
path: '/receipt', path: '/receipt',
builder: (context, state) => const ReceiptScreen(), builder: (context, state) => const ReceiptScreen(),
), ),
GoRoute(
path: '/integration',
builder: (context, state) => const IntegrationScreen(),
),
GoRoute( GoRoute(
path: '/recipients', path: '/recipients',
builder: (context, state) => const RecipientsScreen(), builder: (context, state) => const RecipientsScreen(),

View File

@@ -73,7 +73,7 @@ class _GatewayScreenState extends State<GatewayScreen> {
builder: (context, child) { builder: (context, child) {
if (controller.model.status == 'SUCCESS') { if (controller.model.status == 'SUCCESS') {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
context.go('/receipt'); context.go('/integration');
}); });
} }
return controller.model.isLoading return controller.model.isLoading

View File

@@ -103,7 +103,7 @@ class _HomeScreenState extends State<HomeScreen>
if(prefs.getString("userId") == null) { if(prefs.getString("userId") == null) {
prefs.setString("userId", Uuid().v4()); prefs.setString("userId", Uuid().v4());
}; }
await homeController.getTransactions(prefs.getString("userId")!); await homeController.getTransactions(prefs.getString("userId")!);
} }

View File

@@ -0,0 +1,75 @@
import 'package:flutter/cupertino.dart';
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/transaction_controller.dart';
import 'package:shared_preferences/shared_preferences.dart';
class IntegrationModel {
Map<String, dynamic>? transaction;
String status = '';
bool isLoading = false;
String? errorMessage;
}
class IntegrationController extends ChangeNotifier {
final IntegrationModel model = IntegrationModel();
final Http http = Http();
late TransactionController transactionController;
late SharedPreferences prefs;
BuildContext context;
IntegrationController(this.context) {
transactionController = Provider.of<TransactionController>(
context,
listen: false,
);
}
void _showErrorSnackBar(String? message) {
WidgetsBinding.instance.addPostFrameCallback((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message.toString()),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.deepOrange,
),
);
});
}
Future<void> doIntegration() async {
try {
model.isLoading = true;
model.status = '';
notifyListeners();
dynamic workflowResponse = await http.get(
'/public/transaction/integration/'
'${transactionController.model.confirmationData['id']}'
);
logger.i(workflowResponse.toString());
dynamic response = workflowResponse['body'];
if (response['status'] == 'SUCCESS') {
model.status = response['status'];
transactionController.updateReceiptData(response);
} else {
model.status = response['status'];
model.errorMessage = response['errorMessage'];
_showErrorSnackBar(response['errorMessage']);
}
notifyListeners();
} catch (e) {
model.isLoading = false;
model.errorMessage = e.toString();
notifyListeners();
}
}
}

View File

@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'integration_controller.dart';
class IntegrationScreen extends StatefulWidget {
const IntegrationScreen({super.key});
@override
State<IntegrationScreen> createState() => _IntegrationScreenState();
}
class _IntegrationScreenState extends State<IntegrationScreen> {
late IntegrationController controller;
@override
void initState() {
super.initState();
controller = IntegrationController(context);
controller.doIntegration();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Completing your transaction'),
),
body: ListenableBuilder(
listenable: controller,
builder: (context, child) {
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,
children: [
SizedBox(height: 75),
SizedBox(height: 5),
Text(
'We\'ve successfully collected your funds',
style: TextStyle(fontSize: 22),
textAlign: TextAlign.center,
),
Image.asset('assets/peak-animation.gif', width: 150),
SizedBox(
width: 50,
child: LinearProgressIndicator(),
),
SizedBox(height: 50),
Text(
'We are now updating your billing account',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
Text(
'This won\'t take long',
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
SizedBox(height: 30,),
if(controller.model.status == 'FAILED')
OutlinedButton(
child: Text('Retry'),
onPressed: () {
controller.doIntegration();
},
)
],
),
),
);
},
),
);
}
}

View File

@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:qpay/abstracts/AbstractController.dart'; import 'package:qpay/abstracts/AbstractController.dart';
import 'package:qpay/http/http.dart'; import 'package:qpay/http/http.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../onboarding_controller.dart'; import '../onboarding_controller.dart';
@@ -19,6 +20,8 @@ class PhoneController extends AbstractController {
BuildContext context; BuildContext context;
late OnboardingController onboardingController; late OnboardingController onboardingController;
late SharedPreferences prefs;
final Http http = Http(); final Http http = Http();
PhoneController(this.context) { PhoneController(this.context) {
@@ -33,7 +36,10 @@ class PhoneController extends AbstractController {
notifyListeners(); notifyListeners();
} }
Future<void> register() async { Future<void> register() async {
prefs = await SharedPreferences.getInstance();
Map user = { Map user = {
"username": onboardingController.model.googleUser?.email, "username": onboardingController.model.googleUser?.email,
"email": onboardingController.model.googleUser?.email, "email": onboardingController.model.googleUser?.email,
@@ -41,7 +47,8 @@ class PhoneController extends AbstractController {
"lastName": onboardingController.model.googleUser?.displayName?.split(" ").last, "lastName": onboardingController.model.googleUser?.displayName?.split(" ").last,
"password": onboardingController.model.googleUser?.id, "password": onboardingController.model.googleUser?.id,
"photoUrl": onboardingController.model.googleUser?.photoUrl, "photoUrl": onboardingController.model.googleUser?.photoUrl,
"phone": onboardingController.model.phone "phone": onboardingController.model.phone,
"tempUid": prefs.get("userId")
}; };
try { try {

View File

@@ -32,7 +32,7 @@ class VerifyController extends AbstractController {
Future<void> verifyOtp () async { Future<void> verifyOtp () async {
Map user = { Map user = {
"username": onboardingController.model.phone, "username": onboardingController.model.googleUser?.email,
"otpCode": model.code, "otpCode": model.code,
"otpType": "REGISTRATION", "otpType": "REGISTRATION",
"workflowId": onboardingController.model.workflowId, "workflowId": onboardingController.model.workflowId,
@@ -64,7 +64,7 @@ class VerifyController extends AbstractController {
Future<void> resendOtp () async { Future<void> resendOtp () async {
Map user = { Map user = {
"username": onboardingController.model.phone, "username": onboardingController.model.googleUser?.email,
"phone": onboardingController.model.phone, "phone": onboardingController.model.phone,
"workflowId": onboardingController.model.workflowId, "workflowId": onboardingController.model.workflowId,
}; };

View File

@@ -71,6 +71,9 @@ class _PayScreenState extends State<PayScreen> with TickerProviderStateMixin {
transactionController.model.formData.creditName ?? ''; transactionController.model.formData.creditName ?? '';
_emailController.text = _emailController.text =
transactionController.model.formData.creditEmail ?? ''; transactionController.model.formData.creditEmail ?? '';
_amountController.text =
transactionController.model.formData.amount;
// Initialize phone number and country code // Initialize phone number and country code
// updatePhoneNumber(transactionController.model.formData.creditPhone ?? ''); // updatePhoneNumber(transactionController.model.formData.creditPhone ?? '');