import 'dart:async'; import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_countdown_timer/current_remaining_time.dart'; import 'package:flutter_countdown_timer/flutter_countdown_timer.dart'; import 'package:initial_folder/base_service.dart'; import 'package:initial_folder/helper/user_info.dart'; import 'package:initial_folder/models/detail_order_model.dart'; import 'package:initial_folder/models/history_transaction_model.dart'; import 'package:initial_folder/providers/cart_provider.dart'; import 'package:initial_folder/providers/carts_provider.dart'; import 'package:initial_folder/providers/order_provider.dart'; import 'package:initial_folder/providers/page_provider.dart'; import 'package:initial_folder/screens/checkout/components/bar_batas_bayar.dart'; import 'package:initial_folder/screens/checkout/components/bottom_sheet_detail.dart'; import 'package:initial_folder/screens/checkout/components/tab_bar_batas_bayar.dart'; import 'package:initial_folder/screens/home/home_screen.dart'; import 'package:initial_folder/services/cancel_payment_service.dart'; import 'package:initial_folder/size_config.dart'; import 'package:initial_folder/theme.dart'; import 'package:initial_folder/widgets/custom_navigator_pop.dart'; import 'package:initial_folder/widgets/login_regist/default_button_payment.dart'; import 'package:intl/intl.dart'; import 'package:provider/provider.dart'; import 'package:pusher_client/pusher_client.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:shimmer/shimmer.dart'; import 'package:initial_folder/screens/checkout/success_paid_course.dart'; import 'package:initial_folder/providers/payments_provider.dart' as payProv; class BatasBayarBank extends StatefulWidget { BatasBayarBank({ this.historyTransactionModel, this.isFromHistory, this.idCart, }); final HistoryTransactionModel? historyTransactionModel; final bool? isFromHistory; final List? idCart; @override State createState() => _BatasBayarBankState(); } class _BatasBayarBankState extends State { Channel? _channel; String? statusTransaction; String? statusMessage; bool isLoading = false; PusherClient? pusher; Duration? remainingTime; String capitalize(String s) => s[0].toUpperCase() + s.substring(1).toLowerCase(); Future saveRemainingTime(Duration remainingTime) async { final prefs = await SharedPreferences.getInstance(); prefs.setInt('remainingTime', remainingTime.inSeconds); } Future deleteCourse() async { List idCarts = widget.idCart!; for (var element in idCarts) { await Provider.of(context, listen: false) .deleteCart(element); await Provider.of(context, listen: false).getCarts(); } } Future initPusher() async { int? idUser = await UsersInfo().getIdUser(); PusherClient pusher = PusherClient( '92060797e94ac7033edb', PusherOptions(cluster: 'ap1'), autoConnect: false); pusher.connect(); pusher.onConnectionStateChange((state) { print(state!.currentState); }); pusher.onConnectionError((error) { print(error); }); _channel = pusher.subscribe('payment-channel'); _channel!.bind( 'paid-event-$idUser', (event) async { print("Event? ini apaan dah ${event!.data!}"); if (mounted) { final status = jsonDecode(event.data!); final newStatusTransaction = status['status_code']; final newStatusMessage = status['message']; if (newStatusMessage.contains("Berhasil !") && newStatusTransaction == '200') { await deleteCourse(); if (mounted) { setState(() { statusTransaction = newStatusTransaction; statusMessage = newStatusMessage; }); Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (context) => SuccessPaidCourse(), ), (route) => false, ); } } else if (newStatusMessage.contains("Dibatalkan") && newStatusTransaction == '200') { await deleteCourse(); if (mounted) { setState(() { statusTransaction = newStatusTransaction; statusMessage = newStatusMessage; }); Navigator.pop(context); showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: Theme.of(context).colorScheme.background, elevation: 0.0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), contentPadding: EdgeInsets.fromLTRB(12, 26, 22, 15), content: Padding( padding: EdgeInsets.only( bottom: getProportionateScreenHeight(14)), child: Text( textAlign: TextAlign.center, "Transaksi berhasil dibatalkan", style: thirdTextStyle.copyWith( fontSize: getProportionateScreenWidth(12), ), ), ), ), ); } } } }, ); } @override void initState() { initPusher(); super.initState(); } @override void dispose() { if (remainingTime != null) { saveRemainingTime(remainingTime!); } super.dispose(); } Widget _buildBatasBayarHistory() { List detailOrder = Provider.of(context).detailOrder; var orders = Provider.of(context, listen: false).orders; var selected = Provider.of(context); selected.selectedThumbnail = orders[0].imageUrl; selected.selectedTitle = orders[0].title; selected.selectedInstructor = orders[0].instructor; PageProvider pageProvider = Provider.of(context); Widget mandiriPay() { return detailOrder[0].bankName == 'mandiri' ? Column( children: [ Row( children: [ Text( detailOrder[0].billerCode.toString(), style: thirdTextStyle.copyWith( letterSpacing: 1, fontWeight: semiBold, fontSize: getProportionateScreenWidth(14), ), ), Spacer(), GestureDetector( onTap: () { Clipboard.setData(ClipboardData( text: detailOrder[0].billerCode.toString())) .then( (_) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Berhasil Menyalin Kode Pembayaran'), ), ); }, ); }, child: Text( "Salin", style: primaryTextStyle.copyWith( letterSpacing: 0.5, color: primaryColor, fontSize: getProportionateScreenWidth(10), fontWeight: reguler), ), ), ], ), ], ) : Row(); } Widget indomartPay() { return detailOrder[0].bankName == 'indomaret' ? Column( children: [ Row( children: [ Text( detailOrder[0].merchantId!, style: thirdTextStyle.copyWith( letterSpacing: 1, fontWeight: semiBold, fontSize: getProportionateScreenWidth(14), ), ), Spacer(), GestureDetector( onTap: () { Clipboard.setData( ClipboardData(text: detailOrder[0].merchantId!)) .then( (_) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Berhasil Menyalin Kode Merchant'), ), ); }, ); }, child: Text( "Salin", style: primaryTextStyle.copyWith( letterSpacing: 0.5, color: primaryColor, fontSize: getProportionateScreenWidth(10), fontWeight: reguler, ), ), ), ], ), ], ) : Row(); } return Scaffold( appBar: AppBar( scrolledUnderElevation: 0.0, centerTitle: true, backgroundColor: Theme.of(context).colorScheme.background, title: Text( 'Menunggu Pembayaran', style: thirdTextStyle.copyWith( fontWeight: semiBold, fontSize: getProportionateScreenWidth(14), ), ), leading: IconButton( icon: Icon( Icons.arrow_back_ios, color: Theme.of(context).colorScheme.onBackground, size: getProportionateScreenWidth(17), ), onPressed: () { pageProvider.currentIndex == 0; Navigator.pushAndRemoveUntil( context, CustomNavigatorPop(child: HomeScreen()), (route) => false, ); }, ), ), body: SingleChildScrollView( child: Consumer( builder: (context, state, _) { if (state.stateProcess == payProv.Process.loading) { return Padding( padding: const EdgeInsets.all(20), child: Shimmer.fromColors( baseColor: Colors.white, highlightColor: Colors.grey, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: getProportionateScreenHeight(40)), Container( width: double.infinity, height: getProportionateScreenHeight(250), decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.white, ), ), SizedBox(height: getProportionateScreenHeight(20)), Container( width: double.infinity, height: getProportionateScreenHeight(40), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.white, ), ), SizedBox(height: getProportionateScreenHeight(30)), Container( width: double.infinity, height: getProportionateScreenHeight(170), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Colors.white, ), ), ], ), ), ); } else if (state.state == payProv.ResultState.gagal && state.stateProcess == payProv.Process.uninitialized) { return Center( child: Text('Gagal Mengambil Pesanan'), ); } else if (state.state == payProv.ResultState.success && state.stateProcess == payProv.Process.uninitialized) { return Container( margin: EdgeInsets.symmetric( horizontal: getProportionateScreenWidth(8)), child: Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ SizedBox(height: getProportionateScreenHeight(10)), Center( child: Container( width: double.infinity, decoration: BoxDecoration( borderRadius: BorderRadius.circular(4), color: Theme.of(context).colorScheme.primaryContainer, boxShadow: [ BoxShadow( color: secondaryColor.withOpacity(0.2), spreadRadius: 1, blurRadius: 2, offset: Offset(0, getProportionateScreenHeight(4)), ), ], ), padding: EdgeInsets.symmetric( horizontal: getProportionateScreenWidth(10), vertical: getProportionateScreenHeight(15), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Batas Waktu Pembayaran', style: thirdTextStyle.copyWith( fontFamily: "Poppins", fontSize: getProportionateScreenWidth(11), ), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( DateFormat('E, d MMM y H:m WIB').format( detailOrder[0].transactionTimeLimit), style: thirdTextStyle.copyWith( fontWeight: semiBold, fontSize: SizeConfig.blockHorizontal! * 3, ), ), Container( decoration: BoxDecoration( color: sevenColor, borderRadius: BorderRadius.circular(4), ), padding: EdgeInsets.symmetric( horizontal: getProportionateScreenWidth(3), vertical: getProportionateScreenHeight(2), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Icon( Icons.access_time, color: baruTextutih, size: getProportionateScreenWidth(14), ), SizedBox( width: getProportionateScreenWidth(2)), CountdownTimer( endTime: DateTime.now() .add(Duration(hours: 24)) .millisecondsSinceEpoch, widgetBuilder: (_, CurrentRemainingTime? time) { if (time == null) { return Text( '00:00:00', style: thirdTextStyle.copyWith( fontWeight: semiBold, letterSpacing: 1, fontSize: getProportionateScreenWidth( 10), color: baruTextutih, ), ); } else { remainingTime = Duration( hours: time.hours ?? 0, minutes: time.min ?? 0, seconds: time.sec ?? 0, ); return Text( '${time.hours}:${time.min}:${time.sec}', style: thirdTextStyle.copyWith( fontWeight: semiBold, letterSpacing: 1, fontSize: getProportionateScreenWidth( 10), color: baruTextutih, ), ); } }, ), ], ), ), ], ), SizedBox(height: getProportionateScreenHeight(10)), Text( "Kursus", style: thirdTextStyle.copyWith( fontFamily: "Poppins", fontSize: getProportionateScreenWidth(11), ), ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: orders.map((e) { return listCourse( imageUrl: e.imageUrl, instructor: e.instructor, title: e.title, price: e.price, discountPrice: e.discountPrice, ); }).toList(), ), Text( 'Metode Pembayaran', style: thirdTextStyle.copyWith( letterSpacing: 0.5, fontWeight: reguler, fontSize: getProportionateScreenWidth(10), ), ), SizedBox(height: getProportionateScreenHeight(8)), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( detailOrder[0].bankName == 'mandiri' ? 'Mandiri Virtual Account' : detailOrder[0].bankName == 'permata' ? 'Permata Virtual Account' : detailOrder[0].bankName == 'bni' ? 'BNI Virtual Account' : detailOrder[0].bankName == 'bca' ? 'BCA Virtual Account' : detailOrder[0].bankName == 'indomaret' ? 'Indomaret' : 'Alfamart', style: thirdTextStyle.copyWith( letterSpacing: 1, fontWeight: semiBold, fontSize: getProportionateScreenWidth(13), ), ), Container( width: getProportionateScreenWidth(50), height: getProportionateScreenWidth(17), decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), image: DecorationImage( fit: BoxFit.fill, image: detailOrder[0].bankName == 'mandiri' ? AssetImage( "assets/images/mandiri.png") : detailOrder[0].bankName == 'permata' ? AssetImage( "assets/images/permata.png") : detailOrder[0].bankName == 'bni' ? AssetImage( "assets/images/bni.png") : detailOrder[0].bankName == 'bca' ? AssetImage( "assets/images/bca.png") : detailOrder[0] .bankName == 'alfamart' ? AssetImage( "assets/images/alfamart.png") : AssetImage( "assets/images/indomaret.png"), ), ), ), ], ), SizedBox(height: getProportionateScreenHeight(16)), Text( detailOrder[0].bankName == 'alfamart' || detailOrder[0].bankName == 'indomaret' ? 'Nomor Pesanan' : 'Nomor Virtual Akun', style: thirdTextStyle.copyWith( letterSpacing: 0.5, fontWeight: reguler, fontSize: getProportionateScreenWidth(10), ), ), SizedBox(height: getProportionateScreenHeight(8)), Row( children: [ Text( detailOrder[0].virtualNumber ?? 'ABCD', style: thirdTextStyle.copyWith( letterSpacing: 1, fontWeight: semiBold, fontSize: getProportionateScreenWidth(14), ), ), Spacer(), GestureDetector( onTap: () { Clipboard.setData(ClipboardData( text: detailOrder[0].virtualNumber!)) .then( (_) { ScaffoldMessenger.of(context) .showSnackBar( SnackBar( content: Text(detailOrder[0] .bankName == 'alfamart' || detailOrder[0].bankName == 'indomaret' ? 'Berhasil Menyalin Nomor Pesanan' : 'Berhasil Menyalin Nomor Virtual Akun'), ), ); }, ); }, child: Text( "Salin", style: thirdTextStyle.copyWith( letterSpacing: 0.5, color: primaryColor, fontSize: getProportionateScreenWidth(10), fontWeight: reguler), ), ), ], ), detailOrder[0].bankName == 'indomaret' ? Padding( padding: EdgeInsets.only( top: getProportionateScreenHeight(16), bottom: getProportionateScreenHeight(8), ), child: Text( 'Kode Merchant', style: thirdTextStyle.copyWith( letterSpacing: 0.5, fontWeight: reguler, fontSize: getProportionateScreenWidth(10), ), ), ) : SizedBox.shrink(), detailOrder[0].bankName == 'mandiri' ? Padding( padding: EdgeInsets.only( top: getProportionateScreenHeight(16), bottom: getProportionateScreenHeight(8), ), child: Text( 'Kode Pembayaran', style: thirdTextStyle.copyWith( letterSpacing: 0.5, fontWeight: reguler, fontSize: getProportionateScreenWidth(10), ), ), ) : SizedBox.shrink(), indomartPay(), mandiriPay(), SizedBox(height: getProportionateScreenHeight(16)), Text( 'Total Pembayaran', style: thirdTextStyle.copyWith( letterSpacing: 0.5, fontWeight: reguler, fontSize: getProportionateScreenWidth(10), ), ), SizedBox(height: getProportionateScreenHeight(8)), Row( children: [ Text( 'Rp. ${NumberFormat.currency(locale: 'id', symbol: '', decimalDigits: 0).format(double.parse(detailOrder[0].totalPayment))}', style: thirdTextStyle.copyWith( letterSpacing: 1, fontWeight: semiBold, fontSize: getProportionateScreenWidth(14), ), ), Spacer(), GestureDetector( onTap: () { showModalBottomSheet( backgroundColor: Theme.of(context) .colorScheme .background, elevation: 0.0, context: context, shape: RoundedRectangleBorder( borderRadius: BorderRadius.vertical( top: Radius.circular(20), ), ), builder: (context) { return BottomSheetDetail(); }, ); }, child: Text( "Detail Pembayaran", style: thirdTextStyle.copyWith( letterSpacing: 0.5, color: primaryColor, fontSize: getProportionateScreenWidth(10), fontWeight: reguler), ), ), ], ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Column( children: [ SizedBox( height: getProportionateScreenHeight(24)), DefaultButtonPayment( text: 'Ubah Metode Pembayaran', weight: semiBold, press: () { state.selectedIdOrders = ""; Navigator.pop(context); }, width: 320, height: 44, ), SizedBox( height: getProportionateScreenHeight(12)), SizedBox( width: getProportionateScreenWidth(320), height: getProportionateScreenHeight(38), child: TextButton( onPressed: () async { showDialog( context: context, builder: (BuildContext context) { return AlertDialog( elevation: 0.0, backgroundColor: Theme.of(context) .colorScheme .background, contentPadding: EdgeInsets.symmetric( vertical: getProportionateScreenHeight( 20), horizontal: getProportionateScreenWidth( 10), ), actionsPadding: EdgeInsets.only( right: getProportionateScreenWidth( 10), bottom: getProportionateScreenHeight( 12), ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( getProportionateScreenWidth( 4)), ), content: Text( 'Apakah Anda yakin ingin membatalkan transaksi?'), actions: [ GestureDetector( child: Text( 'Ya', style: TextStyle( color: primaryColor, fontSize: getProportionateScreenWidth( 11), ), ), onTap: () async { state.selectedIdOrders = ""; Navigator.of(context) .pop(); CancelPaymentService cancelPaymentService = CancelPaymentService(); setState(() { isLoading = true; }); await cancelPaymentService .cancelPayment( detailOrder[0] .idOrder .toString()); await Future.delayed( Duration(seconds: 2)); setState(() { isLoading = false; }); }, ), SizedBox( width: getProportionateScreenWidth( 5)), GestureDetector( child: Text( 'Tidak', style: TextStyle( color: primaryColor, fontSize: getProportionateScreenWidth( 11), ), ), onTap: () { Navigator.of(context) .pop(); }, ), ], ); }, ); }, child: isLoading ? Container( width: getProportionateScreenWidth( 15), height: getProportionateScreenHeight( 13), child: CircularProgressIndicator( color: primaryColor, ), ) : Text( "Batalkan Transaksi", style: thirdTextStyle.copyWith( fontSize: getProportionateScreenWidth( 12), fontWeight: semiBold, color: primaryColor, letterSpacing: 0.5, ), ), style: TextButton.styleFrom( foregroundColor: Theme.of(context) .colorScheme .background, shape: RoundedRectangleBorder( side: BorderSide(color: primaryColor), borderRadius: BorderRadius.circular( getProportionateScreenWidth(5), ), ), backgroundColor: Colors.transparent, ), ), ), ], ), ], ), ], ), ), ), SizedBox(height: getProportionateScreenHeight(20)), Container( decoration: BoxDecoration( color: Theme.of(context).colorScheme.primaryContainer, borderRadius: BorderRadius.circular(4), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.1), spreadRadius: 0, blurRadius: 4, offset: Offset(0, 4), ), ], ), margin: EdgeInsets.only( left: getProportionateScreenWidth(2), right: getProportionateScreenWidth(2)), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( width: double.infinity, padding: EdgeInsets.only( top: getProportionateScreenHeight(15), bottom: getProportionateScreenHeight(7), left: getProportionateScreenWidth(18), ), decoration: BoxDecoration( color: Theme.of(context) .colorScheme .primaryContainer, boxShadow: [ BoxShadow( color: secondaryColor.withOpacity(0.2), spreadRadius: 0, blurRadius: 2, offset: Offset( 0, getProportionateScreenHeight(4)), ), ], ), child: Text( 'Cara Pembayaran', style: thirdTextStyle.copyWith( letterSpacing: 1, fontWeight: semiBold, fontSize: getProportionateScreenWidth(14), ), ), ), Container( padding: EdgeInsets.symmetric( horizontal: getProportionateScreenWidth(5), ), child: detailOrder[0].bankName == 'indomaret' || detailOrder[0].bankName == 'alfamart' ? BarBatasBayar(detailOrder[0].bankName!) : TabBarBatasBayar(), ), ], ), ), SizedBox(height: getProportionateScreenHeight(30)), ], ), ); } else { return Center( child: Text('Terjadi Kesalahan'), ); } }, ), ), ); } @override Widget build(BuildContext context) { return _buildBatasBayarHistory(); } Widget listCourse({ String? imageUrl, String? title, String? instructor, String? price, String? discountPrice, int? totalPrices, }) { return Container( padding: EdgeInsets.symmetric(vertical: getProportionateScreenHeight(9)), child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ Container( width: getProportionateScreenWidth(60), height: getProportionateScreenHeight(30), decoration: BoxDecoration( borderRadius: BorderRadius.circular(2), image: DecorationImage( image: NetworkImage(imageUrl ?? '$baseUrl/uploads/courses_thumbnail/course_thumbnail_default_57.jpg'), fit: BoxFit.cover, ), ), ), SizedBox(width: getProportionateScreenWidth(10)), Flexible( flex: 7, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title!, maxLines: 2, overflow: TextOverflow.ellipsis, style: thirdTextStyle.copyWith( letterSpacing: 0.5, fontWeight: reguler, fontSize: getProportionateScreenWidth(11), ), ), ], ), ), ], ), ); } }