Initial push to my private server

This commit is contained in:
Hamza-Ayed
2025-09-21 15:02:12 +03:00
parent 7e904ae460
commit f08ee61a7e
32 changed files with 1622 additions and 373 deletions

View File

@@ -1,5 +1,7 @@
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/controller/firebase/local_notification.dart';
import 'package:sefer_driver/controller/functions/network/net_guard.dart';
import 'package:sefer_driver/controller/functions/sms_egypt_controller.dart';
import 'package:sefer_driver/main.dart';
import 'package:sefer_driver/views/auth/captin/login_captin.dart';
import 'package:sefer_driver/views/home/Captin/driver_map_page.dart';
@@ -14,10 +16,12 @@ import '../../../../../constant/colors.dart';
import '../../../../../constant/links.dart';
import '../../../../../controller/firebase/firbase_messge.dart';
import '../../../../../controller/functions/crud.dart';
import '../../../../../controller/functions/encrypt_decrypt.dart';
import '../../../../../controller/home/captin/order_request_controller.dart';
import '../../../../../controller/home/navigation/navigation_view.dart';
import '../../../../Rate/ride_calculate_driver.dart';
import '../../../../auth/syria/registration_view.dart';
import '../../../../widgets/error_snakbar.dart';
GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
final firebaseMessagesController =
@@ -181,10 +185,8 @@ GetBuilder<HomeCaptainController> leftMainMenuCaptainIcons() {
// child: Builder(builder: (context) {
// return IconButton(
// onPressed: () async {
// box.remove(BoxName.agreeTerms);
// Get.to(() => const NavigationView());
// // box.write(BoxName.statusDriverLocation, 'off');
// var finger = await storage.read(key: BoxName.fingerPrint);
//
// },
// icon: const Icon(
// FontAwesome5.grin_tears,

View File

@@ -6,6 +6,7 @@ import 'package:just_audio/just_audio.dart';
import 'package:sefer_driver/constant/api_key.dart';
import '../../../../constant/box_name.dart';
import '../../../../constant/links.dart';
import '../../../../controller/firebase/firbase_messge.dart';
import '../../../../controller/firebase/local_notification.dart';
import '../../../../controller/functions/crud.dart';
import '../../../../main.dart';
@@ -218,16 +219,22 @@ class _OrderOverlayState extends State<OrderOverlay>
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
if (AppLink.endPoint != AppLink.seferCairoServer) {
CRUD().post(
link: "${AppLink.endPoint}/ride/rides/updateStausFromSpeed.php",
payload: {
'id': orderData!.orderId,
'rideTimeStart': DateTime.now().toString(),
'status': 'Apply',
'driver_id': box.read(BoxName.driverID),
});
}
List<String> bodyToPassenger = [
_getData(6).toString(),
_getData(8).toString(),
_getData(9).toString(),
];
final fmc = Get.isRegistered<FirebaseMessagesController>()
? Get.find<FirebaseMessagesController>()
: Get.put(FirebaseMessagesController());
fmc.sendNotificationToDriverMAP(
"Accepted Ride",
'your ride is Accepted'.tr,
_getData(9).toString(),
bodyToPassenger,
'start.wav',
);
final payload = {
// بيانات أساسية
'driver_id': driverId,

View File

@@ -280,7 +280,7 @@ class _OrderRequestPageState extends State<OrderRequestPage> {
: Get.put(FirebaseMessagesController());
fmc.sendNotificationToDriverMAP(
"Accepted Ride".tr,
"Accepted Ride",
'your ride is Accepted'.tr,
controller.myList[9].toString(),
bodyToPassenger,

View File

@@ -4,6 +4,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:get/get.dart';
import 'package:sefer_driver/views/home/my_wallet/pay_out_screen.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
@@ -164,7 +165,7 @@ class CardSeferWalletDriver extends StatelessWidget {
void _showCashOutDialog(
BuildContext context, CaptainWalletController captainWalletController) {
double minAmount = 20.0; // الحد الأدنى للسحب
double minAmount = 20000.0; // الحد الأدنى للسحب
if (double.parse(captainWalletController.totalAmountVisa) >= minAmount) {
Get.defaultDialog(
barrierDismissible: false,
@@ -208,16 +209,29 @@ class CardSeferWalletDriver extends StatelessWidget {
confirm: MyElevatedButton(
title: 'تأكيد'.tr,
onPressed: () async {
box.write(
BoxName.phoneWallet, captainWalletController.phoneWallet);
box.write(BoxName.walletType,
Get.find<SyrianPayoutController>().dropdownValue.toString());
if (captainWalletController.formKey.currentState!.validate()) {
Get.back();
String amountAfterFee =
(double.parse(captainWalletController.totalAmountVisa) - 5)
.toStringAsFixed(0);
await Get.put(PaymobPayout()).payToWalletDriverAll(
amountAfterFee,
Get.find<SyrianPayoutController>().dropdownValue.toString(),
captainWalletController.phoneWallet.text.toString(),
);
Get.to(() => PayoutScreen(
amountToWithdraw:
double.parse(captainWalletController.totalAmountVisa),
payoutPhoneNumber:
captainWalletController.phoneWallet.text.toString(),
walletType: Get.find<SyrianPayoutController>()
.dropdownValue
.toString(),
));
// String amountAfterFee =
// (double.parse(captainWalletController.totalAmountVisa) - 5)
// .toStringAsFixed(0);
// await Get.put(PaymobPayout()).payToWalletDriverAll(
// amountAfterFee,
// Get.find<SyrianPayoutController>().dropdownValue.toString(),
// captainWalletController.phoneWallet.text.toString(),
// );
}
},
kolor: AppColor.greenColor,
@@ -274,7 +288,7 @@ class MyDropDownSyria extends StatelessWidget {
onChanged: (String? newValue) {
controller.changeValue(newValue);
},
items: <String>['syriatel', 'mtn']
items: <String>['Syriatel', 'Cash Mobile', 'Sham Cash']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
@@ -288,7 +302,7 @@ class MyDropDownSyria extends StatelessWidget {
// هذا المتحكم ضروري لعمل القائمة المنسدلة
class SyrianPayoutController extends GetxController {
String dropdownValue = 'syriatel';
String dropdownValue = 'Syriatel';
void changeValue(String? newValue) {
if (newValue != null) {

View File

@@ -0,0 +1,196 @@
import 'package:flutter/material.dart';
import 'package:local_auth/local_auth.dart';
import 'package:sefer_driver/constant/box_name.dart';
import 'package:sefer_driver/main.dart';
import '../../../controller/payment/smsPaymnet/pay_out_syria_controller.dart';
class PayoutScreen extends StatefulWidget {
// استقبال كل البيانات المطلوبة جاهزة
final double amountToWithdraw;
final String payoutPhoneNumber;
final String walletType;
const PayoutScreen({
super.key,
required this.amountToWithdraw,
required this.payoutPhoneNumber,
required this.walletType,
});
@override
_PayoutScreenState createState() => _PayoutScreenState();
}
class _PayoutScreenState extends State<PayoutScreen> {
final _payoutService = PayoutService();
final _localAuth = LocalAuthentication();
bool _isLoading = false;
Future<void> _handlePayoutRequest() async {
try {
// 1. طلب المصادقة البيومترية
bool didAuthenticate = await _localAuth.authenticate(
localizedReason: 'استخدم بصمة الإصبع لتأكيد عملية السحب',
options: const AuthenticationOptions(
biometricOnly: true,
sensitiveTransaction: true,
),
);
if (didAuthenticate && mounted) {
setState(() => _isLoading = true);
// 2. إرسال الطلب إلى السيرفر بالبيانات الجاهزة
final result = await _payoutService.requestPayout(
driverId:
box.read(BoxName.driverID).toString(), // استبدله بـ box.read
amount: widget.amountToWithdraw,
payoutPhoneNumber: widget.payoutPhoneNumber,
walletType: widget.walletType,
);
setState(() => _isLoading = false);
if (result != null && result.contains("successfully")) {
// 3. عرض رسالة النجاح النهائية
_showSuccessDialog();
} else {
_showErrorDialog(result ?? "حدث خطأ غير معروف.");
}
}
} catch (e) {
setState(() => _isLoading = false);
_showErrorDialog("جهازك لا يدعم المصادقة البيومترية أو لم يتم إعدادها.");
debugPrint("Biometric error: $e");
}
}
@override
Widget build(BuildContext context) {
// حساب المبلغ الإجمالي المخصوم
final totalDeducted = widget.amountToWithdraw + PayoutService.payoutFee;
return Scaffold(
appBar: AppBar(title: const Text("تأكيد سحب الأموال")),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Icon(Icons.wallet, size: 64, color: Colors.blue),
const SizedBox(height: 16),
Text(
"تأكيد تفاصيل عملية السحب",
style: Theme.of(context).textTheme.headlineSmall,
textAlign: TextAlign.center,
),
const SizedBox(height: 24),
_buildSummaryCard(totalDeducted),
const SizedBox(height: 32),
_isLoading
? const Center(child: CircularProgressIndicator())
: ElevatedButton.icon(
onPressed: _handlePayoutRequest,
icon: const Icon(Icons.fingerprint),
label: const Text("تأكيد السحب بالبصمة"),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
textStyle: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
),
],
),
),
);
}
Widget _buildSummaryCard(double totalDeducted) {
return Card(
elevation: 2,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_summaryRow("المبلغ المسحوب:",
"${widget.amountToWithdraw.toStringAsFixed(2)} ل.س"),
const Divider(),
_summaryRow("عمولة السحب:",
"${PayoutService.payoutFee.toStringAsFixed(2)} ل.س"),
const Divider(thickness: 1.5),
_summaryRow(
"الإجمالي المخصوم من رصيدك:",
"${totalDeducted.toStringAsFixed(2)} ل.س",
isTotal: true,
),
const SizedBox(height: 16),
_summaryRow("سيتم التحويل إلى هاتف:", widget.payoutPhoneNumber),
_summaryRow("عبر محفظة:", widget.walletType),
],
),
),
);
}
Widget _summaryRow(String title, String value, {bool isTotal = false}) {
final titleStyle = TextStyle(
fontSize: 16,
color: isTotal ? Theme.of(context).primaryColor : Colors.black87,
fontWeight: isTotal ? FontWeight.bold : FontWeight.normal,
);
final valueStyle = titleStyle.copyWith(
fontWeight: FontWeight.bold,
);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title, style: titleStyle),
Text(value, style: valueStyle),
],
),
);
}
void _showErrorDialog(String message) {
if (!mounted) return;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text('حدث خطأ'),
content: Text(message),
actions: [
TextButton(
child: const Text('موافق'),
onPressed: () => Navigator.of(ctx).pop())
],
),
);
}
void _showSuccessDialog() {
if (!mounted) return;
showDialog(
context: context,
barrierDismissible: false,
builder: (ctx) => AlertDialog(
title: const Text('تم إرسال طلبك بنجاح'),
content: Text(
"سيتم تحويل المال إلى المحفظة التي أوردتها (${widget.walletType})، إلى الرقم ${widget.payoutPhoneNumber}، خلال مدة قصيرة. يرجى الانتظار، ستصلك رسالة تأكيد من محفظتك حال وصولها. شكراً لك."),
actions: [
TextButton(
child: const Text('موافق'),
onPressed: () {
Navigator.of(ctx).pop();
Navigator.of(context).pop();
},
),
],
),
);
}
}

View File

@@ -7,12 +7,14 @@ import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/constant/style.dart';
import 'package:sefer_driver/controller/home/payment/captain_wallet_controller.dart';
import 'package:sefer_driver/controller/payment/payment_controller.dart';
import 'package:sefer_driver/controller/payment/smsPaymnet/payment_services.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../controller/functions/crud.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../widgets/elevated_btn.dart';
import '../../widgets/my_textField.dart';
import 'ecash.dart';
@@ -40,21 +42,29 @@ class PointsCaptain extends StatelessWidget {
title: 'Which method you will pay'.tr,
titleStyle: AppStyle.title,
content: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'${'you can buy '.tr}$countPoint ${'L.S'.tr}${'by '.tr}${'$pricePoint'.tr}',
style: AppStyle.title,
),
MyElevatedButton(
title: 'Pay with Credit Card'.tr,
onPressed: () async {
Get.back();
payWithEcashDriver(context, pricePoint.toString());
// var d = jsonDecode(res);
}, //51524
),
// Add some spacing between buttons
GestureDetector(
onTap: () async {
Get.back();
payWithEcashDriver(context, pricePoint.toString());
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Pay with Debit Card'.tr),
const SizedBox(width: 10),
Icon(Icons.credit_card_sharp,
color: AppColor.blueColor, size: 70),
],
)),
GestureDetector(
onTap: () async {
Get.back();
@@ -84,44 +94,122 @@ class PointsCaptain extends StatelessWidget {
}
}));
},
child: Image.asset(
'assets/images/mtn.png',
width: 70,
height: 70,
fit: BoxFit.fill,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Pay by MTN Wallet'.tr),
const SizedBox(width: 10),
Image.asset(
'assets/images/cashMTN.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
)),
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: paymentController.formKey,
child: MyTextForm(
controller:
paymentController.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '963991234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (paymentController.formKey.currentState!
.validate()) {
box.write(
BoxName.phoneWallet,
paymentController
.walletphoneController.text);
await payWithSyriaTelWallet(
context, pricePoint.toString(), 'SYP');
}
}));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Pay by Syriatel Wallet'.tr),
const SizedBox(width: 10),
Image.asset(
'assets/images/syriatel.jpeg',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
)),
GestureDetector(
onTap: () async {
Get.back();
Get.defaultDialog(
barrierDismissible: false,
title: 'Insert Wallet phone number'.tr,
content: Form(
key: paymentController.formKey,
child: MyTextForm(
controller:
paymentController.walletphoneController,
label: 'Insert Wallet phone number'.tr,
hint: '963941234567',
type: TextInputType.phone)),
confirm: MyElevatedButton(
title: 'OK'.tr,
onPressed: () async {
Get.back();
if (paymentController.formKey.currentState!
.validate()) {
box.write(
BoxName.phoneWallet,
paymentController
.walletphoneController.text);
// await payWithSyriaTelWallet(
// context, pricePoint.toString(), 'SYP');
bool isAuthSupported =
await LocalAuthentication()
.isDeviceSupported();
if (isAuthSupported) {
bool didAuthenticate =
await LocalAuthentication()
.authenticate(
localizedReason:
'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
);
if (!didAuthenticate) {
if (Get.isDialogOpen ?? false) Get.back();
print(
"❌ User did not authenticate with biometrics");
return;
}
}
Get.to(() => PaymentScreenSmsProvider(
amount: pricePoint));
}
}));
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('Pay by Sham Cash'.tr),
const SizedBox(width: 10),
Image.asset(
'assets/images/shamCash.png',
width: 70,
height: 70,
fit: BoxFit.fill,
),
],
)),
// MyElevatedButton(
// kolor: AppColor.redColor,
// title: 'Pay with Wallet'.tr,
// onPressed: () async {
// Get.back();
// Get.defaultDialog(
// barrierDismissible: false,
// title: 'Insert Wallet phone number'.tr,
// content: Form(
// key: paymentController.formKey,
// child: MyTextForm(
// controller:
// paymentController.walletphoneController,
// label: 'Insert Wallet phone number'.tr,
// hint: '963941234567',
// type: TextInputType.phone)),
// confirm: MyElevatedButton(
// title: 'OK'.tr,
// onPressed: () async {
// Get.back();
// if (paymentController.formKey.currentState!
// .validate()) {
// box.write(
// BoxName.phoneWallet,
// paymentController
// .walletphoneController.text);
// await payWithMTNWallet(
// context, pricePoint.toString(), 'SYP');
// }
// }));
// },
// ),
],
));
},
@@ -485,7 +573,7 @@ Future<void> payWithMTNWallet(
barrierDismissible: false);
try {
String phone = box.read(BoxName.phoneWallet) ?? '963992952235';
String phone = box.read(BoxName.phoneWallet);
String driverID = box.read(BoxName.driverID).toString();
String formattedAmount = double.parse(amount).toStringAsFixed(0);
@@ -507,12 +595,13 @@ Future<void> payWithMTNWallet(
}
// 1⃣ استدعاء mtn_start_payment.php (الملف الجديد)
var responseData = await CRUD().postWallet(
var responseData = await CRUD().postWalletMtn(
link: AppLink.payWithMTNStart,
payload: {
"amount": formattedAmount,
"passengerId": driverID,
"phone": phone,
"lang": box.read(BoxName.lang) ?? 'ar',
},
);
@@ -540,7 +629,7 @@ Future<void> payWithMTNWallet(
}
if (startRes['status'] != 'success') {
String errorMsg = startRes['message']?.toString() ??
final errorMsg = startRes['message']['Error']?.toString().tr ??
"فشل بدء عملية الدفع. حاول مرة أخرى.";
throw Exception(errorMsg);
}
@@ -555,7 +644,7 @@ Future<void> payWithMTNWallet(
print(
"📄 invoiceNumber: $invoiceNumber, 🔢 operationNumber: $operationNumber, 🧭 guid: $guid");
if (Get.isDialogOpen ?? false)
if (Get.isDialogOpen == true)
Get.back(); // إغلاق مؤشر التحميل قبل عرض حوار OTP
// 2⃣ عرض واجهة إدخال OTP
@@ -609,7 +698,8 @@ Future<void> payWithMTNWallet(
if (Get.isDialogOpen ?? false) Get.back();
print("✅ استجابة mtn_confirm.php:");
print(confirmRes);
// print(confirmRes);
Log.print('confirmRes: ${confirmRes}');
if (confirmRes != null && confirmRes['status'] == 'success') {
Get.defaultDialog(
@@ -635,3 +725,158 @@ Future<void> payWithMTNWallet(
);
}
}
Future<void> payWithSyriaTelWallet(
BuildContext context, String amount, String currency) async {
// Show a loading indicator for better user experience
Get.dialog(const Center(child: CircularProgressIndicator()),
barrierDismissible: false);
try {
String phone = box.read(BoxName.phoneWallet);
String driverID = box.read(BoxName.driverID).toString();
String formattedAmount = double.parse(amount).toStringAsFixed(0);
// --- CHANGE 1: Updated log messages for clarity ---
print("🚀 Starting Syriatel payment process");
print(
"📦 Payload: driverID: $driverID, amount: $formattedAmount, phone: $phone");
// Optional: Biometric authentication
bool isAuthSupported = await LocalAuthentication().isDeviceSupported();
if (isAuthSupported) {
bool didAuthenticate = await LocalAuthentication().authenticate(
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
);
if (!didAuthenticate) {
if (Get.isDialogOpen ?? false) Get.back();
print("❌ User did not authenticate with biometrics");
return;
}
}
// --- CHANGE 2: Updated API link and payload for starting payment ---
// Make sure you have defined `payWithSyriatelStart` in your AppLink class
var responseData = await CRUD().postWalletMtn(
link: AppLink.payWithSyriatelStart, // Use the new Syriatel start link
payload: {
"amount": formattedAmount,
"driverId": driverID, // Key changed from 'passengerId' to 'driverId'
"phone": phone,
"lang": box.read(BoxName.lang) ?? 'ar',
},
);
print("✅ Server response (start_payment.php):");
Log.print('responseData: ${responseData}');
// Robustly parse the server's JSON response
Map<String, dynamic> startRes;
if (responseData is Map<String, dynamic>) {
startRes = responseData;
} else if (responseData is String) {
try {
startRes = json.decode(responseData);
} catch (e) {
throw Exception(
"Failed to parse server response. Response: $responseData");
}
} else {
throw Exception("Received an unexpected data type from the server.");
}
if (startRes['status'] != 'success') {
String errorMsg = startRes['message']?.toString() ??
"Failed to start the payment process. Please try again.";
throw Exception(errorMsg);
}
// --- CHANGE 3: Extract `transactionID` from the response ---
// The response structure is now simpler. We only need the transaction ID.
final messageData = startRes["message"];
final transactionID = messageData["transactionID"].toString();
print("📄 TransactionID: $transactionID");
if (Get.isDialogOpen == true) Get.back(); // Close loading indicator
// Show the OTP input dialog
String? otp = await showDialog<String>(
context: context,
barrierDismissible: false,
builder: (context) {
String input = "";
return AlertDialog(
title: const Text("أدخل كود التحقق"),
content: TextField(
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: "كود OTP"),
onChanged: (val) => input = val,
),
actions: [
TextButton(
child: const Text("تأكيد"),
onPressed: () => Navigator.of(context).pop(input),
),
TextButton(
child: const Text("إلغاء"),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
if (otp == null || otp.isEmpty) {
print("❌ OTP was not entered.");
return;
}
print("🔐 OTP entered: $otp");
Get.dialog(const Center(child: CircularProgressIndicator()),
barrierDismissible: false);
// --- CHANGE 4: Updated API link and payload for confirming payment ---
// Make sure you have defined `payWithSyriatelConfirm` in your AppLink class
var confirmRes = await CRUD().postWalletMtn(
// Changed from postWalletMtn if they are different
link: AppLink.payWithSyriatelConfirm, // Use the new Syriatel confirm link
payload: {
"transactionID": transactionID, // Use the transaction ID
"otp": otp,
// The other parameters (phone, guid, etc.) are no longer needed
},
);
if (Get.isDialogOpen ?? false) Get.back();
print("✅ Response from confirm_payment.php:");
Log.print('confirmRes: ${confirmRes}');
if (confirmRes != null && confirmRes['status'] == 'success') {
Get.defaultDialog(
title: "✅ نجاح",
content: const Text("تمت عملية الدفع وإضافة الرصيد إلى محفظتك."),
);
} else {
// --- CHANGE 5: Simplified error message extraction ---
// The new PHP script sends the error directly in the 'message' field.
String errorMsg =
confirmRes?['message']?.toString() ?? "فشل في تأكيد الدفع";
Get.defaultDialog(
title: "❌ فشل",
content: Text(errorMsg.tr),
);
}
} catch (e, s) {
// --- CHANGE 6: Updated general error log message ---
print("🔥 Error during Syriatel Wallet payment:");
print(e);
print(s);
if (Get.isDialogOpen ?? false) Get.back();
Get.defaultDialog(
title: 'حدث خطأ',
content: Text(e.toString().replaceFirst("Exception: ", "")),
);
}
}