Update: 2026-06-11 18:22:57
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/views/home/map_page_passenger.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../main.dart';
|
||||
@@ -8,6 +7,7 @@ import '../../views/auth/otp_page.dart';
|
||||
import '../../views/widgets/error_snakbar.dart';
|
||||
import '../functions/crud.dart';
|
||||
import '../functions/package_info.dart';
|
||||
import '../functions/country_logic.dart';
|
||||
import 'login_controller.dart';
|
||||
|
||||
// --- Helper Class for Phone Authentication ---
|
||||
@@ -19,67 +19,13 @@ class PhoneAuthHelper {
|
||||
static final String _verifyOtpUrl = '${_baseUrl}verifyOtp.php';
|
||||
static final String _registerUrl = '${_baseUrl}register_passenger.php';
|
||||
|
||||
static String formatSyrianPhone(String phone) {
|
||||
// Remove spaces, symbols, +, -, ()
|
||||
phone = phone.replaceAll(RegExp(r'[ \-\(\)\+]'), '').trim();
|
||||
|
||||
// Normalize 00963 → 963
|
||||
if (phone.startsWith('00963')) {
|
||||
phone = phone.replaceFirst('00963', '963');
|
||||
}
|
||||
|
||||
// Normalize 0963 → 963
|
||||
if (phone.startsWith('0963')) {
|
||||
phone = phone.replaceFirst('0963', '963');
|
||||
}
|
||||
|
||||
// NEW: Fix 96309xxxx → 9639xxxx
|
||||
if (phone.startsWith('96309')) {
|
||||
phone = '9639' + phone.substring(5); // remove the "0" after 963
|
||||
}
|
||||
|
||||
// If starts with 9630 → correct to 9639
|
||||
if (phone.startsWith('9630')) {
|
||||
phone = '9639' + phone.substring(4);
|
||||
}
|
||||
|
||||
// If already in correct format: 9639xxxxxxxx
|
||||
if (phone.startsWith('9639') && phone.length == 12) {
|
||||
return phone;
|
||||
}
|
||||
|
||||
// If starts with 963 but missing the 9
|
||||
if (phone.startsWith('963') && phone.length > 3) {
|
||||
// Ensure it begins with 9639
|
||||
if (!phone.startsWith('9639')) {
|
||||
phone = '9639' + phone.substring(3);
|
||||
}
|
||||
return phone;
|
||||
}
|
||||
|
||||
// If starts with 09xxxxxxxx → 9639xxxxxxxx
|
||||
if (phone.startsWith('09')) {
|
||||
return '963' + phone.substring(1);
|
||||
}
|
||||
|
||||
// If 9xxxxxxxx (9 digits)
|
||||
if (phone.startsWith('9') && phone.length == 9) {
|
||||
return '963' + phone;
|
||||
}
|
||||
|
||||
// If starts with incorrect 0xxxxxxx → assume Syrian and fix
|
||||
if (phone.startsWith('0') && phone.length == 10) {
|
||||
return '963' + phone.substring(1);
|
||||
}
|
||||
|
||||
return phone;
|
||||
}
|
||||
// removed formatSyrianPhone
|
||||
|
||||
/// Sends an OTP to the provided phone number.
|
||||
static Future<bool> sendOtp(String phoneNumber) async {
|
||||
try {
|
||||
// إصلاح الرقم قبل الإرسال
|
||||
final fixedPhone = formatSyrianPhone(phoneNumber);
|
||||
final fixedPhone = CountryLogic.formatCurrentCountryPhone(phoneNumber);
|
||||
|
||||
final response = await CRUD().post(
|
||||
link: _sendOtpUrl,
|
||||
@@ -109,7 +55,7 @@ class PhoneAuthHelper {
|
||||
|
||||
static Future<void> verifyOtp(String phoneNumber, String otpCode) async {
|
||||
try {
|
||||
final fixedPhone = formatSyrianPhone(phoneNumber);
|
||||
final fixedPhone = CountryLogic.formatCurrentCountryPhone(phoneNumber);
|
||||
final response = await CRUD().post(
|
||||
link: _verifyOtpUrl,
|
||||
payload: {
|
||||
|
||||
@@ -666,7 +666,7 @@ class DriverTipWidget extends StatelessWidget {
|
||||
|
||||
Toast.show(
|
||||
context,
|
||||
'${'Tip is '.tr}${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
'${'Tip is '.tr}${(double.parse(controller.totalPassenger.toString())) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
AppColor.cyanBlue);
|
||||
controller.update();
|
||||
},
|
||||
@@ -685,7 +685,7 @@ class DriverTipWidget extends StatelessWidget {
|
||||
box.write(BoxName.tipPercentage, '0.10');
|
||||
Toast.show(
|
||||
context,
|
||||
'${'Tip is'.tr} ${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
'${'Tip is'.tr} ${(double.parse(controller.totalPassenger.toString())) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
AppColor.cyanBlue);
|
||||
controller.update();
|
||||
},
|
||||
@@ -704,7 +704,7 @@ class DriverTipWidget extends StatelessWidget {
|
||||
box.write(BoxName.tipPercentage, '0.15');
|
||||
Toast.show(
|
||||
context,
|
||||
'${'Tip is'.tr} ${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
'${'Tip is'.tr} ${(double.parse(controller.totalPassenger.toString())) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
AppColor.cyanBlue);
|
||||
controller.update();
|
||||
},
|
||||
@@ -723,7 +723,7 @@ class DriverTipWidget extends StatelessWidget {
|
||||
box.write(BoxName.tipPercentage, '0.20');
|
||||
Toast.show(
|
||||
context,
|
||||
'${'Tip is'.tr} ${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
'${'Tip is'.tr} ${(double.parse(controller.totalPassenger.toString())) * (double.parse(box.read(BoxName.tipPercentage.toString())))}',
|
||||
AppColor.cyanBlue);
|
||||
controller.update();
|
||||
},
|
||||
@@ -754,7 +754,7 @@ class DriverTipWidget extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(6),
|
||||
child: Text(
|
||||
'${(controller.totalPassenger) * (double.parse(box.read(BoxName.tipPercentage.toString())))} ${box.read(BoxName.countryCode) == 'Egypt' ? 'LE'.tr : 'JOD'.tr}',
|
||||
'${(double.parse(controller.totalPassenger.toString())) * (double.parse(box.read(BoxName.tipPercentage.toString())))} ${box.read(BoxName.countryCode) == 'Egypt' ? 'LE'.tr : 'JOD'.tr}',
|
||||
style: AppStyle.title,
|
||||
),
|
||||
),
|
||||
|
||||
94
siro_rider/lib/controller/functions/country_logic.dart
Normal file
94
siro_rider/lib/controller/functions/country_logic.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
|
||||
class CountryLogic {
|
||||
/// Formats the phone number based on the country's dialing rules.
|
||||
static String formatPhone(String phone, String country) {
|
||||
phone = phone.replaceAll(RegExp(r'[ \-\(\)\+]'), '').trim();
|
||||
|
||||
if (country == 'Egypt') {
|
||||
// Rule: 010, 011, 012, 015 -> 2010, 2011, 2012, 2015
|
||||
if (phone.startsWith('0020')) phone = phone.replaceFirst('0020', '20');
|
||||
if (phone.startsWith('01') && phone.length >= 10) {
|
||||
return '20${phone.substring(1)}';
|
||||
}
|
||||
if (phone.startsWith('1') &&
|
||||
phone.length >= 9 &&
|
||||
!phone.startsWith('20')) {
|
||||
return '20$phone';
|
||||
}
|
||||
if (!phone.startsWith('20') && phone.length > 8) return '20$phone';
|
||||
} else if (country == 'Jordan') {
|
||||
// Rule: 07x -> 9627x
|
||||
if (phone.startsWith('00962')) phone = phone.replaceFirst('00962', '962');
|
||||
if (phone.startsWith('07') && phone.length >= 9) {
|
||||
return '962${phone.substring(1)}';
|
||||
}
|
||||
if (phone.startsWith('7') &&
|
||||
phone.length >= 8 &&
|
||||
!phone.startsWith('962')) {
|
||||
return '962$phone';
|
||||
}
|
||||
if (!phone.startsWith('962') && phone.length > 7) return '962$phone';
|
||||
} else {
|
||||
// Default to Syria
|
||||
if (phone.startsWith('00963')) phone = phone.replaceFirst('00963', '963');
|
||||
if (phone.startsWith('0963')) phone = phone.replaceFirst('0963', '963');
|
||||
if (phone.startsWith('096309')) {
|
||||
phone = phone.replaceFirst('096309', '963');
|
||||
}
|
||||
if (phone.startsWith('96309')) phone = '9639${phone.substring(5)}';
|
||||
if (phone.startsWith('9630')) phone = '9639${phone.substring(4)}';
|
||||
if (phone.startsWith('9639') && phone.length == 12) return phone;
|
||||
if (phone.startsWith('963') &&
|
||||
phone.length > 3 &&
|
||||
!phone.startsWith('9639')) {
|
||||
phone = '9639${phone.substring(3)}';
|
||||
}
|
||||
if (phone.startsWith('09')) return '963${phone.substring(1)}';
|
||||
if (phone.startsWith('9') && phone.length == 9) return '963$phone';
|
||||
if (phone.startsWith('0') && phone.length == 10) {
|
||||
return '963${phone.substring(1)}';
|
||||
}
|
||||
}
|
||||
return phone;
|
||||
}
|
||||
|
||||
/// Returns the default country prefix (EG, JO, SY) for UI initial selection.
|
||||
static String getCountryPrefix(String country) {
|
||||
if (country == 'Egypt') return 'EG';
|
||||
if (country == 'Jordan') return 'JO';
|
||||
return 'SY';
|
||||
}
|
||||
|
||||
/// Returns the default emergency number for the country.
|
||||
static String getEmergencyNumber(String country) {
|
||||
if (country == 'Egypt') return '122';
|
||||
if (country == 'Jordan') return '911';
|
||||
return '112'; // Syria
|
||||
}
|
||||
|
||||
/// Returns the hint text for phone inputs based on the country.
|
||||
static String getPhoneHint(String country) {
|
||||
if (country == 'Egypt') return 'e.g. 01012345678 (Default +20)';
|
||||
if (country == 'Jordan') return 'e.g. 0791234567 (Default +962)';
|
||||
return 'e.g. 0912345678 (Default +963)'; // Syria
|
||||
}
|
||||
|
||||
/// Helper to format phone using the current country in box.
|
||||
static String formatCurrentCountryPhone(String phone) {
|
||||
String cleanPhone = phone.replaceAll(RegExp(r'[ \-\(\)]'), '').trim();
|
||||
if (cleanPhone.startsWith('+963') || cleanPhone.startsWith('00963')) {
|
||||
return formatPhone(cleanPhone, 'Syria');
|
||||
}
|
||||
if (cleanPhone.startsWith('+20') || cleanPhone.startsWith('0020')) {
|
||||
return formatPhone(cleanPhone, 'Egypt');
|
||||
}
|
||||
if (cleanPhone.startsWith('+962') || cleanPhone.startsWith('00962')) {
|
||||
return formatPhone(cleanPhone, 'Jordan');
|
||||
}
|
||||
|
||||
final country = box.read(BoxName.countryCode) ?? 'Syria';
|
||||
return formatPhone(cleanPhone, country);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:siro_rider/print.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import 'dart:io';
|
||||
import 'package:siro_rider/controller/functions/country_logic.dart';
|
||||
|
||||
void showInBrowser(String url) async {
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
@@ -9,16 +10,11 @@ void showInBrowser(String url) async {
|
||||
}
|
||||
|
||||
Future<void> makePhoneCall(String phoneNumber) async {
|
||||
// 1. تنظيف الرقم (إزالة المسافات والفواصل)
|
||||
String formattedNumber = phoneNumber.replaceAll(RegExp(r'\s+'), '');
|
||||
|
||||
// 2. منطق التنسيق (مع الحفاظ على الأرقام القصيرة مثل 112 كما هي)
|
||||
if (formattedNumber.length > 6) {
|
||||
if (formattedNumber.startsWith('09')) {
|
||||
// إذا كان يبدأ بـ 09 (رقم موبايل سوري محلي) -> +963
|
||||
formattedNumber = '+963${formattedNumber.substring(1)}';
|
||||
} else if (!formattedNumber.startsWith('+')) {
|
||||
// إذا لم يكن دولياً ولا محلياً معروفاً -> إضافة + فقط
|
||||
formattedNumber = CountryLogic.formatCurrentCountryPhone(formattedNumber);
|
||||
if (!formattedNumber.startsWith('+')) {
|
||||
formattedNumber = '+$formattedNumber';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,25 +125,23 @@ class RideLifecycleController extends GetxController {
|
||||
late String driverCompletedRides = '0';
|
||||
late String driverTier = 'Verified driver';
|
||||
late String driverToken = '';
|
||||
|
||||
double kazan = 8;
|
||||
double totalPassenger = 0;
|
||||
String totalPassenger = '0';
|
||||
double totalDriver = 0;
|
||||
double costDistance = 0;
|
||||
double costDuration = 0;
|
||||
double averageDuration = 0;
|
||||
double totalCostPassenger = 0;
|
||||
String totalCostPassenger = '0';
|
||||
|
||||
double totalPassengerSpeed = 0;
|
||||
double totalPassengerBalash = 0;
|
||||
double totalPassengerComfort = 0;
|
||||
double totalPassengerElectric = 0;
|
||||
double totalPassengerLady = 0;
|
||||
double totalPassengerScooter = 0;
|
||||
double totalPassengerVan = 0;
|
||||
double totalPassengerRayehGai = 0;
|
||||
double totalPassengerRayehGaiComfort = 0;
|
||||
double totalPassengerRayehGaiBalash = 0;
|
||||
String totalPassengerSpeed = '0';
|
||||
String totalPassengerBalash = '0';
|
||||
String totalPassengerComfort = '0';
|
||||
String totalPassengerElectric = '0';
|
||||
String totalPassengerLady = '0';
|
||||
String totalPassengerScooter = '0';
|
||||
String totalPassengerVan = '0';
|
||||
String totalPassengerRayehGai = '0';
|
||||
String totalPassengerRayehGaiComfort = '0';
|
||||
String totalPassengerRayehGaiBalash = '0';
|
||||
|
||||
double latePrice = 0;
|
||||
double fuelPrice = 0;
|
||||
@@ -744,7 +742,7 @@ class RideLifecycleController extends GetxController {
|
||||
style: TextStyle(color: AppColor.greenColor)),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
double newPrice = totalPassenger * 1.10;
|
||||
double newPrice = double.parse(totalPassenger) * 1.10;
|
||||
increasePriceAndRestartSearch(newPrice);
|
||||
},
|
||||
),
|
||||
@@ -755,7 +753,7 @@ class RideLifecycleController extends GetxController {
|
||||
}
|
||||
|
||||
Future<void> increasePriceAndRestartSearch(double newPrice) async {
|
||||
totalPassenger = newPrice;
|
||||
totalPassenger = newPrice.toStringAsFixed(2);
|
||||
update();
|
||||
|
||||
await CRUD()
|
||||
@@ -822,6 +820,7 @@ class RideLifecycleController extends GetxController {
|
||||
_isRideStartedProcessed = true;
|
||||
currentRideState.value = RideState.inProgress;
|
||||
statusRide = 'Begin';
|
||||
box.write(BoxName.passengerWalletTotal, '0');
|
||||
|
||||
remainingTimeDriverWaitPassenger5Minute = 0;
|
||||
_stopWaitPassengerTimer();
|
||||
@@ -1413,7 +1412,7 @@ class RideLifecycleController extends GetxController {
|
||||
"date": DateTime.now().toString(),
|
||||
"time": DateTime.now().toString(),
|
||||
"endtime": "00:00:00",
|
||||
"price": totalPassenger.toStringAsFixed(2),
|
||||
"price": double.parse(totalPassenger.toString()).toStringAsFixed(2),
|
||||
"passenger_id": box.read(BoxName.passengerID).toString(),
|
||||
"driver_id": "0",
|
||||
"status": "waiting",
|
||||
@@ -1694,7 +1693,6 @@ class RideLifecycleController extends GetxController {
|
||||
box.write(BoxName.serverChosen, AppLink.server);
|
||||
|
||||
if (newCountry != previousCountry) {
|
||||
unawaited(getKazanPercent());
|
||||
}
|
||||
|
||||
return newCountry;
|
||||
@@ -1775,135 +1773,56 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
void applyPromoCodeToPassenger(BuildContext context) async {
|
||||
if (promoTaken == true) {
|
||||
MyDialog().getDialog(
|
||||
'Promo Already Used'.tr,
|
||||
'You have already used this promo code.'.tr,
|
||||
() => Get.back(),
|
||||
);
|
||||
MyDialog().getDialog('Promo Already Used'.tr, 'You have already used this promo code.'.tr, () => Get.back());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!promoFormKey.currentState!.validate()) return;
|
||||
|
||||
const double minPromoLowSYP = 172;
|
||||
const double minPromoHighSYP = 200;
|
||||
|
||||
|
||||
try {
|
||||
final value = await CRUD().get(
|
||||
link: AppLink.getPassengersPromo,
|
||||
payload: {'promo_code': promo.text},
|
||||
);
|
||||
final res = await CRUD().post(link: AppLink.getPrices, payload: {
|
||||
'distance': distance.toString(),
|
||||
'durationToRide': durationToRide.toString(),
|
||||
'startNameAddress': startNameAddress,
|
||||
'endNameAddress': endNameAddress,
|
||||
'destLat': myDestination.latitude.toString(),
|
||||
'destLng': myDestination.longitude.toString(),
|
||||
'passengerLat': newMyLocation.latitude.toString(),
|
||||
'passengerLng': newMyLocation.longitude.toString(),
|
||||
'walletVal': box.read(BoxName.passengerWalletTotal)?.toString() ?? '0',
|
||||
'activeMenuWaypointCount': activeMenuWaypointCount.toString(),
|
||||
'promo_code': promo.text,
|
||||
'passenger_id' :box.read(BoxName.passengerID),
|
||||
'country': box.read(BoxName.countryCode) ?? '',
|
||||
});
|
||||
|
||||
if (value == 'failure') {
|
||||
MyDialog().getDialog(
|
||||
'Promo Ended'.tr,
|
||||
'The promotion period has ended.'.tr,
|
||||
() => Get.back(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final bool eligibleNow = (totalPassengerSpeed >= minPromoLowSYP) ||
|
||||
(totalPassengerBalash >= minPromoLowSYP) ||
|
||||
(totalPassengerComfort >= minPromoHighSYP) ||
|
||||
(totalPassengerElectric >= minPromoHighSYP) ||
|
||||
(totalPassengerLady >= minPromoHighSYP);
|
||||
|
||||
if (!eligibleNow) {
|
||||
Get.snackbar(
|
||||
'Lowest Price Achieved'.tr,
|
||||
'Cannot apply further discounts.'.tr,
|
||||
backgroundColor: AppColor.yellowColor,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final decode = jsonDecode(value);
|
||||
if (decode["status"] != "success") {
|
||||
MyDialog().getDialog(
|
||||
'Promo Ended'.tr,
|
||||
'The promotion period has ended.'.tr,
|
||||
() => Get.back(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Get.snackbar('Promo Code Accepted'.tr, '',
|
||||
backgroundColor: AppColor.greenColor);
|
||||
|
||||
final firstElement = decode["message"][0];
|
||||
final int discountPercentage =
|
||||
int.tryParse(firstElement['amount'].toString()) ?? 0;
|
||||
|
||||
final double walletVal = double.tryParse(
|
||||
box.read(BoxName.passengerWalletTotal)?.toString() ?? '0') ??
|
||||
0.0;
|
||||
|
||||
final bool isWalletNegative = walletVal < 0;
|
||||
|
||||
double _applyDiscountPerTier({
|
||||
required double fare,
|
||||
required double minThreshold,
|
||||
required bool isWalletNegative,
|
||||
}) {
|
||||
if (fare < minThreshold) return fare;
|
||||
|
||||
final double discount = fare * (discountPercentage / 100.0);
|
||||
double result;
|
||||
|
||||
if (isWalletNegative) {
|
||||
double neg = (-1) * walletVal;
|
||||
result = fare + neg - discount;
|
||||
if (res != 'failure') {
|
||||
var response = jsonDecode(res);
|
||||
if (response['status'] == 'success') {
|
||||
var data = response['data'];
|
||||
totalPassengerSpeed = data['totalPassengerSpeed']?.toString() ?? '0';
|
||||
totalPassengerBalash = data['totalPassengerBalash']?.toString() ?? '0';
|
||||
totalPassengerComfort = data['totalPassengerComfort']?.toString() ?? '0';
|
||||
totalPassengerElectric = data['totalPassengerElectric']?.toString() ?? '0';
|
||||
totalPassengerLady = data['totalPassengerLady']?.toString() ?? '0';
|
||||
totalPassengerScooter = data['totalPassengerScooter']?.toString() ?? '0';
|
||||
totalPassengerVan = data['totalPassengerVan']?.toString() ?? '0';
|
||||
totalPassengerRayehGai = data['totalPassengerRayehGai']?.toString() ?? '0';
|
||||
totalPassengerRayehGaiComfort = data['totalPassengerRayehGaiComfort']?.toString() ?? '0';
|
||||
totalPassengerRayehGaiBalash = data['totalPassengerRayehGaiBalash']?.toString() ?? '0';
|
||||
|
||||
promoTaken = true;
|
||||
update();
|
||||
|
||||
Confetti.launch(
|
||||
context,
|
||||
options: const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6),
|
||||
);
|
||||
} else {
|
||||
result = fare - discount;
|
||||
MyDialog().getDialog('Promo Error'.tr, response['message']?.toString() ?? 'Invalid Promo'.tr, () => Get.back());
|
||||
return;
|
||||
}
|
||||
|
||||
if (result < minThreshold) {
|
||||
result = minThreshold;
|
||||
}
|
||||
return result.clamp(0.0, double.infinity);
|
||||
}
|
||||
|
||||
totalPassengerComfort = _applyDiscountPerTier(
|
||||
fare: totalPassengerComfort,
|
||||
minThreshold: minPromoHighSYP,
|
||||
isWalletNegative: isWalletNegative,
|
||||
);
|
||||
|
||||
totalPassengerElectric = _applyDiscountPerTier(
|
||||
fare: totalPassengerElectric,
|
||||
minThreshold: minPromoHighSYP,
|
||||
isWalletNegative: isWalletNegative,
|
||||
);
|
||||
|
||||
totalPassengerLady = _applyDiscountPerTier(
|
||||
fare: totalPassengerLady,
|
||||
minThreshold: minPromoHighSYP,
|
||||
isWalletNegative: isWalletNegative,
|
||||
);
|
||||
|
||||
totalPassengerSpeed = _applyDiscountPerTier(
|
||||
fare: totalPassengerSpeed,
|
||||
minThreshold: minPromoLowSYP,
|
||||
isWalletNegative: isWalletNegative,
|
||||
);
|
||||
|
||||
totalPassengerBalash = _applyDiscountPerTier(
|
||||
fare: totalPassengerBalash,
|
||||
minThreshold: minPromoLowSYP,
|
||||
isWalletNegative: isWalletNegative,
|
||||
);
|
||||
|
||||
totalDriver = totalDriver - (totalDriver * discountPercentage / 100.0);
|
||||
promoTaken = true;
|
||||
update();
|
||||
|
||||
Confetti.launch(
|
||||
context,
|
||||
options: const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6),
|
||||
);
|
||||
|
||||
Get.back();
|
||||
Get.back();
|
||||
await Future.delayed(const Duration(milliseconds: 120));
|
||||
} catch (e) {
|
||||
Get.snackbar('Error'.tr, e.toString(),
|
||||
@@ -1920,236 +1839,49 @@ class RideLifecycleController extends GetxController {
|
||||
double costForDriver = 0;
|
||||
|
||||
Future bottomSheet() async {
|
||||
const double minFareSYP = 160;
|
||||
const double minBillableKm = 0.3;
|
||||
const double ladyFlatAddon = 20;
|
||||
const double airportAddonSYP = 200;
|
||||
const double damascusAirportBoundAddon = 1400;
|
||||
|
||||
const double electricPerKmUplift = 4;
|
||||
const double electricFlatAddon = 10;
|
||||
|
||||
const double longSpeedThresholdKm = 40.0;
|
||||
const double longSpeedPerKm = 26.0;
|
||||
|
||||
const double mediumDistThresholdKm = 25.0;
|
||||
const double longDistThresholdKm = 35.0;
|
||||
const double longTripPerMin = 6.0;
|
||||
const int minuteCapMedium = 60;
|
||||
const int minuteCapLong = 80;
|
||||
const int freeMinutesLong = 10;
|
||||
|
||||
const double extraReduction100 = 0.07;
|
||||
const double maxReductionCap = 0.35;
|
||||
|
||||
durationToAdd = Duration(seconds: durationToRide);
|
||||
hours = durationToAdd.inHours;
|
||||
minutes = (durationToAdd.inMinutes % 60).round();
|
||||
final DateTime currentTime = DateTime.now();
|
||||
newTime = currentTime.add(durationToAdd);
|
||||
averageDuration = (durationToRide / 60) / distance;
|
||||
final int waypointSurchargeMinutes = activeMenuWaypointCount * 5;
|
||||
final int totalMinutes =
|
||||
(durationToRide / 60).floor() + waypointSurchargeMinutes;
|
||||
|
||||
bool _isAirport(String s) {
|
||||
final t = s.toLowerCase();
|
||||
return t.contains('airport') ||
|
||||
s.contains('مطار') ||
|
||||
s.contains('المطار');
|
||||
}
|
||||
|
||||
bool _isClub(String s) {
|
||||
final t = s.toLowerCase();
|
||||
return t.contains('club') ||
|
||||
t.contains('nightclub') ||
|
||||
t.contains('night club') ||
|
||||
s.contains('ديسكو') ||
|
||||
s.contains('ملهى ليلي');
|
||||
}
|
||||
|
||||
bool _isInsideDamascusAirportBounds(double lat, double lng) {
|
||||
final double northLat = 33.415313;
|
||||
final double southLat = 33.400265;
|
||||
final double eastLng = 36.531505;
|
||||
final double westLng = 36.499687;
|
||||
|
||||
bool isLatInside = (lat <= northLat) && (lat >= southLat);
|
||||
bool isLngInside = (lng <= eastLng) && (lng >= westLng);
|
||||
return isLatInside && isLngInside;
|
||||
}
|
||||
|
||||
final double naturePerMin = naturePrice;
|
||||
final double latePerMin = latePrice;
|
||||
final double heavyPerMin = heavyPrice;
|
||||
|
||||
double _perMinuteByTime(DateTime now, bool clubCtx) {
|
||||
final h = now.hour;
|
||||
if (h >= 21 || h < 1) return latePerMin;
|
||||
if (h >= 1 && h < 5) return clubCtx ? (latePerMin * 2) : latePerMin;
|
||||
if (h >= 14 && h <= 17) return heavyPerMin;
|
||||
return naturePerMin;
|
||||
}
|
||||
|
||||
double _applyMinFare(double fare) =>
|
||||
(fare < minFareSYP) ? minFareSYP : fare;
|
||||
|
||||
double _withCommission(double base) =>
|
||||
(base * (1 + kazan / 100)).ceilToDouble();
|
||||
|
||||
final bool airportCtx =
|
||||
_isAirport(startNameAddress) || _isAirport(endNameAddress);
|
||||
final bool clubCtx = _isClub(startNameAddress) || _isClub(endNameAddress);
|
||||
|
||||
double destLat = 0.0;
|
||||
double destLng = 0.0;
|
||||
try {
|
||||
destLat = myDestination.latitude;
|
||||
destLng = myDestination.longitude;
|
||||
} catch (_) {
|
||||
if (locSearch.coordinatesWithoutEmpty.isNotEmpty) {
|
||||
destLat = double.tryParse(
|
||||
locSearch.coordinatesWithoutEmpty.last.split(',')[0]) ??
|
||||
0.0;
|
||||
destLng = double.tryParse(
|
||||
locSearch.coordinatesWithoutEmpty.last.split(',')[1]) ??
|
||||
0.0;
|
||||
}
|
||||
}
|
||||
|
||||
final bool damascusAirportBoundCtx =
|
||||
_isInsideDamascusAirportBounds(destLat, destLng);
|
||||
final bool isInDamascusAirportBoundCtx = _isInsideDamascusAirportBounds(
|
||||
newMyLocation.latitude.toDouble(),
|
||||
newMyLocation.longitude.toDouble(),
|
||||
);
|
||||
|
||||
final double billableDistance =
|
||||
(distance < minBillableKm) ? minBillableKm : distance;
|
||||
|
||||
final bool isLongSpeed = billableDistance > longSpeedThresholdKm;
|
||||
final double perKmSpeedBaseFromServer = speedPrice;
|
||||
final double perKmSpeed =
|
||||
isLongSpeed ? longSpeedPerKm : perKmSpeedBaseFromServer;
|
||||
|
||||
double reductionPct40 = 0.0;
|
||||
if (perKmSpeedBaseFromServer > 0) {
|
||||
reductionPct40 = (1.0 - (longSpeedPerKm / perKmSpeedBaseFromServer))
|
||||
.clamp(0.0, maxReductionCap);
|
||||
}
|
||||
final double reductionPct100 =
|
||||
(reductionPct40 + extraReduction100).clamp(0.0, maxReductionCap);
|
||||
double distanceReduction = 0.0;
|
||||
if (billableDistance > 100.0) {
|
||||
distanceReduction = reductionPct100;
|
||||
} else if (billableDistance > 40.0) {
|
||||
distanceReduction = reductionPct40;
|
||||
}
|
||||
|
||||
double effectivePerMin = _perMinuteByTime(currentTime, clubCtx);
|
||||
int billableMinutes = totalMinutes;
|
||||
if (billableDistance > longDistThresholdKm) {
|
||||
effectivePerMin = longTripPerMin;
|
||||
final int capped =
|
||||
(billableMinutes > minuteCapLong) ? minuteCapLong : billableMinutes;
|
||||
billableMinutes = capped - freeMinutesLong;
|
||||
if (billableMinutes < 0) billableMinutes = 0;
|
||||
} else if (billableDistance > mediumDistThresholdKm) {
|
||||
effectivePerMin = longTripPerMin;
|
||||
billableMinutes = (billableMinutes > minuteCapMedium)
|
||||
? minuteCapMedium
|
||||
: billableMinutes;
|
||||
}
|
||||
|
||||
final double perKmComfortRaw = comfortPrice;
|
||||
final double perKmDelivery = deliveryPrice;
|
||||
final double perKmVanRaw =
|
||||
(familyPrice > 0 ? familyPrice : (speedPrice + 13));
|
||||
final double perKmElectricRaw = perKmComfortRaw + electricPerKmUplift;
|
||||
|
||||
double perKmComfort = perKmComfortRaw * (1.0 - distanceReduction);
|
||||
double perKmElectric = perKmElectricRaw * (1.0 - distanceReduction);
|
||||
double perKmVan = perKmVanRaw * (1.0 - distanceReduction);
|
||||
perKmComfort = perKmComfort.clamp(0, double.infinity);
|
||||
perKmElectric = perKmElectric.clamp(0, double.infinity);
|
||||
perKmVan = perKmVan.clamp(0, double.infinity);
|
||||
final double perKmBalash = (perKmSpeed - 5).clamp(0, double.infinity);
|
||||
|
||||
double _oneWayFare({
|
||||
required double perKm,
|
||||
required bool isLady,
|
||||
double flatAddon = 0,
|
||||
}) {
|
||||
double fare = billableDistance * perKm;
|
||||
fare += billableMinutes * effectivePerMin;
|
||||
fare += flatAddon;
|
||||
if (isLady) fare += ladyFlatAddon;
|
||||
if (airportCtx) fare += airportAddonSYP;
|
||||
|
||||
if (damascusAirportBoundCtx || isInDamascusAirportBoundCtx) {
|
||||
fare += damascusAirportBoundAddon;
|
||||
}
|
||||
return _applyMinFare(fare);
|
||||
}
|
||||
|
||||
double _roundTripFare({required double perKm}) {
|
||||
double distPart =
|
||||
(billableDistance * 2 * perKm) - ((billableDistance * perKm) * 0.4);
|
||||
double timePart = (billableMinutes * 2) * effectivePerMin;
|
||||
double fare = distPart + timePart;
|
||||
if (airportCtx) fare += airportAddonSYP;
|
||||
|
||||
if (damascusAirportBoundCtx || isInDamascusAirportBoundCtx) {
|
||||
fare += damascusAirportBoundAddon;
|
||||
}
|
||||
return _applyMinFare(fare);
|
||||
}
|
||||
|
||||
final double costSpeed = _oneWayFare(perKm: perKmSpeed, isLady: false);
|
||||
final double costBalash = _oneWayFare(perKm: perKmBalash, isLady: false);
|
||||
final double costComfort = _oneWayFare(perKm: perKmComfort, isLady: false);
|
||||
final double costElectric = _oneWayFare(
|
||||
perKm: perKmElectric, isLady: false, flatAddon: electricFlatAddon);
|
||||
final double costDelivery =
|
||||
_oneWayFare(perKm: perKmDelivery, isLady: false);
|
||||
final double costLady = _oneWayFare(perKm: perKmComfort, isLady: true);
|
||||
final double costVan = _oneWayFare(perKm: perKmVan, isLady: false);
|
||||
final double costRayehGai = _roundTripFare(perKm: perKmSpeed);
|
||||
final double costRayehGaiComfort = _roundTripFare(perKm: perKmComfort);
|
||||
final double costRayehGaiBalash = _roundTripFare(perKm: perKmBalash);
|
||||
|
||||
totalPassengerSpeed = _withCommission(costSpeed);
|
||||
totalPassengerBalash = _withCommission(costBalash);
|
||||
totalPassengerComfort = _withCommission(costComfort);
|
||||
totalPassengerElectric = _withCommission(costElectric);
|
||||
totalPassengerLady = _withCommission(costLady);
|
||||
totalPassengerScooter = _withCommission(costDelivery);
|
||||
totalPassengerVan = _withCommission(costVan);
|
||||
totalPassengerRayehGai = _withCommission(costRayehGai);
|
||||
totalPassengerRayehGaiComfort = _withCommission(costRayehGaiComfort);
|
||||
totalPassengerRayehGaiBalash = _withCommission(costRayehGaiBalash);
|
||||
|
||||
totalPassenger = totalPassengerSpeed;
|
||||
totalCostPassenger = totalPassenger;
|
||||
|
||||
try {
|
||||
final walletStr = box.read(BoxName.passengerWalletTotal).toString();
|
||||
final walletVal = double.tryParse(walletStr) ?? 0.0;
|
||||
if (walletVal < 0) {
|
||||
final neg = (-1) * walletVal;
|
||||
totalPassenger += neg;
|
||||
totalPassengerComfort += neg;
|
||||
totalPassengerElectric += neg;
|
||||
totalPassengerLady += neg;
|
||||
totalPassengerBalash += neg;
|
||||
totalPassengerScooter += neg;
|
||||
totalPassengerRayehGai += neg;
|
||||
totalPassengerRayehGaiComfort += neg;
|
||||
totalPassengerRayehGaiBalash += neg;
|
||||
totalPassengerVan += neg;
|
||||
final res = await CRUD().post(link: AppLink.getPrices, payload: {
|
||||
'distance': distance.toString(),
|
||||
'durationToRide': durationToRide.toString(),
|
||||
'startNameAddress': startNameAddress,
|
||||
'endNameAddress': endNameAddress,
|
||||
'destLat': myDestination.latitude.toString(),
|
||||
'destLng': myDestination.longitude.toString(),
|
||||
'passengerLat': newMyLocation.latitude.toString(),
|
||||
'passengerLng': newMyLocation.longitude.toString(),
|
||||
'walletVal': box.read(BoxName.passengerWalletTotal)?.toString() ?? '0',
|
||||
'activeMenuWaypointCount': activeMenuWaypointCount.toString(),
|
||||
'passenger_id': box.read(BoxName.passengerID) ?? '',
|
||||
'country': box.read(BoxName.countryCode) ?? '',
|
||||
});
|
||||
|
||||
if (res != 'failure') {
|
||||
var response = jsonDecode(res);
|
||||
if (response['status'] == 'success') {
|
||||
var data = response['data'];
|
||||
totalPassengerSpeed = data['totalPassengerSpeed']?.toString() ?? '0';
|
||||
totalPassengerBalash = data['totalPassengerBalash']?.toString() ?? '0';
|
||||
totalPassengerComfort = data['totalPassengerComfort']?.toString() ?? '0';
|
||||
totalPassengerElectric = data['totalPassengerElectric']?.toString() ?? '0';
|
||||
totalPassengerLady = data['totalPassengerLady']?.toString() ?? '0';
|
||||
totalPassengerScooter = data['totalPassengerScooter']?.toString() ?? '0';
|
||||
totalPassengerVan = data['totalPassengerVan']?.toString() ?? '0';
|
||||
totalPassengerRayehGai = data['totalPassengerRayehGai']?.toString() ?? '0';
|
||||
totalPassengerRayehGaiComfort = data['totalPassengerRayehGaiComfort']?.toString() ?? '0';
|
||||
totalPassengerRayehGaiBalash = data['totalPassengerRayehGaiBalash']?.toString() ?? '0';
|
||||
|
||||
totalPassenger = totalPassengerSpeed;
|
||||
totalCostPassenger = totalPassenger;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print("Error: $e");
|
||||
Log.print("Error fetching prices: $e");
|
||||
}
|
||||
|
||||
update();
|
||||
@@ -2694,7 +2426,7 @@ class RideLifecycleController extends GetxController {
|
||||
"end_location": '${endLoc.latitude},${endLoc.longitude}',
|
||||
"date": DateTime.now().toString(),
|
||||
"time": DateTime.now().toString(),
|
||||
"price": totalPassenger.toStringAsFixed(2),
|
||||
"price": double.parse(totalPassenger.toString()).toStringAsFixed(2),
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
'status': 'waiting',
|
||||
'carType': box.read(BoxName.carType),
|
||||
@@ -2720,144 +2452,9 @@ class RideLifecycleController extends GetxController {
|
||||
double familyPrice = 55;
|
||||
double deliveryPrice = 1.2;
|
||||
|
||||
Future<void> getKazanPercent() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getKazanPercent,
|
||||
payload: {'country': box.read(BoxName.countryCode).toString()},
|
||||
);
|
||||
if (res != 'failure') {
|
||||
var json = jsonDecode(res);
|
||||
var dataList = json['data'] ?? json['message'];
|
||||
|
||||
if (dataList != null && dataList is List && dataList.isNotEmpty) {
|
||||
var firstRow = dataList[0];
|
||||
kazan = double.parse(firstRow['kazan'].toString());
|
||||
naturePrice = double.parse(firstRow['naturePrice'].toString());
|
||||
heavyPrice = double.parse(firstRow['heavyPrice'].toString());
|
||||
latePrice = double.parse(firstRow['latePrice'].toString());
|
||||
comfortPrice = double.parse(firstRow['comfortPrice'].toString());
|
||||
speedPrice = double.parse(firstRow['speedPrice'].toString());
|
||||
deliveryPrice = double.parse(firstRow['deliveryPrice'].toString());
|
||||
mashwariPrice = double.parse(firstRow['freePrice'].toString());
|
||||
familyPrice = double.parse(firstRow['familyPrice'].toString());
|
||||
fuelPrice = double.parse(firstRow['fuelPrice'].toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> getPassengerRate() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getPassengerRate,
|
||||
payload: {'passenger_id': box.read(BoxName.passengerID)});
|
||||
if (res != 'failure') {
|
||||
var json = jsonDecode(res);
|
||||
var message = json['data'] ?? json['message'];
|
||||
if (message['rating'] == null) {
|
||||
passengerRate = 5.0;
|
||||
} else {
|
||||
var rating = message['rating'];
|
||||
if (rating is String) {
|
||||
passengerRate = double.tryParse(rating) ?? 5.0;
|
||||
} else if (rating is num) {
|
||||
passengerRate = rating.toDouble();
|
||||
} else {
|
||||
passengerRate = 5.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
passengerRate = 5.0;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addFingerPrint() async {
|
||||
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
|
||||
await CRUD().postWallet(link: AppLink.addFingerPrint, payload: {
|
||||
'token': (box.read(BoxName.tokenFCM.toString())),
|
||||
'passengerID': box.read(BoxName.passengerID).toString(),
|
||||
"fingerPrint": fingerPrint
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> firstTimeRunToGetCoupon() async {
|
||||
if (box.read(BoxName.isFirstTime).toString() == '0' &&
|
||||
box.read(BoxName.isInstall).toString() == '1' &&
|
||||
box.read(BoxName.isGiftToken).toString() == '0') {
|
||||
var promoCode, discount, validity;
|
||||
var resPromo = await CRUD().get(link: AppLink.getPromoFirst, payload: {
|
||||
"passengerID": box.read(BoxName.passengerID).toString(),
|
||||
});
|
||||
if (resPromo != 'failure') {
|
||||
var d1 = jsonDecode(resPromo);
|
||||
promoCode = d1['message']['promo_code'];
|
||||
discount = d1['message']['amount'];
|
||||
validity = d1['message']['validity_end_date'];
|
||||
}
|
||||
box.write(BoxName.isFirstTime, '1');
|
||||
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: SizedBox(
|
||||
width: 300,
|
||||
child: PromoBanner(
|
||||
promoCode: promoCode,
|
||||
discountPercentage: discount,
|
||||
validity: validity,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> detectAndCacheDeviceTier() async {
|
||||
bool isHighEnd = await DevicePerformanceManager.isHighEndDevice();
|
||||
Log.print("Device Analysis - Is Flagship/HighEnd? $isHighEnd");
|
||||
box.write(BoxName.lowEndMode, !isHighEnd);
|
||||
}
|
||||
|
||||
Future<void> initilizeGetStorage() async {
|
||||
if (box.read(BoxName.addWork) == null) {
|
||||
box.write(BoxName.addWork, 'addWork');
|
||||
}
|
||||
if (box.read(BoxName.addHome) == null) {
|
||||
box.write(BoxName.addHome, 'addHome');
|
||||
}
|
||||
if (box.read(BoxName.lowEndMode) == null) {
|
||||
detectAndCacheDeviceTier();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> selectDriverAndCarForMishwariTrip() async {
|
||||
double latitudeOffset = 0.1;
|
||||
double longitudeOffset = 0.12;
|
||||
|
||||
double southwestLat = passengerLocation.latitude - latitudeOffset;
|
||||
double northeastLat = passengerLocation.latitude + latitudeOffset;
|
||||
double southwestLon = passengerLocation.longitude - longitudeOffset;
|
||||
double northeastLon = passengerLocation.longitude + longitudeOffset;
|
||||
|
||||
var payload = {
|
||||
'southwestLat': southwestLat.toString(),
|
||||
'northeastLat': northeastLat.toString(),
|
||||
'southwestLon': southwestLon.toString(),
|
||||
'northeastLon': northeastLon.toString(),
|
||||
};
|
||||
|
||||
Future selectDriverAndCarForMishwariTrip() async {
|
||||
try {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.selectDriverAndCarForMishwariTrip, payload: payload);
|
||||
|
||||
if (res != 'failure') {
|
||||
try {
|
||||
var d = jsonDecode(res);
|
||||
driversForMishwari = d['message'];
|
||||
Log.print('driversForMishwari: $driversForMishwari');
|
||||
update();
|
||||
} catch (e) {
|
||||
Log.print("Error decoding JSON: $e");
|
||||
}
|
||||
}
|
||||
// Logic for mishwari trip driver selection
|
||||
} catch (e) {
|
||||
Log.print("Error Mishwari select: $e");
|
||||
}
|
||||
@@ -4311,7 +3908,6 @@ class RideLifecycleController extends GetxController {
|
||||
|
||||
Future<void> _stagePricingAndState() async {
|
||||
try {
|
||||
await getKazanPercent();
|
||||
} catch (e) {
|
||||
Log.print("Error: $e");
|
||||
}
|
||||
@@ -4663,4 +4259,68 @@ class RideLifecycleController extends GetxController {
|
||||
mapEngine.update();
|
||||
update();
|
||||
}
|
||||
initilizeGetStorage() async {
|
||||
if (box.read(BoxName.addWork) == null) {
|
||||
box.write(BoxName.addWork, 'addWork');
|
||||
}
|
||||
if (box.read(BoxName.addHome) == null) {
|
||||
box.write(BoxName.addHome, 'addHome');
|
||||
}
|
||||
}
|
||||
|
||||
getPassengerRate() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getPassengerRate,
|
||||
payload: {'passenger_id': box.read(BoxName.passengerID)});
|
||||
if (res != 'failure') {
|
||||
var json = jsonDecode(res);
|
||||
var message = json['data'] ?? json['message'];
|
||||
if (message['rating'] == null) {
|
||||
passengerRate = 5.0; // Default rating
|
||||
} else {
|
||||
var rating = message['rating'];
|
||||
if (rating is String) {
|
||||
passengerRate = double.tryParse(rating) ?? 5.0;
|
||||
} else if (rating is num) {
|
||||
passengerRate = rating.toDouble();
|
||||
} else {
|
||||
passengerRate = 5.0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
passengerRate = 5.0;
|
||||
}
|
||||
}
|
||||
|
||||
firstTimeRunToGetCoupon() async {
|
||||
if (box.read(BoxName.isFirstTime).toString() == '0' &&
|
||||
box.read(BoxName.isInstall).toString() == '1' &&
|
||||
box.read(BoxName.isGiftToken).toString() == '0') {
|
||||
var promo, discount, validity;
|
||||
var resPromo = await CRUD().get(link: AppLink.getPromoFirst, payload: {
|
||||
"passengerID": box.read(BoxName.passengerID).toString(),
|
||||
});
|
||||
if (resPromo != 'failure') {
|
||||
var d1 = jsonDecode(resPromo);
|
||||
promo = d1['message']['promo_code'];
|
||||
discount = d1['message']['amount'];
|
||||
validity = d1['message']['validity_end_date'];
|
||||
}
|
||||
box.write(BoxName.isFirstTime, '1');
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: SizedBox(
|
||||
width: 300,
|
||||
child: PromoBanner(
|
||||
promoCode: promo ?? '',
|
||||
discountPercentage: discount ?? '',
|
||||
validity: validity ?? '',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import '../../functions/crud.dart';
|
||||
import '../../functions/tts.dart';
|
||||
import 'ride_lifecycle_controller.dart';
|
||||
import 'location_search_controller.dart';
|
||||
import '../../functions/country_logic.dart';
|
||||
|
||||
class UiInteractionsController extends GetxController {
|
||||
TextEditingController sosPhonePassengerProfile = TextEditingController();
|
||||
@@ -58,7 +59,8 @@ class UiInteractionsController extends GetxController {
|
||||
MyTextForm(
|
||||
controller: sosPhonePassengerProfile,
|
||||
label: 'insert sos phone'.tr,
|
||||
hint: 'e.g. 0912345678 (Default +963)'.tr,
|
||||
hint: CountryLogic.getPhoneHint(
|
||||
box.read(BoxName.countryCode) ?? 'Syria').tr,
|
||||
type: TextInputType.phone,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
@@ -77,7 +79,7 @@ class UiInteractionsController extends GetxController {
|
||||
if (sosFormKey.currentState!.validate()) {
|
||||
Get.back();
|
||||
var numberPhone =
|
||||
formatSyrianPhoneNumber(sosPhonePassengerProfile.text);
|
||||
CountryLogic.formatCurrentCountryPhone(sosPhonePassengerProfile.text);
|
||||
|
||||
await CRUD().post(
|
||||
link: AppLink.updateprofile,
|
||||
@@ -160,36 +162,7 @@ class UiInteractionsController extends GetxController {
|
||||
'whatsapp', box.read(BoxName.sosPhonePassenger), message);
|
||||
}
|
||||
|
||||
String formatSyrianPhone(String phone) {
|
||||
phone = phone.replaceAll(' ', '').replaceAll('+', '');
|
||||
if (phone.startsWith('00963')) {
|
||||
phone = phone.replaceFirst('00963', '963');
|
||||
}
|
||||
if (phone.startsWith('0963')) {
|
||||
phone = phone.replaceFirst('0963', '963');
|
||||
}
|
||||
if (phone.startsWith('963')) {
|
||||
return phone;
|
||||
}
|
||||
if (phone.startsWith('09')) {
|
||||
return '963' + phone.substring(1);
|
||||
}
|
||||
if (phone.startsWith('9') && phone.length == 9) {
|
||||
return '963' + phone;
|
||||
}
|
||||
return phone;
|
||||
}
|
||||
|
||||
String formatSyrianPhoneNumber(String phoneNumber) {
|
||||
String trimmedPhone = phoneNumber.trim();
|
||||
if (trimmedPhone.startsWith('09')) {
|
||||
return '963${trimmedPhone.substring(1)}';
|
||||
}
|
||||
if (trimmedPhone.startsWith('963')) {
|
||||
return trimmedPhone;
|
||||
}
|
||||
return '963$trimmedPhone';
|
||||
}
|
||||
// removed formatSyrianPhone and formatSyrianPhoneNumber
|
||||
|
||||
void sendSMS(String to) async {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
@@ -205,7 +178,7 @@ class UiInteractionsController extends GetxController {
|
||||
void sendWhatsapp(String to) async {
|
||||
final rideLifecycle = Get.find<RideLifecycleController>();
|
||||
final locSearch = Get.find<LocationSearchController>();
|
||||
String formattedPhone = formatSyrianPhone(to);
|
||||
String formattedPhone = CountryLogic.formatCurrentCountryPhone(to);
|
||||
|
||||
String message =
|
||||
'${'${'Hi! This is'.tr} ${(box.read(BoxName.name).toString().split(' ')[0]).toString()}.\n${' I am using'.tr}'} ${AppInformation.appName}${' to ride with'.tr} ${rideLifecycle.passengerName}${' as the driver.'.tr} ${rideLifecycle.passengerName} \n${'is driving a '.tr}${rideLifecycle.model}\n${' with license plate '.tr}${rideLifecycle.licensePlate}.\n${' I am currently located at '.tr} https://www.google.com/maps/place/${locSearch.passengerLocation.latitude},${locSearch.passengerLocation.longitude}.\n${' If you need to reach me, please contact the driver directly at'.tr}\n\n ${rideLifecycle.driverPhone}.';
|
||||
@@ -286,7 +259,7 @@ class UiInteractionsController extends GetxController {
|
||||
return;
|
||||
}
|
||||
|
||||
var numberPhone = formatSyrianPhoneNumber(storedPhone);
|
||||
var numberPhone = CountryLogic.formatCurrentCountryPhone(storedPhone);
|
||||
String trackingLink = rideLifecycle.generateTrackingLink(
|
||||
rideLifecycle.rideId, rideLifecycle.driverId);
|
||||
|
||||
@@ -326,7 +299,7 @@ Thank you for using Siro!
|
||||
Future getTokenForParent() async {
|
||||
_ensureSosNumber(() async {
|
||||
String storedPhone = box.read(BoxName.sosPhonePassenger)!;
|
||||
var numberPhone = formatSyrianPhoneNumber(storedPhone);
|
||||
var numberPhone = CountryLogic.formatCurrentCountryPhone(storedPhone);
|
||||
Log.print("Searching for Parent Token with Phone: $numberPhone");
|
||||
|
||||
var res = await CRUD()
|
||||
@@ -375,7 +348,7 @@ Thank you for using Siro!
|
||||
Get.back();
|
||||
var rawPhone = box.read(BoxName.sosPhonePassenger);
|
||||
if (rawPhone == null) return;
|
||||
var phone = formatSyrianPhoneNumber(rawPhone);
|
||||
var phone = CountryLogic.formatCurrentCountryPhone(rawPhone);
|
||||
|
||||
var message = '''Dear Friend,
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// import 'dart:async';
|
||||
// import 'package:siro_rider/constant/currency.dart';
|
||||
import 'dart:async';
|
||||
// import 'package:siro_rider/services/offline_map_service.dart';
|
||||
// import 'package:siro_rider/services/emergency_signal_service.dart';
|
||||
// import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
@@ -6559,8 +6560,8 @@
|
||||
// if (!promoFormKey.currentState!.validate()) return;
|
||||
|
||||
// // العتبات بالليرة السورية
|
||||
// const double minPromoLowSYP = 172; // Speed / Balash
|
||||
// const double minPromoHighSYP = 200; // Comfort / Electric / Lady
|
||||
// const double minPromoLow${CurrencyHelper.currency} = 172; // Speed / Balash
|
||||
// const double minPromoHigh${CurrencyHelper.currency} = 200; // Comfort / Electric / Lady
|
||||
|
||||
// try {
|
||||
// final value = await CRUD().get(
|
||||
@@ -6723,10 +6724,10 @@
|
||||
// // if (data.isEmpty) return;
|
||||
|
||||
// // === إعدادات عامة ===
|
||||
// const double minFareSYP = 160; // حد أدنى
|
||||
// const double minFare${CurrencyHelper.currency} = 160; // حد أدنى
|
||||
// const double minBillableKm = 0.3; // حد أدنى للمسافة المفوترة
|
||||
// const double ladyFlatAddon = 20; // إضافة ثابتة لـ Lady
|
||||
// const double airportAddonSYP = 200; // إضافة المطار
|
||||
// const double airportAddon${CurrencyHelper.currency} = 200; // إضافة المطار
|
||||
|
||||
// // --- ⬇️ الإضافة الجديدة: إضافة حدود مطار دمشق ⬇️ ---
|
||||
// const double damascusAirportBoundAddon = 1400; // إضافة المطار (حدود)
|
||||
@@ -6814,7 +6815,7 @@
|
||||
|
||||
// // حد أدنى
|
||||
// double _applyMinFare(double fare) =>
|
||||
// (fare < minFareSYP) ? minFareSYP : fare;
|
||||
// (fare < minFareSYP) ? minFare${CurrencyHelper.currency} : fare;
|
||||
|
||||
// // عمولة الراكب (kazan من السيرفر)
|
||||
// double _withCommission(double base) =>
|
||||
@@ -7341,7 +7342,7 @@
|
||||
// double mashwariPrice = 40;
|
||||
// double familyPrice = 55;
|
||||
// double deliveryPrice = 1.2;
|
||||
// double minFareSYP = 16000; // حد أدنى للأجرة (سوريا)
|
||||
// double minFare${CurrencyHelper.currency} = 16000; // حد أدنى للأجرة (سوريا)
|
||||
// double minBillableKm = 1.0; // حد أدنى للمسافة المفوترة
|
||||
// double commissionPct = 15; // عمولة التطبيق % (راكب)
|
||||
|
||||
|
||||
@@ -302,18 +302,30 @@ ${'Download the Siro app now and enjoy your ride!'.tr}
|
||||
'${'Claim your 20 LE gift for inviting'.tr} $passengerName!',
|
||||
() async {
|
||||
Get.back(); // Close dialog first
|
||||
await Get.find<PaymentController>().addPassengersWallet('20');
|
||||
await CRUD().post(
|
||||
link: AppLink.updatePassengerGift,
|
||||
payload: {'id': invitation['id']},
|
||||
|
||||
var response = await CRUD().post(
|
||||
link: AppLink.claimInviteReward,
|
||||
payload: {
|
||||
'invite_id': invitation['id'].toString(),
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
'country_code': box.read(BoxName.countryCode).toString(),
|
||||
},
|
||||
);
|
||||
NotificationCaptainController().addNotificationCaptain(
|
||||
invitation['passengerInviterId'].toString(),
|
||||
"You have got a gift for invitation".tr,
|
||||
'${"You have earned 20".tr} ${'LE'}',
|
||||
false,
|
||||
);
|
||||
fetchDriverStatsPassengers(); // Refresh list
|
||||
|
||||
if (response != 'failure') {
|
||||
var data = jsonDecode(response);
|
||||
if (data['status'] == 'success') {
|
||||
NotificationCaptainController().addNotificationCaptain(
|
||||
invitation['passengerInviterId'].toString(),
|
||||
"You have got a gift for invitation".tr,
|
||||
'${"You have earned 20".tr} ${'LE'}',
|
||||
false,
|
||||
);
|
||||
fetchDriverStatsPassengers(); // Refresh list
|
||||
} else {
|
||||
Get.snackbar('Error'.tr, data['message'] ?? 'Claim failed'.tr, backgroundColor: AppColor.redColor);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -286,6 +286,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "لازم تدخل الكود",
|
||||
"Syria": "سوريا",
|
||||
"SYP": "ل.س",
|
||||
"EGP": "ج.م",
|
||||
"JOD": "د.أ",
|
||||
"Order": "طلب",
|
||||
"OrderVIP": "طلب VIP",
|
||||
"Cancel Trip": "إلغاء المشوار",
|
||||
@@ -1700,6 +1702,8 @@ class MyTranslation extends Translations {
|
||||
"ar-main": {
|
||||
"Syria": "سوريا",
|
||||
"SYP": "ل.س",
|
||||
"EGP": "ج.م",
|
||||
"JOD": "د.أ",
|
||||
"Order": "طلب",
|
||||
"OrderVIP": "طلب VIP",
|
||||
"Cancel Trip": "إلغاء الرحلة",
|
||||
@@ -3127,6 +3131,8 @@ class MyTranslation extends Translations {
|
||||
"ar-eg": {
|
||||
"Syria": "سوريا",
|
||||
"SYP": "ل.س",
|
||||
"EGP": "ج.م",
|
||||
"JOD": "د.أ",
|
||||
"Order": "طلب",
|
||||
"OrderVIP": "طلب VIP",
|
||||
"Cancel Trip": "إلغاء المشوار",
|
||||
@@ -4542,6 +4548,8 @@ class MyTranslation extends Translations {
|
||||
"AR-Gulf": {
|
||||
"Syria": "سوريا",
|
||||
"SYP": "ل.س",
|
||||
"EGP": "ج.م",
|
||||
"JOD": "د.أ",
|
||||
"Order": "طلب",
|
||||
"OrderVIP": "طلب VIP",
|
||||
"Cancel Trip": "إلغاء الرحلة",
|
||||
@@ -5960,6 +5968,8 @@ class MyTranslation extends Translations {
|
||||
"ar-ma": {
|
||||
"Syria": "سوريا",
|
||||
"SYP": "ل.س",
|
||||
"EGP": "ج.م",
|
||||
"JOD": "د.أ",
|
||||
"Order": "طلب",
|
||||
"OrderVIP": "طلب VIP",
|
||||
"Cancel Trip": "إلغي الرحلة",
|
||||
@@ -7571,6 +7581,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Suriye",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Sipariş",
|
||||
"OrderVIP": "VIP Sipariş",
|
||||
"Cancel Trip": "Yolculuğu İptal Et",
|
||||
@@ -9099,6 +9111,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Syrie",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Commande",
|
||||
"OrderVIP": "Commande VIP",
|
||||
"Cancel Trip": "Annuler le trajet",
|
||||
@@ -10669,6 +10683,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Syrien",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Bestellung",
|
||||
"OrderVIP": "VIP-Bestellung",
|
||||
"Cancel Trip": "Fahrt stornieren",
|
||||
@@ -12418,6 +12434,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Siria",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Pedido",
|
||||
"OrderVIP": "Pedido VIP",
|
||||
"Cancel Trip": "Cancelar viaje",
|
||||
@@ -14160,6 +14178,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "سوریه",
|
||||
"SYP": "لیره سوریه",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "درخواست",
|
||||
"OrderVIP": "درخواست VIP",
|
||||
"Cancel Trip": "لغو سفر",
|
||||
@@ -15666,6 +15686,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Συρία",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Αίτημα",
|
||||
"OrderVIP": "VIP Αίτημα",
|
||||
"Cancel Trip": "Ακύρωση Διαδρομής",
|
||||
@@ -17159,6 +17181,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "شام",
|
||||
"SYP": "شامی پاؤن",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "آرڈر",
|
||||
"OrderVIP": "VIP آرڈر",
|
||||
"Cancel Trip": "سفر منسوخ کریں",
|
||||
@@ -18721,6 +18745,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "भारत",
|
||||
"SYP": "₹",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "ऑर्डर",
|
||||
"OrderVIP": "VIP ऑर्डर",
|
||||
"Cancel Trip": "ट्रिप रद्द करें",
|
||||
@@ -20281,6 +20307,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Сирия",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Заказ",
|
||||
"OrderVIP": "VIP Заказ",
|
||||
"Cancel Trip": "Отменить поездку",
|
||||
@@ -21745,6 +21773,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "Siria",
|
||||
"SYP": "SYP",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "Ordine",
|
||||
"OrderVIP": "Ordine VIP",
|
||||
"Cancel Trip": "Annulla corsa",
|
||||
@@ -23244,6 +23274,8 @@ class MyTranslation extends Translations {
|
||||
"you must insert token code": "you must insert token code",
|
||||
"Syria": "叙利亚",
|
||||
"SYP": "叙利亚镑",
|
||||
"EGP": "EGP",
|
||||
"JOD": "JOD",
|
||||
"Order": "طلب",
|
||||
"OrderVIP": "طلب VIP",
|
||||
"Cancel Trip": "إلغاء المشوار",
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
import 'dart:convert';
|
||||
import 'package:siro_rider/constant/api_key.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/firebase/firbase_messge.dart';
|
||||
import 'package:siro_rider/controller/payment/paymob/paymob_response.dart';
|
||||
import 'package:siro_rider/views/home/map_page_passenger.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_state.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_state.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/info.dart';
|
||||
import '../../constant/links.dart';
|
||||
import '../../main.dart';
|
||||
import '../../print.dart';
|
||||
import '../firebase/notification_service.dart';
|
||||
import '../functions/crud.dart';
|
||||
import '../functions/encrypt_decrypt.dart';
|
||||
import '../functions/toast.dart';
|
||||
import 'paymob/e_cash_screen.dart';
|
||||
import '../../views/home/my_wallet/payment_screen_mtn.dart';
|
||||
import '../../views/home/my_wallet/payment_screen_cliq.dart';
|
||||
|
||||
class PaymentController extends GetxController {
|
||||
bool isLoading = false;
|
||||
@@ -34,7 +26,8 @@ class PaymentController extends GetxController {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
final promo = TextEditingController();
|
||||
final walletphoneController = TextEditingController();
|
||||
double totalPassenger = Get.find<RideLifecycleController>().totalPassenger;
|
||||
double totalPassenger = double.parse(
|
||||
Get.find<RideLifecycleController>().totalPassenger.toString());
|
||||
int? selectedAmount = 0;
|
||||
List<dynamic> totalPassengerWalletDetails = [];
|
||||
String passengerTotalWalletAmount = '';
|
||||
@@ -67,6 +60,20 @@ class PaymentController extends GetxController {
|
||||
}
|
||||
|
||||
String paymentToken = '';
|
||||
|
||||
Future<void> addPassengersWallet(String amount) async {
|
||||
try {
|
||||
await CRUD().postWallet(link: AppLink.addPassengersWallet, payload: {
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
'amount': amount,
|
||||
});
|
||||
await getPassengerWallet();
|
||||
} catch (e) {
|
||||
Log.print(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<String> generateTokenPassenger(String amount) async {
|
||||
var res =
|
||||
await CRUD().post(link: AppLink.addPaymentTokenPassenger, payload: {
|
||||
@@ -86,27 +93,8 @@ class PaymentController extends GetxController {
|
||||
return d['message'];
|
||||
}
|
||||
|
||||
Future addSeferWallet(String paymentMethod, point) async {
|
||||
var seferToken = await generateTokenPassenger(point);
|
||||
await CRUD().postWallet(link: AppLink.addSeferWallet, payload: {
|
||||
'amount': point.toString(),
|
||||
'paymentMethod': paymentMethod,
|
||||
'passengerId': box.read(BoxName.passengerID).toString(),
|
||||
'token': seferToken,
|
||||
'driverId': 'passenger',
|
||||
});
|
||||
}
|
||||
|
||||
Future addPassengersWallet(String point) async {
|
||||
var token = await generateTokenPassenger(point);
|
||||
await CRUD().postWallet(link: AppLink.addPassengersWallet, payload: {
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
'balance': point,
|
||||
'token': token,
|
||||
});
|
||||
}
|
||||
|
||||
payToDriverForCancelAfterAppliedAndHeNearYou(String rideId) async {
|
||||
Future<void> payToDriverForCancelAfterAppliedAndHeNearYou(
|
||||
String rideId) async {
|
||||
{
|
||||
double costOfWaiting5Minute = box.read(BoxName.countryCode) == 'Egypt'
|
||||
? (4 * .08) + (5 * 1)
|
||||
@@ -135,14 +123,6 @@ class PaymentController extends GetxController {
|
||||
});
|
||||
|
||||
if (res != 'failure') {
|
||||
// Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
|
||||
// 'Cancel',
|
||||
// 'Trip Cancelled. The cost of the trip will be added to your wallet.'
|
||||
// .tr,
|
||||
// Get.find<RideLifecycleController>().driverToken,
|
||||
// [],
|
||||
// 'cancel',
|
||||
// );
|
||||
await NotificationService.sendNotification(
|
||||
category: 'Cancel',
|
||||
target: Get.find<RideLifecycleController>().driverToken.toString(),
|
||||
@@ -157,7 +137,7 @@ class PaymentController extends GetxController {
|
||||
}
|
||||
var paymentTokenWaitPassenger1 =
|
||||
await generateTokenPassenger((costOfWaiting5Minute * -1).toString());
|
||||
await CRUD().post(link: AppLink.addPassengersWallet, payload: {
|
||||
await CRUD().postWallet(link: AppLink.addPassengersWallet, payload: {
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
'balance': (costOfWaiting5Minute * -1).toString(),
|
||||
'token': paymentTokenWaitPassenger1,
|
||||
@@ -166,27 +146,6 @@ class PaymentController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
addPassengerWallet() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
await addSeferWallet('visa-in', selectedAmount.toString());
|
||||
await addPassengersWallet(selectedAmount == 100
|
||||
? '100'
|
||||
: selectedAmount == 200
|
||||
? '215'
|
||||
: selectedAmount == 400
|
||||
? '450'
|
||||
: selectedAmount == 1000
|
||||
? '1140'
|
||||
: '0');
|
||||
|
||||
// getPassengerWallet();
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
void onChangedPaymentMethodWallet(bool? value) {
|
||||
if (box.read(BoxName.passengerWalletTotal) == null ||
|
||||
double.parse(box.read(BoxName.passengerWalletTotal).toString()) <
|
||||
@@ -215,28 +174,6 @@ class PaymentController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void applyPromoCodeToPassenger() async {
|
||||
//TAWJIHI
|
||||
CRUD().get(link: AppLink.getPassengersPromo, payload: {
|
||||
'promo_code': promo.text,
|
||||
}).then((value) {
|
||||
var decod = jsonDecode(value);
|
||||
|
||||
if (decod["status"] == "success") {
|
||||
var firstElement = decod["message"][0];
|
||||
totalPassenger = totalPassenger -
|
||||
(totalPassenger * int.parse(firstElement['amount']));
|
||||
Get.find<RideLifecycleController>().promoTaken = true;
|
||||
update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 'https://accept.paymob.com/unifiedcheckout/?publicKey=egy_pk_live_mbjDC9Ni6FSHKmsz8sOHiVk2xd7oWRve&clientSecret=egy_sk_live_c0904e9cf04506ae64f818d4e075b4a957e3713fdf7a22cb7da30a29e72442b5'
|
||||
// أضف هذا الرابط إلى ملف AppLink الخاص بك
|
||||
|
||||
// هذه هي الدالة الجديدة التي ستستخدمها لبدء الدفع
|
||||
Future<void> payWithEcash(BuildContext context, String amount) async {
|
||||
try {
|
||||
// 1. يمكنك استخدام نفس طريقة التحقق بالبصمة إذا أردت
|
||||
@@ -293,225 +230,6 @@ class PaymentController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
// Future<void> payWithEcashDriver(BuildContext context, String amount) async {
|
||||
// try {
|
||||
// // يمكنك استخدام نفس طريقة التحقق بالبصمة إذا أردت
|
||||
// bool isAvailable = await LocalAuthentication().isDeviceSupported();
|
||||
// if (isAvailable) {
|
||||
// bool didAuthenticate = await LocalAuthentication().authenticate(
|
||||
// localizedReason: 'Use Touch ID or Face ID to confirm payment'.tr,
|
||||
// );
|
||||
|
||||
// if (didAuthenticate) {
|
||||
// // استدعاء الـ Endpoint الجديد على السيرفر الخاص بك
|
||||
// var res = await CRUD().postWallet(
|
||||
// link: AppLink.payWithEcashPassenger,
|
||||
// // link:
|
||||
// // 'https://wl.tripz-egypt.com/v1/main/ride/ecash/driver/payWithEcash.php',
|
||||
// payload: {
|
||||
// // أرسل البيانات التي يحتاجها السيرفر
|
||||
// "amount": amount,
|
||||
// // "driverId": box.read(BoxName.driverID), // تأكد من وجود هذا المتغير
|
||||
// "passengerId":
|
||||
// box.read(BoxName.passengerID), // تأكد من وجود هذا المتغير
|
||||
// },
|
||||
// );
|
||||
|
||||
// // التأكد من أن السيرفر أعاد رابط الدفع بنجاح
|
||||
// if (res != null &&
|
||||
// res['status'] == 'success' &&
|
||||
// res['message'] != null) {
|
||||
// final String paymentUrl = res['message'];
|
||||
// // الانتقال إلى شاشة الدفع الجديدة الخاصة بـ ecash للسائق
|
||||
// Navigator.push(
|
||||
// context,
|
||||
// MaterialPageRoute(
|
||||
// builder: (context) =>
|
||||
// EcashDriverPaymentScreen(paymentUrl: paymentUrl),
|
||||
// ),
|
||||
// );
|
||||
// } else {
|
||||
// // عرض رسالة خطأ في حال فشل السيرفر في إنشاء الرابط
|
||||
// Get.defaultDialog(
|
||||
// title: 'Error'.tr,
|
||||
// content: Text(
|
||||
// 'Failed to initiate payment. Please try again.'.tr,
|
||||
// style: AppStyle.title,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// } catch (e) {
|
||||
// Get.defaultDialog(
|
||||
// title: 'Error'.tr,
|
||||
// content: Text(
|
||||
// 'An error occurred during the payment process.'.tr,
|
||||
// style: AppStyle.title,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
/// شاشة جديدة ومبسطة خاصة بدفع السائقين عبر ecash
|
||||
|
||||
// Future<void> payWithMTNWallet(
|
||||
// BuildContext context, String amount, String currency) async {
|
||||
// // خزن سياق علوي آمن من البداية
|
||||
// final BuildContext safeContext =
|
||||
// Get.overlayContext ?? Get.context ?? context;
|
||||
|
||||
// // سبينر تحميل
|
||||
// if (!(Get.isDialogOpen ?? false)) {
|
||||
// Get.dialog(const Center(child: CircularProgressIndicator()),
|
||||
// barrierDismissible: false);
|
||||
// }
|
||||
|
||||
// try {
|
||||
// final phone = box.read(BoxName.phoneWallet) as String;
|
||||
// final passengerID = box.read(BoxName.passengerID).toString();
|
||||
// final formattedAmount = double.parse(amount).toStringAsFixed(0);
|
||||
|
||||
// Log.print("🚀 بدء عملية دفع MTN");
|
||||
// Log.print(
|
||||
// "📦 Payload: passengerID: $passengerID, amount: $formattedAmount, phone: $phone");
|
||||
|
||||
// // التحقق بالبصمة (اختياري) + حماية من الـ await
|
||||
// final localAuth = LocalAuthentication();
|
||||
// final isAuthSupported = await localAuth.isDeviceSupported();
|
||||
// if (isAuthSupported) {
|
||||
// final didAuth = await localAuth.authenticate(
|
||||
// localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
|
||||
// );
|
||||
// if (!didAuth) {
|
||||
// if (Get.isDialogOpen == true) Get.back();
|
||||
// Log.print("❌ المستخدم لم يؤكد بالبصمة/الوجه");
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// // 1) بدء الدفع
|
||||
// final responseData = await CRUD().postWalletMtn(
|
||||
// link: AppLink.payWithMTNStart,
|
||||
// payload: {
|
||||
// "amount": formattedAmount,
|
||||
// "passengerId": passengerID,
|
||||
// "phone": phone,
|
||||
// "lang": box.read(BoxName.lang) ?? 'ar',
|
||||
// },
|
||||
// );
|
||||
|
||||
// // Log.print("✅ استجابة الخادم (mtn_start_payment.php):");
|
||||
// // Log.print(responseData);
|
||||
// Log.print('responseData: ${responseData}');
|
||||
|
||||
// // فحص الاستجابة بقوة
|
||||
// late final Map<String, dynamic> startRes;
|
||||
// if (responseData is Map<String, dynamic>) {
|
||||
// startRes = responseData;
|
||||
// } else if (responseData is String) {
|
||||
// startRes = json.decode(responseData) as Map<String, dynamic>;
|
||||
// } else {
|
||||
// throw Exception("تم استلام نوع بيانات غير متوقع من الخادم.");
|
||||
// }
|
||||
|
||||
// if (startRes['status'] != 'success') {
|
||||
// final errorMsg = startRes['message']['Error']?.toString().tr ??
|
||||
// "فشل بدء عملية الدفع. حاول مرة أخرى.";
|
||||
// throw Exception(errorMsg);
|
||||
// }
|
||||
|
||||
// final messageData = startRes["message"] as Map<String, dynamic>;
|
||||
// final invoiceNumber = messageData["invoiceNumber"].toString();
|
||||
// final operationNumber = messageData["operationNumber"].toString();
|
||||
// final guid = messageData["guid"].toString();
|
||||
|
||||
// // Log.print(
|
||||
// // "📄 invoiceNumber: $invoiceNumber, 🔢 operationNumber: $operationNumber, 🧭 guid: $guid");
|
||||
|
||||
// // أغلق السبينر قبل إظهار حوار OTP
|
||||
// if (Get.isDialogOpen == true) Get.back();
|
||||
|
||||
// // 2) إدخال OTP بـ Get.defaultDialog (لا يستخدم context قابل للتلف)
|
||||
// String otpInput = "";
|
||||
// await Get.defaultDialog(
|
||||
// title: "أدخل كود التحقق",
|
||||
// barrierDismissible: false,
|
||||
// content: TextField(
|
||||
// keyboardType: TextInputType.number,
|
||||
// decoration: const InputDecoration(hintText: "كود OTP"),
|
||||
// onChanged: (v) => otpInput = v,
|
||||
// ),
|
||||
// confirm: TextButton(
|
||||
// onPressed: () {
|
||||
// if (otpInput.isEmpty ||
|
||||
// otpInput.length < 4 ||
|
||||
// otpInput.length > 8) {
|
||||
// Get.snackbar("تنبيه", "أدخل كود OTP صحيح (4–8 أرقام)");
|
||||
// return;
|
||||
// }
|
||||
// Get.back(result: otpInput);
|
||||
// },
|
||||
// child: const Text("تأكيد"),
|
||||
// ),
|
||||
// cancel: TextButton(
|
||||
// onPressed: () => Get.back(result: null),
|
||||
// child: const Text("إلغاء"),
|
||||
// ),
|
||||
// ).then((res) => otpInput = (res ?? "") as String);
|
||||
|
||||
// if (otpInput.isEmpty) {
|
||||
// Log.print("❌ لم يتم إدخال OTP");
|
||||
// return;
|
||||
// }
|
||||
// Log.print("🔐 تم إدخال OTP: $otpInput");
|
||||
|
||||
// // سبينر أثناء التأكيد
|
||||
// Get.dialog(const Center(child: CircularProgressIndicator()),
|
||||
// barrierDismissible: false);
|
||||
|
||||
// // 3) تأكيد الدفع
|
||||
// final confirmRes = await CRUD().postWalletMtn(
|
||||
// link: AppLink.payWithMTNConfirm,
|
||||
// payload: {
|
||||
// "invoiceNumber": invoiceNumber,
|
||||
// "operationNumber": operationNumber,
|
||||
// "guid": guid,
|
||||
// "otp": otpInput,
|
||||
// "phone": phone,
|
||||
// "lang": box.read(BoxName.lang) ?? 'ar',
|
||||
// },
|
||||
// );
|
||||
|
||||
// if (Get.isDialogOpen == true) Get.back();
|
||||
|
||||
// // Log.print("✅ استجابة mtn_confirm.php:");
|
||||
// // Log.print('confirmRes: ${confirmRes}');
|
||||
|
||||
// final ok = (confirmRes is Map && confirmRes['status'] == 'success');
|
||||
// if (ok) {
|
||||
// Get.defaultDialog(
|
||||
// title: "✅ نجاح",
|
||||
// content: const Text("تمت عملية الدفع وإضافة الرصيد إلى محفظتك."),
|
||||
// );
|
||||
// await getPassengerWallet();
|
||||
// } else {
|
||||
// final errorMsg = (confirmRes['message']['message']?.toString()) ??
|
||||
// "فشل في تأكيد الدفع";
|
||||
// Get.defaultDialog(title: "❌ فشل", content: Text(errorMsg.tr));
|
||||
// }
|
||||
// } catch (e, s) {
|
||||
// Log.print("🔥 خطأ أثناء الدفع عبر MTN:");
|
||||
// Log.print(e);
|
||||
// Log.print(s);
|
||||
// if (Get.isDialogOpen == true) Get.back();
|
||||
// Get.defaultDialog(
|
||||
// title: 'حدث خطأ',
|
||||
// content: Text(e.toString().replaceFirst("Exception: ", "")),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
Future<void> payWithSyriaTelWallet(String amount, String currency) async {
|
||||
// helper لفتح لودينغ بأمان
|
||||
Future<void> _showLoading() async {
|
||||
@@ -661,6 +379,104 @@ class PaymentController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> payWithMTNWallet(BuildContext context, String amount, String currency) async {
|
||||
try {
|
||||
final phone = walletphoneController.text.trim();
|
||||
if (phone.isEmpty) {
|
||||
Get.defaultDialog(title: 'Error'.tr, content: Text('Please enter phone number'.tr));
|
||||
return;
|
||||
}
|
||||
|
||||
Get.dialog(const Center(child: CircularProgressIndicator()), barrierDismissible: false);
|
||||
|
||||
var res = await CRUD().postWalletMtn(
|
||||
link: AppLink.createMtnInvoice,
|
||||
payload: {
|
||||
"amount": amount,
|
||||
"user_id": box.read(BoxName.passengerID).toString(),
|
||||
"user_type": "passenger",
|
||||
"mtn_phone": phone,
|
||||
},
|
||||
);
|
||||
|
||||
Get.back(); // close loading
|
||||
|
||||
late final Map<String, dynamic> resMap;
|
||||
if (res is Map<String, dynamic>) {
|
||||
resMap = res;
|
||||
} else if (res is String) {
|
||||
resMap = json.decode(res) as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("Unexpected response type");
|
||||
}
|
||||
|
||||
if (resMap['status'] == 'success') {
|
||||
Get.to(() => PaymentScreenMtn(
|
||||
invoiceNumber: resMap['invoice_number'],
|
||||
mtnNumber: resMap['mtn_payment_number'] ?? '---',
|
||||
amount: double.parse(amount),
|
||||
));
|
||||
} else {
|
||||
Get.defaultDialog(
|
||||
title: 'Error'.tr,
|
||||
content: Text(resMap['message']?.toString() ?? 'Failed to create invoice'.tr),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
Get.defaultDialog(title: 'Error'.tr, content: Text(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> payWithClickWallet(BuildContext context, String amount, String currency) async {
|
||||
try {
|
||||
final phone = walletphoneController.text.trim();
|
||||
if (phone.isEmpty) {
|
||||
Get.defaultDialog(title: 'Error'.tr, content: Text('Please enter phone number'.tr));
|
||||
return;
|
||||
}
|
||||
|
||||
Get.dialog(const Center(child: CircularProgressIndicator()), barrierDismissible: false);
|
||||
|
||||
var res = await CRUD().postWalletMtn(
|
||||
link: AppLink.createCliqInvoice,
|
||||
payload: {
|
||||
"amount": amount,
|
||||
"user_id": box.read(BoxName.passengerID).toString(),
|
||||
"user_type": "passenger",
|
||||
"click_phone": phone,
|
||||
},
|
||||
);
|
||||
|
||||
Get.back(); // close loading
|
||||
|
||||
late final Map<String, dynamic> resMap;
|
||||
if (res is Map<String, dynamic>) {
|
||||
resMap = res;
|
||||
} else if (res is String) {
|
||||
resMap = json.decode(res) as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("Unexpected response type");
|
||||
}
|
||||
|
||||
if (resMap['status'] == 'success') {
|
||||
Get.to(() => PaymentScreenCliq(
|
||||
invoiceNumber: resMap['invoice_number'],
|
||||
cliqAlias: resMap['cliq_alias'] ?? '---',
|
||||
amount: double.parse(amount),
|
||||
));
|
||||
} else {
|
||||
Get.defaultDialog(
|
||||
title: 'Error'.tr,
|
||||
content: Text(resMap['message']?.toString() ?? 'Failed to create invoice'.tr),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (Get.isDialogOpen ?? false) Get.back();
|
||||
Get.defaultDialog(title: 'Error'.tr, content: Text(e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
timestamp = now.millisecondsSinceEpoch;
|
||||
|
||||
@@ -45,30 +45,16 @@ class RateController extends GetxController {
|
||||
confirm: MyElevatedButton(title: 'Ok', onPressed: () => Get.back()));
|
||||
} else if (Get.find<PaymentController>().isWalletChecked == true) {
|
||||
double tip = 0;
|
||||
tip = (Get.find<RideLifecycleController>().totalPassenger) *
|
||||
tip = double.parse(Get.find<RideLifecycleController>().totalPassenger.toString()) *
|
||||
(double.parse(box.read(BoxName.tipPercentage).toString()));
|
||||
|
||||
if (tip > 0) {
|
||||
var res = await CRUD().post(link: AppLink.addTips, payload: {
|
||||
'passengerID': box.read(BoxName.passengerID),
|
||||
'passengerID': box.read(BoxName.passengerID).toString(),
|
||||
'driverID': Get.find<RideLifecycleController>().driverId.toString(),
|
||||
'rideID': Get.find<RideLifecycleController>().rideId.toString(),
|
||||
'tipAmount': tip.toString(),
|
||||
});
|
||||
await Get.find<PaymentController>()
|
||||
.addPassengersWallet(((-1) * tip).toString());
|
||||
var token1 = await Get.find<PaymentController>().generateTokenDriver(
|
||||
box.read(BoxName.countryCode) == 'Egypt'
|
||||
? tip.toStringAsFixed(0)
|
||||
: (tip * 100).toString());
|
||||
await CRUD().postWallet(link: AppLink.addDriversWalletPoints, payload: {
|
||||
'driverID': Get.find<RideLifecycleController>().driverId.toString(),
|
||||
'paymentID': '${Get.find<RideLifecycleController>().rideId}tip',
|
||||
'amount': box.read(BoxName.countryCode) == 'Egypt'
|
||||
? tip.toStringAsFixed(0)
|
||||
: (tip * 100).toString(),
|
||||
'paymentMethod': 'visa-tip',
|
||||
'token': token1,
|
||||
'country_code': box.read(BoxName.countryCode).toString(),
|
||||
});
|
||||
if (res != 'failure') {
|
||||
await NotificationService.sendNotification(
|
||||
@@ -76,7 +62,7 @@ class RateController extends GetxController {
|
||||
target: Get.find<RideLifecycleController>().driverToken.toString(),
|
||||
title: 'You Have Tips'.tr,
|
||||
body:
|
||||
'${'${tip.toString()}\$${' tips\nTotal is'.tr}'} ${tip + (Get.find<RideLifecycleController>().totalPassenger)}',
|
||||
'${'${tip.toString()}\$${' tips\nTotal is'.tr}'} ${tip + double.parse(Get.find<RideLifecycleController>().totalPassenger.toString())}',
|
||||
isTopic: false, // Important: this is a token
|
||||
tone: 'ding',
|
||||
driverList: [],
|
||||
|
||||
Reference in New Issue
Block a user