middleware(function ($request, $next) { $provided = $this->extractToken($request); $expected = config('services.bonus_api.token'); if (!$expected || !hash_equals((string) $expected, (string) $provided)) { return response()->json(['message' => 'Unauthorized'], 401); } return $next($request); }); } private function extractToken(Request $request): ?string { $auth = $request->header('Authorization'); if ($auth && str_starts_with($auth, 'Bearer ')) { return substr($auth, 7); } return $request->query('api_token'); // fallback: allow ?api_token= } public function index(Request $request) { try { $query = []; if ($status = $request->query('status')) { $query['status'] = $status; } if ($request->boolean('active_only')) { $query['active_only'] = 1; } $query['per_page'] = min(200, max(1, (int) $request->query('per_page', 50))); $res = $this->client->get($request, '/bonuses', $query, retry: true); if ($res->successful()) { return response()->json($res->json() ?: []); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } public function show(Request $request, int $id) { try { $res = $this->client->get($request, "/bonuses/{$id}", [], retry: true); if ($res->successful()) { return response()->json($res->json() ?: []); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } public function store(Request $request) { $data = $this->validateData($request); try { $res = $this->client->post($request, '/bonuses', $data); if ($res->successful()) { return response()->json($res->json() ?: [], 201); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } public function update(Request $request, int $id) { $data = $this->validateData($request, partial: true); try { $res = $this->client->patch($request, "/bonuses/{$id}", $data); if ($res->successful()) { return response()->json($res->json() ?: []); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } public function destroy(Request $request, int $id) { try { $res = $this->client->delete($request, "/bonuses/{$id}"); if ($res->successful()) { $body = $res->json(); return response()->json($body ?: ['message' => 'Deleted']); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } private function validateData(Request $request, bool $partial = false): array { $required = $partial ? 'sometimes' : 'required'; $rules = [ 'title' => [$required, 'string', 'max:255'], 'type' => ['sometimes', 'nullable', 'string', 'max:64'], 'amount_value' => ['sometimes', 'nullable', 'numeric'], 'amount_unit' => ['sometimes', 'nullable', Rule::in(['USD','EUR','BTC','ETH','PERCENT','SPINS'])], 'min_deposit' => ['sometimes', 'nullable', 'numeric', 'min:0'], 'max_amount' => ['sometimes', 'nullable', 'numeric', 'min:0'], 'currency' => ['sometimes', 'nullable', 'string', 'max:16'], 'code' => ['sometimes', 'nullable', 'string', 'max:64'], 'status' => ['sometimes', 'required', Rule::in(['draft','active','paused','expired'])], 'starts_at' => ['sometimes', 'nullable', 'date'], 'expires_at' => ['sometimes', 'nullable', 'date', 'after_or_equal:starts_at'], 'rules' => ['sometimes', 'nullable', 'array'], 'description' => ['sometimes', 'nullable', 'string'], ]; $validated = $request->validate($rules); // Normalize empty strings to null foreach (['type','amount_unit','currency','code','description'] as $k) { if (array_key_exists($k, $validated) && $validated[$k] === '') { $validated[$k] = null; } } return $validated; } }