feat: implement secure OTP-based payout workflow with dynamic fee calculation and improved authentication checks
This commit is contained in:
@@ -4,13 +4,14 @@ import 'package:siro_driver/controller/functions/crud.dart';
|
||||
class PayoutService {
|
||||
final String _baseUrl =
|
||||
"https://walletintaleq.intaleq.xyz/v1/main/sms_webhook";
|
||||
static const double payoutFee = 5000.0; // عمولة السحب الثابتة
|
||||
static const double payoutFee = 50.0; // عمولة السحب الثابتة
|
||||
|
||||
/// دالة لإنشاء طلب سحب جديد على السيرفر
|
||||
///
|
||||
/// تعيد رسالة النجاح من السيرفر، أو رسالة خطأ في حال الفشل.
|
||||
Future<String?> requestPayout({
|
||||
required String driverId,
|
||||
required String otp,
|
||||
walletType,
|
||||
payoutPhoneNumber,
|
||||
required double amount,
|
||||
@@ -23,6 +24,7 @@ class PayoutService {
|
||||
'amount': amount.toString(),
|
||||
'phone': payoutPhoneNumber.toString(),
|
||||
'wallet_type': walletType.toString(),
|
||||
'otp': otp,
|
||||
}).timeout(const Duration(seconds: 20));
|
||||
|
||||
if (response != 'failure') {
|
||||
@@ -42,4 +44,24 @@ class PayoutService {
|
||||
return "حدث خطأ غير متوقع. يرجى المحاولة مرة أخرى.";
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> requestOtp({required String phone}) async {
|
||||
final url = "https://walletintaleq.intaleq.xyz/v1/auth/otp/request.php";
|
||||
try {
|
||||
final response = await CRUD().postWallet(link: url, payload: {
|
||||
'phone_number': phone,
|
||||
'user_type': 'driver',
|
||||
'context': 'payout',
|
||||
}).timeout(const Duration(seconds: 20));
|
||||
|
||||
if (response != 'failure') {
|
||||
final data = (response);
|
||||
return data['status'] == 'success';
|
||||
}
|
||||
return false;
|
||||
} catch (e) {
|
||||
debugPrint("Exception during OTP request: $e");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,22 +41,15 @@ class _PayoutScreenState extends State<PayoutScreen> {
|
||||
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,
|
||||
);
|
||||
|
||||
// 2. طلب رمز التحقق OTP
|
||||
bool otpSent = await _payoutService.requestOtp(phone: widget.payoutPhoneNumber);
|
||||
setState(() => _isLoading = false);
|
||||
|
||||
if (result != null && result.contains("successfully")) {
|
||||
// 3. عرض رسالة النجاح النهائية
|
||||
_showSuccessDialog();
|
||||
if (otpSent) {
|
||||
// 3. عرض حوار إدخال الرمز
|
||||
_showOtpDialog();
|
||||
} else {
|
||||
_showErrorDialog(result ?? "حدث خطأ غير معروف.");
|
||||
_showErrorDialog("فشل في إرسال رمز التحقق. يرجى المحاولة لاحقاً.");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
@@ -66,6 +59,70 @@ class _PayoutScreenState extends State<PayoutScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
void _showOtpDialog() {
|
||||
final TextEditingController otpController = TextEditingController();
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: const Text('أدخل رمز التحقق (OTP)'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const Text('تم إرسال رمز تحقق إلى رقم هاتفك.'),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: otpController,
|
||||
keyboardType: TextInputType.number,
|
||||
maxLength: 4,
|
||||
decoration: const InputDecoration(
|
||||
border: OutlineInputBorder(),
|
||||
labelText: 'رمز التحقق',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: const Text('إلغاء'),
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
),
|
||||
ElevatedButton(
|
||||
child: const Text('تأكيد'),
|
||||
onPressed: () {
|
||||
final otp = otpController.text.trim();
|
||||
if (otp.length >= 3) {
|
||||
Navigator.of(ctx).pop();
|
||||
_submitPayoutWithOtp(otp);
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _submitPayoutWithOtp(String otp) async {
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
final result = await _payoutService.requestPayout(
|
||||
driverId: box.read(BoxName.driverID).toString(),
|
||||
otp: otp,
|
||||
amount: widget.amountToWithdraw,
|
||||
payoutPhoneNumber: widget.payoutPhoneNumber,
|
||||
walletType: widget.walletType,
|
||||
);
|
||||
|
||||
setState(() => _isLoading = false);
|
||||
|
||||
if (result != null && result.contains("successfully")) {
|
||||
_showSuccessDialog();
|
||||
} else {
|
||||
_showErrorDialog(result ?? "حدث خطأ غير معروف.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// حساب المبلغ الإجمالي المخصوم
|
||||
|
||||
Reference in New Issue
Block a user