From 2c45356b07ca1711c5af08c951273478cc34980f Mon Sep 17 00:00:00 2001 From: haroon amjad Date: Mon, 8 Sep 2025 15:57:15 +0300 Subject: [PATCH] Payment module implementation contd. --- assets/animations/lottie/Loader.json | 1 + lib/core/api/api_client.dart | 34 +- lib/core/api_consts.dart | 32 +- lib/core/app_assets.dart | 1 + lib/core/app_state.dart | 11 + .../VidaPlusProjectListModel.dart | 15 + .../common_models/tamara_request_model.dart | 120 +++++ lib/core/dependencies.dart | 10 + lib/core/enums.dart | 46 ++ lib/core/utils/utils.dart | 23 + .../my_appointments/my_appointments_repo.dart | 150 +++++- .../my_appointments_view_model.dart | 74 +++ .../apple_pay_request_insert_model.dart | 187 +++++++ ...t_check_payment_status_response_model.dart | 60 +++ .../payfort_project_details_resp_model.dart | 37 ++ .../models/sdk_token_response_model.dart | 55 ++ lib/features/payfort/payfort_repo.dart | 150 ++++++ lib/features/payfort/payfort_view_model.dart | 198 +++++++ lib/main.dart | 7 + .../appointment_payment_page.dart | 392 +++++++++++--- .../widgets/appointment_card.dart | 16 +- lib/widgets/common_bottom_sheet.dart | 3 +- lib/widgets/in_app_browser/InAppBrowser.dart | 489 ++++++++++++++++++ pubspec.yaml | 2 + 24 files changed, 2018 insertions(+), 95 deletions(-) create mode 100644 assets/animations/lottie/Loader.json create mode 100644 lib/core/common_models/VidaPlusProjectListModel.dart create mode 100644 lib/core/common_models/tamara_request_model.dart create mode 100644 lib/features/payfort/models/apple_pay_request_insert_model.dart create mode 100644 lib/features/payfort/models/payfort_check_payment_status_response_model.dart create mode 100644 lib/features/payfort/models/payfort_project_details_resp_model.dart create mode 100644 lib/features/payfort/models/sdk_token_response_model.dart create mode 100644 lib/features/payfort/payfort_repo.dart create mode 100644 lib/features/payfort/payfort_view_model.dart create mode 100644 lib/widgets/in_app_browser/InAppBrowser.dart diff --git a/assets/animations/lottie/Loader.json b/assets/animations/lottie/Loader.json new file mode 100644 index 0000000..6ffaba4 --- /dev/null +++ b/assets/animations/lottie/Loader.json @@ -0,0 +1 @@ +{"nm":"newScene","ddd":0,"h":89,"w":87,"meta":{"g":"SVG to Lottie v0.15.1-0.6.0"},"layers":[{"ty":4,"nm":"","sr":1,"st":0,"op":145,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"nm":"","it":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[0.4578999999999951,-0.7915999999999954],[0.5837000000000074,-0.04779999999999518],[0,0],[0.7841999999999985,-1.0872000000000028],[0.08370000000000033,-1.5309999999999988],[0,0],[0,0],[0,0],[0,0],[-0.7730999999999995,-1.0963999999999956],[-1.3689000000000036,-0.00269999999999726],[0,0],[0,0],[-0.4672999999999945,-0.7371000000000052],[-0.06629999999999825,-1.1026000000000025],[0,0],[0,0],[0,0],[0,0],[0.44539999999999935,0.6474000000000046],[0.08979999999999677,1.1196999999999946],[0,0],[0,0],[0.7180999999999997,1.1539999999999964],[1.2956000000000003,0.1752000000000038],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[0.6975000000000051,-1.1477000000000004],[0,-1.5995999999999952],[0,0],[0.5057000000000045,-0.699300000000008],[0.6049000000000007,0],[0,0],[0,0],[0,0],[-0.4679000000000002,0.7723999999999975],[-0.605599999999999,0.045999999999999375],[0,0],[0,0],[-0.708400000000001,1.1539000000000001],[0,1.6081000000000003],[0,0],[0.7216999999999985,1.1640000000000015],[1.4034999999999975,0.0945999999999998],[0,0],[0,0],[0,0],[0.4687000000000001,0.7625999999999991],[0,1.3160999999999987],[0,0],[0,0],[-0.4976000000000056,-0.6899999999999995],[-0.06589999999999918,-1.2172000000000018],[0,0],[0,0],[0,0],[-0.780300000000004,-1.0818000000000012],[-1.3714999999999975,0],[0,0],[0,0],[0,0],[0,0],[-0.7753000000000014,1.1095000000000041],[-0.08019999999999783,1.5247000000000028],[0,0],[0,0],[-0.5198999999999998,0.7158000000000015],[-0.5554000000000059,0.00019999999999953388],[0,0],[0,0],[-1.2428000000000026,1.1377000000000006],[-0.011699999999990496,0.010800000000001475],[0,0]],"o":[[0,1.334100000000003],[-0.44549999999999557,0.7698999999999998],[0,0],[-1.3445999999999998,0.00010000000000331966],[-0.7323999999999984,1.0152999999999963],[0,0],[0,0],[0,0],[0,0],[0.03580000000000183,1.6775999999999982],[0.7805999999999997,1.1067999999999998],[0,0],[0,0],[0.6218000000000075,0.04639999999999844],[0.418200000000013,0.6596999999999937],[0,0],[0,0],[0,0],[0,0],[-0.6043999999999983,0],[-0.4025999999999996,-0.5853999999999928],[0,0],[0,0],[0,-1.6077999999999975],[-0.6773000000000025,-1.0883000000000038],[0,0],[0,0],[0,0],[0,0],[0,0],[0,0],[-1.393099999999997,0.09390000000000498],[-0.6918999999999969,1.1385999999999967],[0,0],[-0.029200000000003,1.3703000000000003],[-0.4995999999999938,0.6905999999999892],[0,0],[0,0],[0,0],[0,-1.3253000000000057],[0.45700000000000074,-0.754400000000004],[0,0],[0,0],[1.3979,-0.09440000000000026],[0.7066999999999979,-1.1512000000000029],[0,0],[0,-1.6160999999999959],[-0.7198000000000029,-1.160899999999998],[0,0],[0,0],[0,0],[-0.5666999999999991,-0.04700000000000415],[-0.47689999999999877,-0.7762999999999991],[0,0],[0,0],[0.5625,0.00020000000000131024],[0.4695999999999998,0.6512999999999991],[0,0],[0,0],[0,0],[0.07270000000000465,1.6426000000000016],[0.7882999999999996,1.092800000000004],[0,0],[0,0],[0,0],[0,0],[1.383499999999998,-0.00010000000000331966],[0.7209999999999965,-1.031799999999997],[0,0],[0,0],[0.028800000000003934,-1.325099999999999],[0.5206999999999979,-0.7166999999999994],[0,0],[0,0],[2.543999999999997,-0.11350000000000016],[0.011899999999997135,-0.010899999999999466],[0,0],[0,0]],"v":[[73.6377,31.1362],[72.8926,34.4077],[71.3115,35.5864],[62.5371,35.5864],[59.2598,37.4057],[57.9971,41.3071],[57.9854,41.6157],[57.9854,41.6528],[58.0527,46.2387],[58.0527,46.2455],[59.2949,50.5053],[62.5947,52.3412],[62.5947,52.3422],[71.3672,52.4077],[73.0439,53.5551],[73.8203,56.2426],[73.835,56.725],[73.7715,72.6703],[52.873,72.6078],[52.8701,72.6078],[51.2695,71.6703],[50.4746,69.0952],[50.4502,68.6],[50.4502,54.7983],[49.3545,50.5307],[46.3604,48.4428],[46.0977,48.4164],[46.0586,48.4145],[46.0186,48.4145],[41.0215,48.4809],[40.9941,48.4809],[40.9678,48.4828],[37.7949,50.5737],[36.7451,54.7983],[36.7451,68.5356],[35.8428,71.6752],[34.1299,72.6733],[17.8916,72.6078],[13.6885,72.6078],[13.625,56.4594],[14.3926,53.2387],[16.0264,52.0756],[24.8906,52.0756],[24.9248,52.0737],[28.126,49.9545],[29.21,45.6918],[29.21,41.7709],[28.0908,37.4721],[24.8594,35.3227],[24.8223,35.3207],[24.7852,35.3207],[16.0234,35.3862],[14.415,34.2192],[13.625,31.0034],[13.625,15.1215],[34.1953,15.1215],[35.8496,16.1098],[36.7363,18.9477],[36.7451,19.1948],[36.7451,32.9526],[36.7461,32.975],[38.0527,37.1635],[41.3623,38.9828],[41.3623,38.9819],[45.8203,39.0483],[45.8271,39.0493],[45.835,39.0493],[49.1445,37.184],[50.373,33.2602],[50.3848,32.9526],[50.3848,19.3276],[51.3115,16.2123],[53.002,15.1879],[67.8867,15.1879],[67.9092,15.187],[73.6025,12.7241],[73.6377,12.6909],[73.6377,31.1362]]}}},{"ty":"st","bm":0,"hd":false,"nm":"","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100},"w":{"a":0,"k":2},"c":{"a":0,"k":[0.9294,0.1098,0.1686]}},{"ty":"tm","bm":0,"hd":false,"nm":"","e":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[100],"t":120}]},"o":{"a":0,"k":0},"s":{"a":null,"k":0},"m":1},{"ty":"tr","a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[0,0]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}}]}],"ind":1}],"v":"5.7.0","fr":60,"op":145,"ip":0,"assets":[]} \ No newline at end of file diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 7db28e9..ca76ae3 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -102,7 +102,7 @@ class ApiClientImp implements ApiClient { if (isRCService) { url = RC_BASE_URL + endPoint; } else { - url = BASE_URL + endPoint; + url = ApiConsts.baseUrl + endPoint; } } try { @@ -152,21 +152,23 @@ class ApiClientImp implements ApiClient { // request.patientOutSA = (request.zipCode == '966' || request.zipCode == '+966') ? 0 : 1; // body['VersionID'] = ApiConsts.appVersionID.toString(); - body['VersionID'] = "50.0"; - body['Channel'] = ApiConsts.appChannelId.toString(); - body['IPAdress'] = ApiConsts.appIpAddress; - body['generalid'] = ApiConsts.appGeneralId; - - body['LanguageID'] = _appState.getLanguageID().toString(); - body['Latitude'] = _appState.userLat.toString(); - body['Longitude'] = _appState.userLong.toString(); - body['DeviceTypeID'] = _appState.deviceTypeID; - if (_appState.appLoginTokenID.isNotEmpty) { - body['LogInTokenID'] = _appState.appLoginTokenID; - } + if (!isExternal) { + body['VersionID'] = "50.0"; + body['Channel'] = ApiConsts.appChannelId.toString(); + body['IPAdress'] = ApiConsts.appIpAddress; + body['generalid'] = ApiConsts.appGeneralId; + + body['LanguageID'] = _appState.getLanguageID().toString(); + body['Latitude'] = _appState.userLat.toString(); + body['Longitude'] = _appState.userLong.toString(); + body['DeviceTypeID'] = _appState.deviceTypeID; + if (_appState.appLoginTokenID.isNotEmpty) { + body['LogInTokenID'] = _appState.appLoginTokenID; + } - body['TokenID'] = "@dm!n"; - body['PatientID'] = "3628599"; + body['TokenID'] = "@dm!n"; + body['PatientID'] = "4767477"; + } body.removeWhere((key, value) => value == null); log("body: ${json.encode(body)}"); @@ -194,7 +196,7 @@ class ApiClientImp implements ApiClient { } else { var parsed = json.decode(utf8.decode(response.bodyBytes)); if (isAllowAny) { - onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); + onSuccess(parsed, statusCode, messageStatus: 1, errorMessage: ""); } else { if (parsed['Response_Message'] != null) { onSuccess(parsed, statusCode, messageStatus: parsed['MessageStatus'], errorMessage: parsed['ErrorEndUserMessage'] ?? parsed['ErrorMessage']); diff --git a/lib/core/api_consts.dart b/lib/core/api_consts.dart index 5a38342..8ae9304 100644 --- a/lib/core/api_consts.dart +++ b/lib/core/api_consts.dart @@ -1,4 +1,5 @@ import 'package:hmg_patient_app_new/core/enums.dart'; +import 'package:amazon_payfort/amazon_payfort.dart'; var MAX_SMALL_SCREEN = 660; final OPENTOK_API_KEY = '46209962'; @@ -697,7 +698,7 @@ var IS_DOCTOR_AVAILABLE_BY_CALENDAR_SCHEDULE = 'Services/OUTPs.svc/REST/HIS_IsDo var getPayFortProjectDetails = "Services/PayFort_Serv.svc/REST/GetPayFortProjectDetails"; var addPayFortApplePayResponse = "Services/PayFort_Serv.svc/REST/AddResponse"; // var payFortEnvironment = FortEnvironment.production; -var applePayMerchantId = "merchant.com.hmgwebservices"; +// var applePayMerchantId = "merchant.com.hmgwebservices"; // var payFortEnvironment = FortEnvironment.test; // var applePayMerchantId = "merchant.com.hmgwebservices.uat"; @@ -725,7 +726,7 @@ const DEACTIVATE_ACCOUNT = 'Services/Patients.svc/REST/PatientAppleActivation_In class ApiConsts { static const maxSmallScreen = 660; - static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.prod; + static AppEnvironmentTypeEnum appEnvironmentType = AppEnvironmentTypeEnum.uat; // static String baseUrl = 'https://uat.hmgwebservices.com/'; // HIS API URL UAT @@ -735,25 +736,52 @@ class ApiConsts { static num VERSION_ID = 18.9; + static var payFortEnvironment = FortEnvironment.production; + static var applePayMerchantId = "merchant.com.hmgwebservices"; + + static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE + // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT + +// var payFortEnvironment = FortEnvironment.test; +// var applePayMerchantId = "merchant.com.hmgwebservices.uat"; + static setBackendURLs() { switch (appEnvironmentType) { case AppEnvironmentTypeEnum.prod: baseUrl = "https://hmgwebservices.com/"; + payFortEnvironment = FortEnvironment.production; + applePayMerchantId = "merchant.com.hmgwebservices"; + SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; break; case AppEnvironmentTypeEnum.dev: baseUrl = "https://uat.hmgwebservices.com/"; + payFortEnvironment = FortEnvironment.test; + applePayMerchantId = "merchant.com.hmgwebservices.uat"; + SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; break; case AppEnvironmentTypeEnum.uat: baseUrl = "https://uat.hmgwebservices.com/"; + payFortEnvironment = FortEnvironment.test; + applePayMerchantId = "merchant.com.hmgwebservices.uat"; + SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; break; case AppEnvironmentTypeEnum.preProd: baseUrl = "https://webservices.hmg.com/"; + payFortEnvironment = FortEnvironment.production; + applePayMerchantId = "merchant.com.hmgwebservices"; + SERVICE_URL = "https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx"; break; case AppEnvironmentTypeEnum.qa: baseUrl = "https://uat.hmgwebservices.com/"; + payFortEnvironment = FortEnvironment.test; + applePayMerchantId = "merchant.com.hmgwebservices.uat"; + SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; break; case AppEnvironmentTypeEnum.staging: baseUrl = "https://uat.hmgwebservices.com/"; + payFortEnvironment = FortEnvironment.test; + applePayMerchantId = "merchant.com.hmgwebservices.uat"; + SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; break; } } diff --git a/lib/core/app_assets.dart b/lib/core/app_assets.dart index 522a6ca..d641598 100644 --- a/lib/core/app_assets.dart +++ b/lib/core/app_assets.dart @@ -110,4 +110,5 @@ class AppAnimations { static const String login = '$lottieBasePath/login.json'; static const String register = '$lottieBasePath/register.json'; static const String checkmark = '$lottieBasePath/checkmark.json'; + static const String loadingAnimation = '$lottieBasePath/Loader.json'; } diff --git a/lib/core/app_state.dart b/lib/core/app_state.dart index 008aeb8..b94aff4 100644 --- a/lib/core/app_state.dart +++ b/lib/core/app_state.dart @@ -1,4 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; +import 'package:hmg_patient_app_new/core/common_models/VidaPlusProjectListModel.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; import 'package:hmg_patient_app_new/features/authentication/models/resp_models/select_device_by_imei.dart'; import 'package:hmg_patient_app_new/services/navigation_service.dart'; @@ -46,6 +47,10 @@ class AppState { set setDeviceToken(v) => deviceToken = v; + String voipToken = ""; + + set setVoipToken(v) => voipToken = v; + String appAuthToken = ""; set setAppAuthToken(v) => appAuthToken = v; @@ -61,4 +66,10 @@ class AppState { String deviceTypeID = ""; set setDeviceTypeID(v) => deviceTypeID = v; + + List vidaPlusProjectList = []; + + setVidaPlusProjectList(List vidaPlusProjectListModelInput) { + vidaPlusProjectList = vidaPlusProjectListModelInput; + } } diff --git a/lib/core/common_models/VidaPlusProjectListModel.dart b/lib/core/common_models/VidaPlusProjectListModel.dart new file mode 100644 index 0000000..7597811 --- /dev/null +++ b/lib/core/common_models/VidaPlusProjectListModel.dart @@ -0,0 +1,15 @@ +class VidaPlusProjectListModel { + int? projectID; + + VidaPlusProjectListModel({this.projectID}); + + VidaPlusProjectListModel.fromJson(Map json) { + projectID = json['ProjectID']; + } + + Map toJson() { + final Map data = new Map(); + data['ProjectID'] = this.projectID; + return data; + } +} diff --git a/lib/core/common_models/tamara_request_model.dart b/lib/core/common_models/tamara_request_model.dart new file mode 100644 index 0000000..f515aae --- /dev/null +++ b/lib/core/common_models/tamara_request_model.dart @@ -0,0 +1,120 @@ +class TamaraRequestModel { + String? merchantReference; + String? merchantIdentifier; + String? clientRequestID; + num? amount; + String? currency; + String? language; + String? commandType; + String? signature; + String? customerEmail; + String? orderDescription; + bool? isInstallment; + num? projectID; + String? accessCode; + String? sHARequestPhase; + String? sHAResponsePhase; + String? customerName; + String? fileNumber; + bool? patientOutSA; + String? deviceToken; + String? longitude; + String? latitude; + String? appointmentNo; + var serviceID; + var liveServiceID; + String? doctorID; + var appointmentDate; + bool? isSchedule; + + TamaraRequestModel( + {this.merchantReference, + this.merchantIdentifier, + this.clientRequestID, + this.amount, + this.currency, + this.language, + this.commandType, + this.signature, + this.customerEmail, + this.orderDescription, + this.isInstallment, + this.projectID, + this.accessCode, + this.sHARequestPhase, + this.sHAResponsePhase, + this.customerName, + this.fileNumber, + this.patientOutSA, + this.deviceToken, + this.longitude, + this.latitude, + this.appointmentNo, + this.serviceID, + this.liveServiceID, + this.appointmentDate, + this.doctorID, + this.isSchedule}); + + TamaraRequestModel.fromJson(Map json) { + merchantReference = json['Merchant_Reference']; + merchantIdentifier = json['Merchant_Identifier']; + clientRequestID = json['ClientRequestID']; + amount = json['Amount']; + currency = json['Currency']; + language = json['Language']; + commandType = json['CommandType']; + signature = json['Signature']; + customerEmail = json['CustomerEmail']; + orderDescription = json['OrderDescription']; + isInstallment = json['IsInstallment']; + projectID = json['ProjectID']; + accessCode = json['Access_code']; + sHARequestPhase = json['SHA_RequestPhase']; + sHAResponsePhase = json['SHA_ResponsePhase']; + customerName = json['CustomerName']; + fileNumber = json['FileNumber']; + patientOutSA = json['PatientOutSA']; + deviceToken = json['DeviceToken']; + longitude = json['Longitude']; + latitude = json['Latitude']; + appointmentNo = json['AppointmentNo']; + serviceID = json['Service_ID']; + liveServiceID = json['Live_ServiceID']; + doctorID = json['DoctorID']; + appointmentDate = json['AppointmentDate']; + isSchedule = json['IsSchedule']; + } + + Map toJson() { + final Map data = new Map(); + data['Merchant_Reference'] = this.merchantReference; + data['Merchant_Identifier'] = this.merchantIdentifier; + data['ClientRequestID'] = this.clientRequestID; + data['Amount'] = this.amount; + data['Currency'] = this.currency; + data['Language'] = this.language; + data['CommandType'] = this.commandType; + data['Signature'] = this.signature; + data['CustomerEmail'] = this.customerEmail; + data['OrderDescription'] = this.orderDescription; + data['IsInstallment'] = this.isInstallment; + data['ProjectID'] = this.projectID; + data['Access_code'] = this.accessCode; + data['SHA_RequestPhase'] = this.sHARequestPhase; + data['SHA_ResponsePhase'] = this.sHAResponsePhase; + data['CustomerName'] = this.customerName; + data['FileNumber'] = this.fileNumber; + data['PatientOutSA'] = this.patientOutSA; + data['DeviceToken'] = this.deviceToken; + data['Longitude'] = this.longitude; + data['Latitude'] = this.latitude; + data['AppointmentNo'] = this.appointmentNo; + data['Service_ID'] = this.serviceID; + data['Live_ServiceID'] = this.liveServiceID; + data['DoctorID'] = this.doctorID; + data['AppointmentDate'] = this.appointmentDate; + data['IsSchedule'] = this.isSchedule; + return data; + } +} diff --git a/lib/core/dependencies.dart b/lib/core/dependencies.dart index 47345b4..b6e72f6 100644 --- a/lib/core/dependencies.dart +++ b/lib/core/dependencies.dart @@ -13,6 +13,8 @@ import 'package:hmg_patient_app_new/features/lab/lab_repo.dart'; import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_repo.dart'; import 'package:hmg_patient_app_new/features/my_appointments/my_appointments_view_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart'; +import 'package:hmg_patient_app_new/features/payfort/payfort_view_model.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_repo.dart'; import 'package:hmg_patient_app_new/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_repo.dart'; @@ -77,6 +79,7 @@ class AppDependencies { getIt.registerLazySingleton(() => RadiologyRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => PrescriptionsRepoImp(loggerService: getIt(), apiClient: getIt())); getIt.registerLazySingleton(() => InsuranceRepoImp(loggerService: getIt(), apiClient: getIt())); + getIt.registerLazySingleton(() => PayfortRepoImp(loggerService: getIt(), apiClient: getIt())); // ViewModels // Global/shared VMs → LazySingleton @@ -116,6 +119,13 @@ class AppDependencies { ), ); + getIt.registerLazySingleton( + () => PayfortViewModel( + payfortRepo: getIt(), + errorHandlerService: getIt(), + ), + ); + getIt.registerLazySingleton( () => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/core/enums.dart b/lib/core/enums.dart index ab5cac1..0ab055f 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -104,3 +104,49 @@ extension OTPTypeEnumExtension on OTPTypeEnum { } } } + +enum ServiceTypeEnum { + advancePayment, //3 + ancillaryOrder, //3 + appointmentPayment, //2 + covidPayment, //2 + erOnlineCheckIn, //3 + liveCareAppointment //4 +} + +extension ServiceTypeEnumExt on ServiceTypeEnum { + String value() { + switch (this) { + case ServiceTypeEnum.advancePayment: + return "Advance Payment"; + case ServiceTypeEnum.ancillaryOrder: + return "Ancillary Order"; + case ServiceTypeEnum.appointmentPayment: + return "Appointment Payment"; + case ServiceTypeEnum.covidPayment: + return "Covid Payment"; + case ServiceTypeEnum.erOnlineCheckIn: + return "ER Online Check In"; + case ServiceTypeEnum.liveCareAppointment: + return "LiveCare Appointment"; + } + } + + int getIdFromServiceEnum() { + switch (this) { + case ServiceTypeEnum.advancePayment: + return 3; + case ServiceTypeEnum.ancillaryOrder: + return 3; + case ServiceTypeEnum.appointmentPayment: + return 2; + case ServiceTypeEnum.covidPayment: + return 2; + case ServiceTypeEnum.erOnlineCheckIn: + return 3; + case ServiceTypeEnum.liveCareAppointment: + return 4; + } + } +} + diff --git a/lib/core/utils/utils.dart b/lib/core/utils/utils.dart index d060489..276151c 100644 --- a/lib/core/utils/utils.dart +++ b/lib/core/utils/utils.dart @@ -297,6 +297,29 @@ class Utils { ).center; } + static Widget getLoadingWidget({String? loadingText}) { + return Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Lottie.asset(AppAnimations.loadingAnimation, repeat: true, reverse: false, frameRate: FrameRate(60), width: 100.h, height: 100.h, fit: BoxFit.fill), + SizedBox(height: 8.h), + (loadingText ?? LocaleKeys.loadingText.tr()).toText16(color: AppColors.blackColor), + SizedBox(height: 8.h), + ], + ).center; + } + + static bool isVidaPlusProject(AppState appState, int projectID) { + bool isVidaPlus = false; + for (var element in appState.vidaPlusProjectList) { + if (element.projectID == projectID) { + isVidaPlus = true; + } + } + return isVidaPlus; + } + static getPhoneNumberWithoutZero(String number) { String newNumber = ""; if (number.startsWith('0')) { diff --git a/lib/features/my_appointments/my_appointments_repo.dart b/lib/features/my_appointments/my_appointments_repo.dart index 9a0528d..5a55909 100644 --- a/lib/features/my_appointments/my_appointments_repo.dart +++ b/lib/features/my_appointments/my_appointments_repo.dart @@ -13,6 +13,20 @@ abstract class MyAppointmentsRepo { Future>> getPatientShareAppointment( {required String patientId, required int projectID, required int clinicID, required String appointmentNo}); + + Future>> createAdvancePayment( + {required String paymentMethodName, + required int projectID, + required int clinicID, + required String appointmentNo, + required num payedAmount, + required String paymentReference, + required String patientID, + required int patientType}); + + Future>> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference, required String appointmentNo}); + + Future>> generateAppointmentQR({required String clinicID, required String projectID, required String appointmentNo, required int isFollowUp}); } class MyAppointmentsRepoImp implements MyAppointmentsRepo { @@ -105,7 +119,6 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { final patientShareObj = PatientAppointmentShareResponseModel.fromJson(list[0]); patientShareObj.isCash = response["IsCash"]; - // patientShareObj.isCash = false; patientShareObj.isEligible = response["IsEligible"]; patientShareObj.isInsured = response["IsInsured"]; @@ -129,4 +142,139 @@ class MyAppointmentsRepoImp implements MyAppointmentsRepo { throw UnimplementedError(); } + + @override + Future> createAdvancePayment({ + required String paymentMethodName, + required int projectID, + required int clinicID, + required String appointmentNo, + required num payedAmount, + required String paymentReference, + required String patientID, + required int patientType, + }) async { + final requestBody = { + "ProjectID": projectID, + "OnlineCheckInAppointment": { + "AppointmentNo": appointmentNo, + "PaymentMethodName": paymentMethodName, + "PaymentAmount": payedAmount == 0 ? "0" : payedAmount.toString(), + "PaymentDate": payedAmount == 0 ? "" : "/Date(${DateTime.now().millisecondsSinceEpoch})/", + "PaymentReferenceNumber": payedAmount == 0 ? "" : paymentReference, + "ProjectID": projectID, + "PatientID": patientID, + "ClinicID": clinicID, + "UserID": patientID, + "Status": patientType + } + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + CREATE_ADVANCE_PAYMENT, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future> addAdvanceNumberRequest({required String advanceNumber, required String paymentReference, required String appointmentNo}) async { + final requestBody = { + "AdvanceNumber": advanceNumber, + "AdvanceNumber_VP": advanceNumber, + "PaymentReferenceNumber": paymentReference, + "AppointmentID": appointmentNo, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + ADD_ADVANCE_NUMBER_REQUEST, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future> generateAppointmentQR({required String clinicID, required String projectID, required String appointmentNo, required int isFollowUp}) async { + final requestBody = { + "AppointmentNo": appointmentNo, + "ClinicID": clinicID, + "ProjectID": projectID, + "IsFollowup": isFollowUp, + }; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post( + GENERATE_QR_APPOINTMENT, + body: requestBody, + onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, + onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, + ); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } } diff --git a/lib/features/my_appointments/my_appointments_view_model.dart b/lib/features/my_appointments/my_appointments_view_model.dart index 0c163ca..439a8e8 100644 --- a/lib/features/my_appointments/my_appointments_view_model.dart +++ b/lib/features/my_appointments/my_appointments_view_model.dart @@ -87,4 +87,78 @@ class MyAppointmentsViewModel extends ChangeNotifier { }, ); } + + Future addAdvanceNumberRequest( + {required String advanceNumber, required String paymentReference, required String appointmentNo, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.addAdvanceNumberRequest(advanceNumber: advanceNumber, paymentReference: paymentReference, appointmentNo: appointmentNo); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future generateAppointmentQR( + {required String clinicID, required String projectID, required String appointmentNo, required int isFollowUp, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await myAppointmentsRepo.generateAppointmentQR(clinicID: clinicID, projectID: projectID, appointmentNo: appointmentNo, isFollowUp: isFollowUp); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future createAdvancePayment( + {required String paymentMethodName, + required int projectID, + required int clinicID, + required String appointmentNo, + required num payedAmount, + required String paymentReference, + required String patientID, + required int patientType, + Function(dynamic)? onSuccess, + Function(String)? onError}) async { + final result = await myAppointmentsRepo.createAdvancePayment( + paymentMethodName: paymentMethodName, + projectID: projectID, + clinicID: clinicID, + appointmentNo: appointmentNo, + payedAmount: payedAmount, + paymentReference: paymentReference, + patientID: patientID, + patientType: patientType); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } } diff --git a/lib/features/payfort/models/apple_pay_request_insert_model.dart b/lib/features/payfort/models/apple_pay_request_insert_model.dart new file mode 100644 index 0000000..6a7f1ac --- /dev/null +++ b/lib/features/payfort/models/apple_pay_request_insert_model.dart @@ -0,0 +1,187 @@ +class ApplePayInsertRequest { + String? clientRequestID; + int? clinicID; + String? currency; + String? customerEmail; + dynamic customerID; + String? customerName; + String? deviceToken; + String? voipToken; + int? doctorID; + String? projectID; + String? serviceID; + int? channelID; + dynamic patientID; + int? patientTypeID; + int? patientOutSA; + dynamic appointmentDate; + int? appointmentNo; + String? orderDescription; + String? liveServiceID; + String? latitude; + String? longitude; + String? amount; + String? isSchedule; + String? language; + int? userName; + String? responseContinueURL; + String? backClickUrl; + String? paymentOption; + double? versionID; + int? channel; + int? languageID; + String? iPAdress; + String? generalid; + String? sessionID; + bool? isDentalAllowedBackend; + int? deviceTypeID; + bool? isMobSDK; + String? merchantReference; + String? merchantIdentifier; + String? commandType; + String? signature; + String? accessCode; + String? shaRequestPhrase; + String? shaResponsePhrase; + String? returnURL; + + ApplePayInsertRequest( + {this.clientRequestID, + this.clinicID, + this.currency, + this.customerEmail, + this.customerID, + this.customerName, + this.deviceToken, + this.voipToken, + this.doctorID, + this.projectID, + this.serviceID, + this.channelID, + this.patientID, + this.patientTypeID, + this.patientOutSA, + this.appointmentDate, + this.appointmentNo, + this.orderDescription, + this.liveServiceID, + this.latitude, + this.longitude, + this.amount, + this.isSchedule, + this.language, + this.userName, + this.responseContinueURL, + this.backClickUrl, + this.paymentOption, + this.versionID, + this.channel, + this.languageID, + this.iPAdress, + this.generalid, + this.sessionID, + this.isDentalAllowedBackend, + this.deviceTypeID, + this.isMobSDK, + this.merchantReference, + this.merchantIdentifier, + this.commandType, + this.signature, + this.accessCode, + this.shaRequestPhrase, + this.shaResponsePhrase, + this.returnURL, + }); + + ApplePayInsertRequest.fromJson(Map json) { + clientRequestID = json['ClientRequestID']; + clinicID = json['ClinicID']; + currency = json['Currency']; + customerEmail = json['CustomerEmail']; + customerID = json['CustomerID']; + customerName = json['CustomerName']; + deviceToken = json['DeviceToken']; + voipToken = json['VoipToken']; + doctorID = json['DoctorID']; + projectID = json['ProjectID']; + serviceID = json['Service_ID']; + channelID = json['Channel_ID']; + patientID = json['PatientID']; + patientTypeID = json['PatientTypeID']; + patientOutSA = json['PatientOutSA']; + appointmentDate = json['AppointmentDate']; + appointmentNo = json['AppointmentNo']; + orderDescription = json['OrderDescription']; + liveServiceID = json['Live_ServiceID']; + latitude = json['Latitude']; + longitude = json['Longitude']; + amount = json['Amount']; + isSchedule = json['IsSchedule']; + language = json['Language']; + userName = json['userName']; + responseContinueURL = json['ResponseContinueURL']; + backClickUrl = json['BackClickUrl']; + paymentOption = json['PaymentOption']; + versionID = json['VersionID']; + channel = json['Channel']; + languageID = json['LanguageID']; + iPAdress = json['IPAdress']; + generalid = json['generalid']; + sessionID = json['SessionID']; + isDentalAllowedBackend = json['isDentalAllowedBackend']; + deviceTypeID = json['DeviceTypeID']; + } + + Map toJson() { + final Map data = new Map(); + data['ClientRequestID'] = this.clientRequestID; + data['ClinicID'] = this.clinicID; + data['Currency'] = this.currency; + data['CustomerEmail'] = this.customerEmail; + data['CustomerID'] = this.customerID; + data['CustomerName'] = this.customerName; + data['DeviceToken'] = this.deviceToken; + data['VoipToken'] = this.voipToken; + data['DoctorID'] = this.doctorID; + data['ProjectID'] = this.projectID; + data['Service_ID'] = this.serviceID; + data['Channel_ID'] = this.channelID; + data['PatientID'] = this.patientID; + data['PatientId'] = this.patientID; + data['PatientTypeID'] = this.patientTypeID; + data['PatientOutSA'] = this.patientOutSA; + data['AppointmentDate'] = this.appointmentDate; + data['AppointmentNo'] = this.appointmentNo; + data['OrderDescription'] = this.orderDescription; + data['Live_ServiceID'] = this.liveServiceID; + data['Latitude'] = this.latitude; + data['Longitude'] = this.longitude; + data['Amount'] = this.amount; + data['IsSchedule'] = this.isSchedule; + data['Language'] = this.language; + data['userName'] = this.userName; + data['ResponseContinueURL'] = this.responseContinueURL; + data['BackClickUrl'] = this.backClickUrl; + data['PaymentOption'] = this.paymentOption; + data['VersionID'] = this.versionID; + data['Channel'] = this.channel; + data['LanguageID'] = this.languageID; + data['IPAdress'] = this.iPAdress; + data['generalid'] = this.generalid; + data['SessionID'] = this.sessionID; + data['isDentalAllowedBackend'] = this.isDentalAllowedBackend; + data['DeviceTypeID'] = this.deviceTypeID; + + data['IsMobSDK'] = this.isMobSDK; + data['Merchant_Reference'] = this.merchantReference; + data['Merchant_Identifier'] = this.merchantIdentifier; + data['CommandType'] = this.commandType; + data['Signature'] = this.signature; + data['Access_code'] = this.accessCode; + data['SHA_RequestPhase'] = this.shaRequestPhrase; + data['SHA_ResponsePhase'] = this.shaResponsePhrase; + data['ReturnURL'] = this.returnURL; + + return data; + } +} diff --git a/lib/features/payfort/models/payfort_check_payment_status_response_model.dart b/lib/features/payfort/models/payfort_check_payment_status_response_model.dart new file mode 100644 index 0000000..78603b7 --- /dev/null +++ b/lib/features/payfort/models/payfort_check_payment_status_response_model.dart @@ -0,0 +1,60 @@ +class PayfortCheckPaymentStatusResponseModel { + num? amount; + String? cardNumber; + dynamic clientRequsetID; + dynamic errorMessage; + String? fortId; + String? merchantReference; + dynamic orderDescription; + dynamic patientFileNumber; + String? paymentMethod; + dynamic rRN; + String? responseMessage; + dynamic vidaAdvancedNumber; + + PayfortCheckPaymentStatusResponseModel( + {this.amount, + this.cardNumber, + this.clientRequsetID, + this.errorMessage, + this.fortId, + this.merchantReference, + this.orderDescription, + this.patientFileNumber, + this.paymentMethod, + this.rRN, + this.responseMessage, + this.vidaAdvancedNumber}); + + PayfortCheckPaymentStatusResponseModel.fromJson(Map json) { + amount = json['Amount']; + cardNumber = json['CardNumber']; + clientRequsetID = json['ClientRequsetID']; + errorMessage = json['ErrorMessage']; + fortId = json['Fort_id']; + merchantReference = json['Merchant_Reference']; + orderDescription = json['OrderDescription']; + patientFileNumber = json['PatientFileNumber']; + paymentMethod = json['PaymentMethod']; + rRN = json['RRN']; + responseMessage = json['Response_Message']; + vidaAdvancedNumber = json['VidaAdvancedNumber']; + } + + Map toJson() { + final Map data = new Map(); + data['Amount'] = this.amount; + data['CardNumber'] = this.cardNumber; + data['ClientRequsetID'] = this.clientRequsetID; + data['ErrorMessage'] = this.errorMessage; + data['Fort_id'] = this.fortId; + data['Merchant_Reference'] = this.merchantReference; + data['OrderDescription'] = this.orderDescription; + data['PatientFileNumber'] = this.patientFileNumber; + data['PaymentMethod'] = this.paymentMethod; + data['RRN'] = this.rRN; + data['Response_Message'] = this.responseMessage; + data['VidaAdvancedNumber'] = this.vidaAdvancedNumber; + return data; + } +} diff --git a/lib/features/payfort/models/payfort_project_details_resp_model.dart b/lib/features/payfort/models/payfort_project_details_resp_model.dart new file mode 100644 index 0000000..b4d9eae --- /dev/null +++ b/lib/features/payfort/models/payfort_project_details_resp_model.dart @@ -0,0 +1,37 @@ +class PayfortProjectDetailsRespModel { + String? accessCode; + int? integrationId; + String? merchantIdentifier; + int? projectID; + String? projectName; + int? servID; + String? shaRequest; + String? shaResponse; + String? signature; + + PayfortProjectDetailsRespModel({this.accessCode, this.integrationId, this.merchantIdentifier, this.projectID, this.projectName, this.servID, this.shaRequest, this.shaResponse, this.signature}); + + PayfortProjectDetailsRespModel.fromJson(Map json) { + accessCode = json['AccessCode']; + integrationId = json['Integration_Id']; + merchantIdentifier = json['MerchantIdentifier']; + projectID = json['ProjectID']; + projectName = json['ProjectName']; + servID = json['ServID']; + shaRequest = json['Sha_Request']; + shaResponse = json['Sha_Response']; + } + + Map toJson() { + final Map data = new Map(); + data['AccessCode'] = this.accessCode; + data['Integration_Id'] = this.integrationId; + data['MerchantIdentifier'] = this.merchantIdentifier; + data['ProjectID'] = this.projectID; + data['ProjectName'] = this.projectName; + data['ServID'] = this.servID; + data['Sha_Request'] = this.shaRequest; + data['Sha_Response'] = this.shaResponse; + return data; + } +} diff --git a/lib/features/payfort/models/sdk_token_response_model.dart b/lib/features/payfort/models/sdk_token_response_model.dart new file mode 100644 index 0000000..a0f48a4 --- /dev/null +++ b/lib/features/payfort/models/sdk_token_response_model.dart @@ -0,0 +1,55 @@ +class SdkTokenResponse { + SdkTokenResponse({ + this.responseCode, + this.deviceId, + this.responseMessage, + this.serviceCommand, + this.sdkToken, + this.signature, + this.merchantIdentifier, + this.accessCode, + this.language, + this.status, + }); + + String? responseCode; + String? deviceId; + String? responseMessage; + String? serviceCommand; + String? sdkToken; + String? signature; + String? merchantIdentifier; + String? accessCode; + String? language; + String? status; + + factory SdkTokenResponse.fromMap(Map data) { + return SdkTokenResponse( + responseCode: data['response_code'], + deviceId: data['device_id'], + responseMessage: data['response_message'], + serviceCommand: data['service_command'], + sdkToken: data['sdk_token'], + signature: data['signature'], + merchantIdentifier: data['merchant_identifier'], + accessCode: data['access_code'], + language: data['language'], + status: data['status'], + ); + } + + Map toMap() { + return { + 'response_code': responseCode, + 'device_id': deviceId, + 'response_message': responseMessage, + 'service_command': serviceCommand, + 'sdk_token': sdkToken, + 'signature': signature, + 'merchant_identifier': merchantIdentifier, + 'access_code': accessCode, + 'language': language, + 'status': status, + }; + } +} diff --git a/lib/features/payfort/payfort_repo.dart b/lib/features/payfort/payfort_repo.dart new file mode 100644 index 0000000..565422b --- /dev/null +++ b/lib/features/payfort/payfort_repo.dart @@ -0,0 +1,150 @@ +import 'package:amazon_payfort/amazon_payfort.dart'; +import 'package:dartz/dartz.dart'; +import 'package:hmg_patient_app_new/core/api/api_client.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/core/common_models/generic_api_model.dart'; +import 'package:hmg_patient_app_new/core/exceptions/api_failure.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/payfort_project_details_resp_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/sdk_token_response_model.dart'; +import 'package:hmg_patient_app_new/services/logger_service.dart'; + +abstract class PayfortRepo { + Future>> getPayfortConfigurations({int? serviceId, int? projectId, int integrationId = 2}); + + Future>> applePayRequestInsert({required ApplePayInsertRequest applePayInsertRequest}); + + Future>> generateSdkSignatureFromAPI({required SdkTokenRequest tokenRequest}); + + Future>> checkPaymentStatus({required String transactionID}); +} + +class PayfortRepoImp implements PayfortRepo { + final ApiClient apiClient; + final LoggerService loggerService; + + PayfortRepoImp({required this.loggerService, required this.apiClient}); + + @override + Future>> getPayfortConfigurations({int? serviceId, int? projectId, int integrationId = 2}) async { + Map body = {"Integration_Id": integrationId, "ServID": serviceId, "ProjectID": projectId}; + + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post(getPayFortProjectDetails, body: body, onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final list = response; + if (list == null || list.isEmpty) { + throw Exception("payfort list is empty"); + } + + final applePayConfigurationResponse = PayfortProjectDetailsRespModel.fromJson(list[0]); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: applePayConfigurationResponse, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, isAllowAny: true); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future> applePayRequestInsert({required ApplePayInsertRequest applePayInsertRequest}) async { + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post(APPLE_PAY_INSERT_REQUEST, body: applePayInsertRequest.toJson(), onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: response["result"], + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, isAllowAny: true); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>> generateSdkSignatureFromAPI({required SdkTokenRequest tokenRequest}) async { + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post(ApiConsts.payFortEnvironment.paymentApi, body: tokenRequest.asRequest(), onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final sdkTokenResponse = SdkTokenResponse.fromMap(response); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: sdkTokenResponse, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, isAllowAny: true, isExternal: true); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } + + @override + Future>> checkPaymentStatus({required String transactionID}) async { + Map body = {"ClientRequestID": transactionID, "IsPharmacy": false}; + try { + GenericApiModel? apiResponse; + Failure? failure; + await apiClient.post(CHECK_PAYMENT_STATUS, body: body, onFailure: (error, statusCode, {messageStatus, failureType}) { + failure = failureType; + }, onSuccess: (response, statusCode, {messageStatus, errorMessage}) { + try { + final paymentStatusResponse = PayfortCheckPaymentStatusResponseModel.fromJson(response); + + apiResponse = GenericApiModel( + messageStatus: messageStatus, + statusCode: statusCode, + errorMessage: null, + data: paymentStatusResponse, + ); + } catch (e) { + failure = DataParsingFailure(e.toString()); + } + }, isAllowAny: true); + if (failure != null) return Left(failure!); + if (apiResponse == null) return Left(ServerFailure("Unknown error")); + return Right(apiResponse!); + } catch (e) { + return Left(UnknownFailure(e.toString())); + } + } +} diff --git a/lib/features/payfort/payfort_view_model.dart b/lib/features/payfort/payfort_view_model.dart new file mode 100644 index 0000000..9c9df6a --- /dev/null +++ b/lib/features/payfort/payfort_view_model.dart @@ -0,0 +1,198 @@ +import 'package:amazon_payfort/amazon_payfort.dart'; +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/api_consts.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/apple_pay_request_insert_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/payfort_check_payment_status_response_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/payfort_project_details_resp_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/models/sdk_token_response_model.dart'; +import 'package:hmg_patient_app_new/features/payfort/payfort_repo.dart'; +import 'package:hmg_patient_app_new/services/error_handler_service.dart'; +import 'package:network_info_plus/network_info_plus.dart'; + +class PayfortViewModel extends ChangeNotifier { + PayfortRepo payfortRepo; + ErrorHandlerService errorHandlerService; + + bool isApplePayConfigurationLoading = false; + + PayfortProjectDetailsRespModel? payfortProjectDetailsRespModel; + PayfortCheckPaymentStatusResponseModel? payfortCheckPaymentStatusResponseModel; + late AmazonPayfort _payfort; + final NetworkInfo _info = NetworkInfo(); + + PayfortViewModel({required this.payfortRepo, required this.errorHandlerService}); + + setIsApplePayConfigurationLoading(bool value) { + isApplePayConfigurationLoading = value; + notifyListeners(); + } + + initPayfortViewModel() async { + _payfort = AmazonPayfort.instance; + await AmazonPayfort.initialize( + PayFortOptions(environment: ApiConsts.payFortEnvironment), + ); + notifyListeners(); + } + + Future getPayfortConfigurations({int? serviceId, int? projectId, int integrationId = 2, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await payfortRepo.getPayfortConfigurations(serviceId: serviceId, projectId: projectId, integrationId: integrationId); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + payfortProjectDetailsRespModel = apiResponse.data!; + // isApplePayConfigurationLoading = false; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future applePayRequestInsert({required ApplePayInsertRequest applePayInsertRequest, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await payfortRepo.applePayRequestInsert(applePayInsertRequest: applePayInsertRequest); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + // payfortProjectDetailsRespModel = apiResponse.data!; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future checkPaymentStatus({required String transactionID, Function(dynamic)? onSuccess, Function(String)? onError}) async { + final result = await payfortRepo.checkPaymentStatus(transactionID: transactionID); + + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + payfortCheckPaymentStatusResponseModel = apiResponse.data!; + notifyListeners(); + if (onSuccess != null) { + onSuccess(apiResponse); + } + } + }, + ); + } + + Future _generateSdkResponse({ + String? applePayAccessCode, + String? merchantIdentifier, + String? applePayShaType, + String? applePayShaRequestPhrase, + }) async { + try { + String? deviceId = await _payfort.getDeviceId(); + + /// Step 2: Generate the Signature + SdkTokenRequest tokenRequest = SdkTokenRequest( + accessCode: applePayAccessCode!, + deviceId: deviceId ?? '', + merchantIdentifier: merchantIdentifier!, + ); + + String? signature = await _payfort.generateSignature( + shaType: applePayShaType!, + concatenatedString: tokenRequest.toConcatenatedString(applePayShaRequestPhrase!), + ); + + tokenRequest = tokenRequest.copyWith(signature: signature); + + /// Step 3: Generate the SDK Token + final result = await payfortRepo.generateSdkSignatureFromAPI(tokenRequest: tokenRequest); + result.fold( + (failure) async => await errorHandlerService.handleError(failure: failure), + (apiResponse) { + if (apiResponse.messageStatus == 2) { + // dialogService.showErrorDialog(message: apiResponse.errorMessage!, onOkPressed: () {}); + } else if (apiResponse.messageStatus == 1) { + // payfortProjectDetailsRespModel = apiResponse.data!; + isApplePayConfigurationLoading = false; + notifyListeners(); + } + }, + ); + } catch (e) { + print("Error here: ${e.toString()}"); + } + return null; + } + + Future paymentWithApplePay({ + SucceededCallback? onSucceeded, + FailedCallback? onFailed, + String? customerName, + String? customerEmail, + String? orderDescription, + num? orderAmount, + String? merchantIdentifier, + String? applePayAccessCode, + String? applePayShaRequestPhrase, + String? merchantReference, + String currency = "SAR", + String applePayShaType = "SHA-256", + String countryIsoCode = "SA", + }) async { + try { + SdkTokenResponse? sdkTokenResponse = await _generateSdkResponse( + applePayAccessCode: applePayAccessCode, + merchantIdentifier: merchantIdentifier, + applePayShaType: applePayShaType, + applePayShaRequestPhrase: applePayShaRequestPhrase, + ); + + if (sdkTokenResponse != null && sdkTokenResponse.sdkToken == null) { + onFailed!((sdkTokenResponse.responseMessage ?? '') as PayFortFailureResult); + return; + } + + /// Step 4: Processing Payment [Don't multiply with 100] + /// Amount value send always round ex. [100] not [100.00, 100.21] + FortRequest request = FortRequest( + command: FortCommand.purchase, + amount: orderAmount!, + customerName: customerName!, + customerEmail: customerEmail!, + // orderDescription: orderDescription!, + orderDescription: "Dr. Sulaiman Al Habib Hospital", + sdkToken: sdkTokenResponse?.sdkToken ?? '', + merchantReference: merchantReference!, + currency: currency, + customerIp: (await _info.getWifiIP() ?? ''), + language: 'en'); + + isApplePayConfigurationLoading = false; + notifyListeners(); + + _payfort.callPayFortForApplePay( + request: request, + countryIsoCode: countryIsoCode, + applePayMerchantId: ApiConsts.applePayMerchantId, + callback: ApplePayResultCallback( + onSucceeded: onSucceeded!, + onFailed: onFailed!, + ), + ); + } catch (e) { + onFailed!(e.toString() as PayFortFailureResult); + } + } +} diff --git a/lib/main.dart b/lib/main.dart index a43959e..2a8ef63 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -13,6 +13,7 @@ import 'package:hmg_patient_app_new/features/insurance/insurance_view_model.dart import 'package:hmg_patient_app_new/features/lab/lab_view_model.dart'; import 'package:hmg_patient_app_new/features/medical_file/medical_file_view_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/features/prescriptions/prescriptions_view_model.dart'; import 'package:hmg_patient_app_new/features/radiology/radiology_view_model.dart'; import 'package:hmg_patient_app_new/routes/app_routes.dart'; @@ -102,6 +103,12 @@ void main() async { errorHandlerService: getIt(), ), ), + ChangeNotifierProvider( + create: (_) => PayfortViewModel( + payfortRepo: getIt(), + errorHandlerService: getIt(), + ), + ), ChangeNotifierProvider( create: (_) => AuthenticationViewModel( authenticationRepo: getIt(), diff --git a/lib/presentation/appointments/appointment_payment_page.dart b/lib/presentation/appointments/appointment_payment_page.dart index 4da2a2d..d6bdc36 100644 --- a/lib/presentation/appointments/appointment_payment_page.dart +++ b/lib/presentation/appointments/appointment_payment_page.dart @@ -1,17 +1,29 @@ 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/authentication/models/resp_models/authenticated_user_resp_model.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/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/shimmer/movies_shimmer_widget.dart'; import 'package:provider/provider.dart'; import 'package:smooth_corner/smooth_corner.dart'; @@ -27,10 +39,18 @@ class AppointmentPaymentPage extends StatefulWidget { class _AppointmentPaymentPageState extends State { late MyAppointmentsViewModel myAppointmentsViewModel; + late PayfortViewModel payfortViewModel; + late AppState appState; + + MyInAppBrowser? browser; + String selectedPaymentMethod = ""; + + String transID = ""; @override void initState() { scheduleMicrotask(() { + payfortViewModel.initPayfortViewModel(); myAppointmentsViewModel.getPatientShareAppointment( widget.patientAppointmentHistoryResponseModel.projectID, widget.patientAppointmentHistoryResponseModel.clinicID, @@ -42,7 +62,9 @@ class _AppointmentPaymentPageState extends State { @override Widget build(BuildContext context) { + appState = getIt.get(); myAppointmentsViewModel = Provider.of(context); + payfortViewModel = Provider.of(context); return Scaffold( backgroundColor: AppColors.bgScaffoldColor, appBar: AppBar( @@ -89,7 +111,10 @@ class _AppointmentPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "MADA"; + openPaymentURL("mada"); + }), SizedBox(height: 16.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -125,7 +150,10 @@ class _AppointmentPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "VISA"; + openPaymentURL("visa"); + }), SizedBox(height: 16.h), Container( decoration: RoundedRectangleBorder().toSmoothCornerDecoration( @@ -155,94 +183,312 @@ class _AppointmentPaymentPageState extends State { ), ], ).paddingSymmetrical(16.h, 16.h), - ).paddingSymmetrical(24.h, 0.h), + ).paddingSymmetrical(24.h, 0.h).onPress(() { + selectedPaymentMethod = "TAMARA"; + openPaymentURL("tamara"); + }), ], ), ), ), Container( - // height: 200.h, - // width: double.infinity, decoration: RoundedRectangleBorder().toSmoothCornerDecoration( color: AppColors.whiteColor, borderRadius: 24.h, hasShadow: false, ), - child: 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( + child: Consumer(builder: (context, payfortVM, child) { + //TODO: Need to add loading state & animation for Apple Pay Configuration + return payfortVM.isApplePayConfigurationLoading + ? const MoviesShimmerWidget().paddingAll(16.h) + : 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: () {}, + 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: [ - "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: () {}, - 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), + "Total amount to pay".needTranslation.toText14(isBold: true), + Utils.getPaymentAmountWithSymbol(myAppointmentsVM.patientAppointmentShareResponseModel!.patientShare!.toString().toText16(isBold: true), AppColors.blackColor, 13, + isSaudiCurrency: true), ], - ), - ) - : 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: [ - "Total amount to pay".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), - Utils.buildSvgWithAssets( - icon: AppAssets.apple_pay_button, - width: 200.h, - height: 80.h, - fit: BoxFit.contain, - ).paddingSymmetrical(24.h, 0.h), - SizedBox(height: 12.h), - ], - ), + ).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), + //TODO: Add Apple Pay Privileges + 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); + startApplePay(); + }), + 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 { + showCommonBottomSheet(context, + child: Utils.getLoadingWidget(), callBackFunc: () {}, title: "", height: ResponsiveExtension.screenHeight * 0.3, isCloseButtonVisible: false, isDismissible: false, isFullScreen: false); + 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: "4767477", + patientType: 1, + onSuccess: (value) async { + print(value); + await myAppointmentsViewModel.addAdvanceNumberRequest( + advanceNumber: Utils.isVidaPlusProject(appState, 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 + } else { + await myAppointmentsViewModel.generateAppointmentQR( + clinicID: widget.patientAppointmentHistoryResponseModel.clinicID, + projectID: widget.patientAppointmentHistoryResponseModel.projectID, + appointmentNo: widget.patientAppointmentHistoryResponseModel.appointmentNo.toString(), + isFollowUp: myAppointmentsViewModel.patientAppointmentShareResponseModel!.isFollowup!); + } + }); + }); + Future.delayed(Duration(milliseconds: 500), () { + Navigator.of(context).pop(); + print(payfortViewModel.payfortCheckPaymentStatusResponseModel!.responseMessage!); + }); + } else {} + }); + // checkPaymentStatus(appo); + } + } + + 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_3628599@HMG.com", + selectedPaymentMethod, + "1", + "Haroon Amjad", + "3628599", + AuthenticatedUser(outSA: 0, mobileNumber: "0593233758"), + 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 { + 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; + + //TODO: Need to pass dynamic params to the payment request instead of static values + applePayInsertRequest.currency = "SAR"; + applePayInsertRequest.customerEmail = "CustID_3628599@HMG.com"; + applePayInsertRequest.customerID = "3628599"; + applePayInsertRequest.customerName = "Haroon Amjad"; + + 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 = "3628599"; + applePayInsertRequest.patientTypeID = 1; + applePayInsertRequest.patientOutSA = 0; + 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 = 3628599; + 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: "Haroon Amjad", + // customerEmail: projectViewModel.authenticatedUserObject.user.emailAddress, + customerEmail: "CustID_3628599@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: "SAR", + onFailed: (failureResult) async { + log("failureResult: ${failureResult.message.toString()}"); + }, + onSucceeded: (successResult) async { + log("successResult: ${successResult.responseMessage.toString()}"); + }, + // projectId: appo.projectID, + // serviceTypeEnum: ServiceTypeEnum.appointmentPayment, + ); + }); + } } diff --git a/lib/presentation/appointments/widgets/appointment_card.dart b/lib/presentation/appointments/widgets/appointment_card.dart index 242a459..074c01a 100644 --- a/lib/presentation/appointments/widgets/appointment_card.dart +++ b/lib/presentation/appointments/widgets/appointment_card.dart @@ -204,7 +204,13 @@ class _AppointmentCardState extends State { flex: 6, child: CustomButton( text: AppointmentType.getNextActionText(widget.patientAppointmentHistoryResponseModel.nextAction), - onPressed: () {}, + onPressed: () { + Navigator.of(context).push( + FadePage( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + ), + ); + }, backgroundColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.1), borderColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction).withOpacity(0.01), textColor: AppointmentType.getNextActionButtonColor(widget.patientAppointmentHistoryResponseModel.nextAction), @@ -237,7 +243,13 @@ class _AppointmentCardState extends State { fit: BoxFit.contain, ), ), - ), + ).onPress(() { + Navigator.of(context).push( + FadePage( + page: AppointmentDetailsPage(patientAppointmentHistoryResponseModel: widget.patientAppointmentHistoryResponseModel), + ), + ); + }), ), ], ), diff --git a/lib/widgets/common_bottom_sheet.dart b/lib/widgets/common_bottom_sheet.dart index f6039eb..78b9480 100644 --- a/lib/widgets/common_bottom_sheet.dart +++ b/lib/widgets/common_bottom_sheet.dart @@ -7,7 +7,7 @@ import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; import 'package:hmg_patient_app_new/theme/colors.dart'; void showCommonBottomSheet(BuildContext context, - {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true, bool isFullScreen = true}) { + {required Widget child, required VoidCallback callBackFunc, String? title, required double height, bool isCloseButtonVisible = true, bool isFullScreen = true, bool isDismissible = true}) { showModalBottomSheet( sheetAnimationStyle: AnimationStyle( duration: Duration(milliseconds: 500), // Custom animation duration @@ -16,6 +16,7 @@ void showCommonBottomSheet(BuildContext context, context: context, isScrollControlled: true, showDragHandle: false, + isDismissible: isDismissible, backgroundColor: AppColors.scaffoldBgColor, builder: (BuildContext context) { return Container( diff --git a/lib/widgets/in_app_browser/InAppBrowser.dart b/lib/widgets/in_app_browser/InAppBrowser.dart new file mode 100644 index 0000000..bea8e60 --- /dev/null +++ b/lib/widgets/in_app_browser/InAppBrowser.dart @@ -0,0 +1,489 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; +import 'package:hmg_patient_app_new/core/api_consts.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/common_models/tamara_request_model.dart'; +import 'package:hmg_patient_app_new/core/dependencies.dart'; +import 'package:hmg_patient_app_new/core/utils/date_util.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/features/authentication/models/resp_models/authenticated_user_resp_model.dart'; + +enum _PAYMENT_TYPE { PACKAGES, PHARMACY, PATIENT } + +var _InAppBrowserOptions = InAppBrowserClassOptions( + inAppWebViewGroupOptions: InAppWebViewGroupOptions( + crossPlatform: InAppWebViewOptions(useShouldOverrideUrlLoading: true, transparentBackground: false), + ios: IOSInAppWebViewOptions(applePayAPIEnabled: true, isFraudulentWebsiteWarningEnabled: false)), + crossPlatform: InAppBrowserOptions(hideUrlBar: true, toolbarTopBackgroundColor: Colors.black), + android: AndroidInAppBrowserOptions(), + ios: IOSInAppBrowserOptions( + hideToolbarBottom: true, + toolbarBottomBackgroundColor: Colors.white, + closeButtonColor: Colors.white, + closeButtonCaption: "Close", + presentationStyle: IOSUIModalPresentationStyle.OVER_FULL_SCREEN)); + +class MyInAppBrowser extends InAppBrowser { + _PAYMENT_TYPE? paymentType; + + static String APPLE_PAY_PAYFORT_URL = 'https://hmgwebservices.com/PayFortWebLive/PayFortApi/MakeApplePayRequest'; // Payfort Payment Gateway URL LIVE + // static String APPLE_PAY_PAYFORT_URL = 'https://hmgwebservices.com/PayFortWebLive/PayFortApi/MakeApplePayRequest'; // Payfort Payment Gateway URL UAT + + // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWeb/pages/SendPayFortRequest.aspx'; // Payfort Payment Gateway URL UAT + + // static String SERVICE_URL = 'https://hmgwebservices.com/PayFortWebLive/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL LIVE + + // static String SERVICE_URL = 'https://uat.hmgwebservices.com/payfortforvidaplus/pages/SendPayFortRequest.aspx'; //Payfort Payment Gateway URL UAT VIDA PLUS + + // static String PRESCRIPTION_PAYMENT_WITH_ORDERID = + // 'https://uat.hmgwebservices.com/epharmacy/checkout/OpcCompleteRedirectionPaymentClientbyOrder?orderID='; + + static String PRESCRIPTION_PAYMENT_WITH_ORDERID = 'https://mdlaboratories.com/exacartapi/checkout/OpcCompleteRedirectionPaymentClientbyOrder?orderID='; //Live + + static List successURLS = ['success?', 'PayFortResponse', 'PayFortSucess', 'mobilepaymentcomplete', 'orderdetails', 'redirectToApplePay', 'mdlaboratories.com/?']; + + static List errorURLS = ['PayfortCancel', 'errorpage', 'Failed', 'orderdetails', 'redirectToApplePay', 'mdlaboratories.com/?', 'cancel', 'canceled']; + + final Function onExitCallback; + final Function? onLoadStartCallback; + final BuildContext? context; + + // AppSharedPreferences sharedPref = AppSharedPreferences(); + // AuthProvider authProvider = new AuthProvider(); + InAppBrowser browser = InAppBrowser(); + + // AuthenticatedUser authUser; + // late AppoitmentAllHistoryResultList? appo; + + String deviceToken = ""; + + double lat = 0.0; + double long = 0.0; + + static bool isPaymentDone = false; + late AppState appState; + + MyInAppBrowser({required this.onExitCallback, this.onLoadStartCallback, this.context}); + + Future onBrowserCreated() async { + print("\n\nBrowser Created!\n\n"); + } + + @override + Future onLoadStart(Uri? url) async { + if (onLoadStartCallback != null) onLoadStartCallback!(url.toString()); + } + + @override + Future onLoadStop(Uri? url) async { + print("\n\nStopped $url\n\n"); + } + + @override + void onLoadError(Uri? url, int code, String message) { + print("Can't load $url.. Error: $message"); + } + + @override + void onProgressChanged(int progress) {} + + @override + void onExit() { + print("\n\nBrowser closed before!\n\n"); + // if (onExitCallback != null) { + try { + onExitCallback(isPaymentDone); + print("\n\nBrowser closed after!\n\n"); + } catch (err) { + print(err.toString()); + } + // } + } + + @override + Future shouldOverrideUrlLoading(NavigationAction navigationAction) { + var url = navigationAction.request.url.toString(); + debugPrint("redirecting/overriding to: $url"); + + // if (paymentType == _PAYMENT_TYPE.PACKAGES && [PACKAGES_PAYMENT_SUCCESS_URL, PACKAGES_PAYMENT_FAIL_URL].contains(url)) { + // isPaymentDone = (url == PACKAGES_PAYMENT_SUCCESS_URL); + // close(); + // } + + return Future.value(NavigationActionPolicy.ALLOW); + } + + // getLanguageID() async { + // return await sharedPref.getStringWithDefaultValue(APP_LANGUAGE, 'ar'); + // } + + // getDeviceToken() async { + // String deviceToken = await sharedPref.getString(PUSH_TOKEN); + // this.deviceToken = deviceToken; + // } + + // openPackagesPaymentBrowser({required int customer_id, required int order_id}) { + // paymentType = _PAYMENT_TYPE.PACKAGES; + // var full_url = '$PACKAGES_REQUEST_PAYMENT_URL?customer_id=$customer_id&order_id=$order_id'; + // this.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(full_url))), options: _InAppBrowserOptions); + // } + + openPaymentBrowser(num amount, String orderDesc, String transactionID, String projId, String emailId, String paymentMethod, dynamic patientType, String patientName, dynamic patientID, + AuthenticatedUser authenticatedUser, InAppBrowser browser, bool isLiveCareAppo, var servID, var LiveServID, BuildContext context, + [var appoDate, var appoNo, var clinicID, var doctorID, var installments]) async { + appState = getIt.get(); + this.browser = browser; + // await getPatientData(); + if (paymentMethod == "ApplePay") { + MyChromeSafariBrowser safariBrowser = MyChromeSafariBrowser(MyInAppBrowser(onExitCallback: browser.onExit), onExitCallback: browser.onExit, onLoadStartCallback: this.browser.onLoadStart); + + // if (context != null) GifLoaderDialogUtils.showMyDialog(context); + + // LiveCareService service = new LiveCareService(); + // ApplePayInsertRequest applePayInsertRequest = new ApplePayInsertRequest(); + + // applePayInsertRequest.clientRequestID = transactionID; + // applePayInsertRequest.clinicID = (clinicID != null && clinicID != "") ? clinicID : 0; + // applePayInsertRequest.currency = authenticatedUser.outSA == 1 ? "AED" : "SAR"; + // applePayInsertRequest.customerEmail = emailId; + // applePayInsertRequest.customerID = patientID; + // applePayInsertRequest.customerName = patientName; + // applePayInsertRequest.deviceToken = await AppSharedPreferences().getString(PUSH_TOKEN); + // applePayInsertRequest.voipToken = await AppSharedPreferences().getString(ONESIGNAL_APNS_TOKEN); + // applePayInsertRequest.doctorID = (doctorID != null && doctorID != "") ? doctorID : 0; + // applePayInsertRequest.projectID = projId; + // applePayInsertRequest.serviceID = servID; + // applePayInsertRequest.channelID = 3; + // applePayInsertRequest.patientID = patientID; + // applePayInsertRequest.patientTypeID = authenticatedUser.patientType; + // applePayInsertRequest.patientOutSA = authenticatedUser.outSA; + // applePayInsertRequest.appointmentDate = (appoDate != null && appoDate != "") ? appoDate : null; + // applePayInsertRequest.appointmentNo = (appoNo != null && appoNo != "") ? appoNo : 0; + // applePayInsertRequest.orderDescription = orderDesc; + // applePayInsertRequest.liveServiceID = LiveServID.toString() == "" ? "0" : LiveServID.toString(); + // applePayInsertRequest.latitude = this.lat.toString(); + // applePayInsertRequest.longitude = this.long.toString(); + // applePayInsertRequest.amount = amount.toString(); + // applePayInsertRequest.isSchedule = ((appoNo != null && appoNo != "") && (appoDate != null && appoDate != "")) ? "1" : "0"; + // applePayInsertRequest.language = await getLanguageID() == 'ar' ? 'ar' : 'en'; + // applePayInsertRequest.userName = authenticatedUser.patientID; + // applePayInsertRequest.responseContinueURL = "http://hmg.com/Documents/success.html"; + // applePayInsertRequest.backClickUrl = "http://hmg.com/Documents/success.html"; + // applePayInsertRequest.paymentOption = "ApplePay"; + // + // service.applePayInsertRequest(applePayInsertRequest, context).then((res) { + // if (context != null) GifLoaderDialogUtils.hideDialog(context); + // String url = "https://hmgwebservices.com/HMGApplePayLive/applepay/pay?apq=" + res['result']; // Prod + // // String url = "https://uat.hmgwebservices.com/HMGApplePayLive/applepay/pay?apq=" + res['result']; // UAT + // // safariBrowser.open(url: Uri.parse(url)); + // this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(url))), options: _InAppBrowserOptions); + // }).catchError((err) { + // print(err); + // if (context != null) GifLoaderDialogUtils.hideDialog(context); + // AppToast.showErrorToast(message: err); + // }); + } else if (paymentMethod == "TAMARA") { + // LiveCareService service = new LiveCareService(); + TamaraRequestModel tamaraRequestModel = new TamaraRequestModel(); + + // if (context != null) GifLoaderDialogUtils.showMyDialog(context); + + tamaraRequestModel.merchantReference = transactionID; + tamaraRequestModel.merchantIdentifier = "Tamara"; + tamaraRequestModel.clientRequestID = transactionID; + tamaraRequestModel.amount = amount; + tamaraRequestModel.currency = "SR"; + tamaraRequestModel.language = appState.isArabic() ? 'AR' : 'EN'; + tamaraRequestModel.commandType = "PURCHASE"; + tamaraRequestModel.customerEmail = emailId; + tamaraRequestModel.orderDescription = orderDesc; + tamaraRequestModel.isInstallment = true; + tamaraRequestModel.projectID = num.parse(projId); + tamaraRequestModel.accessCode = authenticatedUser.mobileNumber!; + tamaraRequestModel.appointmentNo = (appoNo != null && appoNo != "") ? appoNo.toString() : "0"; + tamaraRequestModel.customerName = patientName; + tamaraRequestModel.fileNumber = patientID.toString(); + tamaraRequestModel.patientOutSA = authenticatedUser.outSA == 1 ? true : false; + tamaraRequestModel.deviceToken = await Utils.getStringFromPrefs(CacheConst.pushToken); + tamaraRequestModel.latitude = appState.userLat.toString(); + tamaraRequestModel.longitude = appState.userLong.toString(); + tamaraRequestModel.serviceID = servID; + tamaraRequestModel.liveServiceID = LiveServID; + tamaraRequestModel.doctorID = (doctorID.toString() != null && doctorID != "") ? doctorID.toString() : ""; + tamaraRequestModel.appointmentDate = (appoDate != null && appoDate != "") ? appoDate : null; + tamaraRequestModel.isSchedule = ((appoNo != null && appoNo != "") && (appoDate != null && appoDate != "")) ? true : false; + + // service.tamaraInsertRequest(tamaraRequestModel, context).then((res) { + // // if (context != null) GifLoaderDialogUtils.hideDialog(context); + // generateTamaraURL(amount, orderDesc, transactionID, projId, emailId, paymentMethod, patientType, patientName, patientID, authenticatedUser, isLiveCareAppo, servID, LiveServID, appoDate, + // appoNo, clinicID, doctorID, "", installments) + // .then((value) { + // paymentType = _PAYMENT_TYPE.PATIENT; + // this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); + // }); + // }).catchError((err) { + // print(err); + // // if (context != null) GifLoaderDialogUtils.hideDialog(context); + // // AppToast.showErrorToast(message: err); + // }); + } else { + generateURL(amount, orderDesc, transactionID, projId, emailId, paymentMethod, patientType, patientName, patientID, authenticatedUser, isLiveCareAppo, servID, LiveServID, appoDate, appoNo, + clinicID, doctorID) + .then((value) { + paymentType = _PAYMENT_TYPE.PATIENT; + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); + }); + } + } + + // openPharmacyPaymentBrowser(OrderDetailModel order, double amount, String orderDesc, String transactionID, String emailId, String paymentMethod, String patientName, dynamic patientID, + // AuthenticatedUser authenticatedUser, InAppBrowser browser) { + // this.browser = browser; + // MyChromeSafariBrowser safariBrowser = + // new MyChromeSafariBrowser(new MyInAppBrowser(onExitCallback: browser.onExit), onExitCallback: browser.onExit, onLoadStartCallback: this.browser.onLoadStart, appo: this.appo!); + // // getPatientData(); + // generatePharmacyURL(order, amount, orderDesc, transactionID, emailId, paymentMethod, patientName, patientID, authenticatedUser).then((value) { + // if (order.customValuesXml!.contains("ApplePay")) { + // safariBrowser.open(url: WebUri.uri(Uri.parse(value))); + // } else { + // this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(value))), options: _InAppBrowserOptions); + // } + // }); + // } + + openBrowser(String url) { + this.browser = browser; + this.browser.openUrlRequest(urlRequest: URLRequest(url: WebUri.uri(Uri.parse(url))), options: _InAppBrowserOptions); + } + + Future generateURL(num amount, String orderDesc, String transactionID, String projId, String emailId, String paymentMethod, dynamic patientType, String patientName, dynamic patientID, + AuthenticatedUser authUser, bool isLiveCareAppo, var servID, var LiveServID, + [var appoDate, var appoNo, var clinicID, var doctorID, var patientData]) async { + // getDeviceToken(); + String currentLanguageID = appState.isArabic() ? 'AR' : 'EN'; + String form = isLiveCareAppo ? getLiveCareForm() : getForm(); + + form = form.replaceFirst("EMAIL_VALUE", emailId); + + form = form.replaceFirst('AMOUNT_VALUE', amount.toString()); + form = form.replaceFirst('ORDER_DESCRIPTION_VALUE', orderDesc); + form = form.replaceFirst('ORDER_ID_VALUE', transactionID); + form = form.replaceFirst('REQUEST_ID_VALUE', transactionID); + form = form.replaceFirst('PROJECT_ID_VALUE', projId); + form = form.replaceFirst('PAYMENT_OPTION_VALUE', paymentMethod); + form = form.replaceFirst('LANG_VALUE', currentLanguageID); + form = form.replaceFirst('PATIENT_OUT_SA', authUser.outSA == 0 ? false.toString() : true.toString()); + form = form.replaceFirst('PATIENT_TYPE_ID', patientData == null ? patientType.toString() : "1"); + + Platform.isIOS + ? form = form.replaceFirst('DEVICE_TOKEN', "${await Utils.getStringFromPrefs(CacheConst.pushToken)},${await Utils.getStringFromPrefs(CacheConst.apnsToken)}") + : form = form.replaceFirst('DEVICE_TOKEN', await Utils.getStringFromPrefs(CacheConst.pushToken) ?? ""); + + // form = form.replaceFirst('DEVICE_TOKEN', await AppSharedPreferences().getString(PUSH_TOKEN) + "," + await AppSharedPreferences().getString(ONESIGNAL_APNS_TOKEN)); + // form = form.replaceFirst('DEVICE_TOKEN', await sharedPref.getString(PUSH_TOKEN)); + form = form.replaceFirst('LATITUDE_VALUE', this.lat.toString()); + form = form.replaceFirst('LONGITUDE_VALUE', this.long.toString()); + + // if (servID == "4") + // form = form.replaceFirst('SERVICE_URL_VALUE', MyInAppBrowser.PREAUTH_SERVICE_URL); + // else + form = form.replaceFirst('SERVICE_URL_VALUE', ApiConsts.SERVICE_URL); + + if (servID != null) { + form = form.replaceFirst('SERV_ID', servID); + form = form.replaceFirst('LIVE_SERVICE_ID', LiveServID.toString()); + } else { + form = form.replaceFirst('SERV_ID', "2"); + form = form.replaceFirst('LIVE_SERVICE_ID', "2"); + } + + form = form.replaceFirst('CUSTNAME_VALUE', patientName); + form = form.replaceFirst('CUSTID_VALUE', patientID.toString()); + + if (isLiveCareAppo) { + form = form.replaceFirst('IS_SCHEDULE_VALUE', "true"); + form = form.replaceFirst('APPOINTMENT_DATE_VALUE', appoDate); + form = form.replaceFirst('APPOINTMENT_NO_VALUE', appoNo.toString()); + form = form.replaceFirst('DOCTOR_ID_VALUE', doctorID.toString()); + form = form.replaceFirst('CLINIC_ID_VALUE', clinicID.toString()); + } + + var bytes = utf8.encode(form); + var base64Str = base64.encode(bytes); + return 'data:text/html;base64,' + base64Str; + } + + Future generateTamaraURL(num amount, String orderDesc, String transactionID, String projId, String emailId, String paymentMethod, dynamic patientType, String patientName, dynamic patientID, + AuthenticatedUser authUser, bool isLiveCareAppo, var servID, var LiveServID, + [var appoDate, var appoNo, var clinicID, var doctorID, var patientData, var installments]) async { + // getDeviceToken(); + String currentLanguageID = appState.isArabic() ? 'AR' : 'EN'; + String form = getTamaraForm(); + + form = form.replaceFirst("EMAIL_VALUE", emailId); + + form = form.replaceFirst('AMOUNT_VALUE', amount.toString()); + form = form.replaceFirst('ORDER_DESCRIPTION_VALUE', orderDesc); + form = form.replaceFirst('ORDER_ID_VALUE', transactionID); + form = form.replaceFirst('REQUEST_ID_VALUE', transactionID); + form = form.replaceFirst('PROJECT_ID_VALUE', projId); + form = form.replaceFirst('PAYMENT_OPTION_VALUE', paymentMethod); + form = form.replaceFirst('LANG_VALUE', currentLanguageID); + form = form.replaceFirst('SERVICE_URL_VALUE', "https://mdlaboratories.com/tamaralive/Home/Checkout"); + + form = form.replaceFirst('INSTALLMENTS_VALUE', installments); + form = form.replaceFirst('CUSTNATIONALID_VALUE', authUser.patientIdentificationNo!); + form = form.replaceFirst('CUSTMOBILE_VALUE', authUser.mobileNumber!); + form = form.replaceFirst('CUSTDOB_VALUE', DateUtil.getDayMonthYearDateFormatted(authUser.dateofBirthDataTime!)); + + form = form.replaceFirst('CURRENCY_VALUE', authUser.outSA == 0 ? "SAR" : "AED"); + form = form.replaceFirst('COUNTRY_CODE_VALUE', authUser.outSA == 0 ? "966" : "971"); + form = form.replaceFirst('CUSTNAME_VALUE', patientName); + form = form.replaceFirst('CUSTLASTNAME_VALUE', patientName); + form = form.replaceFirst('CUSTID_VALUE', patientID.toString()); + + var bytes = utf8.encode(form); + var base64Str = base64.encode(bytes); + return 'data:text/html;base64,' + base64Str; + } + + String getForm() { + return ' ' + + '' + + '' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + '' + + '' + + ''; + } + + String getTamaraForm() { + return ' ' + + '' + + '' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + '
' + + '' + + '' + + ''; + } + + String getLiveCareForm() { + return ' ' + + '' + + '' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + '' + + '' + + ''; + } + + safariCallBack() { + print("Safari CallBack!!!"); + } +} + +class MyChromeSafariBrowser extends ChromeSafariBrowser { + final Function? onExitCallback; + final Function? onLoadStartCallback; + + // AppoitmentAllHistoryResultList? appo; + + // MyChromeSafariBrowser(browserFallback, {this.onExitCallback, this.onLoadStartCallback, this.appo}); + MyChromeSafariBrowser(browserFallback, {this.onExitCallback, this.onLoadStartCallback}); + + @override + void onOpened() { + print("ChromeSafari browser opened"); + } + + @override + void onCompletedInitialLoad(bool? didLoadSuccessfully) { + print("ChromeSafari browser initial load completed"); + onLoadStartCallback!("ApplePay"); + } + + @override + void onClosed() { + print("ChromeSafari browser closed"); + MyInAppBrowser.isPaymentDone = true; + onExitCallback!(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 8532035..75f9992 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -71,6 +71,8 @@ dependencies: flutter_staggered_animations: ^1.1.1 smooth_corner: ^1.1.1 maps_launcher: ^3.0.0+1 + amazon_payfort: ^1.1.4 + network_info_plus: ^6.1.4 dev_dependencies: flutter_test: