Files
velocity-pay-flutter/lib/screens/gateway/gateway_web_screen.dart

178 lines
5.9 KiB
Dart

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<GatewayWebScreen> createState() => _GatewayWebScreenState();
}
class _GatewayWebScreenState extends State<GatewayWebScreen> {
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'),
),
],
),
),
),
);
},
),
);
},
),
);
}
}