Initial push to my private server
This commit is contained in:
@@ -284,6 +284,9 @@ class RegistrationView extends StatelessWidget {
|
||||
|
||||
// STEP 3
|
||||
Widget _buildDocumentUploadStep(BuildContext ctx, RegistrationController c) {
|
||||
final String linkUpload =
|
||||
'https://syria.intaleq.xyz/intaleq/auth/syria/uploadImage.php';
|
||||
|
||||
return GetBuilder<RegistrationController>(
|
||||
builder: (ctrl) => SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
@@ -296,23 +299,31 @@ class RegistrationView extends StatelessWidget {
|
||||
const SizedBox(height: 20),
|
||||
_buildImagePickerBox(
|
||||
'Driver License (Front)'.tr,
|
||||
ctrl.driverLicenseFrontImage,
|
||||
() => ctrl.pickImage(ImageType.driverLicenseFront),
|
||||
ctrl.docUrls['driver_license_front'],
|
||||
// () => ctrl.pickImage(ImageType.driverLicenseFront),
|
||||
|
||||
() async =>
|
||||
await ctrl.choosImage(linkUpload, 'driver_license_front'),
|
||||
),
|
||||
_buildImagePickerBox(
|
||||
'Driver License (Back)'.tr,
|
||||
ctrl.driverLicenseBackImage,
|
||||
() => ctrl.pickImage(ImageType.driverLicenseBack),
|
||||
ctrl.docUrls['driver_license_back'],
|
||||
() async =>
|
||||
await ctrl.choosImage(linkUpload, 'driver_license_back'),
|
||||
// () => ctrl.pickImage(ImageType.driverLicenseBack),
|
||||
),
|
||||
_buildImagePickerBox(
|
||||
'Car Registration (Front)'.tr,
|
||||
ctrl.carLicenseFrontImage,
|
||||
() => ctrl.pickImage(ImageType.carLicenseFront),
|
||||
ctrl.docUrls['car_license_front'],
|
||||
() async =>
|
||||
await ctrl.choosImage(linkUpload, 'car_license_front'),
|
||||
// () => ctrl.pickImage(ImageType.carLicenseFront),
|
||||
),
|
||||
_buildImagePickerBox(
|
||||
'Car Registration (Back)'.tr,
|
||||
ctrl.carLicenseBackImage,
|
||||
() => ctrl.pickImage(ImageType.carLicenseBack),
|
||||
ctrl.docUrls['car_license_back'],
|
||||
() async => await ctrl.choosImage(linkUpload, 'car_license_back'),
|
||||
// () => ctrl.pickImage(ImageType.carLicenseBack),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -329,7 +340,7 @@ class RegistrationView extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImagePickerBox(String title, File? img, VoidCallback onTap) {
|
||||
Widget _buildImagePickerBox(String title, String? img, VoidCallback onTap) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
child: InkWell(
|
||||
@@ -337,8 +348,24 @@ class RegistrationView extends StatelessWidget {
|
||||
child: SizedBox(
|
||||
height: 150,
|
||||
width: double.infinity,
|
||||
child: img != null
|
||||
? Image.file(img, fit: BoxFit.fill)
|
||||
child: (img != null && img.isNotEmpty)
|
||||
? Image.network(
|
||||
img,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.broken_image, size: 40, color: Colors.red),
|
||||
const SizedBox(height: 8),
|
||||
Text('Image not available',
|
||||
style: TextStyle(color: Colors.red[700])),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
196
lib/views/home/my_wallet/pay_out_screen.dart
Normal file
196
lib/views/home/my_wallet/pay_out_screen.dart
Normal 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();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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: ", "")),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user