Update: 2026-07-13 04:13:50

This commit is contained in:
Hamza-Ayed
2026-07-13 04:13:50 +03:00
parent 653d73422f
commit 7bf6dc1be7
265 changed files with 11 additions and 15250 deletions
@@ -0,0 +1,104 @@
<?php
/**
* driverWallet/add.php — إضافة رصيد لمحفظة السائق
*
* ═══════════════════════════════════════════════════════════════
* SECURITY FIX:
* - إضافة التحقق من ملكية الحساب (driverID == JWT user_id)
* - لف العملية في Transaction ذرية
* - استخدام FOR UPDATE لمنع Race Condition على التوكن
* - التحقق من صحة المبلغ
* ═══════════════════════════════════════════════════════════════
*/
// Include the database connection file (calls authenticateJWT)
include "../../connect.php";
// ── 1. استخراج user_id من JWT المصادق ──────────────────────────
$jwtUserId = $decodedToken->user_id ?? $decodedToken->sub ?? null;
// ── 2. استخراج والتحقق من البيانات ──────────────────────────
$driverID = filterRequest("driverID");
$paymentID = filterRequest("paymentID");
$amount = filterRequest("amount");
$paymentMethod = filterRequest("paymentMethod");
$token = filterRequest("token");
if (empty($driverID) || !isset($amount) || empty($paymentMethod) || empty($token)) {
printFailure("Missing required parameters");
exit;
}
// ── 3. التحقق من ملكية الحساب ────────────────────────────────
// السائق المصادق يمكنه فقط إضافة رصيد لمحفظته الشخصية
if ($jwtUserId !== null && $driverID !== $jwtUserId) {
error_log(sprintf(
'⚠️ [SECURITY] Ownership mismatch in add.php | jwt_user=%s | requested_driverID=%s | IP=%s',
$jwtUserId, $driverID, $_SERVER['REMOTE_ADDR'] ?? 'unknown'
));
http_response_code(403);
printFailure("Forbidden: You can only modify your own wallet");
exit;
}
// ── 4. التحقق من المبلغ ─────────────────────────────────────
$amount = floatval($amount);
if ($amount <= 0 || $amount > 1000000) {
printFailure("Invalid amount");
exit;
}
// ── 5. العملية الذرية ─────────────────────────────────────────
try {
$con->beginTransaction();
// التحقق من التوكن مع قفل السجل (FOR UPDATE) لمنع Race Condition
$stmt = $con->prepare("SELECT * FROM payment_tokens WHERE token = :token AND isUsed = FALSE FOR UPDATE");
$stmt->execute([':token' => $token]);
$tokenData = $stmt->fetch();
if ($tokenData) {
// إدخال سجل المحفظة
$sql = "INSERT INTO `driverWallet` (
`driverID`,
`paymentID`,
`amount`,
`paymentMethod`
) VALUES (
:driverID,
:paymentID,
:amount,
:paymentMethod
);";
$stmt = $con->prepare($sql);
$stmt->execute([
':driverID' => $driverID,
':paymentID' => $paymentID,
':amount' => $amount,
':paymentMethod' => $paymentMethod
]);
if ($stmt->rowCount() > 0) {
// تحديث حالة التوكن
$stmt = $con->prepare("UPDATE payment_tokens SET isUsed = TRUE WHERE id = :tokenID");
$stmt->execute([':tokenID' => $tokenData['id']]);
$con->commit();
printSuccess("Record saved successfully");
} else {
$con->rollBack();
printFailure("Failed to save record");
}
} else {
$con->rollBack();
printFailure("Invalid or already used token");
}
} catch (Exception $e) {
if ($con->inTransaction()) {
$con->rollBack();
}
error_log("[driverWallet/add] " . $e->getMessage());
printFailure("An error occurred");
}
@@ -0,0 +1,88 @@
<?php
// Include the database connection file
include "../../jwtconnect.php";
//add300ToDriver.php
// Get the request parameters
$driverID = filterRequest("driverID");
$paymentID = filterRequest("paymentID");
$amount = filterRequest("amount");
$paymentMethod = filterRequest("paymentMethod");
$phone = filterRequest("phone");
// -------------------------------------------------------------
// 1) ATOMIC CHECK + INSERT TO PREVENT RACE CONDITION
// -------------------------------------------------------------
$con->beginTransaction();
$check = $con->prepare("
SELECT id
FROM driverWallet
WHERE driverID = :driverID AND paymentMethod = :paymentMethod
LIMIT 1
FOR UPDATE
");
$check->execute([
':driverID' => $driverID,
':paymentMethod' => $paymentMethod
]);
if ($check->rowCount() > 0) {
$con->rollBack();
printFailure("لقد تم منح هذا الدفع للسائق مسبقاً — لا يمكن تكراره.");
exit;
}
// -------------------------------------------------------------
// 2) INSERT INTO driverWallet
// -------------------------------------------------------------
$sql = "INSERT INTO `driverWallet` (
`driverID`,
`paymentID`,
`amount`,
`paymentMethod`
) VALUES (
:driverID,
:paymentID,
:amount,
:paymentMethod
);";
$stmt = $con->prepare($sql);
$stmt->execute(array(
':driverID' => $driverID,
':paymentID' => $paymentID,
':amount' => $amount,
':paymentMethod' => $paymentMethod
));
$con->commit();
if ($stmt->rowCount() > 0) {
printSuccess("Record saved successfully");
// Notify driver
$messageBody = "تم إضافة رصيد بقيمة $amount إلى محفظتك بنجاح.";
// sendWhatsAppFromServer($phone, $messageBody);
// -------------------------------------------------------------
// 3) INSERT 30,000 POINTS FOR NEW DRIVER
// -------------------------------------------------------------
$sqlPoints = "INSERT INTO `paymentsDriverPoints`
(`amount`, `payment_method`, `driverID`, `created_at`, `updated_at`)
VALUES (:amount, :method, :driverID, NOW(), NOW())";
$stmtPoints = $con->prepare($sqlPoints);
$stmtPoints->execute(array(
':amount' => 300,
':method' => $paymentMethod,
':driverID' => $driverID
));
} else {
printFailure("Failed to save record");
}
+125
View File
@@ -0,0 +1,125 @@
<?php
/**
* addFromAdmin.php — إضافة رصيد من المسؤول (Admin Only)
*
* ═══════════════════════════════════════════════════════════════
* SECURITY FIX: كان هذا الملف بدون أي مصادقة! ❌
* الآن يتطلب:
* 1. JWT مصادق (عبر authenticateJWT)
* 2. التحقق من دور المسؤول (admin role)
* 3. التحقق من المبلغ (حد أقصى)
* 4. تسجيل تدقيق لكل عملية (audit log)
* ═══════════════════════════════════════════════════════════════
*/
// Include the database connection WITH JWT authentication
include "../../connect.php";
// connect.php calls authenticateJWT() → 5-layer security check ✅
// ── 1. استخراج user_id من JWT المصادق ──────────────────────────
$adminUserId = $decodedToken->user_id ?? $decodedToken->sub ?? null;
$adminRole = $decodedToken->role ?? null;
// ── 2. التحقق من صلاحيات المسؤول ────────────────────────────
// فقط المسؤول يمكنه إضافة رصيد يدوياً
if ($adminRole !== 'admin' && $adminRole !== 'super_admin') {
error_log(sprintf(
'⚠️ [SECURITY] Non-admin attempted addFromAdmin | user=%s | role=%s | IP=%s',
$adminUserId,
$adminRole ?? '(null)',
$_SERVER['REMOTE_ADDR'] ?? 'unknown'
));
http_response_code(403);
printFailure("Forbidden: Admin access required");
exit;
}
// ── 3. استخراج والتحقق من البيانات ──────────────────────────
$driverID = filterRequest("driverID");
$paymentID = filterRequest("paymentID");
$amount = filterRequest("amount");
$paymentMethod = filterRequest("paymentMethod");
$phone = filterRequest("phone");
if (empty($driverID) || !isset($amount) || empty($paymentMethod)) {
printFailure("Missing required parameters: driverID, amount, paymentMethod");
exit;
}
// ── 4. التحقق من المبلغ ─────────────────────────────────────
$amount = floatval($amount);
if ($amount <= 0 || $amount > 1000000) {
error_log(sprintf(
'⚠️ [SECURITY] Invalid amount in addFromAdmin | admin=%s | amount=%s | driverID=%s',
$adminUserId, $amount, $driverID
));
printFailure("Invalid amount: must be between 0 and 1,000,000");
exit;
}
// ── 5. العملية الذرية ─────────────────────────────────────────
try {
$con->beginTransaction();
// إدخال سجل المحفظة
$sql = "INSERT INTO `driverWallet` (
`driverID`,
`paymentID`,
`amount`,
`paymentMethod`
) VALUES (
:driverID,
:paymentID,
:amount,
:paymentMethod
);";
$stmt = $con->prepare($sql);
$stmt->execute([
':driverID' => $driverID,
':paymentID' => $paymentID ?? ('admin_' . time() . '_' . bin2hex(random_bytes(4))),
':amount' => $amount,
':paymentMethod' => $paymentMethod
]);
if ($stmt->rowCount() > 0) {
// ── 6. تسجيل تدقيق (Audit Log) ─────────────────────────
$auditStmt = $con->prepare(
"INSERT INTO `admin_audit_log` (`admin_id`, `action`, `table_name`, `record_id`, `details`)
VALUES (:admin_id, :action, :table_name, :record_id, :details)"
);
$auditStmt->execute([
':admin_id' => $adminUserId,
':action' => 'addFromAdmin_wallet',
':table_name' => 'driverWallet',
':record_id' => $driverID,
':details' => json_encode([
'amount' => $amount,
'paymentMethod' => $paymentMethod,
'phone' => $phone,
'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
'timestamp' => date('Y-m-d H:i:s')
], JSON_UNESCAPED_UNICODE)
]);
$con->commit();
printSuccess("Record saved successfully");
// إرسال إشعار واتساب للسائق
if (!empty($phone)) {
$messageBody = "تم إضافة رصيد بقيمة $amount إلى محفظتك بنجاح.";
sendWhatsAppFromServer($phone, $messageBody);
}
} else {
$con->rollBack();
printFailure("Failed to save record");
}
} catch (Exception $e) {
if ($con->inTransaction()) {
$con->rollBack();
}
error_log("[addFromAdmin] Error: " . $e->getMessage());
printFailure("An error occurred");
}
@@ -0,0 +1,50 @@
<?php
include "../../connect.php";
//addPaymentToken.php
$driverID = filterRequest("driverID");
$amount = filterRequest("amount");
// Check if required fields are present
if ($driverID === null || $amount === null) {
printFailure("Missing required fields: driverID and amount must be provided");
exit;
}
// Generate a more secure token
$token = generateSecureToken($driverID, $amount);
// Store the token in the database
$stmt = $con->prepare("INSERT INTO payment_tokens (token, driverID, dateCreated, amount) VALUES (?, ?, NOW(), ?)");
try {
$stmt->execute([$token, $driverID, $amount]);
if ($stmt->rowCount() > 0) {
printSuccess($token);
} else {
printFailure("Failed to save record");
}
} catch (PDOException $e) {
error_log("[addPaymentToken] " . $e->getMessage());
printFailure("Database error");
}
function generateSecureToken($driverID, $amount) {
global $secretKey;
// Concatenate the parameters
$data = $driverID . $amount . time();
// Add the secret key from the environment variable
$data .= $secretKey;
// Generate a hash
$hash = hash('sha256', $data);
// Add some randomness
$randomBytes = bin2hex(random_bytes(16));
// Combine hash and random bytes
$token = $hash . $randomBytes;
// Truncate to a reasonable length (e.g., 64 characters)
return substr($token, 0, 64);
}
@@ -0,0 +1,104 @@
<?php
/**
* add_s2s_reward.php — Payment Server Endpoint
*
* Inserts wallet credit/debit records into driverWallet.
* Authenticated via X-S2S-Api-Key header matching the S2S_SHARED_KEY environment variable.
*/
require_once __DIR__ . '/../../jwtconnect.php';
define('S2S_SHARED_KEY', getenv('S2S_SHARED_KEY'));
$providedKey = $_SERVER['HTTP_X_S2S_API_KEY'] ?? '';
if (empty($providedKey) || $providedKey !== S2S_SHARED_KEY) {
http_response_code(401);
printFailure("Unauthorized: Invalid or missing X-S2S-Api-Key.");
exit;
}
$driverID = filterRequest("driverID");
$paymentID = filterRequest("paymentID");
$amount = filterRequest("amount");
$paymentMethod = filterRequest("paymentMethod");
$points = filterRequest("points"); // Optional raw points
if (empty($driverID) || empty($paymentID) || !isset($amount) || empty($paymentMethod)) {
printFailure("Missing required parameters: driverID, paymentID, amount, paymentMethod");
exit;
}
try {
$con->beginTransaction();
// Prevent duplicate challenge claims using paymentsDriverPoints table
if (strpos($paymentMethod, 'daily_') === 0 || strpos($paymentMethod, 'weekly_') === 0) {
$checkSql = "SELECT id FROM paymentsDriverPoints WHERE driverID = :driver_id AND payment_method = :challenge_id AND DATE(created_at) = CURDATE() FOR UPDATE";
$stmtCheck = $con->prepare($checkSql);
$stmtCheck->execute([
':driver_id' => $driverID,
':challenge_id' => $paymentMethod
]);
if ($stmtCheck->rowCount() > 0) {
$con->rollBack();
printFailure("Reward already claimed today");
exit();
}
}
$sql = "INSERT INTO `driverWallet` (
`driverID`,
`paymentID`,
`amount`,
`paymentMethod`
) VALUES (
:driverID,
:paymentID,
:amount,
:paymentMethod
);";
$stmt = $con->prepare($sql);
$stmt->execute([
':driverID' => $driverID,
':paymentID' => $paymentID,
':amount' => $amount,
':paymentMethod' => $paymentMethod
]);
if ($stmt->rowCount() > 0) {
// If points are provided, also insert into paymentsDriverPoints
if (!empty($points)) {
$sqlPoints = "INSERT INTO `paymentsDriverPoints` (
`amount`,
`payment_method`,
`driverID`
) VALUES (
:points,
:paymentMethod,
:driverID
);";
$stmtPoints = $con->prepare($sqlPoints);
$stmtPoints->execute([
':points' => $points,
':paymentMethod' => $paymentMethod,
':driverID' => $driverID
]);
}
$con->commit();
printSuccess("Record saved successfully");
} else {
$con->rollBack();
printFailure("Failed to save record");
}
} catch (Exception $e) {
if ($con->inTransaction()) {
$con->rollBack();
}
error_log("add_s2s_reward: " . $e->getMessage());
printFailure("An error occurred");
}
?>
@@ -0,0 +1,68 @@
<?php
include '../../connect.php';
error_reporting(E_ALL);
ini_set('display_errors', 0);
header('Content-Type: application/json');
$driverID = filterRequest('driverID');
$amount = floatval(filterRequest('amount'));
if (empty($driverID) || empty($amount) || $amount <= 0) {
echo json_encode(['status' => 'error', 'message' => 'Missing required fields or invalid amount']);
exit;
}
try {
$con->beginTransaction();
// 1. Fetch current budget
$stmt = $con->prepare("SELECT SUM(amount) as diff FROM payments WHERE captain_id = :driverID FOR UPDATE");
$stmt->execute([':driverID' => $driverID]);
$sumRow = $stmt->fetch(PDO::FETCH_ASSOC);
$totalBudget = floatval($sumRow['diff']);
if ($totalBudget < $amount) {
$con->rollBack();
echo json_encode(['status' => 'error', 'message' => 'Insufficient budget']);
exit;
}
// 2. Generate unique tokens
$paymentID1 = "budget2pt_" . time() . bin2hex(random_bytes(4));
$paymentID2 = "pt2budget_" . time() . bin2hex(random_bytes(4));
$token1 = bin2hex(random_bytes(32));
$token2 = bin2hex(random_bytes(32));
// 3. Deduct from budget (payments)
$deductAmount = -$amount;
$stmt = $con->prepare("INSERT INTO payments (captain_id, amount, rideId, payment_method, passengerID, token)
VALUES (:driverID, :amount, :rideId, 'myBudget', 'myBudgetToPoint', :token)");
$stmt->execute([
':driverID' => $driverID,
':amount' => $deductAmount,
':rideId' => $paymentID1,
':token' => $token1
]);
// 4. Add to points (paymentsDriverPoints)
$stmt = $con->prepare("INSERT INTO paymentsDriverPoints (captain_id, paymentID, amount, token, paymentMethod)
VALUES (:driverID, :paymentID, :amount, :token, 'fromBudget')");
$stmt->execute([
':driverID' => $driverID,
':paymentID' => $paymentID2,
':amount' => $amount,
':token' => $token2
]);
// Commit Transaction
$con->commit();
echo json_encode(['status' => 'success', 'message' => 'Budget converted to points successfully']);
} catch (Exception $e) {
$con->rollBack();
error_log('[convertBudgetToPoints] Error: ' . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred.']);
}
?>
@@ -0,0 +1,62 @@
<?php
// ==========================================
// Cron Job: Remove Duplicate Records Daily
// Tables: driverWallet, paymentsDriverPoints
// ==========================================
// Load DB Connection
include "../../jwtconnect.php";
// Function to run cleanup query
function runCleanup($con, $deleteQuery, $tableName) {
try {
$stmt = $con->prepare($deleteQuery);
$stmt->execute();
echo "Cleanup completed for table: $tableName\n";
echo "Rows affected: " . $stmt->rowCount() . "\n\n";
} catch (Exception $e) {
echo "Error cleaning $tableName\n";
}
}
// ==========================================
// DELETE DUPLICATES FOR driverWallet
// ==========================================
$deleteDriverWallet = "
DELETE FROM driverWallet
WHERE id NOT IN (
SELECT id FROM (
SELECT
id,
ROW_NUMBER() OVER(PARTITION BY driverID ORDER BY dateCreated DESC) AS rn
FROM driverWallet
) AS subquery
WHERE rn = 1
);";
runCleanup($con, $deleteDriverWallet, "driverWallet");
// ==========================================
// DELETE DUPLICATES FOR paymentsDriverPoints
// ==========================================
$deletePaymentsPoints = "
DELETE FROM paymentsDriverPoints
WHERE id NOT IN (
SELECT id FROM (
SELECT
id,
ROW_NUMBER() OVER(PARTITION BY driverID ORDER BY created_at DESC) AS rn
FROM paymentsDriverPoints
) AS subquery
WHERE rn = 1
);";
runCleanup($con, $deletePaymentsPoints, "paymentsDriverPoints");
echo "Cron job completed successfully.\n";
?>
@@ -0,0 +1,46 @@
<?php
include "../../connect.php";
$driverID = filterRequest("driverID");
$sql = "SELECT
YEAR(`driver_orders`.`created_at`) AS `year`,
MONTH(`driver_orders`.`created_at`) AS `month`,
COUNT(*) AS `total_orders`,
SUM(CASE WHEN `ride`.`status` = 'Finished' THEN 1 ELSE 0 END) AS `completed_orders`,
SUM(CASE WHEN `ride`.`status` = 'Apply' THEN 1 ELSE 0 END) AS `pending_orders`,
SUM(CASE WHEN `ride`.`status` = 'Cancel' THEN 1 ELSE 0 END) AS `canceled_orders`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Finished' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_completed`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Apply' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_pending`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Cancel' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_canceled`,
SUM(CASE WHEN `ride`.`status` = 'Refused' THEN 1 ELSE 0 END) AS `rejected_orders`,
ROUND(SUM(CASE WHEN `ride`.`status` = 'Refused' THEN 1 ELSE 0 END) / COUNT(*) * 100, 2) AS `percent_rejected`
FROM
`driver_orders`
LEFT JOIN `ride` ON `ride`.`id` = `driver_orders`.`order_id`
WHERE
`driver_orders`.`driver_id` = '$driverID'
AND YEAR(`driver_orders`.`created_at`) = YEAR(CURDATE())
AND MONTH(`driver_orders`.`created_at`) = MONTH(CURDATE())
GROUP BY
YEAR(`driver_orders`.`created_at`),
MONTH(`driver_orders`.`created_at`)
ORDER BY
`year`,
`month`;
";
$stmt = $con->prepare($sql);
$stmt->execute();
if ($stmt->rowCount() > 0) {
// Fetch the record
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
printSuccess( $row);
}
else{
// Print a failure message
printFailure($message = "No wallet record found");
}
?>
@@ -0,0 +1,42 @@
<?php
include "../../connect.php";
$driverID = filterRequest("driverID");
$sql = "SELECT
COALESCE(dw.id, 0) AS id,
COALESCE(dw.driverID, '0') AS driverID,
COALESCE(dw.paymentID, '0') AS paymentID,
COALESCE(dw.dateCreated, '1970-01-01 00:00:00') AS dateCreated,
COALESCE(dw.amount, 0) AS amount,
COALESCE(dw.paymentMethod, '0') AS paymentMethod,
COALESCE(dw.dateUpdated, '1970-01-01 00:00:00') AS dateUpdated,
COALESCE((SELECT SUM(amount) FROM driverWallet WHERE driverID = '$driverID'), 0) AS total_amount
FROM
driverWallet dw
WHERE
dw.driverID = '$driverID'
GROUP BY
dw.id,
dw.driverID,
dw.paymentID,
dw.dateCreated,
dw.amount,
dw.paymentMethod,
dw.dateUpdated
";
$stmt = $con->prepare($sql);
$stmt->execute();
if ($stmt->rowCount() > 0) {
// Fetch the record
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
printSuccess( $row);
}
else{
// Print a failure message
printFailure($message = "No wallet record found");
}
?>
@@ -0,0 +1,34 @@
<?php
include "../../connect.php";
$driver_phone = filterRequest("driver_phone");
$sql = "SELECT
`driverToken`.`token`,
`driver`.`id`,
`driver`.`phone`,
`driver`.`name_arabic`as name,
driver.national_number
FROM
`driverToken`
LEFT JOIN `driver` ON `driver`.`id` = `driverToken`.`captain_id`
WHERE
`driver`.`phone` = '$driver_phone'";
$stmt = $con->prepare($sql);
$stmt->execute();
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($data) {
// Print the car location data as JSON
echo json_encode([
'status' => 'success',
'data' => $data
]);
} else {
// Print a failure message
printFailure($message = "No car locations found");
}
?>
@@ -0,0 +1,37 @@
<?php
include "../../connect.php";
$driverID = filterRequest("driverID");
$sql = "SELECT
`id`,
`driverID`,
`paymentID`,
`dateCreated`,
`amount`,
`paymentMethod`,
`dateUpdated`,
(SELECT SUM(`amount`)
FROM `driverWallet`
WHERE `driverID` = '$driverID'
AND `dateCreated` >= DATE_SUB(NOW(), INTERVAL 1 WEEK)
) AS totalAmount
FROM `driverWallet`
WHERE `driverID` = '$driverID'
AND `dateCreated` >= DATE_SUB(NOW(), INTERVAL 1 WEEK)
ORDER BY `dateCreated` DESC;
";
$stmt = $con->prepare($sql);
$stmt->execute();
if ($stmt->rowCount() > 0) {
// Fetch the record
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
printSuccess( $row);
}
else{
// Print a failure message
printFailure($message = "No wallet record found");
}
?>
@@ -0,0 +1,30 @@
<?php
include "../../connect.php";
$driverID = filterRequest("driverID");
$sql = "SELECT
driverWallet.`id`,
driverWallet.amount,
driverWallet.dateCreated as created_at
FROM
`driverWallet`
WHERE
driverWallet.driverID = '$driverID' AND driverWallet.dateCreated >= DATE_SUB(NOW(), INTERVAL 1 MONTH)
ORDER BY
`driverWallet`.`id`
DESC";
$stmt = $con->prepare($sql);
$stmt->execute();
if ($stmt->rowCount() > 0) {
// Fetch the record
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
printSuccess( $row);
}
else{
// Print a failure message
printFailure($message = "No wallet record found");
}
?>
@@ -0,0 +1,68 @@
<?php
/**
* get_s2s_wallet_dashboard.php — Payment Server Endpoint
*
* Returns wallet metrics (challenge points, today's earnings) for a driver.
* Authenticated via X-S2S-Api-Key header matching the S2S_SHARED_KEY environment variable.
*/
require_once __DIR__ . '/../../jwtconnect.php';
define('S2S_SHARED_KEY', getenv('S2S_SHARED_KEY'));
$providedKey = $_SERVER['HTTP_X_S2S_API_KEY'] ?? '';
if (empty($providedKey) || $providedKey !== S2S_SHARED_KEY) {
http_response_code(401);
printFailure("Unauthorized: Invalid or missing X-S2S-Api-Key.");
exit;
}
$driverID = filterRequest("driverID");
if (empty($driverID)) {
printFailure("Missing required parameter: driverID");
exit;
}
try {
// 1. Calculate Sum of claimed challenge points
$stmtChallengePoints = $con->prepare("
SELECT COALESCE(SUM(amount), 0)
FROM `paymentsDriverPoints`
WHERE driverID = :driver_id
AND (payment_method LIKE 'daily_%' OR payment_method LIKE 'weekly_%')
");
$stmtChallengePoints->execute([':driver_id' => $driverID]);
$challengePoints = (int)($stmtChallengePoints->fetchColumn() ?: 0);
// 2. Calculate Today's Earnings
$stmtTodayEarnings = $con->prepare("
SELECT COALESCE(SUM(amount), 0)
FROM payments
WHERE driverID = :driver_id
AND DATE(created_at) = CURDATE()
");
$stmtTodayEarnings->execute([':driver_id' => $driverID]);
$todayEarnings = (float)($stmtTodayEarnings->fetchColumn() ?: 0.0);
// 3. Calculate Total Wallet Balance
$stmtTotalWallet = $con->prepare("
SELECT COALESCE(SUM(amount), 0)
FROM `driverWallet`
WHERE driverID = :driver_id
");
$stmtTotalWallet->execute([':driver_id' => $driverID]);
$totalWallet = (float)($stmtTotalWallet->fetchColumn() ?: 0.0);
printSuccess([
"challengePoints" => $challengePoints,
"todayEarnings" => $todayEarnings,
"totalWallet" => $totalWallet
]);
} catch (Exception $e) {
error_log("[get_s2s_wallet_dashboard] " . $e->getMessage());
printFailure("An error occurred");
}
?>
@@ -0,0 +1,60 @@
<?php
// Include the database connection file
include "../../connect.php";
// Get the request parameters
$driver_id = filterRequest("driver_id");
$payment_amount = filterRequest("payment_amount");
$timePromo = filterRequest("timePromo"); // Example: 'morning' or 'afternoon'
//$createdAt = date("Y-m-d H:i:s"); // Get the current date and time
$currentDate = date("Y-m-d"); // Current date for comparison
// Check if a promotion record for the same driver already exists today
$sqlCheck = "SELECT COUNT(*) FROM `driver_promotions` WHERE `driver_id` = :driver_id AND DATE(`created_at`) = :current_date
and timePromo=:timePromo
";
$stmtCheck = $con->prepare($sqlCheck);
$stmtCheck->execute(array(
':driver_id' => $driver_id,
':current_date' => $currentDate
':timePromo' =>$timePromo
));
$count = $stmtCheck->fetchColumn();
if ($count > 0) {
// A record exists for today, so prevent the insertion
printFailure("A promotion record for this driver already exists for today.");
} else {
// No record exists for today, so insert the new promotion
$sqlInsert = "INSERT INTO `driver_promotions` (
`driver_id`,
`payment_amount`,
`timePromo`
) VALUES (
:driver_id,
:payment_amount,
:timePromo
);";
// Prepare the insert statement
$stmtInsert = $con->prepare($sqlInsert);
$stmtInsert->execute(array(
':driver_id' => $driver_id,
':payment_amount' => $payment_amount,
':timePromo' => $timePromo,
':createdAt' => $createdAt
));
// Check if the query was successful
if ($stmtInsert->rowCount() > 0) {
// Print a success message
printSuccess("Promotion record saved successfully");
} else {
// Print a failure message
printFailure("Failed to save promotion record");
}
}
?>
@@ -0,0 +1,122 @@
<?php
// Connect to database
include '../../connect.php';
// Get trip details
$driverName = filterRequest('name');
$driverEmail = filterRequest('email');
$driverPhone = filterRequest('phone');
$amount = filterRequest('amount');
$newDriverName = filterRequest('newDriver');
$newEmail=filterRequest('newEmail');
// Get language preference from database or user input
$language = 'en'; // Default to English
// Email content
if ($language === 'ar') {
$bodyEmail = "<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f8fa;
color: #14171a;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #1da1f2;
margin-top: 0;
}
p {
line-height: 1.5;
}
a {
color: #1da1f2;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class='container'>
<h1>تفاصيل نقلك على سفر</h1>
<p>شكراً لاستخدام خدمتنا. نتمنى لك يوماً رائعاً!</p>
<p>نريد إعلامك أن مبلغ $amount تم نقله من حسابك إلى السائق الجديد، $newDriverName (هاتف: $driverPhone).</p>
<p>مع خالص التحية،<br> فريق سفر</p>
</div>
</body>
</html>";
} else {
$bodyEmail = "<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f5f8fa;
color: #14171a;
}
.container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background-color: white;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}
h1 {
color: #1da1f2;
margin-top: 0;
}
p {
line-height: 1.5;
}
a {
color: #1da1f2;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
</head>
<body>
<div class='container'>
<img src='https://lh3.googleusercontent.com/a/ACg8ocLe5TgvmTjoFx7KjIoWGxX0G2ryKBTzUZi2-mBYb9DI1dsKQ0WEYh5ZPdnA3WeFbp9VnaTNzJuA0w8S4RiQ7042AKrOwXo3=s576-c-no' alt='SIRO App Logo' style='width: 150px; margin: 20px auto; display: block;'>
<h1>Your SIRO Transfer Details</h1>
<p>Thank you for using our service. We hope you have a great day!</p>
<p>We want to inform you that an amount of $amount has been transferred from your account to the new driver: $newDriverName (Phone: $driverPhone).</p>
<p>Regards,<br> SIRO Team</p>
</div>
</body>
</html>";
}
// Email headers
$supportEmail = 'siroteam@siro.live';
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: text/html; charset=UTF-8\r\n";
$headers .= "From: $supportEmail\r\n";
// Send email
if (!empty($driverEmail)) {
if (mail($driverEmail, "Your SIRO Transfer Details", $bodyEmail, $headers)) {
mail($newEmail, "Your SIRO Transfer Details", $bodyEmail, $headers);
echo "Email sent successfully.";
} else {
echo "Email sending failed.";
}
} else {
echo "Invalid email address: $driverEmail";
}
@@ -0,0 +1,130 @@
<?php
include '../../jwtconnect.php';
// Disable error reporting output for production API
error_reporting(E_ALL);
ini_set('display_errors', 0);
// Set header
header('Content-Type: application/json');
$senderID = filterRequest('senderID');
$receiverID = filterRequest('receiverID'); // Now receiving the ID directly from Main Server
$amount = floatval(filterRequest('amount'));
$country = filterRequest('country'); // e.g. Egypt, Syria, Jordan
if (empty($senderID) || empty($receiverID) || empty($amount) || empty($country)) {
echo json_encode(['status' => 'error', 'message' => 'Missing required fields']);
exit;
}
// --- Payment Key Authentication ---
$expectedKey = getenv('PAYMENT_KEY');
$providedKey = $_SERVER['HTTP_PAYMENT_KEY'] ?? '';
if (empty($expectedKey) || $providedKey !== $expectedKey) {
http_response_code(401);
echo json_encode(['status' => 'error', 'message' => 'Unauthorized Payment Server Access (Invalid Key)']);
exit;
}
// 1. Determine Fee based on Country
$fee = 0;
if (strtolower($country) === 'egypt') {
$fee = 5;
if ($amount < 10) {
echo json_encode(['status' => 'error', 'message' => 'Minimum transfer amount in Egypt is 10']);
exit;
}
} elseif (strtolower($country) === 'syria') {
$fee = 10;
if ($amount < 100) {
echo json_encode(['status' => 'error', 'message' => 'Minimum transfer amount in Syria is 100']);
exit;
}
} elseif (strtolower($country) === 'jordan') {
$fee = 0.25;
if ($amount < 1) {
echo json_encode(['status' => 'error', 'message' => 'Minimum transfer amount in Jordan is 1']);
exit;
}
} else {
// Default fee if unknown
$fee = 5;
}
try {
$con->beginTransaction();
if ($receiverID == $senderID) {
$con->rollBack();
echo json_encode(['status' => 'error', 'message' => 'Cannot transfer to yourself']);
exit;
}
// 2. Fetch Sender Budget (with FOR UPDATE to lock rows)
$stmt = $con->prepare("SELECT SUM(amount) as diff FROM payments WHERE captain_id = :senderID FOR UPDATE");
$stmt->execute([':senderID' => $senderID]);
$sumRow = $stmt->fetch(PDO::FETCH_ASSOC);
$totalBudget = floatval($sumRow['diff']);
if ($totalBudget < $amount) {
$con->rollBack();
echo json_encode(['status' => 'error', 'message' => 'Insufficient budget']);
exit;
}
$amountForReceiver = $amount - $fee;
if ($amountForReceiver <= 0) {
$con->rollBack();
echo json_encode(['status' => 'error', 'message' => 'Transfer amount must be greater than the fee']);
exit;
}
// 3. Generate unique Tokens and paymentIDs
$paymentID1 = "transfer_" . time() . bin2hex(random_bytes(4));
$paymentID2 = "transfer_recv_" . time() . bin2hex(random_bytes(4));
$token1 = bin2hex(random_bytes(32));
$token2 = bin2hex(random_bytes(32));
$siroToken = bin2hex(random_bytes(32));
// 4. Deduct from Sender (payments table)
$deductAmount = -$amount;
$stmt = $con->prepare("INSERT INTO payments (captain_id, amount, rideId, payment_method, passengerID, token)
VALUES (:senderID, :amount, :rideId, 'cash_transfer', :receiverRef, :token)");
$stmt->execute([
':senderID' => $senderID,
':amount' => $deductAmount,
':rideId' => $paymentID1,
':receiverRef' => 'To ' . $receiverID,
':token' => $token1
]);
// 5. Add to Receiver Points (paymentsDriverPoints table)
$stmt = $con->prepare("INSERT INTO paymentsDriverPoints (captain_id, paymentID, amount, token, paymentMethod)
VALUES (:receiverID, :paymentID, :amount, :token, 'Transfer')");
$stmt->execute([
':receiverID' => $receiverID,
':paymentID' => $paymentID2,
':amount' => $amountForReceiver,
':token' => $token2
]);
// 6. Add Fee to Siro Wallet
$stmt = $con->prepare("INSERT INTO siroWallet (amount, paymentMethod, passengerId, token, driverId)
VALUES (:fee, 'payout fee', 'driver', :token, :senderID)");
$stmt->execute([
':fee' => $fee,
':token' => $siroToken,
':senderID' => $senderID
]);
// Commit Transaction
$con->commit();
echo json_encode(['status' => 'success', 'message' => 'Transfer completed successfully on payment server']);
} catch (Exception $e) {
$con->rollBack();
error_log('[transfer] Error: ' . $e->getMessage());
echo json_encode(['status' => 'error', 'message' => 'An internal error occurred.']);
}
?>