From e53b522f77b6c3288aa05ac65662c1265db52b97 Mon Sep 17 00:00:00 2001 From: Daeng Deni Mardaeni Date: Wed, 27 Aug 2025 17:07:57 +0700 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(API):=20standarisasi=20respons?= =?UTF-8?q?e=20API=20dengan=20ResponseCode=20enum=20dan=20penambahan=20str?= =?UTF-8?q?uktur=20meta?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Menambahkan ResponseCode enum untuk standarisasi semua response API. - Integrasi meta data: nomor rekening, periode, request_id, dan reference_code. - Memperbarui validasi input dengan response code standar (INVALID_FIELD). - Struktur response dibuat konsisten untuk success dan error. - Logging diperkuat untuk debugging dan monitoring. --- app/Enums/ResponseCode.php | 137 ++++++++++++++++++ .../Api/AccountBalanceController.php | 58 ++++++-- app/Http/Requests/BalanceSummaryRequest.php | 44 ++++-- app/Http/Resources/BalanceSummaryResource.php | 23 +-- routes/api.php | 2 +- 5 files changed, 217 insertions(+), 47 deletions(-) create mode 100644 app/Enums/ResponseCode.php diff --git a/app/Enums/ResponseCode.php b/app/Enums/ResponseCode.php new file mode 100644 index 0000000..1d8393c --- /dev/null +++ b/app/Enums/ResponseCode.php @@ -0,0 +1,137 @@ + 'Success', + self::INVALID_FIELD => 'Invalid Field', + self::MISSING_FIELD => 'Missing Field', + self::INVALID_FORMAT => 'Invalid Format', + self::DATA_NOT_FOUND => 'Data Not Found', + self::DUPLICATE_REQUEST => 'Duplicate Request', + self::ACCOUNT_ALREADY_EXISTS => 'Account Already Exists', + self::ACCOUNT_NOT_FOUND => 'Account Not Found', + self::INVALID_TOKEN => 'Invalid Token', + self::UNAUTHORIZED => 'Unauthorized', + self::SYSTEM_MALFUNCTION => 'System Malfunction', + self::TIMEOUT => 'Timeout', + self::SERVICE_UNAVAILABLE => 'Service Unavailable', + self::GENERAL_ERROR => 'General Error', + }; + } + + /** + * Mendapatkan deskripsi response berdasarkan kode + * + * @return string + */ + public function getDescription(): string + { + return match($this) { + self::SUCCESS => 'Permintaan berhasil', + self::INVALID_FIELD => 'Field tertentu tidak sesuai aturan', + self::MISSING_FIELD => 'Field wajib tidak dikirim', + self::INVALID_FORMAT => 'Format salah', + self::DATA_NOT_FOUND => 'Data yang diminta tidak ditemukan', + self::DUPLICATE_REQUEST => 'Request ID sama, sudah pernah diproses', + self::ACCOUNT_ALREADY_EXISTS => 'Nomor rekening / username / email sudah terdaftar', + self::ACCOUNT_NOT_FOUND => 'Nomor rekening / akun tidak ditemukan', + self::INVALID_TOKEN => 'Token tidak valid', + self::UNAUTHORIZED => 'Tidak punya akses', + self::SYSTEM_MALFUNCTION => 'Gangguan teknis di server', + self::TIMEOUT => 'Request timeout', + self::SERVICE_UNAVAILABLE => 'Layanan tidak tersedia', + self::GENERAL_ERROR => 'Kesalahan umum', + }; + } + + /** + * Mendapatkan HTTP status code berdasarkan response code + * + * @return int + */ + public function getHttpStatus(): int + { + return match($this) { + self::SUCCESS => 200, + self::INVALID_FIELD, + self::MISSING_FIELD, + self::INVALID_FORMAT => 400, + self::DATA_NOT_FOUND, + self::ACCOUNT_NOT_FOUND => 404, + self::DUPLICATE_REQUEST, + self::ACCOUNT_ALREADY_EXISTS => 409, + self::INVALID_TOKEN, + self::UNAUTHORIZED => 401, + self::SYSTEM_MALFUNCTION, + self::GENERAL_ERROR => 500, + self::TIMEOUT => 408, + self::SERVICE_UNAVAILABLE => 503, + }; + } + + /** + * Membuat response array standar + * + * @param mixed $data + * @param string|null $message + * @return array + */ + public function toResponse($data = null, ?string $message = null): array + { + return [ + 'response_code' => $this->value, + 'response_message' => $message ?? $this->getMessage(), + 'response_description' => $this->getDescription(), + 'data' => $data, + 'meta' => [ + 'account_number' => $data['account_number'] ?? null, + 'period' => [ + 'start_date' => $data['period']['start_date'] ?? null, + 'end_date' => $data['period']['end_date'] ?? null, + ], + 'generated_at' => now()->toDateTimeString(), + 'request_id' => request()->header('X-Request-ID', uniqid('req_')) + ], + ]; + } + } diff --git a/app/Http/Controllers/Api/AccountBalanceController.php b/app/Http/Controllers/Api/AccountBalanceController.php index afc67a2..b622255 100644 --- a/app/Http/Controllers/Api/AccountBalanceController.php +++ b/app/Http/Controllers/Api/AccountBalanceController.php @@ -10,6 +10,7 @@ use Modules\Webstatement\Http\Requests\DetailedBalanceRequest; use Modules\Webstatement\Services\AccountBalanceService; use Modules\Webstatement\Http\Resources\BalanceSummaryResource; use Modules\Webstatement\Http\Resources\DetailedBalanceResource; +use Modules\Webstatement\Enums\ResponseCode; use Exception; class AccountBalanceController extends Controller @@ -30,17 +31,16 @@ class AccountBalanceController extends Controller public function getBalanceSummary(BalanceSummaryRequest $request): JsonResponse { try { - $accountNumber = $request->input('account_number'); - $startDate = $request->input('start_date'); - $endDate = $request->input('end_date'); + $startDate = $request->input('start_date'); + $endDate = $request->input('end_date'); Log::info('Account balance summary requested', [ 'account_number' => $accountNumber, - 'start_date' => $startDate, - 'end_date' => $endDate, - 'ip' => $request->ip(), - 'user_agent' => $request->userAgent() + 'start_date' => $startDate, + 'end_date' => $endDate, + 'ip' => $request->ip(), + 'user_agent' => $request->userAgent() ]); $result = $this->accountBalanceService->getBalanceSummary( @@ -49,21 +49,49 @@ class AccountBalanceController extends Controller $endDate ); - return (new BalanceSummaryResource($result))->response(); + if (empty($result)) { + return response()->json( + ResponseCode::DATA_NOT_FOUND->toResponse( + null, + 'Data saldo tidak ditemukan untuk nomor rekening: ' . $accountNumber + ), + ResponseCode::DATA_NOT_FOUND->getHttpStatus() + ); + } + + return response()->json( + ResponseCode::SUCCESS->toResponse( + (new BalanceSummaryResource($result))->toArray($request), + + ), + ResponseCode::SUCCESS->getHttpStatus() + ); } catch (Exception $e) { Log::error('Error getting account balance summary', [ 'error' => $e->getMessage(), - 'file' => $e->getFile(), - 'line' => $e->getLine(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), 'trace' => $e->getTraceAsString() ]); - return response()->json([ - 'success' => false, - 'message' => 'Terjadi kesalahan saat mengambil data saldo', - 'error' => config('app.debug') ? $e->getMessage() : null - ], 500); + $responseCode = match ($e->getCode()) { + 404 => ResponseCode::DATA_NOT_FOUND, + 401 => ResponseCode::UNAUTHORIZED, + 403 => ResponseCode::UNAUTHORIZED, + 408 => ResponseCode::TIMEOUT, + 503 => ResponseCode::SERVICE_UNAVAILABLE, + 400 => ResponseCode::INVALID_FIELD, + default => ResponseCode::SYSTEM_MALFUNCTION + }; + + return response()->json( + $responseCode->toResponse( + null, + config('app.debug') ? $e->getMessage() : 'Terjadi kesalahan sistem' + ), + $responseCode->getHttpStatus() + ); } } } diff --git a/app/Http/Requests/BalanceSummaryRequest.php b/app/Http/Requests/BalanceSummaryRequest.php index faee1c5..aacf2f2 100644 --- a/app/Http/Requests/BalanceSummaryRequest.php +++ b/app/Http/Requests/BalanceSummaryRequest.php @@ -57,19 +57,43 @@ class BalanceSummaryRequest extends FormRequest 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 50 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', + '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(); + $firstError = $errors->first(); + + throw new \Illuminate\Http\Exceptions\HttpResponseException( + response()->json( + \Modules\Webstatement\Enums\ResponseCode::INVALID_FIELD->toResponse( + null, + $firstError + ), + \Modules\Webstatement\Enums\ResponseCode::INVALID_FIELD->getHttpStatus() + ) + ); + } + /** * Prepare the data for validation. * diff --git a/app/Http/Resources/BalanceSummaryResource.php b/app/Http/Resources/BalanceSummaryResource.php index 1ca616d..db9f87f 100644 --- a/app/Http/Resources/BalanceSummaryResource.php +++ b/app/Http/Resources/BalanceSummaryResource.php @@ -4,6 +4,7 @@ namespace Modules\Webstatement\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; +use Illuminate\Support\Str; class BalanceSummaryResource extends JsonResource { @@ -37,27 +38,7 @@ class BalanceSummaryResource extends JsonResource ], 'transactions_on_end_date' => $this['closing_balance']['transactions_on_end_date'], 'formatted_transactions_on_end_date' => number_format($this['closing_balance']['transactions_on_end_date'], 2, ',', '.'), - ], - ]; - } - - /** - * Get additional meta data. - * - * @param Request $request - * @return array - */ - public function with($request): array - { - return [ - 'meta' => [ - 'account_number' => $this['account_number'], - 'period' => [ - 'start_date' => $this['period']['start_date'], - 'end_date' => $this['period']['end_date'], - ], - 'generated_at' => now()->toDateTimeString(), - ], + ] ]; } } diff --git a/routes/api.php b/routes/api.php index 310abd2..15db96e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -11,5 +11,5 @@ // Account Balance API Routes Route::prefix('balance')->group(function () { - Route::get('/', [AccountBalanceController::class, 'getBalanceSummary']); + Route::post('/', [AccountBalanceController::class, 'getBalanceSummary']); });