api()->get(rtrim(config('api.url'), '/') . '/api/shoraka/readiness')->throw()->json(); abort_unless(!empty($response['ready']), 503, 'Shoraka belum dikonfigurasi. Pembiayaan baharu ditahan.'); } public function api() { return Http::withOptions(['verify' => config('app.api_verify')]) ->acceptJson()->timeout(75); } public function url(string $reference, string $suffix = ''): string { return rtrim(config('api.url'), '/') . '/api/shoraka/gadai/' . rawurlencode(Auth::user()->cawangan) . '/' . rawurlencode($reference) . $suffix; } public function hasOrder(string $reference): bool { $snapshot = $this->snapshot($reference); return !empty($snapshot['order']); } public function snapshot(string $reference): array { $response = $this->api()->get($this->url($reference)); if ($response->status() === 404) { return []; } if ($response->serverError()) { abort(503, 'Shoraka belum dikonfigurasi. Pembiayaan baharu ditahan.'); } $response->throw(); return $response->json()['data'] ?? []; } public function listForBranch(string $branch) { $response = $this->api()->get(rtrim(config('api.url'), '/') . '/api/shoraka/transactions', [ 'kodcaw' => $branch, 'per_page' => 100, ])->throw()->json(); return collect($response['data'] ?? []); } public function search(array $filters, ?string $onlyBranch): array { $query = [ 'per_page' => 25, 'page' => max(1, (int) request()->input('page', 1)), 'with_branches' => 1, ]; if ($onlyBranch) { $query['kodcaw'] = $onlyBranch; $query['branch_scope'] = $onlyBranch; } elseif (!empty($filters['kodcaw'])) { $query['kodcaw'] = $filters['kodcaw']; } foreach (['norujukan', 'status', 'date_from', 'date_to'] as $field) { if (!empty($filters[$field])) { $query[$field] = $filters[$field]; } } $response = $this->api()->get(rtrim(config('api.url'), '/') . '/api/shoraka/transactions', $query)->throw()->json(); $items = collect($response['data'] ?? []); $records = new LengthAwarePaginator( $items, (int) ($response['total'] ?? 0), (int) ($response['per_page'] ?? 25), (int) ($response['current_page'] ?? 1), ['path' => request()->url(), 'query' => request()->query()] ); return [$records, collect($response['branches'] ?? [])]; } public function hold(Request $request, string $reference, string $action, array $extra = []) { if ($this->hasOrder($reference)) { return $this->failed($request, 'Order Shoraka telah dihantar. Jangan hantar semula.'); } $context = array_merge($request->except(['_token', 'password']), $extra, ['action' => $action]); try { $response = $this->api()->post($this->url($reference, '/submit'), [ 'teller' => Auth::user()->name, ]); } catch (Throwable $exception) { if ($this->hasOrder($reference)) { return $this->failed($request, 'Order Shoraka telah dihantar. Jangan hantar semula.'); } return $this->failed($request, 'Order Shoraka tidak berjaya dihantar. Gadaian tidak dihantar.'); } if (!$response->successful()) { $message = $response->json()['message'] ?? ($this->hasOrder($reference) ? 'Order Shoraka telah dihantar. Jangan hantar semula.' : 'Order Shoraka tidak berjaya dihantar. Gadaian tidak dihantar.'); return $this->failed($request, $message); } $snapshot = $this->waitUntilCompleted($reference); $status = $snapshot['order']['status'] ?? null; if ($status !== 'Completed') { return $this->failed($request, 'Pengesahan Shoraka belum Completed. Jangan hantar semula.'); } if (empty($snapshot['can_complete']) || (array_key_exists('pinjaman', $context) && number_format((float) $context['pinjaman'], 2, '.', '') !== number_format((float) ($snapshot['order_amount'] ?? 0), 2, '.', ''))) { return $this->failed($request, 'Amaun pembiayaan berubah. Bayaran tidak diproses.'); } return $this->complete($request, $reference, $context); } private function waitUntilCompleted(string $reference): array { set_time_limit(150); $deadline = microtime(true) + 120; $snapshot = []; do { try { $this->api()->post($this->url($reference, '/refresh'))->throw(); $snapshot = $this->snapshot($reference); } catch (Throwable $exception) { $snapshot = []; } $status = $snapshot['order']['status'] ?? null; if ($status === 'Completed' || in_array($status, ['Cancelled', 'FAILED', 'UNKNOWN'], true)) { return $snapshot; } if (microtime(true) >= $deadline) { return $snapshot; } sleep(2); } while (true); } private function complete(Request $request, string $reference, array $context) { $branch = Auth::user()->cawangan; $saved = new Request($context); $saved->setLaravelSession($request->session()); if (($context['action'] ?? null) === 'gadai') { return app(GadaianController::class)->completeShorakaFinancing($saved); } if (!empty($context['update_history'])) { $this->api()->put(rtrim(config('api.url'), '/') . '/api/gadai/updatehistory/' . rawurlencode($branch) . '/' . rawurlencode($reference), [ 'historygadaian' => $saved->norujukan, 'jenistebus' => $saved->jenistebus, ])->throw(); } $cash = app(AliranTunaiController::class); if (($context['action'] ?? null) === 'autotawarruq') { $cash->alirantunaiTebusAutoTawarruq($saved); } else { $cash->alirantunaiTebus($saved); } event(new \App\Events\Dashboard\TebusNotify('lulus', $branch)); event(new GadaiNotify('lulus', $branch)); return redirect()->route('customer_info'); } public function printData(string $reference): ?array { $response = $this->api()->get($this->url($reference))->throw()->json()['data']; $order = $response['order'] ?? null; if (!$order) { return null; } abort_unless(!empty($response['can_complete']), 409, 'SAG ditahan: Shoraka belum Completed.'); $details = $response['details'] ?? []; $certificateDetails = $details['certificateDetails1'] ?? ''; if (is_array($certificateDetails)) { $certificateDetails = $certificateDetails[0] ?? ''; } $certificate = is_string($certificateDetails) ? trim(explode('|', $certificateDetails)[0]) : ''; abort_unless($certificate !== '' && isset($details['unit'], $details['volume'], $details['commodityType']), 409, 'Maklumat sijil belum lengkap. Semak status Shoraka dahulu.'); return [ 'komoditi' => ['no_sijil' => $certificate], 'product' => [ 'kodproduk' => $details['commodityType'], 'description' => $details['commodityType'] === '000-COPPER' ? 'Copper' : 'Plastic resin', 'unit' => $details['unit'], ], 'kuantiti' => $details['volume'], ]; } private function failed(Request $request, string $message) { if ($request->ajax() || $request->expectsJson()) { abort(502, $message); } return redirect()->back()->with('error', $message); } }