$this->safeDate($query['date_from'] ?? ''), 'date_to' => $this->safeDate($query['date_to'] ?? ''), 'mem_no' => $this->safeInt($query['mem_no'] ?? null), 'bank_code' => $this->safeStr($query['bank_code'] ?? '', 10), 'status' => $this->safeStr($query['status'] ?? '', 10), // ok/fail/200/520... 'account' => $this->safeStr($query['account'] ?? '', 40), 'name' => $this->safeStr($query['name'] ?? '', 40), 'q' => $this->safeStr($query['q'] ?? '', 120), ]; if ($filters['date_from'] && $filters['date_to']) { if (strcmp($filters['date_from'], $filters['date_to']) > 0) { [$filters['date_from'], $filters['date_to']] = [$filters['date_to'], $filters['date_from']]; } } $page = $this->repo->paginate($filters, 30); $items = []; foreach ($page->items() as $it) { $r = is_array($it) ? $it : (array)$it; $reqRaw = (string)($r['request_data'] ?? ''); $resRaw = (string)($r['result_data'] ?? ''); $req = $this->decodeJson($reqRaw); $res = $this->decodeJson($resRaw); $memNo = (int)($r['mem_no'] ?? 0); $account = trim((string)($req['account'] ?? '')); $accountMasked = $this->maskAccount($account); $bankCode = trim((string)($req['bank_code'] ?? '')); $proType = trim((string)($req['account_protype'] ?? '')); $name = trim((string)($req['mam_accountname'] ?? '')); $status = (int)($res['status'] ?? 0); $ok = ($status === 200); $depositor = trim((string)($res['depositor'] ?? '')); $errCode = trim((string)($res['error_code'] ?? '')); $errMsg = trim((string)($res['error_message'] ?? '')); // JSON pretty (api_key는 마스킹) $reqPretty = $this->prettyJson($this->maskApiKey($req), $reqRaw); $resPretty = $this->prettyJson($res, $resRaw); $items[] = array_merge($r, [ 'mem_link' => $memNo > 0 ? ('/members/' . $memNo) : null, 'account' => $account, 'account_masked' => $accountMasked, 'bank_code' => $bankCode, 'account_protype' => $proType, 'mam_accountname' => $name, 'status_int' => $status, 'status_label' => $ok ? 'SUCCESS' : 'FAIL', 'status_badge' => $ok ? 'badge--ok' : 'badge--bad', 'depositor' => $depositor, 'error_code' => $errCode, 'error_message' => $errMsg, 'request_pretty' => $reqPretty, 'result_pretty' => $resPretty, ]); } return [ 'filters' => $filters, 'page' => $page, 'items' => $items, ]; } private function decodeJson(string $raw): array { $raw = trim($raw); if ($raw === '') return []; $arr = json_decode($raw, true); return is_array($arr) ? $arr : []; } private function prettyJson(array $arr, string $fallbackRaw): string { if (!empty($arr)) { return (string)json_encode($arr, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } return $fallbackRaw; } private function maskApiKey(array $req): array { if (!isset($req['api_key'])) return $req; $v = (string)$req['api_key']; $v = trim($v); if ($v === '') return $req; // 앞 4 + ... + 뒤 4 $head = mb_substr($v, 0, 4); $tail = mb_substr($v, -4); $req['api_key'] = $head . '…' . $tail; return $req; } private function maskAccount(string $account): string { $s = trim($account); if ($s === '') return ''; $len = mb_strlen($s); if ($len <= 4) return $s; return str_repeat('*', max(0, $len - 4)) . mb_substr($s, -4); } private function safeStr(mixed $v, int $max): string { $s = trim((string)$v); if ($s === '') return ''; if (mb_strlen($s) > $max) $s = mb_substr($s, 0, $max); return $s; } private function safeInt(mixed $v): ?int { if ($v === null || $v === '') return null; if (!is_numeric($v)) return null; $n = (int)$v; return $n >= 0 ? $n : null; } private function safeDate(mixed $v): ?string { $s = trim((string)$v); if ($s === '') return null; return preg_match('/^\d{4}-\d{2}-\d{2}$/', $s) ? $s : null; } }