import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:go_router/go_router.dart'; import 'package:qpay/models/responsive_policy.dart'; import 'package:qpay/screens/gateway/gateway_controller.dart'; import 'package:web/web.dart' as web; class GatewayWebScreen extends StatefulWidget { const GatewayWebScreen({super.key}); @override State createState() => _GatewayWebScreenState(); } class _GatewayWebScreenState extends State { late GatewayController controller; late String url; bool integrationStarted = false; bool redirecting = false; @override void initState() { super.initState(); controller = GatewayController(context); url = controller.transactionController.model.receiptData?['targetUrl']; } void _redirectToGateway() { if (redirecting) return; setState(() { redirecting = true; }); bool simulate = bool.parse( dotenv.env['SIMULATE_PAYMENT_SUCCESS'] ?? 'false', ); if (simulate) { // In simulation mode, just go to polling WidgetsBinding.instance.addPostFrameCallback((_) { context.go('/poll'); }); return; } // Save the transaction ID to localStorage so index.html can redirect // the user back to /poll/:id when the gateway returns to this app. final transactionId = controller.transactionController.model.confirmationData['id']; if (transactionId != null) { web.window.localStorage.setItem( 'pendingTransactionId', transactionId.toString(), ); } // Navigate the current browser tab to the payment gateway URL. // After payment, the gateway will redirect back to this app's host. // index.html then reads the saved transaction ID and navigates to // /poll/:id so the app can immediately start polling for the result. web.window.location.href = url; } @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), ) : const SizedBox.shrink(), ), body: ListenableBuilder( listenable: controller, builder: (context, child) { return Center( child: LayoutBuilder( builder: (context, constraints) { return SizedBox( width: constraints.maxWidth > ResponsivePolicy.md ? ResponsivePolicy.md.toDouble() : constraints.maxWidth.toDouble(), child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(20.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 40), // Info icon Icon( Icons.lock_outline, size: 64, color: Theme.of(context).colorScheme.primary, ), const SizedBox(height: 24), Text( 'You are about to leave this app to complete your payment on a secure payment gateway.', style: const TextStyle(fontSize: 18), textAlign: TextAlign.center, ), const SizedBox(height: 16), Text( 'After completing payment, you will be automatically redirected back to this app to see your transaction status.', style: TextStyle( fontSize: 14, color: Colors.grey[600], ), textAlign: TextAlign.center, ), const SizedBox(height: 40), Image.asset( 'assets/velocity-animation.gif', width: 150, ), const SizedBox(height: 40), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: redirecting ? null : _redirectToGateway, style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric( vertical: 16, ), ), child: Text( redirecting ? 'Redirecting...' : 'Proceed to Payment Gateway', style: const TextStyle(fontSize: 16), ), ), ), const SizedBox(height: 16), TextButton( onPressed: () { context.go('/poll'); }, child: const Text('Check transaction status'), ), ], ), ), ), ); }, ), ); }, ), ); } }