diff --git a/app/Http/Controllers/Api/PaymentTokenController.php b/app/Http/Controllers/Api/PaymentTokenController.php new file mode 100644 index 0000000..185a95f --- /dev/null +++ b/app/Http/Controllers/Api/PaymentTokenController.php @@ -0,0 +1,61 @@ +buildToken($request->user()->id, 'android/ios_passenger', $request->input('fingerPrint')); + } + + // 2. مسار السائق + public function generateDriverToken(Request $request) + { + // تحقق خاص بالسائق (ضرورة وجود بصمة جهاز قوية) + return $this->buildToken($request->user()->id, 'android/ios_driver', $request->input('fingerPrint')); + } + + // 3. مسار المدير + public function generateAdminToken(Request $request) + { + // المدراء لديهم صلاحيات أوسع، قد يكون الـ aud مختلفاً + return $this->buildToken($request->user()->id, 'web_admin', 'admin_secure_context'); + } + + // 4. دالة البناء المركزية (Private) + private function buildToken($userId, $audience, $fingerprint) + { + $keyPath = env('PAYMENT_INTERNAL_KEY_PATH'); + + if (!File::exists($keyPath)) { + return response()->json(['status' => 'error', 'message' => 'Security Key Missing'], 500); + } + + $internalSecret = trim(File::get_contents($keyPath)); + + $payload = [ + 'iss' => 'Intaleq_V2', + 'sub' => $userId, + 'aud' => $audience, + 'iat' => time(), + 'exp' => time() + 60, + 'fingerprint' => $fingerprint + ]; + + $token = JWT::encode($payload, $internalSecret, 'HS256'); + + return response()->json([ + 'status' => 'success', + 'token' => $token, + 'hmac' => hash_hmac('sha256', $token, $internalSecret) + ]); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 0275895..b280169 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -323,25 +323,28 @@ class AuthController extends Controller 'created_at' => now(), ]); - // 11. Insert Documents + // 11. Insert Documents (Wait, the user said registration response MUST return jwt, api_secret, and hmac immediately to allow subsequent protected file uploads) + // So we don't need to insert documents here if they are uploaded later. But keeping the existing code won't hurt, just the response changes. $docKeys = [ 'driver_license_front', 'driver_license_back', 'car_license_front', 'car_license_back' ]; $docUrls = []; foreach ($docKeys as $k) { - $url = $request->input($k); - $docUrls[$k] = $url; - $name = basename(parse_url($url, PHP_URL_PATH) ?? ''); - if ($name === '') { $name = $k . '_' . time() . '.jpg'; } + if ($request->has($k)) { + $url = $request->input($k); + $docUrls[$k] = $url; + $name = basename(parse_url($url, PHP_URL_PATH) ?? ''); + if ($name === '') { $name = $k . '_' . time() . '.jpg'; } - DB::connection('primary')->table('driver_documents')->insert([ - 'driverID' => $data['id'], - 'doc_type' => $k, - 'image_name' => $name, - 'link' => $url, - 'upload_date' => now(), - ]); + DB::connection('primary')->table('driver_documents')->insert([ + 'driverID' => $data['id'], + 'doc_type' => $k, + 'image_name' => $name, + 'link' => $url, + 'upload_date' => now(), + ]); + } } DB::connection('primary')->commit(); @@ -360,11 +363,29 @@ class AuthController extends Controller \Illuminate\Support\Facades\Log::error("FCM Admin notification failed: " . $e->getMessage()); } + // Generate JWT and API Keys + $fingerprint = $request->input('fingerprint', 'unknown'); + $jwt = $this->createJwt($data['id'], 'driver', $fingerprint, 14400); + + $apiKey = bin2hex(random_bytes(16)); + $apiSecret = bin2hex(random_bytes(32)); + DB::connection('primary')->table('driver')->where('id', $data['id'])->update([ + 'api_key' => $apiKey, + 'api_secret' => $apiSecret + ]); + + // To be compatible with frontend, generate Wallet HMAC + $hmac = hash_hmac('sha256', $jwt, config('intaleq.secret_key_hmac', '')); + return response()->json([ 'status' => 'success', 'driverID' => $data['id'], 'carRegID' => $carRegId, - 'documents' => $docUrls + 'documents' => $docUrls, + 'jwt' => $jwt, + 'api_key' => $apiKey, + 'api_secret' => $apiSecret, + 'hmac' => $hmac ]); } catch (\Exception $e) { diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index 87dec57..0267886 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -51,7 +51,7 @@ class UploadController extends Controller { $request->validate([ 'image' => 'required|file', - 'doc_type' => 'required|string|in:license,registration,criminal,id_front,id_back', + 'doc_type' => 'required|string|in:license,registration,criminal,id_front,id_back,driver_license_front,driver_license_back,car_license_front,car_license_back', ]); return $this->handleImageUpload($request, 'driver_documents', 'documents'); diff --git a/app/Http/Middleware/HmacAuthMiddleware.php b/app/Http/Middleware/HmacAuthMiddleware.php index 8a69bb4..2237e19 100644 --- a/app/Http/Middleware/HmacAuthMiddleware.php +++ b/app/Http/Middleware/HmacAuthMiddleware.php @@ -81,10 +81,16 @@ class HmacAuthMiddleware } // Compute expected signature - // METHOD:PATH:TIMESTAMP:NONCE:BODY $method = strtoupper($request->method()); $path = $request->path(); // returns path without leading slash (e.g. v2/ride/create) - $body = $request->getContent(); + + if (str_contains(strtolower($request->header('Content-Type', '')), 'multipart/form-data')) { + $inputs = $request->except(array_keys($request->allFiles())); + ksort($inputs); + $body = json_encode($inputs); + } else { + $body = $request->getContent(); + } $payload = "$method:$path:$timestamp:$nonce:$body"; $expected = hash_hmac(self::ALGORITHM, $payload, $user->api_secret); diff --git a/config/intaleq.php b/config/intaleq.php index dcb136a..532f9ee 100644 --- a/config/intaleq.php +++ b/config/intaleq.php @@ -67,6 +67,8 @@ return [ 'Tripz-Walletios', 'TripzWallet:android', 'TripzWallet:ios', + 'allowedWallet1android', + 'allowedWallet1ios', ], 'wallet_app_password' => env('passwordnewpassenger', ''), 'fp_pepper' => env('FP_PEPPER', ''), diff --git a/routes/api.php b/routes/api.php index 6701c14..769e0dd 100644 --- a/routes/api.php +++ b/routes/api.php @@ -29,6 +29,8 @@ use App\Http\Controllers\MiscController; use App\Http\Controllers\InviteController; use App\Http\Controllers\DriverDocController; use App\Http\Controllers\SupportController; +use App\Http\Controllers\Api\PaymentTokenController; + /* |-------------------------------------------------------------------------- @@ -48,7 +50,8 @@ Route::prefix('v2/auth')->group(function () { // Passenger Route::post('/passenger/login', [AuthController::class, 'passengerLogin']); Route::post('/passenger/register', [AuthController::class, 'passengerRegister']); - Route::post('/passenger/wallet-login', [AuthController::class, 'passengerWalletLogin']); + // to be replaced by dedicated PaymentTokenController for better separation of concerns + // Route::post('/passenger/wallet-login', [AuthController::class, 'passengerWalletLogin']); Route::get('/passenger/login-google', [AuthController::class, 'passengerLoginGoogle']); // Driver @@ -63,9 +66,16 @@ Route::prefix('v2/auth')->group(function () { // Silent JWT Handshake (Compatibility with V1 background flow) Route::post('/passenger/login-jwt', [AuthController::class, 'passengerJwtHandshake']); Route::post('/driver/login-jwt', [AuthController::class, 'driverJwtHandshake']); - Route::post('/driver/wallet-token', [AuthController::class, 'getWalletToken']); + // Route::post('/driver/wallet-token', [AuthController::class, 'getWalletToken']); + // to be replaced by dedicated PaymentTokenController for better separation of concerns +}); +Route::prefix('v2/payment')->middleware(['hmac.auth', 'jwt.auth'])->group(function () { + Route::post('/passenger/generate-token', [PaymentTokenController::class, 'generatePassengerToken']); + Route::post('/driver/generate-token', [PaymentTokenController::class, 'generateDriverToken']); + + // يفضل إضافة مسار الإدارة داخل مجموعة الإدارة الموجودة مسبقاً أو حمايته بـ middleware إضافي + Route::post('/admin/generate-token', [PaymentTokenController::class, 'generateAdminToken'])->middleware('admin'); }); - // Admin Error Logging (public — accepts error reports from Flutter apps) Route::post('v2/admin/errors', [MiscController::class, 'logClientError']);