Files

362 lines
11 KiB
Dart

import 'dart:convert';
import 'dart:io';
import 'package:file_saver/file_saver.dart';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:initial_folder/base_service.dart';
import 'package:initial_folder/providers/order_provider.dart';
import 'package:initial_folder/screens/profile/account_sign_in/riwayat_transaksi.dart';
import 'package:initial_folder/screens/profile/account_sign_in/riwayat_transaksi_pending.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:initial_folder/theme.dart';
import 'package:url_launcher/url_launcher.dart'; // Import url_launcher
import 'success_paid_course.dart';
class SnapPaymentPage extends StatefulWidget {
final String transactionToken;
final String orderId;
final int grossAmount;
final String courseTitle;
final String courseThumbnail;
final String courseInstructor;
final String courseId;
SnapPaymentPage({
required this.transactionToken,
required this.orderId,
required this.grossAmount,
required this.courseTitle,
required this.courseThumbnail,
required this.courseInstructor,
required this.courseId,
});
@override
_SnapPaymentPageState createState() => _SnapPaymentPageState();
}
class _SnapPaymentPageState extends State<SnapPaymentPage> {
// Controller untuk WebView
late WebViewController _controller;
double _progress = 0; // Menyimpan progress loading WebView
bool _isLoading = true; // Status loading halaman
String? baseUrlmidtrans = dotenv.env['BASE_URL_MIDTRANS']; // Base URL Midtrans
@override
void initState() {
super.initState();
// Inisialisasi WebViewController dan pengaturan event handler
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: _onProgress, // Handler progress loading
onPageStarted: _onPageStarted, // Handler saat halaman mulai dimuat
onPageFinished: _onPageFinished, // Handler saat halaman selesai dimuat
onWebResourceError: _onWebResourceError, // Handler error resource
onNavigationRequest: _onNavigationRequest, // Handler navigasi
),
)
..addJavaScriptChannel(
'BlobDataChannel',
// Handler pesan dari JavaScript untuk download QRIS
onMessageReceived: (JavaScriptMessage message) async {
if (message.message.startsWith('error:')) {
// Jika error saat fetch blob
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Download Sedang Tidak Tersedia'),
content: Text(
'Silahkan screenshot Qris tersebut untuk menyimpan ke perangkat kamu.'),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
),
);
// ScaffoldMessenger.of(context).showSnackBar(
// SnackBar(content: Text('Gagal mengunduh QRIS. Silakan coba lagi.')),
// );
} else {
// Jika berhasil, proses data blob
await _handleBlobData(message.message);
}
},
)
// Memuat halaman pembayaran Midtrans
..loadRequest(Uri.parse(
"$baseUrlmidtrans/snap/v2/vtweb/${widget.transactionToken}",
));
}
// Handler progress loading WebView
void _onProgress(int progressValue) {
setState(() {
_progress = progressValue / 100;
});
}
// Handler saat halaman mulai dimuat
void _onPageStarted(String url) {
setState(() {
_isLoading = true;
});
}
// Handler saat halaman selesai dimuat
void _onPageFinished(String url) {
setState(() {
_isLoading = false;
_progress = 0;
});
}
// Handler jika terjadi error pada resource WebView
void _onWebResourceError(WebResourceError error) {
setState(() {
_isLoading = false;
});
_showErrorDialog(error.description ?? 'Unknown Error');
}
// Handler navigasi pada WebView
NavigationDecision _onNavigationRequest(NavigationRequest request) {
// Redirect ke aplikasi pembayaran jika diperlukan
if (request.url.startsWith('https://gopay.co.id/') ||
request.url.startsWith('https://app.shopeepay.co.id/')) {
print('Redirecting to GoPay/GoJek app');
_launchURL(request.url);
return NavigationDecision.prevent;
}
// Jika link blob (download QRIS), jalankan fetch blob
if (request.url.startsWith('blob:')) {
print('Redirecting download qris');
_fetchBlobData(baseUrlmidtrans ?? "https://app.sandbox.midtrans.com",
widget.transactionToken);
return NavigationDecision.prevent;
}
// Redirect ke halaman riwayat transaksi jika status pending
if (request.url.contains('transaction_status=pending') ||
request.url.contains('https://vocasia.id/')) {
Navigator.of(context)
.pushReplacementNamed(RiwayatTransaksiPending.routeName);
return NavigationDecision.prevent;
}
// Jika pembayaran sukses, tambahkan order dan redirect ke halaman sukses
if (request.url.contains('transaction_status=settlement') ||
request.url.contains('transaction_status=capture')) {
var orderProvider = Provider.of<OrderProvider>(context, listen: false);
orderProvider.addOrder(
id: widget.courseId,
title: widget.courseTitle,
price: widget.grossAmount.toString(),
imageUrl: widget.courseThumbnail,
instructor: widget.courseInstructor,
discountPrice: '',
);
Navigator.of(context).pushReplacementNamed(
SuccessPaidCourse.routeName,
);
return NavigationDecision.prevent;
}
// Jika user menekan tombol kembali di halaman pembayaran
if (request.url.contains('action=back')) {
Navigator.of(context).pop();
return NavigationDecision.prevent;
}
// Default: lanjutkan navigasi
return NavigationDecision.navigate;
}
// Menjalankan JavaScript untuk mengambil data blob QRIS dari WebView
Future<void> _fetchBlobData(String baseUrl, String transactionId) async {
print('Fetching transaction URL: $transactionId');
final realUrl =
baseUrl + "/snap/v1/transactions/" + transactionId + "/qr-code";
final script = '''
(async function() {
try {
const response = await fetch('$baseUrl')
const blob = await response.blob();
const reader = new FileReader();
reader.onloadend = function() {
const base64data = reader.result.split(',')[1];
BlobDataChannel.postMessage(base64data);
};
reader.readAsDataURL(blob);
} catch (error) {
console.error('Failed to fetch blob:', error);
BlobDataChannel.postMessage('error: ' + error.message);
}
})();
''';
_controller.runJavaScript(script);
}
// Menyimpan data QRIS hasil download ke file lokal
Future<void> _handleBlobData(String base64Data) async {
try {
final decodedBytes = base64Decode(base64Data);
final directory = await getApplicationDocumentsDirectory();
final path = directory.path;
final file = File('$path/qris_download.png');
await file.writeAsBytes(decodedBytes);
await FileSaver.instance.saveAs(
name: 'qris_download',
ext: 'png',
mimeType: MimeType.png,
file: file,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('QRIS berhasil diunduh')),
);
} catch (e) {
_showErrorDialog('Gagal mengunduh QRIS: $e');
}
}
// Menampilkan dialog error
void _showErrorDialog(String errorMessage) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Error'),
content: Text(errorMessage),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'),
),
],
),
);
}
// Membuka aplikasi eksternal (misal: GoPay/ShopeePay)
Future<void> _launchURL(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
_showErrorDialog('Could not open the app');
}
}
// Handler ketika user menekan tombol back (hardware/software)
Future<bool> _onWillPop() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Pembayaran Belum Selesai'),
content: Text('Apakah Anda yakin ingin keluar dari halaman ini?'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Batal'),
),
TextButton(
onPressed: () {
Navigator.of(context).pop(true);
Navigator.of(context)
.pushReplacementNamed(RiwayatTransaksi.routeName);
},
child: Text('Keluar'),
),
],
),
)) ??
false;
}
// Melakukan reload halaman pembayaran
void _refreshPage() {
_controller.reload();
}
@override
Widget build(BuildContext context) {
// Widget utama halaman pembayaran
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
appBar: PreferredSize(
preferredSize: Size.fromHeight(kToolbarHeight),
child: AppBar(
elevation: 5,
title: Center(
child: Text(
'Pembayaran',
style: primaryTextStyle.copyWith(
color: Colors.white,
),
),
),
backgroundColor: primaryColor,
iconTheme: IconThemeData(color: Colors.white),
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () async {
bool shouldExit = await _onWillPop();
if (shouldExit) {
Navigator.of(context)
.pushReplacementNamed(RiwayatTransaksi.routeName);
}
},
),
actions: [
IconButton(
icon: Icon(Icons.refresh),
onPressed: _refreshPage,
),
],
),
),
body: Stack(
children: [
// Widget WebView untuk menampilkan halaman pembayaran
RefreshIndicator(
onRefresh: () async {
_refreshPage();
},
child: WebViewWidget(controller: _controller),
),
// Progress bar saat loading
if (_isLoading)
LinearProgressIndicator(
value: _progress,
color: sevenColor,
backgroundColor: secondaryColor,
),
// Spinner loading di tengah layar
if (_isLoading)
Center(
child: CircularProgressIndicator(),
),
],
),
),
);
}
}