From 28312a498944118086fbfd6373f1f1d7720bb6da Mon Sep 17 00:00:00 2001 From: Vusumuzi Khoza Date: Wed, 26 Nov 2025 14:58:16 +0200 Subject: [PATCH] move from embedded to redirect --- lib/main.dart | 7 +- lib/screens/confirm/confirm_controller.dart | 6 +- lib/screens/gateway/gateway_redirect.dart | 136 ++++++++++++++++++++ 3 files changed, 146 insertions(+), 3 deletions(-) create mode 100644 lib/screens/gateway/gateway_redirect.dart diff --git a/lib/main.dart b/lib/main.dart index e089fb2..25a751a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'package:provider/provider.dart'; import 'package:go_router/go_router.dart'; import 'package:qpay/navbar.dart'; import 'package:qpay/screens/confirm/confirm_screen.dart'; +import 'package:qpay/screens/gateway/gateway_redirect.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'; @@ -83,7 +84,7 @@ class _MyAppState extends State { themeMode: ThemeMode.light, // Force light mode regardless of OS setting debugShowCheckedModeBanner: false, routerConfig: GoRouter( - initialLocation: '/', + initialLocation: '/gateway-redirect', routes: [ ShellRoute( builder: (context, state, child) { @@ -119,6 +120,10 @@ class _MyAppState extends State { path: '/gateway-web', builder: (context, state) => const GatewayWebScreen(), ), + GoRoute( + path: '/gateway-redirect', + builder: (context, state) => const GatewayRedirectScreen(), + ), GoRoute( path: '/poll', builder: (context, state) => const PollScreen(), diff --git a/lib/screens/confirm/confirm_controller.dart b/lib/screens/confirm/confirm_controller.dart index c579f7d..517146b 100644 --- a/lib/screens/confirm/confirm_controller.dart +++ b/lib/screens/confirm/confirm_controller.dart @@ -85,14 +85,16 @@ class ConfirmController extends ChangeNotifier { if (transactionController.model.selectedPaymentProcessor.authType == "WEB") { if (context.mounted) { if(kIsWeb){ - context.push('/gateway-web'); + // the one we use here depends on when mpgs embedded bug will be fixed + // context.push('/gateway-web'); + context.push('/gateway-redirect'); }else { context.push('/gateway'); } } } else { await pollTransaction(transactionController.model.receiptData?['id']); - context.push('/integration'); + context.push('/poll'); } } else { model.status = response['status']; diff --git a/lib/screens/gateway/gateway_redirect.dart b/lib/screens/gateway/gateway_redirect.dart new file mode 100644 index 0000000..35c9a14 --- /dev/null +++ b/lib/screens/gateway/gateway_redirect.dart @@ -0,0 +1,136 @@ +import 'dart:js_interop'; + +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:go_router/go_router.dart'; +import 'package:qpay/interop.dart'; +import 'package:qpay/models/responsive_policy.dart'; +import 'package:qpay/screens/gateway/gateway_controller.dart'; +import 'package:skeletonizer/skeletonizer.dart'; +import 'package:url_launcher/url_launcher.dart'; +import 'package:webview_flutter/webview_flutter.dart'; +import 'package:web/web.dart' as web; + +class GatewayRedirectScreen extends StatefulWidget { + const GatewayRedirectScreen({super.key}); + + @override + State createState() => _GatewayRedirectScreenState(); +} + +class _GatewayRedirectScreenState extends State { + late GatewayController controller; + late String url; + String? sessionID; + bool redirected = false; + + bool integrationStarted = false; + + @override + void initState() { + super.initState(); + controller = GatewayController(context); + + url = controller.transactionController.model.receiptData?['targetUrl']; + } + + void updateRedirect(){ + setState(() { + redirected = true; + }); + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Complete your payment'), + leading: context.canPop() + ? IconButton( + onPressed: () { + context.pop(); + }, + icon: const Icon(Icons.arrow_back), + ) + : SizedBox.shrink(), + ), + body: ListenableBuilder( + listenable: controller, + builder: (context, child) { + if (controller.model.status == 'SUCCESS') { + WidgetsBinding.instance.addPostFrameCallback((_) { + context.go('/integration'); + }); + } + + return Container( + padding: EdgeInsets.all(50), + child: Center( + child: LayoutBuilder( + builder: (context, constraints) { + return SizedBox( + width: double.parse(constraints.maxWidth > ResponsivePolicy.md ? + ResponsivePolicy.md.toString() : + constraints.maxWidth.toString()), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox(height: 75), + + Column( + children: [ + Text( + 'You are about to be redirected to an external gateway to complete your payment. ' + 'Once complete you can close the gateway tab and return to this one to check your ' + 'transaction status.', + style: TextStyle(fontSize: 20), + textAlign: TextAlign.center, + ), + SizedBox(height: 50,), + Image.asset('assets/velocity-animation.gif', width: 150), + SizedBox(height: 50), + + if(!redirected) + OutlinedButton( + child: Text('Redirect'), + onPressed: () async { + updateRedirect(); + final Uri uri = Uri.parse(url); + if (!await launchUrl( + uri, + mode: LaunchMode.platformDefault, // Default behavior, usually opens in new tab on web + webOnlyWindowName: '_blank', // Specifically for web to open in a new tab + )) { + throw Exception('Could not launch $url'); + } + }, + ), + + if(redirected) + OutlinedButton( + child: Text('Check Status'), + onPressed: () { + context.go("/poll"); + }, + ) + ], + ), + ], + ), + ); + } + ), + ), + ); + }, + ), + ); + } +}