header('X-Signature'); $timestamp = $this->header('X-Timestamp'); $apiKey = $this->header('X-Api-Key'); // Validasi keberadaan header yang diperlukan if (!$signature || !$timestamp || !$apiKey) { Log::warning('HMAC validation failed - missing required headers', [ 'signature' => $signature, 'timestamp' => $timestamp, 'apiKey' => $apiKey, 'ip' => $this->ip(), 'user_agent' => $this->userAgent() ]); return false; } // Validasi API key dari config $expectedApiKey = config('webstatement.api_key'); if ($apiKey !== $expectedApiKey) { Log::warning('HMAC validation failed - invalid API key', [ 'provided_api_key' => $apiKey, 'expected_api_key' => $expectedApiKey, 'ip' => $this->ip(), 'user_agent' => $this->userAgent() ]); return false; } // Ambil secret key dari config $secretKey = config('webstatement.secret_key'); // Ambil parameter untuk validasi HMAC $httpMethod = $this->method(); $relativeUrl = $this->path(); $requestBody = $this->getContent(); // Validasi HMAC signature $isValid = validateHmac512( $httpMethod, $relativeUrl, $apiKey, $requestBody, $timestamp, $secretKey, $signature ); if (!$isValid) { Log::warning('HMAC validation failed - invalid signature', [ 'http_method' => $httpMethod, 'relative_url' => $relativeUrl, 'api_key' => $apiKey, 'timestamp' => $timestamp, 'ip' => $this->ip(), 'user_agent' => $this->userAgent() ]); } return $isValid; } catch (\Exception $e) { Log::error('HMAC validation error', [ 'error' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'ip' => $this->ip(), 'user_agent' => $this->userAgent() ]); return false; } } /** * Get the validation rules that apply to the request. * * @return array */ public function rules(): array { return [ 'account_number' => [ 'required', 'string', 'max:50', 'regex:/^[A-Z0-9-]+$/i' // Hanya alphanumeric dan dash ], 'start_date' => [ 'required', 'date_format:Y-m-d', 'before_or_equal:end_date', 'after_or_equal:1900-01-01', 'before_or_equal:today' ], 'end_date' => [ 'required', 'date_format:Y-m-d', 'after_or_equal:start_date', 'after_or_equal:1900-01-01', 'before_or_equal:today' ], ]; } /** * Get custom messages for validator errors. * * @return array */ public function messages(): array { return [ 'account_number.required' => 'Nomor rekening wajib diisi.', 'account_number.string' => 'Nomor rekening harus berupa teks.', 'account_number.max' => 'Nomor rekening maksimal :max karakter.', 'account_number.regex' => 'Nomor rekening hanya boleh mengandung huruf, angka, dan strip.', 'start_date.required' => 'Tanggal awal wajib diisi.', 'start_date.date_format' => 'Format tanggal awal harus YYYY-MM-DD.', 'start_date.before_or_equal' => 'Tanggal awal harus sebelum atau sama dengan tanggal akhir.', 'end_date.required' => 'Tanggal akhir wajib diisi.', 'end_date.date_format' => 'Format tanggal akhir harus YYYY-MM-DD.', 'end_date.after_or_equal' => 'Tanggal akhir harus sesudah atau sama dengan tanggal awal.', ]; } /** * Handle a failed validation attempt. * * @param \Illuminate\Contracts\Validation\Validator $validator * @return void * * @throws \Illuminate\Validation\ValidationException */ protected function failedValidation($validator) { $errors = $validator->errors(); throw new HttpResponseException( response()->json( ResponseCode::INVALID_FIELD->toResponse( ['errors' => $errors->all()], 'Validasi gagal' ), ResponseCode::INVALID_FIELD->getHttpStatus() ) ); } /** * Handle failed authorization. * * @return void * * @throws \Illuminate\Auth\Access\AuthorizationException */ protected function failedAuthorization() { $xApiKey = $this->header('X-Api-Key'); $expectedApiKey = config('webstatement.api_key'); // Cek apakah ini karena invalid API key if ($xApiKey !== $expectedApiKey) { throw new HttpResponseException( response()->json( ResponseCode::INVALID_TOKEN->toResponse( null, 'API Key tidak valid' ), ResponseCode::INVALID_TOKEN->getHttpStatus() ) ); } // Untuk kasus HMAC signature tidak valid throw new HttpResponseException( response()->json( ResponseCode::UNAUTHORIZED->toResponse( null, 'Signature tidak valid' ), ResponseCode::UNAUTHORIZED->getHttpStatus() ) ); } /** * Prepare the data for validation. * * @return void */ protected function prepareForValidation(): void { Log::info('Balance summary request received', [ 'input' => $this->all(), 'ip' => $this->ip(), 'user_agent' => $this->userAgent() ]); $acount = AccountBalance::where('account_number', $this->account_number)->first(); if (!$acount) { throw new HttpResponseException( response()->json( ResponseCode::ACCOUNT_NOT_FOUND->toResponse( null, 'Nomor rekening tidak ditemukan' ), ResponseCode::ACCOUNT_NOT_FOUND->getHttpStatus() ) ); } } }