insert(['flow_key' => hash('sha256', $kind . ':' . $secret), 'kind' => $kind, 'payload' => json_encode($payload, JSON_THROW_ON_ERROR), 'expires_at' => time() + $ttl, 'consumed' => 0]); } public static function peek(string $kind, string $secret, string $sid): array { $row = Db::name('iam_v2_flow')->where('flow_key', hash('sha256', $kind . ':' . $secret))->find(); if (!$row || $row['expires_at'] <= time() || (int) $row['consumed'] !== 0) { throw new RuntimeException('TRANSACTION_EXPIRED'); } $p = json_decode($row['payload'], true, 32, JSON_THROW_ON_ERROR); if (($p['sessionId'] ?? '') !== $sid) { throw new RuntimeException('ACCOUNT_MISMATCH'); } return $p; } public static function take(string $kind, string $secret, string $sid): array { return Db::transaction(function () use ($kind, $secret, $sid) { $key = hash('sha256', $kind . ':' . $secret); $row = Db::name('iam_v2_flow')->where('flow_key', $key)->lock(true)->find(); if (!$row || $row['expires_at'] <= time() || (int) $row['consumed'] !== 0) { throw new RuntimeException('TRANSACTION_EXPIRED'); } $p = json_decode($row['payload'], true, 32, JSON_THROW_ON_ERROR); if (($p['sessionId'] ?? '') !== $sid) { throw new RuntimeException('ACCOUNT_MISMATCH'); } if (Db::name('iam_v2_flow')->where('flow_key', $key)->where('consumed', 0)->update(['consumed' => 1]) !== 1) { throw new RuntimeException('TRANSACTION_EXPIRED'); } return $p; }); } public static function form(string $url, array $fields): string { $nonce = bin2hex(random_bytes(18)); $escape = fn($s) => htmlspecialchars((string) $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); $inputs = ''; foreach ($fields as $name => $value) { $inputs .= ''; } $origin = parse_url($url, PHP_URL_SCHEME) . '://' . parse_url($url, PHP_URL_HOST) . (parse_url($url, PHP_URL_PORT) ? ':' . parse_url($url, PHP_URL_PORT) : ''); return '正在验证身份
' . $inputs . '
'; } }