--- title: Electron SDK --- # Electron SDK Electron SDK 完全在 **主行程** 執行,透過作業系統原生視窗顯示驗證碼 UI。渲染行程不會參與驗證流程——為了安全請保持這種隔離。 ## GitHub 上的範例 ::: tip 📦 [Captcha-La/electron-demo](https://github.com/Captcha-La/electron-demo) — 完整可執行範例,包含所有整合步驟。 ::: ## 安裝 ```bash npm install @captchala/electron ``` 套件目標為 **Electron 28+**(demo 鎖定 `^33.0.0`)與 **Node 18+**。完整執行 demo: ```bash npm install npm start ``` ## 快速開始 ```js // main.js (Electron main process — never the renderer) const { app, BrowserWindow, ipcMain } = require('electron'); const path = require('path'); const { CaptchalaClient } = require('@captchala/electron'); let currentClient = null; ipcMain.handle('verify', async (event, opts) => { // 1. Fetch a one-shot server_token from YOUR backend. const serverToken = await fetchServerTokenFromYourBackend(opts.action); // 2. Tear down any prior client, then build a fresh one. if (currentClient) { currentClient.destroy(); currentClient = null; } currentClient = new CaptchalaClient({ appKey: 'YOUR_APP_KEY', serverToken, action: opts.action, // 'login' | 'register' | 'pay' | … lang: opts.lang, // 'en' | 'zh-CN' | 'ja' | … theme: opts.theme, // 'light' | 'dark' timeoutMs: 15000, retryCount: 3, onReady: () => event.sender.send('captcha-status', 'ready'), onFail: (e) => event.sender.send('captcha-status', `recoverable: ${e.code}`), }); try { const result = await currentClient.verify(); // Send result.passToken to YOUR backend for validation. return { success: true, passToken: result.passToken }; } catch (err) { return { success: false, error: err.message }; } finally { if (currentClient) { currentClient.destroy(); currentClient = null; } } }); function createWindow() { const win = new BrowserWindow({ width: 520, height: 640, resizable: false, webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: false, }, }); win.loadFile('index.html'); } app.whenReady().then(createWindow); app.on('window-all-closed', () => app.quit()); ``` ```js // preload.js — bridge the renderer to the main-process verify(). const { contextBridge, ipcRenderer } = require('electron'); contextBridge.exposeInMainWorld('captchala', { verify: (opts) => ipcRenderer.invoke('verify', opts), onStatus: (callback) => ipcRenderer.on('captcha-status', (_, msg) => callback(msg)), }); ``` ## API 一覽 | 符號 | 用途 | | --- | --- | | `new CaptchalaClient({...})` | 建立一次性客戶端。傳入 `appKey`、`serverToken`、`action`、`lang`、`theme`、`timeoutMs`、`retryCount`,以及事件處理函式。 | | `await client.verify()` | 開啟原生驗證碼視窗。成功時 resolve 回傳結果,硬錯誤時 reject。 | | `onReady` / `onFail` | 可選的建構子回呼。`onFail` 表示 *可恢復* 的失敗(SDK 會自動重試)。 | | `result.passToken` | 用於伺服器端驗證的 token,對客戶端始終為不透明字串。 | | `client.destroy()` | 關閉視窗並釋放資源。每個 client 實例使用完畢後都應呼叫。 | | `CaptchalaClient.version()` | 靜態方法,回傳 SDK 版本字串。適合放在支援 / 除錯浮層中。 | ## 伺服器端驗證 將 `result.passToken`(或 `result.token`)送到你的後端,再呼叫 CaptchaLa API 驗證。**切勿在用戶端程式碼中暴露 `X-App-Secret`**。 ```bash POST https://apiv1.captcha.la/v1/validate X-App-Key: YOUR_APP_KEY X-App-Secret: YOUR_APP_SECRET Content-Type: application/json { "pass_token": "" } ``` 完整驗證端點及 `X-App-Key` / `X-App-Secret` 流程請見 [API 參考](../api-reference)。 ## 常見問題 - **不要在 renderer 中 import SDK** `@captchala/electron` **僅能在主行程使用**。如同 demo 的 `preload.js`,透過 `contextBridge` + `ipcRenderer.invoke()` 暴露結果。維持 `nodeIntegration: false`、`contextIsolation: true`。 - **執行時找不到模組** `@captchala/electron` 應放在 `dependencies`(不是 `devDependencies`),必須打包進最終應用。electron-builder 等工具會自動包含。 - **驗證碼視窗出現在主視窗後面** 這是 Linux/X11 的常見問題。如果你包了一層 `BrowserWindow`,傳入 `parent`;或在 `verify()` 回傳後呼叫 `mainWindow.focus()`。 - **需要 Electron 28+** demo 中宣告 `electron: ^33.0.0`。較舊的 Electron 缺少 SDK 依賴的 API;請鎖定 `28+` 與 Node 18+。 ## 系統需求 - Electron 28+(demo 使用 33.x) - Node 18+ - 跨平台:macOS、Windows 10 1809+、Linux(X11 / Wayland)