25-10-9/1

This commit is contained in:
Hamza-Ayed
2025-10-09 23:31:28 +03:00
parent de84662e02
commit 092f4de7d3
7 changed files with 344 additions and 347 deletions

View File

@@ -47,8 +47,8 @@ android {
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = 29 minSdk = 29
targetSdk = 36 targetSdk = 36
versionCode = 22 versionCode = 25
versionName = '1.0.22' versionName = '1.0.25'
multiDexEnabled = true multiDexEnabled = true
ndk { ndk {
abiFilters "armeabi-v7a", "arm64-v8a" abiFilters "armeabi-v7a", "arm64-v8a"

View File

@@ -33,11 +33,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>10</string> <string>12</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1.0.10</string> <string>1.0.12</string>
<key>FirebaseAppDelegateProxyEnabled</key> <key>FirebaseAppDelegateProxyEnabled</key>
<string>NO</string> <string>NO</string>
<key>GMSApiKey</key> <key>GMSApiKey</key>

View File

@@ -1,31 +1,31 @@
List<String> passengerMessages = [ List<String> passengerMessages = [
// --- رسائل العروض والتوفير --- // --- رسائل العروض والتوفير ---
"مشاويرك علينا بأحلى الأسعار! 🚗 فوت على تطبيق انطلق وشوف العروض الجديدة اللي ناطرتك. 🌟", "وفر على حالك: 🚗 أسعار انطلق نازلة كتير! شوف العروض الجديدة وفوت هلأ قبل ما تخلص. 🌟",
"بدك توفّر بمشاويرك؟ 🤔 افتح تطبيق انطلق هلأ! أسعارنا ما بتلاقي متلها.", "خصم اليوم: 🤔 لا تفوّت الفرصة! افتح تطبيق انطلق وشوف الأسعار يلي ما بتنعاد.",
"خصم خاص الك اليوم! 🎁 افتح تطبيق انطلق واحجز مشوارك الجاي بسعر أقل.", "عروض نارية: 🎁 اليوم خصم خاص إلك، احجز مشوارك الجاي بسعر ولا أروع!",
"لا تحمل هم المصاريف! 💸 مع انطلق، كل مشاويرك اقتصادية ومريحة.", "مشاوير اقتصادية: 💸 مع انطلق بتتحرك براحتك وبتدفع أقل، جرب وشوف الفرق!",
// --- رسائل السهولة والراحة --- // --- رسائل السهولة والراحة ---
"كبسة زر ومشوارك صار عندك! 📲 افتح تطبيق انطلق واحجز بسهولة.", "مشوار بكبسة زر: 📲 افتح تطبيق انطلق، وخلِّي السيارة توصلك لعندك بثواني.",
"ليش معجوق بالمواصلات؟ 🤔 انطلق بيوصلك وين ما بدك براحة وسرعة.", "ارتاح من المواصلات: 🤔 خلّي انطلق يريحك من الانتظار والزحمة، وانطلق وين ما بدك.",
"سيارتك ناطرتك! 🚕 وين ما كنت، اطلب من انطلق وبنوصلك بأسرع وقت.", "سيارتك جاهزة: 🚕 الكابتن ناطر طلبك، حدد وجهتك وخلّي الطريق علينا.",
"مشوارك الجاي مع انطلق! 🛣️ استرخي واستمتع بالطريق، نحنا بنهتم بالتفاصيل.", "رحلة مريحة: 🛣️ ارتاح بالكرسي، نحنا منهتم بكل التفاصيل من الباب للباب.",
// --- رسائل الأمان والثقة --- // --- رسائل الأمان والثقة ---
"مشوارك آمن ومريح مع انطلق. 🙏 كباتنّا محترفين و بيهمنا توصل بالسلامة.", "رحلتك بأمان: 🙏 كل كباتنّا مدرّبين وملتزمين، وسلامتك أولويتنا.",
"انطلق بقلب مطمن! 🔒 سلامتك أولويتنا بكل رحلة بتطلعها معنا.", "سافر وانت مطمّن: 🔒 مع انطلق كل شي موثوق ومسجّل لتكون مرتاح البال.",
"مع انطلق، فيك تشارك تفاصيل رحلتك مع اللي بتحبن ليضل بالك مرتاح. ❤️", "شارك رحلتك: ❤️ فيك تبعت تفاصيل المشوار لأهلك أو رفقاتك بخطوة وحدة.",
"كل رحلاتنا مسجّلة لضمان أمانك. سافر بثقة مع انطلق. ✅", "انطلق بثقة: ✅ كل الرحلات مراقبة لتضمن تجربة آمنة ومريحة 100%.",
// --- رسائل تفاعلية ومناسبات خاصة --- // --- رسائل تفاعلية ومناسبات ---
"الويكند بلّش! 🥳 مشاويرك مع الصحبة صارت أسهل وأوفر. اطلب انطلق.", "الويكند بلّش: 🥳 خلي مشاويرك مع الأصحاب علينا، وفر وقتك وفلوسك مع انطلق.",
"رايح عالشغل؟ 💼 خلي انطلق يوصلك بلا عجقة السير وخليك مرتاح.", "رايح عالشغل: 💼 لا تتأخر، افتح التطبيق وخلي الكابتن يوصلك بلا تعب.",
"الدنيا شوب؟ ☀️ اطلب من انطلق والكابتن بيوصل لعندك.", "الشمس مولّعة: ☀️ لا تمشي تحت الحر، خلي السيارة تجي لعندك.",
"تأخرت؟ لا تاكل هم! 🏃‍♂️ تطبيق انطلق هو أسرع طريق لتوصل عبكير.", "مستعجل: 🏃‍♂️ لا تقلق، انطلق أسرع طريق لتوصل عموعدك.",
// --- رسائل تشجيعية عامة --- // --- رسائل تشجيعية عامة ---
"وين وجهتك اليوم؟ 🗺️ مدينة كاملة بانتظارك، وخلي مشوارك على انطلق.", "وين رايح اليوم؟ 🗺️ وين ما كانت وجهتك، انطلق بيخدمك بكل مكان وبأي وقت.",
"جرّبت فئات سيارات انطلق الجديدة؟ 🚘 دلّل حالك بتجربة مميزة اليوم.", "جرب شي جديد: 🚘 شوف فئات السيارات الجديدة وخلي رحلتك أريح وأجمل.",
"شكراً لاختيارك انطلق! ⭐ منتمنى الك دايماً رحلات سعيدة ومريحة.", "شكراً لاختيارك: ⭐ وجودك معنا بيفرحنا، ونتمنى دايماً تكون رحلتك مريحة وسعيدة.",
"عند انطلق كل شي جديد! ✨ فوت عالبرنامج وضلّك على اطلاع بآخر خدماتنا وعروضنا." "كل يوم جديد: ✨ فوت عالتطبيق وتابع آخر التحديثات والعروض يلي نازلة خصيصاً إلك."
]; ];

View File

@@ -48,7 +48,7 @@ class LoginController extends GetxController {
var dev = ''; var dev = '';
@override @override
void onInit() async { void onInit() async {
// await getAppTester(); await getJWT();
// Log.print('box.read(BoxName.isTest): ${box.read(BoxName.isTest)}'); // Log.print('box.read(BoxName.isTest): ${box.read(BoxName.isTest)}');
box.write(BoxName.countryCode, 'Syria'); box.write(BoxName.countryCode, 'Syria');
FirebaseMessagesController().getToken(); FirebaseMessagesController().getToken();

View File

@@ -22,26 +22,29 @@ class PhoneAuthHelper {
/// Sends an OTP to the provided phone number. /// Sends an OTP to the provided phone number.
static Future<bool> sendOtp(String phoneNumber) async { static Future<bool> sendOtp(String phoneNumber) async {
try { try {
// Log.print('_sendOtpUrl: ${_sendOtpUrl}');
// Log.print('phoneNumber: ${phoneNumber}');
final response = await CRUD().post( final response = await CRUD().post(
link: _sendOtpUrl, link: _sendOtpUrl,
payload: {'receiver': phoneNumber}, payload: {'receiver': phoneNumber},
); );
Log.print('response: ${response}'); // Log.print('response: ${response}');
if (response != 'failure') { if (response != 'failure') {
final data = (response); final data = (response);
// if (data['status'] == 'success') { if (data['status'] == 'success') {
mySnackbarSuccess('An OTP has been sent to your WhatsApp number.'.tr); mySnackbarSuccess('An OTP has been sent to your WhatsApp number.'.tr);
return true; return true;
// } else { } else {
// mySnackeBarError(data['message'] ?? 'Failed to send OTP.'); mySnackeBarError(data['message'] ?? 'Failed to send OTP.');
// return false; return false;
// } }
} else { } else {
mySnackeBarError('Server error. Please try again.'.tr); mySnackeBarError('Server error. Please try again.'.tr);
return false; return false;
} }
} catch (e) { } catch (e) {
Log.print('e: ${e}'); // Log.print('e: ${e}');
// mySnackeBarError('An error occurred: $e'); // mySnackeBarError('An error occurred: $e');
return false; return false;
} }

View File

@@ -673,312 +673,305 @@ class PaymentController extends GetxController {
/// شاشة جديدة ومبسطة خاصة بدفع السائقين عبر ecash /// شاشة جديدة ومبسطة خاصة بدفع السائقين عبر ecash
Future<void> payWithMTNWallet( // Future<void> payWithMTNWallet(
BuildContext context, String amount, String currency) async { // BuildContext context, String amount, String currency) async {
// خزن سياق علوي آمن من البداية // // خزن سياق علوي آمن من البداية
final BuildContext safeContext = // final BuildContext safeContext =
Get.overlayContext ?? Get.context ?? context; // Get.overlayContext ?? Get.context ?? context;
// سبينر تحميل // // سبينر تحميل
if (!(Get.isDialogOpen ?? false)) { // if (!(Get.isDialogOpen ?? false)) {
Get.dialog(const Center(child: CircularProgressIndicator()), // Get.dialog(const Center(child: CircularProgressIndicator()),
barrierDismissible: false); // 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);
// print("🚀 بدء عملية دفع MTN");
// 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();
// print("❌ المستخدم لم يؤكد بالبصمة/الوجه");
// return;
// }
// }
// // 1) بدء الدفع
// final responseData = await CRUD().postWalletMtn(
// link: AppLink.payWithMTNStart,
// payload: {
// "amount": formattedAmount,
// "passengerId": passengerID,
// "phone": phone,
// "lang": box.read(BoxName.lang) ?? 'ar',
// },
// );
// // print("✅ استجابة الخادم (mtn_start_payment.php):");
// // 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();
// // 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 صحيح (48 أرقام)");
// 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) {
// print("❌ لم يتم إدخال OTP");
// return;
// }
// 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();
// // 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) {
// print("🔥 خطأ أثناء الدفع عبر MTN:");
// print(e);
// 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 {
if (!(Get.isDialogOpen ?? false)) {
Get.dialog(const Center(child: CircularProgressIndicator()),
barrierDismissible: false);
}
} }
// helper لإغلاق أي حوار مفتوح
void _closeAnyDialog() {
if (Get.isDialogOpen ?? false) {
Get.back();
}
}
await _showLoading();
try { try {
final phone = box.read(BoxName.phoneWallet) as String; final phone = box.read(BoxName.phoneWallet) as String;
final passengerID = box.read(BoxName.passengerID).toString(); final passengerId = box.read(BoxName.passengerID).toString();
final formattedAmount = double.parse(amount).toStringAsFixed(0); final formattedAmount = double.parse(amount).toStringAsFixed(0);
print("🚀 بدء عملية دفع MTN"); print("🚀 Syriatel payment start");
print( print(
"📦 Payload: passengerID: $passengerID, amount: $formattedAmount, phone: $phone"); "📦 Payload => passengerId:$passengerId amount:$formattedAmount phone:$phone");
// التحقق بالبصمة (اختياري) + حماية من الـ await // مصادقة حيوية (اختياري)
final localAuth = LocalAuthentication(); final auth = LocalAuthentication();
final isAuthSupported = await localAuth.isDeviceSupported(); if (await auth.isDeviceSupported()) {
if (isAuthSupported) { final ok = await auth.authenticate(
final didAuth = await localAuth.authenticate(
localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع', localizedReason: 'استخدم بصمة الإصبع أو الوجه لتأكيد الدفع',
); );
if (!didAuth) { if (!ok) {
if (Get.isDialogOpen == true) Get.back(); _closeAnyDialog();
print("المستخدم لم يؤكد بالبصمة/الوجه"); print("User did not authenticate");
return; return;
} }
} }
// 1) بدء الدفع // 1) بدء عملية الدفع
final responseData = await CRUD().postWalletMtn( final startRaw = await CRUD().postWalletMtn(
link: AppLink.payWithMTNStart, link: AppLink.payWithSyriatelStart,
payload: { payload: {
"amount": formattedAmount, "amount": formattedAmount,
"passengerId": passengerID, "passengerId": passengerId,
"phone": phone, "phone": phone,
"lang": box.read(BoxName.lang) ?? 'ar', "lang": box.read(BoxName.lang) ?? 'ar',
}, },
); );
// print("✅ استجابة الخادم (mtn_start_payment.php):"); print("✅ Server response (start): $startRaw");
// print(responseData);
Log.print('responseData: ${responseData}');
// فحص الاستجابة بقوة // تحويل الاستجابة إلى Map
late final Map<String, dynamic> startRes; late final Map<String, dynamic> startRes;
if (responseData is Map<String, dynamic>) { if (startRaw is Map<String, dynamic>) {
startRes = responseData; startRes = startRaw;
} else if (responseData is String) { } else if (startRaw is String) {
startRes = json.decode(responseData) as Map<String, dynamic>; startRes = json.decode(startRaw) as Map<String, dynamic>;
} else { } else {
throw Exception("تم استلام نوع بيانات غير متوقع من الخادم."); throw Exception("Unexpected start response type");
} }
if (startRes['status'] != 'success') { if (startRes['status'] != 'success') {
final errorMsg = startRes['message']['Error']?.toString().tr ?? final msg =
"فشل بدء عملية الدفع. حاول مرة أخرى."; (startRes['message'] ?? 'Failed to start payment').toString();
throw Exception(errorMsg); throw Exception(msg);
} }
final messageData = startRes["message"] as Map<String, dynamic>; final messageData = startRes['message'] as Map<String, dynamic>;
final invoiceNumber = messageData["invoiceNumber"].toString(); final transactionID = messageData['transactionID'].toString();
final operationNumber = messageData["operationNumber"].toString(); print("📄 transactionID: $transactionID");
final guid = messageData["guid"].toString();
// print( // 2) اطلب من المستخدم إدخال OTP عبر Get.dialog (بدون context)
// "📄 invoiceNumber: $invoiceNumber, 🔢 operationNumber: $operationNumber, 🧭 guid: $guid"); _closeAnyDialog(); // أغلق اللودينغ أولاً
final otpController = TextEditingController();
// أغلق السبينر قبل إظهار حوار OTP final otp = await Get.dialog<String>(
if (Get.isDialogOpen == true) Get.back(); AlertDialog(
title: const Text("أدخل كود التحقق"),
// 2) إدخال OTP بـ Get.defaultDialog (لا يستخدم context قابل للتلف) content: TextField(
String otpInput = ""; controller: otpController,
await Get.defaultDialog( keyboardType: TextInputType.number,
title: "أدخل كود التحقق", decoration: const InputDecoration(hintText: "كود OTP"),
barrierDismissible: false, ),
content: TextField( actions: [
keyboardType: TextInputType.number, TextButton(
decoration: const InputDecoration(hintText: "كود OTP"), child: const Text("تأكيد"),
onChanged: (v) => otpInput = v, onPressed: () => Get.back(result: otpController.text.trim()),
),
confirm: TextButton(
onPressed: () {
if (otpInput.isEmpty ||
otpInput.length < 4 ||
otpInput.length > 8) {
Get.snackbar("تنبيه", "أدخل كود OTP صحيح (48 أرقام)");
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) {
print("❌ لم يتم إدخال OTP");
return;
}
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();
// 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) {
print("🔥 خطأ أثناء الدفع عبر MTN:");
print(e);
print(s);
if (Get.isDialogOpen == true) Get.back();
Get.defaultDialog(
title: 'حدث خطأ',
content: Text(e.toString().replaceFirst("Exception: ", "")),
);
}
}
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):");
print(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(
TextButton( child: const Text("إلغاء"),
child: const Text("تأكيد"), onPressed: () => Get.back(result: null),
onPressed: () => Navigator.of(context).pop(input), ),
), ],
TextButton( ),
child: const Text("إلغاء"), barrierDismissible: false,
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
); );
if (otp == null || otp.isEmpty) { if (otp == null || otp.isEmpty) {
print("❌ OTP was not entered."); print("❌ OTP not provided");
return; return;
} }
print("🔐 OTP entered: $otp"); print("🔐 OTP: $otp");
Get.dialog(const Center(child: CircularProgressIndicator()), await _showLoading();
barrierDismissible: false);
// --- CHANGE 4: Updated API link and payload for confirming payment --- // 3) تأكيد الدفع
// Make sure you have defined `payWithSyriatelConfirm` in your AppLink class final confirmRaw = await CRUD().postWallet(
var confirmRes = await CRUD().postWallet( link: AppLink.payWithSyriatelConfirm,
// Changed from postWalletMtn if they are different
link:
AppLink.payWithSyriatelConfirm, // Use the new Syriatel confirm link
payload: { payload: {
"transactionID": transactionID, // Use the transaction ID "transactionID": transactionID,
"otp": otp, "otp": otp,
// The other parameters (phone, guid, etc.) are no longer needed
}, },
); );
if (Get.isDialogOpen ?? false) Get.back(); _closeAnyDialog(); // أغلق اللودينغ
print("✅ Response from confirm_payment.php:"); print("✅ Response (confirm): $confirmRaw");
Log.print('confirmRes: ${confirmRes}');
if (confirmRes != null && confirmRes['status'] == 'success') { late final Map<String, dynamic> confirmRes;
if (confirmRaw is Map<String, dynamic>) {
confirmRes = confirmRaw;
} else if (confirmRaw is String) {
confirmRes = json.decode(confirmRaw) as Map<String, dynamic>;
} else {
throw Exception("Unexpected confirm response type");
}
if (confirmRes['status'] == 'success') {
Get.defaultDialog( Get.defaultDialog(
title: "✅ نجاح", title: "✅ نجاح",
content: const Text("تمت عملية الدفع وإضافة الرصيد إلى محفظتك."), content: const Text("تمت عملية الدفع وإضافة الرصيد إلى محفظتك."),
); );
} else { } else {
// --- CHANGE 5: Simplified error message extraction --- final msg = (confirmRes['message'] ?? 'فشل في تأكيد الدفع').toString();
// The new PHP script sends the error directly in the 'message' field.
String errorMsg =
confirmRes?['message']?.toString() ?? "فشل في تأكيد الدفع";
Get.defaultDialog( Get.defaultDialog(
title: "❌ فشل", title: "❌ فشل",
content: Text(errorMsg.tr), content: Text(msg),
); );
} }
} catch (e, s) { } catch (e, s) {
// --- CHANGE 6: Updated general error log message --- print("🔥 Error during Syriatel Wallet payment:\n$e\n$s");
print("🔥 Error during Syriatel Wallet payment:"); _closeAnyDialog();
print(e);
print(s);
if (Get.isDialogOpen ?? false) Get.back();
Get.defaultDialog( Get.defaultDialog(
title: 'حدث خطأ', title: 'حدث خطأ',
content: Text(e.toString().replaceFirst("Exception: ", "")), content: Text(e.toString().replaceFirst("Exception: ", "")),

View File

@@ -338,68 +338,69 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
// ); // );
// }, // },
// ), // ),
GestureDetector( // GestureDetector(
onTap: () async { // onTap: () async {
Get.back(); // Get.back();
Get.defaultDialog( // Get.defaultDialog(
barrierDismissible: false, // barrierDismissible: false,
title: 'Insert Wallet phone number'.tr, // title: 'Insert Wallet phone number'.tr,
content: Form( // content: Form(
key: controller.formKey, // key: controller.formKey,
child: MyTextForm( // child: MyTextForm(
controller: controller.walletphoneController, // controller: controller.walletphoneController,
label: 'Insert Wallet phone number'.tr, // label: 'Insert Wallet phone number'.tr,
hint: '963941234567', // hint: '963941234567',
type: TextInputType.phone)), // type: TextInputType.phone)),
confirm: MyElevatedButton( // confirm: MyElevatedButton(
title: 'OK'.tr, // title: 'OK'.tr,
onPressed: () async { // onPressed: () async {
Get.back(); // Get.back();
if (controller.formKey.currentState!.validate()) { // if (controller.formKey.currentState!.validate()) {
if (controller.selectedAmount != 0) { // if (controller.selectedAmount != 0) {
controller.isLoading = true; // controller.isLoading = true;
controller.update(); // controller.update();
box.write(BoxName.phoneWallet, // box.write(BoxName.phoneWallet,
(controller.walletphoneController.text)); // (controller.walletphoneController.text));
Get.back(); // Get.back();
await controller.payWithMTNWallet( // await controller.payWithMTNWallet(
context, // context,
controller.selectedAmount.toString(), // controller.selectedAmount.toString(),
'SYP', // 'SYP',
); // );
await controller.getPassengerWallet(); // await controller.getPassengerWallet();
// controller.isLoading = false;
// controller.update();
// } else {
// Toast.show(
// context,
// '⚠️ You need to choose an amount!'.tr,
// AppColor.redColor,
// );
// }
// }
// }));
// },
// child: Padding(
// padding: const EdgeInsets.all(8.0),
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// Text(
// 'Pay by MTN Wallet'.tr,
// style: AppStyle.title,
// ),
// const SizedBox(width: 10),
// Image.asset(
// 'assets/images/cashMTN.png',
// width: 70,
// height: 70,
// fit: BoxFit.contain,
// ),
// ],
// ),
// )),
controller.isLoading = false;
controller.update();
} else {
Toast.show(
context,
'⚠️ You need to choose an amount!'.tr,
AppColor.redColor,
);
}
}
}));
},
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Pay by MTN Wallet'.tr,
style: AppStyle.title,
),
const SizedBox(width: 10),
Image.asset(
'assets/images/cashMTN.png',
width: 70,
height: 70,
fit: BoxFit.contain,
),
],
),
)),
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
Get.back(); Get.back();
@@ -420,7 +421,7 @@ void showPaymentOptions(BuildContext context, PaymentController controller) {
if (controller.formKey.currentState!.validate()) { if (controller.formKey.currentState!.validate()) {
box.write(BoxName.phoneWallet, box.write(BoxName.phoneWallet,
controller.walletphoneController.text); controller.walletphoneController.text);
await controller.payWithSyriaTelWallet(context, await controller.payWithSyriaTelWallet(
controller.selectedAmount.toString(), 'SYP'); controller.selectedAmount.toString(), 'SYP');
} }
})); }));