🔑 實作 API Key 驗證:讓客戶付費買你的 API

在前面的章節中,我們都是扮演「呼叫別人 API」的角色(例如打 Stripe、打綠界、打 OpenAI)。 但如果你自己寫出了一個超強的 AI 圖片生成模型、或是一個非常精準的股市預測演算法,你想要把它做成 API,開放給全世界的工程師付費使用,你該怎麼做?

這時候,你需要建立一套**「API Key 驗證系統」**!

本章將帶你從零到一實作一套商業級的 API Key 邏輯,包含:產生金鑰、驗證身分,以及計算使用額度!


1. 什麼是 API Key?

API Key 就是一組難以猜測的長字串(例如 sk_live_abc123def456...)。 當客戶付錢給你後,你給他這組 Key。以後他呼叫你的 API 時,必須在 HTTP Header 裡面帶著這把 Key。你的後端只要看到這把 Key,就知道「哦!這是王大明的請求,扣他 1 點額度」。

資料庫設計 (PostgreSQL 範例)

為了管理 API Key,我們需要在資料庫建立一張關聯表:

CREATE TABLE api_keys (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id UUID REFERENCES users(id), -- 這是誰的 Key
  key_value VARCHAR(255) UNIQUE NOT NULL, -- 實際的字串 (例如 sk_live_...)
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  last_used_at TIMESTAMP WITH TIME ZONE,
  is_active BOOLEAN DEFAULT TRUE,
  usage_count INTEGER DEFAULT 0 -- 呼叫次數統計
);

2. 產生一把安全的 API Key

API Key 絕對不能是循序漸進的(例如 001, 002),否則駭客隨便猜就猜到了。 我們可以使用 Node.js 內建的 crypto 模組,來產生一把隨機且安全的字串。

// src/app/api/generate-key/route.ts
import { NextResponse } from 'next/server';
import crypto from 'crypto';
import { db } from '@/lib/db'; // 你的資料庫連線

// 產生隨機字串的 Helper Function
function generateApiKey() {
  // 產生 32 bytes 的隨機十六進位字串,前面加上自定義的 prefix
  const randomStr = crypto.randomBytes(32).toString('hex');
  return `vibe_sk_${randomStr}`; 
}

export async function POST(request: Request) {
  // 假設你有驗證使用者是否登入
  const userId = 'user-123'; 
  
  const newKey = generateApiKey();

  // 存入資料庫
  const insertedKey = await db.api_keys.insert({
    user_id: userId,
    key_value: newKey,
    is_active: true
  });

  return NextResponse.json({ 
    message: "API Key 建立成功!請妥善保存,它只會顯示這一次。",
    apiKey: newKey 
  });
}

⚠️ 資安實踐:在業界最嚴格的做法中,資料庫不應該儲存明文的 API Key,而是儲存 hash(API Key)。這就像密碼一樣,即使資料庫被盜,駭客也拿不到原始的金鑰。但在小型專案中,儲存明文或是加密儲存是可以接受的權衡。


3. 在你的核心 API 中實作「驗證大門」

現在客戶拿到 vibe_sk_xxx... 這把鑰匙了。 當他來呼叫你那支會賺錢的「AI 股市預測 API」時,我們必須在門口設下警衛檢查。

業界標準的做法是:請客戶將 API Key 放在 HTTP Header 的 Authorization: Bearer <API_KEY> 裡面。

// src/app/api/stock-predict/route.ts
import { NextResponse } from 'next/server';
import { db } from '@/lib/db';

export async function POST(request: Request) {
  // 1. 從 Header 取出 Authorization 欄位
  const authHeader = request.headers.get('Authorization');
  
  // 檢查格式是否正確 (Bearer vibe_sk_...)
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return NextResponse.json({ error: "Missing or invalid API Key format. Use 'Bearer <YOUR_KEY>'" }, { status: 401 });
  }

  // 取出真正的金鑰字串
  const providedKey = authHeader.split(' ')[1];

  // 2. 去資料庫比對這把鑰匙
  const keyRecord = await db.api_keys.findUnique({
    where: { key_value: providedKey }
  });

  // 如果鑰匙不存在,或是被停用了
  if (!keyRecord || !keyRecord.is_active) {
    return NextResponse.json({ error: "Invalid or inactive API Key" }, { status: 403 });
  }

  // 3. 檢查使用者的付費額度是否足夠 (可選)
  const user = await db.users.findUnique({ where: { id: keyRecord.user_id }});
  if (user.credits <= 0) {
    return NextResponse.json({ error: "Insufficient credits. Please top up your account." }, { status: 402 }); 
    // 402 是 Payment Required,超適合這裡用!
  }

  // ------------------------------------
  // 執行你真正值錢的商業邏輯
  // ------------------------------------
  const predictionResult = { stock: "TSLA", trend: "UP", confidence: 0.95 };

  // 4. 執行完畢,扣除點數並更新統計
  await db.users.update({
    where: { id: user.id },
    data: { credits: user.credits - 1 }
  });

  // 順便記錄這把 Key 的最後使用時間與次數
  await db.api_keys.update({
    where: { id: keyRecord.id },
    data: { 
      usage_count: keyRecord.usage_count + 1,
      last_used_at: new Date()
    }
  });

  // 5. 滿載而歸的 Response!
  return NextResponse.json({
    data: predictionResult,
    meta: {
      credits_remaining: user.credits - 1
    }
  });
}

4. 客戶端如何呼叫你的 API?

有了這道完美的大門,現在你可以理直氣壯地寫出你的 API 官方文件了! 教你的客戶用以下的方式來呼叫:

用 Fetch (JavaScript/TypeScript):

const response = await fetch('https://api.yourdomain.com/stock-predict', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    // 這裡放上客戶花大錢買的 API Key!
    'Authorization': 'Bearer vibe_sk_abc123...'
  },
  body: JSON.stringify({ symbol: "TSLA" })
});
const data = await response.json();

用 cURL (終端機):

curl -X POST https://api.yourdomain.com/stock-predict \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer vibe_sk_abc123..." \
  -d '{"symbol": "TSLA"}'

太棒了!你現在已經擁有了一套完整的 B2B (企業對企業) SaaS 商業架構!只要你能寫出有價值的演算法,搭配這套 API Key 驗證機制,你就能坐在家裡看著呼叫次數 (usage_count) 不斷飆高,等著每個月向客戶收費了!💸

解鎖完整教學內容

本章為付費內容。加入專案即可解鎖超過 5000 字的深度解析,包含 10 個以上神級 Prompt 與真實 Source Code 範例!