import 'dart:async'; import 'dart:developer'; import 'dart:io'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hmg_patient_app_new/core/app_assets.dart'; import 'package:hmg_patient_app_new/core/app_state.dart'; import 'package:hmg_patient_app_new/core/cache_consts.dart'; import 'package:hmg_patient_app_new/core/dependencies.dart'; import 'package:hmg_patient_app_new/core/enums.dart'; import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; import 'package:hmg_patient_app_new/core/utils/size_utils.dart'; import 'package:hmg_patient_app_new/core/utils/utils.dart'; import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/features/my_appointments/models/resp_models/patient_appointment_history_response_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; import 'package:hmg_patient_app_new/generated/locale_keys.g.dart'; import 'package:hmg_patient_app_new/presentation/appointments/my_appointments_page.dart'; import 'package:hmg_patient_app_new/presentation/home/navigation_screen.dart'; import 'package:hmg_patient_app_new/presentation/insurance/insurance_home_page.dart'; import 'package:hmg_patient_app_new/widgets/appbar/collapsing_list_view.dart'; import 'package:hmg_patient_app_new/services/cache_service.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; import 'package:hmg_patient_app_new/widgets/common_bottom_sheet.dart'; import 'package:hmg_patient_app_new/widgets/in_app_browser/InAppBrowser.dart'; import 'package:hmg_patient_app_new/widgets/loader/bottomsheet_loader.dart'; import 'package:hmg_patient_app_new/widgets/routes/custom_page_route.dart'; import 'package:provider/provider.dart'; import 'package:smooth_corner/smooth_corner.dart'; class AppointmentPaymentPage extends StatefulWidget { AppointmentPaymentPage({super.key, required this.patientAppointmentHistoryResponseModel}); PatientAppointmentHistoryResponseModel patientAppointmentHistoryResponseModel; @override State createState() => _AppointmentPaymentPageState(); } class _AppointmentPaymentPageState extends State { late MyAppointmentsViewModel myAppointmentsViewModel; late PayfortViewModel payfortViewModel; late AppState appState; MyInAppBrowser? browser; String selectedPaymentMethod = ""; String transID = ""; bool isShowTamara = false; @override void initState() { scheduleMicrotask(() { payfortViewModel.initPayfortViewModel(); myAppointmentsViewModel.getTamaraInstallmentsDetails().then((val) { if (myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! >= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.minLimit!.amount! && myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax! <= myAppointmentsViewModel.getTamaraInstallmentsDetailsResponseModel!.maxLimit!.amount!) { setState(() { isShowTamara = true; }); } }); payfortViewModel.setIsApplePayConfigurationLoading(false); myAppointmentsViewModel.getPatientShareAppointment( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, onError: (err) { Navigator.of(context).pop(); Navigator.of(context).pop(); }); }); super.initState(); } @override Widget build(BuildContext context) { appState = getIt.get(); myAppointmentsViewModel = Provider.of(context); payfortViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, body: Consumer(builder: (context, myAppointmentsVM, child) { return Column( children: [ Expanded( child: CollapsingListView( title: "Appointment Payment".needTranslation, child: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox(height: 24.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false, ), child: Row( mainAxisSize: MainAxisSize.max, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset(AppAssets.mada, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), "Mada".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], ), SizedBox(width: 8.h), const Spacer(), Transform.flip( flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, width: 18.h, height: 13.h, fit: BoxFit.contain, ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ), ], ).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(24.h, 0.h).onPress(() { selectedPaymentMethod = "MADA"; openPaymentURL("mada"); }), SizedBox(height: 16.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false, ), child: Row( mainAxisSize: MainAxisSize.max, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Image.asset(AppAssets.visa, width: 50.h, height: 50.h), SizedBox(width: 8.h), Image.asset(AppAssets.Mastercard, width: 40.h, height: 40.h), ], ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), "Visa or Mastercard".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], ), SizedBox(width: 8.h), const Spacer(), Transform.flip( flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, width: 18.h, height: 13.h, fit: BoxFit.contain, ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ), ], ).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(24.h, 0.h).onPress(() { selectedPaymentMethod = "VISA"; openPaymentURL("visa"); }), SizedBox(height: 16.h), isShowTamara ? Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 20.h, hasShadow: false, ), child: Row( mainAxisSize: MainAxisSize.max, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Image.asset(AppAssets.tamara_en, width: 72.h, height: 25.h).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), SizedBox(height: 16.h), "Tamara".needTranslation.toText16(isBold: true).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ], ), SizedBox(width: 8.h), const Spacer(), Transform.flip( flipX: appState.isArabic() ? true : false, child: Utils.buildSvgWithAssets( icon: AppAssets.forward_arrow_icon, iconColor: AppColors.blackColor, width: 18.h, height: 13.h, fit: BoxFit.contain, ).toShimmer2(isShow: myAppointmentsVM.isAppointmentPatientShareLoading), ), ], ).paddingSymmetrical(16.h, 16.h), ).paddingSymmetrical(24.h, 0.h).onPress(() { selectedPaymentMethod = "TAMARA"; openPaymentURL("tamara"); }) : SizedBox.shrink(), ], ), ), ), ), myAppointmentsVM.isAppointmentPatientShareLoading ? SizedBox.shrink() : Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: false, ), child: Consumer(builder: (context, payfortVM, child) { //TODO: Need to add loading state & animation for Apple Pay Configuration return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ (myAppointmentsVM.patientAppointmentShareResponseModel!.isCash ?? true) ? Container( height: 50.h, decoration: ShapeDecoration( color: AppColors.secondaryLightRedBorderColor, shape: SmoothRectangleBorder( borderRadius: BorderRadius.only(topLeft: Radius.circular(24), topRight: Radius.circular(24)), smoothness: 1, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "Insurance expired or inactive".needTranslation.toText14(color: AppColors.primaryRedColor, weight: FontWeight.w500).paddingSymmetrical(24.h, 0.h), CustomButton( text: LocaleKeys.updateInsurance.tr(context: context), onPressed: () { Navigator.of(context).push( CustomPageRoute( page: InsuranceHomePage(), ), ); }, backgroundColor: AppColors.primaryRedColor, borderColor: AppColors.secondaryLightRedBorderColor, textColor: AppColors.whiteColor, fontSize: 10, fontWeight: FontWeight.w500, borderRadius: 8, padding: EdgeInsets.fromLTRB(15, 0, 15, 0), height: 30.h, ).paddingSymmetrical(24.h, 0.h), ], ), ) : const SizedBox(), SizedBox(height: 24.h), "Total amount to pay".needTranslation.toText18(isBold: true).paddingSymmetrical(24.h, 0.h), SizedBox(height: 17.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "Amount before tax".needTranslation.toText14(isBold: true), Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "VAT 15%".needTranslation.toText14(isBold: true, color: AppColors.greyTextColor), Utils.getPaymentAmountWithSymbol( myAppointmentsVM.patientAppointmentShareResponseModel!.patientTaxAmount!.toString().toText14(isBold: true, color: AppColors.greyTextColor), AppColors.greyTextColor, 13, isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), SizedBox(height: 17.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ "".needTranslation.toText14(isBold: true), Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShareWithTax!.toString().toText24(isBold: true), AppColors.blackColor, 17, isSaudiCurrency: true), ], ).paddingSymmetrical(24.h, 0.h), Platform.isIOS ? Utils.buildSvgWithAssets( icon: AppAssets.apple_pay_button, width: 200.h, height: 80.h, fit: BoxFit.contain, ).paddingSymmetrical(24.h, 0.h).onPress(() { // payfortVM.setIsApplePayConfigurationLoading(true); if (Utils.havePrivilege(103)) { startApplePay(); } else { openPaymentURL("ApplePay"); } }) : SizedBox(height: 12.h), SizedBox(height: 12.h), ], ); }), ), ], ); }), ); } onBrowserLoadStart(String url) { print("onBrowserLoadStart"); print(url); if (selectedPaymentMethod == "tamara") { if (Platform.isAndroid) { Uri uri = new Uri.dataFromString(url); // tamaraPaymentStatus = uri.queryParameters['status']!; // tamaraOrderID = uri.queryParameters['AuthorizePaymentId']!; } else { Uri uri = new Uri.dataFromString(url); // tamaraPaymentStatus = uri.queryParameters['paymentStatus']!; // tamaraOrderID = uri.queryParameters['orderId']!; } } // if(selectedPaymentMethod != "TAMARA") { MyInAppBrowser.successURLS.forEach((element) { if (url.contains(element)) { browser?.close(); MyInAppBrowser.isPaymentDone = true; return; } }); // } // if(selectedPaymentMethod != "TAMARA") { MyInAppBrowser.errorURLS.forEach((element) { if (url.contains(element)) { browser?.close(); MyInAppBrowser.isPaymentDone = false; return; } }); // } } onBrowserExit(bool isPaymentMade) async { print("onBrowserExit Called!!!!"); if (selectedPaymentMethod == "TAMARA") { // checkTamaraPaymentStatus(transID!, appo); // if (tamaraPaymentStatus != null && tamaraPaymentStatus.toLowerCase() == "approved") { // updateTamaraRequestStatus("success", "14", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo); // } else { // updateTamaraRequestStatus("Failed", "00", Utils.getAppointmentTransID(appo.projectID, appo.clinicID, appo.appointmentNo), tamaraOrderID, num.parse(selectedInstallments), appo); // } } else { checkPaymentStatus(); // checkPaymentStatus(appo); } } void checkPaymentStatus() async { LoaderBottomSheet.showLoader(); await payfortViewModel.checkPaymentStatus( transactionID: transID, onSuccess: (apiResponse) async { print(apiResponse.data); if (payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!.toLowerCase() == "success") { await myAppointmentsViewModel.createAdvancePayment( paymentMethodName: selectedPaymentMethod, projectID: widget.patientAppointmentHistoryResponseModel.projectID, clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), payedAmount: payfortViewModel.payfortCheckPaymentStatusResponseModel!.amount!, paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, patientID: appState.getAuthenticatedUser()!.patientId.toString(), patientType: appState.getAuthenticatedUser()!.patientType!, onSuccess: (value) async { print(value); await myAppointmentsViewModel.addAdvanceNumberRequest( advanceNumber: Utils.isVidaPlusProject(widget.patientAppointmentHistoryResponseModel.projectID) ? value.data['OnlineCheckInAppointments'][0]['AdvanceNumber_VP'].toString() : value.data['OnlineCheckInAppointments'][0]['AdvanceNumber'].toString(), paymentReference: payfortViewModel.payfortCheckPaymentStatusResponseModel!.fortId!, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), onSuccess: (value) async { if (widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment!) { //TODO: Implement LiveCare Check-In API Call await myAppointmentsViewModel.insertLiveCareVIDARequest( clientRequestID: transID, patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel, onSuccess: (apiResponse) { Future.delayed(Duration(milliseconds: 500), () { LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, CustomPageRoute( page: LandingNavigation(), ), (r) => false); }); }, onError: (error) {}); } else { await myAppointmentsViewModel.generateAppointmentQR( clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, projectID: widget.patientAppointmentHistoryResponseModel.projectID, appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!, onSuccess: (apiResponse) { Future.delayed(Duration(milliseconds: 500), () { LoaderBottomSheet.hideLoader(); Navigator.pushAndRemoveUntil( context, CustomPageRoute( page: LandingNavigation(), ), (r) => false); // Navigator.of(context).push( // CustomPageRoute(page: MyAppointmentsPage()), // ); }); }); } }); }); } else { showCommonBottomSheetWithoutHeight( context, child: Utils.getErrorWidget(loadingText: "Payment Failed! Please try again.".needTranslation), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); } }); } openPaymentURL(String paymentMethod) { browser = MyInAppBrowser(onExitCallback: onBrowserExit, onLoadStartCallback: onBrowserLoadStart, context: context); transID = Utils.getAppointmentTransID( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.appointmentNo, ); //TODO: Need to pass dynamic params to the payment request instead of static values browser?.openPaymentBrowser( myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!, "Appointment check in", transID, widget.patientAppointmentHistoryResponseModel.projectID.toString(), "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", selectedPaymentMethod, appState.getAuthenticatedUser()!.patientType.toString(), "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", appState.getAuthenticatedUser()!.patientId.toString(), appState.getAuthenticatedUser()!, browser!, widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment ?? false, "2", widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? myAppointmentsViewModel.patientAppointmentShareResponseModel!.clinicID.toString() : "", context, myAppointmentsViewModel.patientAppointmentShareResponseModel!.appointmentDate, myAppointmentsViewModel.patientAppointmentShareResponseModel!.appointmentNo, myAppointmentsViewModel.patientAppointmentShareResponseModel!.clinicID, myAppointmentsViewModel.patientAppointmentShareResponseModel!.doctorID, "3"); } startApplePay() async { showCommonBottomSheet(context, child: Utils.getLoadingWidget(), callBackFunc: (str) {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); transID = Utils.getAppointmentTransID( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, widget.patientAppointmentHistoryResponseModel.appointmentNo, ); ApplePayInsertRequest applePayInsertRequest = ApplePayInsertRequest(); await payfortViewModel.getPayfortConfigurations( serviceId: ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum(), projectId: widget.patientAppointmentHistoryResponseModel.projectID, integrationId: 2); applePayInsertRequest.clientRequestID = transID; applePayInsertRequest.clinicID = widget.patientAppointmentHistoryResponseModel.clinicID; applePayInsertRequest.currency = appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED"; applePayInsertRequest.customerEmail = "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com"; applePayInsertRequest.customerID = appState.getAuthenticatedUser()!.patientId.toString(); applePayInsertRequest.customerName = "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}"; applePayInsertRequest.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken); applePayInsertRequest.voipToken = await Utils.getStringFromPrefs(CacheConst.voipToken); applePayInsertRequest.doctorID = widget.patientAppointmentHistoryResponseModel.doctorID; applePayInsertRequest.projectID = widget.patientAppointmentHistoryResponseModel.projectID.toString(); applePayInsertRequest.serviceID = ServiceTypeEnum.appointmentPayment.getIdFromServiceEnum().toString(); applePayInsertRequest.channelID = 3; applePayInsertRequest.patientID = appState.getAuthenticatedUser()!.patientId.toString(); applePayInsertRequest.patientTypeID = appState.getAuthenticatedUser()!.patientType; applePayInsertRequest.patientOutSA = appState.getAuthenticatedUser()!.outSa; applePayInsertRequest.appointmentDate = widget.patientAppointmentHistoryResponseModel.appointmentDate; applePayInsertRequest.appointmentNo = widget.patientAppointmentHistoryResponseModel.appointmentNo; applePayInsertRequest.orderDescription = "Appointment Payment"; applePayInsertRequest.liveServiceID = "0"; applePayInsertRequest.latitude = "0.0"; applePayInsertRequest.longitude = "0.0"; applePayInsertRequest.amount = myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString(); applePayInsertRequest.isSchedule = widget.patientAppointmentHistoryResponseModel.isLiveCareAppointment! ? "1" : "0"; applePayInsertRequest.language = appState.isArabic() ? 'ar' : 'en'; applePayInsertRequest.languageID = appState.isArabic() ? 1 : 2; applePayInsertRequest.userName = appState.getAuthenticatedUser()!.patientId; applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html"; applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html"; applePayInsertRequest.paymentOption = "ApplePay"; applePayInsertRequest.isMobSDK = true; applePayInsertRequest.merchantReference = transID; applePayInsertRequest.merchantIdentifier = payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier; applePayInsertRequest.commandType = "PURCHASE"; applePayInsertRequest.signature = payfortViewModel.payfortProjectDetailsRespModel!.signature; applePayInsertRequest.accessCode = payfortViewModel.payfortProjectDetailsRespModel!.accessCode; applePayInsertRequest.shaRequestPhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaRequest; applePayInsertRequest.shaResponsePhrase = payfortViewModel.payfortProjectDetailsRespModel!.shaResponse; applePayInsertRequest.returnURL = ""; //TODO: Need to pass dynamic params to the Apple Pay instead of static values await payfortViewModel.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest).then((value) { payfortViewModel.paymentWithApplePay( customerName: "${appState.getAuthenticatedUser()!.firstName} ${appState.getAuthenticatedUser()!.lastName}", // customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress, customerEmail: "CustID_${appState.getAuthenticatedUser()!.patientId.toString()}@HMG.com", orderDescription: "Appointment Payment", orderAmount: double.parse(myAppointmentsViewModel.patientAppointmentShareResponseModel!.patientShareWithTax!.toString()), merchantReference: transID, merchantIdentifier: payfortViewModel.payfortProjectDetailsRespModel!.merchantIdentifier, applePayAccessCode: payfortViewModel.payfortProjectDetailsRespModel!.accessCode, applePayShaRequestPhrase: payfortViewModel.payfortProjectDetailsRespModel!.shaRequest, currency: appState.getAuthenticatedUser()!.outSa! == 0 ? "SAR" : "AED", onFailed: (failureResult) async { log("failureResult: ${failureResult.message.toString()}"); Navigator.of(context).pop(); showCommonBottomSheetWithoutHeight( context, child: Utils.getErrorWidget(loadingText: failureResult.message.toString()), callBackFunc: () {}, isFullScreen: false, isCloseButtonVisible: true, ); }, onSucceeded: (successResult) async { Navigator.of(context).pop(); log("successResult: ${successResult.responseMessage.toString()}"); selectedPaymentMethod = successResult.paymentOption ?? "VISA"; checkPaymentStatus(); }, // projectId: appo.projectID, // serviceTypeEnum: ServiceTypeEnum.appointmentPayment, ); }); } }